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;
+}