diff --git a/k11_extension/Makefile b/k11_extension/Makefile index b0fbae8..0225678 100644 --- a/k11_extension/Makefile +++ b/k11_extension/Makefile @@ -14,7 +14,7 @@ dir_build := build ARCH := -mcpu=mpcore -mfpu=vfp ASFLAGS := $(ARCH) -CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -I$(dir_include) -fno-builtin -std=c11 -Wno-main -g -flto -O2 -ffast-math \ +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -I$(dir_include) -fno-builtin -std=c11 -Wno-main -flto -O2 -ffast-math \ -mword-relocations -ffunction-sections -fdata-sections LDFLAGS := -nostdlib -Wl,--gc-sections,--nmagic $(ARCH) diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h index 2b2951f..dd11e99 100644 --- a/k11_extension/include/globals.h +++ b/k11_extension/include/globals.h @@ -44,8 +44,12 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable * extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse); extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout); extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token); +extern Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address); extern Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages); extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages); +extern Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm); +extern Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage); +extern Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz); extern Result (*KEvent__Clear)(KEvent *this); extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); extern void (*KObjectMutex__ErrorOccured)(void); @@ -53,8 +57,11 @@ extern void (*KObjectMutex__ErrorOccured)(void); extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask); extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); +extern void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list); + extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); extern void (*SleepThread)(s64 ns); +extern Result (*CreateEvent)(Handle *out, ResetType resetType); extern Result (*CloseHandle)(Handle handle); extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); @@ -65,6 +72,7 @@ extern Result (*SendSyncRequest)(Handle handle); extern Result (*OpenProcess)(Handle *out, u32 processId); extern Result (*GetProcessId)(u32 *out, Handle process); extern Result (*DebugActiveProcess)(Handle *out, u32 processId); +extern Result (*SignalEvent)(Handle event); extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size); extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3); @@ -129,9 +137,12 @@ typedef struct CfwInfo u32 config, multiConfig, bootConfig; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 rosalinaFlags; } CfwInfo; extern CfwInfo cfwInfo; extern vu32 rosalinaState; extern bool hasStartedRosalinaNetworkFuncsOnce; + +KLinkedList* KLinkedList__Initialize(KLinkedList *list); diff --git a/k11_extension/include/kernel.h b/k11_extension/include/kernel.h index e13f9e7..5e19f0a 100644 --- a/k11_extension/include/kernel.h +++ b/k11_extension/include/kernel.h @@ -105,6 +105,14 @@ typedef struct ALIGN(4) KMutex union KProcess *owner; } KMutex; +typedef struct KAddressArbiter +{ + KAutoObject autoObject; + struct KThread *first; + struct KThread *last; + union KProcess *owner; +} KAddressArbiter; + /* 92 */ typedef struct KMutexLinkedList { @@ -112,6 +120,30 @@ typedef struct KMutexLinkedList KMutex *last; } KMutexLinkedList; +enum +{ + TOKEN_KAUTOOBJECT = 0, + TOKEN_KSYNCHRONIZATIONOBJECT = 1, + TOKEN_KEVENT = 0x1F, + TOKEN_KSEMAPHORE = 0x2F, + TOKEN_KTIMER = 0x35, + TOKEN_KMUTEX = 0x39, + TOKEN_KDEBUG = 0x4D, + TOKEN_KSERVERPORT = 0x55, + TOKEN_KDMAOBJECT = 0x59, + TOKEN_KCLIENTPORT = 0x65, + TOKEN_KCODESET = 0x68, + TOKEN_KSESSION = 0x70, + TOKEN_KTHREAD = 0x8D, + TOKEN_KSERVERSESSION = 0x95, + TOKEN_KADDRESSARBITER = 0x98, + TOKEN_KCLIENTSESSION = 0xA5, + TOKEN_KPORT = 0xA8, + TOKEN_KSHAREDMEMORY = 0xB0, + TOKEN_KPROCESS = 0xC5, + TOKEN_KRESOURCELIMIT = 0xC8 +}; + /* 45 */ typedef struct KClassToken { @@ -540,6 +572,20 @@ typedef struct KBlockInfo u32 pageCount; } KBlockInfo; +typedef struct KSharedMemory +{ + KAutoObject autoObject; + KLinkedList ownedKBlockInfo; + union KProcess *owner; + u32 ownerPermissions; + u32 otherPermissions; + u8 isBlockInfoGenerated; + s8 allBlockInfoGenerated; + u8 unknown_1; + u8 unknown_2; + u32 address; +} KSharedMemory; + /* 25 */ typedef struct KMemoryBlock { @@ -1037,10 +1083,26 @@ typedef struct KProcess##sys\ KThread *mainThread;\ u32 interruptEnabledFlags[4];\ KProcessHandleTable handleTable;\ - u8 gap234[52];\ + /* Custom fields for plugin system + { */ \ + u32 customFlags; /* see KProcess_CustomFlags enum below */ \ + Handle onMemoryLayoutChangeEvent;\ + Handle onProcessExitEvent;\ + Handle resumeProcessExitEvent;\ + /* } */ \ + u8 gap234[36];\ u64 unused;\ } KProcess##sys; +enum KProcess_CustomFlags +{ + ForceRWXPages = 1 << 0, + SignalOnMemLayoutChanges = 1 << 1, + SignalOnExit = 1 << 2, + + MemLayoutChanged = 1 << 16 +}; + INSTANCIATE_KPROCESS(N3DS); INSTANCIATE_KPROCESS(O3DS8x); INSTANCIATE_KPROCESS(O3DSPre8x); diff --git a/k11_extension/include/mmu.h b/k11_extension/include/mmu.h new file mode 100644 index 0000000..fce63c6 --- /dev/null +++ b/k11_extension/include/mmu.h @@ -0,0 +1,129 @@ +#pragma once + +#include "types.h" +#include "kernel.h" + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b00 +} Desc_TranslationFault; + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b01 + u32 sbz : 3; + u32 domain : 4; + u32 p : 1; + u32 addr : 21; +} Desc_CoarsePageTable; + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b10 + u32 b : 1; + u32 c : 1; + u32 xn : 1; + u32 domain : 4; + u32 p : 1; + u32 ap : 2; + u32 tex : 3; + u32 apx : 1; + u32 s : 1; + u32 ng : 1; + u32 bit18 : 1; ///< 0 + u32 sbz : 1; + u32 addr : 12; +} Desc_Section; + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b10 + u32 b : 1; + u32 c : 1; + u32 xn : 1; + u32 domain : 4; + u32 p : 1; + u32 ap : 2; + u32 tex : 3; + u32 sbz : 3; + u32 bit18 : 1; ///< 1 + u32 sbz2 : 5; + u32 addr : 8; +} Desc_Supersection; + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b11 +} Desc_Reserved; + +typedef struct +{ + u32 bits1_0 : 2; ///< 0b01 + u32 b : 1; + u32 c : 1; + u32 ap : 2; + u32 sbz : 3; + u32 apx : 1; + u32 s : 1; + u32 ng : 1; + u32 tex : 3; + u32 xn : 1; + u32 addr : 16; +} Desc_LargePage; + +typedef struct +{ + u32 xn : 1; + u32 bit1 : 1; ///< 1 + u32 b : 1; + u32 c : 1; + u32 ap : 2; + u32 tex : 3; + u32 apx : 1; + u32 s : 1; + u32 ng : 1; + u32 addr : 20; +} Desc_SmallPage; + +typedef union +{ + u32 raw; + + Desc_TranslationFault translationFault; + Desc_CoarsePageTable coarsePageTable; + Desc_Section section; + Desc_Supersection supersection; + Desc_Reserved reserved; + +} L1Descriptor; + +typedef union +{ + u32 raw; + + Desc_TranslationFault translationFault; + Desc_LargePage largePage; + Desc_SmallPage smallPage; +} L2Descriptor; + +typedef enum +{ + Descriptor_TranslationFault, + Descriptor_CoarsePageTable, + Descriptor_Section, + Descriptor_Supersection, + Descriptor_Reserved, + Descriptor_LargePage, + Descriptor_SmallPage +} DescType; + +void L1MMUTable__RWXForAll(u32 *table); +void L2MMUTable__RWXForAll(u32 *table); +u32 L1MMUTable__GetPAFromVA(u32 *table, u32 va); +u32 L2MMUTable__GetPAFromVA(u32 *table, u32 va); +u32 L1MMUTable__GetAddressUserPerm(u32 *table, u32 va); +u32 L2MMUTable__GetAddressUserPerm(u32 *table, u32 va); + +void KProcessHwInfo__SetMMUTableToRWX(KProcessHwInfo *hwInfo); +u32 KProcessHwInfo__GetPAFromVA(KProcessHwInfo *hwInfo, u32 va); +u32 KProcessHwInfo__GetAddressUserPerm(KProcessHwInfo *hwInfo, u32 va); diff --git a/k11_extension/include/svc/ControlProcess.h b/k11_extension/include/svc/ControlProcess.h new file mode 100644 index 0000000..c0a1414 --- /dev/null +++ b/k11_extension/include/svc/ControlProcess.h @@ -0,0 +1,21 @@ +#pragma once + +#include "utils.h" +#include "kernel.h" +#include "svc.h" + +/// Operations for svcControlProcess +typedef enum ProcessOp +{ + PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch + ///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0) + PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access + ///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0) + PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, + PROCESSOP_GET_ON_EXIT_EVENT, + PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process + ///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va) + PROCESSOP_SCHEDULE_THREADS, +} ProcessOp; + +Result ControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3); diff --git a/k11_extension/include/svc/MapProcessMemoryEx.h b/k11_extension/include/svc/MapProcessMemoryEx.h index e1017aa..3c6fcc2 100644 --- a/k11_extension/include/svc/MapProcessMemoryEx.h +++ b/k11_extension/include/svc/MapProcessMemoryEx.h @@ -30,4 +30,5 @@ #include "kernel.h" #include "svc.h" -Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size); +Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size); +Result MapProcessMemoryExWrapper(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size); diff --git a/k11_extension/source/fatalExceptionHandlersMain.c b/k11_extension/source/fatalExceptionHandlersMain.c index b37f9fd..e16fdc8 100644 --- a/k11_extension/source/fatalExceptionHandlersMain.c +++ b/k11_extension/source/fatalExceptionHandlersMain.c @@ -28,11 +28,43 @@ #include "utils.h" #include "kernel.h" #include "memory.h" +#include "mmu.h" #include "globals.h" #define REG_DUMP_SIZE 4 * 23 #define CODE_DUMP_SIZE 96 +// Return true if parameters are invalid +static bool checkExceptionHandlerValidity(KProcess *process, vu32 *threadLocalStorage) +{ + if (process == NULL) + return true; + + u32 stackBottom = threadLocalStorage[0x11]; + u32 exceptionBuf = threadLocalStorage[0x12]; + MemoryInfo memInfo; + PageInfo pageInfo; + KProcessHwInfo *hwInfo = hwInfoOfProcess(process); + + u32 perm = KProcessHwInfo__GetAddressUserPerm(hwInfo, threadLocalStorage[0x10]); + + if (stackBottom != 1) + { + if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)stackBottom) + || (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW) + return true; + } + + if (exceptionBuf > 1) + { + if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)exceptionBuf) + || (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW) + return true; + } + + return (perm & MEMPERM_RX) != MEMPERM_RX; +} + bool isExceptionFatal(u32 spsr, u32 *regs, u32 index) { if(CONFIG(DISABLEARM11EXCHANDLERS)) return false; @@ -43,7 +75,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index) KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0) - return false; + return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage); if(currentProcess != NULL) { @@ -52,7 +84,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index) thread = KPROCESS_GET_RVALUE(currentProcess, mainThread); if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0) - return false; + return checkExceptionHandlerValidity(currentProcess, thread->threadLocalStorage); if(index == 3 && strcmp(codeSetOfProcess(currentProcess)->processName, "menu") == 0 && // workaround a Home Menu bug leading to a dabort regs[0] == 0x3FFF && regs[2] == 0 && regs[5] == 2 && regs[7] == 1) @@ -70,6 +102,7 @@ bool isDataAbortExceptionRangeControlled(u32 spsr, u32 addr) ((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz) ); } + void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId) { ExceptionDumpHeader dumpHeader; diff --git a/k11_extension/source/globals.c b/k11_extension/source/globals.c index 62042e2..c05c280 100644 --- a/k11_extension/source/globals.c +++ b/k11_extension/source/globals.c @@ -40,8 +40,12 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse); Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout); Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token); +Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address); Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages); Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages); +Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm); +Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage); +Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz); Result (*KEvent__Clear)(KEvent *this); void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); void (*KObjectMutex__ErrorOccured)(void); @@ -49,8 +53,11 @@ void (*KObjectMutex__ErrorOccured)(void); void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask); void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this); +void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list); + Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); void (*SleepThread)(s64 ns); +Result (*CreateEvent)(Handle *out, ResetType resetType); Result (*CloseHandle)(Handle handle); Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type); Result (*GetSystemInfo)(s64 *out, s32 type, s32 param); @@ -61,6 +68,7 @@ Result (*SendSyncRequest)(Handle handle); Result (*OpenProcess)(Handle *out, u32 processId); Result (*GetProcessId)(u32 *out, Handle process); Result (*DebugActiveProcess)(Handle *out, u32 processId); +Result (*SignalEvent)(Handle event); Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size); Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3); @@ -112,3 +120,10 @@ CfwInfo cfwInfo; vu32 rosalinaState; bool hasStartedRosalinaNetworkFuncsOnce; + +KLinkedList* KLinkedList__Initialize(KLinkedList *list) +{ + list->size = 0; + list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes; + return list; +} diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c index 013d1fc..d064637 100644 --- a/k11_extension/source/main.c +++ b/k11_extension/source/main.c @@ -96,11 +96,61 @@ void configHook(vu8 *cfgPage) *isDevUnit = true; // enable debug features } +void KProcessHwInfo__MapL1Section_Hook(void); +void KProcessHwInfo__MapL2Section_Hook(void); + +static void installMmuHooks(void) +{ + u32 *mapL1Section = NULL; + u32 *mapL2Section = NULL; + u32 *off; + + for(off = (u32 *)officialSVCs[0x1F]; *off != 0xE1CD60F0; ++off); + off = decodeARMBranch(off + 1); + + for (; *off != 0xE58D5000; ++off); + off = decodeARMBranch(off + 1); + + for (; *off != 0xE58DC000; ++off); + off = decodeARMBranch(off + 1); + for (; *off != 0xE1A0000B; ++off); + off = decodeARMBranch(off + 1); + for (; *off != 0xE59D2030; ++off); + off = decodeARMBranch(off + 1); + + for (; *off != 0xE88D1100; ++off); + mapL2Section = (u32 *)PA_FROM_VA_PTR(decodeARMBranch(off + 1)); + + do + { + for (; *off != 0xE58D8000; ++off); + u32 *loc = (u32 *)PA_FROM_VA_PTR(decodeARMBranch(++off)); + if (loc != mapL2Section) + mapL1Section = loc; + } while (mapL1Section == NULL); + + mapL1Section[1] = 0xE28FE004; // add lr, pc, #4 + mapL1Section[2] = 0xE51FF004; // ldr pc, [pc, #-4] + mapL1Section[3] = (u32)KProcessHwInfo__MapL1Section_Hook; + + mapL2Section[1] = 0xE28FE004; // add lr, pc, #4 + mapL2Section[2] = 0xE51FF004; // ldr pc, [pc, #-4] + mapL2Section[3] = (u32)KProcessHwInfo__MapL2Section_Hook; +} + static void findUsefulSymbols(void) { u32 *off; - for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++); + // Patch ERRF__DumpException + for(off = (u32 *)0xFFFF0000; *off != 0xE1A04005; ++off); + ++off; + *(u32 *)PA_FROM_VA_PTR(off) = makeARMBranch(off, off + 51, false); + + for(; *off != 0xE2100102; ++off); + KProcessHwInfo__QueryMemory = (Result (*)(KProcessHwInfo *, MemoryInfo *, PageInfo *, void *))decodeARMBranch(off - 1); + + for(; *off != 0xE1A0D002; off++); off += 3; initFPU = (void (*) (void))off; @@ -163,6 +213,18 @@ static void findUsefulSymbols(void) for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++); KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeARMBranch(off - 1); + for (off = (u32 *)officialSVCs[0x70]; *off != 0xE8881200 && *off != 0xE8891900; ++off); + for (off = (u32 *)decodeARMBranch(off + 1); *off != 0xE2101102; ++off); + KProcessHwInfo__CheckVaState = (Result (*)(KProcessHwInfo *, u32, u32, u32, u32))decodeARMBranch(off - 1); + for (; *off != 0xE28D1008; ++off); + KProcessHwInfo__GetListOfKBlockInfoForVA = (Result (*)(KProcessHwInfo*, KLinkedList*, u32, u32))decodeARMBranch(off + 1); + + for (; *off != 0xE2000102; ++off); + KProcessHwInfo__MapListOfKBlockInfo = (Result (*)(KProcessHwInfo*, u32, KLinkedList*, u32, u32, u32))decodeARMBranch(off - 1); + + for (; *off != 0xE8BD8FF0; ++off); + KLinkedList_KBlockInfo__Clear = (void (*)(KLinkedList *))decodeARMBranch(off - 6); + for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++); KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeARMBranch(++off); for(; *off != 0xE320F000; off++); @@ -208,6 +270,7 @@ static void findUsefulSymbols(void) ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool)) decodeARMBranch((u32 *)officialSVCs[0x01] + 5); SleepThread = (void (*)(s64))officialSVCs[0x0A]; + CreateEvent = (Result (*)(Handle *, ResetType))decodeARMBranch((u32 *)officialSVCs[0x17] + 3); CloseHandle = (Result (*)(Handle))officialSVCs[0x23]; GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x29] + 3); GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeARMBranch((u32 *)officialSVCs[0x2A] + 3); @@ -218,6 +281,7 @@ static void findUsefulSymbols(void) OpenProcess = (Result (*)(Handle *, u32))decodeARMBranch((u32 *)officialSVCs[0x33] + 3); GetProcessId = (Result (*)(u32 *, Handle))decodeARMBranch((u32 *)officialSVCs[0x35] + 3); DebugActiveProcess = (Result (*)(Handle *, u32))decodeARMBranch((u32 *)officialSVCs[0x60] + 3); + SignalEvent = (Result (*)(Handle event))officialSVCs[0x18]; UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72]; KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1); @@ -249,6 +313,8 @@ static void findUsefulSymbols(void) invalidateInstructionCacheRange = (void (*)(void *, u32))off2; } } + + installMmuHooks(); } void main(FcramLayout *layout, KCoreContext *ctxs) diff --git a/k11_extension/source/mmu.c b/k11_extension/source/mmu.c new file mode 100644 index 0000000..5538904 --- /dev/null +++ b/k11_extension/source/mmu.c @@ -0,0 +1,315 @@ +#include "mmu.h" +#include "globals.h" +#include "utils.h" + +DescType L1Descriptor__GetType(u32 descriptor) +{ + L1Descriptor pdesc = {descriptor}; + + if (pdesc.reserved.bits1_0 == 0b00) + return Descriptor_TranslationFault; + if (pdesc.reserved.bits1_0 == 0b01) + return Descriptor_CoarsePageTable; + if (pdesc.reserved.bits1_0 == 0b10) + return pdesc.section.bit18 == 0 ? Descriptor_Section : Descriptor_Supersection; + return Descriptor_Reserved; +} + +DescType L2Descriptor__GetType(u32 descriptor) +{ + L2Descriptor pdesc = {descriptor}; + + if (pdesc.translationFault.bits1_0 == 0b01) + return Descriptor_LargePage; + if (pdesc.smallPage.bit1 == 1) + return Descriptor_SmallPage; + + return Descriptor_TranslationFault; +} + +void L1MMUTable__RWXForAll(u32 *table) +{ + u32 *tableEnd = table + 1024; + + for (; table != tableEnd; ++table) + { + L1Descriptor descriptor = {*table}; + + switch (L1Descriptor__GetType(descriptor.raw)) + { + case Descriptor_CoarsePageTable: + { + u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000); + + L2MMUTable__RWXForAll(l2table); + break; + } + case Descriptor_Section: + { + descriptor.section.xn = 0; + descriptor.section.apx = 0; + descriptor.section.ap = 3; + *table = descriptor.raw; + break; + } + case Descriptor_Supersection: + { + descriptor.supersection.xn = 0; + descriptor.supersection.ap = 3; + *table = descriptor.raw; + break; + } + default: + break; + } + } +} + +void L2MMUTable__RWXForAll(u32 *table) +{ + u32 *tableEnd = table + 256; + + for (; table != tableEnd; ++table) + { + L2Descriptor descriptor = {*table}; + + switch (L2Descriptor__GetType(descriptor.raw)) + { + case Descriptor_LargePage: + { + descriptor.largePage.xn = 0; + descriptor.largePage.apx = 0; + descriptor.largePage.ap = 3; + *table = descriptor.raw; + break; + } + case Descriptor_SmallPage: + { + descriptor.smallPage.xn = 0; + descriptor.smallPage.apx = 0; + descriptor.smallPage.ap = 3; + *table = descriptor.raw; + break; + } + default: + break; + } + } +} + +u32 L1MMUTable__GetPAFromVA(u32 *table, u32 va) +{ + u32 pa = 0; + L1Descriptor descriptor = {table[va >> 20]}; + + switch (L1Descriptor__GetType(descriptor.raw)) + { + case Descriptor_CoarsePageTable: + { + u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000); + + pa = L2MMUTable__GetPAFromVA(l2table, va); + break; + } + case Descriptor_Section: + { + pa = descriptor.section.addr << 20; + pa |= (va << 12) >> 12; + break; + } + case Descriptor_Supersection: + { + pa = descriptor.supersection.addr << 24; + pa |= (va << 8) >> 8; + break; + } + default: + // VA not found + break; + } + + return pa; +} + +u32 L2MMUTable__GetPAFromVA(u32 *table, u32 va) +{ + u32 pa = 0; + L2Descriptor descriptor = {table[(va << 12) >> 24]}; + + switch(L2Descriptor__GetType(descriptor.raw)) + { + case Descriptor_LargePage: + { + pa = descriptor.largePage.addr << 16; + pa |= va & 0xFFFF; + break; + } + case Descriptor_SmallPage: + { + pa = descriptor.smallPage.addr << 12; + pa |= va & 0xFFF; + break; + } + default: + break; + } + + return pa; +} + +u32 L1MMUTable__GetAddressUserPerm(u32 *table, u32 va) +{ + u32 perm = 0; + L1Descriptor descriptor = {table[va >> 20]}; + + switch (L1Descriptor__GetType(descriptor.raw)) + { + case Descriptor_CoarsePageTable: + { + u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000); + + perm = L2MMUTable__GetAddressUserPerm(l2table, va); + break; + } + case Descriptor_Section: + { + perm = descriptor.section.ap >> 1; + + if (perm) + { + perm |= (!descriptor.section.apx && (descriptor.section.ap & 1)) << 1; + perm |= (!descriptor.section.xn) << 2; + } + break; + } + case Descriptor_Supersection: + { + perm = descriptor.supersection.ap >> 1; + + if (perm) + { + perm |= (descriptor.supersection.ap & 1) << 1; + perm |= (!descriptor.supersection.xn) << 2; + } + break; + } + default: + // VA not found + break; + } + + return perm; +} + +u32 L2MMUTable__GetAddressUserPerm(u32 *table, u32 va) +{ + u32 perm = 0; + L2Descriptor descriptor = {table[(va << 12) >> 24]}; + + switch(L2Descriptor__GetType(descriptor.raw)) + { + case Descriptor_LargePage: + { + perm = descriptor.largePage.ap >> 1; + if (perm) + { + perm |= (!descriptor.largePage.apx && (descriptor.largePage.ap & 1)) << 1; + perm |= (!descriptor.largePage.xn) << 2; + } + break; + } + case Descriptor_SmallPage: + { + perm = descriptor.smallPage.ap >> 1; + if (perm) + { + perm |= (!descriptor.smallPage.apx && (descriptor.smallPage.ap & 1)) << 1; + perm |= (!descriptor.smallPage.xn) << 2; + } + break; + } + default: + break; + } + + return perm; +} + +void KProcessHwInfo__SetMMUTableToRWX(KProcessHwInfo *hwInfo) +{ + KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex); + u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA); + + KObjectMutex__Acquire(mutex); + + L1MMUTable__RWXForAll(table); + + KObjectMutex__Release(mutex); +} + +u32 KProcessHwInfo__GetPAFromVA(KProcessHwInfo *hwInfo, u32 va) +{ + KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex); + u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA); + + KObjectMutex__Acquire(mutex); + + u32 pa = L1MMUTable__GetPAFromVA(table, va); + + KObjectMutex__Release(mutex); + + return pa; +} + +u32 KProcessHwInfo__GetAddressUserPerm(KProcessHwInfo *hwInfo, u32 va) +{ + KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex); + u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA); + + KObjectMutex__Acquire(mutex); + + u32 perm = L1MMUTable__GetAddressUserPerm(table, va); + + KObjectMutex__Release(mutex); + + return perm; +} + +static union +{ + u32 raw; + struct + { + u32 xn : 1; + u32 unkn : 1; + u32 cb : 2; + u32 ap : 2; + u32 tex : 3; + u32 apx : 1; + u32 s : 1; + u32 ng : 1; + }; +} g_rwxState; + +// This function patch the permission when memory is mapped in the mmu table (rwx) +KProcessHwInfo *PatchDescriptorAccessControl(KProcessHwInfo *hwInfo, u32 **outState) +{ + KProcess *process = (KProcess *)((u32)hwInfo - 0x1C); + u32 state = **outState; + u32 flags = KPROCESS_GET_RVALUE(process, customFlags); + + if (flags & SignalOnMemLayoutChanges) + *KPROCESS_GET_PTR(process, customFlags) |= MemLayoutChanged; + + if (!(flags & ForceRWXPages)) + return hwInfo; + + g_rwxState.raw = state; + g_rwxState.xn = 0; + g_rwxState.ap = 3; + g_rwxState.apx = 0; + + *outState = &g_rwxState.raw; + + return hwInfo; +} diff --git a/k11_extension/source/svc.c b/k11_extension/source/svc.c index bea25ae..e595a28 100644 --- a/k11_extension/source/svc.c +++ b/k11_extension/source/svc.c @@ -44,6 +44,7 @@ #include "svc/MapProcessMemoryEx.h" #include "svc/UnmapProcessMemoryEx.h" #include "svc/ControlService.h" +#include "svc/ControlProcess.h" #include "svc/CopyHandle.h" #include "svc/TranslateHandle.h" @@ -59,13 +60,16 @@ void signalSvcEntry(u8 *pageEnd) // Since DBGEVENT_SYSCALL_ENTRY is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!) if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId)) + { SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId); + } } void signalSvcReturn(u8 *pageEnd) { u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5); KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; + u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags); if(svcId == 0xFE) svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x1FFFFFFF. We don't support catching svcIds >= 0x100 atm either @@ -73,6 +77,13 @@ void signalSvcReturn(u8 *pageEnd) // Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!) if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId)) SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId); + + // Signal if the memory layout of the process changed + if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged) + { + *KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged; + SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent)); + } } void postprocessSvc(void) @@ -93,10 +104,28 @@ void *svcHook(u8 *pageEnd) u32 svcId = *(u8 *)(pageEnd - 0xB5); if(svcId == 0xFE) svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x3FFFFFFF. We don't support catching svcIds >= 0x100 atm either + switch(svcId) { case 0x01: return ControlMemoryHookWrapper; + case 0x03: /* svcExitProcess */ + { + // Signal that the process is about to be terminated + u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags); + + if (flags & SignalOnExit) + { + SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onProcessExitEvent)); + + KEvent* event = (KEvent *)KProcessHandleTable__ToKAutoObject(handleTableOfProcess(currentProcess), + KPROCESS_GET_RVALUE(currentProcess, resumeProcessExitEvent)); + WaitSynchronization1(NULL, currentCoreContext->objectContext.currentThread, (KSynchronizationObject *)event, 10000000000ULL); + ((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); + } + return officialSVCs[0x3]; + } + case 0x17: if(strcmp(codeSetOfProcess(currentProcess)->processName, "pm") == 0) // only called twice in pm, by the same function { @@ -152,7 +181,7 @@ void *svcHook(u8 *pageEnd) return invalidateEntireInstructionCache; case 0xA0: - return MapProcessMemoryEx; + return MapProcessMemoryExWrapper; case 0xA1: return UnmapProcessMemoryEx; case 0xA2: @@ -164,6 +193,8 @@ void *svcHook(u8 *pageEnd) return CopyHandleWrapper; case 0xB2: return TranslateHandleWrapper; + case 0xB3: + return ControlProcess; default: return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL; diff --git a/k11_extension/source/svc/ControlProcess.c b/k11_extension/source/svc/ControlProcess.c new file mode 100644 index 0000000..d77d94a --- /dev/null +++ b/k11_extension/source/svc/ControlProcess.c @@ -0,0 +1,215 @@ +#include "svc/ControlProcess.h" +#include "memory.h" +#include "mmu.h" +#include "synchronization.h" + +typedef bool (*ThreadPredicate)(KThread *thread); + +void rosalinaLockThread(KThread *thread); +void rosalinaRescheduleThread(KThread *thread, bool lock); + +Result ControlProcess(Handle processHandle, ProcessOp op, u32 varg2, u32 varg3) +{ + Result res = 0; + KProcess *process; + KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); + + if(processHandle == CUR_PROCESS_HANDLE) + { + process = currentCoreContext->objectContext.currentProcess; + KAutoObject__AddReference((KAutoObject *)process); + } + else + process = KProcessHandleTable__ToKProcess(handleTable, processHandle); + + if(process == NULL) + return 0xD8E007F7; // invalid handle + + switch (op) + { + case PROCESSOP_GET_ALL_HANDLES: + { + KProcessHandleTable *table = handleTableOfProcess(process); + u32 *originalHandleList = (u32 *)varg2; + u32 count = 0; + u32 searchForToken = varg3; + HandleDescriptor *handleDesc = table->handleTable == NULL ? table->internalTable : table->handleTable; + + for (u32 idx = 0; idx < (u32)table->maxHandleCount; ++idx, ++handleDesc) + { + if (handleDesc->pointer == NULL) + continue; + + if (searchForToken) + { + KClassToken token; + + handleDesc->pointer->vtable->GetClassToken(&token, handleDesc->pointer); + if (searchForToken != token.flags) + continue; + } + + *originalHandleList++ = idx | ((handleDesc->info << 16) >> 1); + ++count; + } + res = count; + break; + } + + case PROCESSOP_SET_MMU_TO_RWX: + { + KProcessHwInfo *hwInfo = hwInfoOfProcess(process); + + *KPROCESS_GET_PTR(process, customFlags) |= ForceRWXPages; + KProcessHwInfo__SetMMUTableToRWX(hwInfo); + break; + } + case PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT: + { + // Only accept current process for this command + if (process != currentCoreContext->objectContext.currentProcess) + { + res = 0xD8E007F7; // invalid handle + break; + } + + Handle *onMemoryLayoutChangeEvent = KPROCESS_GET_PTR(process, onMemoryLayoutChangeEvent); + + if (*onMemoryLayoutChangeEvent == 0) + res = CreateEvent(onMemoryLayoutChangeEvent, RESET_ONESHOT); + + if (res >= 0) + { + *KPROCESS_GET_PTR(process, customFlags) |= SignalOnMemLayoutChanges; + KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onMemoryLayoutChangeEvent); + + createHandleForThisProcess((Handle *)varg2, event); + ((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< This avoid an extra operation on process exit + ///< Closing the handle in the handle table will destroy the event + } + + break; + } + + case PROCESSOP_GET_ON_EXIT_EVENT: + { + // Only accept current process for this command + if (process != currentCoreContext->objectContext.currentProcess) + { + res = 0xD8E007F7; // invalid handle + break; + } + + Handle *onProcessExitEvent = KPROCESS_GET_PTR(process, onProcessExitEvent); + Handle *resumeProcessExitEvent = KPROCESS_GET_PTR(process, resumeProcessExitEvent); + + if (*onProcessExitEvent == 0) + res = CreateEvent(onProcessExitEvent, RESET_ONESHOT); + if (*resumeProcessExitEvent == 0) + res |= CreateEvent(resumeProcessExitEvent, RESET_ONESHOT); + + if (res >= 0) + { + *KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit; + KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onProcessExitEvent); + + createHandleForThisProcess((Handle *)varg2, event); + ((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher + + event = KProcessHandleTable__ToKAutoObject(handleTable, *resumeProcessExitEvent); + + createHandleForThisProcess((Handle *)varg3, event); + ((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher + } + + break; + } + case PROCESSOP_GET_PA_FROM_VA: + { + KProcessHwInfo *hwInfo = hwInfoOfProcess(process); + + u32 pa = KProcessHwInfo__GetPAFromVA(hwInfo, varg3); + *(u32 *)varg2 = pa; + + if (pa == 0) + res = 0xE0E01BF5; ///< Invalid address + + break; + } + case PROCESSOP_SCHEDULE_THREADS: + { + ThreadPredicate threadPredicate = (ThreadPredicate)varg3; + + KRecursiveLock__Lock(criticalSectionLock); + + if (varg2 == 0) // Unlock + { + for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next) + { + KThread *thread = (KThread *)node->key; + + if((thread->schedulingMask & 0xF) == 2) // thread is terminating + continue; + + if(thread->schedulingMask & 0x40) + rosalinaRescheduleThread(thread, false); + } + } + else // Lock + { + bool currentThreadsFound = false; + + for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next) + { + KThread *thread = (KThread *)node->key; + + if(thread->ownerProcess != process + || (threadPredicate != NULL && !threadPredicate(thread))) + continue; + + if(thread == coreCtxs[thread->coreId].objectContext.currentThread) + currentThreadsFound = true; + else + rosalinaLockThread(thread); + } + + if(currentThreadsFound) + { + for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next) + { + KThread *thread = (KThread *)node->key; + + if(thread->ownerProcess != process + || (threadPredicate != NULL && !threadPredicate(thread))) + continue; + + if(!(thread->schedulingMask & 0x40)) + { + rosalinaLockThread(thread); + KRecursiveLock__Lock(criticalSectionLock); + if(thread->coreId != getCurrentCoreID()) + { + u32 cpsr = __get_cpsr(); + __disable_irq(); + coreCtxs[thread->coreId].objectContext.currentScheduler->triggerCrossCoreInterrupt = true; + currentCoreContext->objectContext.currentScheduler->triggerCrossCoreInterrupt = true; + __set_cpsr_cx(cpsr); + } + KRecursiveLock__Unlock(criticalSectionLock); + } + } + KScheduler__TriggerCrossCoreInterrupt(currentCoreContext->objectContext.currentScheduler); + } + } + + KRecursiveLock__Unlock(criticalSectionLock); + break; + } + default: + res = 0xF8C007F4; + } + + ((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process); + + return res; +} diff --git a/k11_extension/source/svc/GetHandleInfo.c b/k11_extension/source/svc/GetHandleInfo.c index 56b7d7d..d829d87 100644 --- a/k11_extension/source/svc/GetHandleInfo.c +++ b/k11_extension/source/svc/GetHandleInfo.c @@ -29,11 +29,14 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type) { - if(type == 0x10000) // KDebug and KProcess: get context ID + Result res = 0; + + if(type >= 0x10000) { - KProcessHwInfo *hwInfo; + KProcessHwInfo *hwInfo; KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); - KAutoObject *obj; + KAutoObject *obj; + if(handle == CUR_PROCESS_HANDLE) { obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess); @@ -45,18 +48,82 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type) if(obj == NULL) return 0xD8E007F7; - if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0) - hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner); - else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0) - hwInfo = hwInfoOfProcess((KProcess *)obj); - else - hwInfo = NULL; + switch (type) + { + case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo) + { + if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0) + hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner); + else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0) + hwInfo = hwInfoOfProcess((KProcess *)obj); + else + hwInfo = NULL; - *out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1; + *out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1; + break; + } + case 0x10001: ///< Get referenced object flags (token) + { + KClassToken token; + + obj->vtable->GetClassToken(&token, obj); + *out = token.flags; + break; + } + case 0x10002: ///< Get object owner + { + Handle hOut; + KClassToken token; + KProcess * owner = NULL; + + obj->vtable->GetClassToken(&token, obj); + switch(token.flags) + { + case TOKEN_KEVENT: + owner = ((KEvent *)obj)->owner; + break; + case TOKEN_KSEMAPHORE: + owner = ((KSemaphore *)obj)->owner; + break; + case TOKEN_KTIMER: + owner = ((KTimer *)obj)->owner; + break; + case TOKEN_KMUTEX: + owner = ((KMutex *)obj)->owner; + break; + case TOKEN_KDEBUG: + owner = ((KDebug *)obj)->owner; + break; + case TOKEN_KTHREAD: + owner = ((KThread *)obj)->ownerProcess; + break; + case TOKEN_KADDRESSARBITER: + owner = ((KAddressArbiter *)obj)->owner; + break; + case TOKEN_KSHAREDMEMORY: + owner = ((KSharedMemory *)obj)->owner; + break; + default: + break; + } + + if (owner == NULL) + res = 0xD8E007F7; + + res = createHandleForThisProcess(&hOut, (KAutoObject *)owner); + *out = hOut; + + break; + } + + default: + res = 0xF8C007F4; + break; + } obj->vtable->DecrementReferenceCount(obj); - return 0; + return res; } - else - return GetHandleInfo(out, handle, type); + + return GetHandleInfo(out, handle, type); } diff --git a/k11_extension/source/svc/GetProcessInfo.c b/k11_extension/source/svc/GetProcessInfo.c index adf5647..0f2641e 100644 --- a/k11_extension/source/svc/GetProcessInfo.c +++ b/k11_extension/source/svc/GetProcessInfo.c @@ -79,6 +79,14 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type) *out = ttb & ~((1 << (14 - TTBCR)) - 1); break; } + case 0x10009: + { + KProcessHwInfo *hwInfo = hwInfoOfProcess(process); + u32 mmusize = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableSize); + u32 mmupa = (u32)PA_FROM_VA_PTR(KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA)); + *out = (s64)(mmusize | ((s64)mmupa << 32)); + break; + } default: res = 0xD8E007ED; // invalid enum value break; diff --git a/k11_extension/source/svc/GetSystemInfo.c b/k11_extension/source/svc/GetSystemInfo.c index 9cb8b51..cd86bcb 100644 --- a/k11_extension/source/svc/GetSystemInfo.c +++ b/k11_extension/source/svc/GetSystemInfo.c @@ -64,6 +64,9 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param) case 0x101: *out = cfwInfo.rosalinaMenuCombo; break; + case 0x102: + *out = cfwInfo.rosalinaFlags; + break; case 0x200: // isRelease *out = cfwInfo.flags & 1; diff --git a/k11_extension/source/svc/KernelSetState.c b/k11_extension/source/svc/KernelSetState.c index 28362e6..e6a1781 100644 --- a/k11_extension/source/svc/KernelSetState.c +++ b/k11_extension/source/svc/KernelSetState.c @@ -35,6 +35,7 @@ static u32 nbEnabled = 0; static u32 maskedPids[MAX_DEBUG]; static u32 masks[MAX_DEBUG][8] = {0}; +static u32 *homeBtnPressed = NULL; bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId) { @@ -178,6 +179,15 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3) KRecursiveLock__Unlock(&dbgParamsLock); break; } + case 0x10007: + { + // A bit crude but do the job for a simple notification + reboot, nothing sensitive here + if (varg1 > 255 && homeBtnPressed == NULL) + homeBtnPressed = PA_FROM_VA_PTR((u32 *)varg1); + else if (homeBtnPressed != NULL && *homeBtnPressed == 0) + *homeBtnPressed = varg1; + break; + } default: { res = KernelSetState(type, varg1, varg2, varg3); diff --git a/k11_extension/source/svc/MapProcessMemoryEx.c b/k11_extension/source/svc/MapProcessMemoryEx.c index 27a3ef2..dd18b77 100644 --- a/k11_extension/source/svc/MapProcessMemoryEx.c +++ b/k11_extension/source/svc/MapProcessMemoryEx.c @@ -26,19 +26,61 @@ #include "svc/MapProcessMemoryEx.h" -Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size) +Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size) { + Result res = 0; + u32 sizeInPage = size >> 12; + KLinkedList list; + KProcess *srcProcess; + KProcess *dstProcess; KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); - KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess); - KProcess *process = KProcessHandleTable__ToKProcess(handleTable, processHandle); - if(process == NULL) + if (dstProcessHandle == CUR_PROCESS_HANDLE) + { + dstProcess = currentCoreContext->objectContext.currentProcess; + KAutoObject__AddReference((KAutoObject *)dstProcess); + } + else + dstProcess = KProcessHandleTable__ToKProcess(handleTable, dstProcessHandle); + + if (dstProcess == NULL) return 0xD8E007F7; - Result res = KProcessHwInfo__MapProcessMemory(currentHwInfo, hwInfoOfProcess(process), dst, src, size >> 12); + if (srcProcessHandle == CUR_PROCESS_HANDLE) + { + srcProcess = currentCoreContext->objectContext.currentProcess; + KAutoObject__AddReference((KAutoObject *)srcProcess); + } + else + srcProcess = KProcessHandleTable__ToKProcess(handleTable, srcProcessHandle); - KAutoObject *obj = (KAutoObject *)process; - obj->vtable->DecrementReferenceCount(obj); + if (srcProcess == NULL) + { + res = 0xD8E007F7; + goto exit1; + } + + KLinkedList__Initialize(&list); + + res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfoOfProcess(srcProcess), &list, vaSrc, sizeInPage); + + if (res >= 0) + { + // Check if the destination address is free and large enough + res = KProcessHwInfo__CheckVaState(hwInfoOfProcess(dstProcess), vaDst, size, 0, 0); + if (res == 0) + res = KProcessHwInfo__MapListOfKBlockInfo(hwInfoOfProcess(dstProcess), vaDst, &list, 0x5806, MEMPERM_RW | 0x18, 0); + } + + KLinkedList_KBlockInfo__Clear(&list); + + ((KAutoObject *)srcProcess)->vtable->DecrementReferenceCount((KAutoObject *)srcProcess); + +exit1: + ((KAutoObject *)dstProcess)->vtable->DecrementReferenceCount((KAutoObject *)dstProcess); + + invalidateEntireInstructionCache(); + flushEntireDataCache(); return res; } diff --git a/k11_extension/source/svc/UnmapProcessMemoryEx.c b/k11_extension/source/svc/UnmapProcessMemoryEx.c index 30ffc6a..8887ea5 100644 --- a/k11_extension/source/svc/UnmapProcessMemoryEx.c +++ b/k11_extension/source/svc/UnmapProcessMemoryEx.c @@ -29,12 +29,42 @@ Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size) { - if(kernelVersion < SYSTEM_VERSION(2, 37, 0)) // < 6.x - return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB + Result res = 0; + u32 sizeInPage = size >> 12; + KLinkedList list; + KProcess *process; + KProcessHwInfo *hwInfo; + KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); - KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess); + if (processHandle == CUR_PROCESS_HANDLE) + { + process = currentCoreContext->objectContext.currentProcess; + KAutoObject__AddReference((KAutoObject *)process); + } + else + process = KProcessHandleTable__ToKProcess(handleTable, processHandle); - Result res = KProcessHwInfo__UnmapProcessMemory(currentHwInfo, dst, size >> 12); + if (process == NULL) + return 0xD8E007F7; + + hwInfo = hwInfoOfProcess(process); + + KLinkedList__Initialize(&list); + + res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfo, &list, (u32)dst, sizeInPage); + + if (res >= 0) + { + // Check for dst address to be in the right state (0x5806 as we set it with svcMapProcessMemoryEx) + res = KProcessHwInfo__CheckVaState(hwInfo, (u32)dst, size, 0x5806, 0); + if (res == 0) + res = KProcessHwInfo__MapListOfKBlockInfo(hwInfo, (u32)dst, &list, 0, 0, 0); + } + + KLinkedList_KBlockInfo__Clear(&list); + + + ((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process); invalidateEntireInstructionCache(); flushEntireDataCache(); diff --git a/k11_extension/source/svc/wrappers.s b/k11_extension/source/svc/wrappers.s index 91dc359..dfeddc7 100644 --- a/k11_extension/source/svc/wrappers.s +++ b/k11_extension/source/svc/wrappers.s @@ -84,3 +84,12 @@ ControlMemoryEx: ldr r1, [sp, #12] add sp, #20 pop {pc} + +.global MapProcessMemoryExWrapper +.type MapProcessMemoryExWrapper, %function +MapProcessMemoryExWrapper: + push {lr} + str r4, [sp, #-4]! + bl MapProcessMemoryEx + add sp, #4 + pop {pc} diff --git a/k11_extension/source/utils.s b/k11_extension/source/utils.s index 528c52f..5e0447b 100644 --- a/k11_extension/source/utils.s +++ b/k11_extension/source/utils.s @@ -96,6 +96,36 @@ KObjectMutex__Release: blx r12 bx lr +.global KProcessHwInfo__MapL1Section_Hook +.type KProcessHwInfo__MapL1Section_Hook, %function +KProcessHwInfo__MapL1Section_Hook: + @r0 => hwInfo + @sp + 0x34 => our ptr to state + add r1, sp, #0x34 + str lr, [sp, #-4]! + bl PatchDescriptorAccessControl + ldr lr, [sp], #4 + ldmfd sp, {r0-r4} + sub sp, sp, #0x14 + add r4, sp, #0x48 + mov r11, #0 + mov pc, lr + +.global KProcessHwInfo__MapL2Section_Hook +.type KProcessHwInfo__MapL2Section_Hook, %function +KProcessHwInfo__MapL2Section_Hook: + @r0 => hwInfo + @sp + 0x34 => our ptr to state + add r1, sp, #0x34 + str lr, [sp, #-4]! + bl PatchDescriptorAccessControl + ldr lr, [sp], #4 + ldmfd sp, {r0-r4} + sub sp, sp, #0x4C + mov r4, r1 + mov r6, r2 + mov pc, lr + .global safecpy .type safecpy, %function safecpy: diff --git a/source/patches.c b/source/patches.c index 38966b0..8b5f0bf 100644 --- a/source/patches.c +++ b/source/patches.c @@ -126,6 +126,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool isSafeMode, u32 baseK11VA, u32 * u32 config, multiConfig, bootConfig; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 rosalinaFlags; } info; }; @@ -195,6 +196,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool isSafeMode, u32 baseK11VA, u32 * info->bootConfig = configData.bootConfig; info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId; info->rosalinaMenuCombo = configData.rosalinaMenuCombo; + info->rosalinaFlags = configData.rosalinaFlags; info->versionMajor = VERSION_MAJOR; info->versionMinor = VERSION_MINOR; info->versionBuild = VERSION_BUILD; @@ -221,6 +223,10 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm u8 *ControlMemoryPos = instrPos + 8 + displ; u32 *off; + // Patch ControlMemory bounds checks for mem mapping + for (off = (u32 *)ControlMemoryPos; *off != 0xE0E01BF5; ++off); + *off = 0; + /* Here we replace currentProcess->processID == 1 by additionnalParameter == 1. This patch should be generic enough to work even on firmware version 5.0. diff --git a/source/types.h b/source/types.h index addd0ff..7600734 100644 --- a/source/types.h +++ b/source/types.h @@ -69,6 +69,7 @@ typedef struct __attribute__((packed)) u32 config, multiConfig, bootConfig; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 rosalinaFlags; } CfgData; typedef struct __attribute__((packed)) diff --git a/sysmodules/loader/source/loader.c b/sysmodules/loader/source/loader.c index ec0f0c2..01cc71a 100644 --- a/sysmodules/loader/source/loader.c +++ b/sysmodules/loader/source/loader.c @@ -207,6 +207,21 @@ static Result HBLDR_Init(Handle *session) return res; } +static Result PLGLDR_Init(Handle *session) +{ + Result res; + while (1) + { + res = svcConnectToPort(session, "plg:ldr"); + if (R_LEVEL(res) != RL_PERMANENT || + R_SUMMARY(res) != RS_NOTFOUND || + R_DESCRIPTION(res) != RD_NOT_FOUND + ) break; + svcSleepThread(500000); + } + return res; +} + static Result loader_GetProgramInfo(exheader_header *exheader, u64 prog_handle) { Result res; @@ -365,6 +380,9 @@ static Result loader_LoadProcess(Handle *process, u64 prog_handle) // load code if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0) { + u32 *code = (u32 *)shared_addr.text_addr; + bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E; + memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8); codesetinfo.program_id = progid; codesetinfo.text_addr = vaddr.text_addr; @@ -383,6 +401,33 @@ static Result loader_LoadProcess(Handle *process, u64 prog_handle) svcCloseHandle(codeset); if (res >= 0) { + // Try to load a plugin for the game + if (!isHomebrew && ((u32)((progid >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)) + { + // Special case handling: games rebooting the 3DS on old models + if (!isN3DS && (g_exheader.arm11systemlocalcaps.flags[2] >> 4) > 0) + { + // Check if the plugin loader is enabled, otherwise skip the loading part + s64 out; + + svcGetSystemInfo(&out, 0x10000, 0x102); + if ((out & 1) == 0) + return 0; + } + + Handle plgldr = 0; + + if (R_SUCCEEDED(PLGLDR_Init(&plgldr))) + { + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(1, 0, 2); + cmdbuf[1] = IPC_Desc_SharedHandles(1); + cmdbuf[2] = *process; + svcSendSyncRequest(plgldr); + svcCloseHandle(plgldr); + } + } return 0; } } @@ -582,14 +627,14 @@ void __appExit() void __sync_init(); void __sync_fini(); void __system_initSyscalls(); - + void __ctru_exit() { __appExit(); __sync_fini(); svcExitProcess(); } - + void initSystem() { __sync_init(); @@ -662,7 +707,7 @@ int main() g_active_handles--; reply_target = 0; } - else + else { svcBreak(USERBREAK_ASSERT); } diff --git a/sysmodules/rosalina/include/3gx.h b/sysmodules/rosalina/include/3gx.h new file mode 100644 index 0000000..3a20e50 --- /dev/null +++ b/sysmodules/rosalina/include/3gx.h @@ -0,0 +1,37 @@ +#pragma once +#include <3ds/types.h> +#include "ifile.h" + +#define _3GX_MAGIC (0x3130303024584733) /* "3GX$0001" */ + +typedef struct PACKED +{ + u32 authorLen; + const char * authorMsg; + u32 titleLen; + const char * titleMsg; + u32 summaryLen; + const char * summaryMsg; + u32 descriptionLen; + const char * descriptionMsg; +} _3gx_Infos; + +typedef struct PACKED +{ + u32 count; + u32 * titles; +} _3gx_Targets; + +typedef struct PACKED +{ + u64 magic; + u32 version; + u32 codeSize; + u32 * code; + _3gx_Infos infos; + _3gx_Targets targets; +} _3gx_Header; + + +Result Read_3gx_Header(IFile *file, _3gx_Header *header); +Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst); diff --git a/sysmodules/rosalina/include/csvc.h b/sysmodules/rosalina/include/csvc.h index e4c7ff9..543a2a6 100644 --- a/sysmodules/rosalina/include/csvc.h +++ b/sysmodules/rosalina/include/csvc.h @@ -71,18 +71,20 @@ void svcInvalidateEntireInstructionCache(void); ///@{ /** * @brief Maps a block of process memory. - * @param process Handle of the process. + * @param dstProcessHandle Handle of the process to map the memory in (destination) * @param destAddress Address of the mapped block in the current process. + * @param srcProcessHandle Handle of the process to map the memory from (source) * @param srcAddress Address of the mapped block in the source process. * @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes). */ -Result svcMapProcessMemoryEx(Handle process, u32 destAddr, u32 srcAddr, u32 size); +Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size); /** * @brief Unmaps a block of process memory. - * @param process Handle of the process. - * @param destAddress Address of the block of memory to unmap, in the current (destination) process. + * @param process Handle of the process to unmap the memory from + * @param destAddress Address of the block of memory to unmap * @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes). + * This function should only be used to unmap memory mapped with svcMapProcessMemoryEx */ Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size); @@ -134,4 +136,19 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess * @param in The input handle. */ Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in); + +/// Operations for svcControlProcess +typedef enum ProcessOp +{ + PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch + ///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0) + PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access + ///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0) + PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, + PROCESSOP_GET_ON_EXIT_EVENT, + PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process + ///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va) +} ProcessOp; + +Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3); ///@} diff --git a/sysmodules/rosalina/include/draw.h b/sysmodules/rosalina/include/draw.h index eb6d52c..88314a3 100644 --- a/sysmodules/rosalina/include/draw.h +++ b/sysmodules/rosalina/include/draw.h @@ -64,6 +64,7 @@ #define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F) #define COLOR_RED RGB565(0x1F, 0x00, 0x00) #define COLOR_GREEN RGB565(0x00, 0x1F, 0x00) +#define COLOR_LIME RGB565(0x00, 0xFF, 0x00) #define COLOR_BLACK RGB565(0x00, 0x00, 0x00) #define DRAW_MAX_FORMATTED_STRING_SIZE 512 diff --git a/sysmodules/rosalina/include/gdb/remote_command.h b/sysmodules/rosalina/include/gdb/remote_command.h index 56182ca..7eb2cf7 100644 --- a/sysmodules/rosalina/include/gdb/remote_command.h +++ b/sysmodules/rosalina/include/gdb/remote_command.h @@ -31,8 +31,10 @@ #define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name) #define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name) +GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA); GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo); GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle); +GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles); GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig); GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions); GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches); diff --git a/sysmodules/rosalina/include/memory.h b/sysmodules/rosalina/include/memory.h index 49f9f60..fc7003b 100644 --- a/sysmodules/rosalina/include/memory.h +++ b/sysmodules/rosalina/include/memory.h @@ -35,6 +35,7 @@ void *memset32(void *dest, u32 value, u32 size); u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize); char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, u32 size); +char *strcat(char *dest, const char *src); s32 strnlen(const char *string, s32 maxlen); s32 strlen(const char *string); s32 strcmp(const char *str1, const char *str2); diff --git a/sysmodules/rosalina/include/menu.h b/sysmodules/rosalina/include/menu.h index 48fb370..7e245c7 100644 --- a/sysmodules/rosalina/include/menu.h +++ b/sysmodules/rosalina/include/menu.h @@ -75,14 +75,17 @@ extern Handle terminationRequestEvent; extern u32 menuCombo; -u32 waitInputWithTimeout(u32 msec); -u32 waitInput(void); +u32 waitInputWithTimeout(u32 msec); +u32 waitInput(void); -u32 waitComboWithTimeout(u32 msec); -u32 waitCombo(void); +u32 waitComboWithTimeout(u32 msec); +u32 waitCombo(void); MyThread *menuCreateThread(void); -void menuEnter(void); -void menuLeave(void); -void menuThreadMain(void); -void menuShow(Menu *root); +void menuEnter(void); +void menuLeave(void); +void menuThreadMain(void); +void menuShow(Menu *root); +void DispMessage(const char *title, const char *message); +u32 DispErrMessage(const char *title, const char *message, const Result error); +void DisplayPluginMenu(u32 *cmdbuf); diff --git a/sysmodules/rosalina/include/plgldr.h b/sysmodules/rosalina/include/plgldr.h new file mode 100644 index 0000000..421f3f1 --- /dev/null +++ b/sysmodules/rosalina/include/plgldr.h @@ -0,0 +1,32 @@ +#pragma once + +#include <3ds/types.h> + +#define MAX_BUFFER (50) +#define MAX_ITEMS_COUNT (64) + +typedef struct +{ + bool noFlash; + u32 lowTitleId; + char path[256]; + u32 config[32]; +} PluginLoadParameters; + +typedef struct +{ + u32 nbItems; + u8 states[MAX_ITEMS_COUNT]; + char title[MAX_BUFFER]; + char items[MAX_ITEMS_COUNT][MAX_BUFFER]; + char hints[MAX_ITEMS_COUNT][MAX_BUFFER]; +} PluginMenu; + +Result plgLdrInit(void); +void plgLdrExit(void); +Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled); +Result PLGLDR__SetPluginLoaderState(bool enabled); +Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters); +Result PLGLDR__DisplayMenu(PluginMenu *menu); +Result PLGLDR__DisplayMessage(const char *title, const char *body); +Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error); diff --git a/sysmodules/rosalina/include/plgloader.h b/sysmodules/rosalina/include/plgloader.h new file mode 100644 index 0000000..d2d659e --- /dev/null +++ b/sysmodules/rosalina/include/plgloader.h @@ -0,0 +1,9 @@ +#pragma once + +#include <3ds/types.h> +#include "MyThread.h" + +MyThread * PluginLoader__CreateThread(void); +bool PluginLoader__IsEnabled(void); +void PluginLoader__MenuCallback(void); +void PluginLoader__UpdateMenu(void); diff --git a/sysmodules/rosalina/include/utils.h b/sysmodules/rosalina/include/utils.h index 466c1ab..1934f24 100644 --- a/sysmodules/rosalina/include/utils.h +++ b/sysmodules/rosalina/include/utils.h @@ -28,6 +28,7 @@ #include <3ds/svc.h> #include <3ds/result.h> +#include <3ds/ipc.h> #include "csvc.h" // For accessing physmem uncached (and directly) @@ -56,4 +57,19 @@ static inline void *decodeARMBranch(const void *src) return (void *)((const u8 *)src + 8 + off); } +static inline void assertSuccess(Result res) +{ + if(R_FAILED(res)) + svcBreak(USERBREAK_PANIC); +} + +static inline void error(u32* cmdbuf, Result rc) +{ + cmdbuf[0] = IPC_MakeHeader(0, 1, 0); + cmdbuf[1] = rc; +} + +extern bool isN3DS; + Result OpenProcessByName(const char *name, Handle *h); +Result SaveSettings(void); diff --git a/sysmodules/rosalina/source/3gx.c b/sysmodules/rosalina/source/3gx.c new file mode 100644 index 0000000..5bf2b1e --- /dev/null +++ b/sysmodules/rosalina/source/3gx.c @@ -0,0 +1,62 @@ +#include <3ds.h> +#include "3gx.h" + +Result Read_3gx_Header(IFile *file, _3gx_Header *header) +{ + u64 total; + char * dst; + Result res = 0; + + res = IFile_Read(file, &total, header, sizeof(_3gx_Header)); + if (R_FAILED(res)) + return res; + + // Read author + file->pos = (u32)header->infos.authorMsg; + dst = (char *)header + sizeof(_3gx_Header); + res = IFile_Read(file, &total, dst, header->infos.authorLen); + if (R_FAILED(res)) + return res; + + // Relocate ptr + header->infos.authorMsg = dst; + + // Read title + file->pos = (u32)header->infos.titleMsg; + dst += header->infos.authorLen; + res = IFile_Read(file, &total, dst, header->infos.titleLen); + if (R_FAILED(res)) + return res; + + // Relocate ptr + header->infos.titleMsg = dst; + + // Declare other members as null (unused in our case) + header->infos.summaryLen = 0; + header->infos.summaryMsg = NULL; + header->infos.descriptionLen = 0; + header->infos.descriptionMsg = NULL; + + // Read targets compatibility + file->pos = (u32)header->targets.titles; + dst += header->infos.titleLen; + dst += 4 - ((u32)dst & 3); // 4 bytes aligned + res = IFile_Read(file, &total, dst, header->targets.count * sizeof(u32)); + if (R_FAILED(res)) + return res; + + // Relocate ptr + header->targets.titles = (u32 *)dst; + return res; +} + +Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst) +{ + u64 total; + Result res = 0; + + file->pos = (u32)header->code; + res = IFile_Read(file, &total, dst, header->codeSize); + + return res; +} diff --git a/sysmodules/rosalina/source/csvc.s b/sysmodules/rosalina/source/csvc.s index 658992f..8ff3cda 100644 --- a/sysmodules/rosalina/source/csvc.s +++ b/sysmodules/rosalina/source/csvc.s @@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache SVC_END SVC_BEGIN svcMapProcessMemoryEx + str r4, [sp, #-4]! + ldr r4, [sp, #4] svc 0xA0 + ldr r4, [sp], #4 bx lr SVC_END @@ -99,3 +102,8 @@ SVC_BEGIN svcTranslateHandle str r1, [r2] bx lr SVC_END + +SVC_BEGIN svcControlProcess + svc 0xB3 + bx lr +SVC_END diff --git a/sysmodules/rosalina/source/errdisp.c b/sysmodules/rosalina/source/errdisp.c index ca512d1..8ed7e29 100644 --- a/sysmodules/rosalina/source/errdisp.c +++ b/sysmodules/rosalina/source/errdisp.c @@ -32,12 +32,6 @@ #include "fmt.h" #include "ifile.h" -static inline void assertSuccess(Result res) -{ - if(R_FAILED(res)) - svcBreak(USERBREAK_PANIC); -} - static MyThread errDispThread; static u8 ALIGN(8) errDispThreadStack[0x2000]; diff --git a/sysmodules/rosalina/source/gdb/remote_command.c b/sysmodules/rosalina/source/gdb/remote_command.c index 80a488f..64adc1b 100644 --- a/sysmodules/rosalina/source/gdb/remote_command.c +++ b/sysmodules/rosalina/source/gdb/remote_command.c @@ -29,6 +29,7 @@ #include "csvc.h" #include "fmt.h" #include "gdb/breakpoints.h" +#include "utils.h" struct { @@ -36,8 +37,10 @@ struct GDBCommandHandler handler; } remoteCommandHandlers[] = { + { "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) }, { "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) }, { "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) }, + { "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) }, { "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) }, { "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) }, { "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) }, @@ -51,6 +54,50 @@ static const char *GDB_SkipSpaces(const char *pos) return nextpos; } +GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA) +{ + bool ok; + int n; + u32 val; + u32 pa; + char * end; + char outbuf[GDB_BUF_LEN / 2 + 1]; + + if(ctx->commandData[0] == 0) + return GDB_ReplyErrno(ctx, EILSEQ); + + val = xstrtoul(ctx->commandData, &end, 0, true, &ok); + + if(!ok) + return GDB_ReplyErrno(ctx, EILSEQ); + + if (val >= 0x40000000) + pa = svcConvertVAToPA((const void *)val, false); + else + { + Handle process; + Result r = svcOpenProcess(&process, ctx->pid); + if(R_FAILED(r)) + { + n = sprintf(outbuf, "Invalid process (wtf?)\n"); + goto end; + } + r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val); + svcCloseHandle(process); + + if (R_FAILED(r)) + { + n = sprintf(outbuf, "An error occured: %08X\n", r); + goto end; + } + } + + n = sprintf(outbuf, "va: 0x%08X, pa: 0x%08X, b31: 0x%08X\n", val, pa, pa | (1 << 31)); +end: + return GDB_SendHexPacket(ctx, outbuf, n); +} + + GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo) { char outbuf[GDB_BUF_LEN / 2 + 1]; @@ -135,6 +182,29 @@ end: return GDB_SendHexPacket(ctx, outbuf, n); } +enum +{ + TOKEN_KAUTOOBJECT = 0, + TOKEN_KSYNCHRONIZATIONOBJECT = 1, + TOKEN_KEVENT = 0x1F, + TOKEN_KSEMAPHORE = 0x2F, + TOKEN_KTIMER = 0x35, + TOKEN_KMUTEX = 0x39, + TOKEN_KDEBUG = 0x4D, + TOKEN_KSERVERPORT = 0x55, + TOKEN_KDMAOBJECT = 0x59, + TOKEN_KCLIENTPORT = 0x65, + TOKEN_KCODESET = 0x68, + TOKEN_KSESSION = 0x70, + TOKEN_KTHREAD = 0x8D, + TOKEN_KSERVERSESSION = 0x95, + TOKEN_KCLIENTSESSION = 0xA5, + TOKEN_KPORT = 0xA8, + TOKEN_KSHAREDMEMORY = 0xB0, + TOKEN_KPROCESS = 0xC5, + TOKEN_KRESOURCELIMIT = 0xC8 +}; + GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle) { bool ok; @@ -143,10 +213,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle) int n; Result r; u32 kernelAddr; + s64 token; Handle handle, process; s64 refcountRaw; u32 refcount; - char classBuf[32], serviceBuf[12] = { 0 }; + char classBuf[32], serviceBuf[12] = {0}, ownerBuf[50] = { 0 }; char outbuf[GDB_BUF_LEN / 2 + 1]; if(ctx->commandData[0] == 0) @@ -178,12 +249,29 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle) svcTranslateHandle(&kernelAddr, classBuf, handle); svcGetHandleInfo(&refcountRaw, handle, 1); + svcGetHandleInfo(&token, handle, 0x10001); svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle); refcount = (u32)(refcountRaw - 1); + if(serviceBuf[0] != 0) n = sprintf(outbuf, "(%s *)0x%08x /* %s handle, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references"); + else if (token == TOKEN_KPROCESS) + { + svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000); + n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references"); + } else - n = sprintf(outbuf, "(%s *)0x%08x /* %u %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references"); + { + s64 owner; + + if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002))) + { + svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000); + svcCloseHandle((u32)owner); + sprintf(ownerBuf, " owner: %s", serviceBuf); + } + n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf); + } end: svcCloseHandle(handle); @@ -191,6 +279,68 @@ end: return GDB_SendHexPacket(ctx, outbuf, n); } +GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles) +{ + bool ok; + u32 val; + char *end; + int n = 0; + Result r; + s32 count = 0; + Handle process, procHandles[0x100]; + char outbuf[GDB_BUF_LEN / 2 + 1]; + + if(ctx->commandData[0] == 0) + val = 0; ///< All handles + else + { // Get handles of specified type + val = xstrtoul(ctx->commandData, &end, 0, true, &ok); + + if(!ok) + return GDB_ReplyErrno(ctx, EILSEQ); + + end = (char *)GDB_SkipSpaces(end); + + if(*end != 0) + return GDB_ReplyErrno(ctx, EILSEQ); + } + + r = svcOpenProcess(&process, ctx->pid); + if(R_FAILED(r)) + { + n = sprintf(outbuf, "Invalid process (wtf?)\n"); + goto end; + } + + if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val))) + n = sprintf(outbuf, "An error occured: %08X\n", count); + else if (count == 0) + n = sprintf(outbuf, "Process has no handles ?\n"); + else + { + n = sprintf(outbuf, "Found %d handles.\n", count); + + const char *comma = ""; + for (s32 i = 0; i < count && n < (GDB_BUF_LEN >> 1) - 20; ++i) + { + Handle handle = procHandles[i]; + + n += sprintf(outbuf + n, "%s0x%08X", comma, handle); + + if (((i + 1) % 8) == 0) + { + outbuf[n++] = '\n'; + comma = ""; + } + else + comma = ", "; + } + } +end: + svcCloseHandle(process); + return GDB_SendHexPacket(ctx, outbuf, n); +} + extern bool isN3DS; GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig) { diff --git a/sysmodules/rosalina/source/hbloader.c b/sysmodules/rosalina/source/hbloader.c index 3a8d426..ba1ed79 100644 --- a/sysmodules/rosalina/source/hbloader.c +++ b/sysmodules/rosalina/source/hbloader.c @@ -117,12 +117,6 @@ static const u32 kernelCaps[] = 0xFE000200, // Handle table size: 0x200 }; -static inline void assertSuccess(Result res) -{ - if(R_FAILED(res)) - svcBreak(USERBREAK_PANIC); -} - static MyThread hbldrThread; static u8 ALIGN(8) hbldrThreadStack[THREAD_STACK_SIZE]; static u16 hbldrTarget[PATH_MAX+1]; @@ -133,12 +127,6 @@ MyThread *hbldrCreateThread(void) return &hbldrThread; } -static inline void error(u32* cmdbuf, Result rc) -{ - cmdbuf[0] = IPC_MakeHeader(0, 1, 0); - cmdbuf[1] = rc; -} - static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size) { u32 i; diff --git a/sysmodules/rosalina/source/input_redirection.c b/sysmodules/rosalina/source/input_redirection.c index e3725c0..5d4bada 100644 --- a/sysmodules/rosalina/source/input_redirection.c +++ b/sysmodules/rosalina/source/input_redirection.c @@ -166,7 +166,7 @@ Result InputRedirection_DoOrUndoPatches(void) totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize); svcGetProcessInfo(&startAddress, processHandle, 0x10005); - res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize); + res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize); if(R_SUCCEEDED(res)) { @@ -203,21 +203,21 @@ Result InputRedirection_DoOrUndoPatches(void) u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue)); if(off == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -1; } u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue)); if(off2 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -2; } u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode)); if(off3 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -3; } @@ -230,7 +230,7 @@ Result InputRedirection_DoOrUndoPatches(void) } } - res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); } svcCloseHandle(processHandle); @@ -244,7 +244,7 @@ Result InputRedirection_DoOrUndoPatches(void) totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize); svcGetProcessInfo(&startAddress, processHandle, 0x10005); - res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize); + res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize); if(R_SUCCEEDED(res)) { @@ -298,7 +298,7 @@ Result InputRedirection_DoOrUndoPatches(void) u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4); if(off == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -4; } @@ -308,7 +308,7 @@ Result InputRedirection_DoOrUndoPatches(void) off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld)); if(off2 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -5; } @@ -320,7 +320,7 @@ Result InputRedirection_DoOrUndoPatches(void) u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode)); if(off3 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -6; } @@ -343,7 +343,7 @@ Result InputRedirection_DoOrUndoPatches(void) } } - res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); } svcCloseHandle(processHandle); diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c index 5f8f6be..bf296cd 100644 --- a/sysmodules/rosalina/source/main.c +++ b/sysmodules/rosalina/source/main.c @@ -36,6 +36,7 @@ #include "MyThread.h" #include "menus/process_patches.h" #include "menus/miscellaneous.h" +#include "plgloader.h" // this is called before main bool isN3DS; @@ -111,7 +112,8 @@ int main(void) Result res = 0; Handle notificationHandle; - MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(), *hbldrThread = hbldrCreateThread(); + MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(), + *hbldrThread = hbldrCreateThread(), *plgloaderThread = PluginLoader__CreateThread(); if(R_FAILED(srvEnableNotification(¬ificationHandle))) svcBreak(USERBREAK_ASSERT); @@ -147,6 +149,7 @@ int main(void) MyThread_Join(menuThread, -1LL); MyThread_Join(errDispThread, -1LL); MyThread_Join(hbldrThread, -1LL); + MyThread_Join(plgloaderThread, -1LL); svcCloseHandle(notificationHandle); return 0; diff --git a/sysmodules/rosalina/source/memory.c b/sysmodules/rosalina/source/memory.c index bb14562..8fb257b 100644 --- a/sysmodules/rosalina/source/memory.c +++ b/sysmodules/rosalina/source/memory.c @@ -118,6 +118,18 @@ char *strncpy(char *dest, const char *src, u32 size) return dest; } +char *strcat(char *dest, const char *src) +{ + char *dst = dest; + + while(*dest) ++dest; + while (*src) + *dest++ = *src++; + *dest = 0; + + return dst; +} + s32 strnlen(const char *string, s32 maxlen) { s32 size; diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index ae32bf3..87a2ed3 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -32,6 +32,7 @@ #include "ifile.h" #include "menus.h" #include "utils.h" +#include "plgloader.h" #include "menus/n3ds.h" #include "menus/cheats.h" #include "minisoc.h" @@ -127,9 +128,11 @@ u32 waitCombo(void) static MyThread menuThread; static u8 ALIGN(8) menuThreadStack[THREAD_STACK_SIZE]; static u8 batteryLevel = 255; +static u32 homeBtnPressed = 0; MyThread *menuCreateThread(void) { + svcKernelSetState(0x10007, &homeBtnPressed); if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, THREAD_STACK_SIZE, 52, CORE_SYSTEM))) svcBreak(USERBREAK_PANIC); return &menuThread; @@ -138,7 +141,9 @@ MyThread *menuCreateThread(void) extern bool isN3DS; u32 menuCombo; -void menuThreadMain(void) +u32 DispWarningOnHome(void); + +void menuThreadMain(void) { if(!isN3DS) { @@ -159,10 +164,11 @@ void menuThreadMain(void) isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u")) && isAcURegistered; - if (isAcURegistered) + else { menuEnter(); if(isN3DS) N3DSMenu_UpdateStatus(); + PluginLoader__UpdateMenu(); menuShow(&rosalinaMenu); menuLeave(); } @@ -173,6 +179,16 @@ void menuThreadMain(void) Cheat_ApplyKeyCheats(); } } + + // Check for home button on O3DS Mode3 with plugin loaded + if (homeBtnPressed != 0) + { + if (DispWarningOnHome()) + svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway + + homeBtnPressed = 0; + } + svcSleepThread(50 * 1000 * 1000LL); } } @@ -344,3 +360,158 @@ void menuShow(Menu *root) } while(!terminationRequest); } + +static const char *__press_b_to_close = "Press [B] to close"; + +void DispMessage(const char *title, const char *message) +{ + menuEnter(); + + Draw_Lock(); + Draw_ClearFramebuffer(); + Draw_FlushFramebuffer(); + + Draw_DrawString(10, 10, COLOR_TITLE, title); + + Draw_DrawString(30, 30, COLOR_WHITE, message); + Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close); + + + u32 keys = 0; + + do + { + keys = waitComboWithTimeout(1000); + }while (!terminationRequest && !(keys & BUTTON_B)); + + Draw_Unlock(); ///< Keep it locked until we exit the message + menuLeave(); +} + +u32 DispErrMessage(const char *title, const char *message, const Result error) +{ + char buf[100]; + + sprintf(buf, "Error code: 0x%08X", error); + menuEnter(); + + Draw_Lock(); + Draw_ClearFramebuffer(); + Draw_FlushFramebuffer(); + + Draw_DrawString(10, 10, COLOR_TITLE, title); + + u32 posY = Draw_DrawString(30, 30, COLOR_WHITE, message); + Draw_DrawString(30, posY + 20, COLOR_RED, buf); + Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close); + + u32 keys = 0; + + do + { + keys = waitComboWithTimeout(1000); + }while (!terminationRequest && !(keys & BUTTON_B)); + + Draw_Unlock(); ///< Keep it locked until we exit the message + menuLeave(); + return error; +} + +u32 DispWarningOnHome(void) +{ + menuEnter(); + + Draw_Lock(); + Draw_ClearFramebuffer(); + Draw_FlushFramebuffer(); + + Draw_DrawString(10, 10, COLOR_TITLE, "Warning"); + + u32 posY = Draw_DrawString(30, 40, COLOR_WHITE, "Due to memory shortage the home button\nis disabled."); + Draw_DrawString(30, posY + 20, COLOR_WHITE, "Press [DPAD UP + B] to exit the application."); + Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close); + + + u32 keys = 0; + + do + { + keys = waitComboWithTimeout(1000); + }while (!terminationRequest && !(keys & BUTTON_B)); + + Draw_Unlock(); ///< Keep it locked until we exit the message + menuLeave(); + + return (keys & BUTTON_UP) > 0; +} + + +typedef char string[50]; +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +void DisplayPluginMenu(u32 *cmdbuf) +{ + u32 cursor = 0; + u32 nbItems = cmdbuf[1]; + u8 *states = (u8 *)cmdbuf[3]; + char buffer[60]; + const char *title = (const char *)cmdbuf[5]; + const string *items = (const string *)cmdbuf[7]; + const string *hints = (const string *)cmdbuf[9]; + + menuEnter(); + Draw_Lock(); + + do + { + // Draw the menu + { + // Clear screen + Draw_ClearFramebuffer(); + Draw_FlushFramebuffer(); + + // Draw title + Draw_DrawString(10, 10, COLOR_TITLE, title); + + // Draw items + u32 i = MAX(0, (int)cursor - 7); + u32 end = MIN(nbItems, i + 16); + u32 posY = 30; + + for (; i < end; ++i, posY += 10) + { + sprintf(buffer, "[ ] %s", items[i]); + Draw_DrawString(30, posY, COLOR_WHITE, buffer); + + if (i == cursor) Draw_DrawCharacter(10, posY, COLOR_TITLE, '>'); + if (states[i]) Draw_DrawCharacter(36, posY, COLOR_LIME, 'x'); + } + + // Draw hint + if (hints[cursor]) + Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]); + } + + // Wait for input + u32 pressed = waitInput(); + + if (pressed & BUTTON_A) + states[cursor] = !states[cursor]; + + if (pressed & BUTTON_B) + break; + + if (pressed & BUTTON_DOWN) + if (++cursor >= nbItems) + cursor = 0; + + if (pressed & BUTTON_UP) + if (--cursor >= nbItems) + cursor = nbItems - 1; + + } while (true); + + Draw_Unlock(); + menuLeave(); +} diff --git a/sysmodules/rosalina/source/menus.c b/sysmodules/rosalina/source/menus.c index 85c7d4a..dbff44c 100644 --- a/sysmodules/rosalina/source/menus.c +++ b/sysmodules/rosalina/source/menus.c @@ -35,16 +35,18 @@ #include "menus/debugger.h" #include "menus/miscellaneous.h" #include "menus/sysconfig.h" +#include "plgloader.h" #include "ifile.h" #include "memory.h" #include "fmt.h" Menu rosalinaMenu = { "Rosalina menu", - .nbItems = 10, + .nbItems = 11, { { "New 3DS menu...", MENU, .menu = &N3DSMenu }, { "Cheats...", METHOD, .method = &RosalinaMenu_Cheats }, + { "", METHOD, .method = PluginLoader__MenuCallback}, { "Process list", METHOD, .method = &RosalinaMenu_ProcessList }, { "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot }, { "Debugger options...", MENU, .menu = &debuggerMenu }, @@ -52,7 +54,7 @@ Menu rosalinaMenu = { { "Miscellaneous options...", MENU, .menu = &miscellaneousMenu }, { "Power off", METHOD, .method = &RosalinaMenu_PowerOff }, { "Reboot", METHOD, .method = &RosalinaMenu_Reboot }, - { "Credits", METHOD, .method = &RosalinaMenu_ShowCredits } + { "Credits", METHOD, .method = &RosalinaMenu_ShowCredits }, } }; diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 6877bfc..c4a82b1 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -30,6 +30,7 @@ #include "memory.h" #include "draw.h" #include "hbloader.h" +#include "plgloader.h" #include "fmt.h" #include "utils.h" // for makeARMBranch #include "minisoc.h" @@ -168,7 +169,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void) while(!(waitInput() & BUTTON_B) && !terminationRequest); } -void MiscellaneousMenu_SaveSettings(void) +Result SaveSettings(void) { Result res; @@ -183,12 +184,14 @@ void MiscellaneousMenu_SaveSettings(void) u32 config, multiConfig, bootConfig; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 rosalinaFlags; } configData; u32 formatVersion; u32 config, multiConfig, bootConfig; s64 out; bool isSdMode; + if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT); formatVersion = (u32)out; if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT); @@ -208,6 +211,7 @@ void MiscellaneousMenu_SaveSettings(void) configData.bootConfig = bootConfig; configData.hbldr3dsxTitleId = HBLDR_3DSX_TID; configData.rosalinaMenuCombo = menuCombo; + configData.rosalinaFlags = PluginLoader__IsEnabled(); FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW; res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE); @@ -215,6 +219,14 @@ void MiscellaneousMenu_SaveSettings(void) if(R_SUCCEEDED(res)) res = IFile_Write(&file, &total, &configData, sizeof(configData), 0); + IFile_Close(&file); + return res; +} + +void MiscellaneousMenu_SaveSettings(void) +{ + Result res = SaveSettings(); + Draw_Lock(); Draw_ClearFramebuffer(); Draw_FlushFramebuffer(); diff --git a/sysmodules/rosalina/source/menus/process_list.c b/sysmodules/rosalina/source/menus/process_list.c index 14c49e7..cd62e22 100644 --- a/sysmodules/rosalina/source/menus/process_list.c +++ b/sysmodules/rosalina/source/menus/process_list.c @@ -223,8 +223,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info) svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress); heapTotalSize = mem.size; - Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize); - Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize); + Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize); + Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize); bool codeAvailable = R_SUCCEEDED(codeRes); bool heapAvailable = R_SUCCEEDED(heapRes); @@ -565,9 +565,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info) } if(codeAvailable) - svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize); if(heapAvailable) - svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize); svcCloseHandle(processHandle); } diff --git a/sysmodules/rosalina/source/menus/process_patches.c b/sysmodules/rosalina/source/menus/process_patches.c index ca096b1..6ead37e 100644 --- a/sysmodules/rosalina/source/menus/process_patches.c +++ b/sysmodules/rosalina/source/menus/process_patches.c @@ -105,12 +105,12 @@ static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result s64 textTotalRoundedSize = 0, startAddress = 0; svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text svcGetProcessInfo(&startAddress, processHandle, 0x10005); - if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize))) + if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize))) return res; res = func(textTotalRoundedSize); - svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize); return res; } diff --git a/sysmodules/rosalina/source/plgldr.c b/sysmodules/rosalina/source/plgldr.c new file mode 100644 index 0000000..3752523 --- /dev/null +++ b/sysmodules/rosalina/source/plgldr.c @@ -0,0 +1,143 @@ +#include <3ds.h> +#include "plgldr.h" +#include + +static Handle plgLdrHandle; +static int plgLdrRefCount; + +Result plgLdrInit(void) +{ + Result res = 0; + + if (AtomicPostIncrement(&plgLdrRefCount) == 0) + res = svcConnectToPort(&plgLdrHandle, "plg:ldr"); + + if (R_FAILED(res)) + AtomicDecrement(&plgLdrRefCount); + + return res; +} + +void plgLdrExit(void) +{ + if (AtomicDecrement(&plgLdrRefCount)) + return; + + svcCloseHandle(plgLdrHandle); +} + +Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(2, 0, 0); + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + *isEnabled = cmdbuf[2]; + } + return res; +} + +Result PLGLDR__SetPluginLoaderState(bool enabled) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(3, 1, 0); + cmdbuf[1] = (u32)enabled; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(4, 2, 4); + cmdbuf[1] = (u32)parameters->noFlash; + cmdbuf[2] = parameters->lowTitleId; + cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R); + cmdbuf[4] = (u32)parameters->path; + cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R); + cmdbuf[6] = (u32)parameters->config; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__DisplayMenu(PluginMenu *menu) +{ + Result res = 0; + + u32 nbItems = menu->nbItems; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(5, 1, 8); + cmdbuf[1] = nbItems; + cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW); + cmdbuf[3] = (u32)menu->states; + cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R); + cmdbuf[5] = (u32)menu->title; + cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R); + cmdbuf[7] = (u32)menu->items; + cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R); + cmdbuf[9] = (u32)menu->hints; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__DisplayMessage(const char *title, const char *body) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(6, 0, 4); + cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R); + cmdbuf[2] = (u32)title; + cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R); + cmdbuf[4] = (u32)body; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(7, 1, 4); + cmdbuf[1] = error; + cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R); + cmdbuf[3] = (u32)title; + cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R); + cmdbuf[5] = (u32)body; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} diff --git a/sysmodules/rosalina/source/plgloader.c b/sysmodules/rosalina/source/plgloader.c new file mode 100644 index 0000000..8867251 --- /dev/null +++ b/sysmodules/rosalina/source/plgloader.c @@ -0,0 +1,599 @@ +#include <3ds.h> +#include "3gx.h" +#include "ifile.h" +#include "utils.h" // for makeARMBranch +#include "plgloader.h" +#include "fmt.h" +#include "menu.h" +#include "menus.h" +#include "memory.h" +#include "sleep.h" + +#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE) +#define MemBlockSize (5*1024*1024) /* 5 MiB */ + +typedef struct +{ + Result code; + const char * message; +} Error; + +typedef struct +{ + bool isEnabled; + bool noFlash; + u32 titleid; + char path[256]; + u32 config[32]; +} PluginLoadParameters; + +#define HeaderMagic (0x24584733) /* "3GX$" */ + +typedef struct +{ + u32 magic; + u32 version; + u32 heapVA; + u32 heapSize; + u32 pluginSize; + const char* pluginPathPA; + u32 isDefaultPlugin; + u32 reserved[25]; + u32 config[32]; +} PluginHeader; + +static bool g_isEnabled; +static u8 * g_memBlock; +static char g_path[256]; +static Handle g_process = 0; +static u32 g_codeSize; +static u32 g_heapSize; +static Error g_error; +static PluginLoadParameters g_userDefinedLoadParameters; + +static MyThread g_pluginLoaderThread; +static u8 ALIGN(8) g_pluginLoaderThreadStack[0x4000]; + +// pluginLoader.s +void gamePatchFunc(void); + +void PluginLoader__ThreadMain(void); +MyThread * PluginLoader__CreateThread(void) +{ + s64 out; + + svcGetSystemInfo(&out, 0x10000, 0x102); + g_isEnabled = out & 1; + g_userDefinedLoadParameters.isEnabled = false; + + if(R_FAILED(MyThread_Create(&g_pluginLoaderThread, PluginLoader__ThreadMain, g_pluginLoaderThreadStack, 0x4000, 20, CORE_SYSTEM))) + svcBreak(USERBREAK_PANIC); + return &g_pluginLoaderThread; +} + +bool PluginLoader__IsEnabled(void) +{ + return g_isEnabled; +} + +void PluginLoader__MenuCallback(void) +{ + g_isEnabled = !g_isEnabled; + SaveSettings(); + PluginLoader__UpdateMenu(); +} + +void PluginLoader__UpdateMenu(void) +{ + static const char *status[2] = + { + "Plugin Loader: [Disabled]", + "Plugin Loader: [Enabled]" + }; + + rosalinaMenu.items[isN3DS + 1].title = status[g_isEnabled]; +} + +static Result MapPluginInProcess(Handle proc, u32 size) +{ + u32 heapsize = MemBlockSize - size; + Result res; + PluginHeader *header = (PluginHeader *)g_memBlock; + + header->heapVA = 0x06000000; + g_heapSize = header->heapSize = heapsize; + g_codeSize = size; + + // From now on, all memory page mapped to the process should be rwx + svcControlProcess(proc, PROCESSOP_SET_MMU_TO_RWX, 0, 0); + + // Plugin + if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x07000000, CUR_PROCESS_HANDLE, (u32)g_memBlock, size)))) + { + g_error.message = "Couldn't map plugin memory block"; + g_error.code = res; + return res; + } + + // Heap (to be used by the plugin) + if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x06000000, CUR_PROCESS_HANDLE, (u32)g_memBlock + size, heapsize)))) + { + g_error.message = "Couldn't map heap memory block"; + g_error.code = res; + goto exit; + } + + // Clear heap section + memset32(g_memBlock + size, 0, heapsize); + +exit: + return res; +} + +static u32 strlen16(const u16 *str) +{ + u32 size = 0; + + while (str && *str++) ++size; + return size; +} + +static Result FindPluginFile(u64 tid) +{ + char filename[256]; + u32 entriesNb = 0; + bool found = false; + Handle dir = 0; + Result res; + FS_Archive sdmcArchive = 0; + FS_DirectoryEntry entries[10]; + + sprintf(g_path, "/luma/plugins/%016llX", tid); + + if (R_FAILED((res =FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""))))) + goto exit; + + if (R_FAILED((res = FSUSER_OpenDirectory(&dir, sdmcArchive, fsMakePath(PATH_ASCII, g_path))))) + goto exit; + + strcat(g_path, "/"); + while (!found && R_SUCCEEDED(FSDIR_Read(dir, &entriesNb, 10, entries))) + { + if (entriesNb == 0) + break; + + static const u16 * validExtension = u"3gx"; + + for (u32 i = 0; i < entriesNb; ++i) + { + FS_DirectoryEntry *entry = &entries[i]; + + // If entry is a folder, skip it + if (entry->attributes & 1) + continue; + + // Check extension + u32 size = strlen16(entry->name); + if (size <= 5) + continue; + + u16 *fileExt = entry->name + size - 3; + + if (memcmp(fileExt, validExtension, 3 * sizeof(u16))) + continue; + + // Convert name from utf16 to utf8 + int units = utf16_to_utf8((u8 *)filename, entry->name, 100); + if (units == -1) + continue; + filename[units] = 0; + found = true; + break; + } + } + + if (!found) + res = MAKERESULT(28, 4, 0, 1018); + else + { + u32 len = strlen(g_path); + filename[256 - len] = 0; + strcat(g_path, filename); + } + + ((PluginHeader *)g_memBlock)->pluginPathPA = PA_FROM_VA_PTR(g_path); + +exit: + FSDIR_Close(dir); + FSUSER_CloseArchive(sdmcArchive); + + return res; +} + +static Result OpenFile(IFile *file, const char *path) +{ + return IFile_Open(file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ); +} + +static Result CheckPluginCompatibility(_3gx_Header *header, u32 processTitle) +{ + static char errorBuf[0x100]; + + if (header->targets.count == 0) + return 0; + + for (u32 i = 0; i < header->targets.count; ++i) + { + if (header->targets.titles[i] == processTitle) + return 0; + } + + sprintf(errorBuf, "The plugin - %s -\nis not compatible with this game.\n" \ + "Contact \"%s\" for more infos.", header->infos.titleMsg, header->infos.authorMsg); + g_error.message = errorBuf; + + return -1; +} + +static bool TryToLoadPlugin(Handle process) +{ + u64 fileSize; + u64 tid; + u32 procStart = 0x00100000; + IFile plugin; + PluginHeader *hdr = (PluginHeader *)g_memBlock; + _3gx_Header *header; + Result res; + + // Clear the memblock + memset32(g_memBlock, 0, MemBlockSize); + + hdr->magic = HeaderMagic; + + // Get title id + svcGetProcessInfo((s64 *)&tid, process, 0x10001); + if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000)))) + { + g_error.message = "Couldn't map process"; + g_error.code = res; + return false; + } + + // Try to open plugin file + if (g_userDefinedLoadParameters.isEnabled && (u32)tid == g_userDefinedLoadParameters.titleid) + { + g_userDefinedLoadParameters.isEnabled = false; + if (OpenFile(&plugin, g_userDefinedLoadParameters.path)) + goto exitFail; + hdr->pluginPathPA = PA_FROM_VA_PTR(g_userDefinedLoadParameters.path); + memcpy(hdr->config, g_userDefinedLoadParameters.config, 32 * sizeof(u32)); + } + else + { + if (R_FAILED(FindPluginFile(tid)) || OpenFile(&plugin, g_path)) + { + // Try to open default plugin + const char *defaultPath = "/luma/plugins/default.3gx"; + if (OpenFile(&plugin, defaultPath)) + goto exitFail; + hdr->isDefaultPlugin = 1; + hdr->pluginPathPA = PA_FROM_VA_PTR(defaultPath); + } + } + + if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize)))) + g_error.message = "Couldn't get file size"; + + // Plugins will rarely exceed 1MB so this is fine + header = (_3gx_Header *)(g_memBlock + MemBlockSize - (u32)fileSize); + + // Read header + if (!res && R_FAILED((res = Read_3gx_Header(&plugin, header)))) + g_error.message = "Couldn't read file"; + + // Check titles compatibility + if (!res) res = CheckPluginCompatibility(header, (u32)tid); + + // Read code + if (!res && R_FAILED(res = Read_3gx_Code(&plugin, header, g_memBlock + sizeof(PluginHeader)))) + g_error.message = "Couldn't read plugin's code"; + + // Close file + IFile_Close(&plugin); + if (R_FAILED(res)) + { + g_error.code = res; + goto exitFail; + } + + hdr->version = header->version; + // Code size must be page aligned + fileSize = (header->codeSize + 0x1100) & ~0xFFF; + + if (MapPluginInProcess(process, fileSize) == 0) + // Install hook + { + extern u32 g_savedGameInstr[2]; + u32 *game = (u32 *)procStart; + + g_savedGameInstr[0] = game[0]; + g_savedGameInstr[1] = game[1]; + + game[0] = 0xE51FF004; // ldr pc, [pc, #-4] + game[1] = (u32)PA_FROM_VA_PTR(gamePatchFunc); + } + else + goto exitFail; + + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000); + return true; + +exitFail: + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000); + return false; +} + +static void SetKernelConfigurationMemoryFlag(bool loaded) +{ + u32 *flag = (u32 *)PA_FROM_VA_PTR(0x1FF800F0); + + *flag = loaded; +} + +static void PluginLoader_HandleCommands(void) +{ + u32 *cmdbuf = getThreadCommandBuffer(); + + switch (cmdbuf[0] >> 16) + { + case 1: // Load plugin + { + if (cmdbuf[0] != IPC_MakeHeader(1, 0, 2)) + { + error(cmdbuf, 0xD9001830); + break; + } + + Handle process = cmdbuf[2]; + + if (g_isEnabled && TryToLoadPlugin(process)) + { + if (!g_userDefinedLoadParameters.isEnabled && g_userDefinedLoadParameters.noFlash) + g_userDefinedLoadParameters.noFlash = false; + else + { + for (u32 i = 0; i < 64; i++) + { + REG32(0x10202204) = 0x01FF9933; + svcSleepThread(5000000); + } + REG32(0x10202204) = 0; + } + + g_process = process; + } + else + svcCloseHandle(process); + + cmdbuf[0] = IPC_MakeHeader(1, 1, 0); + cmdbuf[1] = 0; + break; + } + + case 2: // Check if plugin loader is enabled + { + if (cmdbuf[0] != IPC_MakeHeader(2, 0, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + cmdbuf[0] = IPC_MakeHeader(2, 2, 0); + cmdbuf[1] = 0; + cmdbuf[2] = (u32)g_isEnabled; + break; + } + + case 3: // Enable / Disable plugin loader + { + if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + if (cmdbuf[1] != g_isEnabled) + { + g_isEnabled = cmdbuf[1]; + SaveSettings(); + } + + cmdbuf[0] = IPC_MakeHeader(3, 1, 0); + cmdbuf[1] = 0; + break; + } + + case 4: // Define next plugin load settings + { + if (cmdbuf[0] != IPC_MakeHeader(4, 2, 4)) + { + error(cmdbuf, 0xD9001830); + break; + } + + g_userDefinedLoadParameters.isEnabled = true; + g_userDefinedLoadParameters.noFlash = cmdbuf[1]; + g_userDefinedLoadParameters.titleid = cmdbuf[2]; + strncpy(g_userDefinedLoadParameters.path, (const char *)cmdbuf[4], 255); + memcpy(g_userDefinedLoadParameters.config, (void *)cmdbuf[6], 32 * sizeof(u32)); + + cmdbuf[0] = IPC_MakeHeader(4, 1, 0); + cmdbuf[1] = 0; + break; + } + + case 5: // Display menu + { + if (cmdbuf[0] != IPC_MakeHeader(5, 1, 8)) + { + error(cmdbuf, 0xD9001830); + break; + } + + u32 nbItems = cmdbuf[1]; + u32 states = cmdbuf[3]; + DisplayPluginMenu(cmdbuf); + + cmdbuf[0] = IPC_MakeHeader(5, 1, 2); + cmdbuf[1] = 0; + cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW); + cmdbuf[3] = states; + break; + } + + case 6: // Display message + { + if (cmdbuf[0] != IPC_MakeHeader(6, 0, 4)) + { + error(cmdbuf, 0xD9001830); + break; + } + + const char *title = (const char *)cmdbuf[2]; + const char *body = (const char *)cmdbuf[4]; + + DispMessage(title, body); + + cmdbuf[0] = IPC_MakeHeader(6, 1, 0); + cmdbuf[1] = 0; + break; + } + + case 7: // Display error message + { + if (cmdbuf[0] != IPC_MakeHeader(7, 1, 4)) + { + error(cmdbuf, 0xD9001830); + break; + } + + const char *title = (const char *)cmdbuf[3]; + const char *body = (const char *)cmdbuf[5]; + + DispErrMessage(title, body, cmdbuf[1]); + + cmdbuf[0] = IPC_MakeHeader(7, 1, 0); + cmdbuf[1] = 0; + break; + } + } +} + +void PluginLoader__ThreadMain(void) +{ + const char *title = "Plugin loader"; + + Result res = 0; + MemOp memRegion = isN3DS ? MEMOP_REGION_BASE : MEMOP_REGION_SYSTEM; + Handle handles[3]; + Handle serverHandle, clientHandle, sessionHandle = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + u32 replyTarget = 0; + u32 nbHandle; + s32 index; + + // Wait for the system to be completely started + { + bool isAcuRegistered = false; + + while (true) + { + if (R_SUCCEEDED(srvIsServiceRegistered(&isAcuRegistered, "ac:u")) + && isAcuRegistered) + break; + svcSleepThread(100000); + } + } + + // Init memory block to hold the plugin + { + u32 free = (u32)osGetMemRegionFree(memRegion); + u32 temp = 0; + + svcControlMemoryEx(&temp, 0x00100000, 0, free - MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true); + if (R_FAILED((res = svcControlMemoryEx((u32 *)&g_memBlock, 0x07000000, 0, MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true)))) + { + svcSleepThread(5000000000ULL); ///< Wait until the system started to display the error + DispErrMessage(title, "Couldn't allocate memblock", res); + return; + } + svcControlMemoryEx(&temp, (u32)temp, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true); + } + + assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "plg:ldr", 1)); + + do + { + g_error.message = NULL; + g_error.code = 0; + handles[0] = serverHandle; + handles[1] = sessionHandle == 0 ? g_process : sessionHandle; + handles[2] = g_process; + + if(replyTarget == 0) // k11 + cmdbuf[0] = 0xFFFF0000; + + nbHandle = 1 + (sessionHandle != 0) + (g_process != 0); + res = svcReplyAndReceive(&index, handles, nbHandle, replyTarget); + + if(R_FAILED(res)) + { + if((u32)res == 0xC920181A) // session closed by remote + { + svcCloseHandle(sessionHandle); + sessionHandle = 0; + replyTarget = 0; + } + else + svcBreak(USERBREAK_PANIC); + } + else + { + if(index == 0) + { + Handle session; + assertSuccess(svcAcceptSession(&session, serverHandle)); + + if(sessionHandle == 0) + sessionHandle = session; + else + svcCloseHandle(session); + } + else if (index == 1 && handles[1] == sessionHandle) + { + PluginLoader_HandleCommands(); + replyTarget = sessionHandle; + } + else ///< The process in which we injected the plugin is terminating + { + // Unmap plugin's memory before closing the process + svcUnmapProcessMemoryEx(g_process, 0x07000000, g_codeSize); + svcUnmapProcessMemoryEx(g_process, 0x06000000, g_heapSize); + svcCloseHandle(g_process); + g_process = 0; + } + } + + if (g_error.message != NULL) + DispErrMessage(title, g_error.message, g_error.code); + + SetKernelConfigurationMemoryFlag(g_process != 0); + + } while(!terminationRequest); + + svcCloseHandle(sessionHandle); + svcCloseHandle(clientHandle); + svcCloseHandle(serverHandle); + svcControlMemoryEx((u32 *)&g_memBlock, (u32)g_memBlock, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true); +} diff --git a/sysmodules/rosalina/source/pluginLoader.s b/sysmodules/rosalina/source/pluginLoader.s new file mode 100644 index 0000000..dde16e5 --- /dev/null +++ b/sysmodules/rosalina/source/pluginLoader.s @@ -0,0 +1,33 @@ +.section .data +.balign 4 +.arm + +.global gamePatchFunc +.type gamePatchFunc, %function +gamePatchFunc: + stmfd sp!, {r0-r12, lr} + mrs r0, cpsr + stmfd sp!, {r0} + adr r0, g_savedGameInstr + ldr r1, =0x00100000 + ldr r2, [r0] + str r2, [r1] + ldr r2, [r0, #4] + str r2, [r1, #4] + svc 0x92 + svc 0x94 + +startplugin: + ldr r5, =0x07000100 + blx r5 + +exit: + ldmfd sp!, {r0} + msr cpsr, r0 + ldmfd sp!, {r0-r12, lr} + ldr lr, =0x00100000 + mov pc, lr + +.global g_savedGameInstr +g_savedGameInstr: + .word 0, 0 diff --git a/sysmodules/rosalina/source/sleep.c b/sysmodules/rosalina/source/sleep.c index 4c6afe3..0919d5b 100644 --- a/sysmodules/rosalina/source/sleep.c +++ b/sysmodules/rosalina/source/sleep.c @@ -55,7 +55,6 @@ bool Sleep__Status(void) if (g_isSleeping) { LightEvent_Wait(&g_onWakeUpEvent); - svcSleepThread(1000000000ULL); return true; } return false; diff --git a/sysmodules/sm/source/notifications.c b/sysmodules/sm/source/notifications.c index 82c722b..eebee0a 100644 --- a/sysmodules/sm/source/notifications.c +++ b/sysmodules/sm/source/notifications.c @@ -19,6 +19,14 @@ static bool doPublishNotification(ProcessData *processData, u32 notificationId, } } + // Handle special case for home button notifications on Mode3 O3DS with plugin loaded + if ((notificationId == 0x204 || notificationId == 0x205) + && *(u32 *)0x1FF80030 == 3 && *(u32 *)0x1FF800F0) + { + svcKernelSetState(0x10007, 1); + return true; + } + if(processData->nbPendingNotifications < 0x10) { s32 count;