diff --git a/Makefile b/Makefile index 591a3d7..cf2c946 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,8 @@ dir_k11_extension := k11_extension dir_sysmodules := sysmodules dir_loader := $(dir_sysmodules)/loader dir_rosalina := $(dir_sysmodules)/rosalina +dir_sm := $(dir_sysmodules)/sm +dir_pxi := $(dir_sysmodules)/pxi dir_build := build dir_out := out @@ -60,7 +62,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/chainloader.bin.o $(dir_build)/arm9_exceptions.bin.o -modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi +modules = $(dir_build)/loader.cxi $(dir_build)/rosalina.cxi $(dir_build)/sm.cxi $(dir_build)/pxi.cxi define bin2o bin2s $< | $(AS) -o $(@) @@ -83,6 +85,8 @@ clean: @$(MAKE) -C $(dir_k11_extension) clean @$(MAKE) -C $(dir_loader) clean @$(MAKE) -C $(dir_rosalina) clean + @$(MAKE) -C $(dir_sm) clean + @$(MAKE) -C $(dir_pxi) clean @rm -rf $(dir_out) $(dir_build) .PRECIOUS: $(dir_build)/%.bin @@ -93,6 +97,9 @@ clean: .PHONY: $(dir_k11_extension) .PHONY: $(dir_loader) .PHONY: $(dir_rosalina) +.PHONY: $(dir_sm) +.PHONY: $(dir_pxi) + $(dir_out)/$(name)$(revision).7z: all @mkdir -p "$(@D)" @@ -125,6 +132,14 @@ $(dir_build)/rosalina.cxi: $(dir_rosalina) @mkdir -p "$(@D)" @$(MAKE) -C $< +$(dir_build)/sm.cxi: $(dir_sm) + @mkdir -p "$(@D)" + @$(MAKE) -C $< + +$(dir_build)/pxi.cxi: $(dir_pxi) + @mkdir -p "$(@D)" + @$(MAKE) -C $< + $(dir_build)/%.bin.o: $(dir_build)/%.bin @$(bin2o) diff --git a/sysmodules/pxi/LICENSE b/sysmodules/pxi/LICENSE new file mode 100644 index 0000000..d4607d1 --- /dev/null +++ b/sysmodules/pxi/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 TuxSH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sysmodules/pxi/Makefile b/sysmodules/pxi/Makefile new file mode 100644 index 0000000..49a71f2 --- /dev/null +++ b/sysmodules/pxi/Makefile @@ -0,0 +1,52 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +CC := arm-none-eabi-gcc +AS := arm-none-eabi-as +LD := arm-none-eabi-ld +OC := arm-none-eabi-objcopy + +name := pxi + +dir_source := source +dir_build := build +dir_out := ../../$(dir_build) + +LIBS := -lctru +LIBDIRS := $(CTRULIB) +LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) + +ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -g -ffast-math -mword-relocations \ + -ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS +LDFLAGS := -specs=3dsx.specs -Wl,--gc-sections $(ARCH) + +objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.c)) + +.PHONY: all +all: $(dir_out)/$(name).cxi + +.PHONY: clean +clean: + @rm -rf $(dir_build) + +$(dir_out)/$(name).cxi: $(dir_build)/$(name).elf + @makerom -f ncch -rsf $(name).rsf -nocodepadding -o $@ -elf $< + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS) + +$(dir_build)/memory.o : CFLAGS += -O3 + +$(dir_build)/%.o: $(dir_source)/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< +include $(call rwildcard, $(dir_build), *.d) diff --git a/sysmodules/pxi/README.md b/sysmodules/pxi/README.md new file mode 100644 index 0000000..7a22126 --- /dev/null +++ b/sysmodules/pxi/README.md @@ -0,0 +1,15 @@ +# 3ds_pxi +Open source replacement of the ARM11 PXI system module. +This is licensed under the MIT license. + +# Usage +To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/) and copy pxi.cxi to /luma/sysmodules/. + +# Credits +This list is not complete at all: + +* @Subv, for the process patch that used to be used in Luma3DS which I modified to assist me in PXI sysmodule reverse-engineering +* @yifanlu, for the work his own work on loader +* @Mrrraou, for intensive testing back in June/July 2016 +* @jackron, for makerom support and help +* Many #Cakey and #3dsdev folks I haven't mentioned here, etc. diff --git a/sysmodules/pxi/pxi.rsf b/sysmodules/pxi/pxi.rsf new file mode 100644 index 0000000..b31dbd5 --- /dev/null +++ b/sysmodules/pxi/pxi.rsf @@ -0,0 +1,123 @@ +BasicInfo: + Title : pxi + CompanyCode : "00" + ProductCode : lennybuilder + ContentType : Application + Logo : None + +TitleInfo: + UniqueId : 0x14 + Category : Base + Version : 2 + +Option: + UseOnSD : false + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + IdealProcessor : 1 + AffinityMask : 3 + + Priority : 13 + + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : true + SpecialMemoryArrange : false + ResourceLimitCategory : Other + + CoreVersion : 2 + DescVersion : 2 + + MemoryType : Base # Application / System / Base + HandleTableSize: 0 + IORegisterMapping: + - 1ec63000 + SystemCallAccess: + AcceptSession: 74 + ArbitrateAddress: 34 + Break: 60 + BindInterrupt: 80 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreatePort: 71 + CreateSemaphore: 21 + CreateSessionToPort: 72 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + FlushProcessDataCache: 84 + GetCurrentProcessorNumber: 17 + GetDmaState: 87 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdealProcessor: 6 + GetProcessIdOfThread: 54 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + InvalidateProcessDataCache: 82 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + ReplyAndReceive1: 75 + ReplyAndReceive2: 76 + ReplyAndReceive3: 77 + ReplyAndReceive4: 78 + ReplyAndReceive: 79 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + StartInterProcessDma: 85 + StopDma: 86 + StoreProcessDataCache: 83 + UnmapMemoryBlock: 32 + UnbindInterrupt: 81 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + - 0x50 + - 0x51 + - 0x52 + - 0x53 + ServiceAccessControl: + FileSystemAccess: + +SystemControlInfo: + SaveDataSize: 0KB # It doesn't use any save data. + RemasterVersion: 0 + StackSize: 0x1000 diff --git a/sysmodules/pxi/source/MyThread.c b/sysmodules/pxi/source/MyThread.c new file mode 100644 index 0000000..8d18f15 --- /dev/null +++ b/sysmodules/pxi/source/MyThread.c @@ -0,0 +1,42 @@ +/* +MyThread.c: + Small threading library, based off ctrulib. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "MyThread.h" +#include "memory.h" + +static void _thread_begin(void* arg) +{ + MyThread *t = (MyThread *)arg; + t->ep(); + MyThread_Exit(); +} + +Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity) +{ + t->ep = entrypoint; + t->stacktop = (u8 *)stack + stackSize; + + return svcCreateThread(&t->handle, _thread_begin, (u32)t, (u32*)t->stacktop, prio, affinity); +} + +Result MyThread_Join(MyThread *thread, s64 timeout_ns) +{ + if (thread == NULL) return 0; + Result res = svcWaitSynchronization(thread->handle, timeout_ns); + if(R_FAILED(res)) return res; + + svcCloseHandle(thread->handle); + thread->handle = (Handle)0; + + return res; +} + +void MyThread_Exit(void) +{ + svcExitThread(); +} diff --git a/sysmodules/pxi/source/MyThread.h b/sysmodules/pxi/source/MyThread.h new file mode 100644 index 0000000..4ea68bf --- /dev/null +++ b/sysmodules/pxi/source/MyThread.h @@ -0,0 +1,28 @@ +/* +MyThread.h: + Small threading library, based off ctrulib. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> + +#define THREAD_STACK_SIZE 0x1000 + +typedef struct MyThread +{ + Handle handle; + void (*ep)(void); + bool finished; + void* stacktop; +} MyThread; + +Result MyThread_Create(MyThread *t, void (*entrypoint)(void), void *stack, u32 stackSize, int prio, int affinity); +Result MyThread_Join(MyThread *thread, s64 timeout_ns); +void MyThread_Exit(void); diff --git a/sysmodules/pxi/source/PXI.c b/sysmodules/pxi/source/PXI.c new file mode 100644 index 0000000..62c850f --- /dev/null +++ b/sysmodules/pxi/source/PXI.c @@ -0,0 +1,139 @@ +/* +PXI.c: + PXI I/O functions. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "PXI.h" + +void PXIReset(void) +{ + REG_PXI_SYNC = 0; + REG_PXI_CNT = CNT_CLEAR_SEND_FIFO; + + for(u32 i = 0; i < 16; i += 2) + { + REG_PXI_RECV; + REG_PXI_RECV; + } + + REG_PXI_CNT = 0; + REG_PXI_CNT = CNT_ENABLE_FIFOs | CNT_ACKNOWLEDGE_FIFO_ERROR | CNT_CLEAR_SEND_FIFO; +} + +void PXITriggerSync9IRQ(void) +{ + REG_PXI_INTERRUPT_CNT |= SYNC_TRIGGER_SYNC9_IRQ; +} + +bool PXIIsSendFIFOFull(void) +{ + return (REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS) != 0; +} + +void PXISendByte(u8 byte) +{ + REG_PXI_BYTE_SENT_TO_REMOTE = byte; +} + +void PXISendWord(u32 word) +{ + while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS); + REG_PXI_SEND = word; +} + +void PXISendBuffer(const u32 *buffer, u32 nbWords) +{ + for(; nbWords > 0; nbWords--) + { + while(REG_PXI_CNT & CNT_SEND_FIFO_FULL_STATUS); + REG_PXI_SEND = *buffer++; + } +} + +bool PXIIsReceiveFIFOEmpty(void) +{ + return (REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS) != 0; +} + +u8 PXIReceiveByte(void) +{ + return REG_PXI_BYTE_RECEIVED_FROM_REMOTE; +} + +u32 PXIReceiveWord(void) +{ + while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS); + return REG_PXI_RECV; +} + +void PXIReceiveBuffer(u32 *buffer, u32 nbWords) +{ + for(; nbWords > 0; nbWords--) + { + while(REG_PXI_CNT & CNT_RECEIVE_FIFO_EMPTY_STATUS); + *buffer++ = REG_PXI_RECV; + } +} + +Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt) +{ + Result res = 0; + u32 mask = CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ; + if(receiveFIFONotEmptyInterrupt != NULL) + { + res = svcBindInterrupt(0x53, *receiveFIFONotEmptyInterrupt, 0, false); + if(R_FAILED(res)) + { + unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt); + return res; + } + REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ; + } + + if(sendFIFOEmptyInterrupt != NULL) + { + res = svcBindInterrupt(0x52, *sendFIFOEmptyInterrupt, 0, false); + if(R_FAILED(res)) + { + unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt); + return res; + } + REG_PXI_CNT = (REG_PXI_CNT & mask) | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ; + } + + if(syncInterrupt != NULL) + { + res = svcBindInterrupt(0x50, *syncInterrupt, 0, false); + if(R_FAILED(res)) + { + unbindPXIInterrupts(syncInterrupt, receiveFIFONotEmptyInterrupt, sendFIFOEmptyInterrupt); + return res; + } + REG_PXI_INTERRUPT_CNT |= SYNC_ENABLE_SYNC11_IRQ; + } + + return res; +} + +void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt) +{ + if(receiveFIFONotEmptyInterrupt != NULL) + { + REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_SEND_FIFO_EMPTY_IRQ; + svcUnbindInterrupt(0x53, *receiveFIFONotEmptyInterrupt); + } + + if(sendFIFOEmptyInterrupt != NULL) + { + REG_PXI_CNT &= CNT_ENABLE_FIFOs | CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ; + svcUnbindInterrupt(0x52, *sendFIFOEmptyInterrupt); + } + if(syncInterrupt != NULL) + { + REG_PXI_INTERRUPT_CNT &= ~SYNC_ENABLE_SYNC11_IRQ; + svcUnbindInterrupt(0x50, *syncInterrupt); + } +} diff --git a/sysmodules/pxi/source/PXI.h b/sysmodules/pxi/source/PXI.h new file mode 100644 index 0000000..90ff467 --- /dev/null +++ b/sysmodules/pxi/source/PXI.h @@ -0,0 +1,47 @@ +/* +PXI.h: + PXI I/O functions. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds.h> + +#define PXI_REGS_BASE 0x1EC63000 +#define REG_PXI_SYNC *(vu32 *)(PXI_REGS_BASE + 0) + #define REG_PXI_BYTE_RECEIVED_FROM_REMOTE *(vu8 *)(PXI_REGS_BASE) + #define REG_PXI_BYTE_SENT_TO_REMOTE *(vu8 *)(PXI_REGS_BASE + 1) + #define REG_PXI_INTERRUPT_CNT *(vu8 *)(PXI_REGS_BASE + 3) + #define SYNC_TRIGGER_SYNC9_IRQ (1U << 6) + #define SYNC_ENABLE_SYNC11_IRQ (1U << 7) + +#define REG_PXI_CNT *(vu16 *)(PXI_REGS_BASE + 4) + #define CNT_SEND_FIFO_FULL_STATUS (1U << 1) + #define CNT_ENABLE_SEND_FIFO_EMPTY_IRQ (1U << 2) + #define CNT_CLEAR_SEND_FIFO (1U << 3) + #define CNT_RECEIVE_FIFO_EMPTY_STATUS (1U << 8) + #define CNT_ENABLE_RECEIVE_FIFO_NOT_EMPTY_IRQ (1U << 10) + #define CNT_ACKNOWLEDGE_FIFO_ERROR (1U << 14) + #define CNT_ENABLE_FIFOs (1U << 15) + +#define REG_PXI_SEND *(vu32 *)(PXI_REGS_BASE + 8) +#define REG_PXI_RECV *(vu32 *)(PXI_REGS_BASE + 12) + +void PXIReset(void); +void PXITriggerSync9IRQ(void); + +bool PXIIsSendFIFOFull(void); +void PXISendByte(u8 byte); +void PXISendWord(u32 word); +void PXISendBuffer(const u32 *buffer, u32 nbWords); + +bool PXIIsReceiveFIFOEmpty(void); +u8 PXIReceiveByte(void); +u32 PXIReceiveWord(void); +void PXIReceiveBuffer(u32 *buffer, u32 nbWords); + +Result bindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt); +void unbindPXIInterrupts(Handle *syncInterrupt, Handle *receiveFIFONotEmptyInterrupt, Handle *sendFIFOEmptyInterrupt); diff --git a/sysmodules/pxi/source/common.h b/sysmodules/pxi/source/common.h new file mode 100644 index 0000000..130a744 --- /dev/null +++ b/sysmodules/pxi/source/common.h @@ -0,0 +1,94 @@ +/* +common.h: + Common types and global variables. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds.h> + +typedef enum SessionState +{ + STATE_IDLE = 0, + STATE_RECEIVED_FROM_ARM11 = 1, + STATE_SENT_TO_ARM9 = 2, + STATE_RECEIVED_FROM_ARM9 = 3 +} SessionState; + +typedef struct SessionData +{ + SessionState state; + u32 buffer[0x100/4]; + + Handle handle; + u32 usedStaticBuffers; + + RecursiveLock lock; +} SessionData; + +#define NB_STATIC_BUFFERS 21 + +typedef struct SessionManager +{ + Handle sendAllBuffersToArm9Event, replySemaphore, PXISRV11CommandReceivedEvent, PXISRV11ReplySentEvent; + u32 latest_PXI_MC5_val, pendingArm9Commands; + u32 receivedServiceId; + RecursiveLock senderLock; + bool sendingDisabled; + SessionData sessionData[10]; + + u32 currentlyProvidedStaticBuffers, freeStaticBuffers; +} SessionManager; + +//Page alignment is mandatory there +extern u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x1000/4]; + +extern Handle PXISyncInterrupt, PXITransferMutex; +extern Handle terminationRequestedEvent; +extern bool shouldTerminate; +extern SessionManager sessionManager; + +extern const u32 nbStaticBuffersByService[10]; + +static inline Result assertSuccess(Result res) +{ + if(R_FAILED(res)) svcBreak(USERBREAK_PANIC); + return res; +} + +static inline s32 getMSBPosition(u32 val) +{ + return 31 - (s32) __builtin_clz(val); +} + +static inline s32 getLSBPosition(u32 val) +{ + return __builtin_ffs(val) - 1; +} + +static inline u32 clearMSBs(u32 val, u32 nb) +{ + for(u32 i = 0; i < nb; i++) + { + s32 pos = getMSBPosition(val); + if(pos == -1) break; + val &= ~(1 << pos); + } + + return val; +} + +static inline u32 countNbBitsSet(u32 val) +{ + u32 nb = 0; + while(val != 0) + { + val = clearMSBs(val, 1); + nb++; + } + + return nb; +} diff --git a/sysmodules/pxi/source/main.c b/sysmodules/pxi/source/main.c new file mode 100644 index 0000000..883afd1 --- /dev/null +++ b/sysmodules/pxi/source/main.c @@ -0,0 +1,211 @@ +/* +main.c + (De)initialization stuff. It's also here where sessions are being accepted. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "PXI.h" +#include "common.h" +#include "MyThread.h" +#include "receiver.h" +#include "sender.h" +#include "memory.h" + +Handle PXISyncInterrupt = 0, PXITransferMutex = 0; +Handle terminationRequestedEvent = 0; +bool shouldTerminate = false; +SessionManager sessionManager = {0}; + +const char *serviceNames[10] = +{ + "pxi:mc", + + "PxiFS0", + "PxiFS1", + "PxiFSB", + "PxiFSR", + + "PxiPM", + + "pxi:dev", //in the official PXI module maxSessions(pxi:dev) == 2. It doesn't matter anyways, since srvSysRegisterService is always called with 1 + "pxi:am9", + "pxi:ps9", + + "pxi:srv11", +}; + +const u32 nbStaticBuffersByService[10] = {0, 2, 2, 2, 2, 1, 4, 4, 4, 0}; + +u32 ALIGN(0x1000) staticBuffers[NB_STATIC_BUFFERS][0x400] = {{0}}; + +static inline void initPXI(void) +{ + Result res; + + Handle handles[2] = {0}; + + PXIReset(); + + if(PXISyncInterrupt != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B + assertSuccess(svcCreateEvent(&PXISyncInterrupt, RESET_ONESHOT)); + + if(PXITransferMutex != 0) svcBreak(USERBREAK_PANIC); //0xE0A0183B + assertSuccess(svcCreateMutex(&PXITransferMutex, false)); + + assertSuccess(svcCreateEvent(&handles[0], RESET_ONESHOT)); //receive FIFO not empty + assertSuccess(svcCreateEvent(&handles[1], RESET_ONESHOT)); //send FIFO empty + assertSuccess(bindPXIInterrupts(&PXISyncInterrupt, &handles[0], &handles[1])); + + s32 handleIndex; + do + { + while(!PXIIsSendFIFOFull()) PXISendWord(0); + + res = assertSuccess(svcWaitSynchronization(handles[0], 0LL)); + if(R_DESCRIPTION(res) == RD_TIMEOUT) + assertSuccess(svcWaitSynchronizationN(&handleIndex, handles, 2, false, -1LL)); + else + handleIndex = 0; + } while(handleIndex != 0); + + + + unbindPXIInterrupts(NULL, &handles[0], &handles[1]); + + PXISendByte(1); + while(PXIReceiveByte() < 1); + + while (!PXIIsReceiveFIFOEmpty()) + PXIReceiveWord(); + + PXISendByte(2); + while(PXIReceiveByte() < 2); + + svcCloseHandle(handles[0]); + svcCloseHandle(handles[1]); +} + +static inline void exitPXI(void) +{ + unbindPXIInterrupts(&PXISyncInterrupt, NULL, NULL); + svcCloseHandle(PXITransferMutex); + svcCloseHandle(PXISyncInterrupt); + PXIReset(); +} + +static u8 ALIGN(8) receiverStack[THREAD_STACK_SIZE]; +static u8 ALIGN(8) senderStack[THREAD_STACK_SIZE]; +static u8 ALIGN(8) PXISRV11HandlerStack[THREAD_STACK_SIZE]; + +// this is called before main +void __appInit() +{ + assertSuccess(svcCreateEvent(&terminationRequestedEvent, RESET_STICKY)); + + assertSuccess(svcCreateEvent(&sessionManager.sendAllBuffersToArm9Event, RESET_ONESHOT)); + assertSuccess(svcCreateSemaphore(&sessionManager.replySemaphore, 0, 9)); + assertSuccess(svcCreateEvent(&sessionManager.PXISRV11CommandReceivedEvent, RESET_ONESHOT)); + assertSuccess(svcCreateEvent(&sessionManager.PXISRV11ReplySentEvent, RESET_ONESHOT)); + initPXI(); + + for(Result res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) + { + res = srvInit(); + if(R_FAILED(res) && res != (Result)0xD88007FA) + svcBreak(USERBREAK_PANIC); + } +} + +// this is called after main exits +void __appExit() +{ + srvExit(); + exitPXI(); + + svcCloseHandle(terminationRequestedEvent); + svcCloseHandle(sessionManager.sendAllBuffersToArm9Event); + svcCloseHandle(sessionManager.replySemaphore); + svcCloseHandle(sessionManager.PXISRV11CommandReceivedEvent); + svcCloseHandle(sessionManager.PXISRV11ReplySentEvent); +} + +// stubs for non-needed pre-main functions +void __system_initSyscalls(){} + + +Result __sync_init(void); +Result __sync_fini(void); + +void __ctru_exit() +{ + __appExit(); + __sync_fini(); + svcExitProcess(); +} + +void initSystem() +{ + __sync_init(); + __system_initSyscalls(); + __appInit(); +} + +int main(void) +{ + Handle handles[10] = {0}; //notification handle + service handles + MyThread receiverThread = {0}, senderThread = {0}, PXISRV11HandlerThread = {0}; + + for(u32 i = 0; i < 9; i++) + assertSuccess(srvRegisterService(handles + 1 + i, serviceNames[i], 1)); + + assertSuccess(MyThread_Create(&receiverThread, receiver, receiverStack, THREAD_STACK_SIZE, 0x2D, -2)); + assertSuccess(MyThread_Create(&senderThread, sender, senderStack, THREAD_STACK_SIZE, 0x2D, -2)); + assertSuccess(MyThread_Create(&PXISRV11HandlerThread, PXISRV11Handler, PXISRV11HandlerStack, THREAD_STACK_SIZE, 0x2D, -2)); + + assertSuccess(srvEnableNotification(&handles[0])); + + while(!shouldTerminate) + { + s32 index = 0; + assertSuccess(svcWaitSynchronizationN(&index, handles, 10, false, -1LL)); + + if(index == 0) + { + u32 notificationId; + assertSuccess(srvReceiveNotification(¬ificationId)); + if(notificationId == 0x100) shouldTerminate = true; + } + + else + { + Handle session = 0; + SessionData *data = &sessionManager.sessionData[index - 1]; + assertSuccess(svcAcceptSession(&session, handles[index])); + + RecursiveLock_Lock(&sessionManager.senderLock); + if(data->handle != 0) + svcBreak(USERBREAK_PANIC); + + data->handle = session; + assertSuccess(svcSignalEvent(sessionManager.sendAllBuffersToArm9Event)); + + RecursiveLock_Unlock(&sessionManager.senderLock); + } + + } + + u32 PXIMC_OnPXITerminate = 0x10000; //TODO: see if this is correct + sessionManager.sessionData[0].state = STATE_SENT_TO_ARM9; + sendPXICmdbuf(NULL, 0, &PXIMC_OnPXITerminate); + + assertSuccess(MyThread_Join(&receiverThread, -1LL)); + assertSuccess(MyThread_Join(&senderThread, -1LL)); + assertSuccess(MyThread_Join(&PXISRV11HandlerThread, -1LL)); + + for(u32 i = 0; i < 10; i++) + svcCloseHandle(handles[i]); + + return 0; +} diff --git a/sysmodules/pxi/source/memory.c b/sysmodules/pxi/source/memory.c new file mode 100644 index 0000000..5d30780 --- /dev/null +++ b/sysmodules/pxi/source/memory.c @@ -0,0 +1,19 @@ +/* +memory.c: + Memory functions + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "memory.h" + +//Adpated from CakesFW +void memcpy(void *dest, const void *src, u32 size) +{ + u8 *destc = (u8 *)dest; + const u8 *srcc = (const u8 *)src; + + for(u32 i = 0; i < size; i++) + destc[i] = srcc[i]; +} diff --git a/sysmodules/pxi/source/memory.h b/sysmodules/pxi/source/memory.h new file mode 100644 index 0000000..5ce0d02 --- /dev/null +++ b/sysmodules/pxi/source/memory.h @@ -0,0 +1,13 @@ +/* +memory.h: + Memory functions + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds/types.h> + +void memcpy(void *dest, const void *src, u32 size); diff --git a/sysmodules/pxi/source/receiver.c b/sysmodules/pxi/source/receiver.c new file mode 100644 index 0000000..3ba73d9 --- /dev/null +++ b/sysmodules/pxi/source/receiver.c @@ -0,0 +1,69 @@ +/* +receiver.c: + Fetches replies coming from Process9, writing them in the appropriate buffer. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "receiver.h" +#include "PXI.h" +#include "memory.h" + +static inline void receiveFromArm9(void) +{ + u32 serviceId = PXIReceiveWord(); + + //The offcical implementation can return 0xD90043FA + if(((serviceId >= 10)) || (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9)) + svcBreak(USERBREAK_PANIC); + + sessionManager.receivedServiceId = serviceId; + RecursiveLock_Lock(&sessionManager.sessionData[serviceId].lock); + u32 replyHeader = PXIReceiveWord(); + u32 replySizeWords = (replyHeader & 0x3F) + ((replyHeader & 0xFC0) >> 6) + 1; + + if(replySizeWords > 0x40) svcBreak(USERBREAK_PANIC); + + u32 *buf = sessionManager.sessionData[serviceId].buffer; + + buf[0] = replyHeader; + PXIReceiveBuffer(buf + 1, replySizeWords - 1); + sessionManager.sessionData[serviceId].state = STATE_RECEIVED_FROM_ARM9; + RecursiveLock_Unlock(&sessionManager.sessionData[serviceId].lock); + + if(serviceId == 0 && shouldTerminate) + { + assertSuccess(svcSignalEvent(terminationRequestedEvent)); + return; + } + + if(serviceId != 9) + { + s32 count; + assertSuccess(svcReleaseSemaphore(&count, sessionManager.replySemaphore, 1)); + } + else + { + assertSuccess(svcSignalEvent(sessionManager.PXISRV11CommandReceivedEvent)); + assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL)); + if( (sessionManager.sessionData[serviceId].state != STATE_SENT_TO_ARM9)) + svcBreak(USERBREAK_PANIC); + } +} + +void receiver(void) +{ + Handle handles[] = {PXISyncInterrupt, terminationRequestedEvent}; + + assertSuccess(svcWaitSynchronization(sessionManager.PXISRV11ReplySentEvent, -1LL)); + while(true) + { + s32 index; + assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL)); + + if(index == 1) return; + while(!PXIIsReceiveFIFOEmpty()) + receiveFromArm9(); + } +} diff --git a/sysmodules/pxi/source/receiver.h b/sysmodules/pxi/source/receiver.h new file mode 100644 index 0000000..820f636 --- /dev/null +++ b/sysmodules/pxi/source/receiver.h @@ -0,0 +1,13 @@ +/* +receiver.h: + Fetches replies coming from Process9, writing them in the appropriate buffer. + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +void receiver(void); diff --git a/sysmodules/pxi/source/sender.c b/sysmodules/pxi/source/sender.c new file mode 100644 index 0000000..6fdc718 --- /dev/null +++ b/sysmodules/pxi/source/sender.c @@ -0,0 +1,300 @@ +/* +sender.c + Handles commands from arm11 processes, then sends them to Process9, and replies to arm11 processes the replies received from Process9 (=> receiver.c). + (except for PXISRV11) + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "sender.h" +#include "PXI.h" +#include "memory.h" + +Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer) +{ + + Result res = 0; + + if(additionalHandle != NULL) + { + s32 index = 1; + bool cancelled = false; + Handle handles[2] = {PXITransferMutex, *additionalHandle}; + + res = assertSuccess(svcWaitSynchronization(PXITransferMutex, 0LL)); + if(R_DESCRIPTION(res) == RD_TIMEOUT) + { + assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL)); + cancelled = index == 1; + } + else + cancelled = false; + + if(cancelled) + return 0xD92043FB; // cancel requested + } + else + assertSuccess(svcWaitSynchronization(PXITransferMutex, -1LL)); + + PXISendWord(serviceId & 0xFF); + PXITriggerSync9IRQ(); //notify arm9 + PXISendBuffer(buffer, (buffer[0] & 0x3F) + ((buffer[0] & 0xFC0) >> 6) + 1); + + svcReleaseMutex(PXITransferMutex); + return 0; +} + +static void updateTLSForStaticBuffers(void) +{ + u32 *staticBufs = getThreadStaticBuffers(); + u32 val = sessionManager.currentlyProvidedStaticBuffers; + for(u32 i = 0; i < 4; i++) + { + s32 pos = getLSBPosition(val); + if(pos != -1) + { + staticBufs[2 * i] = IPC_Desc_StaticBuffer(0x1000, 0); + staticBufs[2 * i + 1] = (u32)&staticBuffers[pos]; + val &= ~(1 << pos); + } + else + { + staticBufs[2 * i] = IPC_Desc_StaticBuffer(0, 0); + staticBufs[2 * i + 1] = 0; + } + } +} + +static void acquireStaticBuffers(void) +{ + u32 freeStaticBuffersOrig = sessionManager.freeStaticBuffers; + sessionManager.freeStaticBuffers = clearMSBs(sessionManager.freeStaticBuffers, 4); + sessionManager.currentlyProvidedStaticBuffers = ~sessionManager.freeStaticBuffers & freeStaticBuffersOrig; + updateTLSForStaticBuffers(); +} + +static void releaseStaticBuffers(u32 *src, u32 nb) +{ + u32 val = clearMSBs(*src, nb); + sessionManager.freeStaticBuffers |= ~val & *src; + *src = val; +} + +void sender(void) +{ + Handle handles[12] = {terminationRequestedEvent, sessionManager.sendAllBuffersToArm9Event, sessionManager.replySemaphore}; + Handle replyTarget = 0; + Result res = 0; + s32 index; + + u32 *cmdbuf = getThreadCommandBuffer(); + + u32 nbIdleSessions = 0; + u32 posToServiceId[9] = {0}; + RecursiveLock_Lock(&sessionManager.senderLock); + + //Setting static buffers is needed for IPC translation types 2 and 3 (otherwise ReplyAndReceive will dereference NULL) + sessionManager.freeStaticBuffers = (1 << NB_STATIC_BUFFERS) - 1; + acquireStaticBuffers(); + + do + { + if(replyTarget == 0) //send to arm9 + { + for(u32 i = 0; i < 9; i++) + { + SessionData *data = &sessionManager.sessionData[i]; + if(data->handle == 0 || data->state != STATE_RECEIVED_FROM_ARM11) + continue; + + if(sessionManager.sendingDisabled) + { + if (sessionManager.pendingArm9Commands != 0 || sessionManager.latest_PXI_MC5_val == 0) + continue; + } + + else + sessionManager.pendingArm9Commands++; + + RecursiveLock_Lock(&data->lock); + data->state = STATE_SENT_TO_ARM9; + res = sendPXICmdbuf(&terminationRequestedEvent, i, data->buffer); + RecursiveLock_Unlock(&data->lock); + + if(R_FAILED(res)) + goto terminate; + + } + cmdbuf[0] = 0xFFFF0000; //Kernel11 + } + + nbIdleSessions = 0; + for(u32 i = 0; i < 9; i++) + { + if(sessionManager.sessionData[i].handle != 0 && sessionManager.sessionData[i].state == STATE_IDLE) + { + handles[3 + nbIdleSessions] = sessionManager.sessionData[i].handle; + posToServiceId[nbIdleSessions++] = i; + } + } + + RecursiveLock_Unlock(&sessionManager.senderLock); + res = svcReplyAndReceive(&index, handles, 3 + nbIdleSessions, replyTarget); + RecursiveLock_Lock(&sessionManager.senderLock); + + if((u32)res == 0xC920181A) //session closed by remote + { + u32 i; + + if(index == -1) + for(i = 0; i < 9 && replyTarget != sessionManager.sessionData[i].handle; i++); + + else + i = posToServiceId[index - 3]; + + if(i >= 9) svcBreak(USERBREAK_PANIC); + + svcCloseHandle(sessionManager.sessionData[i].handle); + sessionManager.sessionData[i].handle = replyTarget = 0; + continue; + } + + else if(R_FAILED(res) || (u32)index >= 3 + nbIdleSessions) + svcBreak(USERBREAK_PANIC); + + switch(index) + { + case 0: //terminaton requested + break; + + case 1: + replyTarget = 0; + continue; + + case 2: //arm9 reply + { + u32 sessionId = 0; + for(sessionId = 0; sessionId < 9 && sessionManager.sessionData[sessionId].state != STATE_RECEIVED_FROM_ARM9; sessionId++); + if(sessionId == 9) svcBreak(USERBREAK_PANIC); + SessionData *data = &sessionManager.sessionData[sessionId]; + + RecursiveLock_Lock(&data->lock); + if(data->state != STATE_RECEIVED_FROM_ARM9) svcBreak(USERBREAK_PANIC); + if(sessionManager.latest_PXI_MC5_val == 2) + { + if(sessionManager.pendingArm9Commands != 0) svcBreak(USERBREAK_PANIC); + sessionManager.sendingDisabled = false; + } + else if(sessionManager.latest_PXI_MC5_val == 0) + (sessionManager.pendingArm9Commands)--; + + u32 bufSize = 4 * ((data->buffer[0] & 0x3F) + ((data->buffer[0] & 0xFC0) >> 6) + 1); + if(bufSize > 0x100) svcBreak(USERBREAK_PANIC); + memcpy(cmdbuf, data->buffer, bufSize); + + releaseStaticBuffers(&data->usedStaticBuffers, 4); + + data->state = STATE_IDLE; + replyTarget = data->handle; + + RecursiveLock_Unlock(&data->lock); + break; + } + + default: //arm11 command received + { + u32 serviceId = posToServiceId[index - 3]; + SessionData *data = &sessionManager.sessionData[serviceId]; + RecursiveLock_Lock(&data->lock); + + if(data->state != STATE_IDLE) svcBreak(USERBREAK_PANIC); + + if(!(serviceId == 0 && (cmdbuf[0] >> 16) == 5)) //if not pxi:mc 5 + sessionManager.latest_PXI_MC5_val = 0; + else if((u8)(cmdbuf[1]) != 0) + { + sessionManager.latest_PXI_MC5_val = 1; + if(sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC); + sessionManager.sendingDisabled = true; + } + else + { + sessionManager.latest_PXI_MC5_val = 2; + if(!sessionManager.sendingDisabled) svcBreak(USERBREAK_PANIC); + } + + u32 bufSize = 4 * ((cmdbuf[0] & 0x3F) + ((cmdbuf[0] & 0xFC0) >> 6) + 1); + if(bufSize > 0x100) svcBreak(USERBREAK_PANIC); + memcpy(data->buffer, cmdbuf, bufSize); + + data->state = STATE_RECEIVED_FROM_ARM11; + replyTarget = 0; + + releaseStaticBuffers(&sessionManager.currentlyProvidedStaticBuffers, 4 - nbStaticBuffersByService[serviceId]); + data->usedStaticBuffers = sessionManager.currentlyProvidedStaticBuffers; + acquireStaticBuffers(); + + RecursiveLock_Unlock(&data->lock); + break; + } + } + } + while(index != 0); + +terminate: + for(u32 i = 0; i < 9; i++) + { + if(sessionManager.sessionData[i].handle != 0) + svcCloseHandle(sessionManager.sessionData[i].handle); + } + + RecursiveLock_Unlock(&sessionManager.senderLock); +} + +void PXISRV11Handler(void) +{ + // Assumption: only 1 request is sent to this service at a time + Handle handles[] = {sessionManager.PXISRV11CommandReceivedEvent, terminationRequestedEvent}; + SessionData *data = &sessionManager.sessionData[9]; + + data->state = STATE_SENT_TO_ARM9; + assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent)); + + while(true) + { + s32 index; + assertSuccess(svcWaitSynchronizationN(&index, handles, 2, false, -1LL)); + + if(index == 1) return; + else + { + RecursiveLock_Lock(&data->lock); + + if(data->state != STATE_RECEIVED_FROM_ARM9) + svcBreak(USERBREAK_PANIC); + + data->state = STATE_IDLE; + + if(data->buffer[0] >> 16 != 1) + { + data->buffer[0] = 0x40; + data->buffer[1] = 0xD900182F; //unimplemented/invalid command + } + else + { + data->buffer[0] = 0x10040; + data->buffer[1] = srvPublishToSubscriber(data->buffer[1], 1); + + data->state = STATE_RECEIVED_FROM_ARM11; + if(data->buffer[1] == 0xD8606408) + svcBreak(USERBREAK_PANIC); + } + + assertSuccess(sendPXICmdbuf(&terminationRequestedEvent, 9, data->buffer)); + data->state = STATE_SENT_TO_ARM9; + assertSuccess(svcSignalEvent(sessionManager.PXISRV11ReplySentEvent)); + RecursiveLock_Unlock(&data->lock); + } + } +} diff --git a/sysmodules/pxi/source/sender.h b/sysmodules/pxi/source/sender.h new file mode 100644 index 0000000..3f70cce --- /dev/null +++ b/sysmodules/pxi/source/sender.h @@ -0,0 +1,15 @@ +/* +sender.h + Handles commands from arm11 processes, then sends them to Process9, and replies to arm11 processes the replies received from Process9 (=> receiver.c) (except for PXISRV11). + +(c) TuxSH, 2016-2017 +This is part of 3ds_pxi, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +Result sendPXICmdbuf(Handle *additionalHandle, u32 serviceId, u32 *buffer); +void sender(void); +void PXISRV11Handler(void); diff --git a/sysmodules/sm/LICENSE b/sysmodules/sm/LICENSE new file mode 100644 index 0000000..6e8d147 --- /dev/null +++ b/sysmodules/sm/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 TuxSH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sysmodules/sm/Makefile b/sysmodules/sm/Makefile new file mode 100644 index 0000000..7f870bf --- /dev/null +++ b/sysmodules/sm/Makefile @@ -0,0 +1,52 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/3ds_rules + +CC := arm-none-eabi-gcc +AS := arm-none-eabi-as +LD := arm-none-eabi-ld +OC := arm-none-eabi-objcopy + +name := sm + +dir_source := source +dir_build := build +dir_out := ../../$(dir_build) + +LIBS := -lctru +LIBDIRS := $(CTRULIB) +LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) + +ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -g -ffast-math -mword-relocations \ + -ffunction-sections -fdata-sections -fno-strict-aliasing $(INCLUDE) -DARM11 -D_3DS +LDFLAGS := -specs=3dsx.specs -Wl,--gc-sections $(ARCH) + +objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.c)) + +.PHONY: all +all: $(dir_out)/$(name).cxi + +.PHONY: clean +clean: + @rm -rf $(dir_build) + +$(dir_out)/$(name).cxi: $(dir_build)/$(name).elf + @makerom -f ncch -rsf $(name).rsf -nocodepadding -o $@ -elf $< + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS) + +$(dir_build)/memory.o : CFLAGS += -O3 + +$(dir_build)/%.o: $(dir_source)/%.c + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< +include $(call rwildcard, $(dir_build), *.d) diff --git a/sysmodules/sm/README.md b/sysmodules/sm/README.md new file mode 100644 index 0000000..b3e1885 --- /dev/null +++ b/sysmodules/sm/README.md @@ -0,0 +1,9 @@ +# 3ds_sm +Open source replacement of the ARM11 SM system module. +This is licensed under the MIT license. + +# Usage +To run this system module, use a recent release or commit of [Luma3DS](https://github.com/AuroraWright/Luma3DS/), build this project and copy sm.cxi to /luma/sysmodules/. + +# Credits +Everyone that helped me fix some of stupid bugs I had been making: @fincs, @Hikari-chin, etc. diff --git a/sysmodules/sm/sm.rsf b/sysmodules/sm/sm.rsf new file mode 100644 index 0000000..04b4849 --- /dev/null +++ b/sysmodules/sm/sm.rsf @@ -0,0 +1,111 @@ +BasicInfo: + Title : sm + CompanyCode : "00" + ProductCode : lennybuilder + ContentType : Application + Logo : None + +TitleInfo: + UniqueId : 0x10 + Category : Base + Version : 2 + +Option: + UseOnSD : false + FreeProductCode : true # Removes limitations on ProductCode + MediaFootPadding : false # If true CCI files are created with padding + EnableCrypt : false # Enables encryption for NCCH and CIA + EnableCompress : true # Compresses exefs code + +AccessControlInfo: + IdealProcessor : 1 + AffinityMask : 3 + + Priority : 20 + + DisableDebug : false + EnableForceDebug : false + CanWriteSharedPage : false + CanUsePrivilegedPriority : false + CanUseNonAlphabetAndNumber : false + PermitMainFunctionArgument : false + CanShareDeviceMemory : false + RunnableOnSleep : true + SpecialMemoryArrange : false + ResourceLimitCategory : Other + + CoreVersion : 2 + DescVersion : 2 + + MemoryType : Base # Application / System / Base + HandleTableSize: 0x200 + IORegisterMapping: + SystemCallAccess: + AcceptSession: 74 + ArbitrateAddress: 34 + Break: 60 + CancelTimer: 28 + ClearEvent: 25 + ClearTimer: 29 + CloseHandle: 35 + ConnectToPort: 45 + ControlMemory: 1 + CreateAddressArbiter: 33 + CreateEvent: 23 + CreateMemoryBlock: 30 + CreateMutex: 19 + CreatePort: 71 + CreateSemaphore: 21 + CreateSessionToPort: 72 + CreateSession: 73 + CreateThread: 8 + CreateTimer: 26 + DuplicateHandle: 39 + ExitProcess: 3 + ExitThread: 9 + GetCurrentProcessorNumber: 17 + GetHandleInfo: 41 + GetProcessId: 53 + GetProcessIdealProcessor: 6 + GetProcessIdOfThread: 54 + GetProcessInfo: 43 + GetResourceLimit: 56 + GetResourceLimitCurrentValues: 58 + GetResourceLimitLimitValues: 57 + GetSystemInfo: 42 + GetSystemTick: 40 + GetThreadContext: 59 + GetThreadId: 55 + GetThreadIdealProcessor: 15 + GetThreadInfo: 44 + GetThreadPriority: 11 + MapMemoryBlock: 31 + OutputDebugString: 61 + QueryMemory: 2 + ReleaseMutex: 20 + ReleaseSemaphore: 22 + ReplyAndReceive1: 75 + ReplyAndReceive2: 76 + ReplyAndReceive3: 77 + ReplyAndReceive4: 78 + ReplyAndReceive: 79 + SendSyncRequest1: 46 + SendSyncRequest2: 47 + SendSyncRequest3: 48 + SendSyncRequest4: 49 + SendSyncRequest: 50 + SetThreadPriority: 12 + SetTimer: 27 + SignalEvent: 24 + SleepThread: 10 + UnmapMemoryBlock: 32 + WaitSynchronization1: 36 + WaitSynchronizationN: 37 + InterruptNumbers: + ServiceAccessControl: + FileSystemAccess: + +SystemControlInfo: + SaveDataSize: 0KB # It doesn't use any save data. + RemasterVersion: 0 + StackSize: 0x1000 diff --git a/sysmodules/sm/source/common.h b/sysmodules/sm/source/common.h new file mode 100644 index 0000000..b55780a --- /dev/null +++ b/sysmodules/sm/source/common.h @@ -0,0 +1,51 @@ +/* +common.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds.h> +#include "memory.h" + +#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4)) +#define IS_PRE_93 (osGetFirmVersion() < SYSTEM_VERSION(2, 48, 3)) + +extern u32 nbSection0Modules; +extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore; + +struct SessionDataList; + +typedef struct SessionData +{ + struct SessionData *prev, *next; + struct SessionDataList *parent; + u32 pid; + u32 replayCmdbuf[4]; + Handle busyClientPortHandle; + Handle handle; + bool isSrvPm; +} SessionData; + +typedef struct SessionDataList +{ + SessionData *first, *last; +} SessionDataList; + +extern SessionDataList sessionDataInUseList, freeSessionDataList; +extern SessionDataList sessionDataWaitingForServiceOrPortRegisterList, sessionDataToWakeUpAfterServiceOrPortRegisterList; +extern SessionDataList sessionDataWaitingPortReadyList; + +static inline void panic(void) +{ + svcBreak(USERBREAK_PANIC); + for(;;) svcSleepThread(0); +} + +static inline void assertSuccess(Result res) +{ + if(R_FAILED(res)) + panic(); +} diff --git a/sysmodules/sm/source/list.c b/sysmodules/sm/source/list.c new file mode 100644 index 0000000..7945c3b --- /dev/null +++ b/sysmodules/sm/source/list.c @@ -0,0 +1,106 @@ +/* +list.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "list.h" +#include "memory.h" +#include "common.h" + +struct ListBase; +typedef struct ListNodeBase +{ + struct ListNodeBase *prev, *next; + struct ListBase *parent; +} ListNodeBase; + +typedef struct ListBase +{ + ListNodeBase *first, *last; +} ListBase; + +void buildList(void *list, void *pool, u32 nb, u32 elementSize) +{ + ListBase *listB = (ListBase *)list; + for(u32 i = 0; i < nb; i++) + { + ListNodeBase *node = (ListNodeBase *)((u8 *)pool + i * elementSize); + node->prev = i == 0 ? NULL : (ListNodeBase *)((u8 *)pool + (i - 1) * elementSize); + node->next = i == (nb - 1) ? NULL : (ListNodeBase *)((u8 *)pool + (i + 1) * elementSize); + node->parent = list; + } + + listB->first = (ListNodeBase *)pool; + listB->last = (ListNodeBase *)((u8 *)pool + (nb - 1) * elementSize); +} + +void moveNode(void *node, void *dst, bool back) +{ + ListNodeBase *nodeB = (ListNodeBase *)node; + ListBase *dstB = (ListBase *)dst; + ListBase *srcB = nodeB->parent; + + if(dstB == srcB) + return; + + // Remove the node in the source list + if(nodeB->prev != NULL) + nodeB->prev->next = nodeB->next; + + if(nodeB->next != NULL) + nodeB->next->prev = nodeB->prev; + + // Update the source list if needed + if(nodeB == srcB->first) + srcB->first = nodeB->next; + if(nodeB == srcB->last) + srcB->last = nodeB->prev; + + // Insert the node in the destination list + if(back) + { + if(dstB->last != NULL) + dstB->last->next = nodeB; + + nodeB->prev = dstB->last; + nodeB->next = NULL; + dstB->last = nodeB; + } + else + { + if(dstB->first != NULL) + dstB->first->prev = nodeB; + + nodeB->next = dstB->first; + nodeB->prev = NULL; + dstB->first = nodeB; + } + + // Normalize the destination list + if(dstB->first != NULL && dstB->last == NULL) + dstB->last = dstB->first; + else if(dstB->first == NULL && dstB->last != NULL) + dstB->first = dstB->last; + + nodeB->parent = dstB; +} + +void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back) +{ + ListBase *freeListB = (ListBase *)freeList; + + if(freeListB->first == NULL) + panic(); + + ListNodeBase *node = freeListB->first; + ListNodeBase nodeBk = *node; + + memset(node, 0, elementSize); + *node = nodeBk; + + moveNode(node, inUseList, back); + + return node; +} diff --git a/sysmodules/sm/source/list.h b/sysmodules/sm/source/list.h new file mode 100644 index 0000000..14150fe --- /dev/null +++ b/sysmodules/sm/source/list.h @@ -0,0 +1,14 @@ +/* +list.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds/types.h> + +void buildList(void *list, void *pool, u32 nb, u32 elementSize); +void moveNode(void *node, void *dst, bool back); +void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back); diff --git a/sysmodules/sm/source/main.c b/sysmodules/sm/source/main.c new file mode 100644 index 0000000..298f950 --- /dev/null +++ b/sysmodules/sm/source/main.c @@ -0,0 +1,299 @@ +/* +main.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "common.h" +#include "memory.h" +#include "services.h" +#include "processes.h" +#include "srv.h" +#include "srv_pm.h" +#include "list.h" + +extern u32 __ctru_heap; +extern u32 __ctru_linear_heap; +extern char (*serviceAccessListBuffers)[34][8]; + +u32 __ctru_heap_size = 0x4000; +u32 __ctru_linear_heap_size = 0; + +u32 nbSection0Modules; +Handle resumeGetServiceHandleOrPortRegisteredSemaphore; + +SessionDataList sessionDataInUseList = {NULL, NULL}, freeSessionDataList = {NULL, NULL}; +SessionDataList sessionDataWaitingForServiceOrPortRegisterList = {NULL, NULL}, sessionDataToWakeUpAfterServiceOrPortRegisterList = {NULL, NULL}; +SessionDataList sessionDataWaitingPortReadyList = {NULL, NULL}; + +static SessionData sessionDataPool[76]; +static ProcessData processDataPool[64]; + +static u8 ALIGN(4) serviceAccessListStaticBuffer[0x110]; + +void __appInit(void) +{ + s64 out; + + u32 *staticBuffers = getThreadStaticBuffers(); + staticBuffers[0] = IPC_Desc_StaticBuffer(0x110, 0); + staticBuffers[1] = (u32)serviceAccessListStaticBuffer; + + svcGetSystemInfo(&out, 26, 0); + nbSection0Modules = out; + assertSuccess(svcCreateSemaphore(&resumeGetServiceHandleOrPortRegisteredSemaphore, 0, 64)); + serviceAccessListBuffers = (char (*)[34][8])__ctru_heap; + + buildList(&freeSessionDataList, sessionDataPool, sizeof(sessionDataPool) / sizeof(SessionData), sizeof(SessionData)); + buildList(&freeProcessDataList, processDataPool, sizeof(processDataPool) / sizeof(ProcessData), sizeof(ProcessData)); +} + + +// this is called after main exits +void __appExit(void){} + +void __system_allocateHeaps(void) +{ + u32 tmp = 0; + + // Allocate the application heap + __ctru_heap = 0x08000000; + svcControlMemory(&tmp, __ctru_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE); + + __ctru_linear_heap = 0; +} + +void __system_initSyscalls(void){} + +Result __sync_init(void); +Result __sync_fini(void); + +void __ctru_exit(void){} + +void initSystem(void) +{ + __sync_init(); + __system_allocateHeaps(); + __appInit(); +} + +int main(void) +{ + Result res; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 nbHandles = 3, nbSessions = 0, nbSrvPmSessions = 0; + + Handle clientPortDummy; + Handle srvPort, srvPmPort; + Handle handles[0xE3] = { 0 }; + Handle replyTarget = 0; + + u32 smPid; + SessionData *sessionData; + + assertSuccess(svcGetProcessId(&smPid, CUR_PROCESS_HANDLE)); + assertSuccess(svcCreatePort(&srvPort, &clientPortDummy, "srv:", 64)); + + if(IS_PRE_7X) + assertSuccess(svcCreatePort(&srvPmPort, &clientPortDummy, "srv:pm", 64)); + else + assertSuccess(doRegisterService(smPid, &srvPmPort, "srv:pm", 6, 64)); + + handles[0] = resumeGetServiceHandleOrPortRegisteredSemaphore; + handles[1] = srvPort; + handles[2] = srvPmPort; + + for(;;) + { + s32 id; + if(replyTarget == 0) + cmdbuf[0] = 0xFFFF0000; // Kernel11 + + res = svcReplyAndReceive(&id, handles, nbHandles, replyTarget); + if(res == (Result)0xC920181A) // unreachable remote + { + // Note: if a process has ended, pm will call UnregisterProcess on it + if(id < 0) + { + for(id = 3; (u32)id < nbHandles && handles[id] != replyTarget; id++); + if((u32)id >= nbHandles) + panic(); + } + + if(id < 3) + panic(); + else if((u32)id < 3 + nbSessions) // Session closed + { + sessionData = NULL; + for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++) + { + if(sessionDataPool[i].handle == handles[id]) + sessionData = &sessionDataPool[i]; + } + + if(sessionData != NULL) + { + if(sessionData->busyClientPortHandle != 0) // remove unreferenced client port handles from array + { + u32 refCount = 0; + for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++) + { + if(sessionDataPool[i].busyClientPortHandle == sessionData->busyClientPortHandle) + ++refCount; + } + if(refCount <= 1) + { + u32 i; + for(i = 3 + nbSessions; i < nbHandles && handles[i] == sessionData->busyClientPortHandle; i++); + if(i < nbHandles) + handles[i] = handles[--nbHandles]; + } + } + + --nbHandles; + for(u32 i = (u32)id; i < nbHandles; i++) + handles[i] = handles[i + 1]; + --nbSessions; + svcCloseHandle(sessionData->handle); + if(sessionData->isSrvPm) + --nbSrvPmSessions; + moveNode(sessionData, &freeSessionDataList, false); + } + else + panic(); + } + else // Port closed + { + SessionData *nextSessionData = NULL; + + // Update the command postponing reason accordingly + for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL; sessionData = nextSessionData) + { + nextSessionData = sessionData->next; + if(sessionData->busyClientPortHandle == handles[id]) + { + sessionData->replayCmdbuf[1] = 0xD0406401; // unregistered service or named port + moveNode(sessionData, &sessionDataWaitingForServiceOrPortRegisterList, true); + svcCloseHandle(handles[id]); + handles[id] = handles[--nbHandles]; + sessionData->busyClientPortHandle = 0; + } + } + } + + replyTarget = 0; + } + else if(R_FAILED(res)) + panic(); + else + { + replyTarget = 0; + if(id == 1) // New srv: session + { + Handle session; + assertSuccess(svcAcceptSession(&session, srvPort)); + sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false); + sessionData->pid = (u32)-1; + sessionData->handle = session; + for(u32 i = nbHandles; i > 3 + nbSessions; i--) + handles[i] = handles[i - 1]; + handles[3 + nbSessions++] = session; + ++nbHandles; + } + else if(id == 2) // New srv:pm session + { + Handle session; + if(!IS_PRE_7X && nbSrvPmSessions >= 1) + panic(); + assertSuccess(svcAcceptSession(&session, srvPmPort)); + sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false); + sessionData->pid = (u32)-1; + sessionData->handle = session; + sessionData->isSrvPm = true; + for(u32 i = nbHandles; i > 3 + nbSessions; i--) + handles[i] = handles[i - 1]; + handles[3 + nbSessions++] = session; + ++nbHandles; + ++nbSrvPmSessions; + } + else + { + if(id == 0) // Resume SRV:GetServiceHandle or GetPort due to service or named port not registered + { + if(sessionDataToWakeUpAfterServiceOrPortRegisterList.first == NULL) + panic(); + sessionData = sessionDataToWakeUpAfterServiceOrPortRegisterList.first; + moveNode(sessionData, &sessionDataInUseList, false); + for(u32 i = nbHandles; i > 3 + nbSessions; i--) + handles[i] = handles[i - 1]; + handles[3 + nbSessions++] = sessionData->handle; + ++nbHandles; + if(sessionData->isSrvPm) + ++nbSrvPmSessions; + memcpy(cmdbuf, sessionData->replayCmdbuf, 16); + } + else if((u32)id >= 3 + nbSessions) // Resume SRV:GetServiceHandle if service was full + { + SessionData *sTmp; + for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL && sessionData->busyClientPortHandle != handles[id]; + sessionData = sessionData->next); + if(sessionData == NULL) + panic(); + moveNode(sessionData, &sessionDataInUseList, false); + for(sTmp = sessionDataWaitingPortReadyList.first; sTmp != NULL && sTmp->busyClientPortHandle != handles[id]; sTmp = sTmp->next); + if(sTmp == NULL) + handles[id] = handles[--nbHandles]; + for(u32 i = nbHandles + 1; i > 3 + nbSessions; i--) + handles[i] = handles[i - 1]; + handles[3 + nbSessions++] = sessionData->handle; + ++nbHandles; + if(sessionData->isSrvPm) + ++nbSrvPmSessions; + memcpy(cmdbuf, sessionData->replayCmdbuf, 16); + sessionData->busyClientPortHandle = 0; + } + else + { + for(sessionData = sessionDataInUseList.first; sessionData != NULL && sessionData->handle != handles[id]; sessionData = sessionData->next); + if(sessionData == NULL) + panic(); + } + + for(id = 3; (u32)id < 3 + nbSessions && handles[id] != sessionData->handle; id++); + if((u32)id >= 3 + nbSessions) + panic(); + + res = sessionData->isSrvPm ? srvPmHandleCommands(sessionData) : srvHandleCommands(sessionData); + + if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK) + { + SessionDataList *dstList = NULL; + if(res == (Result)0xD0406401) // service or named port not registered yet + dstList = &sessionDataWaitingForServiceOrPortRegisterList; + else if(res == (Result)0xD0406402) // service full + { + u32 i; + dstList = &sessionDataWaitingPortReadyList; + for(i = 3 + nbSessions; i < nbHandles && handles[i] != sessionData->busyClientPortHandle; i++); + if(i >= nbHandles) + handles[nbHandles++] = sessionData->busyClientPortHandle; + } + else + panic(); + + --nbHandles; + for(u32 i = (u32)id; i < nbHandles; i++) + handles[i] = handles[i + 1]; + --nbSessions; + if(sessionData->isSrvPm) + --nbSrvPmSessions; + moveNode(sessionData, dstList, true); + } + else + replyTarget = sessionData->handle; + } + } + } + return 0; +} diff --git a/sysmodules/sm/source/memory.c b/sysmodules/sm/source/memory.c new file mode 100644 index 0000000..6b3336d --- /dev/null +++ b/sysmodules/sm/source/memory.c @@ -0,0 +1,27 @@ +/* +memory.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "memory.h" + +/* +//Adpated from CakesFW +void memcpy(void *dest, const void *src, u32 size) +{ + u8 *destc = (u8 *)dest; + const u8 *srcc = (const u8 *)src; + + for(u32 i = 0; i < size; i++) + destc[i] = srcc[i]; +}*/ + +s32 strnlen(const char *string, s32 maxlen) +{ + s32 size; + for(size = 0; size < maxlen && *string; string++, size++); + + return size; +} diff --git a/sysmodules/sm/source/memory.h b/sysmodules/sm/source/memory.h new file mode 100644 index 0000000..6b1ceef --- /dev/null +++ b/sysmodules/sm/source/memory.h @@ -0,0 +1,18 @@ +/* +memory.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds/types.h> + +//void memcpy(void *dest, const void *src, u32 size); +#define memcpy __builtin_memcpy +#define memset __builtin_memset +#define strncmp __builtin_strncmp +#define strncpy __builtin_strncpy + +s32 strnlen(const char *string, s32 maxlen); diff --git a/sysmodules/sm/source/notifications.c b/sysmodules/sm/source/notifications.c new file mode 100644 index 0000000..82c722b --- /dev/null +++ b/sysmodules/sm/source/notifications.c @@ -0,0 +1,191 @@ +/* +notifications.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "notifications.h" +#include "processes.h" + +static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags) +{ + if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending + { + for(u16 i = 0; i < processData->nbPendingNotifications; i++) + { + if(processData->pendingNotifications[(processData->pendingNotificationIndex + i) % 16] == notificationId) + return true; + } + } + + if(processData->nbPendingNotifications < 0x10) + { + s32 count; + + processData->pendingNotifications[processData->pendingNotificationIndex] = notificationId; + processData->pendingNotificationIndex = (processData->pendingNotificationIndex + 1) % 16; + ++processData->nbPendingNotifications; + assertSuccess(svcReleaseSemaphore(&count, processData->notificationSemaphore, 1)); + + return true; + } + else + return (flags & 2) != 0; +} + +Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore) +{ + ProcessData *processData = findProcessData(sessionData->pid); + + if(processData == NULL) + { + // Section 0 modules have access to all services, so we need to register them here for notifications + if(sessionData->pid < nbSection0Modules) + processData = doRegisterProcess(sessionData->pid, NULL, 0); + else + return 0xD8806404; + } + + processData->notificationEnabled = true; + *notificationSemaphore = processData->notificationSemaphore; + + return 0; +} + +Result Subscribe(SessionData *sessionData, u32 notificationId) +{ + ProcessData *processData = findProcessData(sessionData->pid); + + + if(processData == NULL || !processData->notificationEnabled) + return 0xD8806404; + + for(u16 i = 0; i < processData->nbSubscribed; i++) + { + if(processData->subscribedNotifications[i] == notificationId) + return 0xD9006403; + } + + if(processData->nbSubscribed < 0x11) + { + processData->subscribedNotifications[processData->nbSubscribed++] = notificationId; + return 0; + } + else + return 0xD9006405; +} + +Result Unsubscribe(SessionData *sessionData, u32 notificationId) +{ + ProcessData *processData = findProcessData(sessionData->pid); + + if(processData == NULL || !processData->notificationEnabled) + return 0xD8806404; + + u16 i; + for(i = 0; i < processData->nbSubscribed && processData->subscribedNotifications[i] != notificationId; i++); + + if(i == processData->nbSubscribed) + return 0xD8806404; + else + { + processData->subscribedNotifications[i] = processData->subscribedNotifications[--processData->nbSubscribed]; + return 0; + } +} + +Result ReceiveNotification(SessionData *sessionData, u32 *notificationId) +{ + ProcessData *processData = findProcessData(sessionData->pid); + + if(processData == NULL || !processData->notificationEnabled || processData->nbPendingNotifications == 0) + { + if(processData->nbPendingNotifications) + *notificationId = 0; + return 0xD8806404; + } + else + { + --processData->nbPendingNotifications; + *notificationId = processData->pendingNotifications[processData->receivedNotificationIndex]; + processData->receivedNotificationIndex = (processData->receivedNotificationIndex + 1) % 16; + return 0; + } +} + +Result PublishToSubscriber(u32 notificationId, u32 flags) +{ + for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next) + { + if(!node->notificationEnabled) + continue; + + u16 i; + for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++); + if(i >= node->nbSubscribed) + continue; + + if(!doPublishNotification(node, notificationId, flags)) + return 0xD8606408; + } + + return 0; +} + +Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags) +{ + u32 nb = 0; + for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next) + { + if(!node->notificationEnabled) + continue; + + u16 i; + for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++); + if(i >= node->nbSubscribed) + continue; + + if(!doPublishNotification(node, notificationId, flags)) + return 0xD8606408; + else if(pidList != NULL && nb < 60) + pidList[nb++] = node->pid; + } + + if(pidCount != NULL) + *pidCount = nb; + + return 0; +} + +Result PublishToProcess(Handle process, u32 notificationId) +{ + u32 pid; + Result res = svcGetProcessId(&pid, process); + if(R_FAILED(res)) + return res; + + ProcessData *processData = findProcessData(pid); + if(processData == NULL || !processData->notificationEnabled) + res = 0xD8806404; + else if(!doPublishNotification(processData, notificationId, 0)) + res = 0xD8606408; + else + res = 0; + + svcCloseHandle(process); + return res; +} + +Result PublishToAll(u32 notificationId) +{ + for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next) + { + if(!node->notificationEnabled) + continue; + else if(!doPublishNotification(node, notificationId, 0)) + return 0xD8606408; + } + + return 0; +} diff --git a/sysmodules/sm/source/notifications.h b/sysmodules/sm/source/notifications.h new file mode 100644 index 0000000..a8b7fbf --- /dev/null +++ b/sysmodules/sm/source/notifications.h @@ -0,0 +1,19 @@ +/* +notifications.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore); +Result Subscribe(SessionData *sessionData, u32 notificationId); +Result Unsubscribe(SessionData *sessionData, u32 notificationId); +Result ReceiveNotification(SessionData *sessionData, u32 *notificationId); +Result PublishToSubscriber(u32 notificationId, u32 flags); +Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags); +Result PublishToProcess(Handle process, u32 notificationId); +Result PublishToAll(u32 notificationId); diff --git a/sysmodules/sm/source/processes.c b/sysmodules/sm/source/processes.c new file mode 100644 index 0000000..3f92071 --- /dev/null +++ b/sysmodules/sm/source/processes.c @@ -0,0 +1,88 @@ +/* +processes.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "list.h" +#include "processes.h" +#include "services.h" + +ProcessDataList processDataInUseList = { NULL, NULL }, freeProcessDataList = { NULL, NULL }; +char (*serviceAccessListBuffers)[34][8]; + +// The kernel limits the number of processes to 47 anyways... +static u64 freeServiceAccessListBuffersIds = (1ULL << 59) - 1; + +ProcessData *findProcessData(u32 pid) +{ + for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next) + { + if(node->pid == pid) + return node; + } + + return NULL; +} + +ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize) +{ + ProcessData *processData = (ProcessData *)allocateNode(&processDataInUseList, &freeProcessDataList, sizeof(ProcessData), false); + if(serviceAccessListSize != 0) + { + s32 bufferId = 63 - __builtin_clzll(freeServiceAccessListBuffersIds); + if(bufferId == -1) + panic(); + else + { + freeServiceAccessListBuffersIds &= ~(1ULL << bufferId); + processData->serviceAccessList = serviceAccessListBuffers[bufferId]; + processData->serviceAccessListSize = serviceAccessListSize; + memcpy(processData->serviceAccessList, serviceAccessList, serviceAccessListSize); + } + } + + assertSuccess(svcCreateSemaphore(&processData->notificationSemaphore, 0, 0x10)); + processData->pid = pid; + + return processData; +} + +Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize) +{ + serviceAccessListSize = serviceAccessListSize - (serviceAccessListSize % 8); + if(findProcessData(pid) != NULL) + return 0xD9006403; + else if(serviceAccessListSize > 8 * (IS_PRE_93 ? 32 : 34)) + return 0xD9006405; + else + { + doRegisterProcess(pid, serviceAccessList, serviceAccessListSize); + return 0; + } +} + +Result UnregisterProcess(u32 pid) +{ + ProcessData *processData = findProcessData(pid); + if(processData == NULL) + return 0xD8806404; + + svcCloseHandle(processData->notificationSemaphore); + + // Unregister the services registered by the process + for(u32 i = 0; i < nbServices; i++) + { + if(servicesInfo[i].pid == pid) + { + svcCloseHandle(servicesInfo[i].clientPort); + servicesInfo[i] = servicesInfo[--nbServices]; + } + } + + freeServiceAccessListBuffersIds |= 1ULL << (u32)((processData->serviceAccessList - serviceAccessListBuffers[0]) / 34); + + moveNode(processData, &freeProcessDataList, false); + return 0; +} diff --git a/sysmodules/sm/source/processes.h b/sysmodules/sm/source/processes.h new file mode 100644 index 0000000..3f9b927 --- /dev/null +++ b/sysmodules/sm/source/processes.h @@ -0,0 +1,48 @@ +/* +processes.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +struct ProcessDataList; + +typedef struct ProcessData +{ + struct ProcessData *prev, *next; + struct ProcessDataList *parent; + + u32 pid; + + Handle notificationSemaphore; + + char (*serviceAccessList)[8]; + u32 serviceAccessListSize; + + bool notificationEnabled; + // Circular buffer + u16 receivedNotificationIndex; + u16 pendingNotificationIndex; + + u16 nbPendingNotifications; + u32 pendingNotifications[16]; + u16 nbSubscribed; + u32 subscribedNotifications[17]; +} ProcessData; + +typedef struct ProcessDataList +{ + ProcessData *first, *last; +} ProcessDataList; + +extern ProcessDataList processDataInUseList, freeProcessDataList; + +ProcessData *findProcessData(u32 pid); +ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize); + +Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize); +Result UnregisterProcess(u32 pid); diff --git a/sysmodules/sm/source/services.c b/sysmodules/sm/source/services.c new file mode 100644 index 0000000..25d422d --- /dev/null +++ b/sysmodules/sm/source/services.c @@ -0,0 +1,217 @@ +/* +services.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "services.h" +#include "processes.h" +#include "memory.h" +#include "list.h" + +ServiceInfo servicesInfo[0xA0] = { 0 }; +u32 nbServices = 0; // including "ports" registered with getPort + +static Result checkServiceName(const char *name, s32 nameSize) +{ + if(nameSize <= 0 || nameSize > 8) + return 0xD9006405; + else if(strnlen(name, nameSize) < nameSize) + return 0xD9006407; + else + return 0; +} + +static inline bool areServiceNamesEqual(const char *name, const char *name2, s32 nameSize) +{ + return strncmp(name, name2, nameSize) == 0 && (nameSize == 8 || name[nameSize] == 0); +} + +static s32 findServicePortByName(bool isNamedPort, const char *name, s32 nameSize) +{ + ServiceInfo *info; + for(info = servicesInfo; info < servicesInfo + nbServices && (info->isNamedPort != isNamedPort || !areServiceNamesEqual(info->name, name, nameSize)); info++); + + return info >= servicesInfo + nbServices ? -1 : info - servicesInfo; +} + +static bool checkServiceAccess(SessionData *sessionData, const char *name, s32 nameSize) +{ + if(sessionData->pid < nbSection0Modules) + return true; + + ProcessData *processData = findProcessData(sessionData->pid); + if(processData == NULL) + return false; + + for(u32 i = 0; i < processData->serviceAccessListSize; i++) + { + if(areServiceNamesEqual(processData->serviceAccessList[i], name, nameSize)) + return true; + } + + return false; +} + +static Result doRegisterServiceOrPort(u32 pid, Handle *serverPort, Handle clientPort, const char *name, s32 nameSize, s32 maxSessions, bool isNamedPort) +{ + Result res = checkServiceName(name, nameSize); + Handle portServer, portClient; + + if(R_FAILED(res)) + return res; + else if(findServicePortByName(isNamedPort, name, nameSize) != -1) + return 0xD9001BFC; + + if(nbServices >= 0xA0) + return 0xD86067F3; + + if(!isNamedPort) + { + res = svcCreatePort(&portServer, &portClient, NULL, maxSessions); + if(R_FAILED(res)) + return 0xD9001BFC; + } + else + portClient = clientPort; + + ServiceInfo *serviceInfo = &servicesInfo[nbServices++]; + strncpy(serviceInfo->name, name, 8); + + serviceInfo->pid = pid; + serviceInfo->clientPort = portClient; + serviceInfo->isNamedPort = isNamedPort; + + SessionData *nextSessionData; + s32 n = 0; + + for(SessionData *node = sessionDataWaitingForServiceOrPortRegisterList.first; node != NULL; node = nextSessionData) + { + nextSessionData = node->next; + if((node->replayCmdbuf[0] & 0xF0000) == (!isNamedPort ? 0x50000 : 0x80000) && + areServiceNamesEqual((const char *)(node->replayCmdbuf + 1), name, (s32)node->replayCmdbuf[3])) + { + moveNode(node, &sessionDataToWakeUpAfterServiceOrPortRegisterList, true); + ++n; + } + } + if(n > 0) + { + s32 count; + assertSuccess(svcReleaseSemaphore(&count, resumeGetServiceHandleOrPortRegisteredSemaphore, n)); + } + + if(!isNamedPort) + *serverPort = portServer; + + return res; +} + +Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions) +{ + return doRegisterServiceOrPort(pid, serverPort, 0, name, nameSize, maxSessions, false); +} + +Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions) +{ + return doRegisterService(sessionData->pid, serverPort, name, nameSize, maxSessions); +} + +Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize) +{ + return doRegisterServiceOrPort(sessionData->pid, NULL, clientPort, name, nameSize, -1, true); +} + +static Result UnregisterServiceOrPort(SessionData *sessionData, const char *name, s32 nameSize, bool isNamedPort) +{ + Result res = checkServiceName(name, nameSize); + s32 serviceId; + + if(R_FAILED(res)) + return res; + + serviceId = findServicePortByName(isNamedPort, name, nameSize); + if(serviceId == -1) + return 0xD8801BFA; + else if(servicesInfo[serviceId].pid != sessionData->pid) + return 0xD8E06406; + else + { + svcCloseHandle(servicesInfo[serviceId].clientPort); + + servicesInfo[serviceId] = servicesInfo[--nbServices]; + return 0; + } +} + +Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize) +{ + return UnregisterServiceOrPort(sessionData, name, nameSize, false); +} + +Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize) +{ + return UnregisterServiceOrPort(sessionData, name, nameSize, true); +} + +Result IsServiceRegistered(SessionData *sessionData, bool *isRegistered, const char *name, s32 nameSize) +{ + Result res = checkServiceName(name, nameSize); + + if(R_FAILED(res)) + return res; + else if(!checkServiceAccess(sessionData, name, nameSize)) + return 0xD8E06406; + else + { + *isRegistered = findServicePortByName(false, name, nameSize) != -1; + return 0; + } +} + +Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags) +{ + Result res = checkServiceName(name, nameSize); + s32 serviceId; + + if(R_FAILED(res)) + return res; + else if(!checkServiceAccess(sessionData, name, nameSize)) + return 0xD8E06406; + + serviceId = findServicePortByName(false, name, nameSize); + if(serviceId == -1) + return 0xD0406401; + else + { + Handle port = servicesInfo[serviceId].clientPort; + res = svcCreateSessionToPort(session, port); + if(res == (Result)0xD0401834 && !(flags & 1)) + { + sessionData->busyClientPortHandle = port; + return 0xD0406402; + } + + return 0; + } +} + +Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags) +{ + Result res = checkServiceName(name, nameSize); + s32 serviceId; + + if(R_FAILED(res)) + return res; + else if(!checkServiceAccess(sessionData, name, nameSize)) + return 0xD8E06406; + + serviceId = findServicePortByName(true, name, nameSize); + if(serviceId == -1) + return flags != 0 ? 0xD0406401 : 0xD8801BFA; + else if(flags == 0) + *port = servicesInfo[serviceId].clientPort; + + return 0; +} diff --git a/sysmodules/sm/source/services.h b/sysmodules/sm/source/services.h new file mode 100644 index 0000000..8c79882 --- /dev/null +++ b/sysmodules/sm/source/services.h @@ -0,0 +1,32 @@ +/* +services.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include <3ds.h> + +#include "common.h" + +typedef struct ServiceInfo +{ + char name[8]; + Handle clientPort; + u32 pid; + bool isNamedPort; +} ServiceInfo; + +extern ServiceInfo servicesInfo[0xA0]; +extern u32 nbServices; + +Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions); +Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions); +Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize); +Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize); +Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize); +Result IsServiceRegistered(SessionData *SessionData, bool *isRegistered, const char *name, s32 nameSize); +Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags); +Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags); diff --git a/sysmodules/sm/source/srv.c b/sysmodules/sm/source/srv.c new file mode 100644 index 0000000..1b54b34 --- /dev/null +++ b/sysmodules/sm/source/srv.c @@ -0,0 +1,181 @@ +/* +srv.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "srv.h" + +#include "services.h" +#include "notifications.h" +#include "processes.h" + +Result srvHandleCommands(SessionData *sessionData) +{ + Result res = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + switch(cmdbuf[0] >> 16) + { + case 1: // RegisterClient + { + if(cmdbuf[0] == IPC_MakeHeader(1, 0, 2) && cmdbuf[1] == IPC_Desc_CurProcessHandle()) + { + sessionData->pid = cmdbuf[2]; + cmdbuf[0] = IPC_MakeHeader(1, 1, 0); + cmdbuf[1] = 0; + } + else + goto invalid_command; + break; + } + + case 2: // EnableNotification + { + Handle notificationSemaphore = 0; + res = EnableNotification(sessionData, ¬ificationSemaphore); + cmdbuf[0] = IPC_MakeHeader(2, 1, 2); + cmdbuf[1] = (u32)res; + cmdbuf[2] = IPC_Desc_SharedHandles(1); + cmdbuf[3] = notificationSemaphore; + break; + } + + case 3: // RegisterService + { + Handle serverPort = 0; + res = RegisterService(sessionData, &serverPort, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (s32)cmdbuf[4]); + cmdbuf[0] = IPC_MakeHeader(3, 1, 2); + cmdbuf[1] = (u32)res; + cmdbuf[2] = IPC_Desc_MoveHandles(1); + cmdbuf[3] = serverPort; + break; + } + + case 4: // UnregisterService + { + res = UnregisterService(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]); + cmdbuf[0] = IPC_MakeHeader(4, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + case 5: // GetServiceHandle + { + Handle session = 0; + res = GetServiceHandle(sessionData, &session, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], cmdbuf[4]); + if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK) + memcpy(sessionData->replayCmdbuf, cmdbuf, 16); + else + { + cmdbuf[0] = IPC_MakeHeader(5, 1, 2); + cmdbuf[1] = (u32)res; + cmdbuf[2] = IPC_Desc_MoveHandles(1); + cmdbuf[3] = session; + } + break; + } + + case 6: // RegisterPort + { + if(cmdbuf[0] == IPC_MakeHeader(6, 3, 2) && cmdbuf[4] == IPC_Desc_SharedHandles(1)) + { + res = RegisterPort(sessionData, (Handle)cmdbuf[5], (const char *)(cmdbuf + 1), (s32)cmdbuf[3]); + cmdbuf[0] = IPC_MakeHeader(6, 1, 0); + cmdbuf[1] = (u32)res; + } + else + goto invalid_command; + break; + } + + case 7: // UnregisterPort + { + res = UnregisterPort(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]); + cmdbuf[0] = IPC_MakeHeader(7, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + case 8: // GetPort + { + Handle port = 0; + res = GetPort(sessionData, &port, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (u8)cmdbuf[4]); + if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK) + memcpy(sessionData->replayCmdbuf, cmdbuf, 16); + else + { + cmdbuf[0] = IPC_MakeHeader(8, 1, 2); + cmdbuf[1] = (u32)res; + cmdbuf[2] = IPC_Desc_SharedHandles(1); + cmdbuf[3] = port; + } + break; + } + + case 9: // Subscribe + { + res = Subscribe(sessionData, cmdbuf[1]); + cmdbuf[0] = IPC_MakeHeader(9, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + case 10: // Unsubscribe + { + res = Unsubscribe(sessionData, cmdbuf[1]); + cmdbuf[0] = IPC_MakeHeader(10, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + case 11: // ReceiveNotification + { + u32 notificationId; + res = ReceiveNotification(sessionData, ¬ificationId); + cmdbuf[0] = IPC_MakeHeader(11, 2, 0);; + cmdbuf[1] = (u32)res; + cmdbuf[2] = notificationId; + break; + } + + case 12: // PublishToSubscriber + { + res = PublishToSubscriber(cmdbuf[1], cmdbuf[2]); + cmdbuf[0] = IPC_MakeHeader(12, 1, 0);; + cmdbuf[1] = (u32)res; + break; + } + + case 13: // PublishAndGetSubscriber + { + u32 pidCount; + res = PublishAndGetSubscriber(&pidCount, cmdbuf + 3, cmdbuf[1], 0); + cmdbuf[0] = IPC_MakeHeader(13, 62, 0); + cmdbuf[1] = (u32)res; + cmdbuf[2] = pidCount; + break; + } + + case 14: // IsServiceRegistered + { + bool isRegistered; + res = IsServiceRegistered(sessionData, &isRegistered, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]); + cmdbuf[0] = IPC_MakeHeader(14, 2, 0);; + cmdbuf[1] = (u32)res; + cmdbuf[2] = isRegistered ? 1 : 0; + break; + } + + default: + goto invalid_command; + break; + } + + return res; + +invalid_command: + cmdbuf[0] = IPC_MakeHeader(0, 1, 0);; + cmdbuf[1] = (u32)0xD9001830; + return 0xD9001830; +} diff --git a/sysmodules/sm/source/srv.h b/sysmodules/sm/source/srv.h new file mode 100644 index 0000000..24e23bd --- /dev/null +++ b/sysmodules/sm/source/srv.h @@ -0,0 +1,12 @@ +/* +srv.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +Result srvHandleCommands(SessionData *sessionData); diff --git a/sysmodules/sm/source/srv_pm.c b/sysmodules/sm/source/srv_pm.c new file mode 100644 index 0000000..490986f --- /dev/null +++ b/sysmodules/sm/source/srv_pm.c @@ -0,0 +1,81 @@ +/* +srv_pm.c + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#include "srv_pm.h" +#include "srv.h" +#include "services.h" +#include "notifications.h" +#include "processes.h" + +Result srvPmHandleCommands(SessionData *sessionData) +{ + Result res = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 mask = (cmdbuf[0] & 0x04000000) >> 16; + + if(IS_PRE_7X && mask == 0) + return srvHandleCommands(sessionData); + else if(!IS_PRE_7X && mask != 0) + goto invalid_command; + + switch((cmdbuf[0] >> 16) & ~mask) + { + case 1: // PublishToProcess + { + if(cmdbuf[0] == IPC_MakeHeader(mask | 1, 1, 2) && cmdbuf[2] == IPC_Desc_SharedHandles(1)) + { + res = PublishToProcess((Handle)cmdbuf[3], cmdbuf[1]); + cmdbuf[0] = IPC_MakeHeader(mask | 1, 1, 0); + cmdbuf[1] = (u32)res; + } + else + goto invalid_command; + break; + } + + case 2: // PublishToAll + { + res = PublishToAll(cmdbuf[1]); + cmdbuf[0] = IPC_MakeHeader(mask | 2, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + case 3: // RegisterProcess + { + if(cmdbuf[0] == IPC_MakeHeader(mask | 3, 2, 2) && (cmdbuf[3] & 0x3C0F) == (IPC_Desc_StaticBuffer(0x110, 0) & 0x3C0F)) + { + res = RegisterProcess(cmdbuf[1], (char (*)[8])cmdbuf[4], cmdbuf[3] >> 14); + cmdbuf[0] = IPC_MakeHeader(mask | 3, 1, 0); + cmdbuf[1] = (u32)res; + } + else + goto invalid_command; + break; + } + + case 4: + { + res = UnregisterProcess(cmdbuf[1]); + cmdbuf[0] = IPC_MakeHeader(mask | 4, 1, 0); + cmdbuf[1] = (u32)res; + break; + } + + default: + goto invalid_command; + break; + } + + return res; + +invalid_command: + cmdbuf[0] = IPC_MakeHeader(0, 1, 0);; + cmdbuf[1] = (u32)0xD900182F; + return 0xD900182F; +} + diff --git a/sysmodules/sm/source/srv_pm.h b/sysmodules/sm/source/srv_pm.h new file mode 100644 index 0000000..a9ca8d9 --- /dev/null +++ b/sysmodules/sm/source/srv_pm.h @@ -0,0 +1,12 @@ +/* +srv_pm.h + +(c) TuxSH, 2017 +This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details). +*/ + +#pragma once + +#include "common.h" + +Result srvPmHandleCommands(SessionData *sessionData);