diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini index 0fb6b0a9..d7a03f59 100644 Binary files a/arm9/data/config_template.ini and b/arm9/data/config_template.ini differ diff --git a/arm9/source/config.c b/arm9/source/config.c index 3f61e577..d35feaaa 100644 --- a/arm9/source/config.c +++ b/arm9/source/config.c @@ -460,6 +460,11 @@ static int configIniHandler(void* user, const char* section, const char* name, c CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value)); cfg->rosalinaMenuCombo = opt; return 1; + } else if (strcmp(name, "plugin_loader_enabled") == 0) { + bool opt; + CHECK_PARSE_OPTION(parseBoolOption(&opt, value)); + cfg->pluginLoaderFlags = opt ? cfg->pluginLoaderFlags | 1 : cfg->pluginLoaderFlags & ~1; + return 1; } else if (strcmp(name, "ntp_tz_offset_min") == 0) { s64 opt; CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899)); @@ -650,7 +655,7 @@ static size_t saveLumaIniConfigToStr(char *out) pinNumDigits, n3dsCpuStr, autobootModeStr, - cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, + cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1), (int)cfg->ntpTzOffetMinutes, (int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct, diff --git a/arm9/source/config.h b/arm9/source/config.h index aabdb5ee..e390c37f 100644 --- a/arm9/source/config.h +++ b/arm9/source/config.h @@ -36,7 +36,7 @@ #define CONFIG_FILE "config.ini" #define CONFIG_VERSIONMAJOR 3 -#define CONFIG_VERSIONMINOR 8 +#define CONFIG_VERSIONMINOR 9 #define BOOTCFG_NAND BOOTCONFIG(0, 1) #define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3) diff --git a/arm9/source/patches.c b/arm9/source/patches.c index 5a179ff7..559b93ad 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -134,6 +134,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32 u32 splashDurationMsec; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 pluginLoaderFlags; s16 ntpTzOffetMinutes; ScreenFiltersCfgData topScreenFilter; @@ -216,6 +217,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32 info->splashDurationMsec = configData.splashDurationMsec; info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId; info->rosalinaMenuCombo = configData.rosalinaMenuCombo; + info->pluginLoaderFlags = configData.pluginLoaderFlags; info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes; info->topScreenFilter = configData.topScreenFilter; info->bottomScreenFilter = configData.bottomScreenFilter; diff --git a/arm9/source/types.h b/arm9/source/types.h index 3f0ddded..f6f96f59 100644 --- a/arm9/source/types.h +++ b/arm9/source/types.h @@ -77,6 +77,7 @@ typedef struct CfgData { u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 pluginLoaderFlags; s16 ntpTzOffetMinutes; ScreenFiltersCfgData topScreenFilter; diff --git a/k11_extension/include/globals.h b/k11_extension/include/globals.h index d6e4dcb7..839a2833 100644 --- a/k11_extension/include/globals.h +++ b/k11_extension/include/globals.h @@ -31,6 +31,7 @@ extern KRecursiveLock *criticalSectionLock; extern KObjectList *threadList; +extern KObjectList *resourceLimitList; extern KObjectMutex *synchronizationMutex; extern void (*KRecursiveLock__Lock)(KRecursiveLock *this); @@ -44,18 +45,27 @@ 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 Result (*KEvent__Signal)(KEvent *this); extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); 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 Result (*doControlMemory)(KProcessHwInfo *this, u32 addr, u32 requestedNbPages, u32 pa, u32 state, u32 perm, u32 a7, u32 region); extern Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); 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); @@ -66,6 +76,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); @@ -98,6 +109,7 @@ extern bool *isDevUnit; extern vu8 *configPage; extern u32 kernelVersion; extern FcramLayout fcramLayout; +extern FcramDescriptor *fcramDescriptor; extern KCoreContext *coreCtxs; @@ -111,9 +123,10 @@ extern Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBase extern InterruptManager *interruptManager; extern KBaseInterruptEvent *customInterruptEvent; -extern void (*initFPU)(void); -extern void (*mcuReboot)(void); -extern void (*coreBarrier)(void); +extern void (*initFPU)(void); +extern void (*mcuReboot)(void); +extern void (*coreBarrier)(void); +extern void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region); typedef struct ScreenFiltersCfgData { u16 cct; @@ -139,6 +152,7 @@ typedef struct CfwInfo u32 splashDurationMsec; u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 pluginLoaderFlags; s16 ntpTzOffetMinutes; ScreenFiltersCfgData topScreenFilter; @@ -153,6 +167,24 @@ typedef struct CfwInfo extern CfwInfo cfwInfo; extern u32 kextBasePa; extern u32 stolenSystemMemRegionSize; +extern bool disableThreadRedirection; extern vu32 rosalinaState; extern bool hasStartedRosalinaNetworkFuncsOnce; +extern KEvent* signalPluginEvent; + +typedef enum +{ + PLG_CFG_NONE = 0, + PLG_CFG_RUNNING = 1, + PLG_CFG_INHOME = 2, + PLG_CFG_EXITING = 3, + + PLG_CFG_HOME_EVENT = 1 << 16, + PLG_CFG_EXIT_EVENT = 2 << 16 +} PLG_CFG_STATUS; + +void PLG_SignalEvent(u32 event); +void PLG__WakeAppThread(void); +u32 PLG_GetStatus(void); +KLinkedList* KLinkedList__Initialize(KLinkedList *list); diff --git a/k11_extension/include/kernel.h b/k11_extension/include/kernel.h index a0596c3d..8b3c0027 100644 --- a/k11_extension/include/kernel.h +++ b/k11_extension/include/kernel.h @@ -106,6 +106,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 { @@ -113,6 +121,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 { @@ -489,6 +521,9 @@ typedef enum MemOp MEMOP_REGION_SYSTEM = 0x200, MEMOP_REGION_BASE = 0x300, MEMOP_LINEAR = 0x10000, + + MEMOP_OP_MASK = 0xFF, + MEMOP_REGION_MASK = 0xF00, } MemOp; /* 17 */ @@ -541,6 +576,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 { @@ -1038,10 +1087,24 @@ 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;\ + /* } */ \ + u8 gap234[44];\ 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); @@ -1135,6 +1198,28 @@ typedef struct FcramLayout u32 baseSize; } FcramLayout; +typedef struct RegionDescriptor +{ + void *firstMemoryBlock; + void *lastMemoryBlock; + void *regionStart; + u32 regionSizeInBytes; +} RegionDescriptor; + +typedef struct FcramDescriptor +{ + RegionDescriptor appRegion; + RegionDescriptor sysRegion; + RegionDescriptor baseRegion; + RegionDescriptor * regionDescsPtr; + u32 fcramStart; + u32 fcramSizeInPages; + u32 baseMemoryStart; + u32 kernelUsageInBytes; + u32 unknown; + KObjectMutex mutex; +} FcramDescriptor; + extern bool isN3DS; extern void *officialSVCs[0x7E]; diff --git a/k11_extension/include/mmu.h b/k11_extension/include/mmu.h new file mode 100644 index 00000000..fce63c6a --- /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/ControlMemoryUnsafe.h b/k11_extension/include/svc/ControlMemoryUnsafe.h new file mode 100644 index 00000000..90162731 --- /dev/null +++ b/k11_extension/include/svc/ControlMemoryUnsafe.h @@ -0,0 +1,34 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2018 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +#include "utils.h" +#include "kernel.h" +#include "svc.h" + +Result ControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm); +Result ControlMemoryUnsafeWrapper(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm); diff --git a/k11_extension/include/svc/ControlProcess.h b/k11_extension/include/svc/ControlProcess.h new file mode 100644 index 00000000..cabc6d71 --- /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_SIGNAL_ON_EXIT, + 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/ExitProcess.h b/k11_extension/include/svc/ExitProcess.h new file mode 100644 index 00000000..ae06c648 --- /dev/null +++ b/k11_extension/include/svc/ExitProcess.h @@ -0,0 +1,6 @@ +#include "utils.h" +#include "kernel.h" +#include "svc.h" + +void ExitProcessHook(void); +void ExitProcessHookWrapper(void); \ No newline at end of file diff --git a/k11_extension/include/svc/KernelSetState.h b/k11_extension/include/svc/KernelSetState.h index 9179b77d..187d8f12 100644 --- a/k11_extension/include/svc/KernelSetState.h +++ b/k11_extension/include/svc/KernelSetState.h @@ -30,7 +30,7 @@ #include "kernel.h" #include "svc.h" -extern bool svcSignalingEnabled; +extern u8 svcSignalingEnabled; bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId); Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3); diff --git a/k11_extension/include/svc/MapProcessMemoryEx.h b/k11_extension/include/svc/MapProcessMemoryEx.h index 4f74ca3c..0b357afd 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 09a27325..78acaf64 100644 --- a/k11_extension/source/fatalExceptionHandlersMain.c +++ b/k11_extension/source/fatalExceptionHandlersMain.c @@ -28,10 +28,43 @@ #include "fatalExceptionHandlers.h" #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 48 +#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) { @@ -43,7 +76,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 +85,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 +103,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; @@ -96,7 +130,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId) registerDump[15] = pc; //Dump code - u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem + u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - (dumpHeader.codeDumpSize >> 1) ; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize); //Copy register dump and code dump diff --git a/k11_extension/source/globals.c b/k11_extension/source/globals.c index 47918aa4..c816317f 100644 --- a/k11_extension/source/globals.c +++ b/k11_extension/source/globals.c @@ -25,6 +25,8 @@ */ #include "globals.h" +#include "utils.h" +#include "ipc.h" KRecursiveLock *criticalSectionLock; KObjectList *threadList; @@ -40,18 +42,29 @@ 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); +Result (*KEvent__Signal)(KEvent *this); + void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this); 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); +Result (*doControlMemory)(KProcessHwInfo *this, u32 addr, u32 requestedNbPages, u32 pa, u32 state, u32 perm, u32 a7, u32 region); + Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId); 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); @@ -62,6 +75,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); @@ -94,6 +108,7 @@ bool *isDevUnit; vu8 *configPage; u32 kernelVersion; FcramLayout fcramLayout; +FcramDescriptor *fcramDescriptor; KCoreContext *coreCtxs; void *originalHandlers[8] = {NULL}; @@ -104,15 +119,67 @@ Result (*InterruptManager__MapInterrupt)(InterruptManager *manager, KBaseInterru u32 coreID, u32 priority, bool disableUponReceipt, bool levelHighActive); InterruptManager *interruptManager; -void (*initFPU)(void); -void (*mcuReboot)(void); -void (*coreBarrier)(void); +void (*initFPU)(void); +void (*mcuReboot)(void); +void (*coreBarrier)(void); +void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region); CfwInfo cfwInfo; u32 kextBasePa; u32 stolenSystemMemRegionSize; +bool disableThreadRedirection = false; vu32 rosalinaState; bool hasStartedRosalinaNetworkFuncsOnce; - +KEvent* signalPluginEvent = NULL; u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess; + +KLinkedList* KLinkedList__Initialize(KLinkedList *list) +{ + list->size = 0; + list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes; + return list; +} + +void PLG_SignalEvent(u32 event) +{ + KThread *currentThread = currentCoreContext->objectContext.currentThread; + + // Set configuration memory field with event + *(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0) |= event; + + // Send notification 0x1001 + { + u32 *cmdbuf = (u32 *)((u8 *)currentCoreContext->objectContext.currentThread->threadLocalStorage + 0x80); + u32 backup[3] = { cmdbuf[0], cmdbuf[1], cmdbuf[2] }; + Handle srvHandle; + SessionInfo *info = SessionInfo_FindFirst("srv:"); + + Result res = createHandleForThisProcess(&srvHandle, &info->session->clientSession.syncObject.autoObject); + + if (res >= 0) + { + cmdbuf[0] = 0x000C0080; + cmdbuf[1] = 0x1001; + cmdbuf[2] = 0; + + SendSyncRequest(srvHandle); + CloseHandle(srvHandle); + } + + cmdbuf[0] = backup[0]; cmdbuf[1] = backup[1]; cmdbuf[2] = backup[2]; + } + // Wait for notification 0x1002 + WaitSynchronization1(NULL, currentThread, (KSynchronizationObject *)signalPluginEvent, U64_MAX); +} + +void PLG__WakeAppThread(void) +{ + KEvent__Signal(signalPluginEvent); +} + +u32 PLG_GetStatus(void) +{ + return (*(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0)) & 0xFFFF; +} + diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c index c4cce09b..0ff4e68e 100644 --- a/k11_extension/source/main.c +++ b/k11_extension/source/main.c @@ -130,11 +130,81 @@ void configHook(vu8 *cfgPage) flagsKProcess = KPROCESS_OFFSETOF(kernelFlags); } +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++); + // Get fcramDescriptor + for (off = (u32 *)0xFFF00000; ; ++off) + { + if ( (off[0] >> 16) == 0xE59F + && (off[1] >> 16) == 0xE3A0 + && (off[2] >> 16) == 0xE3A0 + && (off[3] >> 16) == 0xE1A0 + && (off[4] >> 16) == 0xEB00) + { + fcramDescriptor = (FcramDescriptor *)off[2 + (off[0] & 0xFFFF) / 4]; + break; + } + } + + // Get kAlloc + for (; *off != 0xE1A00005 || *(off + 1) != 0xE320F000; ++off); + off = decodeArmBranch(off + 2); + for (; (*off >> 16) != 0xEB00; ++off); + kAlloc = (void* (*)(FcramDescriptor *, u32, u32, u32))decodeArmBranch(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; @@ -171,6 +241,8 @@ static void findUsefulSymbols(void) KEvent__Clear = (Result (*)(KEvent *))decodeArmBranch(off + 1); for(off = (u32 *)KEvent__Clear; *off != 0xE8BD8070; off++); synchronizationMutex = *(KObjectMutex **)(off + 1); + for(off = (u32 *)officialSVCs[0x18]; *off != 0xE1A04005; ++off); + KEvent__Signal = (Result (*)(KEvent *))decodeArmBranch(off + 1); for(off = (u32 *)officialSVCs[0x24]; *off != 0xE59F004C; off++); WaitSynchronization1 = (Result (*)(void *, KThread *, KSynchronizationObject *, s64))decodeArmBranch(off + 6); @@ -197,6 +269,21 @@ 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 *)KProcessHwInfo__MapListOfKBlockInfo; *off != 0xE1A0000B; ++off); + doControlMemory = (Result (*)(KProcessHwInfo*, u32, u32, u32, u32, u32, u32, u32))decodeArmBranch(off + 1); + for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++); KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off); for(; *off != 0xE320F000; off++); @@ -243,6 +330,7 @@ static void findUsefulSymbols(void) decodeArmBranch((u32 *)officialSVCs[0x01] + 5); CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 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); @@ -253,6 +341,8 @@ 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); @@ -284,6 +374,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 00000000..8f483be7 --- /dev/null +++ b/k11_extension/source/mmu.c @@ -0,0 +1,319 @@ +#include "mmu.h" +#include "globals.h" +#include "utils.h" + +extern u8 svcSignalingEnabled; + +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 permissions 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) { + svcSignalingEnabled |= 2; + *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 516098f0..dceb1eea 100644 --- a/k11_extension/source/svc.c +++ b/k11_extension/source/svc.c @@ -44,8 +44,11 @@ #include "svc/MapProcessMemoryEx.h" #include "svc/UnmapProcessMemoryEx.h" #include "svc/ControlService.h" +#include "svc/ControlProcess.h" +#include "svc/ExitProcess.h" #include "svc/CopyHandle.h" #include "svc/TranslateHandle.h" +#include "svc/ControlMemoryUnsafe.h" void *officialSVCs[0x7E] = {NULL}; void *alteredSvcTable[0x100] = {NULL}; @@ -63,6 +66,7 @@ void buildAlteredSvcTable(void) memcpy(alteredSvcTable, officialSVCs, 4 * 0x7E); alteredSvcTable[0x01] = ControlMemoryHookWrapper; + alteredSvcTable[0x03] = ExitProcessHookWrapper; if (isN3DS) alteredSvcTable[0x08] = CreateThreadHookWrapper; @@ -90,13 +94,15 @@ void buildAlteredSvcTable(void) alteredSvcTable[0x93] = invalidateInstructionCacheRange; alteredSvcTable[0x94] = invalidateEntireInstructionCache; - alteredSvcTable[0xA0] = MapProcessMemoryEx; + alteredSvcTable[0xA0] = MapProcessMemoryExWrapper; alteredSvcTable[0xA1] = UnmapProcessMemoryEx; alteredSvcTable[0xA2] = ControlMemoryEx; + alteredSvcTable[0xA3] = ControlMemoryUnsafeWrapper; alteredSvcTable[0xB0] = ControlService; alteredSvcTable[0xB1] = CopyHandleWrapper; alteredSvcTable[0xB2] = TranslateHandleWrapper; + alteredSvcTable[0xB3] = ControlProcess; } void signalSvcEntry(u32 svcId) @@ -111,10 +117,18 @@ void signalSvcEntry(u32 svcId) void signalSvcReturn(u32 svcId) { KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; + u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags); // Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!) - if(debugOfProcess(currentProcess) != NULL && svcId != 0xFF && shouldSignalSyscallDebugEvent(currentProcess, svcId)) + if((svcSignalingEnabled & 1) != 0 && (currentProcess) != NULL && svcId != 0xFF && shouldSignalSyscallDebugEvent(currentProcess, svcId)) SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId); + + if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged) + { + *KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged; + SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent)); + svcSignalingEnabled &= ~2; + } } void postprocessSvc(void) diff --git a/k11_extension/source/svc/ControlMemoryUnsafe.c b/k11_extension/source/svc/ControlMemoryUnsafe.c new file mode 100644 index 00000000..d842ebc5 --- /dev/null +++ b/k11_extension/source/svc/ControlMemoryUnsafe.c @@ -0,0 +1,84 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2018 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "globals.h" +#include "memory.h" +#include "svc/ControlMemoryUnsafe.h" + +Result ControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm) +{ + Result res = 0; + KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; + KProcessHwInfo *hwInfo = hwInfoOfProcess(currentProcess); + + KAutoObject__AddReference((KAutoObject *)currentProcess); + + size = size >> 12 << 12; + switch (op & MEMOP_OP_MASK) + { + case MEMOP_FREE: + { + res = doControlMemory(hwInfo, addr0, size >> 12, 0, 0, 0, 0, 0); + break; + } + case MEMOP_COMMIT: + { + u32 pAddr = 0; + u32 state = 0xBB05; + u32 region = op & MEMOP_REGION_MASK; + + perm = (perm & 7) | 0x18; + if (op & MEMOP_LINEAR) + { + void *kvAddr = kAlloc(fcramDescriptor, size >> 12, 0, region); + + if (!kvAddr) + { + res = 0xD86007F3; + break; + } + + memset(kvAddr, 0, size >> 2); + flushDataCacheRange(kvAddr, size); + pAddr = (u32)kvAddr + 0x40000000; + state = 0x3907; + } + + res = doControlMemory(hwInfo, addr0, size >> 12, pAddr, state, perm, 0, region); + if (res >= 0 && out) + *out = addr0; + break; + } + + default: + res = 0xE0E01BEE; + break; + } + + ((KAutoObject *)currentProcess)->vtable->DecrementReferenceCount((KAutoObject *)currentProcess); + + return res; +} diff --git a/k11_extension/source/svc/ControlProcess.c b/k11_extension/source/svc/ControlProcess.c new file mode 100644 index 00000000..97862554 --- /dev/null +++ b/k11_extension/source/svc/ControlProcess.c @@ -0,0 +1,208 @@ +#include "svc/ControlProcess.h" +#include "memory.h" +#include "mmu.h" +#include "synchronization.h" + +typedef bool (*ThreadPredicate)(KThread *thread); + +// Lock bit has to be different from Rosalina to avoid unintended unlock when using Rosalina menu +static void rescheduleThread(KThread *thread, bool lock) +{ + KRecursiveLock__Lock(criticalSectionLock); + + u32 oldSchedulingMask = thread->schedulingMask; + if(lock) + thread->schedulingMask |= 0x20; + else + thread->schedulingMask &= ~0x20; + + KScheduler__AdjustThread(currentCoreContext->objectContext.currentScheduler, thread, oldSchedulingMask); + + KRecursiveLock__Unlock(criticalSectionLock); +} + +static void lockThread(KThread *thread) +{ + KThread *syncThread = synchronizationMutex->owner; + + if(syncThread == NULL || syncThread != thread) + rescheduleThread(thread, true); +} + +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); + } + + break; + } + + case PROCESSOP_SIGNAL_ON_EXIT: + { + *KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit; + 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->ownerProcess == process && (thread->schedulingMask & 0x20) + && (threadPredicate == NULL || threadPredicate(thread))) + rescheduleThread(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 + lockThread(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 & 0x20)) + { + lockThread(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/CreateThread.c b/k11_extension/source/svc/CreateThread.c index da748e97..37561ca8 100644 --- a/k11_extension/source/svc/CreateThread.c +++ b/k11_extension/source/svc/CreateThread.c @@ -29,7 +29,7 @@ Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId) { u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess); - if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && processorId == 1 && (flags & 0xF00) == 0x100) + if (isN3DS && CONFIG(REDIRECTAPPTHREADS) && !disableThreadRedirection && processorId == 1 && (flags & 0xF00) == 0x100) processorId = 2; return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId); diff --git a/k11_extension/source/svc/ExitProcess.c b/k11_extension/source/svc/ExitProcess.c new file mode 100644 index 00000000..b4e9bc8e --- /dev/null +++ b/k11_extension/source/svc/ExitProcess.c @@ -0,0 +1,32 @@ +#include "svc/ExitProcess.h" + +void ExitProcessHook(void) { + KProcess *currentProcess = currentCoreContext->objectContext.currentProcess; + u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags); + + if (flags & SignalOnExit) + { + // Signal that the process is about to be terminated + if (PLG_GetStatus() == PLG_CFG_RUNNING) + PLG_SignalEvent(PLG_CFG_EXIT_EVENT); + + // Unlock all threads that might be locked + { + KRecursiveLock__Lock(criticalSectionLock); + + for (KLinkedListNode *node = threadList->list.nodes.first; + node != (KLinkedListNode *)&threadList->list.nodes; + node = node->next) + { + KThread *thread = (KThread *)node->key; + + if (thread->ownerProcess == currentProcess && thread->schedulingMask & 0x20) + thread->schedulingMask &= ~0x20; + } + + KRecursiveLock__Unlock(criticalSectionLock); + } + } + + return ((void(*)())officialSVCs[0x3])(); +} \ No newline at end of file diff --git a/k11_extension/source/svc/GetHandleInfo.c b/k11_extension/source/svc/GetHandleInfo.c index ba7bfeda..d87f3d3c 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 2d4b1b75..58c7176b 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 6ed13c05..ffd63375 100644 --- a/k11_extension/source/svc/GetSystemInfo.c +++ b/k11_extension/source/svc/GetSystemInfo.c @@ -69,14 +69,15 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param) case 6: *out = cfwInfo.splashDurationMsec; break; - case 0x10: *out = (s64)cfwInfo.autobootTwlTitleId; break; case 0x11: *out = cfwInfo.autobootCtrAppmemtype; break; - + case 0x80: + *out = fcramDescriptor->appRegion.regionSizeInBytes; + break; case 0x100: *out = (s64)cfwInfo.hbldr3dsxTitleId; break; @@ -116,7 +117,12 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param) case 0x10C: *out = (s64)cfwInfo.bottomScreenFilter.invert; break; - + case 0x180: + *out = cfwInfo.pluginLoaderFlags; + break; + case 0x181: + *out = disableThreadRedirection; + break; case 0x200: // isRelease *out = cfwInfo.flags & 1; break; diff --git a/k11_extension/source/svc/KernelSetState.c b/k11_extension/source/svc/KernelSetState.c index 324e4f79..172260cb 100644 --- a/k11_extension/source/svc/KernelSetState.c +++ b/k11_extension/source/svc/KernelSetState.c @@ -35,8 +35,9 @@ static u32 nbEnabled = 0; static u32 maskedPids[MAX_DEBUG]; static u32 masks[MAX_DEBUG][8] = {0}; +static bool forceBetterSoc = false; -bool svcSignalingEnabled = false; +u8 svcSignalingEnabled = 0; bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId) { @@ -67,7 +68,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask) { maskedPids[nbEnabled] = pid; memcpy(&masks[nbEnabled++], tmpMask, 32); - svcSignalingEnabled = true; + svcSignalingEnabled |= 1; } else { @@ -87,7 +88,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask) } maskedPids[--nbEnabled] = 0; memset(&masks[nbEnabled], 0, 32); - svcSignalingEnabled = false; + svcSignalingEnabled &= ~1; } KRecursiveLock__Unlock(&syscallDebugEventMaskLock); @@ -101,6 +102,16 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3) switch(type) { + case 0xA: // Type 10 (ConfigureNew3DSCPU) + { + if (varg1 & (1 << 2)) // Lock faster speed + forceBetterSoc = true; + else if (varg1 & (1 << 3)) // Unlock faster speed + forceBetterSoc = false; + else + res = KernelSetState(type, forceBetterSoc ? 3 : varg1, varg2, varg3); + break; + } case 0x10000: { do @@ -190,6 +201,20 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3) KRecursiveLock__Unlock(&dbgParamsLock); break; } + case 0x10007: + { + if (signalPluginEvent == NULL && varg1) + { + KProcessHandleTable *table = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); + signalPluginEvent = (KEvent *)KProcessHandleTable__ToKAutoObject(table, varg1); + } + break; + } + case 0x10080: + { + disableThreadRedirection = varg1 != 0; + 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 3e1c4801..5d0da167 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/SendSyncRequest.c b/k11_extension/source/svc/SendSyncRequest.c index 60d259d9..1ba5ac16 100644 --- a/k11_extension/source/svc/SendSyncRequest.c +++ b/k11_extension/source/svc/SendSyncRequest.c @@ -162,6 +162,43 @@ Result SendSyncRequestHook(Handle handle) break; } + case 0x00C0080: // srv: publishToSubscriber + { + SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession); + + if (info != NULL && strcmp(info->name, "srv:") == 0 && cmdbuf[1] == 0x1002) + { + // Wake up application thread + PLG__WakeAppThread(); + cmdbuf[0] = 0xC0040; + cmdbuf[1] = 0; + skip = true; + } + break; + } + + case 0x00D0080: // APT:ReceiveParameter + { + SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession); + + if (info != NULL && strncmp(info->name, "APT:", 4) == 0 && cmdbuf[1] == 0x300) + { + res = SendSyncRequest(handle); + skip = true; + + if (res >= 0) + { + u32 plgStatus = PLG_GetStatus(); + u32 command = cmdbuf[3]; + + if ((plgStatus == PLG_CFG_RUNNING && command == 3) // COMMAND_RESPONSE + || (plgStatus == PLG_CFG_INHOME && (command >= 10 || command <= 12))) // COMMAND_WAKEUP_BY_EXIT || COMMAND_WAKEUP_BY_PAUSE + PLG_SignalEvent(PLG_CFG_HOME_EVENT); + } + } + break; + } + case 0x4010082: { SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession); diff --git a/k11_extension/source/svc/UnmapProcessMemoryEx.c b/k11_extension/source/svc/UnmapProcessMemoryEx.c index f351a33f..7dbef2df 100644 --- a/k11_extension/source/svc/UnmapProcessMemoryEx.c +++ b/k11_extension/source/svc/UnmapProcessMemoryEx.c @@ -29,12 +29,30 @@ Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size) { + Result res = 0; + KProcess *process; + KProcessHwInfo *hwInfo; + KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess); + if(GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB - 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); + + res = KProcessHwInfo__UnmapProcessMemory(hwInfo, dst, size >> 12); + + ((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 68fc1d8a..6d07ccde 100644 --- a/k11_extension/source/svc/wrappers.s +++ b/k11_extension/source/svc/wrappers.s @@ -69,6 +69,14 @@ ControlMemoryHookWrapper: add sp, #12 pop {pc} + +.global ExitProcessHookWrapper +.type ExitProcessHookWrapper, %function +ExitProcessHookWrapper: + push {lr} + bl ExitProcessHook + pop {pc} + .global ControlMemoryEx .type ControlMemoryEx, %function ControlMemoryEx: @@ -97,3 +105,21 @@ CreateThreadHookWrapper: ldr r1, [sp, #8] add sp, sp, #12 pop {pc} + +.global ControlMemoryUnsafeWrapper +.type ControlMemoryUnsafeWrapper, %function +ControlMemoryUnsafeWrapper: + push {lr} + str r4, [sp, #-4]! + bl ControlMemoryUnsafe + add sp, #4 + pop {pc} + +.global MapProcessMemoryExWrapper +.type MapProcessMemoryExWrapper, %function +MapProcessMemoryExWrapper: + push {lr} + str r4, [sp, #-4]! + bl MapProcessMemoryEx + add sp, #4 + pop {pc} \ No newline at end of file diff --git a/k11_extension/source/utils.s b/k11_extension/source/utils.s index bb1f7053..686e7a9f 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/sysmodules/loader/source/loader.c b/sysmodules/loader/source/loader.c index 721db7ba..5487dbde 100644 --- a/sysmodules/loader/source/loader.c +++ b/sysmodules/loader/source/loader.c @@ -8,8 +8,19 @@ #define SYSMODULE_CXI_COOKIE_MASK 0xEEEE000000000000ull +// Used by the custom loader command 0x101 (ControlApplicationMemoryModeOverride) +typedef struct ControlApplicationMemoryModeOverrideConfig { + u32 query : 1; //< Only query the current configuration, do not update it. + u32 enable_o3ds : 1; //< Enable o3ds memory mode override + u32 enable_n3ds : 1; //< Enable n3ds memory mode override + u32 o3ds_mode : 3; //< O3ds memory mode + u32 n3ds_mode : 3; //< N3ds memory mode +} ControlApplicationMemoryModeOverrideConfig; + +static ControlApplicationMemoryModeOverrideConfig g_memoryOverrideConfig = { 0 }; + extern u32 config, multiConfig, bootConfig; -extern bool isN3DS, isSdMode; +extern bool isN3DS, isSdMode, nextGamePatchDisabled; static u64 g_cached_programHandle; // for exheader info only static ExHeader_Info g_exheaderInfo; @@ -110,6 +121,11 @@ static inline bool IsSysmoduleId(u64 tid) return (tid >> 32) == 0x00040130; } +static inline bool IsApplicationId(u64 tid) +{ + return (tid >> 32) == 0x00040000; +} + static inline bool IsSysmoduleCxiCookie(u64 programHandle) { return (programHandle >> 32) == (SYSMODULE_CXI_COOKIE_MASK >> 32); @@ -211,6 +227,69 @@ static Result loadCode(const ExHeader_Info *exhi, u64 programHandle, const prog_ return 0; } +static u32 plgldrRefcount = 0; +static Handle plgldrHandle = 0; + +Result plgldrInit(void) +{ + Result res; + if (AtomicPostIncrement(&plgldrRefcount)) return 0; + + for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) { + res = svcConnectToPort(&plgldrHandle, "plg:ldr"); + if(R_FAILED(res) && res != (Result)0xD88007FA) { + AtomicDecrement(&plgldrRefcount); + return res; + } + } + + return 0; +} + +void plgldrExit(void) +{ + if (AtomicDecrement(&plgldrRefcount)) return; + svcCloseHandle(plgldrHandle); +} + +// Get plugin loader state +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; +} + +// Try to load a plugin for the game +static Result PLGLDR_LoadPlugin(u32 processID) +{ + // Special case handling: games rebooting the 3DS on old models + if (!isN3DS && g_exheaderInfo.aci.local_caps.core_info.o3ds_system_mode > 0) + { + // Check if the plugin loader is enabled, otherwise skip the loading part + bool enabled = false; + + PLGLDR__IsPluginLoaderEnabled(&enabled); + if (!enabled) { + return 0; + } + } + + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(1, 1, 0); + cmdbuf[1] = processID; + return svcSendSyncRequest(plgldrHandle); +} + static inline bool IsHioId(u64 id) { // FS loads HIO titles at boot when it can. For HIO titles, title/programId and "program handle" @@ -256,6 +335,8 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle) if (R_FAILED(res)) return res; + u64 originalTitleId = exheaderInfo->aci.local_caps.title_id; + // Tweak 3dsx placeholder title exheaderInfo if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id)) { @@ -263,7 +344,6 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle) } else { - u64 originalTitleId = exheaderInfo->aci.local_caps.title_id; bool exhLoadedExternally = false; if (CONFIG(PATCHGAMES)) { @@ -280,6 +360,13 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle) if(exhLoadedExternally) exheaderInfo->aci.local_caps.title_id = originalTitleId; } + + if (IsApplicationId(originalTitleId)) { + if (g_memoryOverrideConfig.enable_o3ds) + exheaderInfo->aci.local_caps.core_info.o3ds_system_mode = g_memoryOverrideConfig.o3ds_mode; + if (g_memoryOverrideConfig.enable_n3ds) + exheaderInfo->aci.local_caps.core_info.n3ds_system_mode = g_memoryOverrideConfig.n3ds_mode; + } return res; } @@ -336,6 +423,9 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh u64 titleId = exhi->aci.local_caps.title_id; if (R_SUCCEEDED(res = loadCode(exhi, programHandle, &mapped))) { + u32 *code = (u32 *)mapped.text_addr; + bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E; + memcpy(&csh.name, csi->name, 8); csh.program_id = titleId; csh.text_addr = vaddr.text_addr; @@ -354,6 +444,16 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh res = svcCreateProcess(outProcessHandle, codeset, exhi->aci.kernel_caps.descriptors, count); svcCloseHandle(codeset); res = R_SUCCEEDED(res) ? 0 : res; + + // check for plugin + if (!res && !isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)) + { + u32 processID; + assertSuccess(svcGetProcessId(&processID, *outProcessHandle)); + assertSuccess(plgldrInit()); + assertSuccess(PLGLDR_LoadPlugin(processID)); + plgldrExit(); + } } } @@ -441,6 +541,7 @@ void loaderHandleCommands(void *ctx) (void)ctx; FS_ProgramInfo title; FS_ProgramInfo update; + ControlApplicationMemoryModeOverrideConfig memModeOverride; u32* cmdbuf; u16 cmdid; int res; @@ -480,6 +581,20 @@ void loaderHandleCommands(void *ctx) cmdbuf[2] = IPC_Desc_StaticBuffer(sizeof(ExHeader_Info), 0); //0x1000002; cmdbuf[3] = (u32)&g_exheaderInfo; // official Loader makes a copy here, but this is isn't necessary break; + // Custom + case 0x100: // DisableNextGamePatch + nextGamePatchDisabled = true; + cmdbuf[0] = IPC_MakeHeader(0x100, 1, 0); + cmdbuf[1] = (Result)0; + break; + case 0x101: // ControlApplicationMemoryModeOverride + memcpy(&memModeOverride, &cmdbuf[1], sizeof(ControlApplicationMemoryModeOverrideConfig)); + if (!memModeOverride.query) + g_memoryOverrideConfig = memModeOverride; + cmdbuf[0] = IPC_MakeHeader(0x101, 2, 0); + cmdbuf[1] = (Result)0; + memcpy(&cmdbuf[2], &g_memoryOverrideConfig, sizeof(ControlApplicationMemoryModeOverrideConfig)); + break; default: // error cmdbuf[0] = IPC_MakeHeader(0, 1, 0); cmdbuf[1] = 0xD900182F; diff --git a/sysmodules/loader/source/main.c b/sysmodules/loader/source/main.c index 29581ac1..f349286e 100644 --- a/sysmodules/loader/source/main.c +++ b/sysmodules/loader/source/main.c @@ -10,7 +10,7 @@ #include "hbldr.h" u32 config, multiConfig, bootConfig; -bool isN3DS, isSdMode; +bool isN3DS, isSdMode, nextGamePatchDisabled; // MAKE SURE fsreg has been init before calling this static Result fsldrPatchPermissions(void) @@ -115,7 +115,7 @@ void initSystem(void) } static const ServiceManagerServiceEntry services[] = { - { "Loader", 1, loaderHandleCommands, false }, + { "Loader", 2, loaderHandleCommands, false }, { "hb:ldr", 2, hbldrHandleCommands, true }, { NULL }, }; @@ -129,6 +129,8 @@ static_assert(ARGVBUF_SIZE > 2 * PATH_MAX, "Wrong 3DSX argv buffer size"); int main(void) { + nextGamePatchDisabled = false; + // Loader doesn't use any input static buffer, so we should be fine u32 *sbuf = getThreadStaticBuffers(); sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0); diff --git a/sysmodules/loader/source/patcher.c b/sysmodules/loader/source/patcher.c index e427abd1..1b04756b 100644 --- a/sysmodules/loader/source/patcher.c +++ b/sysmodules/loader/source/patcher.c @@ -930,7 +930,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro if(!applyCodeIpsPatch(progId, code, size)) goto error; } - if(CONFIG(PATCHGAMES)) + if(CONFIG(PATCHGAMES) && !(isApp && nextGamePatchDisabled)) { if (!isSysmodule) { @@ -952,6 +952,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro } } + nextGamePatchDisabled = false; return; error: diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 9f125f07..64267847 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -42,7 +42,7 @@ enum singleOptions }; extern u32 config, multiConfig, bootConfig; -extern bool isN3DS, isSdMode; +extern bool isN3DS, isSdMode, nextGamePatchDisabled; void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress); bool loadTitleCodeSection(u64 progId, u8 *code, u32 size); diff --git a/sysmodules/pm/source/reslimit.c b/sysmodules/pm/source/reslimit.c index b0572576..66cc5e02 100644 --- a/sysmodules/pm/source/reslimit.c +++ b/sysmodules/pm/source/reslimit.c @@ -311,7 +311,11 @@ Result setAppCpuTimeLimit(s64 limit) // Prevent apps from enabling preemption on core1 (and kernel will // redirect apps threads from core 1 to 2). if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS)) - return 0; + { + s64 disableThreadRedir = 0; + if (R_SUCCEEDED(svcGetSystemInfo(&disableThreadRedir, 0x10000, 0x181)) && !disableThreadRedir) + return 0; + } ResourceLimitType category = RESLIMIT_CPUTIME; return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1); diff --git a/sysmodules/rosalina/Makefile b/sysmodules/rosalina/Makefile index a966feea..e923bcab 100644 --- a/sysmodules/rosalina/Makefile +++ b/sysmodules/rosalina/Makefile @@ -24,7 +24,7 @@ export HBLDR_DEFAULT_3DSX_TITLE_NAME ?= "hblauncher_loader" #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source source/gdb source/menus source/redshift +SOURCES := source source/gdb source/menus source/plugin source/redshift DATA := source/gdb/xml data INCLUDES := include include/gdb include/menus include/redshift diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini index 0fb6b0a9..d7a03f59 100644 Binary files a/sysmodules/rosalina/data/config_template.ini and b/sysmodules/rosalina/data/config_template.ini differ diff --git a/sysmodules/rosalina/include/csvc.h b/sysmodules/rosalina/include/csvc.h index e4c7ff98..f6d8ce98 100644 --- a/sysmodules/rosalina/include/csvc.h +++ b/sysmodules/rosalina/include/csvc.h @@ -18,6 +18,10 @@ #include <3ds/types.h> +/// Allows or disables thread redirection patches for the new thread if specified in the affinity field in svcCreateThread +#define AFFINITY_DISABLE_THREAD_REDIRECTION(x) ((x >= 0) ? (x | 0x40) : x) +#define AFFINITY_ALLOW_THREAD_REDIRECTION(x) ((x >= 0) ? (x & ~0x40) : x) + /// Operations for svcControlService typedef enum ServiceOp { @@ -71,18 +75,20 @@ void svcInvalidateEntireInstructionCache(void); ///@{ /** * @brief Maps a block of process memory. - * @param process Handle of the process. - * @param destAddress Address of the mapped block in the current process. - * @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). + * @param dstProcessHandle Handle of the process to map the memory in (destination) + * @param destAddress Start address of the memory block in the destination process + * @param srcProcessHandle Handle of the process to map the memory from (source) + * @param srcAddress Start address of the memory 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 srcAddress, 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); @@ -104,6 +110,20 @@ Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size); * @sa svcControlMemory */ Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader); + +/** + * @brief Controls memory mapping, this version removes all checks which were being done + * The only operations supported are MEMOP_FREE, MEMOP_ALLOC and MEMOP_ALLOC_LINEAR + * All memory allocated with this svc, must be freed with this svc as well + * @param[out] addr_out The virtual address resulting from the operation. Usually the same as addr0. + * @param addr0 The virtual address to be used for the operation. + * @param size The requested size for @ref MEMOP_ALLOC and @ref MEMOP_ALLOC_LINEAR. + * @param op Operation flags. See @ref MemOp. + * @param perm A combination of @ref MEMPERM_READ and @ref MEMPERM_WRITE + * Value 0 is used when unmapping memory. + * @sa svcControlMemory + */ +Result svcControlMemoryUnsafe(u32 *out, u32 addr0, u32 size, MemOp op, MemPerm perm); ///@} ///@name System @@ -134,4 +154,31 @@ 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 + ///< s32 count = svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0) + ///< Returns how many handles were found + + PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access (in the mmu table only) + ///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0) + + PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, ///< Get the handle of an event which will be signaled each time the memory layout of this process changes + ///< svcControlProcess(handle, PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, &eventHandleOut, 0) + + PROCESSOP_SIGNAL_ON_EXIT, ///< Set a flag to be signaled when the process will be exited + ///< svcControlProcess(handle, PROCESSOP_SIGNAL_ON_EXIT, 0, 0) + PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the VAddr within the process + ///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&PAOut, VAddr) + + PROCESSOP_SCHEDULE_THREADS, ///< Lock / Unlock the process's threads + ///< svcControlProcess(handle, PROCESSOP_SCHEDULE_THREADS, lock, threadPredicate) + ///< lock: 0 to unlock threads, any other value to lock threads + ///< threadPredicate: can be NULL or a funcptr to a predicate (typedef bool (*ThreadPredicate)(KThread *thread);) + ///< The predicate must return true to operate on the thread +} 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 982f4a1d..1b77e47e 100644 --- a/sysmodules/rosalina/include/draw.h +++ b/sysmodules/rosalina/include/draw.h @@ -77,6 +77,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 38fc0c1f..61cafee9 100644 --- a/sysmodules/rosalina/include/gdb/remote_command.h +++ b/sysmodules/rosalina/include/gdb/remote_command.h @@ -12,11 +12,15 @@ #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); GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess); +GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc); +GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetThreadPriority); GDB_DECLARE_QUERY_HANDLER(Rcmd); diff --git a/sysmodules/rosalina/include/input_redirection.h b/sysmodules/rosalina/include/input_redirection.h index 253c2bf9..ad806778 100644 --- a/sysmodules/rosalina/include/input_redirection.h +++ b/sysmodules/rosalina/include/input_redirection.h @@ -38,3 +38,4 @@ MyThread *inputRedirectionCreateThread(void); void inputRedirectionThreadMain(void); Result InputRedirection_Disable(s64 timeout); Result InputRedirection_DoOrUndoPatches(void); + diff --git a/sysmodules/rosalina/include/luma_config.h b/sysmodules/rosalina/include/luma_config.h index ca8afd2a..b289b1b2 100644 --- a/sysmodules/rosalina/include/luma_config.h +++ b/sysmodules/rosalina/include/luma_config.h @@ -56,3 +56,4 @@ enum multiOptions void LumaConfig_ConvertComboToString(char *out, u32 combo); Result LumaConfig_SaveSettings(void); +void LumaConfig_RequestSaveSettings(void); diff --git a/sysmodules/rosalina/include/menu.h b/sysmodules/rosalina/include/menu.h index b4d6dd6d..4656b8b1 100644 --- a/sysmodules/rosalina/include/menu.h +++ b/sysmodules/rosalina/include/menu.h @@ -86,7 +86,10 @@ bool menuCheckN3ds(void); u32 menuCountItems(const Menu *menu); 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/plugin.h b/sysmodules/rosalina/include/plugin.h new file mode 100644 index 00000000..6db9a161 --- /dev/null +++ b/sysmodules/rosalina/include/plugin.h @@ -0,0 +1,5 @@ +#pragma once + +#include "plugin/plgloader.h" +#include "plugin/plgldr.h" +#include "plugin/3gx.h" \ No newline at end of file diff --git a/sysmodules/rosalina/include/plugin/3gx.h b/sysmodules/rosalina/include/plugin/3gx.h new file mode 100644 index 00000000..4b087cbf --- /dev/null +++ b/sysmodules/rosalina/include/plugin/3gx.h @@ -0,0 +1,76 @@ +#pragma once +#include <3ds/types.h> +#include "ifile.h" + +#define _3GX_MAGIC (0x3230303024584733) /* "3GX$0002" */ + +typedef struct PACKED +{ + u32 authorLen; + const char* authorMsg; + u32 titleLen; + const char* titleMsg; + u32 summaryLen; + const char* summaryMsg; + u32 descriptionLen; + const char* descriptionMsg; + union { + u32 flags; + struct { + u32 embeddedExeLoadFunc : 1; + u32 embeddedSwapSaveLoadFunc : 1; + u32 memoryRegionSize : 2; + u32 unused : 28; + }; + }; + u32 exeLoadChecksum; + u32 builtInLoadExeArgs[4]; + u32 builtInSwapSaveLoadArgs[4]; +} _3gx_Infos; + +typedef struct PACKED +{ + u32 count; + u32 * titles; +} _3gx_Targets; + +typedef struct PACKED +{ + u32 nbSymbols; + u32 symbolsOffset; + u32 nameTableOffset; +} _3gx_Symtable; + +typedef struct PACKED +{ + u32 codeOffset; + u32 rodataOffset; + u32 dataOffset; + u32 codeSize; + u32 rodataSize; + u32 dataSize; + u32 bssSize; + u32 exeLoadFuncOffset; // NOP terminated + u32 swapSaveFuncOffset; // NOP terminated + u32 swapLoadFuncOffset; // NOP terminated +} _3gx_Executable; + +typedef struct PACKED +{ + u64 magic; + u32 version; + u32 reserved; + _3gx_Infos infos; + _3gx_Executable executable; + _3gx_Targets targets; + _3gx_Symtable symtable; +} _3gx_Header; + + +Result Check_3gx_Magic(IFile *file); +Result Read_3gx_Header(IFile *file, _3gx_Header *header); +Result Read_3gx_ParseHeader(IFile *file, _3gx_Header *header); +Result Read_3gx_LoadSegments(IFile *file, _3gx_Header *header, void *dst); +Result Read_3gx_EmbeddedPayloads(IFile *file, _3gx_Header *header); +Result Set_3gx_LoadParams(u32* loadFunc, u32* params); +void Reset_3gx_LoadParams(void); \ No newline at end of file diff --git a/sysmodules/rosalina/include/plugin/plgldr.h b/sysmodules/rosalina/include/plugin/plgldr.h new file mode 100644 index 00000000..c0b45e4d --- /dev/null +++ b/sysmodules/rosalina/include/plugin/plgldr.h @@ -0,0 +1,78 @@ +#pragma once + +#include <3ds/types.h> + +#define MAX_BUFFER (50) +#define MAX_ITEMS_COUNT (64) + +#define HeaderMagic (0x24584733) /* "3GX$" */ + +typedef struct +{ + bool noFlash; + u8 pluginMemoryStrategy; + 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; + +typedef enum +{ + PLG_WAIT = -1, + PLG_OK = 0, + PLG_SLEEP_ENTRY = 1, + PLG_SLEEP_EXIT = 2, + PLG_ABOUT_TO_SWAP = 3, + PLG_ABOUT_TO_EXIT = 4, + PLG_HOME_ENTER = 5, + PLG_HOME_EXIT = 6, +} PLG_Event; + +typedef enum +{ + PLG_STRATEGY_NONE = 2, + PLG_STRATEGY_SWAP = 0, + PLG_STRATEGY_MODE3 = 1 +} PluginMemoryStrategy; + +typedef struct +{ + u32 magic; + u32 version; + u32 heapVA; + u32 heapSize; + u32 exeSize; // Include sizeof(PluginHeader) + .text + .rodata + .data + .bss (0x1000 aligned too) + u32 isDefaultPlugin; + s32* plgldrEvent; ///< Used for synchronization + s32* plgldrReply; ///< Used for synchronization + u8 notifyHomeEvent; + u8 padding[3]; + u32 reserved[23]; + u32 config[32]; +} PluginHeader; + +typedef void (*OnPlgLdrEventCb_t)(s32 eventType); + +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); +Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock); +Result PLGLDR__SetSwapSettings(char* swapPath, void* saveFunc, void* loadFunc, void* args); +Result PLGLDR__SetExeLoadSettings(void* loadFunc, void* args); +Result PLGLDR__GetVersion(u32 *version); +void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb); +void PLGLDR__Status(void); diff --git a/sysmodules/rosalina/include/plugin/plgloader.h b/sysmodules/rosalina/include/plugin/plgloader.h new file mode 100644 index 00000000..ff5a2c8f --- /dev/null +++ b/sysmodules/rosalina/include/plugin/plgloader.h @@ -0,0 +1,105 @@ +#pragma once + +#include <3ds/types.h> +#include "MyThread.h" +#include "plgldr.h" +#include "utils.h" + +void PluginLoader__Init(void); +bool PluginLoader__IsEnabled(void); +void PluginLoader__MenuCallback(void); +void PluginLoader__UpdateMenu(void); +void PluginLoader__HandleKernelEvent(u32 notifId); +void PluginLoader__HandleCommands(void *ctx); + +void PluginLoader__Error(const char *message, Result res); + +Result MemoryBlock__SetSize(u32 size); +Result MemoryBlock__IsReady(void); +Result MemoryBlock__Free(void); +Result MemoryBlock__ToSwapFile(void); +Result MemoryBlock__FromSwapFile(void); +Result MemoryBlock__MountInProcess(void); +Result MemoryBlock__UnmountFromProcess(void); +Result MemoryBlock__SetSwapSettings(u32* func, bool isDec, u32* params); +void MemoryBlock__ResetSwapSettings(void); +PluginHeader* MemoryBlock__GetMappedPluginHeader(); + +extern u32 g_loadSaveSwapArgs[0x4]; +extern u32 g_loadExeArgs[0x4]; +extern char g_swapFileName[256]; +extern u32 g_memBlockSize; + +u32 saveSwapFunc(void* startAddr, void* endAddr, void* args); +u32 loadSwapFunc(void* startAddr, void* endAddr, void* args); +u32 loadExeFunc(void* startAddr, void* endAddr, void* args); + +bool TryToLoadPlugin(Handle process); +void PLG__NotifyEvent(PLG_Event event, bool signal); +void PLG__SetConfigMemoryStatus(u32 status); +u32 PLG__GetConfigMemoryStatus(void); +u32 PLG__GetConfigMemoryEvent(void); + + +typedef enum +{ + PLG_CFG_NONE = 0, + PLG_CFG_RUNNING = 1, + PLG_CFG_INHOME = 2, + PLG_CFG_EXITING = 3, + + PLG_CFG_HOME_EVENT = 1 << 16, + PLG_CFG_EXIT_EVENT = 2 << 16 +} PLG_CFG_STATUS; + +typedef struct +{ + bool isReady; + bool isAppRegion; + u8 * memblock; +} MemoryBlock; + +typedef struct +{ + Result code; + const char * message; +} Error; + +typedef struct +{ + bool isEnabled; + bool pluginIsSwapped; + bool pluginIsHome; + const char * pluginPath; + MemoryBlock memblock; + Error error; + PluginHeader header; + Handle target; + Handle arbiter; + Handle kernelEvent; + + bool useUserLoadParameters; + PluginLoadParameters userLoadParameters; + + s32 plgEvent; + s32 plgReply; + s32 * plgEventPA; + s32 * plgReplyPA; + + bool isExeLoadFunctionset; + bool isSwapFunctionset; + u8 pluginMemoryStrategy; + u32 exeLoadChecksum; + u32 swapLoadChecksum; +} PluginLoaderContext; + +extern PluginLoaderContext PluginLoaderCtx; + +// Used by the custom loader command 0x101 (ControlApplicationMemoryModeOverride) +typedef struct ControlApplicationMemoryModeOverrideConfig { + u32 query : 1; //< Only query the current configuration, do not update it. + u32 enable_o3ds : 1; //< Enable o3ds memory mode override + u32 enable_n3ds : 1; //< Enable n3ds memory mode override + u32 o3ds_mode : 3; //< O3ds memory mode + u32 n3ds_mode : 3; //< N3ds memory mode +} ControlApplicationMemoryModeOverrideConfig; \ No newline at end of file diff --git a/sysmodules/rosalina/include/sleep.h b/sysmodules/rosalina/include/sleep.h new file mode 100644 index 00000000..521ae029 --- /dev/null +++ b/sysmodules/rosalina/include/sleep.h @@ -0,0 +1,31 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2018 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#pragma once + +void Sleep__Init(void); +void Sleep__HandleNotification(u32 notifId); +bool Sleep__Status(void); diff --git a/sysmodules/rosalina/include/sock_util.h b/sysmodules/rosalina/include/sock_util.h index dd0d0b7c..ed33866e 100644 --- a/sysmodules/rosalina/include/sock_util.h +++ b/sysmodules/rosalina/include/sock_util.h @@ -77,3 +77,4 @@ void server_run(struct sock_server *serv); void server_kill_connections(struct sock_server *serv); void server_set_should_close_all(struct sock_server *serv); void server_finalize(struct sock_server *serv); +bool Wifi__IsConnected(void); \ No newline at end of file diff --git a/sysmodules/rosalina/include/utils.h b/sysmodules/rosalina/include/utils.h index f531d90b..d9e77394 100644 --- a/sysmodules/rosalina/include/utils.h +++ b/sysmodules/rosalina/include/utils.h @@ -29,6 +29,7 @@ #include <3ds/svc.h> #include <3ds/srv.h> #include <3ds/result.h> +#include <3ds/ipc.h> #include "csvc.h" #include "luma_shared_config.h" @@ -60,6 +61,24 @@ 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); +extern bool saveSettingsRequest; +void RequestSaveSettings(void); static inline bool isServiceUsable(const char *name) { bool r; diff --git a/sysmodules/rosalina/source/csvc.s b/sysmodules/rosalina/source/csvc.s index 658992f3..da698753 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 @@ -79,6 +82,19 @@ SVC_BEGIN svcControlMemoryEx bx lr SVC_END +SVC_BEGIN svcControlMemoryUnsafe + str r4, [sp, #-4]! + ldr r4, [sp, #4] + svc 0xA3 + ldr r4, [sp], #4 + bx lr +SVC_END + +SVC_BEGIN svcFreeMemory + svc 0xA3 + bx lr +SVC_END + SVC_BEGIN svcControlService svc 0xB0 bx lr @@ -99,3 +115,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 e1bd01de..c1e2294e 100644 --- a/sysmodules/rosalina/source/errdisp.c +++ b/sysmodules/rosalina/source/errdisp.c @@ -34,12 +34,6 @@ extern Handle preTerminationEvent; -static inline void assertSuccess(Result res) -{ - if(R_FAILED(res)) - svcBreak(USERBREAK_PANIC); -} - static MyThread errDispThread; static u8 ALIGN(8) errDispThreadStack[0xD00]; @@ -440,4 +434,4 @@ void errDispThreadMain(void) svcCloseHandle(sessionHandle); svcCloseHandle(clientHandle); svcCloseHandle(serverHandle); -} +} \ No newline at end of file diff --git a/sysmodules/rosalina/source/gdb/remote_command.c b/sysmodules/rosalina/source/gdb/remote_command.c index 11270d74..39691c0d 100644 --- a/sysmodules/rosalina/source/gdb/remote_command.c +++ b/sysmodules/rosalina/source/gdb/remote_command.c @@ -10,6 +10,7 @@ #include "csvc.h" #include "fmt.h" #include "gdb/breakpoints.h" +#include "utils.h" #include "../utils.h" @@ -19,12 +20,16 @@ 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) }, { "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) }, + { "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) }, + { "getthreadpriority" , GDB_REMOTE_COMMAND_HANDLER(GetThreadPriority)} }; static const char *GDB_SkipSpaces(const char *pos) @@ -34,6 +39,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: %08lX\n", r); + goto end; + } + } + + n = sprintf(outbuf, "va: 0x%08lX, pa: 0x%08lX, b31: 0x%08lX\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]; @@ -118,6 +167,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; @@ -126,10 +198,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) @@ -161,12 +234,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%08lx /* %s handle, %lu %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%08lx /* process: %s, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references"); + } else - n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %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%08lx /* %lu %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf); + } end: svcCloseHandle(handle); @@ -174,6 +264,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: %08lX\n", count); + else if (count == 0) + n = sprintf(outbuf, "Process has no handles ?\n"); + else + { + n = sprintf(outbuf, "Found %ld 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%08lX", 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) { @@ -249,6 +401,53 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) return GDB_SendHexPacket(ctx, outbuf, n); } +GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc) +{ + if(ctx->commandData[0] == '0') + { + memset(ctx->svcMask, 0, 32); + return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM); + } + else if(ctx->commandData[0] == '1') + { + if(ctx->commandData[1] == ';') + { + u32 id; + const char *pos = ctx->commandData + 1; + memset(ctx->svcMask, 0, 32); + + do + { + pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';'); + if(pos == NULL) + return GDB_ReplyErrno(ctx, EILSEQ); + + if(id < 0xFE) + ctx->svcMask[id / 32] |= 1 << (31 - (id % 32)); + } + while(*pos != 0); + } + else + memset(ctx->svcMask, 0xFF, 32); + + return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM); + } + else + return GDB_ReplyErrno(ctx, EILSEQ); +} + +s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId); +GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetThreadPriority) +{ + int n; + char outbuf[GDB_BUF_LEN / 2 + 1]; + + n = sprintf(outbuf, "Thread (%ld) priority: 0x%02lX\n", ctx->selectedThreadId, + GDB_GetDynamicThreadPriority(ctx, ctx->selectedThreadId)); + + return GDB_SendHexPacket(ctx, outbuf, n); +} + GDB_DECLARE_QUERY_HANDLER(Rcmd) { char commandData[GDB_BUF_LEN / 2 + 1]; diff --git a/sysmodules/rosalina/source/gdb/thread.c b/sysmodules/rosalina/source/gdb/thread.c index b7ed5632..97e4eaeb 100644 --- a/sysmodules/rosalina/source/gdb/thread.c +++ b/sysmodules/rosalina/source/gdb/thread.c @@ -107,7 +107,7 @@ int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid) return sprintf(outbuf, "%lx", tid); } -static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId) +s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId) { Handle process, thread; Result r; diff --git a/sysmodules/rosalina/source/input_redirection.c b/sysmodules/rosalina/source/input_redirection.c index d3293914..02530217 100644 --- a/sysmodules/rosalina/source/input_redirection.c +++ b/sysmodules/rosalina/source/input_redirection.c @@ -32,6 +32,8 @@ #include "process_patches.h" #include "menus.h" #include "memory.h" +#include "sleep.h" +#include "sock_util.h" bool inputRedirectionEnabled = false; Handle inputRedirectionThreadStartedEvent; @@ -121,6 +123,13 @@ void inputRedirectionThreadMain(void) pfd.events = POLLIN; pfd.revents = 0; + if (Sleep__Status()) + { + while (!Wifi__IsConnected() + && inputRedirectionEnabled && !preTerminationRequested) + svcSleepThread(1000000000ULL); + } + int pollres = socPoll(&pfd, 1, 10); if(pollres > 0 && (pfd.revents & POLLIN)) { @@ -214,7 +223,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc 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) && !patchPrepared) { @@ -238,7 +247,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc 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 -1; } @@ -248,7 +257,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld)); if(off2 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -2; } } @@ -256,7 +265,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode)); if(off3 == NULL) { - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return -3; } @@ -315,7 +324,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc } svcInvalidateEntireInstructionCache(); - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return res; } @@ -347,28 +356,28 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat 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) && !patchPrepared) { 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; } @@ -404,7 +413,7 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat } } - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return res; } diff --git a/sysmodules/rosalina/source/luma_config.c b/sysmodules/rosalina/source/luma_config.c index b5b419dc..40364c80 100644 --- a/sysmodules/rosalina/source/luma_config.c +++ b/sysmodules/rosalina/source/luma_config.c @@ -32,6 +32,7 @@ #include "config_template_ini.h" #include "ifile.h" #include "menus/miscellaneous.h" +#include "plugin/plgloader.h" typedef struct CfgData { u16 formatVersionMajor, formatVersionMinor; @@ -41,6 +42,7 @@ typedef struct CfgData { u64 hbldr3dsxTitleId; u32 rosalinaMenuCombo; + u32 pluginLoaderFlags; s16 ntpTzOffetMinutes; ScreenFilter topScreenFilter; @@ -50,6 +52,8 @@ typedef struct CfgData { u8 autobootCtrAppmemtype; } CfgData; +bool saveSettingsRequest = false; + void LumaConfig_ConvertComboToString(char *out, u32 combo) { static const char *keys[] = { @@ -170,7 +174,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg) pinNumDigits, n3dsCpuStr, autobootModeStr, - cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, + cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1), (int)cfg->ntpTzOffetMinutes, (int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct, @@ -190,6 +194,10 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg) return n < 0 ? 0 : (size_t)n; } +void LumaConfig_RequestSaveSettings(void) { + saveSettingsRequest = true; +} + Result LumaConfig_SaveSettings(void) { char inibuf[0x2000]; @@ -238,6 +246,7 @@ Result LumaConfig_SaveSettings(void) configData.splashDurationMsec = splashDurationMsec; configData.hbldr3dsxTitleId = Luma_SharedConfig->selected_hbldr_3dsx_tid; configData.rosalinaMenuCombo = menuCombo; + configData.pluginLoaderFlags = PluginLoader__IsEnabled(); configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset; configData.topScreenFilter = topScreenFilter; configData.bottomScreenFilter = bottomScreenFilter; diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c index d61b7408..b170ed34 100644 --- a/sysmodules/rosalina/source/main.c +++ b/sysmodules/rosalina/source/main.c @@ -30,6 +30,7 @@ #include "service_manager.h" #include "errdisp.h" #include "utils.h" +#include "sleep.h" #include "MyThread.h" #include "menus/miscellaneous.h" #include "menus/debugger.h" @@ -43,6 +44,7 @@ #include "shell.h" #include "task_runner.h" +#include "plugin.h" bool isN3DS; @@ -63,6 +65,7 @@ void __wrap_exit(int rc) // Kernel will take care of it all /* pmDbgExit(); + acExit(); fsExit(); svcCloseHandle(*fsRegGetSessionHandle()); srvExit(); @@ -161,6 +164,9 @@ static void handleSleepNotification(u32 notificationId) static void handleShellNotification(u32 notificationId) { + // Quick dirty fix + Sleep__HandleNotification(notificationId); + if (notificationId == 0x213) { // Shell opened // Note that this notification is also fired on system init. @@ -223,6 +229,7 @@ static void handleRestartHbAppNotification(u32 notificationId) #endif static const ServiceManagerServiceEntry services[] = { + { "plg:ldr", 1, PluginLoader__HandleCommands, true }, { NULL }, }; @@ -239,11 +246,16 @@ static const ServiceManagerNotificationEntry notifications[] = { { 0x214, handleShellNotification }, { 0x1000, handleNextApplicationDebuggedByForce }, { 0x2000, handlePreTermNotification }, + { 0x1001, PluginLoader__HandleKernelEvent }, { 0x000, NULL }, }; +// Some changes to commit int main(void) { + Sleep__Init(); + PluginLoader__Init(); + if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY))) svcBreak(USERBREAK_ASSERT); diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index f3aafe0b..f61fa446 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -32,9 +32,11 @@ #include "ifile.h" #include "menus.h" #include "utils.h" +#include "luma_config.h" #include "menus/n3ds.h" #include "menus/cheats.h" #include "minisoc.h" +#include "plugin.h" #include "menus/screen_filters.h" #include "shell.h" @@ -257,6 +259,9 @@ MyThread *menuCreateThread(void) return &menuThread; } +u32 menuCombo; +u32 g_blockMenuOpen = 0; + void menuThreadMain(void) { if(isN3DS) @@ -279,13 +284,19 @@ void menuThreadMain(void) Cheat_ApplyCheats(); - if((scanHeldKeys() & menuCombo) == menuCombo) + if(((scanHeldKeys() & menuCombo) == menuCombo) && !g_blockMenuOpen) { menuEnter(); if(isN3DS) N3DSMenu_UpdateStatus(); + PluginLoader__UpdateMenu(); menuShow(&rosalinaMenu); menuLeave(); } + + if (saveSettingsRequest) { + LumaConfig_SaveSettings(); + saveSettingsRequest = false; + } } } diff --git a/sysmodules/rosalina/source/menus.c b/sysmodules/rosalina/source/menus.c index 48e627c3..2d815433 100644 --- a/sysmodules/rosalina/source/menus.c +++ b/sysmodules/rosalina/source/menus.c @@ -35,6 +35,7 @@ #include "menus/miscellaneous.h" #include "menus/sysconfig.h" #include "menus/screen_filters.h" +#include "plugin.h" #include "ifile.h" #include "memory.h" #include "fmt.h" @@ -48,6 +49,7 @@ Menu rosalinaMenu = { { "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot }, { "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness }, { "Cheats...", METHOD, .method = &RosalinaMenu_Cheats }, + { "", METHOD, .method = PluginLoader__MenuCallback}, { "Process list", METHOD, .method = &RosalinaMenu_ProcessList }, { "Debugger options...", MENU, .menu = &debuggerMenu }, { "System configuration...", MENU, .menu = &sysconfigMenu }, diff --git a/sysmodules/rosalina/source/menus/miscellaneous.c b/sysmodules/rosalina/source/menus/miscellaneous.c index 534e2c2d..5ca1a489 100644 --- a/sysmodules/rosalina/source/menus/miscellaneous.c +++ b/sysmodules/rosalina/source/menus/miscellaneous.c @@ -36,6 +36,7 @@ #include "minisoc.h" #include "ifile.h" #include "pmdbgext.h" +#include "plugin.h" #include "process_patches.h" typedef struct DspFirmSegmentHeader { diff --git a/sysmodules/rosalina/source/menus/process_list.c b/sysmodules/rosalina/source/menus/process_list.c index 03548c91..cbe08017 100644 --- a/sysmodules/rosalina/source/menus/process_list.c +++ b/sysmodules/rosalina/source/menus/process_list.c @@ -245,8 +245,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); @@ -613,9 +613,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/plugin/3gx.c b/sysmodules/rosalina/source/plugin/3gx.c new file mode 100644 index 00000000..57369868 --- /dev/null +++ b/sysmodules/rosalina/source/plugin/3gx.c @@ -0,0 +1,173 @@ +#include <3ds.h> +#include +#include +#include "plugin.h" +#include "ifile.h" +#include "utils.h" + +u32 g_loadExeArgs[0x4]; + +static inline u32 invertEndianness(u32 val) +{ + return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val & 0xFF0000) >> 8) | ((val & 0xFF000000) >> 24); +} + +Result Check_3gx_Magic(IFile *file) +{ + u64 magic; + u64 total; + Result res; + int verDif; + + file->pos = 0; + if (R_FAILED((res = IFile_Read(file, &total, &magic, sizeof(u64))))) + return res; + + if ((u32)magic != (u32)_3GX_MAGIC) //Invalid file type + return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, 1); + + else if ((verDif = invertEndianness((u32)(magic >> 32)) - invertEndianness((u32)(_3GX_MAGIC >> 32))) != 0) //Invalid plugin version (2 -> outdated plugin; 3 -> outdated loader) + return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, (verDif < 0) ? 2 : 3); + + else return 0; +} + +Result Read_3gx_Header(IFile *file, _3gx_Header *header) +{ + u64 total; + Result res = 0; + + file->pos = 0; + res = IFile_Read(file, &total, header, sizeof(_3gx_Header)); + + return res; +} + +Result Read_3gx_ParseHeader(IFile *file, _3gx_Header *header) +{ + u64 total; + char * dst; + Result res = 0; + + // 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_LoadSegments(IFile *file, _3gx_Header *header, void *dst) +{ + u32 size; + u64 total; + Result res = 0; + _3gx_Executable *exeHdr = &header->executable; + PluginLoaderContext *ctx = &PluginLoaderCtx; + + file->pos = exeHdr->codeOffset; + size = exeHdr->codeSize + exeHdr->rodataSize + exeHdr->dataSize; + res = IFile_Read(file, &total, dst, size); + + + if (!res && !ctx->isExeLoadFunctionset) return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_NO_DATA); + u32 checksum = 0; + if (!res) checksum = loadExeFunc(dst, dst + size, g_loadExeArgs); + if (!res && checksum != ctx->exeLoadChecksum) res = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS); + Reset_3gx_LoadParams(); + return res; +} + +Result Read_3gx_EmbeddedPayloads(IFile *file, _3gx_Header *header) +{ + u32 tempBuff[32]; + u32 tempBuff2[4]; + u64 total; + Result res = 0; + PluginLoaderContext *ctx = &PluginLoaderCtx; + + if (header->infos.embeddedExeLoadFunc) { + file->pos = header->executable.exeLoadFuncOffset; + res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff)); + memcpy(tempBuff2, header->infos.builtInLoadExeArgs, sizeof(tempBuff2)); + if (!res) res = Set_3gx_LoadParams(tempBuff, tempBuff2); + if (!res) ctx->isExeLoadFunctionset = true; + } + if (!res && header->infos.embeddedSwapSaveLoadFunc) { + file->pos = header->executable.swapSaveFuncOffset; + res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff)); + memcpy(tempBuff2, header->infos.builtInSwapSaveLoadArgs, sizeof(tempBuff2)); + if (!res) res = MemoryBlock__SetSwapSettings(tempBuff, false, tempBuff2); + file->pos = header->executable.swapLoadFuncOffset; + if (!res) res = IFile_Read(file, &total, tempBuff, sizeof(tempBuff)); + if (!res) res = MemoryBlock__SetSwapSettings(tempBuff, true, tempBuff2); + if (!res) ctx->isSwapFunctionset = true; + } + return res; +} + +Result Set_3gx_LoadParams(u32* loadFunc, u32* params) +{ + u32* loadExeFuncAddr = PA_FROM_VA_PTR((u32)loadExeFunc); //Bypass mem permissions + + memcpy(g_loadExeArgs, params, sizeof(g_loadExeArgs)); + + int i = 0; + for (; i < 32 && loadFunc[i] != 0xE320F000; i++) + loadExeFuncAddr[i] = loadFunc[i]; + + if (i >= 32) { + return -1; + } + return 0; +} + +void Reset_3gx_LoadParams(void) +{ + PluginLoaderContext *ctx = &PluginLoaderCtx; + u32* loadExeFuncAddr = PA_FROM_VA_PTR((u32)loadExeFunc); //Bypass mem permissions + + memset(g_loadExeArgs, 0, sizeof(g_loadExeArgs)); + + loadExeFuncAddr[0] = 0xE12FFF1E; // BX LR + + for (int i = 1; i < 32; i++) { + loadExeFuncAddr[i] = 0xE320F000; // NOP + } + + ctx->isExeLoadFunctionset = false; + + svcInvalidateEntireInstructionCache(); +} diff --git a/sysmodules/rosalina/source/plugin/display.c b/sysmodules/rosalina/source/plugin/display.c new file mode 100644 index 00000000..5b954c69 --- /dev/null +++ b/sysmodules/rosalina/source/plugin/display.c @@ -0,0 +1,130 @@ +#include <3ds.h> +#include "menu.h" +#include "draw.h" +#include + +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 (!preTerminationRequested && !(keys & KEY_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%08lX", 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 (!preTerminationRequested && !(keys & KEY_B)); + + Draw_Unlock(); ///< Keep it locked until we exit the message + menuLeave(); + return error; +} + +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][0]) + Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]); + } + + // Wait for input + u32 pressed = waitInput(); + + if (pressed & KEY_A) + states[cursor] = !states[cursor]; + + if (pressed & KEY_B) + break; + + if (pressed & KEY_DOWN) + if (++cursor >= nbItems) + cursor = 0; + + if (pressed & KEY_UP) + if (--cursor >= nbItems) + cursor = nbItems - 1; + + } while (true); + + Draw_Unlock(); + menuLeave(); +} diff --git a/sysmodules/rosalina/source/plugin/file_loader.c b/sysmodules/rosalina/source/plugin/file_loader.c new file mode 100644 index 00000000..6d38dcfe --- /dev/null +++ b/sysmodules/rosalina/source/plugin/file_loader.c @@ -0,0 +1,309 @@ +#include <3ds.h> +#include +#include +#include "csvc.h" +#include "plugin.h" +#include "ifile.h" +#include "ifile.h" +#include "utils.h" + +// Use a global to avoid stack overflow, those structs are quite heavy +static FS_DirectoryEntry g_entries[10]; + +static char g_path[256]; +static const char *g_dirPath = "/luma/plugins/%016llX"; +static const char *g_defaultPath = "/luma/plugins/default.3gx"; + +// pluginLoader.s +void gamePatchFunc(void); + +static u32 strlen16(const u16 *str) +{ + if (!str) return 0; + + const u16 *strEnd = str; + + while (*strEnd) ++strEnd; + + return strEnd - str; +} + +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 = g_entries; + + memset(entries, 0, sizeof(g_entries)); + sprintf(g_path, g_dirPath, 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); + PluginLoaderCtx.pluginPath = 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 OpenPluginFile(u64 tid, IFile *plugin) +{ + if (R_FAILED(FindPluginFile(tid)) || OpenFile(plugin, g_path)) + { + // Try to open default plugin + if (OpenFile(plugin, g_defaultPath)) + return -1; + + PluginLoaderCtx.pluginPath = g_defaultPath; + PluginLoaderCtx.header.isDefaultPlugin = 1; + return 0; + } + + return 0; +} + +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); + + PluginLoaderCtx.error.message = errorBuf; + + return -1; +} + +bool TryToLoadPlugin(Handle process) +{ + u64 tid; + u64 fileSize; + IFile plugin; + Result res; + _3gx_Header fileHeader; + _3gx_Header *header = NULL; + _3gx_Executable *exeHdr = NULL; + PluginLoaderContext *ctx = &PluginLoaderCtx; + PluginHeader *pluginHeader = &ctx->header; + const u32 memRegionSizes[] = + { + 5 * 1024 * 1024, // 5 MiB + 2 * 1024 * 1024, // 2 MiB + 10 * 1024 * 1024, // 10 MiB + 5 * 1024 * 1024, // 5 MiB (Reserved) + }; + + // Get title id + svcGetProcessInfo((s64 *)&tid, process, 0x10001); + + memset(pluginHeader, 0, sizeof(PluginHeader)); + pluginHeader->magic = HeaderMagic; + + // Try to open plugin file + if (ctx->useUserLoadParameters && (u32)tid == ctx->userLoadParameters.lowTitleId) + { + ctx->useUserLoadParameters = false; + ctx->pluginMemoryStrategy = ctx->userLoadParameters.pluginMemoryStrategy; + if (OpenFile(&plugin, ctx->userLoadParameters.path)) + return false; + + ctx->pluginPath = ctx->userLoadParameters.path; + + memcpy(pluginHeader->config, ctx->userLoadParameters.config, 32 * sizeof(u32)); + } + else + { + if (R_FAILED(OpenPluginFile(tid, &plugin))) + return false; + } + + if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize)))) + ctx->error.message = "Couldn't get file size"; + + if (!res && R_FAILED(res = Check_3gx_Magic(&plugin))) + { + const char * errors[] = + { + "Couldn't read file.", + "Invalid plugin file\nNot a valid 3GX plugin format!", + "Outdated plugin file\nCheck for an updated plugin.", + "Outdated plugin loader\nCheck for Luma3DS updates." + }; + + ctx->error.message = errors[R_MODULE(res) == RM_LDR ? R_DESCRIPTION(res) : 0]; + } + + // Read header + if (!res && R_FAILED((res = Read_3gx_Header(&plugin, &fileHeader)))) + ctx->error.message = "Couldn't read file"; + + // Set memory region size according to header + if (!res && R_FAILED((res = MemoryBlock__SetSize(memRegionSizes[fileHeader.infos.memoryRegionSize])))) { + ctx->error.message = "Couldn't set memblock size."; + } + + // Ensure memory block is mounted + if (!res && R_FAILED((res = MemoryBlock__IsReady()))) + ctx->error.message = "Failed to allocate memory."; + + // Plugins will not exceed 5MB so this is fine + if (!res) { + header = (_3gx_Header *)(ctx->memblock.memblock + g_memBlockSize - (u32)fileSize); + memcpy(header, &fileHeader, sizeof(_3gx_Header)); + } + + // Parse rest of header + if (!res && R_FAILED((res = Read_3gx_ParseHeader(&plugin, header)))) + ctx->error.message = "Couldn't read file"; + + // Read embedded save/load functions + if (!res && R_FAILED((res = Read_3gx_EmbeddedPayloads(&plugin, header)))) + ctx->error.message = "Invalid save/load payloads."; + + // Save exe checksum + if (!res) + ctx->exeLoadChecksum = header->infos.exeLoadChecksum; + + // Check titles compatibility + if (!res) res = CheckPluginCompatibility(header, (u32)tid); + + // Read code + if (!res && R_FAILED(res = Read_3gx_LoadSegments(&plugin, header, ctx->memblock.memblock + sizeof(PluginHeader)))) { + if (res == MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_NO_DATA)) ctx->error.message = "This plugin requires a loading function."; + else if (res == MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS)) ctx->error.message = "This plugin file is corrupted."; + else ctx->error.message = "Couldn't read plugin's code"; + } + + if (R_FAILED(res)) + { + ctx->error.code = res; + goto exitFail; + } + + pluginHeader->version = header->version; + // Code size must be page aligned + exeHdr = &header->executable; + pluginHeader->exeSize = (sizeof(PluginHeader) + exeHdr->codeSize + exeHdr->rodataSize + exeHdr->dataSize + exeHdr->bssSize + 0x1000) & ~0xFFF; + pluginHeader->heapVA = 0x06000000; + pluginHeader->heapSize = g_memBlockSize - pluginHeader->exeSize; + pluginHeader->plgldrEvent = ctx->plgEventPA; + pluginHeader->plgldrReply = ctx->plgReplyPA; + + // Clear old event data + PLG__NotifyEvent(PLG_OK, false); + + // Copy header to memblock + memcpy(ctx->memblock.memblock, pluginHeader, sizeof(PluginHeader)); + // Clear heap + memset(ctx->memblock.memblock + pluginHeader->exeSize, 0, pluginHeader->heapSize); + + // Enforce RWX mmu mapping + svcControlProcess(process, PROCESSOP_SET_MMU_TO_RWX, 0, 0); + // Ask the kernel to signal when the process is about to be terminated + svcControlProcess(process, PROCESSOP_SIGNAL_ON_EXIT, 0, 0); + + // Mount the plugin memory in the process + if (R_SUCCEEDED(MemoryBlock__MountInProcess())) + // Install hook + { + u32 procStart = 0x00100000; + u32 *game = (u32 *)procStart; + + extern u32 g_savedGameInstr[2]; + + if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000)))) + { + ctx->error.message = "Couldn't map process"; + ctx->error.code = res; + goto exitFail; + } + + 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); + svcFlushEntireDataCache(); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000); + } + else + goto exitFail; + + + IFile_Close(&plugin); + return true; + +exitFail: + IFile_Close(&plugin); + MemoryBlock__Free(); + + return false; +} diff --git a/sysmodules/rosalina/source/plugin/memoryblock.c b/sysmodules/rosalina/source/plugin/memoryblock.c new file mode 100644 index 00000000..b541535d --- /dev/null +++ b/sysmodules/rosalina/source/plugin/memoryblock.c @@ -0,0 +1,281 @@ +#include <3ds.h> +#include +#include +#include "plugin.h" +#include "ifile.h" +#include "utils.h" + +#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE) + +u32 g_loadSaveSwapArgs[4]; +char g_swapFileName[256]; +u32 g_memBlockSize = 5 * 1024 * 1024; + +Result MemoryBlock__SetSize(u32 size) { + PluginLoaderContext *ctx = &PluginLoaderCtx; + MemoryBlock *memblock = &ctx->memblock; + + if (memblock->isReady) + return MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_LDR, RD_ALREADY_INITIALIZED); + + g_memBlockSize = size; + return 0; +} + +Result MemoryBlock__IsReady(void) +{ + PluginLoaderContext *ctx = &PluginLoaderCtx; + MemoryBlock *memblock = &ctx->memblock; + + if (memblock->isReady) + return 0; + + Result res; + + if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3)) + { + s64 appRegionSize = 0; + s64 appRegionUsed = 0; + u32 appRegionFree; + u32 gameReserveSize; + vu32* appMemAllocPtr = (vu32 *)PA_FROM_VA_PTR(0x1FF80040); + u32 appMemAlloc = *appMemAllocPtr; + u32 temp; + + memblock->isAppRegion = true; + + svcGetSystemInfo(&appRegionSize, 0x10000, 0x80); + svcGetSystemInfo(&appRegionUsed, 0, 1); + + appRegionFree = appRegionSize - appRegionUsed; + + // Check if appmemalloc reports the entire available memory + if ((u32)appRegionSize == appMemAlloc) + *appMemAllocPtr -= g_memBlockSize; ///< Remove plugin share from available memory + + gameReserveSize = appRegionFree - g_memBlockSize; + + // First reserve the game memory size (to avoid heap relocation) + res = svcControlMemoryUnsafe((u32 *)&temp, 0x30000000, + gameReserveSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW); + + // Then allocate our plugin memory block + if (R_SUCCEEDED(res)) + res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000, + g_memBlockSize, MEMOP_REGION_APP | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW); + + // Finally release game reserve block + if (R_SUCCEEDED(res)) + res = svcControlMemoryUnsafe((u32 *)&temp, temp, gameReserveSize, MEMOP_FREE, 0); + } + else + { + memblock->isAppRegion = false; + res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, 0x07000000, + g_memBlockSize, MEMOP_REGION_SYSTEM | MEMOP_ALLOC | MEMOP_LINEAR_FLAG, MEMPERM_RW); + } + + if (R_FAILED(res)) { + if (isN3DS || (ctx->pluginMemoryStrategy == PLG_STRATEGY_MODE3)) + PluginLoader__Error("Cannot map plugin memory.", res); + else + PluginLoader__Error("A console reboot is needed to\nclose extended memory games.\n\nPress [B] to reboot.", res); + svcKernelSetState(7); + } + else + { + // Clear the memblock + memset(memblock->memblock, 0, g_memBlockSize); + memblock->isReady = true; + + /*if (isN3DS) + { + // Check if appmemalloc reports the entire available memory + s64 appRegionSize = 0; + vu32* appMemAlloc = (vu32 *)PA_FROM_VA_PTR(0x1FF80040); + + svcGetSystemInfo(&appRegionSize, 0x10000, 0x80); + if ((u32)appRegionSize == *appMemAlloc) + *appMemAlloc -= g_memBlockSize; ///< Remove plugin share from available memory + } */ + } + + return res; +} + +Result MemoryBlock__Free(void) +{ + MemoryBlock *memblock = &PluginLoaderCtx.memblock; + + if (!memblock->isReady) + return 0; + + MemOp memRegion = memblock->isAppRegion ? MEMOP_REGION_APP : MEMOP_REGION_SYSTEM; + Result res = svcControlMemoryUnsafe((u32 *)&memblock->memblock, (u32)memblock->memblock, + g_memBlockSize, memRegion | MEMOP_FREE, 0); + + memblock->isReady = false; + memblock->memblock = NULL; + + if (R_FAILED(res)) + PluginLoader__Error("Couldn't free memblock", res); + + return res; +} + +#define FS_OPEN_RWC (FS_OPEN_READ | FS_OPEN_WRITE | FS_OPEN_CREATE) + +Result MemoryBlock__ToSwapFile(void) +{ + MemoryBlock *memblock = &PluginLoaderCtx.memblock; + PluginLoaderContext *ctx = &PluginLoaderCtx; + + u64 written = 0; + u64 toWrite = g_memBlockSize; + IFile file; + Result res = 0; + + svcFlushDataCacheRange(memblock->memblock, g_memBlockSize); + res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), + fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_RWC); + + if (R_FAILED(res)) { + PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + + if (!ctx->isSwapFunctionset) { + PluginLoader__Error("CRITICAL: Swap save function\nis not set.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + ctx->swapLoadChecksum = saveSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs); + + res = IFile_Write(&file, &written, memblock->memblock, toWrite, FS_WRITE_FLUSH); + + if (R_FAILED(res) || written != toWrite) { + PluginLoader__Error("CRITICAL: Couldn't write swap to SD.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + + IFile_Close(&file); + return res; +} + +Result MemoryBlock__FromSwapFile(void) +{ + MemoryBlock *memblock = &PluginLoaderCtx.memblock; + + u64 read = 0; + u64 toRead = g_memBlockSize; + IFile file; + Result res = 0; + + res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), + fsMakePath(PATH_ASCII, g_swapFileName), FS_OPEN_READ); + + if (R_FAILED(res)) { + PluginLoader__Error("CRITICAL: Failed to open swap file.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + + res = IFile_Read(&file, &read, memblock->memblock, toRead); + + if (R_FAILED(res) || read != toRead) { + PluginLoader__Error("CRITICAL: Couldn't read swap from SD.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + + u32 checksum = loadSwapFunc(memblock->memblock, memblock->memblock + g_memBlockSize, g_loadSaveSwapArgs); + + PluginLoaderContext *ctx = &PluginLoaderCtx; + if (checksum != ctx->swapLoadChecksum) { + res = -1; + PluginLoader__Error("CRITICAL: Swap file is corrupted.\n\nConsole will now reboot.", res); + svcKernelSetState(7); + } + + svcFlushDataCacheRange(memblock->memblock, g_memBlockSize); + IFile_Close(&file); + return res; +} + +Result MemoryBlock__MountInProcess(void) +{ + Handle target = PluginLoaderCtx.target; + Error *error = &PluginLoaderCtx.error; + PluginHeader *header = &PluginLoaderCtx.header; + MemoryBlock *memblock = &PluginLoaderCtx.memblock; + + Result res = 0; + + // Executable + if (R_FAILED((res = svcMapProcessMemoryEx(target, 0x07000000, CUR_PROCESS_HANDLE, (u32)memblock->memblock, header->exeSize)))) + { + error->message = "Couldn't map exe memory block"; + error->code = res; + return res; + } + + // Heap (to be used by the plugin) + if (R_FAILED((res = svcMapProcessMemoryEx(target, header->heapVA, CUR_PROCESS_HANDLE, (u32)memblock->memblock + header->exeSize, header->heapSize)))) + { + error->message = "Couldn't map heap memory block"; + error->code = res; + } + + return res; +} + +Result MemoryBlock__UnmountFromProcess(void) +{ + Handle target = PluginLoaderCtx.target; + PluginHeader *header = &PluginLoaderCtx.header; + + Result res = 0; + + res = svcUnmapProcessMemoryEx(target, 0x07000000, header->exeSize); + res |= svcUnmapProcessMemoryEx(target, header->heapVA, header->heapSize); + + return res; +} + +Result MemoryBlock__SetSwapSettings(u32* func, bool isLoad, u32* params) +{ + u32* physAddr = PA_FROM_VA_PTR(isLoad ? (u32)loadSwapFunc : (u32)saveSwapFunc); //Bypass mem permissions + + memcpy(g_loadSaveSwapArgs, params, sizeof(g_loadSaveSwapArgs)); + + int i = 0; + for (; i < 32 && func[i] != 0xE320F000; i++) + physAddr[i] = func[i]; + + if (i >= 32) { + return -1; + } + return 0; +} + +void MemoryBlock__ResetSwapSettings(void) +{ + u32* savePhysAddr = PA_FROM_VA_PTR((u32)saveSwapFunc); //Bypass mem permissions + u32* loadPhysAddr = PA_FROM_VA_PTR((u32)loadSwapFunc); + PluginLoaderContext *ctx = &PluginLoaderCtx; + + memset(g_loadSaveSwapArgs, 0, sizeof(g_loadSaveSwapArgs)); + + savePhysAddr[0] = loadPhysAddr[0] = 0xE12FFF1E; // BX LR + + for (int i = 1; i < 32; i++) { + savePhysAddr[i] = loadPhysAddr[i] = 0xE320F000; // NOP + } + + strcpy(g_swapFileName, "/luma/plugins/.swap"); + ctx->isSwapFunctionset = false; + + svcInvalidateEntireInstructionCache(); +} + +PluginHeader* MemoryBlock__GetMappedPluginHeader() { + MemoryBlock *memblock = &PluginLoaderCtx.memblock; + return memblock->isReady ? NULL : (PluginHeader*)memblock->memblock; +} \ No newline at end of file diff --git a/sysmodules/rosalina/source/plugin/plgldr.c b/sysmodules/rosalina/source/plugin/plgldr.c new file mode 100644 index 00000000..4228aa84 --- /dev/null +++ b/sysmodules/rosalina/source/plugin/plgldr.c @@ -0,0 +1,339 @@ +#include <3ds.h> +#include "plugin.h" +#include +#include "csvc.h" + +static Handle plgLdrHandle; +static Handle plgLdrArbiter; +static s32* plgEvent; +static s32* plgReply; +static int plgLdrRefCount; +static bool plgIsN3DS; +static OnPlgLdrEventCb_t onPlgLdrEventCb = NULL; + +static Result PLGLDR__GetArbiter(void) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(9, 0, 0); + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + plgLdrArbiter = cmdbuf[3]; + } + return res; +} + +Result plgLdrInit(void) +{ + s64 out = 0; + Result res = 0; + + svcGetSystemInfo(&out, 0x10000, 0x201); + plgIsN3DS = out != 0; + if (AtomicPostIncrement(&plgLdrRefCount) == 0) + res = svcConnectToPort(&plgLdrHandle, "plg:ldr"); + + if (R_SUCCEEDED(res) + && R_SUCCEEDED((res = PLGLDR__GetArbiter()))) + { + PluginHeader *header = (PluginHeader *)0x07000000; + + plgEvent = header->plgldrEvent; + plgReply = header->plgldrReply; + } + else + plgLdrExit(); + + return res; +} + +void plgLdrExit(void) +{ + if (AtomicDecrement(&plgLdrRefCount)) + return; + + if (plgLdrHandle) + svcCloseHandle(plgLdrHandle); + if (plgLdrArbiter) + svcCloseHandle(plgLdrArbiter); + plgLdrHandle = plgLdrArbiter = 0; +} + +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 | (((u32)parameters->pluginMemoryStrategy) << 8); + 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; +} +void Flash(u32 color); +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]; + } + else Flash(0xFF0000); + + 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; +} + +Result PLGLDR__GetVersion(u32 *version) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(8, 0, 0); + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + if (cmdbuf[0] != IPC_MakeHeader(8, 2, 0)) + return 0xD900182F; + + res = cmdbuf[1]; + if (version) + *version = cmdbuf[2]; + } + return res; +} + +Result PLGLDR__GetPluginPath(char *path) +{ + if (path == NULL) + return MAKERESULT(28, 7, 254, 1014); ///< Usage, App, Invalid argument + + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(10, 0, 2); + cmdbuf[1] = IPC_Desc_Buffer(255, IPC_BUFFER_RW); + cmdbuf[2] = (u32)path; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__SetRosalinaMenuBlock(bool shouldBlock) +{ + Result res = 0; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(11, 1, 0); + cmdbuf[1] = (u32)shouldBlock; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__SetSwapSettings(char* swapPath, void* saveFunc, void* loadFunc, void* args) +{ + Result res = 0; + + const char* trueSwapPath; + if (swapPath) trueSwapPath = swapPath; + else trueSwapPath = "\0"; + + u32 buf[4] = { 0 }; + u32* trueArgs; + if (args) trueArgs = args; + else trueArgs = buf; + + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(12, 2, 4); + cmdbuf[1] = (saveFunc) ? svcConvertVAToPA(saveFunc, false) | (1 << 31) : 0; + cmdbuf[2] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0; + cmdbuf[3] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R); + cmdbuf[4] = (u32)trueArgs; + cmdbuf[5] = IPC_Desc_Buffer(strlen(trueSwapPath) + 1, IPC_BUFFER_R); + cmdbuf[6] = (u32)trueSwapPath; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + +Result PLGLDR__SetExeLoadSettings(void* loadFunc, void* args) +{ + Result res = 0; + + u32 buf[4] = { 0 }; + u32* trueArgs; + if (args) trueArgs = args; + else trueArgs = buf; + + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(13, 1, 2); + cmdbuf[1] = (loadFunc) ? svcConvertVAToPA(loadFunc, false) | (1 << 31) : 0; + cmdbuf[2] = IPC_Desc_Buffer(4 * sizeof(u32), IPC_BUFFER_R); + cmdbuf[3] = (u32)trueArgs; + + if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle)))) + { + res = cmdbuf[1]; + } + return res; +} + + +void PLGLDR__SetEventCallback(OnPlgLdrEventCb_t cb) +{ + onPlgLdrEventCb = cb; +} + +static s32 __ldrex__(s32 *addr) +{ + s32 val; + do + val = __ldrex(addr); + while (__strex(addr, val)); + + return val; +} + +static void __strex__(s32 *addr, s32 val) +{ + do + __ldrex(addr); + while (__strex(addr, val)); +} + +void PLGLDR__Status(void) +{ + s32 event = __ldrex__(plgEvent); // exclusive read necessary? + + if (event <= 0) + return; + + if (onPlgLdrEventCb) + onPlgLdrEventCb(event); + + __strex__(plgReply, PLG_OK); + __strex__(plgEvent, PLG_WAIT); + + if (event < PLG_ABOUT_TO_SWAP) + return; + + svcArbitrateAddress(plgLdrArbiter, (u32)plgReply, ARBITRATION_SIGNAL, 1, 0); + if (event == PLG_ABOUT_TO_SWAP) + svcArbitrateAddress(plgLdrArbiter, (u32)plgEvent, ARBITRATION_WAIT_IF_LESS_THAN, PLG_OK, 0); + else if (event == PLG_ABOUT_TO_EXIT) + { + plgLdrExit(); + svcExitThread(); + } +} diff --git a/sysmodules/rosalina/source/plugin/plgloader.c b/sysmodules/rosalina/source/plugin/plgloader.c new file mode 100644 index 00000000..5db5324b --- /dev/null +++ b/sysmodules/rosalina/source/plugin/plgloader.c @@ -0,0 +1,593 @@ +#include <3ds.h> +#include "ifile.h" +#include "utils.h" // for makeARMBranch +#include "luma_config.h" +#include "plugin.h" +#include "fmt.h" +#include "menu.h" +#include "menus.h" +#include "memory.h" +#include "sleep.h" +#include "task_runner.h" + +#define PLGLDR_VERSION (SYSTEM_VERSION(1, 0, 1)) + +#define THREADVARS_MAGIC 0x21545624 // !TV$ + +static const char *g_title = "Plugin loader"; +PluginLoaderContext PluginLoaderCtx; +extern u32 g_blockMenuOpen; + +void IR__Patch(void); +void IR__Unpatch(void); + +void PluginLoader__Init(void) +{ + PluginLoaderContext *ctx = &PluginLoaderCtx; + + memset(ctx, 0, sizeof(PluginLoaderContext)); + + s64 pluginLoaderFlags = 0; + + svcGetSystemInfo(&pluginLoaderFlags, 0x10000, 0x180); + ctx->isEnabled = pluginLoaderFlags & 1; + + ctx->plgEventPA = (s32 *)PA_FROM_VA_PTR(&ctx->plgEvent); + ctx->plgReplyPA = (s32 *)PA_FROM_VA_PTR(&ctx->plgReply); + + ctx->pluginMemoryStrategy = PLG_STRATEGY_SWAP; + + MemoryBlock__ResetSwapSettings(); + + assertSuccess(svcCreateAddressArbiter(&ctx->arbiter)); + assertSuccess(svcCreateEvent(&ctx->kernelEvent, RESET_ONESHOT)); + + svcKernelSetState(0x10007, ctx->kernelEvent, 0, 0); +} + +void PluginLoader__Error(const char *message, Result res) +{ + DispErrMessage(g_title, message, res); +} + +bool PluginLoader__IsEnabled(void) +{ + return PluginLoaderCtx.isEnabled; +} + +void PluginLoader__MenuCallback(void) +{ + PluginLoaderCtx.isEnabled = !PluginLoaderCtx.isEnabled; + LumaConfig_RequestSaveSettings(); + PluginLoader__UpdateMenu(); +} + +void PluginLoader__UpdateMenu(void) +{ + static const char *status[2] = + { + "Plugin Loader: [Disabled]", + "Plugin Loader: [Enabled]" + }; + + rosalinaMenu.items[3].title = status[PluginLoaderCtx.isEnabled]; +} + +static ControlApplicationMemoryModeOverrideConfig g_memorymodeoverridebackup = { 0 }; +Result PluginLoader__SetMode3AppMode(bool enable) +{ + Handle loaderHandle; + Result res = srvGetServiceHandle(&loaderHandle, "Loader"); + + if (R_FAILED(res)) return res; + + u32 *cmdbuf = getThreadCommandBuffer(); + + if (enable) { + ControlApplicationMemoryModeOverrideConfig* mode = (ControlApplicationMemoryModeOverrideConfig*)&cmdbuf[1]; + + memset(mode, 0, sizeof(ControlApplicationMemoryModeOverrideConfig)); + mode->query = true; + cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride + + if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle))) && R_SUCCEEDED(res = cmdbuf[1])) + { + memcpy(&g_memorymodeoverridebackup, &cmdbuf[2], sizeof(ControlApplicationMemoryModeOverrideConfig)); + + memset(mode, 0, sizeof(ControlApplicationMemoryModeOverrideConfig)); + mode->enable_o3ds = true; + mode->o3ds_mode = SYSMODE_DEV2; + cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride + if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle)))) { + res = cmdbuf[1]; + } + } + } else { + ControlApplicationMemoryModeOverrideConfig* mode = (ControlApplicationMemoryModeOverrideConfig*)&cmdbuf[1]; + *mode = g_memorymodeoverridebackup; + cmdbuf[0] = IPC_MakeHeader(0x101, 1, 0); // ControlApplicationMemoryModeOverride + if (R_SUCCEEDED((res = svcSendSyncRequest(loaderHandle)))) { + res = cmdbuf[1]; + } + } + + svcCloseHandle(loaderHandle); + return res; +} +static void j_PluginLoader__SetMode3AppMode(void* arg) {(void)arg; PluginLoader__SetMode3AppMode(false);} + +void CheckMemory(void); + +void PLG__NotifyEvent(PLG_Event event, bool signal); + +void PluginLoader__HandleCommands(void *_ctx) +{ + (void)_ctx; + + u32 *cmdbuf = getThreadCommandBuffer(); + PluginLoaderContext *ctx = &PluginLoaderCtx; + + switch (cmdbuf[0] >> 16) + { + case 1: // Load plugin + { + if (cmdbuf[0] != IPC_MakeHeader(1, 1, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + ctx->plgEvent = PLG_OK; + svcOpenProcess(&ctx->target, cmdbuf[1]); + + if (ctx->useUserLoadParameters && ctx->userLoadParameters.pluginMemoryStrategy == PLG_STRATEGY_MODE3) + TaskRunner_RunTask(j_PluginLoader__SetMode3AppMode, NULL, 0); + + bool flash = !(ctx->useUserLoadParameters && ctx->userLoadParameters.noFlash); + if (ctx->isEnabled && TryToLoadPlugin(ctx->target)) + { + if (flash) + { + // A little flash to notify the user that the plugin is loaded + for (u32 i = 0; i < 64; i++) + { + REG32(0x10202204) = 0x01FF9933; + svcSleepThread(5000000); + } + REG32(0x10202204) = 0; + } + //if (!ctx->userLoadParameters.noIRPatch) + // IR__Patch(); + PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING); + } + else + { + svcCloseHandle(ctx->target); + ctx->target = 0; + } + + 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)ctx->isEnabled; + break; + } + + case 3: // Enable / Disable plugin loader + { + if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + if (cmdbuf[1] != ctx->isEnabled) + { + ctx->isEnabled = cmdbuf[1]; + LumaConfig_RequestSaveSettings(); + PluginLoader__UpdateMenu(); + } + + 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; + } + + PluginLoadParameters *params = &ctx->userLoadParameters; + + ctx->useUserLoadParameters = true; + params->noFlash = cmdbuf[1] & 0xFF; + params->pluginMemoryStrategy = (cmdbuf[1] >> 8) & 0xFF; + params->lowTitleId = cmdbuf[2]; + + strncpy(params->path, (const char *)cmdbuf[4], 255); + memcpy(params->config, (void *)cmdbuf[6], 32 * sizeof(u32)); + + if (params->pluginMemoryStrategy == PLG_STRATEGY_MODE3) + cmdbuf[1] = PluginLoader__SetMode3AppMode(true); + else + cmdbuf[1] = 0; + + cmdbuf[0] = IPC_MakeHeader(4, 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; + } + + case 8: // Get PLGLDR Version + { + if (cmdbuf[0] != IPC_MakeHeader(8, 0, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + cmdbuf[0] = IPC_MakeHeader(8, 2, 0); + cmdbuf[1] = 0; + cmdbuf[2] = PLGLDR_VERSION; + break; + } + + case 9: // Get the arbiter (events) + { + if (cmdbuf[0] != IPC_MakeHeader(9, 0, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + cmdbuf[0] = IPC_MakeHeader(9, 1, 2); + cmdbuf[1] = 0; + cmdbuf[2] = IPC_Desc_SharedHandles(1); + cmdbuf[3] = ctx->arbiter; + break; + } + + case 10: // Get plugin path + { + if (cmdbuf[0] != IPC_MakeHeader(10, 0, 2)) + { + error(cmdbuf, 0xD9001830); + break; + } + + char *path = (char *)cmdbuf[2]; + strncpy(path, ctx->pluginPath, 255); + + cmdbuf[0] = IPC_MakeHeader(10, 1, 2); + cmdbuf[1] = 0; + cmdbuf[2] = IPC_Desc_Buffer(255, IPC_BUFFER_RW); + cmdbuf[3] = (u32)path; + + break; + } + + case 11: // Set rosalina menu block + { + if (cmdbuf[0] != IPC_MakeHeader(11, 1, 0)) + { + error(cmdbuf, 0xD9001830); + break; + } + + g_blockMenuOpen = cmdbuf[1]; + + cmdbuf[0] = IPC_MakeHeader(11, 1, 0); + cmdbuf[1] = 0; + break; + } + + case 12: // Set swap settings + { + if (cmdbuf[0] != IPC_MakeHeader(12, 2, 4)) + { + error(cmdbuf, 0xD9001830); + break; + } + cmdbuf[0] = IPC_MakeHeader(12, 1, 0); + MemoryBlock__ResetSwapSettings(); + if (!cmdbuf[1] || !cmdbuf[2]) { + cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS); + break; + } + + u32* remoteSavePhysAddr = (u32*)(cmdbuf[1] | (1 << 31)); + u32* remoteLoadPhysAddr = (u32*)(cmdbuf[2] | (1 << 31)); + + Result ret = MemoryBlock__SetSwapSettings(remoteSavePhysAddr, false, (u32*)cmdbuf[4]); + if (!ret) ret = MemoryBlock__SetSwapSettings(remoteLoadPhysAddr, true, (u32*)cmdbuf[4]); + + if (ret) { + cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_TOO_LARGE); + MemoryBlock__ResetSwapSettings(); + break; + } + + ctx->isSwapFunctionset = true; + + if (((char*)cmdbuf[6])[0] != '\0') strncpy(g_swapFileName, (char*)cmdbuf[6], 255); + + svcInvalidateEntireInstructionCache(); // Could use the range one + + cmdbuf[1] = 0; + break; + } + + case 13: // Set plugin exe load func + { + if (cmdbuf[0] != IPC_MakeHeader(13, 1, 2)) + { + error(cmdbuf, 0xD9001830); + break; + } + cmdbuf[0] = IPC_MakeHeader(13, 1, 0); + Reset_3gx_LoadParams(); + if (!cmdbuf[1]) { + cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_INVALID_ADDRESS); + break; + } + + u32* remoteLoadExeFuncAddr = (u32*)(cmdbuf[1] | (1 << 31)); + Result ret = Set_3gx_LoadParams(remoteLoadExeFuncAddr, (u32*)cmdbuf[3]); + if (ret) + { + cmdbuf[1] = MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_LDR, RD_TOO_LARGE); + Reset_3gx_LoadParams(); + break; + } + + ctx->isExeLoadFunctionset = true; + + svcInvalidateEntireInstructionCache(); // Could use the range one + + cmdbuf[1] = 0; + break; + } + + default: // Unknown command + { + error(cmdbuf, 0xD900182F); + break; + } + } + + if (ctx->error.message) + { + PluginLoader__Error(ctx->error.message, ctx->error.code); + ctx->error.message = NULL; + ctx->error.code = 0; + } +} + +static bool ThreadPredicate(u32 *kthread) +{ + // Check if the thread is part of the plugin + u32 *tls = (u32 *)kthread[0x26]; + + return *tls == THREADVARS_MAGIC; +} + +static void __strex__(s32 *addr, s32 val) +{ + do + __ldrex(addr); + while (__strex(addr, val)); +} + +void PLG__NotifyEvent(PLG_Event event, bool signal) +{ + if (!PluginLoaderCtx.plgEventPA) return; + + __strex__(PluginLoaderCtx.plgEventPA, event); + if (signal) + svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgEventPA, ARBITRATION_SIGNAL, 1, 0); +} + +void PLG__WaitForReply(void) +{ + __strex__(PluginLoaderCtx.plgReplyPA, PLG_WAIT); + svcArbitrateAddress(PluginLoaderCtx.arbiter, (u32)PluginLoaderCtx.plgReplyPA, ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT, PLG_OK, 10000000000ULL); +} + +void PLG__SetConfigMemoryStatus(u32 status) +{ + *(vu32 *)PA_FROM_VA_PTR(0x1FF800F0) = status; +} + +u32 PLG__GetConfigMemoryStatus(void) +{ + return (*(vu32 *)PA_FROM_VA_PTR((u32 *)0x1FF800F0)) & 0xFFFF; +} + +u32 PLG__GetConfigMemoryEvent(void) +{ + return (*(vu32 *)PA_FROM_VA_PTR(0x1FF800F0)) & ~0xFFFF; +} + +static void WaitForProcessTerminated(void *arg) +{ + (void)arg; + PluginLoaderContext *ctx = &PluginLoaderCtx; + + // Wait until all threads of the process have finished (svcWaitSynchronization == 0) or 5 seconds have passed. + for (u32 i = 0; svcWaitSynchronization(ctx->target, 0) != 0 && i < 100; i++) svcSleepThread(50000000); // 50ms + + // Unmap plugin's memory before closing the process + if (!ctx->pluginIsSwapped) { + MemoryBlock__UnmountFromProcess(); + MemoryBlock__Free(); + } + // Terminate process + svcCloseHandle(ctx->target); + // Reset plugin loader state + PLG__SetConfigMemoryStatus(PLG_CFG_NONE); + ctx->pluginIsSwapped = false; + ctx->pluginIsHome = false; + ctx->target = 0; + ctx->isExeLoadFunctionset = false; + ctx->isSwapFunctionset = false; + ctx->pluginMemoryStrategy = PLG_STRATEGY_SWAP; + g_blockMenuOpen = 0; + MemoryBlock__ResetSwapSettings(); + //if (!ctx->userLoadParameters.noIRPatch) + // IR__Unpatch(); +} + +void PluginLoader__HandleKernelEvent(u32 notifId) +{ + (void)notifId; + if (PLG__GetConfigMemoryStatus() == PLG_CFG_EXITING) + { + srvPublishToSubscriber(0x1002, 0); + return; + } + + PluginLoaderContext *ctx = &PluginLoaderCtx; + u32 event = PLG__GetConfigMemoryEvent(); + + if (event == PLG_CFG_EXIT_EVENT) + { + PLG__SetConfigMemoryStatus(PLG_CFG_EXITING); + if (!ctx->pluginIsSwapped) + { + // Signal the plugin that the game is exiting + PLG__NotifyEvent(PLG_ABOUT_TO_EXIT, false); + // Wait for plugin reply + PLG__WaitForReply(); + } + // Start a task to wait for process to be terminated + TaskRunner_RunTask(WaitForProcessTerminated, NULL, 0); + } + else if (event == PLG_CFG_HOME_EVENT) + { + if ((ctx->pluginMemoryStrategy == PLG_STRATEGY_SWAP) && !isN3DS) { + if (ctx->pluginIsSwapped) + { + // Reload data from swap file + MemoryBlock__IsReady(); + MemoryBlock__FromSwapFile(); + MemoryBlock__MountInProcess(); + // Unlock plugin threads + svcControlProcess(ctx->target, PROCESSOP_SCHEDULE_THREADS, 0, (u32)ThreadPredicate); + // Resume plugin execution + PLG__NotifyEvent(PLG_OK, true); + PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING); + } + else + { + // Signal plugin that it's about to be swapped + PLG__NotifyEvent(PLG_ABOUT_TO_SWAP, false); + // Wait for plugin reply + PLG__WaitForReply(); + // Lock plugin threads + svcControlProcess(ctx->target, PROCESSOP_SCHEDULE_THREADS, 1, (u32)ThreadPredicate); + // Put data into file and release memory + MemoryBlock__UnmountFromProcess(); + MemoryBlock__ToSwapFile(); + MemoryBlock__Free(); + PLG__SetConfigMemoryStatus(PLG_CFG_INHOME); + } + ctx->pluginIsSwapped = !ctx->pluginIsSwapped; + } else { + // Needed for compatibility with old plugins that don't expect the PLG_HOME events. + volatile PluginHeader* mappedHeader = PA_FROM_VA_PTR(MemoryBlock__GetMappedPluginHeader()); + bool doNotification = mappedHeader ? mappedHeader->notifyHomeEvent : false; + if (ctx->pluginIsHome) + { + if (doNotification) { + // Signal plugin that it's about to exit home menu + PLG__NotifyEvent(PLG_HOME_EXIT, false); + // Wait for plugin reply + PLG__WaitForReply(); + } + PLG__SetConfigMemoryStatus(PLG_CFG_RUNNING); + } + else + { + if (doNotification) { + // Signal plugin that it's about to enter home menu + PLG__NotifyEvent(PLG_HOME_ENTER, false); + // Wait for plugin reply + PLG__WaitForReply(); + } + PLG__SetConfigMemoryStatus(PLG_CFG_INHOME); + } + ctx->pluginIsHome = !ctx->pluginIsHome; + } + + } + srvPublishToSubscriber(0x1002, 0); +} diff --git a/sysmodules/rosalina/source/plugin/pluginLoader.s b/sysmodules/rosalina/source/plugin/pluginLoader.s new file mode 100644 index 00000000..21dff076 --- /dev/null +++ b/sysmodules/rosalina/source/plugin/pluginLoader.s @@ -0,0 +1,36 @@ +.section .data +.balign 4 +.arm + +.global gamePatchFunc +.type gamePatchFunc, %function +gamePatchFunc: + stmfd sp!, {r0-r12} + 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: + adr r0, g_savedGameInstr + push {r0} + ldr r5, =0x07000100 + blx r5 + add sp, sp, #4 + +exit: + ldmfd sp!, {r0} + msr cpsr, r0 + ldmfd sp!, {r0-r12} + ldr lr, =0x00100000 + mov pc, lr + +.global g_savedGameInstr +g_savedGameInstr: + .word 0, 0 diff --git a/sysmodules/rosalina/source/plugin/swapFunc.s b/sysmodules/rosalina/source/plugin/swapFunc.s new file mode 100644 index 00000000..0568dc4c --- /dev/null +++ b/sysmodules/rosalina/source/plugin/swapFunc.s @@ -0,0 +1,111 @@ +.section .text +.balign 4 +.arm + +.global saveSwapFunc +.type saveSwapFunc, %function +saveSwapFunc: + BX LR + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + +.global loadSwapFunc +.type loadSwapFunc, %function +loadSwapFunc: + BX LR + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + +.global loadExeFunc +.type loadExeFunc, %function +loadExeFunc: + BX LR + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP \ No newline at end of file diff --git a/sysmodules/rosalina/source/process_patches.c b/sysmodules/rosalina/source/process_patches.c index 07f5d0b6..e72ecf67 100644 --- a/sysmodules/rosalina/source/process_patches.c +++ b/sysmodules/rosalina/source/process_patches.c @@ -77,7 +77,7 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func) // NOTE: we suppose .text, .rodata, .data+.bss are contiguous & in that order u32 totalSize = (u32)(textSize + roSize + rwSize); - if (R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize))) + if (R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize))) { svcCloseHandle(processHandle); return res; @@ -85,6 +85,6 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func) res = func(processHandle, (u32)textSize, (u32)roSize, (u32)rwSize); - svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize); + svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize); return res; } diff --git a/sysmodules/rosalina/source/sleep.c b/sysmodules/rosalina/source/sleep.c new file mode 100644 index 00000000..dfc0d6c9 --- /dev/null +++ b/sysmodules/rosalina/source/sleep.c @@ -0,0 +1,76 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2018 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds.h> +#include "plugin/plgldr.h" +#include "plugin/plgloader.h" + +static bool g_isSleeping = false; +static LightEvent g_onWakeUpEvent; + +void IR__HandleSleep(bool isSleeping); ///< See in input_redirection.c + +void Sleep__Init(void) +{ + // No need anymore, handled by ServiceManager + // srvSubscribe(0x214); ///< Sleep entry + // srvSubscribe(0x213); ///< Sleep exit + LightEvent_Init(&g_onWakeUpEvent, RESET_STICKY); +} + +void Sleep__HandleNotification(u32 notifId) +{ + if (notifId == 0x214) ///< Sleep entry + { + LightEvent_Clear(&g_onWakeUpEvent); + g_isSleeping = true; + // IR patch creates sleep issue, so we need to handle it + // IR__HandleSleep(g_isSleeping); + // Plugins do not receive 0x214 notifications, so we send it via our custom service + if (PLG__GetConfigMemoryStatus() == PLG_CFG_RUNNING) + PLG__NotifyEvent(PLG_SLEEP_ENTRY, false); + } + else if (notifId == 0x213) ///< Sleep exit + { + g_isSleeping = false; + LightEvent_Signal(&g_onWakeUpEvent); + // IR patch creates sleep issue, so we need to handle it + // IR__HandleSleep(g_isSleeping); + // Plugins actually receives 0x213 notifications, but since we send sleep entry, let's do sleep exit as well + if (PLG__GetConfigMemoryStatus() == PLG_CFG_RUNNING) + PLG__NotifyEvent(PLG_SLEEP_EXIT, false); + } +} + +bool Sleep__Status(void) +{ + if (g_isSleeping) + { + LightEvent_Wait(&g_onWakeUpEvent); + return true; + } + return false; +} diff --git a/sysmodules/rosalina/source/sock_util.c b/sysmodules/rosalina/source/sock_util.c index d32ef672..c68ea115 100644 --- a/sysmodules/rosalina/source/sock_util.c +++ b/sysmodules/rosalina/source/sock_util.c @@ -9,10 +9,12 @@ #include <3ds/result.h> #include <3ds/svc.h> #include <3ds/synchronization.h> +#include <3ds/services/ac.h> #include #include "memory.h" #include "minisoc.h" #include "sock_util.h" +#include "sleep.h" extern Handle preTerminationEvent; extern bool preTerminationRequested; @@ -185,6 +187,14 @@ void server_run(struct sock_server *serv) for(nfds_t i = 0; i < serv->nfds; i++) fds[i].revents = 0; + + if (Sleep__Status()) + { + while (!Wifi__IsConnected() + && serv->running && !preTerminationRequested) + svcSleepThread(1000000000ULL); + } + int pollres = socPoll(fds, serv->nfds, 50); if(server_should_exit(serv) || pollres < -10000) @@ -321,3 +331,13 @@ void server_finalize(struct sock_server *serv) svcClearEvent(serv->started_event); svcCloseHandle(serv->started_event); } + +bool Wifi__IsConnected(void) +{ + u32 status = 0; + u32 wifistatus = 0; + + acInit(); + return R_SUCCEEDED(ACU_GetWifiStatus(&wifistatus)) && wifistatus > 0 + && R_SUCCEEDED(ACU_GetStatus(&status)) && status != 1; +}