mirror of
https://github.com/LumaTeam/Luma3DS.git
synced 2026-02-22 01:44:38 +00:00
Merge 3GX plugin loader fork (#1916)
This commit adds all the changes made to the 3GX plugin loader fork of Luma3DS. The most important features are:
- Add 3GX plugin loader support. New service added to rosalina: plg:ldr
- Add svcControlProcess, svcControlMemoryUnsafe and improve svcMapProcessMemoryEx (breaking change)
- Allow applications to override certain configurations depending on their needs:
- Disable core2 thread redirection
- Disable game patching for the next app
- Force New 3DS speedup
- Force next application in a specific memory mode
- Block the opening of the Rosalina menu
- Add GDB commands to list all process handles and catch all SVC (latter is for IDA Pro as gdb client supports it)
- Other changes necessary for plugins to work properly. Please check changed files in this PR for more details.
---------
Co-authored-by: PabloMK7 <hackyglitch@gmail.com>
Co-authored-by: Nanquitas <nath.doidi@gmail.com>
Co-authored-by: TuxSH <1922548+TuxSH@users.noreply.github.com>
This commit is contained in:
parent
b20c79eda4
commit
c055fb6f5e
Binary file not shown.
@ -460,6 +460,11 @@ static int configIniHandler(void* user, const char* section, const char* name, c
|
|||||||
CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value));
|
CHECK_PARSE_OPTION(parseKeyComboOption(&opt, value));
|
||||||
cfg->rosalinaMenuCombo = opt;
|
cfg->rosalinaMenuCombo = opt;
|
||||||
return 1;
|
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) {
|
} else if (strcmp(name, "ntp_tz_offset_min") == 0) {
|
||||||
s64 opt;
|
s64 opt;
|
||||||
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899));
|
CHECK_PARSE_OPTION(parseDecIntOption(&opt, value, -779, 899));
|
||||||
@ -650,7 +655,7 @@ static size_t saveLumaIniConfigToStr(char *out)
|
|||||||
pinNumDigits, n3dsCpuStr,
|
pinNumDigits, n3dsCpuStr,
|
||||||
autobootModeStr,
|
autobootModeStr,
|
||||||
|
|
||||||
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr,
|
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1),
|
||||||
(int)cfg->ntpTzOffetMinutes,
|
(int)cfg->ntpTzOffetMinutes,
|
||||||
|
|
||||||
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
|
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
#define CONFIG_FILE "config.ini"
|
#define CONFIG_FILE "config.ini"
|
||||||
#define CONFIG_VERSIONMAJOR 3
|
#define CONFIG_VERSIONMAJOR 3
|
||||||
#define CONFIG_VERSIONMINOR 8
|
#define CONFIG_VERSIONMINOR 9
|
||||||
|
|
||||||
#define BOOTCFG_NAND BOOTCONFIG(0, 1)
|
#define BOOTCFG_NAND BOOTCONFIG(0, 1)
|
||||||
#define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3)
|
#define BOOTCFG_EMUINDEX BOOTCONFIG(1, 3)
|
||||||
|
|||||||
@ -134,6 +134,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
u32 splashDurationMsec;
|
u32 splashDurationMsec;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
|
u32 pluginLoaderFlags;
|
||||||
s16 ntpTzOffetMinutes;
|
s16 ntpTzOffetMinutes;
|
||||||
|
|
||||||
ScreenFiltersCfgData topScreenFilter;
|
ScreenFiltersCfgData topScreenFilter;
|
||||||
@ -216,6 +217,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
|
|||||||
info->splashDurationMsec = configData.splashDurationMsec;
|
info->splashDurationMsec = configData.splashDurationMsec;
|
||||||
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
|
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
|
||||||
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
|
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
|
||||||
|
info->pluginLoaderFlags = configData.pluginLoaderFlags;
|
||||||
info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes;
|
info->ntpTzOffetMinutes = configData.ntpTzOffetMinutes;
|
||||||
info->topScreenFilter = configData.topScreenFilter;
|
info->topScreenFilter = configData.topScreenFilter;
|
||||||
info->bottomScreenFilter = configData.bottomScreenFilter;
|
info->bottomScreenFilter = configData.bottomScreenFilter;
|
||||||
|
|||||||
@ -77,6 +77,7 @@ typedef struct CfgData {
|
|||||||
|
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
|
u32 pluginLoaderFlags;
|
||||||
s16 ntpTzOffetMinutes;
|
s16 ntpTzOffetMinutes;
|
||||||
|
|
||||||
ScreenFiltersCfgData topScreenFilter;
|
ScreenFiltersCfgData topScreenFilter;
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
extern KRecursiveLock *criticalSectionLock;
|
extern KRecursiveLock *criticalSectionLock;
|
||||||
extern KObjectList *threadList;
|
extern KObjectList *threadList;
|
||||||
|
extern KObjectList *resourceLimitList;
|
||||||
extern KObjectMutex *synchronizationMutex;
|
extern KObjectMutex *synchronizationMutex;
|
||||||
|
|
||||||
extern void (*KRecursiveLock__Lock)(KRecursiveLock *this);
|
extern void (*KRecursiveLock__Lock)(KRecursiveLock *this);
|
||||||
@ -44,18 +45,27 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *
|
|||||||
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
||||||
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
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 (*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__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
||||||
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, 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__Clear)(KEvent *this);
|
||||||
|
extern Result (*KEvent__Signal)(KEvent *this);
|
||||||
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
||||||
extern void (*KObjectMutex__ErrorOccured)(void);
|
extern void (*KObjectMutex__ErrorOccured)(void);
|
||||||
|
|
||||||
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
||||||
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
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 (*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 Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
|
||||||
extern void (*SleepThread)(s64 ns);
|
extern void (*SleepThread)(s64 ns);
|
||||||
|
extern Result (*CreateEvent)(Handle *out, ResetType resetType);
|
||||||
extern Result (*CloseHandle)(Handle handle);
|
extern Result (*CloseHandle)(Handle handle);
|
||||||
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||||
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
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 (*OpenProcess)(Handle *out, u32 processId);
|
||||||
extern Result (*GetProcessId)(u32 *out, Handle process);
|
extern Result (*GetProcessId)(u32 *out, Handle process);
|
||||||
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
||||||
|
extern Result (*SignalEvent)(Handle event);
|
||||||
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
||||||
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
||||||
|
|
||||||
@ -98,6 +109,7 @@ extern bool *isDevUnit;
|
|||||||
extern vu8 *configPage;
|
extern vu8 *configPage;
|
||||||
extern u32 kernelVersion;
|
extern u32 kernelVersion;
|
||||||
extern FcramLayout fcramLayout;
|
extern FcramLayout fcramLayout;
|
||||||
|
extern FcramDescriptor *fcramDescriptor;
|
||||||
|
|
||||||
extern KCoreContext *coreCtxs;
|
extern KCoreContext *coreCtxs;
|
||||||
|
|
||||||
@ -114,6 +126,7 @@ extern KBaseInterruptEvent *customInterruptEvent;
|
|||||||
extern void (*initFPU)(void);
|
extern void (*initFPU)(void);
|
||||||
extern void (*mcuReboot)(void);
|
extern void (*mcuReboot)(void);
|
||||||
extern void (*coreBarrier)(void);
|
extern void (*coreBarrier)(void);
|
||||||
|
extern void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region);
|
||||||
|
|
||||||
typedef struct ScreenFiltersCfgData {
|
typedef struct ScreenFiltersCfgData {
|
||||||
u16 cct;
|
u16 cct;
|
||||||
@ -139,6 +152,7 @@ typedef struct CfwInfo
|
|||||||
u32 splashDurationMsec;
|
u32 splashDurationMsec;
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
|
u32 pluginLoaderFlags;
|
||||||
s16 ntpTzOffetMinutes;
|
s16 ntpTzOffetMinutes;
|
||||||
|
|
||||||
ScreenFiltersCfgData topScreenFilter;
|
ScreenFiltersCfgData topScreenFilter;
|
||||||
@ -153,6 +167,24 @@ typedef struct CfwInfo
|
|||||||
extern CfwInfo cfwInfo;
|
extern CfwInfo cfwInfo;
|
||||||
extern u32 kextBasePa;
|
extern u32 kextBasePa;
|
||||||
extern u32 stolenSystemMemRegionSize;
|
extern u32 stolenSystemMemRegionSize;
|
||||||
|
extern bool disableThreadRedirection;
|
||||||
|
|
||||||
extern vu32 rosalinaState;
|
extern vu32 rosalinaState;
|
||||||
extern bool hasStartedRosalinaNetworkFuncsOnce;
|
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);
|
||||||
|
|||||||
@ -106,6 +106,14 @@ typedef struct ALIGN(4) KMutex
|
|||||||
union KProcess *owner;
|
union KProcess *owner;
|
||||||
} KMutex;
|
} KMutex;
|
||||||
|
|
||||||
|
typedef struct KAddressArbiter
|
||||||
|
{
|
||||||
|
KAutoObject autoObject;
|
||||||
|
struct KThread *first;
|
||||||
|
struct KThread *last;
|
||||||
|
union KProcess *owner;
|
||||||
|
} KAddressArbiter;
|
||||||
|
|
||||||
/* 92 */
|
/* 92 */
|
||||||
typedef struct KMutexLinkedList
|
typedef struct KMutexLinkedList
|
||||||
{
|
{
|
||||||
@ -113,6 +121,30 @@ typedef struct KMutexLinkedList
|
|||||||
KMutex *last;
|
KMutex *last;
|
||||||
} KMutexLinkedList;
|
} 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 */
|
/* 45 */
|
||||||
typedef struct KClassToken
|
typedef struct KClassToken
|
||||||
{
|
{
|
||||||
@ -489,6 +521,9 @@ typedef enum MemOp
|
|||||||
MEMOP_REGION_SYSTEM = 0x200,
|
MEMOP_REGION_SYSTEM = 0x200,
|
||||||
MEMOP_REGION_BASE = 0x300,
|
MEMOP_REGION_BASE = 0x300,
|
||||||
MEMOP_LINEAR = 0x10000,
|
MEMOP_LINEAR = 0x10000,
|
||||||
|
|
||||||
|
MEMOP_OP_MASK = 0xFF,
|
||||||
|
MEMOP_REGION_MASK = 0xF00,
|
||||||
} MemOp;
|
} MemOp;
|
||||||
|
|
||||||
/* 17 */
|
/* 17 */
|
||||||
@ -541,6 +576,20 @@ typedef struct KBlockInfo
|
|||||||
u32 pageCount;
|
u32 pageCount;
|
||||||
} KBlockInfo;
|
} 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 */
|
/* 25 */
|
||||||
typedef struct KMemoryBlock
|
typedef struct KMemoryBlock
|
||||||
{
|
{
|
||||||
@ -1038,10 +1087,24 @@ typedef struct KProcess##sys\
|
|||||||
KThread *mainThread;\
|
KThread *mainThread;\
|
||||||
u32 interruptEnabledFlags[4];\
|
u32 interruptEnabledFlags[4];\
|
||||||
KProcessHandleTable handleTable;\
|
KProcessHandleTable handleTable;\
|
||||||
u8 gap234[52];\
|
/* Custom fields for plugin system */ \
|
||||||
|
/* { */ \
|
||||||
|
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
|
||||||
|
Handle onMemoryLayoutChangeEvent;\
|
||||||
|
/* } */ \
|
||||||
|
u8 gap234[44];\
|
||||||
u64 unused;\
|
u64 unused;\
|
||||||
} KProcess##sys;
|
} KProcess##sys;
|
||||||
|
|
||||||
|
enum KProcess_CustomFlags
|
||||||
|
{
|
||||||
|
ForceRWXPages = 1 << 0,
|
||||||
|
SignalOnMemLayoutChanges = 1 << 1,
|
||||||
|
SignalOnExit = 1 << 2,
|
||||||
|
|
||||||
|
MemLayoutChanged = 1 << 16
|
||||||
|
};
|
||||||
|
|
||||||
INSTANCIATE_KPROCESS(N3DS);
|
INSTANCIATE_KPROCESS(N3DS);
|
||||||
INSTANCIATE_KPROCESS(O3DS8x);
|
INSTANCIATE_KPROCESS(O3DS8x);
|
||||||
INSTANCIATE_KPROCESS(O3DSPre8x);
|
INSTANCIATE_KPROCESS(O3DSPre8x);
|
||||||
@ -1135,6 +1198,28 @@ typedef struct FcramLayout
|
|||||||
u32 baseSize;
|
u32 baseSize;
|
||||||
} FcramLayout;
|
} 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 bool isN3DS;
|
||||||
extern void *officialSVCs[0x7E];
|
extern void *officialSVCs[0x7E];
|
||||||
|
|
||||||
|
|||||||
129
k11_extension/include/mmu.h
Normal file
129
k11_extension/include/mmu.h
Normal file
@ -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);
|
||||||
34
k11_extension/include/svc/ControlMemoryUnsafe.h
Normal file
34
k11_extension/include/svc/ControlMemoryUnsafe.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
21
k11_extension/include/svc/ControlProcess.h
Normal file
21
k11_extension/include/svc/ControlProcess.h
Normal file
@ -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);
|
||||||
6
k11_extension/include/svc/ExitProcess.h
Normal file
6
k11_extension/include/svc/ExitProcess.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "svc.h"
|
||||||
|
|
||||||
|
void ExitProcessHook(void);
|
||||||
|
void ExitProcessHookWrapper(void);
|
||||||
@ -30,7 +30,7 @@
|
|||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
extern bool svcSignalingEnabled;
|
extern u8 svcSignalingEnabled;
|
||||||
|
|
||||||
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId);
|
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId);
|
||||||
Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
||||||
|
|||||||
@ -30,4 +30,5 @@
|
|||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "svc.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);
|
||||||
|
|||||||
@ -28,10 +28,43 @@
|
|||||||
#include "fatalExceptionHandlers.h"
|
#include "fatalExceptionHandlers.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "mmu.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
#define REG_DUMP_SIZE 4 * 23
|
#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)
|
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;
|
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
||||||
|
|
||||||
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
||||||
return false;
|
return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage);
|
||||||
|
|
||||||
if(currentProcess != NULL)
|
if(currentProcess != NULL)
|
||||||
{
|
{
|
||||||
@ -52,7 +85,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
|
|||||||
|
|
||||||
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
|
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
|
||||||
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
|
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
|
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)
|
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)
|
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
||||||
{
|
{
|
||||||
ExceptionDumpHeader dumpHeader;
|
ExceptionDumpHeader dumpHeader;
|
||||||
@ -96,7 +130,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
|
|||||||
registerDump[15] = pc;
|
registerDump[15] = pc;
|
||||||
|
|
||||||
//Dump code
|
//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);
|
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
|
||||||
|
|
||||||
//Copy register dump and code dump
|
//Copy register dump and code dump
|
||||||
|
|||||||
@ -25,6 +25,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
KRecursiveLock *criticalSectionLock;
|
KRecursiveLock *criticalSectionLock;
|
||||||
KObjectList *threadList;
|
KObjectList *threadList;
|
||||||
@ -40,18 +42,29 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H
|
|||||||
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
|
||||||
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
|
||||||
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
|
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__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
|
||||||
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, 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__Clear)(KEvent *this);
|
||||||
|
Result (*KEvent__Signal)(KEvent *this);
|
||||||
|
|
||||||
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
|
||||||
void (*KObjectMutex__ErrorOccured)(void);
|
void (*KObjectMutex__ErrorOccured)(void);
|
||||||
|
|
||||||
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
|
||||||
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
|
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 (*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);
|
Result (*CreateThread)(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId);
|
||||||
void (*SleepThread)(s64 ns);
|
void (*SleepThread)(s64 ns);
|
||||||
|
Result (*CreateEvent)(Handle *out, ResetType resetType);
|
||||||
Result (*CloseHandle)(Handle handle);
|
Result (*CloseHandle)(Handle handle);
|
||||||
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
|
||||||
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
|
||||||
@ -62,6 +75,7 @@ Result (*SendSyncRequest)(Handle handle);
|
|||||||
Result (*OpenProcess)(Handle *out, u32 processId);
|
Result (*OpenProcess)(Handle *out, u32 processId);
|
||||||
Result (*GetProcessId)(u32 *out, Handle process);
|
Result (*GetProcessId)(u32 *out, Handle process);
|
||||||
Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
Result (*DebugActiveProcess)(Handle *out, u32 processId);
|
||||||
|
Result (*SignalEvent)(Handle event);
|
||||||
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
|
||||||
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
|
||||||
|
|
||||||
@ -94,6 +108,7 @@ bool *isDevUnit;
|
|||||||
vu8 *configPage;
|
vu8 *configPage;
|
||||||
u32 kernelVersion;
|
u32 kernelVersion;
|
||||||
FcramLayout fcramLayout;
|
FcramLayout fcramLayout;
|
||||||
|
FcramDescriptor *fcramDescriptor;
|
||||||
KCoreContext *coreCtxs;
|
KCoreContext *coreCtxs;
|
||||||
|
|
||||||
void *originalHandlers[8] = {NULL};
|
void *originalHandlers[8] = {NULL};
|
||||||
@ -107,12 +122,64 @@ InterruptManager *interruptManager;
|
|||||||
void (*initFPU)(void);
|
void (*initFPU)(void);
|
||||||
void (*mcuReboot)(void);
|
void (*mcuReboot)(void);
|
||||||
void (*coreBarrier)(void);
|
void (*coreBarrier)(void);
|
||||||
|
void* (*kAlloc)(FcramDescriptor *fcramDesc, u32 nbPages, u32 alignment, u32 region);
|
||||||
|
|
||||||
CfwInfo cfwInfo;
|
CfwInfo cfwInfo;
|
||||||
u32 kextBasePa;
|
u32 kextBasePa;
|
||||||
u32 stolenSystemMemRegionSize;
|
u32 stolenSystemMemRegionSize;
|
||||||
|
bool disableThreadRedirection = false;
|
||||||
|
|
||||||
vu32 rosalinaState;
|
vu32 rosalinaState;
|
||||||
bool hasStartedRosalinaNetworkFuncsOnce;
|
bool hasStartedRosalinaNetworkFuncsOnce;
|
||||||
|
KEvent* signalPluginEvent = NULL;
|
||||||
u32 pidOffsetKProcess, hwInfoOffsetKProcess, codeSetOffsetKProcess, handleTableOffsetKProcess, debugOffsetKProcess, flagsKProcess;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -130,11 +130,81 @@ void configHook(vu8 *cfgPage)
|
|||||||
flagsKProcess = KPROCESS_OFFSETOF(kernelFlags);
|
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)
|
static void findUsefulSymbols(void)
|
||||||
{
|
{
|
||||||
u32 *off;
|
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;
|
off += 3;
|
||||||
initFPU = (void (*) (void))off;
|
initFPU = (void (*) (void))off;
|
||||||
|
|
||||||
@ -171,6 +241,8 @@ static void findUsefulSymbols(void)
|
|||||||
KEvent__Clear = (Result (*)(KEvent *))decodeArmBranch(off + 1);
|
KEvent__Clear = (Result (*)(KEvent *))decodeArmBranch(off + 1);
|
||||||
for(off = (u32 *)KEvent__Clear; *off != 0xE8BD8070; off++);
|
for(off = (u32 *)KEvent__Clear; *off != 0xE8BD8070; off++);
|
||||||
synchronizationMutex = *(KObjectMutex **)(off + 1);
|
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++);
|
for(off = (u32 *)officialSVCs[0x24]; *off != 0xE59F004C; off++);
|
||||||
WaitSynchronization1 = (Result (*)(void *, KThread *, KSynchronizationObject *, s64))decodeArmBranch(off + 6);
|
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++);
|
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
|
||||||
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeArmBranch(off - 1);
|
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++);
|
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
|
||||||
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
|
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeArmBranch(++off);
|
||||||
for(; *off != 0xE320F000; off++);
|
for(; *off != 0xE320F000; off++);
|
||||||
@ -243,6 +330,7 @@ static void findUsefulSymbols(void)
|
|||||||
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
|
decodeArmBranch((u32 *)officialSVCs[0x01] + 5);
|
||||||
CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5);
|
CreateThread = (Result (*)(Handle *, u32, u32, u32, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x08] + 5);
|
||||||
SleepThread = (void (*)(s64))officialSVCs[0x0A];
|
SleepThread = (void (*)(s64))officialSVCs[0x0A];
|
||||||
|
CreateEvent = (Result (*)(Handle *, ResetType))decodeArmBranch((u32 *)officialSVCs[0x17] + 3);
|
||||||
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
|
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
|
||||||
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
|
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeArmBranch((u32 *)officialSVCs[0x29] + 3);
|
||||||
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeArmBranch((u32 *)officialSVCs[0x2A] + 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);
|
OpenProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x33] + 3);
|
||||||
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
|
GetProcessId = (Result (*)(u32 *, Handle))decodeArmBranch((u32 *)officialSVCs[0x35] + 3);
|
||||||
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
|
DebugActiveProcess = (Result (*)(Handle *, u32))decodeArmBranch((u32 *)officialSVCs[0x60] + 3);
|
||||||
|
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
|
||||||
|
|
||||||
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
|
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
|
||||||
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
|
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
|
||||||
|
|
||||||
@ -284,6 +374,8 @@ static void findUsefulSymbols(void)
|
|||||||
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
|
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
installMmuHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(FcramLayout *layout, KCoreContext *ctxs)
|
void main(FcramLayout *layout, KCoreContext *ctxs)
|
||||||
|
|||||||
319
k11_extension/source/mmu.c
Normal file
319
k11_extension/source/mmu.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -44,8 +44,11 @@
|
|||||||
#include "svc/MapProcessMemoryEx.h"
|
#include "svc/MapProcessMemoryEx.h"
|
||||||
#include "svc/UnmapProcessMemoryEx.h"
|
#include "svc/UnmapProcessMemoryEx.h"
|
||||||
#include "svc/ControlService.h"
|
#include "svc/ControlService.h"
|
||||||
|
#include "svc/ControlProcess.h"
|
||||||
|
#include "svc/ExitProcess.h"
|
||||||
#include "svc/CopyHandle.h"
|
#include "svc/CopyHandle.h"
|
||||||
#include "svc/TranslateHandle.h"
|
#include "svc/TranslateHandle.h"
|
||||||
|
#include "svc/ControlMemoryUnsafe.h"
|
||||||
|
|
||||||
void *officialSVCs[0x7E] = {NULL};
|
void *officialSVCs[0x7E] = {NULL};
|
||||||
void *alteredSvcTable[0x100] = {NULL};
|
void *alteredSvcTable[0x100] = {NULL};
|
||||||
@ -63,6 +66,7 @@ void buildAlteredSvcTable(void)
|
|||||||
memcpy(alteredSvcTable, officialSVCs, 4 * 0x7E);
|
memcpy(alteredSvcTable, officialSVCs, 4 * 0x7E);
|
||||||
|
|
||||||
alteredSvcTable[0x01] = ControlMemoryHookWrapper;
|
alteredSvcTable[0x01] = ControlMemoryHookWrapper;
|
||||||
|
alteredSvcTable[0x03] = ExitProcessHookWrapper;
|
||||||
|
|
||||||
if (isN3DS)
|
if (isN3DS)
|
||||||
alteredSvcTable[0x08] = CreateThreadHookWrapper;
|
alteredSvcTable[0x08] = CreateThreadHookWrapper;
|
||||||
@ -90,13 +94,15 @@ void buildAlteredSvcTable(void)
|
|||||||
alteredSvcTable[0x93] = invalidateInstructionCacheRange;
|
alteredSvcTable[0x93] = invalidateInstructionCacheRange;
|
||||||
alteredSvcTable[0x94] = invalidateEntireInstructionCache;
|
alteredSvcTable[0x94] = invalidateEntireInstructionCache;
|
||||||
|
|
||||||
alteredSvcTable[0xA0] = MapProcessMemoryEx;
|
alteredSvcTable[0xA0] = MapProcessMemoryExWrapper;
|
||||||
alteredSvcTable[0xA1] = UnmapProcessMemoryEx;
|
alteredSvcTable[0xA1] = UnmapProcessMemoryEx;
|
||||||
alteredSvcTable[0xA2] = ControlMemoryEx;
|
alteredSvcTable[0xA2] = ControlMemoryEx;
|
||||||
|
alteredSvcTable[0xA3] = ControlMemoryUnsafeWrapper;
|
||||||
|
|
||||||
alteredSvcTable[0xB0] = ControlService;
|
alteredSvcTable[0xB0] = ControlService;
|
||||||
alteredSvcTable[0xB1] = CopyHandleWrapper;
|
alteredSvcTable[0xB1] = CopyHandleWrapper;
|
||||||
alteredSvcTable[0xB2] = TranslateHandleWrapper;
|
alteredSvcTable[0xB2] = TranslateHandleWrapper;
|
||||||
|
alteredSvcTable[0xB3] = ControlProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void signalSvcEntry(u32 svcId)
|
void signalSvcEntry(u32 svcId)
|
||||||
@ -111,10 +117,18 @@ void signalSvcEntry(u32 svcId)
|
|||||||
void signalSvcReturn(u32 svcId)
|
void signalSvcReturn(u32 svcId)
|
||||||
{
|
{
|
||||||
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
|
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!)
|
// 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);
|
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)
|
void postprocessSvc(void)
|
||||||
|
|||||||
84
k11_extension/source/svc/ControlMemoryUnsafe.c
Normal file
84
k11_extension/source/svc/ControlMemoryUnsafe.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
208
k11_extension/source/svc/ControlProcess.c
Normal file
208
k11_extension/source/svc/ControlProcess.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@
|
|||||||
Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId)
|
Result CreateThreadHook(Handle *outThreadHandle, u32 ep, u32 arg, u32 stackTop, s32 priority, s32 processorId)
|
||||||
{
|
{
|
||||||
u32 flags = flagsOfProcess(currentCoreContext->objectContext.currentProcess);
|
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;
|
processorId = 2;
|
||||||
|
|
||||||
return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId);
|
return CreateThread(outThreadHandle, ep, arg, stackTop, priority, processorId);
|
||||||
|
|||||||
32
k11_extension/source/svc/ExitProcess.c
Normal file
32
k11_extension/source/svc/ExitProcess.c
Normal file
@ -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])();
|
||||||
|
}
|
||||||
@ -29,11 +29,14 @@
|
|||||||
|
|
||||||
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
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);
|
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
|
||||||
KAutoObject *obj;
|
KAutoObject *obj;
|
||||||
|
|
||||||
if(handle == CUR_PROCESS_HANDLE)
|
if(handle == CUR_PROCESS_HANDLE)
|
||||||
{
|
{
|
||||||
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
|
||||||
@ -45,6 +48,10 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
|||||||
if(obj == NULL)
|
if(obj == NULL)
|
||||||
return 0xD8E007F7;
|
return 0xD8E007F7;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo)
|
||||||
|
{
|
||||||
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
|
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
|
||||||
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
|
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
|
||||||
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
|
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
|
||||||
@ -53,10 +60,70 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
|
|||||||
hwInfo = NULL;
|
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);
|
obj->vtable->DecrementReferenceCount(obj);
|
||||||
return 0;
|
return res;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return GetHandleInfo(out, handle, type);
|
return GetHandleInfo(out, handle, type);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,14 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type)
|
|||||||
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
|
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
|
||||||
break;
|
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:
|
default:
|
||||||
res = 0xD8E007ED; // invalid enum value
|
res = 0xD8E007ED; // invalid enum value
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -69,14 +69,15 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
case 6:
|
case 6:
|
||||||
*out = cfwInfo.splashDurationMsec;
|
*out = cfwInfo.splashDurationMsec;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x10:
|
case 0x10:
|
||||||
*out = (s64)cfwInfo.autobootTwlTitleId;
|
*out = (s64)cfwInfo.autobootTwlTitleId;
|
||||||
break;
|
break;
|
||||||
case 0x11:
|
case 0x11:
|
||||||
*out = cfwInfo.autobootCtrAppmemtype;
|
*out = cfwInfo.autobootCtrAppmemtype;
|
||||||
break;
|
break;
|
||||||
|
case 0x80:
|
||||||
|
*out = fcramDescriptor->appRegion.regionSizeInBytes;
|
||||||
|
break;
|
||||||
case 0x100:
|
case 0x100:
|
||||||
*out = (s64)cfwInfo.hbldr3dsxTitleId;
|
*out = (s64)cfwInfo.hbldr3dsxTitleId;
|
||||||
break;
|
break;
|
||||||
@ -116,7 +117,12 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
|
|||||||
case 0x10C:
|
case 0x10C:
|
||||||
*out = (s64)cfwInfo.bottomScreenFilter.invert;
|
*out = (s64)cfwInfo.bottomScreenFilter.invert;
|
||||||
break;
|
break;
|
||||||
|
case 0x180:
|
||||||
|
*out = cfwInfo.pluginLoaderFlags;
|
||||||
|
break;
|
||||||
|
case 0x181:
|
||||||
|
*out = disableThreadRedirection;
|
||||||
|
break;
|
||||||
case 0x200: // isRelease
|
case 0x200: // isRelease
|
||||||
*out = cfwInfo.flags & 1;
|
*out = cfwInfo.flags & 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -35,8 +35,9 @@
|
|||||||
static u32 nbEnabled = 0;
|
static u32 nbEnabled = 0;
|
||||||
static u32 maskedPids[MAX_DEBUG];
|
static u32 maskedPids[MAX_DEBUG];
|
||||||
static u32 masks[MAX_DEBUG][8] = {0};
|
static u32 masks[MAX_DEBUG][8] = {0};
|
||||||
|
static bool forceBetterSoc = false;
|
||||||
|
|
||||||
bool svcSignalingEnabled = false;
|
u8 svcSignalingEnabled = 0;
|
||||||
|
|
||||||
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
|
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
|
||||||
{
|
{
|
||||||
@ -67,7 +68,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask)
|
|||||||
{
|
{
|
||||||
maskedPids[nbEnabled] = pid;
|
maskedPids[nbEnabled] = pid;
|
||||||
memcpy(&masks[nbEnabled++], tmpMask, 32);
|
memcpy(&masks[nbEnabled++], tmpMask, 32);
|
||||||
svcSignalingEnabled = true;
|
svcSignalingEnabled |= 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -87,7 +88,7 @@ Result SetSyscallDebugEventMask(u32 pid, bool enable, const u32 *mask)
|
|||||||
}
|
}
|
||||||
maskedPids[--nbEnabled] = 0;
|
maskedPids[--nbEnabled] = 0;
|
||||||
memset(&masks[nbEnabled], 0, 32);
|
memset(&masks[nbEnabled], 0, 32);
|
||||||
svcSignalingEnabled = false;
|
svcSignalingEnabled &= ~1;
|
||||||
}
|
}
|
||||||
|
|
||||||
KRecursiveLock__Unlock(&syscallDebugEventMaskLock);
|
KRecursiveLock__Unlock(&syscallDebugEventMaskLock);
|
||||||
@ -101,6 +102,16 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
|||||||
|
|
||||||
switch(type)
|
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:
|
case 0x10000:
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
@ -190,6 +201,20 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
|
|||||||
KRecursiveLock__Unlock(&dbgParamsLock);
|
KRecursiveLock__Unlock(&dbgParamsLock);
|
||||||
break;
|
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:
|
default:
|
||||||
{
|
{
|
||||||
res = KernelSetState(type, varg1, varg2, varg3);
|
res = KernelSetState(type, varg1, varg2, varg3);
|
||||||
|
|||||||
@ -26,19 +26,61 @@
|
|||||||
|
|
||||||
#include "svc/MapProcessMemoryEx.h"
|
#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);
|
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;
|
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;
|
if (srcProcess == NULL)
|
||||||
obj->vtable->DecrementReferenceCount(obj);
|
{
|
||||||
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,6 +162,43 @@ Result SendSyncRequestHook(Handle handle)
|
|||||||
break;
|
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:
|
case 0x4010082:
|
||||||
{
|
{
|
||||||
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
SessionInfo *info = SessionInfo_Lookup(clientSession->parentSession);
|
||||||
|
|||||||
@ -29,12 +29,30 @@
|
|||||||
|
|
||||||
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
|
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
|
if(GET_VERSION_MINOR(kernelVersion) < 37) // < 6.x
|
||||||
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
|
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();
|
invalidateEntireInstructionCache();
|
||||||
flushEntireDataCache();
|
flushEntireDataCache();
|
||||||
|
|||||||
@ -69,6 +69,14 @@ ControlMemoryHookWrapper:
|
|||||||
add sp, #12
|
add sp, #12
|
||||||
pop {pc}
|
pop {pc}
|
||||||
|
|
||||||
|
|
||||||
|
.global ExitProcessHookWrapper
|
||||||
|
.type ExitProcessHookWrapper, %function
|
||||||
|
ExitProcessHookWrapper:
|
||||||
|
push {lr}
|
||||||
|
bl ExitProcessHook
|
||||||
|
pop {pc}
|
||||||
|
|
||||||
.global ControlMemoryEx
|
.global ControlMemoryEx
|
||||||
.type ControlMemoryEx, %function
|
.type ControlMemoryEx, %function
|
||||||
ControlMemoryEx:
|
ControlMemoryEx:
|
||||||
@ -97,3 +105,21 @@ CreateThreadHookWrapper:
|
|||||||
ldr r1, [sp, #8]
|
ldr r1, [sp, #8]
|
||||||
add sp, sp, #12
|
add sp, sp, #12
|
||||||
pop {pc}
|
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}
|
||||||
@ -96,6 +96,36 @@ KObjectMutex__Release:
|
|||||||
blx r12
|
blx r12
|
||||||
bx lr
|
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
|
.global safecpy
|
||||||
.type safecpy, %function
|
.type safecpy, %function
|
||||||
safecpy:
|
safecpy:
|
||||||
|
|||||||
@ -8,8 +8,19 @@
|
|||||||
|
|
||||||
#define SYSMODULE_CXI_COOKIE_MASK 0xEEEE000000000000ull
|
#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 u32 config, multiConfig, bootConfig;
|
||||||
extern bool isN3DS, isSdMode;
|
extern bool isN3DS, isSdMode, nextGamePatchDisabled;
|
||||||
|
|
||||||
static u64 g_cached_programHandle; // for exheader info only
|
static u64 g_cached_programHandle; // for exheader info only
|
||||||
static ExHeader_Info g_exheaderInfo;
|
static ExHeader_Info g_exheaderInfo;
|
||||||
@ -110,6 +121,11 @@ static inline bool IsSysmoduleId(u64 tid)
|
|||||||
return (tid >> 32) == 0x00040130;
|
return (tid >> 32) == 0x00040130;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool IsApplicationId(u64 tid)
|
||||||
|
{
|
||||||
|
return (tid >> 32) == 0x00040000;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool IsSysmoduleCxiCookie(u64 programHandle)
|
static inline bool IsSysmoduleCxiCookie(u64 programHandle)
|
||||||
{
|
{
|
||||||
return (programHandle >> 32) == (SYSMODULE_CXI_COOKIE_MASK >> 32);
|
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;
|
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)
|
static inline bool IsHioId(u64 id)
|
||||||
{
|
{
|
||||||
// FS loads HIO titles at boot when it can. For HIO titles, title/programId and "program handle"
|
// 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))
|
if (R_FAILED(res))
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
u64 originalTitleId = exheaderInfo->aci.local_caps.title_id;
|
||||||
|
|
||||||
// Tweak 3dsx placeholder title exheaderInfo
|
// Tweak 3dsx placeholder title exheaderInfo
|
||||||
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
@ -263,7 +344,6 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u64 originalTitleId = exheaderInfo->aci.local_caps.title_id;
|
|
||||||
bool exhLoadedExternally = false;
|
bool exhLoadedExternally = false;
|
||||||
if (CONFIG(PATCHGAMES))
|
if (CONFIG(PATCHGAMES))
|
||||||
{
|
{
|
||||||
@ -281,6 +361,13 @@ static Result GetProgramInfoImpl(ExHeader_Info *exheaderInfo, u64 programHandle)
|
|||||||
exheaderInfo->aci.local_caps.title_id = originalTitleId;
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,6 +423,9 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh
|
|||||||
u64 titleId = exhi->aci.local_caps.title_id;
|
u64 titleId = exhi->aci.local_caps.title_id;
|
||||||
if (R_SUCCEEDED(res = loadCode(exhi, programHandle, &mapped)))
|
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);
|
memcpy(&csh.name, csi->name, 8);
|
||||||
csh.program_id = titleId;
|
csh.program_id = titleId;
|
||||||
csh.text_addr = vaddr.text_addr;
|
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);
|
res = svcCreateProcess(outProcessHandle, codeset, exhi->aci.kernel_caps.descriptors, count);
|
||||||
svcCloseHandle(codeset);
|
svcCloseHandle(codeset);
|
||||||
res = R_SUCCEEDED(res) ? 0 : res;
|
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;
|
(void)ctx;
|
||||||
FS_ProgramInfo title;
|
FS_ProgramInfo title;
|
||||||
FS_ProgramInfo update;
|
FS_ProgramInfo update;
|
||||||
|
ControlApplicationMemoryModeOverrideConfig memModeOverride;
|
||||||
u32* cmdbuf;
|
u32* cmdbuf;
|
||||||
u16 cmdid;
|
u16 cmdid;
|
||||||
int res;
|
int res;
|
||||||
@ -480,6 +581,20 @@ void loaderHandleCommands(void *ctx)
|
|||||||
cmdbuf[2] = IPC_Desc_StaticBuffer(sizeof(ExHeader_Info), 0); //0x1000002;
|
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
|
cmdbuf[3] = (u32)&g_exheaderInfo; // official Loader makes a copy here, but this is isn't necessary
|
||||||
break;
|
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
|
default: // error
|
||||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||||
cmdbuf[1] = 0xD900182F;
|
cmdbuf[1] = 0xD900182F;
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
#include "hbldr.h"
|
#include "hbldr.h"
|
||||||
|
|
||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
bool isN3DS, isSdMode;
|
bool isN3DS, isSdMode, nextGamePatchDisabled;
|
||||||
|
|
||||||
// MAKE SURE fsreg has been init before calling this
|
// MAKE SURE fsreg has been init before calling this
|
||||||
static Result fsldrPatchPermissions(void)
|
static Result fsldrPatchPermissions(void)
|
||||||
@ -115,7 +115,7 @@ void initSystem(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "Loader", 1, loaderHandleCommands, false },
|
{ "Loader", 2, loaderHandleCommands, false },
|
||||||
{ "hb:ldr", 2, hbldrHandleCommands, true },
|
{ "hb:ldr", 2, hbldrHandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
@ -129,6 +129,8 @@ static_assert(ARGVBUF_SIZE > 2 * PATH_MAX, "Wrong 3DSX argv buffer size");
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
nextGamePatchDisabled = false;
|
||||||
|
|
||||||
// Loader doesn't use any input static buffer, so we should be fine
|
// Loader doesn't use any input static buffer, so we should be fine
|
||||||
u32 *sbuf = getThreadStaticBuffers();
|
u32 *sbuf = getThreadStaticBuffers();
|
||||||
sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0);
|
sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0);
|
||||||
|
|||||||
@ -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(!applyCodeIpsPatch(progId, code, size)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(CONFIG(PATCHGAMES))
|
if(CONFIG(PATCHGAMES) && !(isApp && nextGamePatchDisabled))
|
||||||
{
|
{
|
||||||
if (!isSysmodule)
|
if (!isSysmodule)
|
||||||
{
|
{
|
||||||
@ -952,6 +952,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextGamePatchDisabled = false;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|||||||
@ -42,7 +42,7 @@ enum singleOptions
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern u32 config, multiConfig, bootConfig;
|
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);
|
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);
|
bool loadTitleCodeSection(u64 progId, u8 *code, u32 size);
|
||||||
|
|||||||
@ -311,7 +311,11 @@ Result setAppCpuTimeLimit(s64 limit)
|
|||||||
// Prevent apps from enabling preemption on core1 (and kernel will
|
// Prevent apps from enabling preemption on core1 (and kernel will
|
||||||
// redirect apps threads from core 1 to 2).
|
// redirect apps threads from core 1 to 2).
|
||||||
if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS))
|
if (IS_N3DS && CONFIG(REDIRECTAPPTHREADS))
|
||||||
|
{
|
||||||
|
s64 disableThreadRedir = 0;
|
||||||
|
if (R_SUCCEEDED(svcGetSystemInfo(&disableThreadRedir, 0x10000, 0x181)) && !disableThreadRedir)
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceLimitType category = RESLIMIT_CPUTIME;
|
ResourceLimitType category = RESLIMIT_CPUTIME;
|
||||||
return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1);
|
return svcSetResourceLimitValues(g_manager.reslimits[0], &category, &limit, 1);
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export HBLDR_DEFAULT_3DSX_TITLE_NAME ?= "hblauncher_loader"
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
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
|
DATA := source/gdb/xml data
|
||||||
INCLUDES := include include/gdb include/menus include/redshift
|
INCLUDES := include include/gdb include/menus include/redshift
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
#include <3ds/types.h>
|
#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
|
/// Operations for svcControlService
|
||||||
typedef enum ServiceOp
|
typedef enum ServiceOp
|
||||||
{
|
{
|
||||||
@ -71,18 +75,20 @@ void svcInvalidateEntireInstructionCache(void);
|
|||||||
///@{
|
///@{
|
||||||
/**
|
/**
|
||||||
* @brief Maps a block of process memory.
|
* @brief Maps a block of process memory.
|
||||||
* @param process Handle of the process.
|
* @param dstProcessHandle Handle of the process to map the memory in (destination)
|
||||||
* @param destAddress Address of the mapped block in the current process.
|
* @param destAddress Start address of the memory block in the destination process
|
||||||
* @param srcAddress Address of the mapped block in the source process.
|
* @param srcProcessHandle Handle of the process to map the memory from (source)
|
||||||
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
|
* @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.
|
* @brief Unmaps a block of process memory.
|
||||||
* @param process Handle of the process.
|
* @param process Handle of the process to unmap the memory from
|
||||||
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
|
* @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).
|
* @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);
|
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
|
||||||
|
|
||||||
@ -104,6 +110,20 @@ Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
|
|||||||
* @sa svcControlMemory
|
* @sa svcControlMemory
|
||||||
*/
|
*/
|
||||||
Result svcControlMemoryEx(u32* addr_out, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
|
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
|
///@name System
|
||||||
@ -134,4 +154,31 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
|
|||||||
* @param in The input handle.
|
* @param in The input handle.
|
||||||
*/
|
*/
|
||||||
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
|
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);
|
||||||
///@}
|
///@}
|
||||||
|
|||||||
@ -77,6 +77,7 @@
|
|||||||
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
|
||||||
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
|
||||||
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
|
||||||
|
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
|
||||||
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
|
||||||
|
|
||||||
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
#define DRAW_MAX_FORMATTED_STRING_SIZE 512
|
||||||
|
|||||||
@ -12,11 +12,15 @@
|
|||||||
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
|
||||||
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_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(SyncRequestInfo);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
|
||||||
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
|
||||||
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc);
|
||||||
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetThreadPriority);
|
||||||
|
|
||||||
GDB_DECLARE_QUERY_HANDLER(Rcmd);
|
GDB_DECLARE_QUERY_HANDLER(Rcmd);
|
||||||
|
|||||||
@ -38,3 +38,4 @@ MyThread *inputRedirectionCreateThread(void);
|
|||||||
void inputRedirectionThreadMain(void);
|
void inputRedirectionThreadMain(void);
|
||||||
Result InputRedirection_Disable(s64 timeout);
|
Result InputRedirection_Disable(s64 timeout);
|
||||||
Result InputRedirection_DoOrUndoPatches(void);
|
Result InputRedirection_DoOrUndoPatches(void);
|
||||||
|
|
||||||
|
|||||||
@ -56,3 +56,4 @@ enum multiOptions
|
|||||||
|
|
||||||
void LumaConfig_ConvertComboToString(char *out, u32 combo);
|
void LumaConfig_ConvertComboToString(char *out, u32 combo);
|
||||||
Result LumaConfig_SaveSettings(void);
|
Result LumaConfig_SaveSettings(void);
|
||||||
|
void LumaConfig_RequestSaveSettings(void);
|
||||||
|
|||||||
@ -90,3 +90,6 @@ void menuEnter(void);
|
|||||||
void menuLeave(void);
|
void menuLeave(void);
|
||||||
void menuThreadMain(void);
|
void menuThreadMain(void);
|
||||||
void menuShow(Menu *root);
|
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);
|
||||||
|
|||||||
5
sysmodules/rosalina/include/plugin.h
Normal file
5
sysmodules/rosalina/include/plugin.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "plugin/plgloader.h"
|
||||||
|
#include "plugin/plgldr.h"
|
||||||
|
#include "plugin/3gx.h"
|
||||||
76
sysmodules/rosalina/include/plugin/3gx.h
Normal file
76
sysmodules/rosalina/include/plugin/3gx.h
Normal file
@ -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);
|
||||||
78
sysmodules/rosalina/include/plugin/plgldr.h
Normal file
78
sysmodules/rosalina/include/plugin/plgldr.h
Normal file
@ -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);
|
||||||
105
sysmodules/rosalina/include/plugin/plgloader.h
Normal file
105
sysmodules/rosalina/include/plugin/plgloader.h
Normal file
@ -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;
|
||||||
31
sysmodules/rosalina/include/sleep.h
Normal file
31
sysmodules/rosalina/include/sleep.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
@ -77,3 +77,4 @@ void server_run(struct sock_server *serv);
|
|||||||
void server_kill_connections(struct sock_server *serv);
|
void server_kill_connections(struct sock_server *serv);
|
||||||
void server_set_should_close_all(struct sock_server *serv);
|
void server_set_should_close_all(struct sock_server *serv);
|
||||||
void server_finalize(struct sock_server *serv);
|
void server_finalize(struct sock_server *serv);
|
||||||
|
bool Wifi__IsConnected(void);
|
||||||
@ -29,6 +29,7 @@
|
|||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/srv.h>
|
#include <3ds/srv.h>
|
||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
|
#include <3ds/ipc.h>
|
||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
#include "luma_shared_config.h"
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
@ -60,6 +61,24 @@ static inline void *decodeArmBranch(const void *src)
|
|||||||
return (void *)((const u8 *)src + 8 + off);
|
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)
|
static inline bool isServiceUsable(const char *name)
|
||||||
{
|
{
|
||||||
bool r;
|
bool r;
|
||||||
|
|||||||
@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
|
|||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
SVC_BEGIN svcMapProcessMemoryEx
|
SVC_BEGIN svcMapProcessMemoryEx
|
||||||
|
str r4, [sp, #-4]!
|
||||||
|
ldr r4, [sp, #4]
|
||||||
svc 0xA0
|
svc 0xA0
|
||||||
|
ldr r4, [sp], #4
|
||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
@ -79,6 +82,19 @@ SVC_BEGIN svcControlMemoryEx
|
|||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
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_BEGIN svcControlService
|
||||||
svc 0xB0
|
svc 0xB0
|
||||||
bx lr
|
bx lr
|
||||||
@ -99,3 +115,8 @@ SVC_BEGIN svcTranslateHandle
|
|||||||
str r1, [r2]
|
str r1, [r2]
|
||||||
bx lr
|
bx lr
|
||||||
SVC_END
|
SVC_END
|
||||||
|
|
||||||
|
SVC_BEGIN svcControlProcess
|
||||||
|
svc 0xB3
|
||||||
|
bx lr
|
||||||
|
SVC_END
|
||||||
|
|||||||
@ -34,12 +34,6 @@
|
|||||||
|
|
||||||
extern Handle preTerminationEvent;
|
extern Handle preTerminationEvent;
|
||||||
|
|
||||||
static inline void assertSuccess(Result res)
|
|
||||||
{
|
|
||||||
if(R_FAILED(res))
|
|
||||||
svcBreak(USERBREAK_PANIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
static MyThread errDispThread;
|
static MyThread errDispThread;
|
||||||
static u8 ALIGN(8) errDispThreadStack[0xD00];
|
static u8 ALIGN(8) errDispThreadStack[0xD00];
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "csvc.h"
|
#include "csvc.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "gdb/breakpoints.h"
|
#include "gdb/breakpoints.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
|
|
||||||
@ -19,12 +20,16 @@ struct
|
|||||||
GDBCommandHandler handler;
|
GDBCommandHandler handler;
|
||||||
} remoteCommandHandlers[] =
|
} remoteCommandHandlers[] =
|
||||||
{
|
{
|
||||||
|
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
|
||||||
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
|
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
|
||||||
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
|
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
|
||||||
|
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
|
||||||
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
|
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
|
||||||
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
|
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
|
||||||
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
|
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
|
||||||
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
|
{ "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)
|
static const char *GDB_SkipSpaces(const char *pos)
|
||||||
@ -34,6 +39,50 @@ static const char *GDB_SkipSpaces(const char *pos)
|
|||||||
return nextpos;
|
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)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
|
||||||
{
|
{
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||||
@ -118,6 +167,29 @@ end:
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
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)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
@ -126,10 +198,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
|||||||
int n;
|
int n;
|
||||||
Result r;
|
Result r;
|
||||||
u32 kernelAddr;
|
u32 kernelAddr;
|
||||||
|
s64 token;
|
||||||
Handle handle, process;
|
Handle handle, process;
|
||||||
s64 refcountRaw;
|
s64 refcountRaw;
|
||||||
u32 refcount;
|
u32 refcount;
|
||||||
char classBuf[32], serviceBuf[12] = { 0 };
|
char classBuf[32], serviceBuf[12] = {0}, ownerBuf[50] = { 0 };
|
||||||
char outbuf[GDB_BUF_LEN / 2 + 1];
|
char outbuf[GDB_BUF_LEN / 2 + 1];
|
||||||
|
|
||||||
if(ctx->commandData[0] == 0)
|
if(ctx->commandData[0] == 0)
|
||||||
@ -161,12 +234,29 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
|
|||||||
|
|
||||||
svcTranslateHandle(&kernelAddr, classBuf, handle);
|
svcTranslateHandle(&kernelAddr, classBuf, handle);
|
||||||
svcGetHandleInfo(&refcountRaw, handle, 1);
|
svcGetHandleInfo(&refcountRaw, handle, 1);
|
||||||
|
svcGetHandleInfo(&token, handle, 0x10001);
|
||||||
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
|
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
|
||||||
refcount = (u32)(refcountRaw - 1);
|
refcount = (u32)(refcountRaw - 1);
|
||||||
|
|
||||||
if(serviceBuf[0] != 0)
|
if(serviceBuf[0] != 0)
|
||||||
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
|
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
|
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:
|
end:
|
||||||
svcCloseHandle(handle);
|
svcCloseHandle(handle);
|
||||||
@ -174,6 +264,68 @@ end:
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
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;
|
extern bool isN3DS;
|
||||||
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
|
||||||
{
|
{
|
||||||
@ -249,6 +401,53 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
|
|||||||
return GDB_SendHexPacket(ctx, outbuf, n);
|
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)
|
GDB_DECLARE_QUERY_HANDLER(Rcmd)
|
||||||
{
|
{
|
||||||
char commandData[GDB_BUF_LEN / 2 + 1];
|
char commandData[GDB_BUF_LEN / 2 + 1];
|
||||||
|
|||||||
@ -107,7 +107,7 @@ int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid)
|
|||||||
return sprintf(outbuf, "%lx", tid);
|
return sprintf(outbuf, "%lx", tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId)
|
s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId)
|
||||||
{
|
{
|
||||||
Handle process, thread;
|
Handle process, thread;
|
||||||
Result r;
|
Result r;
|
||||||
|
|||||||
@ -32,6 +32,8 @@
|
|||||||
#include "process_patches.h"
|
#include "process_patches.h"
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "sleep.h"
|
||||||
|
#include "sock_util.h"
|
||||||
|
|
||||||
bool inputRedirectionEnabled = false;
|
bool inputRedirectionEnabled = false;
|
||||||
Handle inputRedirectionThreadStartedEvent;
|
Handle inputRedirectionThreadStartedEvent;
|
||||||
@ -121,6 +123,13 @@ void inputRedirectionThreadMain(void)
|
|||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
pfd.revents = 0;
|
pfd.revents = 0;
|
||||||
|
|
||||||
|
if (Sleep__Status())
|
||||||
|
{
|
||||||
|
while (!Wifi__IsConnected()
|
||||||
|
&& inputRedirectionEnabled && !preTerminationRequested)
|
||||||
|
svcSleepThread(1000000000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
int pollres = socPoll(&pfd, 1, 10);
|
int pollres = socPoll(&pfd, 1, 10);
|
||||||
if(pollres > 0 && (pfd.revents & POLLIN))
|
if(pollres > 0 && (pfd.revents & POLLIN))
|
||||||
{
|
{
|
||||||
@ -214,7 +223,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
|
|||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
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)
|
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);
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
|
||||||
if(off == NULL)
|
if(off == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +257,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
|
|||||||
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
|
||||||
if(off2 == NULL)
|
if(off2 == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,7 +265,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
|
|||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
|
||||||
if(off3 == NULL)
|
if(off3 == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +324,7 @@ static Result InputRedirection_DoUndoIrPatches(Handle processHandle, bool doPatc
|
|||||||
}
|
}
|
||||||
|
|
||||||
svcInvalidateEntireInstructionCache();
|
svcInvalidateEntireInstructionCache();
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -347,28 +356,28 @@ static Result InputRedirection_DoUndoHidPatches(Handle processHandle, bool doPat
|
|||||||
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
|
||||||
|
|
||||||
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
|
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)
|
if (R_SUCCEEDED(res) && !patchPrepared)
|
||||||
{
|
{
|
||||||
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
|
||||||
if(off == NULL)
|
if(off == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
|
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
|
||||||
if(off2 == NULL)
|
if(off2 == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
|
||||||
if(off3 == NULL)
|
if(off3 == NULL)
|
||||||
{
|
{
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return -3;
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
#include "config_template_ini.h"
|
#include "config_template_ini.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
|
#include "plugin/plgloader.h"
|
||||||
|
|
||||||
typedef struct CfgData {
|
typedef struct CfgData {
|
||||||
u16 formatVersionMajor, formatVersionMinor;
|
u16 formatVersionMajor, formatVersionMinor;
|
||||||
@ -41,6 +42,7 @@ typedef struct CfgData {
|
|||||||
|
|
||||||
u64 hbldr3dsxTitleId;
|
u64 hbldr3dsxTitleId;
|
||||||
u32 rosalinaMenuCombo;
|
u32 rosalinaMenuCombo;
|
||||||
|
u32 pluginLoaderFlags;
|
||||||
s16 ntpTzOffetMinutes;
|
s16 ntpTzOffetMinutes;
|
||||||
|
|
||||||
ScreenFilter topScreenFilter;
|
ScreenFilter topScreenFilter;
|
||||||
@ -50,6 +52,8 @@ typedef struct CfgData {
|
|||||||
u8 autobootCtrAppmemtype;
|
u8 autobootCtrAppmemtype;
|
||||||
} CfgData;
|
} CfgData;
|
||||||
|
|
||||||
|
bool saveSettingsRequest = false;
|
||||||
|
|
||||||
void LumaConfig_ConvertComboToString(char *out, u32 combo)
|
void LumaConfig_ConvertComboToString(char *out, u32 combo)
|
||||||
{
|
{
|
||||||
static const char *keys[] = {
|
static const char *keys[] = {
|
||||||
@ -170,7 +174,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg)
|
|||||||
pinNumDigits, n3dsCpuStr,
|
pinNumDigits, n3dsCpuStr,
|
||||||
autobootModeStr,
|
autobootModeStr,
|
||||||
|
|
||||||
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr,
|
cfg->hbldr3dsxTitleId, rosalinaMenuComboStr, (int)(cfg->pluginLoaderFlags & 1),
|
||||||
(int)cfg->ntpTzOffetMinutes,
|
(int)cfg->ntpTzOffetMinutes,
|
||||||
|
|
||||||
(int)cfg->topScreenFilter.cct, (int)cfg->bottomScreenFilter.cct,
|
(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;
|
return n < 0 ? 0 : (size_t)n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LumaConfig_RequestSaveSettings(void) {
|
||||||
|
saveSettingsRequest = true;
|
||||||
|
}
|
||||||
|
|
||||||
Result LumaConfig_SaveSettings(void)
|
Result LumaConfig_SaveSettings(void)
|
||||||
{
|
{
|
||||||
char inibuf[0x2000];
|
char inibuf[0x2000];
|
||||||
@ -238,6 +246,7 @@ Result LumaConfig_SaveSettings(void)
|
|||||||
configData.splashDurationMsec = splashDurationMsec;
|
configData.splashDurationMsec = splashDurationMsec;
|
||||||
configData.hbldr3dsxTitleId = Luma_SharedConfig->selected_hbldr_3dsx_tid;
|
configData.hbldr3dsxTitleId = Luma_SharedConfig->selected_hbldr_3dsx_tid;
|
||||||
configData.rosalinaMenuCombo = menuCombo;
|
configData.rosalinaMenuCombo = menuCombo;
|
||||||
|
configData.pluginLoaderFlags = PluginLoader__IsEnabled();
|
||||||
configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset;
|
configData.ntpTzOffetMinutes = (s16)lastNtpTzOffset;
|
||||||
configData.topScreenFilter = topScreenFilter;
|
configData.topScreenFilter = topScreenFilter;
|
||||||
configData.bottomScreenFilter = bottomScreenFilter;
|
configData.bottomScreenFilter = bottomScreenFilter;
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
#include "service_manager.h"
|
#include "service_manager.h"
|
||||||
#include "errdisp.h"
|
#include "errdisp.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "sleep.h"
|
||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
#include "menus/debugger.h"
|
#include "menus/debugger.h"
|
||||||
@ -43,6 +44,7 @@
|
|||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
|
|
||||||
#include "task_runner.h"
|
#include "task_runner.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
bool isN3DS;
|
bool isN3DS;
|
||||||
|
|
||||||
@ -63,6 +65,7 @@ void __wrap_exit(int rc)
|
|||||||
// Kernel will take care of it all
|
// Kernel will take care of it all
|
||||||
/*
|
/*
|
||||||
pmDbgExit();
|
pmDbgExit();
|
||||||
|
acExit();
|
||||||
fsExit();
|
fsExit();
|
||||||
svcCloseHandle(*fsRegGetSessionHandle());
|
svcCloseHandle(*fsRegGetSessionHandle());
|
||||||
srvExit();
|
srvExit();
|
||||||
@ -161,6 +164,9 @@ static void handleSleepNotification(u32 notificationId)
|
|||||||
|
|
||||||
static void handleShellNotification(u32 notificationId)
|
static void handleShellNotification(u32 notificationId)
|
||||||
{
|
{
|
||||||
|
// Quick dirty fix
|
||||||
|
Sleep__HandleNotification(notificationId);
|
||||||
|
|
||||||
if (notificationId == 0x213) {
|
if (notificationId == 0x213) {
|
||||||
// Shell opened
|
// Shell opened
|
||||||
// Note that this notification is also fired on system init.
|
// Note that this notification is also fired on system init.
|
||||||
@ -223,6 +229,7 @@ static void handleRestartHbAppNotification(u32 notificationId)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
|
{ "plg:ldr", 1, PluginLoader__HandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -239,11 +246,16 @@ static const ServiceManagerNotificationEntry notifications[] = {
|
|||||||
{ 0x214, handleShellNotification },
|
{ 0x214, handleShellNotification },
|
||||||
{ 0x1000, handleNextApplicationDebuggedByForce },
|
{ 0x1000, handleNextApplicationDebuggedByForce },
|
||||||
{ 0x2000, handlePreTermNotification },
|
{ 0x2000, handlePreTermNotification },
|
||||||
|
{ 0x1001, PluginLoader__HandleKernelEvent },
|
||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Some changes to commit
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
Sleep__Init();
|
||||||
|
PluginLoader__Init();
|
||||||
|
|
||||||
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
|
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
|
||||||
svcBreak(USERBREAK_ASSERT);
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
|
|||||||
@ -32,9 +32,11 @@
|
|||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "menus.h"
|
#include "menus.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "luma_config.h"
|
||||||
#include "menus/n3ds.h"
|
#include "menus/n3ds.h"
|
||||||
#include "menus/cheats.h"
|
#include "menus/cheats.h"
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "menus/screen_filters.h"
|
#include "menus/screen_filters.h"
|
||||||
#include "shell.h"
|
#include "shell.h"
|
||||||
|
|
||||||
@ -257,6 +259,9 @@ MyThread *menuCreateThread(void)
|
|||||||
return &menuThread;
|
return &menuThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 menuCombo;
|
||||||
|
u32 g_blockMenuOpen = 0;
|
||||||
|
|
||||||
void menuThreadMain(void)
|
void menuThreadMain(void)
|
||||||
{
|
{
|
||||||
if(isN3DS)
|
if(isN3DS)
|
||||||
@ -279,13 +284,19 @@ void menuThreadMain(void)
|
|||||||
|
|
||||||
Cheat_ApplyCheats();
|
Cheat_ApplyCheats();
|
||||||
|
|
||||||
if((scanHeldKeys() & menuCombo) == menuCombo)
|
if(((scanHeldKeys() & menuCombo) == menuCombo) && !g_blockMenuOpen)
|
||||||
{
|
{
|
||||||
menuEnter();
|
menuEnter();
|
||||||
if(isN3DS) N3DSMenu_UpdateStatus();
|
if(isN3DS) N3DSMenu_UpdateStatus();
|
||||||
|
PluginLoader__UpdateMenu();
|
||||||
menuShow(&rosalinaMenu);
|
menuShow(&rosalinaMenu);
|
||||||
menuLeave();
|
menuLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saveSettingsRequest) {
|
||||||
|
LumaConfig_SaveSettings();
|
||||||
|
saveSettingsRequest = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
#include "menus/sysconfig.h"
|
#include "menus/sysconfig.h"
|
||||||
#include "menus/screen_filters.h"
|
#include "menus/screen_filters.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
@ -48,6 +49,7 @@ Menu rosalinaMenu = {
|
|||||||
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
{ "Take screenshot", METHOD, .method = &RosalinaMenu_TakeScreenshot },
|
||||||
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
|
{ "Change screen brightness", METHOD, .method = &RosalinaMenu_ChangeScreenBrightness },
|
||||||
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
|
||||||
|
{ "", METHOD, .method = PluginLoader__MenuCallback},
|
||||||
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
|
||||||
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
{ "Debugger options...", MENU, .menu = &debuggerMenu },
|
||||||
{ "System configuration...", MENU, .menu = &sysconfigMenu },
|
{ "System configuration...", MENU, .menu = &sysconfigMenu },
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "pmdbgext.h"
|
#include "pmdbgext.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "process_patches.h"
|
#include "process_patches.h"
|
||||||
|
|
||||||
typedef struct DspFirmSegmentHeader {
|
typedef struct DspFirmSegmentHeader {
|
||||||
|
|||||||
@ -245,8 +245,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
|
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
|
||||||
heapTotalSize = mem.size;
|
heapTotalSize = mem.size;
|
||||||
|
|
||||||
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
|
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
|
||||||
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
|
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
|
||||||
|
|
||||||
bool codeAvailable = R_SUCCEEDED(codeRes);
|
bool codeAvailable = R_SUCCEEDED(codeRes);
|
||||||
bool heapAvailable = R_SUCCEEDED(heapRes);
|
bool heapAvailable = R_SUCCEEDED(heapRes);
|
||||||
@ -613,9 +613,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(codeAvailable)
|
if(codeAvailable)
|
||||||
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
|
||||||
if(heapAvailable)
|
if(heapAvailable)
|
||||||
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
|
||||||
|
|
||||||
svcCloseHandle(processHandle);
|
svcCloseHandle(processHandle);
|
||||||
}
|
}
|
||||||
|
|||||||
173
sysmodules/rosalina/source/plugin/3gx.c
Normal file
173
sysmodules/rosalina/source/plugin/3gx.c
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#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();
|
||||||
|
}
|
||||||
130
sysmodules/rosalina/source/plugin/display.c
Normal file
130
sysmodules/rosalina/source/plugin/display.c
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include "menu.h"
|
||||||
|
#include "draw.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
309
sysmodules/rosalina/source/plugin/file_loader.c
Normal file
309
sysmodules/rosalina/source/plugin/file_loader.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
281
sysmodules/rosalina/source/plugin/memoryblock.c
Normal file
281
sysmodules/rosalina/source/plugin/memoryblock.c
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
339
sysmodules/rosalina/source/plugin/plgldr.c
Normal file
339
sysmodules/rosalina/source/plugin/plgldr.c
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
#include <3ds.h>
|
||||||
|
#include "plugin.h"
|
||||||
|
#include <string.h>
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
||||||
593
sysmodules/rosalina/source/plugin/plgloader.c
Normal file
593
sysmodules/rosalina/source/plugin/plgloader.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
36
sysmodules/rosalina/source/plugin/pluginLoader.s
Normal file
36
sysmodules/rosalina/source/plugin/pluginLoader.s
Normal file
@ -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
|
||||||
111
sysmodules/rosalina/source/plugin/swapFunc.s
Normal file
111
sysmodules/rosalina/source/plugin/swapFunc.s
Normal file
@ -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
|
||||||
@ -77,7 +77,7 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func)
|
|||||||
|
|
||||||
// NOTE: we suppose .text, .rodata, .data+.bss are contiguous & in that order
|
// NOTE: we suppose .text, .rodata, .data+.bss are contiguous & in that order
|
||||||
u32 totalSize = (u32)(textSize + roSize + rwSize);
|
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);
|
svcCloseHandle(processHandle);
|
||||||
return res;
|
return res;
|
||||||
@ -85,6 +85,6 @@ Result OperateOnProcessByName(const char *name, OperateOnProcessCb func)
|
|||||||
|
|
||||||
res = func(processHandle, (u32)textSize, (u32)roSize, (u32)rwSize);
|
res = func(processHandle, (u32)textSize, (u32)roSize, (u32)rwSize);
|
||||||
|
|
||||||
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
|
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
76
sysmodules/rosalina/source/sleep.c
Normal file
76
sysmodules/rosalina/source/sleep.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
@ -9,10 +9,12 @@
|
|||||||
#include <3ds/result.h>
|
#include <3ds/result.h>
|
||||||
#include <3ds/svc.h>
|
#include <3ds/svc.h>
|
||||||
#include <3ds/synchronization.h>
|
#include <3ds/synchronization.h>
|
||||||
|
#include <3ds/services/ac.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
#include "sock_util.h"
|
#include "sock_util.h"
|
||||||
|
#include "sleep.h"
|
||||||
|
|
||||||
extern Handle preTerminationEvent;
|
extern Handle preTerminationEvent;
|
||||||
extern bool preTerminationRequested;
|
extern bool preTerminationRequested;
|
||||||
@ -185,6 +187,14 @@ void server_run(struct sock_server *serv)
|
|||||||
|
|
||||||
for(nfds_t i = 0; i < serv->nfds; i++)
|
for(nfds_t i = 0; i < serv->nfds; i++)
|
||||||
fds[i].revents = 0;
|
fds[i].revents = 0;
|
||||||
|
|
||||||
|
if (Sleep__Status())
|
||||||
|
{
|
||||||
|
while (!Wifi__IsConnected()
|
||||||
|
&& serv->running && !preTerminationRequested)
|
||||||
|
svcSleepThread(1000000000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
int pollres = socPoll(fds, serv->nfds, 50);
|
int pollres = socPoll(fds, serv->nfds, 50);
|
||||||
|
|
||||||
if(server_should_exit(serv) || pollres < -10000)
|
if(server_should_exit(serv) || pollres < -10000)
|
||||||
@ -321,3 +331,13 @@ void server_finalize(struct sock_server *serv)
|
|||||||
svcClearEvent(serv->started_event);
|
svcClearEvent(serv->started_event);
|
||||||
svcCloseHandle(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;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user