From 035c50c6256aafde8ef78b95f190a1ab20240dec Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Sat, 12 Aug 2023 18:00:08 +0200 Subject: [PATCH] Fix support for all older versions - Move emuNAND hook to ITCM, fixing previously untested emuNAND support for 5.0 (and possibly more versions). This was a very long-standing bug - Fix booting on versions 4.x to 8.x, and restore full support for 5.x to 8.x. All Arm11 custom sysmodules and k11ext have been disabled on 4.x because the Luma3DS v13.0 changes couldn't be ported (this means no Rosalina and no region-free and such, and while I could restore some of the functionality, you should just update *after* installing Luma, like you've been instructed to) --- arm9/linker.ld | 1 + arm9/source/emunand.c | 49 +++---------------------- arm9/source/emunand.h | 2 +- arm9/source/emunand_patch.s | 63 ++++++++++++++++++++++++++++++++ arm9/source/firm.c | 8 ++--- arm9/source/large_patches.s | 64 --------------------------------- k11_extension/source/main.c | 36 ++++++------------- sysmodules/loader/source/main.c | 6 +++- 8 files changed, 89 insertions(+), 140 deletions(-) create mode 100644 arm9/source/emunand_patch.s diff --git a/arm9/linker.ld b/arm9/linker.ld index 4365b8c..aa5ac82 100644 --- a/arm9/linker.ld +++ b/arm9/linker.ld @@ -46,6 +46,7 @@ SECTIONS chainloader.o(.text*) i2c.o(.text*) arm9_exception_handlers.o(.text*) + KEEP (*(.emunand_patch)) *(.arm9_exception_handlers.rodata*) chainloader.o(.rodata*) diff --git a/arm9/source/emunand.c b/arm9/source/emunand.c index 0032424..147fe8a 100644 --- a/arm9/source/emunand.c +++ b/arm9/source/emunand.c @@ -111,21 +111,6 @@ void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCt else *nandType = FIRMWARE_SYSNAND; } -static inline bool getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space) -{ - static const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; - - //Looking for the last free space before Process9 - *freeK9Space = memsearch(pos, pattern, size, sizeof(pattern)); - - if(*freeK9Space == NULL || (u32)(pos + size - *freeK9Space) < 0x455 + emunandPatchSize || - *(u32 *)(*freeK9Space + 0x455 + emunandPatchSize - 4) != 0xFFFFFFFF) return false; - - *freeK9Space += 0x455; - - return true; -} - static inline u32 getOldSdmmc(u32 *sdmmc, u32 firmVersion) { switch(firmVersion) @@ -158,7 +143,7 @@ static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc) return 0; } -static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset) +static inline u32 patchNandRw(u8 *pos, u32 size, u32 hookAddr) { //Look for read/write code static const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05}; @@ -176,32 +161,13 @@ static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset) writeOffset -= 3; *readOffset = *writeOffset = 0x4C00; readOffset[1] = writeOffset[1] = 0x47A0; - ((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset; + ((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = hookAddr; return 0; } -static inline u32 patchMpu(u8 *pos, u32 size) +u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion) { - //Look for MPU pattern - static const u8 pattern[] = {0x03, 0x00, 0x24, 0x00}; - - u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)); - - if(off == NULL) return 1; - - off[1] = 0x0036; - off[0xC] = off[0x12] = 0x0603; - - return 0; -} - -u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u8 *kernel9Address, u32 firmVersion) -{ - u8 *freeK9Space; - - if(!getFreeK9Space(arm9Section, kernel9Size, &freeK9Space)) return 1; - u32 ret = 0; //Add the data of the found EmuNAND @@ -213,15 +179,8 @@ u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 proce ret += !ISN3DS && firmVersion < 0x25 ? getOldSdmmc(&sdmmc, firmVersion) : getSdmmc(process9Offset, process9Size, &sdmmc); if(!ret) emunandPatchSdmmcStructPtr = sdmmc; - //Copy EmuNAND code - memcpy(freeK9Space, emunandPatch, emunandPatchSize); - //Add EmuNAND hooks - u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address); - ret += patchNandRw(process9Offset, process9Size, branchOffset); - - //Set MPU - ret += patchMpu(arm9Section, kernel9Size); + ret += patchNandRw(process9Offset, process9Size, (u32)emunandPatch); return ret; } diff --git a/arm9/source/emunand.h b/arm9/source/emunand.h index 369baab..60623a1 100644 --- a/arm9/source/emunand.h +++ b/arm9/source/emunand.h @@ -38,4 +38,4 @@ extern u32 emuOffset, emuHeader; void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCtrNandParams); -u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u8 *kernel9Address, u32 firmVersion); +u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion); diff --git a/arm9/source/emunand_patch.s b/arm9/source/emunand_patch.s new file mode 100644 index 0000000..6cbab55 --- /dev/null +++ b/arm9/source/emunand_patch.s @@ -0,0 +1,63 @@ +.section .emunand_patch, "aw", %progbits +.arm +.align 4 + +@ Code originally by Normmatt + +.global emunandPatch +emunandPatch: + @ Original code that still needs to be executed + mov r4, r0 + mov r5, r1 + mov r7, r2 + mov r6, r3 + @ End + + @ If we're already trying to access the SD, return + ldr r2, [r0, #4] + ldr r1, emunandPatchSdmmcStructPtr + cmp r2, r1 + beq out + + str r1, [r0, #4] @ Set object to be SD + ldr r2, [r0, #8] @ Get sector to read + cmp r2, #0 @ For GW compatibility, see if we're trying to read the ncsd header (sector 0) + + ldr r3, emunandPatchNandOffset + add r2, r3 @ Add the offset to the NAND in the SD + + ldreq r3, emunandPatchNcsdHeaderOffset + addeq r2, r3 @ If we're reading the ncsd header, add the offset of that sector + + str r2, [r0, #8] @ Store sector to read + + out: + @ Restore registers. + mov r1, r5 + mov r2, r7 + mov r3, r6 + + @ Return 4 bytes behind where we got called, + @ due to the offset of this function being stored there + mov r0, lr + add r0, #4 + bx r0 + +.pool + +.global emunandPatchSdmmcStructPtr +.global emunandPatchNandOffset +.global emunandPatchNcsdHeaderOffset + +emunandPatchSdmmcStructPtr: .word 0 @ Pointer to sdmmc struct +emunandPatchNandOffset: .word 0 @ For rednand this should be 1 +emunandPatchNcsdHeaderOffset: .word 0 @ Depends on nand manufacturer + emunand type (GW/RED) + +.pool +.balign 4 + +_emunandPatchEnd: + +.global emunandPatchSize +emunandPatchSize: + .word _emunandPatchEnd - emunandPatch diff --git a/arm9/source/firm.c b/arm9/source/firm.c index 0554a3f..811e203 100755 --- a/arm9/source/firm.c +++ b/arm9/source/firm.c @@ -422,7 +422,7 @@ static void mergeSection0(FirmwareType firmType, u32 firmVersion, bool loadFromS } // SAFE_FIRM only for N3DS and only if ENABLESAFEFIRMROSALINA is on - if((firmType == NATIVE_FIRM || firmType == SAFE_FIRM) && (ISN3DS || firmVersion >= 0x1D)) + if((firmType == NATIVE_FIRM || firmType == SAFE_FIRM) && (ISN3DS || firmVersion >= 0x25)) { //2) Merge that info with our own modules' for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize) @@ -528,8 +528,8 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora ret = 0; #ifndef BUILD_FOR_EXPLOIT_DEV - //Skip on FIRMs < 4.0 - if(ISN3DS || firmVersion >= 0x1D) + //Skip on FIRMs < 5.0 + if(ISN3DS || firmVersion >= 0x25) { //Find the Kernel11 SVC table and handler, exceptions page and free space locations u8 *arm11Section1 = (u8 *)firm + firm->section[1].offset; @@ -550,7 +550,7 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora ret += patchSignatureChecks(process9Offset, process9Size); //Apply EmuNAND patches - if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, firm->section[2].address, firmVersion); + if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(process9Offset, process9Size, firmVersion); //Apply FIRM0/1 writes patches on SysNAND to protect A9LH else if(isFirmProtEnabled) ret += patchFirmWrites(process9Offset, process9Size); diff --git a/arm9/source/large_patches.s b/arm9/source/large_patches.s index 90f3ffd..c66f63b 100644 --- a/arm9/source/large_patches.s +++ b/arm9/source/large_patches.s @@ -1,67 +1,3 @@ -.section .large_patch.emunand, "aw", %progbits -.arm -.align 4 - -@ Code originally by Normmatt - -.global emunandPatch -emunandPatch: - @ Original code that still needs to be executed - mov r4, r0 - mov r5, r1 - mov r7, r2 - mov r6, r3 - @ End - - @ If we're already trying to access the SD, return - ldr r2, [r0, #4] - ldr r1, emunandPatchSdmmcStructPtr - cmp r2, r1 - beq out - - str r1, [r0, #4] @ Set object to be SD - ldr r2, [r0, #8] @ Get sector to read - cmp r2, #0 @ For GW compatibility, see if we're trying to read the ncsd header (sector 0) - - ldr r3, emunandPatchNandOffset - add r2, r3 @ Add the offset to the NAND in the SD - - ldreq r3, emunandPatchNcsdHeaderOffset - addeq r2, r3 @ If we're reading the ncsd header, add the offset of that sector - - str r2, [r0, #8] @ Store sector to read - - out: - @ Restore registers. - mov r1, r5 - mov r2, r7 - mov r3, r6 - - @ Return 4 bytes behind where we got called, - @ due to the offset of this function being stored there - mov r0, lr - add r0, #4 - bx r0 - -.pool - -.global emunandPatchSdmmcStructPtr -.global emunandPatchNandOffset -.global emunandPatchNcsdHeaderOffset - -emunandPatchSdmmcStructPtr: .word 0 @ Pointer to sdmmc struct -emunandPatchNandOffset: .word 0 @ For rednand this should be 1 -emunandPatchNcsdHeaderOffset: .word 0 @ Depends on nand manufacturer + emunand type (GW/RED) - -.pool -.balign 4 - -_emunandPatchEnd: - -.global emunandPatchSize -emunandPatchSize: - .word _emunandPatchEnd - emunandPatch - @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ Code originally from delebile and mid-kid diff --git a/k11_extension/source/main.c b/k11_extension/source/main.c index 0ff4e68..8210be6 100644 --- a/k11_extension/source/main.c +++ b/k11_extension/source/main.c @@ -135,33 +135,17 @@ void KProcessHwInfo__MapL2Section_Hook(void); static void installMmuHooks(void) { - u32 *mapL1Section = NULL; - u32 *mapL2Section = NULL; + // Older versions of k11 had different VA memory mappings + u32 k11TextStartVa = (u32)originalHandlers[2] & ~0xFFFF; u32 *off; - for(off = (u32 *)officialSVCs[0x1F]; *off != 0xE1CD60F0; ++off); - off = decodeArmBranch(off + 1); + for (off = (u32 *)k11TextStartVa; off[0] != 0xE3A05801 || off[1] != 0xE2010EE3; off++); + for (; (off[0] >> 16) != 0xE92D; off--); + u32 *mapL2Section = PA_FROM_VA_PTR(off); // fragile, might break due to cache - 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); + for (off = (u32 *)k11TextStartVa; off[0] != 0x13A0A401 || off[1] != 0x03A0A601; off++); + for (; (off[0] >> 16) != 0xE92D; off--); + u32 *mapL1Section = PA_FROM_VA_PTR(off); mapL1Section[1] = 0xE28FE004; // add lr, pc, #4 mapL1Section[2] = 0xE51FF004; // ldr pc, [pc, #-4] @@ -176,8 +160,10 @@ static void findUsefulSymbols(void) { u32 *off; + // Older versions of k11 had different VA memory mappings + u32 k11TextStartVa = (u32)originalHandlers[2] & ~0xFFFF; // Get fcramDescriptor - for (off = (u32 *)0xFFF00000; ; ++off) + for (off = (u32 *)k11TextStartVa; ; ++off) { if ( (off[0] >> 16) == 0xE59F && (off[1] >> 16) == 0xE3A0 diff --git a/sysmodules/loader/source/main.c b/sysmodules/loader/source/main.c index f349286..a039b5b 100644 --- a/sysmodules/loader/source/main.c +++ b/sysmodules/loader/source/main.c @@ -59,7 +59,11 @@ static inline void loadCFWInfo(void) if (numKips >= 6) panic(0xDEADCAFE); - config = 0; // all options 0 +#ifndef BUILD_FOR_EXPLOIT_DEV + config = 1u << PATCHVERSTRING; // all options 0, except maybe the MSET version display patch +#else + config = 0; +#endif multiConfig = 0; bootConfig = 0; isN3DS = OS_KernelConfig->app_memtype >= 6;