Skip to content

Commit

Permalink
Fix support for all older versions
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
TuxSH committed Aug 12, 2023
1 parent 5989d9d commit 035c50c
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 140 deletions.
1 change: 1 addition & 0 deletions arm9/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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*)
Expand Down
49 changes: 4 additions & 45 deletions arm9/source/emunand.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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};
Expand All @@ -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;

return 0;
}

static inline u32 patchMpu(u8 *pos, u32 size)
{
//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;
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = hookAddr;

return 0;
}

u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u8 *kernel9Address, u32 firmVersion)
u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion)
{
u8 *freeK9Space;

if(!getFreeK9Space(arm9Section, kernel9Size, &freeK9Space)) return 1;

u32 ret = 0;

//Add the data of the found EmuNAND
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion arm9/source/emunand.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
63 changes: 63 additions & 0 deletions arm9/source/emunand_patch.s
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions arm9/source/firm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
64 changes: 0 additions & 64 deletions arm9/source/large_patches.s
Original file line number Diff line number Diff line change
@@ -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
Expand Down
36 changes: 11 additions & 25 deletions k11_extension/source/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion sysmodules/loader/source/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 035c50c

Please sign in to comment.