Merge branch 'Nanquitas-master'

This commit is contained in:
PabloMK7 2019-06-29 16:26:26 +02:00
commit 0b68baa0dd
54 changed files with 2703 additions and 88 deletions

View File

@ -126,6 +126,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} info;
};
@ -195,6 +196,7 @@ u32 installK11Extension(u8 *pos, u32 size, bool needToInitSd, u32 baseK11VA, u32
info->bootConfig = configData.bootConfig;
info->hbldr3dsxTitleId = configData.hbldr3dsxTitleId;
info->rosalinaMenuCombo = configData.rosalinaMenuCombo;
info->rosalinaFlags = configData.rosalinaFlags;
info->versionMajor = VERSION_MAJOR;
info->versionMinor = VERSION_MINOR;
info->versionBuild = VERSION_BUILD;
@ -221,6 +223,10 @@ u32 patchKernel11(u8 *pos, u32 size, u32 baseK11VA, u32 *arm11SvcTable, u32 *arm
u8 *ControlMemoryPos = instrPos + 8 + displ;
u32 *off;
// Patch ControlMemory bounds checks for mem mapping
for (off = (u32 *)ControlMemoryPos; *off != 0xE0E01BF5; ++off);
*off = 0;
/*
Here we replace currentProcess->processID == 1 by additionnalParameter == 1.
This patch should be generic enough to work even on firmware version 5.0.

View File

@ -69,6 +69,7 @@ typedef struct __attribute__((packed, aligned(4)))
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} CfgData;
typedef struct

View File

@ -159,7 +159,7 @@ def main(args=None):
print("{0:<15}{1:<20}Access type: {2}".format("FAR", "{0:08x}".format(registers[19]), "Write" if registers[17] & (1 << 11) != 0 else "Read"))
thumb = registers[16] & 0x20 != 0
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
addr = registers[15] - codeDumpSize / 2 + (2 if thumb else 4)
print("\nCode dump:\n")
@ -167,8 +167,9 @@ def main(args=None):
try:
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
if os.name == "nt" and path[0] == '/':
path = ''.join((path[1], ':', path[2:]))
path = ''.join(('c:', path[0], path[5:]))
objdump_res = subprocess.check_output((
path, "-marm", "-b", "binary",
@ -176,6 +177,7 @@ def main(args=None):
"--stop-address="+hex(addr + codeDumpSize), "-D", "-z", "-M",
"reg-names-std" + (",force-thumb" if thumb else ""), args.filename
)).decode("utf-8")
objdump_res = '\n'.join(objdump_res[objdump_res.find('<.data+'):].split('\n')[1:])
except: objdump_res = ""

View File

@ -44,8 +44,12 @@ extern KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *
extern void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
extern Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
extern Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
extern Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
extern Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
extern Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
extern Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
extern Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
extern Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
extern Result (*KEvent__Clear)(KEvent *this);
extern void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
extern void (*KObjectMutex__ErrorOccured)(void);
@ -53,8 +57,11 @@ extern void (*KObjectMutex__ErrorOccured)(void);
extern void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
extern void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
extern void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
extern Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
extern void (*SleepThread)(s64 ns);
extern Result (*CreateEvent)(Handle *out, ResetType resetType);
extern Result (*CloseHandle)(Handle handle);
extern Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
extern Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@ -65,6 +72,7 @@ extern Result (*SendSyncRequest)(Handle handle);
extern Result (*OpenProcess)(Handle *out, u32 processId);
extern Result (*GetProcessId)(u32 *out, Handle process);
extern Result (*DebugActiveProcess)(Handle *out, u32 processId);
extern Result (*SignalEvent)(Handle event);
extern Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
extern Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@ -129,9 +137,12 @@ typedef struct CfwInfo
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} CfwInfo;
extern CfwInfo cfwInfo;
extern vu32 rosalinaState;
extern bool hasStartedRosalinaNetworkFuncsOnce;
KLinkedList* KLinkedList__Initialize(KLinkedList *list);

View File

@ -105,6 +105,14 @@ typedef struct ALIGN(4) KMutex
union KProcess *owner;
} KMutex;
typedef struct KAddressArbiter
{
KAutoObject autoObject;
struct KThread *first;
struct KThread *last;
union KProcess *owner;
} KAddressArbiter;
/* 92 */
typedef struct KMutexLinkedList
{
@ -112,6 +120,30 @@ typedef struct KMutexLinkedList
KMutex *last;
} KMutexLinkedList;
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KADDRESSARBITER = 0x98,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
/* 45 */
typedef struct KClassToken
{
@ -540,6 +572,20 @@ typedef struct KBlockInfo
u32 pageCount;
} KBlockInfo;
typedef struct KSharedMemory
{
KAutoObject autoObject;
KLinkedList ownedKBlockInfo;
union KProcess *owner;
u32 ownerPermissions;
u32 otherPermissions;
u8 isBlockInfoGenerated;
s8 allBlockInfoGenerated;
u8 unknown_1;
u8 unknown_2;
u32 address;
} KSharedMemory;
/* 25 */
typedef struct KMemoryBlock
{
@ -1037,10 +1083,26 @@ typedef struct KProcess##sys\
KThread *mainThread;\
u32 interruptEnabledFlags[4];\
KProcessHandleTable handleTable;\
u8 gap234[52];\
/* Custom fields for plugin system
{ */ \
u32 customFlags; /* see KProcess_CustomFlags enum below */ \
Handle onMemoryLayoutChangeEvent;\
Handle onProcessExitEvent;\
Handle resumeProcessExitEvent;\
/* } */ \
u8 gap234[36];\
u64 unused;\
} KProcess##sys;
enum KProcess_CustomFlags
{
ForceRWXPages = 1 << 0,
SignalOnMemLayoutChanges = 1 << 1,
SignalOnExit = 1 << 2,
MemLayoutChanged = 1 << 16
};
INSTANCIATE_KPROCESS(N3DS);
INSTANCIATE_KPROCESS(O3DS8x);
INSTANCIATE_KPROCESS(O3DSPre8x);

129
k11_extension/include/mmu.h Normal file
View 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);

View 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_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
PROCESSOP_SCHEDULE_THREADS,
} ProcessOp;
Result ControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);

View File

@ -30,4 +30,5 @@
#include "kernel.h"
#include "svc.h"
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size);
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);
Result MapProcessMemoryExWrapper(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size);

View File

@ -28,10 +28,43 @@
#include "fatalExceptionHandlers.h"
#include "utils.h"
#include "kernel.h"
#include "memory.h"
#include "mmu.h"
#include "globals.h"
#define REG_DUMP_SIZE 4 * 23
#define CODE_DUMP_SIZE 48
#define CODE_DUMP_SIZE 96
// Return true if parameters are invalid
static bool checkExceptionHandlerValidity(KProcess *process, vu32 *threadLocalStorage)
{
if (process == NULL)
return true;
u32 stackBottom = threadLocalStorage[0x11];
u32 exceptionBuf = threadLocalStorage[0x12];
MemoryInfo memInfo;
PageInfo pageInfo;
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 perm = KProcessHwInfo__GetAddressUserPerm(hwInfo, threadLocalStorage[0x10]);
if (stackBottom != 1)
{
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)stackBottom)
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
return true;
}
if (exceptionBuf > 1)
{
if (KProcessHwInfo__QueryMemory(hwInfo, &memInfo, &pageInfo, (void *)exceptionBuf)
|| (memInfo.permissions & MEMPERM_RW) != MEMPERM_RW)
return true;
}
return (perm & MEMPERM_RX) != MEMPERM_RX;
}
bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
{
@ -43,7 +76,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
return false;
return checkExceptionHandlerValidity(currentProcess, (vu32 *)thread->threadLocalStorage);
if(currentProcess != NULL)
{
@ -52,7 +85,7 @@ bool isExceptionFatal(u32 spsr, u32 *regs, u32 index)
thread = KPROCESS_GET_RVALUE(currentProcess, mainThread);
if(thread != NULL && thread->threadLocalStorage != NULL && *((vu32 *)thread->threadLocalStorage + 0x10) != 0)
return false;
return checkExceptionHandlerValidity(currentProcess, thread->threadLocalStorage);
if(index == 3 && strcmp(codeSetOfProcess(currentProcess)->processName, "menu") == 0 && // workaround a Home Menu bug leading to a dabort
regs[0] == 0x3FFF && regs[2] == 0 && regs[5] == 2 && regs[7] == 1)
@ -70,6 +103,7 @@ bool isDataAbortExceptionRangeControlled(u32 spsr, u32 addr)
((u32)safecpy <= addr && addr < (u32)safecpy + safecpy_sz)
);
}
void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
{
ExceptionDumpHeader dumpHeader;
@ -96,7 +130,7 @@ void fatalExceptionHandlersMain(u32 *registerDump, u32 type, u32 cpuId)
registerDump[15] = pc;
//Dump code
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - (dumpHeader.codeDumpSize >> 1) ; //wouldn't work well on 32-bit Thumb instructions, but it isn't much of a problem
dumpHeader.codeDumpSize = ((u32)instr & (((cpsr & 0x20) != 0) ? 1 : 3)) != 0 ? 0 : safecpy(codeDump, instr, dumpHeader.codeDumpSize);
//Copy register dump and code dump

View File

@ -40,8 +40,12 @@ KAutoObject * (*KProcessHandleTable__ToKAutoObject)(KProcessHandleTable *this, H
void (*KSynchronizationObject__Signal)(KSynchronizationObject *this, bool isPulse);
Result (*WaitSynchronization1)(void *this_unused, KThread *thread, KSynchronizationObject *syncObject, s64 timeout);
Result (*KProcessHandleTable__CreateHandle)(KProcessHandleTable *this, Handle *out, KAutoObject *obj, u8 token);
Result (*KProcessHwInfo__QueryMemory)(KProcessHwInfo *this, MemoryInfo *memoryInfo, PageInfo *pageInfo, void *address);
Result (*KProcessHwInfo__MapProcessMemory)(KProcessHwInfo *this, KProcessHwInfo *other, void *dst, void *src, u32 nbPages);
Result (*KProcessHwInfo__UnmapProcessMemory)(KProcessHwInfo *this, void *addr, u32 nbPages);
Result (*KProcessHwInfo__CheckVaState)(KProcessHwInfo *hwInfo, u32 va, u32 size, u32 state, u32 perm);
Result (*KProcessHwInfo__GetListOfKBlockInfoForVA)(KProcessHwInfo *hwInfo, KLinkedList *list, u32 va, u32 sizeInPage);
Result (*KProcessHwInfo__MapListOfKBlockInfo)(KProcessHwInfo *this, u32 va, KLinkedList *list, u32 state, u32 perm, u32 sbz);
Result (*KEvent__Clear)(KEvent *this);
void (*KObjectMutex__WaitAndAcquire)(KObjectMutex *this);
void (*KObjectMutex__ErrorOccured)(void);
@ -49,8 +53,11 @@ void (*KObjectMutex__ErrorOccured)(void);
void (*KScheduler__AdjustThread)(KScheduler *this, KThread *thread, u32 oldSchedulingMask);
void (*KScheduler__AttemptSwitchingThreadContext)(KScheduler *this);
void (*KLinkedList_KBlockInfo__Clear)(KLinkedList *list);
Result (*ControlMemory)(u32 *addrOut, u32 addr0, u32 addr1, u32 size, MemOp op, MemPerm perm, bool isLoader);
void (*SleepThread)(s64 ns);
Result (*CreateEvent)(Handle *out, ResetType resetType);
Result (*CloseHandle)(Handle handle);
Result (*GetHandleInfo)(s64 *out, Handle handle, u32 type);
Result (*GetSystemInfo)(s64 *out, s32 type, s32 param);
@ -61,6 +68,7 @@ Result (*SendSyncRequest)(Handle handle);
Result (*OpenProcess)(Handle *out, u32 processId);
Result (*GetProcessId)(u32 *out, Handle process);
Result (*DebugActiveProcess)(Handle *out, u32 processId);
Result (*SignalEvent)(Handle event);
Result (*UnmapProcessMemory)(Handle processHandle, void *dst, u32 size);
Result (*KernelSetState)(u32 type, u32 varg1, u32 varg2, u32 varg3);
@ -112,3 +120,10 @@ CfwInfo cfwInfo;
vu32 rosalinaState;
bool hasStartedRosalinaNetworkFuncsOnce;
KLinkedList* KLinkedList__Initialize(KLinkedList *list)
{
list->size = 0;
list->nodes.first = list->nodes.last = (KLinkedListNode *)&list->nodes;
return list;
}

View File

@ -98,11 +98,61 @@ void configHook(vu8 *cfgPage)
*isDevUnit = true; // enable debug features
}
void KProcessHwInfo__MapL1Section_Hook(void);
void KProcessHwInfo__MapL2Section_Hook(void);
static void installMmuHooks(void)
{
u32 *mapL1Section = NULL;
u32 *mapL2Section = NULL;
u32 *off;
for(off = (u32 *)officialSVCs[0x1F]; *off != 0xE1CD60F0; ++off);
off = decodeARMBranch(off + 1);
for (; *off != 0xE58D5000; ++off);
off = decodeARMBranch(off + 1);
for (; *off != 0xE58DC000; ++off);
off = decodeARMBranch(off + 1);
for (; *off != 0xE1A0000B; ++off);
off = decodeARMBranch(off + 1);
for (; *off != 0xE59D2030; ++off);
off = decodeARMBranch(off + 1);
for (; *off != 0xE88D1100; ++off);
mapL2Section = (u32 *)PA_FROM_VA_PTR(decodeARMBranch(off + 1));
do
{
for (; *off != 0xE58D8000; ++off);
u32 *loc = (u32 *)PA_FROM_VA_PTR(decodeARMBranch(++off));
if (loc != mapL2Section)
mapL1Section = loc;
} while (mapL1Section == NULL);
mapL1Section[1] = 0xE28FE004; // add lr, pc, #4
mapL1Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL1Section[3] = (u32)KProcessHwInfo__MapL1Section_Hook;
mapL2Section[1] = 0xE28FE004; // add lr, pc, #4
mapL2Section[2] = 0xE51FF004; // ldr pc, [pc, #-4]
mapL2Section[3] = (u32)KProcessHwInfo__MapL2Section_Hook;
}
static void findUsefulSymbols(void)
{
u32 *off;
for(off = (u32 *)0xFFFF0000; *off != 0xE1A0D002; off++);
// Patch ERRF__DumpException
for(off = (u32 *)0xFFFF0000; *off != 0xE1A04005; ++off);
++off;
*(u32 *)PA_FROM_VA_PTR(off) = makeARMBranch(off, off + 51, false);
for(; *off != 0xE2100102; ++off);
KProcessHwInfo__QueryMemory = (Result (*)(KProcessHwInfo *, MemoryInfo *, PageInfo *, void *))decodeARMBranch(off - 1);
for(; *off != 0xE1A0D002; off++);
off += 3;
initFPU = (void (*) (void))off;
@ -165,6 +215,18 @@ static void findUsefulSymbols(void)
for(off = (u32 *)officialSVCs[0x72]; *off != 0xE2041102; off++);
KProcessHwInfo__UnmapProcessMemory = (Result (*)(KProcessHwInfo *, void *, u32))decodeARMBranch(off - 1);
for (off = (u32 *)officialSVCs[0x70]; *off != 0xE8881200 && *off != 0xE8891900; ++off);
for (off = (u32 *)decodeARMBranch(off + 1); *off != 0xE2101102; ++off);
KProcessHwInfo__CheckVaState = (Result (*)(KProcessHwInfo *, u32, u32, u32, u32))decodeARMBranch(off - 1);
for (; *off != 0xE28D1008; ++off);
KProcessHwInfo__GetListOfKBlockInfoForVA = (Result (*)(KProcessHwInfo*, KLinkedList*, u32, u32))decodeARMBranch(off + 1);
for (; *off != 0xE2000102; ++off);
KProcessHwInfo__MapListOfKBlockInfo = (Result (*)(KProcessHwInfo*, u32, KLinkedList*, u32, u32, u32))decodeARMBranch(off - 1);
for (; *off != 0xE8BD8FF0; ++off);
KLinkedList_KBlockInfo__Clear = (void (*)(KLinkedList *))decodeARMBranch(off - 6);
for(off = (u32 *)officialSVCs[0x7C]; *off != 0x03530000; off++);
KObjectMutex__WaitAndAcquire = (void (*)(KObjectMutex *))decodeARMBranch(++off);
for(; *off != 0xE320F000; off++);
@ -210,6 +272,7 @@ static void findUsefulSymbols(void)
ControlMemory = (Result (*)(u32 *, u32, u32, u32, MemOp, MemPerm, bool))
decodeARMBranch((u32 *)officialSVCs[0x01] + 5);
SleepThread = (void (*)(s64))officialSVCs[0x0A];
CreateEvent = (Result (*)(Handle *, ResetType))decodeARMBranch((u32 *)officialSVCs[0x17] + 3);
CloseHandle = (Result (*)(Handle))officialSVCs[0x23];
GetHandleInfo = (Result (*)(s64 *, Handle, u32))decodeARMBranch((u32 *)officialSVCs[0x29] + 3);
GetSystemInfo = (Result (*)(s64 *, s32, s32))decodeARMBranch((u32 *)officialSVCs[0x2A] + 3);
@ -220,6 +283,7 @@ static void findUsefulSymbols(void)
OpenProcess = (Result (*)(Handle *, u32))decodeARMBranch((u32 *)officialSVCs[0x33] + 3);
GetProcessId = (Result (*)(u32 *, Handle))decodeARMBranch((u32 *)officialSVCs[0x35] + 3);
DebugActiveProcess = (Result (*)(Handle *, u32))decodeARMBranch((u32 *)officialSVCs[0x60] + 3);
SignalEvent = (Result (*)(Handle event))officialSVCs[0x18];
UnmapProcessMemory = (Result (*)(Handle, void *, u32))officialSVCs[0x72];
KernelSetState = (Result (*)(u32, u32, u32, u32))((u32 *)officialSVCs[0x7C] + 1);
@ -251,6 +315,8 @@ static void findUsefulSymbols(void)
invalidateInstructionCacheRange = (void (*)(void *, u32))off2;
}
}
installMmuHooks();
}
void main(FcramLayout *layout, KCoreContext *ctxs)

315
k11_extension/source/mmu.c Normal file
View File

@ -0,0 +1,315 @@
#include "mmu.h"
#include "globals.h"
#include "utils.h"
DescType L1Descriptor__GetType(u32 descriptor)
{
L1Descriptor pdesc = {descriptor};
if (pdesc.reserved.bits1_0 == 0b00)
return Descriptor_TranslationFault;
if (pdesc.reserved.bits1_0 == 0b01)
return Descriptor_CoarsePageTable;
if (pdesc.reserved.bits1_0 == 0b10)
return pdesc.section.bit18 == 0 ? Descriptor_Section : Descriptor_Supersection;
return Descriptor_Reserved;
}
DescType L2Descriptor__GetType(u32 descriptor)
{
L2Descriptor pdesc = {descriptor};
if (pdesc.translationFault.bits1_0 == 0b01)
return Descriptor_LargePage;
if (pdesc.smallPage.bit1 == 1)
return Descriptor_SmallPage;
return Descriptor_TranslationFault;
}
void L1MMUTable__RWXForAll(u32 *table)
{
u32 *tableEnd = table + 1024;
for (; table != tableEnd; ++table)
{
L1Descriptor descriptor = {*table};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
L2MMUTable__RWXForAll(l2table);
break;
}
case Descriptor_Section:
{
descriptor.section.xn = 0;
descriptor.section.apx = 0;
descriptor.section.ap = 3;
*table = descriptor.raw;
break;
}
case Descriptor_Supersection:
{
descriptor.supersection.xn = 0;
descriptor.supersection.ap = 3;
*table = descriptor.raw;
break;
}
default:
break;
}
}
}
void L2MMUTable__RWXForAll(u32 *table)
{
u32 *tableEnd = table + 256;
for (; table != tableEnd; ++table)
{
L2Descriptor descriptor = {*table};
switch (L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
descriptor.largePage.xn = 0;
descriptor.largePage.apx = 0;
descriptor.largePage.ap = 3;
*table = descriptor.raw;
break;
}
case Descriptor_SmallPage:
{
descriptor.smallPage.xn = 0;
descriptor.smallPage.apx = 0;
descriptor.smallPage.ap = 3;
*table = descriptor.raw;
break;
}
default:
break;
}
}
}
u32 L1MMUTable__GetPAFromVA(u32 *table, u32 va)
{
u32 pa = 0;
L1Descriptor descriptor = {table[va >> 20]};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
pa = L2MMUTable__GetPAFromVA(l2table, va);
break;
}
case Descriptor_Section:
{
pa = descriptor.section.addr << 20;
pa |= (va << 12) >> 12;
break;
}
case Descriptor_Supersection:
{
pa = descriptor.supersection.addr << 24;
pa |= (va << 8) >> 8;
break;
}
default:
// VA not found
break;
}
return pa;
}
u32 L2MMUTable__GetPAFromVA(u32 *table, u32 va)
{
u32 pa = 0;
L2Descriptor descriptor = {table[(va << 12) >> 24]};
switch(L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
pa = descriptor.largePage.addr << 16;
pa |= va & 0xFFFF;
break;
}
case Descriptor_SmallPage:
{
pa = descriptor.smallPage.addr << 12;
pa |= va & 0xFFF;
break;
}
default:
break;
}
return pa;
}
u32 L1MMUTable__GetAddressUserPerm(u32 *table, u32 va)
{
u32 perm = 0;
L1Descriptor descriptor = {table[va >> 20]};
switch (L1Descriptor__GetType(descriptor.raw))
{
case Descriptor_CoarsePageTable:
{
u32 *l2table = (u32 *)((descriptor.coarsePageTable.addr << 10) - 0x40000000);
perm = L2MMUTable__GetAddressUserPerm(l2table, va);
break;
}
case Descriptor_Section:
{
perm = descriptor.section.ap >> 1;
if (perm)
{
perm |= (!descriptor.section.apx && (descriptor.section.ap & 1)) << 1;
perm |= (!descriptor.section.xn) << 2;
}
break;
}
case Descriptor_Supersection:
{
perm = descriptor.supersection.ap >> 1;
if (perm)
{
perm |= (descriptor.supersection.ap & 1) << 1;
perm |= (!descriptor.supersection.xn) << 2;
}
break;
}
default:
// VA not found
break;
}
return perm;
}
u32 L2MMUTable__GetAddressUserPerm(u32 *table, u32 va)
{
u32 perm = 0;
L2Descriptor descriptor = {table[(va << 12) >> 24]};
switch(L2Descriptor__GetType(descriptor.raw))
{
case Descriptor_LargePage:
{
perm = descriptor.largePage.ap >> 1;
if (perm)
{
perm |= (!descriptor.largePage.apx && (descriptor.largePage.ap & 1)) << 1;
perm |= (!descriptor.largePage.xn) << 2;
}
break;
}
case Descriptor_SmallPage:
{
perm = descriptor.smallPage.ap >> 1;
if (perm)
{
perm |= (!descriptor.smallPage.apx && (descriptor.smallPage.ap & 1)) << 1;
perm |= (!descriptor.smallPage.xn) << 2;
}
break;
}
default:
break;
}
return perm;
}
void KProcessHwInfo__SetMMUTableToRWX(KProcessHwInfo *hwInfo)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
L1MMUTable__RWXForAll(table);
KObjectMutex__Release(mutex);
}
u32 KProcessHwInfo__GetPAFromVA(KProcessHwInfo *hwInfo, u32 va)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
u32 pa = L1MMUTable__GetPAFromVA(table, va);
KObjectMutex__Release(mutex);
return pa;
}
u32 KProcessHwInfo__GetAddressUserPerm(KProcessHwInfo *hwInfo, u32 va)
{
KObjectMutex *mutex = KPROCESSHWINFO_GET_PTR(hwInfo, mutex);
u32 *table = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA);
KObjectMutex__Acquire(mutex);
u32 perm = L1MMUTable__GetAddressUserPerm(table, va);
KObjectMutex__Release(mutex);
return perm;
}
static union
{
u32 raw;
struct
{
u32 xn : 1;
u32 unkn : 1;
u32 cb : 2;
u32 ap : 2;
u32 tex : 3;
u32 apx : 1;
u32 s : 1;
u32 ng : 1;
};
} g_rwxState;
// This function patch the permission when memory is mapped in the mmu table (rwx)
KProcessHwInfo *PatchDescriptorAccessControl(KProcessHwInfo *hwInfo, u32 **outState)
{
KProcess *process = (KProcess *)((u32)hwInfo - 0x1C);
u32 state = **outState;
u32 flags = KPROCESS_GET_RVALUE(process, customFlags);
if (flags & SignalOnMemLayoutChanges)
*KPROCESS_GET_PTR(process, customFlags) |= MemLayoutChanged;
if (!(flags & ForceRWXPages))
return hwInfo;
g_rwxState.raw = state;
g_rwxState.xn = 0;
g_rwxState.ap = 3;
g_rwxState.apx = 0;
*outState = &g_rwxState.raw;
return hwInfo;
}

View File

@ -44,6 +44,7 @@
#include "svc/MapProcessMemoryEx.h"
#include "svc/UnmapProcessMemoryEx.h"
#include "svc/ControlService.h"
#include "svc/ControlProcess.h"
#include "svc/CopyHandle.h"
#include "svc/TranslateHandle.h"
@ -59,13 +60,16 @@ void signalSvcEntry(u8 *pageEnd)
// Since DBGEVENT_SYSCALL_ENTRY is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
{
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFE, svcId);
}
}
void signalSvcReturn(u8 *pageEnd)
{
u32 svcId = (u32) *(u8 *)(pageEnd - 0xB5);
KProcess *currentProcess = currentCoreContext->objectContext.currentProcess;
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
if(svcId == 0xFE)
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x1FFFFFFF. We don't support catching svcIds >= 0x100 atm either
@ -73,6 +77,13 @@ void signalSvcReturn(u8 *pageEnd)
// Since DBGEVENT_SYSCALL_RETURN is non blocking, we'll cheat using EXCEVENT_UNDEFINED_SYSCALL (debug->svcId is fortunately an u16!)
if(debugOfProcess(currentProcess) != NULL && shouldSignalSyscallDebugEvent(currentProcess, svcId))
SignalDebugEvent(DBGEVENT_OUTPUT_STRING, 0xFFFFFFFF, svcId);
// Signal if the memory layout of the process changed
if (flags & SignalOnMemLayoutChanges && flags & MemLayoutChanged)
{
*KPROCESS_GET_PTR(currentProcess, customFlags) = flags & ~MemLayoutChanged;
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onMemoryLayoutChangeEvent));
}
}
void postprocessSvc(void)
@ -91,10 +102,27 @@ void *svcHook(u8 *pageEnd)
u32 svcId = *(u8 *)(pageEnd - 0xB5);
if(svcId == 0xFE)
svcId = *(u32 *)(pageEnd - 0x110 + 8 * 4); // r12 ; note: max theortical SVC atm: 0x3FFFFFFF. We don't support catching svcIds >= 0x100 atm either
switch(svcId)
{
case 0x01:
return ControlMemoryHookWrapper;
case 0x03: /* svcExitProcess */
{
// Signal that the process is about to be terminated
u32 flags = KPROCESS_GET_RVALUE(currentProcess, customFlags);
if (flags & SignalOnExit)
{
SignalEvent(KPROCESS_GET_RVALUE(currentProcess, onProcessExitEvent));
KEvent* event = (KEvent *)KProcessHandleTable__ToKAutoObject(handleTableOfProcess(currentProcess),
KPROCESS_GET_RVALUE(currentProcess, resumeProcessExitEvent));
WaitSynchronization1(NULL, currentCoreContext->objectContext.currentThread, (KSynchronizationObject *)event, 10000000000ULL);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event);
}
return officialSVCs[0x3];
}
case 0x29:
return GetHandleInfoHookWrapper;
case 0x2A:
@ -136,7 +164,7 @@ void *svcHook(u8 *pageEnd)
return invalidateEntireInstructionCache;
case 0xA0:
return MapProcessMemoryEx;
return MapProcessMemoryExWrapper;
case 0xA1:
return UnmapProcessMemoryEx;
case 0xA2:
@ -148,6 +176,8 @@ void *svcHook(u8 *pageEnd)
return CopyHandleWrapper;
case 0xB2:
return TranslateHandleWrapper;
case 0xB3:
return ControlProcess;
default:
return (svcId <= 0x7D) ? officialSVCs[svcId] : NULL;

View File

@ -0,0 +1,215 @@
#include "svc/ControlProcess.h"
#include "memory.h"
#include "mmu.h"
#include "synchronization.h"
typedef bool (*ThreadPredicate)(KThread *thread);
void rosalinaLockThread(KThread *thread);
void rosalinaRescheduleThread(KThread *thread, bool lock);
Result ControlProcess(Handle processHandle, ProcessOp op, u32 varg2, u32 varg3)
{
Result res = 0;
KProcess *process;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
if(processHandle == CUR_PROCESS_HANDLE)
{
process = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)process);
}
else
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
if(process == NULL)
return 0xD8E007F7; // invalid handle
switch (op)
{
case PROCESSOP_GET_ALL_HANDLES:
{
KProcessHandleTable *table = handleTableOfProcess(process);
u32 *originalHandleList = (u32 *)varg2;
u32 count = 0;
u32 searchForToken = varg3;
HandleDescriptor *handleDesc = table->handleTable == NULL ? table->internalTable : table->handleTable;
for (u32 idx = 0; idx < (u32)table->maxHandleCount; ++idx, ++handleDesc)
{
if (handleDesc->pointer == NULL)
continue;
if (searchForToken)
{
KClassToken token;
handleDesc->pointer->vtable->GetClassToken(&token, handleDesc->pointer);
if (searchForToken != token.flags)
continue;
}
*originalHandleList++ = idx | ((handleDesc->info << 16) >> 1);
++count;
}
res = count;
break;
}
case PROCESSOP_SET_MMU_TO_RWX:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
*KPROCESS_GET_PTR(process, customFlags) |= ForceRWXPages;
KProcessHwInfo__SetMMUTableToRWX(hwInfo);
break;
}
case PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT:
{
// Only accept current process for this command
if (process != currentCoreContext->objectContext.currentProcess)
{
res = 0xD8E007F7; // invalid handle
break;
}
Handle *onMemoryLayoutChangeEvent = KPROCESS_GET_PTR(process, onMemoryLayoutChangeEvent);
if (*onMemoryLayoutChangeEvent == 0)
res = CreateEvent(onMemoryLayoutChangeEvent, RESET_ONESHOT);
if (res >= 0)
{
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnMemLayoutChanges;
KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onMemoryLayoutChangeEvent);
createHandleForThisProcess((Handle *)varg2, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< This avoid an extra operation on process exit
///< Closing the handle in the handle table will destroy the event
}
break;
}
case PROCESSOP_GET_ON_EXIT_EVENT:
{
// Only accept current process for this command
if (process != currentCoreContext->objectContext.currentProcess)
{
res = 0xD8E007F7; // invalid handle
break;
}
Handle *onProcessExitEvent = KPROCESS_GET_PTR(process, onProcessExitEvent);
Handle *resumeProcessExitEvent = KPROCESS_GET_PTR(process, resumeProcessExitEvent);
if (*onProcessExitEvent == 0)
res = CreateEvent(onProcessExitEvent, RESET_ONESHOT);
if (*resumeProcessExitEvent == 0)
res |= CreateEvent(resumeProcessExitEvent, RESET_ONESHOT);
if (res >= 0)
{
*KPROCESS_GET_PTR(process, customFlags) |= SignalOnExit;
KAutoObject * event = KProcessHandleTable__ToKAutoObject(handleTable, *onProcessExitEvent);
createHandleForThisProcess((Handle *)varg2, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher
event = KProcessHandleTable__ToKAutoObject(handleTable, *resumeProcessExitEvent);
createHandleForThisProcess((Handle *)varg3, event);
((KAutoObject *)event)->vtable->DecrementReferenceCount((KAutoObject *)event); ///< See higher
}
break;
}
case PROCESSOP_GET_PA_FROM_VA:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 pa = KProcessHwInfo__GetPAFromVA(hwInfo, varg3);
*(u32 *)varg2 = pa;
if (pa == 0)
res = 0xE0E01BF5; ///< Invalid address
break;
}
case PROCESSOP_SCHEDULE_THREADS:
{
ThreadPredicate threadPredicate = (ThreadPredicate)varg3;
KRecursiveLock__Lock(criticalSectionLock);
if (varg2 == 0) // Unlock
{
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if((thread->schedulingMask & 0xF) == 2) // thread is terminating
continue;
if(thread->schedulingMask & 0x40)
rosalinaRescheduleThread(thread, false);
}
}
else // Lock
{
bool currentThreadsFound = false;
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if(thread->ownerProcess != process
|| (threadPredicate != NULL && !threadPredicate(thread)))
continue;
if(thread == coreCtxs[thread->coreId].objectContext.currentThread)
currentThreadsFound = true;
else
rosalinaLockThread(thread);
}
if(currentThreadsFound)
{
for(KLinkedListNode *node = threadList->list.nodes.first; node != (KLinkedListNode *)&threadList->list.nodes; node = node->next)
{
KThread *thread = (KThread *)node->key;
if(thread->ownerProcess != process
|| (threadPredicate != NULL && !threadPredicate(thread)))
continue;
if(!(thread->schedulingMask & 0x40))
{
rosalinaLockThread(thread);
KRecursiveLock__Lock(criticalSectionLock);
if(thread->coreId != getCurrentCoreID())
{
u32 cpsr = __get_cpsr();
__disable_irq();
coreCtxs[thread->coreId].objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
currentCoreContext->objectContext.currentScheduler->triggerCrossCoreInterrupt = true;
__set_cpsr_cx(cpsr);
}
KRecursiveLock__Unlock(criticalSectionLock);
}
}
KScheduler__TriggerCrossCoreInterrupt(currentCoreContext->objectContext.currentScheduler);
}
}
KRecursiveLock__Unlock(criticalSectionLock);
break;
}
default:
res = 0xF8C007F4;
}
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
return res;
}

View File

@ -29,11 +29,14 @@
Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
{
if(type == 0x10000) // KDebug and KProcess: get context ID
Result res = 0;
if(type >= 0x10000)
{
KProcessHwInfo *hwInfo;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KAutoObject *obj;
if(handle == CUR_PROCESS_HANDLE)
{
obj = (KAutoObject *)(currentCoreContext->objectContext.currentProcess);
@ -45,6 +48,10 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
if(obj == NULL)
return 0xD8E007F7;
switch (type)
{
case 0x10000: ///< Get ctx id (should probably move it to GetProcessInfo)
{
if(strcmp(classNameOfAutoObject(obj), "KDebug") == 0)
hwInfo = hwInfoOfProcess(((KDebug *)obj)->owner);
else if(strcmp(classNameOfAutoObject(obj), "KProcess") == 0)
@ -53,10 +60,70 @@ Result GetHandleInfoHook(s64 *out, Handle handle, u32 type)
hwInfo = NULL;
*out = hwInfo != NULL ? KPROCESSHWINFO_GET_RVALUE(hwInfo, contextId) : -1;
break;
}
case 0x10001: ///< Get referenced object flags (token)
{
KClassToken token;
obj->vtable->GetClassToken(&token, obj);
*out = token.flags;
break;
}
case 0x10002: ///< Get object owner
{
Handle hOut;
KClassToken token;
KProcess * owner = NULL;
obj->vtable->GetClassToken(&token, obj);
switch(token.flags)
{
case TOKEN_KEVENT:
owner = ((KEvent *)obj)->owner;
break;
case TOKEN_KSEMAPHORE:
owner = ((KSemaphore *)obj)->owner;
break;
case TOKEN_KTIMER:
owner = ((KTimer *)obj)->owner;
break;
case TOKEN_KMUTEX:
owner = ((KMutex *)obj)->owner;
break;
case TOKEN_KDEBUG:
owner = ((KDebug *)obj)->owner;
break;
case TOKEN_KTHREAD:
owner = ((KThread *)obj)->ownerProcess;
break;
case TOKEN_KADDRESSARBITER:
owner = ((KAddressArbiter *)obj)->owner;
break;
case TOKEN_KSHAREDMEMORY:
owner = ((KSharedMemory *)obj)->owner;
break;
default:
break;
}
if (owner == NULL)
res = 0xD8E007F7;
res = createHandleForThisProcess(&hOut, (KAutoObject *)owner);
*out = hOut;
break;
}
default:
res = 0xF8C007F4;
break;
}
obj->vtable->DecrementReferenceCount(obj);
return 0;
return res;
}
else
return GetHandleInfo(out, handle, type);
}

View File

@ -79,6 +79,14 @@ Result GetProcessInfoHook(s64 *out, Handle processHandle, u32 type)
*out = ttb & ~((1 << (14 - TTBCR)) - 1);
break;
}
case 0x10009:
{
KProcessHwInfo *hwInfo = hwInfoOfProcess(process);
u32 mmusize = KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableSize);
u32 mmupa = (u32)PA_FROM_VA_PTR(KPROCESSHWINFO_GET_RVALUE(hwInfo, mmuTableVA));
*out = (s64)(mmusize | ((s64)mmupa << 32));
break;
}
default:
res = 0xD8E007ED; // invalid enum value
break;

View File

@ -64,6 +64,9 @@ Result GetSystemInfoHook(s64 *out, s32 type, s32 param)
case 0x101:
*out = cfwInfo.rosalinaMenuCombo;
break;
case 0x102:
*out = cfwInfo.rosalinaFlags;
break;
case 0x200: // isRelease
*out = cfwInfo.flags & 1;

View File

@ -35,6 +35,7 @@
static u32 nbEnabled = 0;
static u32 maskedPids[MAX_DEBUG];
static u32 masks[MAX_DEBUG][8] = {0};
static u32 *homeBtnPressed = NULL;
bool shouldSignalSyscallDebugEvent(KProcess *process, u8 svcId)
{
@ -178,6 +179,15 @@ Result KernelSetStateHook(u32 type, u32 varg1, u32 varg2, u32 varg3)
KRecursiveLock__Unlock(&dbgParamsLock);
break;
}
case 0x10007:
{
// A bit crude but do the job for a simple notification + reboot, nothing sensitive here
if (varg1 > 255 && homeBtnPressed == NULL)
homeBtnPressed = PA_FROM_VA_PTR((u32 *)varg1);
else if (homeBtnPressed != NULL && *homeBtnPressed == 0)
*homeBtnPressed = varg1;
break;
}
default:
{
res = KernelSetState(type, varg1, varg2, varg3);

View File

@ -26,19 +26,61 @@
#include "svc/MapProcessMemoryEx.h"
Result MapProcessMemoryEx(Handle processHandle, void *dst, void *src, u32 size)
Result MapProcessMemoryEx(Handle dstProcessHandle, u32 vaDst, Handle srcProcessHandle, u32 vaSrc, u32 size)
{
Result res = 0;
u32 sizeInPage = size >> 12;
KLinkedList list;
KProcess *srcProcess;
KProcess *dstProcess;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
KProcess *process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
if(process == NULL)
if (dstProcessHandle == CUR_PROCESS_HANDLE)
{
dstProcess = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)dstProcess);
}
else
dstProcess = KProcessHandleTable__ToKProcess(handleTable, dstProcessHandle);
if (dstProcess == NULL)
return 0xD8E007F7;
Result res = KProcessHwInfo__MapProcessMemory(currentHwInfo, hwInfoOfProcess(process), dst, src, size >> 12);
if (srcProcessHandle == CUR_PROCESS_HANDLE)
{
srcProcess = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)srcProcess);
}
else
srcProcess = KProcessHandleTable__ToKProcess(handleTable, srcProcessHandle);
KAutoObject *obj = (KAutoObject *)process;
obj->vtable->DecrementReferenceCount(obj);
if (srcProcess == NULL)
{
res = 0xD8E007F7;
goto exit1;
}
KLinkedList__Initialize(&list);
res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfoOfProcess(srcProcess), &list, vaSrc, sizeInPage);
if (res >= 0)
{
// Check if the destination address is free and large enough
res = KProcessHwInfo__CheckVaState(hwInfoOfProcess(dstProcess), vaDst, size, 0, 0);
if (res == 0)
res = KProcessHwInfo__MapListOfKBlockInfo(hwInfoOfProcess(dstProcess), vaDst, &list, 0x5806, MEMPERM_RW | 0x18, 0);
}
KLinkedList_KBlockInfo__Clear(&list);
((KAutoObject *)srcProcess)->vtable->DecrementReferenceCount((KAutoObject *)srcProcess);
exit1:
((KAutoObject *)dstProcess)->vtable->DecrementReferenceCount((KAutoObject *)dstProcess);
invalidateEntireInstructionCache();
flushEntireDataCache();
return res;
}

View File

@ -29,12 +29,42 @@
Result UnmapProcessMemoryEx(Handle processHandle, void *dst, u32 size)
{
if(kernelVersion < SYSTEM_VERSION(2, 37, 0)) // < 6.x
return UnmapProcessMemory(processHandle, dst, size); // equivalent when size <= 64MB
Result res = 0;
u32 sizeInPage = size >> 12;
KLinkedList list;
KProcess *process;
KProcessHwInfo *hwInfo;
KProcessHandleTable *handleTable = handleTableOfProcess(currentCoreContext->objectContext.currentProcess);
KProcessHwInfo *currentHwInfo = hwInfoOfProcess(currentCoreContext->objectContext.currentProcess);
if (processHandle == CUR_PROCESS_HANDLE)
{
process = currentCoreContext->objectContext.currentProcess;
KAutoObject__AddReference((KAutoObject *)process);
}
else
process = KProcessHandleTable__ToKProcess(handleTable, processHandle);
Result res = KProcessHwInfo__UnmapProcessMemory(currentHwInfo, dst, size >> 12);
if (process == NULL)
return 0xD8E007F7;
hwInfo = hwInfoOfProcess(process);
KLinkedList__Initialize(&list);
res = KProcessHwInfo__GetListOfKBlockInfoForVA(hwInfo, &list, (u32)dst, sizeInPage);
if (res >= 0)
{
// Check for dst address to be in the right state (0x5806 as we set it with svcMapProcessMemoryEx)
res = KProcessHwInfo__CheckVaState(hwInfo, (u32)dst, size, 0x5806, 0);
if (res == 0)
res = KProcessHwInfo__MapListOfKBlockInfo(hwInfo, (u32)dst, &list, 0, 0, 0);
}
KLinkedList_KBlockInfo__Clear(&list);
((KAutoObject *)process)->vtable->DecrementReferenceCount((KAutoObject *)process);
invalidateEntireInstructionCache();
flushEntireDataCache();

View File

@ -84,3 +84,12 @@ ControlMemoryEx:
ldr r1, [sp, #12]
add sp, #20
pop {pc}
.global MapProcessMemoryExWrapper
.type MapProcessMemoryExWrapper, %function
MapProcessMemoryExWrapper:
push {lr}
str r4, [sp, #-4]!
bl MapProcessMemoryEx
add sp, #4
pop {pc}

View File

@ -96,6 +96,36 @@ KObjectMutex__Release:
blx r12
bx lr
.global KProcessHwInfo__MapL1Section_Hook
.type KProcessHwInfo__MapL1Section_Hook, %function
KProcessHwInfo__MapL1Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x14
add r4, sp, #0x48
mov r11, #0
mov pc, lr
.global KProcessHwInfo__MapL2Section_Hook
.type KProcessHwInfo__MapL2Section_Hook, %function
KProcessHwInfo__MapL2Section_Hook:
@r0 => hwInfo
@sp + 0x34 => our ptr to state
add r1, sp, #0x34
str lr, [sp, #-4]!
bl PatchDescriptorAccessControl
ldr lr, [sp], #4
ldmfd sp, {r0-r4}
sub sp, sp, #0x4C
mov r4, r1
mov r6, r2
mov pc, lr
.global safecpy
.type safecpy, %function
safecpy:

View File

@ -148,6 +148,21 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
return 0;
}
static Result PLGLDR_Init(Handle *session)
{
Result res;
while (1)
{
res = svcConnectToPort(session, "plg:ldr");
if (R_LEVEL(res) != RL_PERMANENT ||
R_SUMMARY(res) != RS_NOTFOUND ||
R_DESCRIPTION(res) != RD_NOT_FOUND
) break;
svcSleepThread(500000);
}
return res;
}
static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
{
Result res = 0;
@ -255,6 +270,9 @@ static Result LoadProcess(Handle *process, u64 programHandle)
// load code
if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_code)))
{
u32 *code = (u32 *)sharedAddr.text_addr;
bool isHomebrew = code[0] == 0xEA000006 && code[8] == 0xE1A0400E;
memcpy(&codesetinfo.name, csi->name, 8);
codesetinfo.program_id = titleId;
codesetinfo.text_addr = vaddr.text_addr;
@ -271,7 +289,37 @@ static Result LoadProcess(Handle *process, u64 programHandle)
{
res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count);
svcCloseHandle(codeset);
res = R_SUCCEEDED(res) ? 0 : res;
if (res >= 0)
{
// Try to load a plugin for the game
if (!isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000))
{
// 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
s64 out;
svcGetSystemInfo(&out, 0x10000, 0x102);
if ((out & 1) == 0)
return 0;
}
Handle plgldr = 0;
if (R_SUCCEEDED(PLGLDR_Init(&plgldr)))
{
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(1, 0, 2);
cmdbuf[1] = IPC_Desc_SharedHandles(1);
cmdbuf[2] = *process;
svcSendSyncRequest(plgldr);
svcCloseHandle(plgldr);
}
}
return 0;
}
}
}

View File

@ -28,7 +28,7 @@ INCLUDES := include include/gdb include/menus include/redshift
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
DEFINES := -DARM11 -D_3DS
CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -O2 -mword-relocations \
CFLAGS := -g -std=gnu11 -Wall -Wextra -O2 -mword-relocations \
-fomit-frame-pointer -ffunction-sections -fdata-sections \
$(ARCH) $(DEFINES)

View File

@ -0,0 +1,37 @@
#pragma once
#include <3ds/types.h>
#include "ifile.h"
#define _3GX_MAGIC (0x3130303024584733) /* "3GX$0001" */
typedef struct PACKED
{
u32 authorLen;
const char * authorMsg;
u32 titleLen;
const char * titleMsg;
u32 summaryLen;
const char * summaryMsg;
u32 descriptionLen;
const char * descriptionMsg;
} _3gx_Infos;
typedef struct PACKED
{
u32 count;
u32 * titles;
} _3gx_Targets;
typedef struct PACKED
{
u64 magic;
u32 version;
u32 codeSize;
u32 * code;
_3gx_Infos infos;
_3gx_Targets targets;
} _3gx_Header;
Result Read_3gx_Header(IFile *file, _3gx_Header *header);
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst);

View File

@ -71,18 +71,20 @@ void svcInvalidateEntireInstructionCache(void);
///@{
/**
* @brief Maps a block of process memory.
* @param process Handle of the process.
* @param dstProcessHandle Handle of the process to map the memory in (destination)
* @param destAddress Address of the mapped block in the current process.
* @param srcProcessHandle Handle of the process to map the memory from (source)
* @param srcAddress Address of the mapped block in the source process.
* @param size Size of the block of the memory to map (truncated to a multiple of 0x1000 bytes).
*/
Result svcMapProcessMemoryEx(Handle process, u32 destAddr, u32 srcAddr, u32 size);
Result svcMapProcessMemoryEx(Handle dstProcessHandle, u32 destAddress, Handle srcProcessHandle, u32 vaSrc, u32 size);
/**
* @brief Unmaps a block of process memory.
* @param process Handle of the process.
* @param destAddress Address of the block of memory to unmap, in the current (destination) process.
* @param process Handle of the process to unmap the memory from
* @param destAddress Address of the block of memory to unmap
* @param size Size of the block of memory to unmap (truncated to a multiple of 0x1000 bytes).
* This function should only be used to unmap memory mapped with svcMapProcessMemoryEx
*/
Result svcUnmapProcessMemoryEx(Handle process, u32 destAddress, u32 size);
@ -134,4 +136,19 @@ Result svcCopyHandle(Handle *out, Handle outProcess, Handle in, Handle inProcess
* @param in The input handle.
*/
Result svcTranslateHandle(u32 *outKAddr, char *outClassName, Handle in);
/// Operations for svcControlProcess
typedef enum ProcessOp
{
PROCESSOP_GET_ALL_HANDLES, ///< List all handles of the process, varg3 can be either 0 to fetch all handles, or token of the type to fetch
///< svcControlProcess(handle, PROCESSOP_GET_ALL_HANDLES, (u32)&outBuf, 0)
PROCESSOP_SET_MMU_TO_RWX, ///< Set the whole memory of the process with rwx access
///< svcControlProcess(handle, PROCESSOP_SET_MMU_TO_RWX, 0, 0)
PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT,
PROCESSOP_GET_ON_EXIT_EVENT,
PROCESSOP_GET_PA_FROM_VA, ///< Get the physical address of the va within the process
///< svcControlProcess(handle, PROCESSOP_GET_PA_FROM_VA, (u32)&outPa, va)
} ProcessOp;
Result svcControlProcess(Handle process, ProcessOp op, u32 varg2, u32 varg3);
///@}

View File

@ -68,6 +68,7 @@
#define COLOR_WHITE RGB565(0x1F, 0x3F, 0x1F)
#define COLOR_RED RGB565(0x1F, 0x00, 0x00)
#define COLOR_GREEN RGB565(0x00, 0x1F, 0x00)
#define COLOR_LIME RGB565(0x00, 0xFF, 0x00)
#define COLOR_BLACK RGB565(0x00, 0x00, 0x00)
#define DRAW_MAX_FORMATTED_STRING_SIZE 512

View File

@ -12,11 +12,14 @@
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc);
GDB_DECLARE_QUERY_HANDLER(Rcmd);

View File

@ -30,6 +30,8 @@
#include <string.h>
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
void *memset32(void *dest, u32 value, u32 size);
void hexItoa(u64 number, char *out, u32 digits, bool uppercase);
unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);
unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok);

View File

@ -86,3 +86,6 @@ void menuEnter(void);
void menuLeave(void);
void menuThreadMain(void);
void menuShow(Menu *root);
void DispMessage(const char *title, const char *message);
u32 DispErrMessage(const char *title, const char *message, const Result error);
void DisplayPluginMenu(u32 *cmdbuf);

View File

@ -0,0 +1,32 @@
#pragma once
#include <3ds/types.h>
#define MAX_BUFFER (50)
#define MAX_ITEMS_COUNT (64)
typedef struct
{
bool noFlash;
u32 lowTitleId;
char path[256];
u32 config[32];
} PluginLoadParameters;
typedef struct
{
u32 nbItems;
u8 states[MAX_ITEMS_COUNT];
char title[MAX_BUFFER];
char items[MAX_ITEMS_COUNT][MAX_BUFFER];
char hints[MAX_ITEMS_COUNT][MAX_BUFFER];
} PluginMenu;
Result plgLdrInit(void);
void plgLdrExit(void);
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled);
Result PLGLDR__SetPluginLoaderState(bool enabled);
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters);
Result PLGLDR__DisplayMenu(PluginMenu *menu);
Result PLGLDR__DisplayMessage(const char *title, const char *body);
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error);

View File

@ -0,0 +1,9 @@
#pragma once
#include <3ds/types.h>
#include "MyThread.h"
MyThread * PluginLoader__CreateThread(void);
bool PluginLoader__IsEnabled(void);
void PluginLoader__MenuCallback(void);
void PluginLoader__UpdateMenu(void);

View 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);

View File

@ -76,3 +76,4 @@ void server_run(struct sock_server *serv);
void server_kill_connections(struct sock_server *serv);
void server_set_should_close_all(struct sock_server *serv);
void server_finalize(struct sock_server *serv);
bool Wifi__IsConnected(void);

View File

@ -28,6 +28,7 @@
#include <3ds/svc.h>
#include <3ds/result.h>
#include <3ds/ipc.h>
#include "csvc.h"
// For accessing physmem uncached (and directly)
@ -56,4 +57,19 @@ static inline void *decodeARMBranch(const void *src)
return (void *)((const u8 *)src + 8 + off);
}
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
extern bool isN3DS;
Result OpenProcessByName(const char *name, Handle *h);
Result SaveSettings(void);

View File

@ -0,0 +1,62 @@
#include <3ds.h>
#include "3gx.h"
Result Read_3gx_Header(IFile *file, _3gx_Header *header)
{
u64 total;
char * dst;
Result res = 0;
res = IFile_Read(file, &total, header, sizeof(_3gx_Header));
if (R_FAILED(res))
return res;
// Read author
file->pos = (u32)header->infos.authorMsg;
dst = (char *)header + sizeof(_3gx_Header);
res = IFile_Read(file, &total, dst, header->infos.authorLen);
if (R_FAILED(res))
return res;
// Relocate ptr
header->infos.authorMsg = dst;
// Read title
file->pos = (u32)header->infos.titleMsg;
dst += header->infos.authorLen;
res = IFile_Read(file, &total, dst, header->infos.titleLen);
if (R_FAILED(res))
return res;
// Relocate ptr
header->infos.titleMsg = dst;
// Declare other members as null (unused in our case)
header->infos.summaryLen = 0;
header->infos.summaryMsg = NULL;
header->infos.descriptionLen = 0;
header->infos.descriptionMsg = NULL;
// Read targets compatibility
file->pos = (u32)header->targets.titles;
dst += header->infos.titleLen;
dst += 4 - ((u32)dst & 3); // 4 bytes aligned
res = IFile_Read(file, &total, dst, header->targets.count * sizeof(u32));
if (R_FAILED(res))
return res;
// Relocate ptr
header->targets.titles = (u32 *)dst;
return res;
}
Result Read_3gx_Code(IFile *file, _3gx_Header *header, void *dst)
{
u64 total;
Result res = 0;
file->pos = (u32)header->code;
res = IFile_Read(file, &total, dst, header->codeSize);
return res;
}

View File

@ -59,7 +59,10 @@ SVC_BEGIN svcInvalidateEntireInstructionCache
SVC_END
SVC_BEGIN svcMapProcessMemoryEx
str r4, [sp, #-4]!
ldr r4, [sp, #4]
svc 0xA0
ldr r4, [sp], #4
bx lr
SVC_END
@ -99,3 +102,8 @@ SVC_BEGIN svcTranslateHandle
str r1, [r2]
bx lr
SVC_END
SVC_BEGIN svcControlProcess
svc 0xB3
bx lr
SVC_END

View File

@ -32,12 +32,6 @@
#include "fmt.h"
#include "ifile.h"
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static char userString[0x100 + 1] = {0};
static inline u32 ERRF_DisplayRegisterValue(u32 posX, u32 posY, const char *name, u32 value)

View File

@ -10,6 +10,7 @@
#include "csvc.h"
#include "fmt.h"
#include "gdb/breakpoints.h"
#include "utils.h"
struct
{
@ -17,12 +18,15 @@ struct
GDBCommandHandler handler;
} remoteCommandHandlers[] =
{
{ "convertvatopa" , GDB_REMOTE_COMMAND_HANDLER(ConvertVAToPA) },
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
{ "listallhandles" , GDB_REMOTE_COMMAND_HANDLER(ListAllHandles) },
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
{ "catchsvc" , GDB_REMOTE_COMMAND_HANDLER(CatchSvc) },
};
static const char *GDB_SkipSpaces(const char *pos)
@ -32,6 +36,50 @@ static const char *GDB_SkipSpaces(const char *pos)
return nextpos;
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ConvertVAToPA)
{
bool ok;
int n;
u32 val;
u32 pa;
char * end;
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
return GDB_ReplyErrno(ctx, EILSEQ);
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
if (val >= 0x40000000)
pa = svcConvertVAToPA((const void *)val, false);
else
{
Handle process;
Result r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
r = svcControlProcess(process, PROCESSOP_GET_PA_FROM_VA, (u32)&pa, val);
svcCloseHandle(process);
if (R_FAILED(r))
{
n = sprintf(outbuf, "An error occured: %08X\n", r);
goto end;
}
}
n = sprintf(outbuf, "va: 0x%08X, pa: 0x%08X, b31: 0x%08X\n", val, pa, pa | (1 << 31));
end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
{
char outbuf[GDB_BUF_LEN / 2 + 1];
@ -116,6 +164,29 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
enum
{
TOKEN_KAUTOOBJECT = 0,
TOKEN_KSYNCHRONIZATIONOBJECT = 1,
TOKEN_KEVENT = 0x1F,
TOKEN_KSEMAPHORE = 0x2F,
TOKEN_KTIMER = 0x35,
TOKEN_KMUTEX = 0x39,
TOKEN_KDEBUG = 0x4D,
TOKEN_KSERVERPORT = 0x55,
TOKEN_KDMAOBJECT = 0x59,
TOKEN_KCLIENTPORT = 0x65,
TOKEN_KCODESET = 0x68,
TOKEN_KSESSION = 0x70,
TOKEN_KTHREAD = 0x8D,
TOKEN_KSERVERSESSION = 0x95,
TOKEN_KCLIENTSESSION = 0xA5,
TOKEN_KPORT = 0xA8,
TOKEN_KSHAREDMEMORY = 0xB0,
TOKEN_KPROCESS = 0xC5,
TOKEN_KRESOURCELIMIT = 0xC8
};
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
{
bool ok;
@ -124,10 +195,11 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
int n;
Result r;
u32 kernelAddr;
s64 token;
Handle handle, process;
s64 refcountRaw;
u32 refcount;
char classBuf[32], serviceBuf[12] = { 0 };
char classBuf[32], serviceBuf[12] = { 0 }, ownerBuf[50] = { 0 };
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
@ -159,12 +231,28 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
svcTranslateHandle(&kernelAddr, classBuf, handle);
svcGetHandleInfo(&refcountRaw, handle, 1);
svcGetHandleInfo(&token, handle, 0x10001);
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
refcount = (u32)(refcountRaw - 1);
if(serviceBuf[0] != 0)
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
else if (token == TOKEN_KPROCESS)
{
svcGetProcessInfo((s64 *)serviceBuf, handle, 0x10000);
n = sprintf(outbuf, "(%s *)0x%08x /* process: %s, %u %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
}
else
n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
{
s64 owner;
if (R_SUCCEEDED(svcGetHandleInfo(&owner, handle, 0x10002)))
{
svcGetProcessInfo((s64 *)serviceBuf, (u32)owner, 0x10000);
svcCloseHandle((u32)owner);
sprintf(ownerBuf, " owner: %s", serviceBuf);
}
n = sprintf(outbuf, "(%s *)0x%08x /* %u %s%s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references", ownerBuf);
}
end:
svcCloseHandle(handle);
@ -172,6 +260,69 @@ end:
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ListAllHandles)
{
bool ok;
u32 val;
char *end;
int n = 0;
Result r;
s32 count = 0;
Handle process, procHandles[0x100];
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
val = 0; ///< All handles
else
{ // Get handles of specified type
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
end = (char *)GDB_SkipSpaces(end);
if(*end != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
}
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
if (R_FAILED(count = svcControlProcess(process, PROCESSOP_GET_ALL_HANDLES, (u32)procHandles, val)))
n = sprintf(outbuf, "An error occured: %08X\n", count);
else if (count == 0)
n = sprintf(outbuf, "Process has no handles ?\n");
else
{
n = sprintf(outbuf, "Found %d handles.\n", count);
const char *comma = "";
for (s32 i = 0; i < count && n < (GDB_BUF_LEN >> 1) - 20; ++i)
{
Handle handle = procHandles[i];
n += sprintf(outbuf + n, "%s0x%08X", comma, handle);
if (((i + 1) % 8) == 0)
{
outbuf[n++] = '\n';
comma = "";
}
else
comma = ", ";
}
}
end:
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
extern bool isN3DS;
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
{
@ -309,6 +460,41 @@ GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(CatchSvc)
{
if(ctx->commandData[0] == '0')
{
memset(ctx->svcMask, 0, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else if(ctx->commandData[0] == '1')
{
if(ctx->commandData[1] == ';')
{
u32 id;
const char *pos = ctx->commandData + 1;
memset(ctx->svcMask, 0, 32);
do
{
pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';');
if(pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
if(id < 0xFE)
ctx->svcMask[id / 32] |= 1 << (31 - (id % 32));
}
while(*pos != 0);
}
else
memset(ctx->svcMask, 0xFF, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else
return GDB_ReplyErrno(ctx, EILSEQ);
}
GDB_DECLARE_QUERY_HANDLER(Rcmd)
{
char commandData[GDB_BUF_LEN / 2 + 1];

View File

@ -122,20 +122,8 @@ static const u32 kernelCaps[] =
0xFE000200, // Handle table size: 0x200
};
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static u16 hbldrTarget[PATH_MAX+1];
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
{
u32 i;

View File

@ -31,6 +31,8 @@
#include "input_redirection.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#include "sock_util.h"
bool inputRedirectionEnabled = false;
Handle inputRedirectionThreadStartedEvent;
@ -96,6 +98,13 @@ void inputRedirectionThreadMain(void)
pfd.events = POLLIN;
pfd.revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& inputRedirectionEnabled && !terminationRequest)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(&pfd, 1, 10);
if(pollres > 0 && (pfd.revents & POLLIN))
{
@ -157,7 +166,7 @@ Result InputRedirection_DoOrUndoPatches(void)
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if(R_SUCCEEDED(res))
{
@ -194,21 +203,21 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &hidOrigRegisterAndValue, totalSize, sizeof(hidOrigRegisterAndValue));
if(off == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -1;
}
u32 *off2 = (u32 *)memsearch((u8 *)off + sizeof(hidOrigRegisterAndValue), &hidOrigRegisterAndValue, totalSize - ((u32)off - 0x00100000), sizeof(hidOrigRegisterAndValue));
if(off2 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -2;
}
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &hidOrigCode, totalSize, sizeof(hidOrigCode));
if(off3 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -3;
}
@ -221,7 +230,7 @@ Result InputRedirection_DoOrUndoPatches(void)
}
}
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
}
svcCloseHandle(processHandle);
@ -235,7 +244,7 @@ Result InputRedirection_DoOrUndoPatches(void)
totalSize = (u32)(textTotalRoundedSize + rodataTotalRoundedSize + dataTotalRoundedSize);
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, totalSize);
res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, totalSize);
if(R_SUCCEEDED(res))
{
@ -289,7 +298,7 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off = (u32 *)memsearch((u8 *)0x00100000, &irOrigReadingCode, totalSize, sizeof(irOrigReadingCode) - 4);
if(off == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -4;
}
@ -299,7 +308,7 @@ Result InputRedirection_DoOrUndoPatches(void)
off2 = (u32 *)memsearch((u8 *)0x00100000, &irOrigWaitSyncCodeOld, totalSize, sizeof(irOrigWaitSyncCodeOld));
if(off2 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -5;
}
@ -311,7 +320,7 @@ Result InputRedirection_DoOrUndoPatches(void)
u32 *off3 = (u32 *)memsearch((u8 *)0x00100000, &irOrigCppFlagCode, totalSize, sizeof(irOrigCppFlagCode));
if(off3 == NULL)
{
svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
return -6;
}
@ -334,7 +343,7 @@ Result InputRedirection_DoOrUndoPatches(void)
}
}
res = svcUnmapProcessMemoryEx(processHandle, 0x00100000, totalSize);
res = svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, totalSize);
}
svcCloseHandle(processHandle);

View File

@ -35,6 +35,7 @@
#include "MyThread.h"
#include "menus/process_patches.h"
#include "menus/miscellaneous.h"
#include "plgloader.h"
#include "menus/debugger.h"
#include "menus/screen_filters.h"
@ -194,12 +195,14 @@ int main(void)
MyThread *menuThread = menuCreateThread();
MyThread *taskRunnerThread = taskRunnerCreateThread();
MyThread *plgloaderThread = PluginLoader__CreateThread();
if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
svcBreak(USERBREAK_PANIC);
MyThread_Join(menuThread, -1LL);
MyThread_Join(taskRunnerThread, -1LL);
MyThread_Join(plgloaderThread, -1LL);
return 0;
}

View File

@ -51,6 +51,15 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
return NULL;
}
void *memset32(void *dest, u32 value, u32 size)
{
u32 *dest32 = (u32 *)dest;
for(u32 i = 0; i < size/4; i++) dest32[i] = value;
return dest;
}
void hexItoa(u64 number, char *out, u32 digits, bool uppercase)
{
const char hexDigits[] = "0123456789ABCDEF";

View File

@ -32,6 +32,7 @@
#include "ifile.h"
#include "menus.h"
#include "utils.h"
#include "plgloader.h"
#include "menus/n3ds.h"
#include "menus/cheats.h"
#include "minisoc.h"
@ -127,9 +128,11 @@ u32 waitCombo(void)
static MyThread menuThread;
static u8 ALIGN(8) menuThreadStack[0x3000];
static u8 batteryLevel = 255;
static u32 homeBtnPressed = 0;
MyThread *menuCreateThread(void)
{
svcKernelSetState(0x10007, &homeBtnPressed);
if(R_FAILED(MyThread_Create(&menuThread, menuThreadMain, menuThreadStack, 0x3000, 52, CORE_SYSTEM)))
svcBreak(USERBREAK_PANIC);
return &menuThread;
@ -138,6 +141,8 @@ MyThread *menuCreateThread(void)
extern bool isN3DS;
u32 menuCombo;
u32 DispWarningOnHome(void);
void menuThreadMain(void)
{
if(!isN3DS)
@ -159,10 +164,11 @@ void menuThreadMain(void)
isAcURegistered = R_SUCCEEDED(srvIsServiceRegistered(&isAcURegistered, "ac:u"))
&& isAcURegistered;
if (isAcURegistered)
else
{
menuEnter();
if(isN3DS) N3DSMenu_UpdateStatus();
PluginLoader__UpdateMenu();
menuShow(&rosalinaMenu);
menuLeave();
}
@ -171,6 +177,16 @@ void menuThreadMain(void)
{
Cheat_ApplyCheats();
}
// Check for home button on O3DS Mode3 with plugin loaded
if (homeBtnPressed != 0)
{
if (DispWarningOnHome())
svcKernelSetState(7); ///< reboot is fine since exiting a mode3 game reboot anyway
homeBtnPressed = 0;
}
svcSleepThread(50 * 1000 * 1000LL);
}
}
@ -342,3 +358,158 @@ void menuShow(Menu *root)
}
while(!terminationRequest);
}
static const char *__press_b_to_close = "Press [B] to close";
void DispMessage(const char *title, const char *message)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!terminationRequest && !(keys & BUTTON_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
}
u32 DispErrMessage(const char *title, const char *message, const Result error)
{
char buf[100];
sprintf(buf, "Error code: 0x%08X", (unsigned int)error);
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, title);
u32 posY = Draw_DrawString(30, 30, COLOR_WHITE, message);
Draw_DrawString(30, posY + 20, COLOR_RED, buf);
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!terminationRequest && !(keys & BUTTON_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return error;
}
u32 DispWarningOnHome(void)
{
menuEnter();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
Draw_DrawString(10, 10, COLOR_TITLE, "Warning");
u32 posY = Draw_DrawString(30, 40, COLOR_WHITE, "Due to memory shortage the home button\nis disabled.");
Draw_DrawString(30, posY + 20, COLOR_WHITE, "Press [DPAD UP + B] to exit the application.");
Draw_DrawString(200, 220, COLOR_TITLE, __press_b_to_close);
u32 keys = 0;
do
{
keys = waitComboWithTimeout(1000);
}while (!terminationRequest && !(keys & BUTTON_B));
Draw_Unlock(); ///< Keep it locked until we exit the message
menuLeave();
return (keys & BUTTON_UP) > 0;
}
typedef char string[50];
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void DisplayPluginMenu(u32 *cmdbuf)
{
u32 cursor = 0;
u32 nbItems = cmdbuf[1];
u8 *states = (u8 *)cmdbuf[3];
char buffer[60];
const char *title = (const char *)cmdbuf[5];
const string *items = (const string *)cmdbuf[7];
const string *hints = (const string *)cmdbuf[9];
menuEnter();
Draw_Lock();
do
{
// Draw the menu
{
// Clear screen
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();
// Draw title
Draw_DrawString(10, 10, COLOR_TITLE, title);
// Draw items
u32 i = MAX(0, (int)cursor - 7);
u32 end = MIN(nbItems, i + 16);
u32 posY = 30;
for (; i < end; ++i, posY += 10)
{
sprintf(buffer, "[ ] %s", items[i]);
Draw_DrawString(30, posY, COLOR_WHITE, buffer);
if (i == cursor) Draw_DrawCharacter(10, posY, COLOR_TITLE, '>');
if (states[i]) Draw_DrawCharacter(36, posY, COLOR_LIME, 'x');
}
// Draw hint
if (hints[cursor])
Draw_DrawString(10, 200, COLOR_TITLE, hints[cursor]);
}
// Wait for input
u32 pressed = waitInput();
if (pressed & BUTTON_A)
states[cursor] = !states[cursor];
if (pressed & BUTTON_B)
break;
if (pressed & BUTTON_DOWN)
if (++cursor >= nbItems)
cursor = 0;
if (pressed & BUTTON_UP)
if (--cursor >= nbItems)
cursor = nbItems - 1;
} while (true);
Draw_Unlock();
menuLeave();
}

View File

@ -36,6 +36,7 @@
#include "menus/miscellaneous.h"
#include "menus/sysconfig.h"
#include "menus/screen_filters.h"
#include "plgloader.h"
#include "ifile.h"
#include "memory.h"
#include "fmt.h"
@ -46,6 +47,7 @@ Menu rosalinaMenu = {
{
{ "New 3DS menu...", MENU, .menu = &N3DSMenu },
{ "Cheats...", METHOD, .method = &RosalinaMenu_Cheats },
{ "", METHOD, .method = PluginLoader__MenuCallback},
{ "Process list", METHOD, .method = &RosalinaMenu_ProcessList },
{ "Take screenshot (slow!)", METHOD, .method = &RosalinaMenu_TakeScreenshot },
{ "Debugger options...", MENU, .menu = &debuggerMenu },
@ -54,7 +56,7 @@ Menu rosalinaMenu = {
{ "Miscellaneous options...", MENU, .menu = &miscellaneousMenu },
{ "Power off", METHOD, .method = &RosalinaMenu_PowerOff },
{ "Reboot", METHOD, .method = &RosalinaMenu_Reboot },
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits }
{ "Credits", METHOD, .method = &RosalinaMenu_ShowCredits },
}
};

View File

@ -31,6 +31,7 @@
#include "memory.h"
#include "draw.h"
#include "hbloader.h"
#include "plgloader.h"
#include "fmt.h"
#include "utils.h" // for makeARMBranch
#include "minisoc.h"
@ -153,7 +154,7 @@ void MiscellaneousMenu_ChangeMenuCombo(void)
while(!(waitInput() & BUTTON_B) && !terminationRequest);
}
void MiscellaneousMenu_SaveSettings(void)
Result SaveSettings(void)
{
Result res;
@ -168,12 +169,14 @@ void MiscellaneousMenu_SaveSettings(void)
u32 config, multiConfig, bootConfig;
u64 hbldr3dsxTitleId;
u32 rosalinaMenuCombo;
u32 rosalinaFlags;
} configData;
u32 formatVersion;
u32 config, multiConfig, bootConfig;
s64 out;
bool isSdMode;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 2))) svcBreak(USERBREAK_ASSERT);
formatVersion = (u32)out;
if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT);
@ -193,6 +196,7 @@ void MiscellaneousMenu_SaveSettings(void)
configData.bootConfig = bootConfig;
configData.hbldr3dsxTitleId = HBLDR_3DSX_TID;
configData.rosalinaMenuCombo = menuCombo;
configData.rosalinaFlags = PluginLoader__IsEnabled();
FS_ArchiveID archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW;
res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/config.bin"), FS_OPEN_CREATE | FS_OPEN_WRITE);
@ -200,6 +204,14 @@ void MiscellaneousMenu_SaveSettings(void)
if(R_SUCCEEDED(res))
res = IFile_Write(&file, &total, &configData, sizeof(configData), 0);
IFile_Close(&file);
return res;
}
void MiscellaneousMenu_SaveSettings(void)
{
Result res = SaveSettings();
Draw_Lock();
Draw_ClearFramebuffer();
Draw_FlushFramebuffer();

View File

@ -232,8 +232,8 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
svcQueryProcessMemory(&mem, &out, processHandle, heapStartAddress);
heapTotalSize = mem.size;
Result codeRes = svcMapProcessMemoryEx(processHandle, codeDestAddress, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(processHandle, heapDestAddress, heapStartAddress, heapTotalSize);
Result codeRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, processHandle, codeStartAddress, codeTotalSize);
Result heapRes = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, processHandle, heapStartAddress, heapTotalSize);
bool codeAvailable = R_SUCCEEDED(codeRes);
bool heapAvailable = R_SUCCEEDED(heapRes);
@ -574,9 +574,9 @@ static void ProcessListMenu_MemoryViewer(const ProcessInfo *info)
}
if(codeAvailable)
svcUnmapProcessMemoryEx(processHandle, codeDestAddress, codeTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, codeDestAddress, codeTotalSize);
if(heapAvailable)
svcUnmapProcessMemoryEx(processHandle, heapDestAddress, heapTotalSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, heapDestAddress, heapTotalSize);
svcCloseHandle(processHandle);
}

View File

@ -105,12 +105,12 @@ static u32 ProcessPatchesMenu_PatchUnpatchProcessByName(const char *name, Result
s64 textTotalRoundedSize = 0, startAddress = 0;
svcGetProcessInfo(&textTotalRoundedSize, processHandle, 0x10002); // only patch .text
svcGetProcessInfo(&startAddress, processHandle, 0x10005);
if(R_FAILED(res = svcMapProcessMemoryEx(processHandle, 0x00100000, (u32) startAddress, textTotalRoundedSize)))
if(R_FAILED(res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, processHandle, (u32) startAddress, textTotalRoundedSize)))
return res;
res = func(textTotalRoundedSize);
svcUnmapProcessMemoryEx(processHandle, 0x00100000, textTotalRoundedSize);
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x00100000, textTotalRoundedSize);
return res;
}

View File

@ -0,0 +1,143 @@
#include <3ds.h>
#include "plgldr.h"
#include <string.h>
static Handle plgLdrHandle;
static int plgLdrRefCount;
Result plgLdrInit(void)
{
Result res = 0;
if (AtomicPostIncrement(&plgLdrRefCount) == 0)
res = svcConnectToPort(&plgLdrHandle, "plg:ldr");
if (R_FAILED(res))
AtomicDecrement(&plgLdrRefCount);
return res;
}
void plgLdrExit(void)
{
if (AtomicDecrement(&plgLdrRefCount))
return;
svcCloseHandle(plgLdrHandle);
}
Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(2, 0, 0);
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
*isEnabled = cmdbuf[2];
}
return res;
}
Result PLGLDR__SetPluginLoaderState(bool enabled)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = (u32)enabled;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__SetPluginLoadParameters(PluginLoadParameters *parameters)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(4, 2, 4);
cmdbuf[1] = (u32)parameters->noFlash;
cmdbuf[2] = parameters->lowTitleId;
cmdbuf[3] = IPC_Desc_Buffer(256, IPC_BUFFER_R);
cmdbuf[4] = (u32)parameters->path;
cmdbuf[5] = IPC_Desc_Buffer(32 * sizeof(u32), IPC_BUFFER_R);
cmdbuf[6] = (u32)parameters->config;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayMenu(PluginMenu *menu)
{
Result res = 0;
u32 nbItems = menu->nbItems;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(5, 1, 8);
cmdbuf[1] = nbItems;
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
cmdbuf[3] = (u32)menu->states;
cmdbuf[4] = IPC_Desc_Buffer(MAX_BUFFER, IPC_BUFFER_R);
cmdbuf[5] = (u32)menu->title;
cmdbuf[6] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[7] = (u32)menu->items;
cmdbuf[8] = IPC_Desc_Buffer(MAX_BUFFER * nbItems, IPC_BUFFER_R);
cmdbuf[9] = (u32)menu->hints;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayMessage(const char *title, const char *body)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(6, 0, 4);
cmdbuf[1] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[2] = (u32)title;
cmdbuf[3] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[4] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}
Result PLGLDR__DisplayErrMessage(const char *title, const char *body, u32 error)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(7, 1, 4);
cmdbuf[1] = error;
cmdbuf[2] = IPC_Desc_Buffer(strlen(title), IPC_BUFFER_R);
cmdbuf[3] = (u32)title;
cmdbuf[4] = IPC_Desc_Buffer(strlen(body), IPC_BUFFER_R);
cmdbuf[5] = (u32)body;
if (R_SUCCEEDED((res = svcSendSyncRequest(plgLdrHandle))))
{
res = cmdbuf[1];
}
return res;
}

View File

@ -0,0 +1,599 @@
#include <3ds.h>
#include "3gx.h"
#include "ifile.h"
#include "utils.h" // for makeARMBranch
#include "plgloader.h"
#include "fmt.h"
#include "menu.h"
#include "menus.h"
#include "memory.h"
#include "sleep.h"
#define MEMPERM_RW (MEMPERM_READ | MEMPERM_WRITE)
#define MemBlockSize (5*1024*1024) /* 5 MiB */
typedef struct
{
Result code;
const char * message;
} Error;
typedef struct
{
bool isEnabled;
bool noFlash;
u32 titleid;
char path[256];
u32 config[32];
} PluginLoadParameters;
#define HeaderMagic (0x24584733) /* "3GX$" */
typedef struct
{
u32 magic;
u32 version;
u32 heapVA;
u32 heapSize;
u32 pluginSize;
const char* pluginPathPA;
u32 isDefaultPlugin;
u32 reserved[25];
u32 config[32];
} PluginHeader;
static bool g_isEnabled;
static u8 * g_memBlock;
static char g_path[256];
static Handle g_process = 0;
static u32 g_codeSize;
static u32 g_heapSize;
static Error g_error;
static PluginLoadParameters g_userDefinedLoadParameters;
static MyThread g_pluginLoaderThread;
static u8 ALIGN(8) g_pluginLoaderThreadStack[0x4000];
// pluginLoader.s
void gamePatchFunc(void);
void PluginLoader__ThreadMain(void);
MyThread * PluginLoader__CreateThread(void)
{
s64 out;
svcGetSystemInfo(&out, 0x10000, 0x102);
g_isEnabled = out & 1;
g_userDefinedLoadParameters.isEnabled = false;
if(R_FAILED(MyThread_Create(&g_pluginLoaderThread, PluginLoader__ThreadMain, g_pluginLoaderThreadStack, 0x4000, 20, CORE_SYSTEM)))
svcBreak(USERBREAK_PANIC);
return &g_pluginLoaderThread;
}
bool PluginLoader__IsEnabled(void)
{
return g_isEnabled;
}
void PluginLoader__MenuCallback(void)
{
g_isEnabled = !g_isEnabled;
SaveSettings();
PluginLoader__UpdateMenu();
}
void PluginLoader__UpdateMenu(void)
{
static const char *status[2] =
{
"Plugin Loader: [Disabled]",
"Plugin Loader: [Enabled]"
};
rosalinaMenu.items[isN3DS + 1].title = status[g_isEnabled];
}
static Result MapPluginInProcess(Handle proc, u32 size)
{
u32 heapsize = MemBlockSize - size;
Result res;
PluginHeader *header = (PluginHeader *)g_memBlock;
header->heapVA = 0x06000000;
g_heapSize = header->heapSize = heapsize;
g_codeSize = size;
// From now on, all memory page mapped to the process should be rwx
svcControlProcess(proc, PROCESSOP_SET_MMU_TO_RWX, 0, 0);
// Plugin
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x07000000, CUR_PROCESS_HANDLE, (u32)g_memBlock, size))))
{
g_error.message = "Couldn't map plugin memory block";
g_error.code = res;
return res;
}
// Heap (to be used by the plugin)
if (R_FAILED((res = svcMapProcessMemoryEx(proc, 0x06000000, CUR_PROCESS_HANDLE, (u32)g_memBlock + size, heapsize))))
{
g_error.message = "Couldn't map heap memory block";
g_error.code = res;
goto exit;
}
// Clear heap section
memset32(g_memBlock + size, 0, heapsize);
exit:
return res;
}
static u32 strlen16(const u16 *str)
{
u32 size = 0;
while (str && *str++) ++size;
return size;
}
static Result FindPluginFile(u64 tid)
{
char filename[256];
u32 entriesNb = 0;
bool found = false;
Handle dir = 0;
Result res;
FS_Archive sdmcArchive = 0;
FS_DirectoryEntry entries[10];
sprintf(g_path, "/luma/plugins/%016llX", tid);
if (R_FAILED((res =FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))))
goto exit;
if (R_FAILED((res = FSUSER_OpenDirectory(&dir, sdmcArchive, fsMakePath(PATH_ASCII, g_path)))))
goto exit;
strcat(g_path, "/");
while (!found && R_SUCCEEDED(FSDIR_Read(dir, &entriesNb, 10, entries)))
{
if (entriesNb == 0)
break;
static const u16 * validExtension = u"3gx";
for (u32 i = 0; i < entriesNb; ++i)
{
FS_DirectoryEntry *entry = &entries[i];
// If entry is a folder, skip it
if (entry->attributes & 1)
continue;
// Check extension
u32 size = strlen16(entry->name);
if (size <= 5)
continue;
u16 *fileExt = entry->name + size - 3;
if (memcmp(fileExt, validExtension, 3 * sizeof(u16)))
continue;
// Convert name from utf16 to utf8
int units = utf16_to_utf8((u8 *)filename, entry->name, 100);
if (units == -1)
continue;
filename[units] = 0;
found = true;
break;
}
}
if (!found)
res = MAKERESULT(28, 4, 0, 1018);
else
{
u32 len = strlen(g_path);
filename[256 - len] = 0;
strcat(g_path, filename);
}
((PluginHeader *)g_memBlock)->pluginPathPA = PA_FROM_VA_PTR(g_path);
exit:
FSDIR_Close(dir);
FSUSER_CloseArchive(sdmcArchive);
return res;
}
static Result OpenFile(IFile *file, const char *path)
{
return IFile_Open(file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, path), FS_OPEN_READ);
}
static Result CheckPluginCompatibility(_3gx_Header *header, u32 processTitle)
{
static char errorBuf[0x100];
if (header->targets.count == 0)
return 0;
for (u32 i = 0; i < header->targets.count; ++i)
{
if (header->targets.titles[i] == processTitle)
return 0;
}
sprintf(errorBuf, "The plugin - %s -\nis not compatible with this game.\n" \
"Contact \"%s\" for more infos.", header->infos.titleMsg, header->infos.authorMsg);
g_error.message = errorBuf;
return -1;
}
static bool TryToLoadPlugin(Handle process)
{
u64 fileSize;
u64 tid;
u32 procStart = 0x00100000;
IFile plugin;
PluginHeader *hdr = (PluginHeader *)g_memBlock;
_3gx_Header *header;
Result res;
// Clear the memblock
memset32(g_memBlock, 0, MemBlockSize);
hdr->magic = HeaderMagic;
// Get title id
svcGetProcessInfo((s64 *)&tid, process, 0x10001);
if (R_FAILED((res = svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, process, procStart, 0x1000))))
{
g_error.message = "Couldn't map process";
g_error.code = res;
return false;
}
// Try to open plugin file
if (g_userDefinedLoadParameters.isEnabled && (u32)tid == g_userDefinedLoadParameters.titleid)
{
g_userDefinedLoadParameters.isEnabled = false;
if (OpenFile(&plugin, g_userDefinedLoadParameters.path))
goto exitFail;
hdr->pluginPathPA = PA_FROM_VA_PTR(g_userDefinedLoadParameters.path);
memcpy(hdr->config, g_userDefinedLoadParameters.config, 32 * sizeof(u32));
}
else
{
if (R_FAILED(FindPluginFile(tid)) || OpenFile(&plugin, g_path))
{
// Try to open default plugin
const char *defaultPath = "/luma/plugins/default.3gx";
if (OpenFile(&plugin, defaultPath))
goto exitFail;
hdr->isDefaultPlugin = 1;
hdr->pluginPathPA = PA_FROM_VA_PTR(defaultPath);
}
}
if (R_FAILED((res = IFile_GetSize(&plugin, &fileSize))))
g_error.message = "Couldn't get file size";
// Plugins will rarely exceed 1MB so this is fine
header = (_3gx_Header *)(g_memBlock + MemBlockSize - (u32)fileSize);
// Read header
if (!res && R_FAILED((res = Read_3gx_Header(&plugin, header))))
g_error.message = "Couldn't read file";
// Check titles compatibility
if (!res) res = CheckPluginCompatibility(header, (u32)tid);
// Read code
if (!res && R_FAILED(res = Read_3gx_Code(&plugin, header, g_memBlock + sizeof(PluginHeader))))
g_error.message = "Couldn't read plugin's code";
// Close file
IFile_Close(&plugin);
if (R_FAILED(res))
{
g_error.code = res;
goto exitFail;
}
hdr->version = header->version;
// Code size must be page aligned
fileSize = (header->codeSize + 0x1100) & ~0xFFF;
if (MapPluginInProcess(process, fileSize) == 0)
// Install hook
{
extern u32 g_savedGameInstr[2];
u32 *game = (u32 *)procStart;
g_savedGameInstr[0] = game[0];
g_savedGameInstr[1] = game[1];
game[0] = 0xE51FF004; // ldr pc, [pc, #-4]
game[1] = (u32)PA_FROM_VA_PTR(gamePatchFunc);
}
else
goto exitFail;
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
return true;
exitFail:
svcUnmapProcessMemoryEx(CUR_PROCESS_HANDLE, procStart, 0x1000);
return false;
}
static void SetKernelConfigurationMemoryFlag(bool loaded)
{
u32 *flag = (u32 *)PA_FROM_VA_PTR(0x1FF800F0);
*flag = loaded;
}
static void PluginLoader_HandleCommands(void)
{
u32 *cmdbuf = getThreadCommandBuffer();
switch (cmdbuf[0] >> 16)
{
case 1: // Load plugin
{
if (cmdbuf[0] != IPC_MakeHeader(1, 0, 2))
{
error(cmdbuf, 0xD9001830);
break;
}
Handle process = cmdbuf[2];
if (g_isEnabled && TryToLoadPlugin(process))
{
if (!g_userDefinedLoadParameters.isEnabled && g_userDefinedLoadParameters.noFlash)
g_userDefinedLoadParameters.noFlash = false;
else
{
for (u32 i = 0; i < 64; i++)
{
REG32(0x10202204) = 0x01FF9933;
svcSleepThread(5000000);
}
REG32(0x10202204) = 0;
}
g_process = process;
}
else
svcCloseHandle(process);
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
cmdbuf[1] = 0;
break;
}
case 2: // Check if plugin loader is enabled
{
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
cmdbuf[0] = IPC_MakeHeader(2, 2, 0);
cmdbuf[1] = 0;
cmdbuf[2] = (u32)g_isEnabled;
break;
}
case 3: // Enable / Disable plugin loader
{
if (cmdbuf[0] != IPC_MakeHeader(3, 1, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
if (cmdbuf[1] != g_isEnabled)
{
g_isEnabled = cmdbuf[1];
SaveSettings();
}
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = 0;
break;
}
case 4: // Define next plugin load settings
{
if (cmdbuf[0] != IPC_MakeHeader(4, 2, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
g_userDefinedLoadParameters.isEnabled = true;
g_userDefinedLoadParameters.noFlash = cmdbuf[1];
g_userDefinedLoadParameters.titleid = cmdbuf[2];
strncpy(g_userDefinedLoadParameters.path, (const char *)cmdbuf[4], 255);
memcpy(g_userDefinedLoadParameters.config, (void *)cmdbuf[6], 32 * sizeof(u32));
cmdbuf[0] = IPC_MakeHeader(4, 1, 0);
cmdbuf[1] = 0;
break;
}
case 5: // Display menu
{
if (cmdbuf[0] != IPC_MakeHeader(5, 1, 8))
{
error(cmdbuf, 0xD9001830);
break;
}
u32 nbItems = cmdbuf[1];
u32 states = cmdbuf[3];
DisplayPluginMenu(cmdbuf);
cmdbuf[0] = IPC_MakeHeader(5, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_Buffer(nbItems, IPC_BUFFER_RW);
cmdbuf[3] = states;
break;
}
case 6: // Display message
{
if (cmdbuf[0] != IPC_MakeHeader(6, 0, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
const char *title = (const char *)cmdbuf[2];
const char *body = (const char *)cmdbuf[4];
DispMessage(title, body);
cmdbuf[0] = IPC_MakeHeader(6, 1, 0);
cmdbuf[1] = 0;
break;
}
case 7: // Display error message
{
if (cmdbuf[0] != IPC_MakeHeader(7, 1, 4))
{
error(cmdbuf, 0xD9001830);
break;
}
const char *title = (const char *)cmdbuf[3];
const char *body = (const char *)cmdbuf[5];
DispErrMessage(title, body, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(7, 1, 0);
cmdbuf[1] = 0;
break;
}
}
}
void PluginLoader__ThreadMain(void)
{
const char *title = "Plugin loader";
Result res = 0;
MemOp memRegion = isN3DS ? MEMOP_REGION_BASE : MEMOP_REGION_SYSTEM;
Handle handles[3];
Handle serverHandle, clientHandle, sessionHandle = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 replyTarget = 0;
u32 nbHandle;
s32 index;
// Wait for the system to be completely started
{
bool isAcuRegistered = false;
while (true)
{
if (R_SUCCEEDED(srvIsServiceRegistered(&isAcuRegistered, "ac:u"))
&& isAcuRegistered)
break;
svcSleepThread(100000);
}
}
// Init memory block to hold the plugin
{
u32 free = (u32)osGetMemRegionFree(memRegion);
u32 temp = 0;
svcControlMemoryEx(&temp, 0x00100000, 0, free - MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true);
if (R_FAILED((res = svcControlMemoryEx((u32 *)&g_memBlock, 0x07000000, 0, MemBlockSize, memRegion | MEMOP_ALLOC, MEMPERM_RW, true))))
{
svcSleepThread(5000000000ULL); ///< Wait until the system started to display the error
DispErrMessage(title, "Couldn't allocate memblock", res);
return;
}
svcControlMemoryEx(&temp, (u32)temp, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
}
assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "plg:ldr", 1));
do
{
g_error.message = NULL;
g_error.code = 0;
handles[0] = serverHandle;
handles[1] = sessionHandle == 0 ? g_process : sessionHandle;
handles[2] = g_process;
if(replyTarget == 0) // k11
cmdbuf[0] = 0xFFFF0000;
nbHandle = 1 + (sessionHandle != 0) + (g_process != 0);
res = svcReplyAndReceive(&index, handles, nbHandle, replyTarget);
if(R_FAILED(res))
{
if((u32)res == 0xC920181A) // session closed by remote
{
svcCloseHandle(sessionHandle);
sessionHandle = 0;
replyTarget = 0;
}
else
svcBreak(USERBREAK_PANIC);
}
else
{
if(index == 0)
{
Handle session;
assertSuccess(svcAcceptSession(&session, serverHandle));
if(sessionHandle == 0)
sessionHandle = session;
else
svcCloseHandle(session);
}
else if (index == 1 && handles[1] == sessionHandle)
{
PluginLoader_HandleCommands();
replyTarget = sessionHandle;
}
else ///< The process in which we injected the plugin is terminating
{
// Unmap plugin's memory before closing the process
svcUnmapProcessMemoryEx(g_process, 0x07000000, g_codeSize);
svcUnmapProcessMemoryEx(g_process, 0x06000000, g_heapSize);
svcCloseHandle(g_process);
g_process = 0;
}
}
if (g_error.message != NULL)
DispErrMessage(title, g_error.message, g_error.code);
SetKernelConfigurationMemoryFlag(g_process != 0);
} while(!terminationRequest);
svcCloseHandle(sessionHandle);
svcCloseHandle(clientHandle);
svcCloseHandle(serverHandle);
svcControlMemoryEx((u32 *)&g_memBlock, (u32)g_memBlock, 0, MemBlockSize, memRegion | MEMOP_FREE, 0, true);
}

View File

@ -0,0 +1,33 @@
.section .data
.balign 4
.arm
.global gamePatchFunc
.type gamePatchFunc, %function
gamePatchFunc:
stmfd sp!, {r0-r12, lr}
mrs r0, cpsr
stmfd sp!, {r0}
adr r0, g_savedGameInstr
ldr r1, =0x00100000
ldr r2, [r0]
str r2, [r1]
ldr r2, [r0, #4]
str r2, [r1, #4]
svc 0x92
svc 0x94
startplugin:
ldr r5, =0x07000100
blx r5
exit:
ldmfd sp!, {r0}
msr cpsr, r0
ldmfd sp!, {r0-r12, lr}
ldr lr, =0x00100000
mov pc, lr
.global g_savedGameInstr
g_savedGameInstr:
.word 0, 0

View File

@ -0,0 +1,61 @@
/*
* 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>
static bool g_isSleeping = false;
static LightEvent g_onWakeUpEvent;
void Sleep__Init(void)
{
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;
}
else if (notifId == 0x213) ///< Sleep exit
{
g_isSleeping = false;
LightEvent_Signal(&g_onWakeUpEvent);
}
}
bool Sleep__Status(void)
{
if (g_isSleeping)
{
LightEvent_Wait(&g_onWakeUpEvent);
return true;
}
return false;
}

View File

@ -9,10 +9,12 @@
#include <3ds/result.h>
#include <3ds/svc.h>
#include <3ds/synchronization.h>
#include <3ds/services/ac.h>
#include <arpa/inet.h>
#include "memory.h"
#include "minisoc.h"
#include "sock_util.h"
#include "sleep.h"
extern Handle terminationRequestEvent;
extern bool terminationRequest;
@ -169,6 +171,14 @@ void server_run(struct sock_server *serv)
for(nfds_t i = 0; i < serv->nfds; i++)
fds[i].revents = 0;
if (Sleep__Status())
{
while (!Wifi__IsConnected()
&& serv->running && !terminationRequest)
svcSleepThread(1000000000ULL);
}
int pollres = socPoll(fds, serv->nfds, 50);
if(server_should_exit(serv))
@ -303,3 +313,13 @@ void server_finalize(struct sock_server *serv)
svcClearEvent(serv->started_event);
svcCloseHandle(serv->started_event);
}
bool Wifi__IsConnected(void)
{
u32 status = 0;
u32 wifistatus = 0;
acInit();
return R_SUCCEEDED(ACU_GetWifiStatus(&wifistatus)) && wifistatus > 0
&& R_SUCCEEDED(ACU_GetStatus(&status)) && status != 1;
}

View File

@ -19,6 +19,14 @@ static bool doPublishNotification(ProcessData *processData, u32 notificationId,
}
}
// Handle special case for home button notifications on Mode3 O3DS with plugin loaded
if ((notificationId == 0x204 || notificationId == 0x205)
&& *(u32 *)0x1FF80030 == 3 && *(u32 *)0x1FF800F0)
{
svcKernelSetState(0x10007, 1);
return true;
}
if(processData->nbPendingNotifications < 0x10)
{
s32 count;