diff --git a/arm9/source/emunand.c b/arm9/source/emunand.c index 147fe8a38..64e47129c 100644 --- a/arm9/source/emunand.c +++ b/arm9/source/emunand.c @@ -111,6 +111,23 @@ void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCt else *nandType = FIRMWARE_SYSNAND; } +static inline u32 getProtoSdmmc(u32 *sdmmc, u32 firmVersion) +{ + switch(firmVersion) + { + case 243: // SDK 0.9.x (0.9.7?) + *sdmmc = (0x080AAA28 + 0x4e0); + break; + case 238: // SDK 0.10 + *sdmmc = (0x080BEA70 + 0x690); + break; + default: + return 1; + } + + return 0; +} + static inline u32 getOldSdmmc(u32 *sdmmc, u32 firmVersion) { switch(firmVersion) @@ -166,6 +183,111 @@ static inline u32 patchNandRw(u8 *pos, u32 size, u32 hookAddr) return 0; } +static inline u32 patchProtoNandRw(u8 *pos, u32 size, u32 hookAddr, u32 hookCidAddr) +{ + //Look for read/write code + static const u8 pattern[] = { + 0x03, 0x00, 0x51, 0xE3, // cmp r1, #3 + 0x02, 0xC0, 0xA0, 0xE1, // mov r12, r2 + 0x04, 0x00, 0x80, 0xE2, // add r0, r0, #4 + }; + + u32 *writeOffset = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)); + + if(writeOffset == NULL) return 1; + + u32 *readOffset = (u32 *)memsearch((u8 *)(writeOffset + 3), pattern, 0x400, sizeof(pattern)); + + if(readOffset == NULL) return 1; + + // Find the sdmmc mount/init(?) function + static const u8 mount_pattern[] = { + 0x20, 0x00, 0x84, 0xE2, // add r0, r4, 0x20 + 0x01, 0x20, 0xA0, 0xE3, // mov r2, #1 + 0x00, 0x10, 0xA0, 0xE3, // mov r1, #0 + }; + u32* mountOffset = (u32*) memsearch(pos, mount_pattern, size, sizeof(mount_pattern)); + if (mountOffset == NULL) return 1; + + // Find the sdmmc read cid function. + static const u8 readcid_pattern[] = { + 0x31, 0xFF, 0x2F, 0xE1, // blx r1 + 0x20, 0x60, 0x9F, 0xE5, // ldr r6, [pc, #0x20] // =failing_result + 0x00, 0x00, 0x50, 0xE3, // cmp r0, #0 + }; + u32* readCidOffset = (u32*) memsearch(pos, readcid_pattern, size, sizeof(readcid_pattern)); + if (readCidOffset == NULL) return 1; + readCidOffset -= 5; + + mountOffset[1] = 0xe3a02000; // mov r2, #0 // sd-card + + readOffset[0] = writeOffset[0] = 0xe52de004; // push {lr} + readOffset[1] = writeOffset[1] = 0xe59fc000; // ldr r12, [pc, #0] + readOffset[2] = writeOffset[2] = 0xe12fff3c; // blx r12 + readOffset[3] = writeOffset[3] = hookAddr; + + readCidOffset[0] = 0xe59fc000; // ldr r12, [pc, #0] + readCidOffset[1] = 0xe12fff3c; // blx r12 + readCidOffset[2] = hookCidAddr; + + // Read the emmc cid into the place hook will copy it from + sdmmc_get_cid(1, emunandPatchNandCid); + + return 0; +} + +static inline u32 patchProtoNandRw238(u8 *pos, u32 size, u32 hookAddr, u32 hookCidAddr) +{ + //Look for read/write code + static const u8 pattern[] = { + 0x03, 0x00, 0x50, 0xE3, // cmp r0, #3 + 0x00, 0x00, 0xA0, 0x13, // movne r0, #0 + 0x01, 0x00, 0xA0, 0x03, // moveq r0, #1 + }; + + u32 *writeOffset = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)); + + if(writeOffset == NULL) return 1; + + u32 *readOffset = (u32 *)memsearch((u8 *)(writeOffset + 3), pattern, 0x400, sizeof(pattern)); + + if(readOffset == NULL) return 1; + + // Find the mmc static ctor... + static const u8 mount_pattern[] = { + 0x08, // last byte of some ptr to something in P9 + 0x01, 0x01, 0x00, 0x00, // emmc controller id + }; + u8* mountOffset = (u8*) memsearch(pos, mount_pattern, size, sizeof(mount_pattern)); + if (mountOffset == NULL) return 1; + mountOffset++; + + // Find the sdmmc read cid function. + static const u8 readcid_pattern[] = { + 0x31, 0xFF, 0x2F, 0xE1, // blx r1 + 0x20, 0x60, 0x9F, 0xE5, // ldr r6, [pc, #0x20] // =failing_result + 0x00, 0x00, 0x50, 0xE3, // cmp r0, #0 + }; + u32* readCidOffset = (u32*) memsearch(pos, readcid_pattern, size, sizeof(readcid_pattern)); + if (readCidOffset == NULL) return 1; + readCidOffset -= 5; + + *(u32*)mountOffset = 0x300; // sd card + + readOffset[0] = writeOffset[0] = 0xe59fc000; // ldr r12, [pc, #0] + readOffset[1] = writeOffset[1] = 0xe12fff3c; // blx r12 + readOffset[2] = writeOffset[2] = hookAddr; + + readCidOffset[0] = 0xe59fc000; // ldr r12, [pc, #0] + readCidOffset[1] = 0xe12fff3c; // blx r12 + readCidOffset[2] = hookCidAddr; + + // Read the emmc cid into the place hook will copy it from + sdmmc_get_cid(1, emunandPatchNandCid); + + return 0; +} + u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion) { u32 ret = 0; @@ -184,3 +306,33 @@ u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion) return ret; } + +u32 patchProtoEmuNand(u8 *process9Offset, u32 process9Size) +{ + extern u32 firmProtoVersion; + u32 ret = 0; + + // Add the data of the found EmuNAND + emunandPatchNandOffset = emuOffset; + emunandPatchNcsdHeaderOffset = emuHeader; + + // Find and add the SDMMC struct + u32 sdmmc; + ret += getProtoSdmmc(&sdmmc, firmProtoVersion); + if(!ret) emunandPatchSdmmcStructPtr = sdmmc; + + // Add EmuNAND hooks + switch (firmProtoVersion) { + case 243: // SDK 0.9.x (0.9.7?) + ret += patchProtoNandRw(process9Offset, process9Size, (u32)emunandProtoPatch, (u32)emunandProtoCidPatch); + break; + case 238: // SDK 0.10.x + ret += patchProtoNandRw238(process9Offset, process9Size, (u32)emunandProtoPatch238, (u32)emunandProtoCidPatch); + break; + default: + ret++; + break; + } + + return ret; +} \ No newline at end of file diff --git a/arm9/source/emunand.h b/arm9/source/emunand.h index 60623a1b9..e92aa2669 100644 --- a/arm9/source/emunand.h +++ b/arm9/source/emunand.h @@ -39,3 +39,4 @@ extern u32 emuOffset, void locateEmuNand(FirmwareSource *nandType, u32 *emunandIndex, bool configureCtrNandParams); u32 patchEmuNand(u8 *process9Offset, u32 process9Size, u32 firmVersion); +u32 patchProtoEmuNand(u8 *process9Offset, u32 process9Size); \ No newline at end of file diff --git a/arm9/source/emunand_patch.s b/arm9/source/emunand_patch.s index 6cbab55c3..a4a4f8cdf 100644 --- a/arm9/source/emunand_patch.s +++ b/arm9/source/emunand_patch.s @@ -45,19 +45,150 @@ emunandPatch: .pool +_emunandPatchEnd: + +.global emunandProtoPatch +emunandProtoPatch: + @ Save registers + push {r0-r3} + + @ If we're already trying to access the SD, return + ldr r2, [r0, #4] + ldr r1, emunandPatchSdmmcStructPtr + cmp r2, r1 + beq _out + + ldrb r2, [r1, #0xc] @ Get sdmc->m_isInitialised + cmp r2, #0 @ Is initialised? + beq _pastSdmc @ if not, use "NAND" object, patched elsewhere to access SD + str r1, [r0, #4] @ Set object to be SD + _pastSdmc: + 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 + pop {r0-r3} + @ Execute original code that got patched. + cmp r1, #3 + mov r12, r2 + add r0, r0, #4 + movne r1, #0 + moveq r1, #1 + @ r2 about to be overwritten, so it's free to use here. + @ Save off our return address and restore lr. + mov r2, lr + pop {lr} + @ r2+0 is return address (patched movne r1, #0) + @ r2+4 is moveq r1, #1 + @ r2+8 is the following instruction (mov r2, r3) + add r2, #8 + bx r2 + +.global emunandProtoCidPatch +emunandProtoCidPatch: + @ If we're already trying to access the SD, return + ldr r4, emunandPatchSdmmcStructPtr + cmp r0, r4 + beq _cid_return + + @ Trying to access nand, so copy the NAND cid into r1 + adr r4, emunandPatchNandCid + ldr r2, [r4, #0] + ldr r3, [r4, #4] + ldr r5, [r4, #8] + ldr r6, [r4, #0xc] + str r2, [r1, #0] + str r3, [r1, #4] + str r5, [r1, #8] + str r6, [r1, #0xc] + @ And return from whence we came + mov r0, #0 + pop {r4-r6, pc} + + _cid_return: + @ Execute original code that got patched. + mov r4, r0 + ldr r0, [r0] + mov r5, r1 + @ lr+0 is return address (patched mov r5, r1) + @ lr+4 is following instruction (ldr r1, [r0,#8]) + add lr, #4 + bx lr + +.global emunandProtoPatch238 +emunandProtoPatch238: + @ Save registers + push {r0-r3} + + @ If we're already trying to access the SD, return + ldr r2, [r4, #4] + ldr r1, emunandPatchSdmmcStructPtr + cmp r2, r1 + beq _out238 + + ldr r2, [r1, #0x24] @ Get sdmc->m_someObjInitedLater + cmp r2, #0 @ Is initialised? + beq _pastSdmc238 @ if not, use "NAND" object, patched elsewhere to access SD + str r1, [r4, #4] @ Set object to be SD + _pastSdmc238: + + ldr r2, [r4, #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, [r4, #8] @ Store sector to read + + _out238: + @ Restore registers + pop {r0-r3} + @ Execute original code that got patched. + cmp r0, #3 + movne r0, #0 + moveq r0, #1 + @ r1 about to be overwritten, so it's free to use here. + @ Save off our return address. + mov r1, lr + @ r1+0 is return address (patched moveq r1, #1) + @ r1+4 is tst r0, #0xff or sub sp, sp, #0xc + add r1, #4 + bx r1 + +.pool + .global emunandPatchSdmmcStructPtr .global emunandPatchNandOffset .global emunandPatchNcsdHeaderOffset +.global emunandPatchNandCid +_emunandPatchBssStart: 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) +emunandPatchNandCid: @ Store emmc cid here, to override "sdmc's" when trying to read emmc's + .word 0,0,0,0 +_emunandPatchBssEnd: .pool .balign 4 -_emunandPatchEnd: - .global emunandPatchSize emunandPatchSize: .word _emunandPatchEnd - emunandPatch + +.global emunandPatchBssSize +emunandPatchBssSize: + .word _emunandPatchBssEnd - _emunandPatchBssStart \ No newline at end of file diff --git a/arm9/source/firm.c b/arm9/source/firm.c index 692dcf157..04eae3245 100755 --- a/arm9/source/firm.c +++ b/arm9/source/firm.c @@ -39,6 +39,7 @@ #include "chainloader.h" static Firm *firm = (Firm *)0x20001000; +u32 firmProtoVersion = 0; static __attribute__((noinline)) bool overlaps(u32 as, u32 ae, u32 bs, u32 be) { @@ -154,7 +155,21 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF u32 firmVersion = 0xFFFFFFFF, firmSize; - bool ctrNandError = isSdMode && !remountCtrNandPartition(false); + bool ctrNandError = true; + bool loadedFromStorage = false; + bool storageLoadError = false; + + // Try loading FIRM from sdmc first if specified. + if (loadFromStorage) { + firmSize = loadFirmFromStorage(*firmType); + if (firmSize != 0) loadedFromStorage = true; + else storageLoadError = true; + } + + // Remount ctrnand and load FIRM from it if loading from sdmc failed. + if (!loadedFromStorage) { + ctrNandError = isSdMode && !remountCtrNandPartition(false); + } if(!ctrNandError) { @@ -169,10 +184,8 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF if(!firmSize || !checkFirm(firmSize)) ctrNandError = true; } } - - bool loadedFromStorage = false; - - if(loadFromStorage || ctrNandError) + // If CTRNAND load failed, and it wasn't tried yet, load FIRM from sdmc. + if (ctrNandError && !storageLoadError) { u32 result = loadFirmFromStorage(*firmType); @@ -181,51 +194,60 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF loadedFromStorage = true; firmSize = result; } - else if(ctrNandError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one."); + else storageLoadError = true; } + // If all attempts failed, panic. + if(ctrNandError && storageLoadError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one."); //Check that the FIRM is right for the console from the Arm9 section address - if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800)) - error("The %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND"); - - if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000) - { - //We can't boot < 3.x EmuNANDs - if(nandType != FIRMWARE_SYSNAND) error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it."); - - //If you want to use SAFE_FIRM on 1.0, use Luma from NAND & comment this line: - if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM."); - - *firmType = NATIVE_FIRM1X2X; - } + bool isO3dsFirm = firm->section[3].offset == 0 && firm->section[2].address == (u8 *)0x8006800; if(loadedFromStorage || ISDEVUNIT) { firmVersion = 0xFFFFFFFF; - if(!ISN3DS && *firmType == NATIVE_FIRM) + if(isO3dsFirm && (*firmType == NATIVE_FIRM || *firmType == NATIVE_FIRM1X2X)) { - __attribute__((aligned(4))) static const u8 hashes[3][0x20] = { + __attribute__((aligned(4))) static const u8 hashes[5][0x20] = { + {0xD7, 0x43, 0x0F, 0x27, 0x8D, 0xC9, 0x3F, 0x4C, 0x96, 0xB5, 0xA8, 0x91, 0x48, 0xDB, 0x08, 0x8A, + 0x7E, 0x46, 0xB3, 0x95, 0x65, 0xA2, 0x05, 0xF1, 0xF2, 0x41, 0x21, 0xF1, 0x0C, 0x59, 0x6A, 0x9D}, + {0x93, 0xDF, 0x49, 0xA1, 0x24, 0x86, 0xBB, 0x6F, 0xAF, 0x49, 0x99, 0x2D, 0xD0, 0x8D, 0xB1, 0x88, + 0x8A, 0x00, 0xB6, 0xDD, 0x36, 0x89, 0xC0, 0xE2, 0xC9, 0xA9, 0x99, 0x62, 0x57, 0x5E, 0x6C, 0x23}, {0x39, 0x75, 0xB5, 0x28, 0x24, 0x5E, 0x8B, 0x56, 0xBC, 0x83, 0x79, 0x41, 0x09, 0x2C, 0x42, 0xE6, 0x26, 0xB6, 0x80, 0x59, 0xA5, 0x56, 0xF9, 0xF9, 0x6E, 0xF3, 0x63, 0x05, 0x58, 0xDF, 0x35, 0xEF}, {0x81, 0x9E, 0x71, 0x58, 0xE5, 0x44, 0x73, 0xF7, 0x48, 0x78, 0x7C, 0xEF, 0x5E, 0x30, 0xE2, 0x28, 0x78, 0x0B, 0x21, 0x23, 0x94, 0x63, 0xE8, 0x4E, 0x06, 0xBB, 0xD6, 0x8D, 0xA0, 0x99, 0xAE, 0x98}, {0x1D, 0xD5, 0xB0, 0xC2, 0xD9, 0x4A, 0x4A, 0xF3, 0x23, 0xDD, 0x2F, 0x65, 0x21, 0x95, 0x9B, 0x7E, - 0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37} + 0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37}, }; u32 i; - for(i = 0; i < 3; i++) if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break; + for(i = 0; i < sizeof(hashes)/sizeof(hashes[0]); i++) + { + if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break; + } switch(i) { + // Beta case 0: - firmVersion = 0x18; + firmVersion = 0x0; + firmProtoVersion = 243; + *firmType = NATIVE_PROTOTYPE; break; case 1: - firmVersion = 0x1D; + firmVersion = 0x0; + firmProtoVersion = 238; + *firmType = NATIVE_PROTOTYPE; break; + // Release case 2: + firmVersion = 0x18; + break; + case 3: + firmVersion = 0x1D; + break; + case 4: firmVersion = 0x1F; break; default: @@ -234,6 +256,20 @@ u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadF } } + if(*firmType != NATIVE_PROTOTYPE && (firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800)) + error("The %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND"); + + if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000) + { + //We can't boot < 3.x EmuNANDs + if(nandType != FIRMWARE_SYSNAND) error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it."); + + //If you want to use SAFE_FIRM on 1.0, use Luma from NAND & comment this line: + if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM."); + + *firmType = NATIVE_FIRM1X2X; + } + return firmVersion; } @@ -737,6 +773,29 @@ u32 patch1x2xNativeAndSafeFirm(void) return ret; } +u32 patchPrototypeNative(FirmwareSource nandType) +{ + u8 *arm9Section = (u8 *)firm + firm->section[2].offset; + + //Find the Process9 .code location, size and memory address + u32 process9Size, + process9MemAddr; + u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr); + + u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200, + ret = 0; + + ret += patchProtoNandSignatureCheck(process9Offset, process9Size); + + //Arm9 exception handlers + ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size); + + //Apply EmuNAND patches + if(nandType != FIRMWARE_SYSNAND) ret += patchProtoEmuNand(process9Offset, process9Size); + + return ret; +} + void launchFirm(int argc, char **argv) { prepareArm11ForFirmlaunch(); diff --git a/arm9/source/firm.h b/arm9/source/firm.h index 591ec2faa..0857e21c8 100644 --- a/arm9/source/firm.h +++ b/arm9/source/firm.h @@ -35,4 +35,5 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch); u32 patchAgbFirm(bool loadFromStorage, bool doUnitinfoPatch); u32 patch1x2xNativeAndSafeFirm(void); +u32 patchPrototypeNative(FirmwareSource nandType); void launchFirm(int argc, char **argv); diff --git a/arm9/source/large_patches.h b/arm9/source/large_patches.h index 6c99243b4..a51f06ed4 100644 --- a/arm9/source/large_patches.h +++ b/arm9/source/large_patches.h @@ -28,9 +28,11 @@ #include "types.h" -extern const u8 emunandPatch[]; -extern const u32 emunandPatchSize; +extern const u8 emunandPatch[], emunandProtoPatch[], emunandProtoCidPatch[]; +extern const u8 emunandProtoPatch238[]; +extern const u32 emunandPatchSize, emunandPatchBssSize; extern u32 emunandPatchSdmmcStructPtr, emunandPatchNandOffset, emunandPatchNcsdHeaderOffset; +extern u32 emunandPatchNandCid[4]; extern const u8 rebootPatch[]; extern const u32 rebootPatchSize; diff --git a/arm9/source/main.c b/arm9/source/main.c index 248bbff30..4d95fe74f 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -386,6 +386,9 @@ void main(int argc, char **argv, u32 magicWord) case NATIVE_FIRM1X2X: res = patch1x2xNativeAndSafeFirm(); break; + case NATIVE_PROTOTYPE: + res = patchPrototypeNative(nandType); + break; } if(res != 0) error("Failed to apply %u FIRM patch(es).", res); diff --git a/arm9/source/patches.c b/arm9/source/patches.c index ba49c3cff..4cb577af5 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -45,6 +45,7 @@ #define K11EXT_VA 0x70000000 extern u16 launchedPath[]; +extern u32 firmProtoVersion; u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) { @@ -54,10 +55,20 @@ u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) Cxi *off = (Cxi *)(temp - 0x100); - *process9Size = (off->ncch.exeFsSize - 1) * 0x200; *process9MemAddr = off->exHeader.systemControlInfo.textCodeSet.address; - return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200; + // Prototype FW has a different NCCH format + if (firmProtoVersion && firmProtoVersion <= 243) + { + *process9Size = off->ncch.exeFsSize; + return (u8 *)off + off->ncch.exeFsOffset; + } + else + { + *process9Size = (off->ncch.exeFsSize - 1) * 0x200; + return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200; + } + } u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage) @@ -836,3 +847,36 @@ u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size) return 0; } + +u32 patchProtoNandSignatureCheck(u8 *pos, u32 size) { + if (firmProtoVersion == 243) { + static const u8 pattern[] = {0x08, 0x31, 0x9F, 0xE5}; + + // Signature check function returns 0 if failed and 1 if succeeded. + // Proc9 breaks if the returned value is 0, change it to break if + // the returned value is 2 (never). + u8 *off = memsearch(pos, pattern, size, sizeof(pattern)); + if (!off) + return 1; + + off[0x20] = 2; + } + + else if (firmProtoVersion == 238) { // SDK 0.10 + // Same patch as for v243 ported to the different ncsd_read() function + static const u8 pattern[] = { + 0x00, 0x11, 0x9f, 0xe5, + 0x00, 0x51, 0x9f, 0xe5, + }; + + u8 *off = memsearch(pos, pattern, size, sizeof(pattern)); + if (!off) + return 1; + + off[0x20] = 2; + } + + else return 1; + + return 0; +} \ No newline at end of file diff --git a/arm9/source/patches.h b/arm9/source/patches.h index b77d092a2..75eb03a9c 100644 --- a/arm9/source/patches.h +++ b/arm9/source/patches.h @@ -31,6 +31,7 @@ * FIRM partition writes patches by delebile * Idea for svcBreak patches from yellows8 and others on #3dsdev * TWL_FIRM patches by Steveice10 and others +* Signature patches for prototype FW by PabloMK7 */ #pragma once @@ -68,3 +69,4 @@ u32 patchTwlShaHashChecks(u8 *pos, u32 size); u32 patchAgbBootSplash(u8 *pos, u32 size); void patchTwlBg(u8 *pos, u32 size); // silently fails u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size); +u32 patchProtoNandSignatureCheck(u8 *pos, u32 size); \ No newline at end of file diff --git a/arm9/source/types.h b/arm9/source/types.h index 437eade14..030dd31bb 100644 --- a/arm9/source/types.h +++ b/arm9/source/types.h @@ -135,7 +135,8 @@ typedef enum FirmwareType AGB_FIRM, SAFE_FIRM, SYSUPDATER_FIRM, - NATIVE_FIRM1X2X + NATIVE_FIRM1X2X, + NATIVE_PROTOTYPE, } FirmwareType; typedef enum bootType diff --git a/sysmodules/loader/source/loader.c b/sysmodules/loader/source/loader.c index 3455dc225..3e9f3cbb6 100644 --- a/sysmodules/loader/source/loader.c +++ b/sysmodules/loader/source/loader.c @@ -272,7 +272,7 @@ Result PLGLDR__IsPluginLoaderEnabled(bool *isEnabled) } // Try to load a plugin for the game -static Result PLGLDR_LoadPlugin(u32 processID) +static Result PLGLDR_LoadPlugin(u32 processID, bool isHomebrew) { // Special case handling: games rebooting the 3DS on old models if (!isN3DS && g_exheaderInfo.aci.local_caps.core_info.o3ds_system_mode > 0) @@ -288,8 +288,9 @@ static Result PLGLDR_LoadPlugin(u32 processID) u32* cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = IPC_MakeHeader(1, 1, 0); + cmdbuf[0] = IPC_MakeHeader(1, 2, 0); cmdbuf[1] = processID; + cmdbuf[2] = isHomebrew; return svcSendSyncRequest(plgldrHandle); } @@ -452,12 +453,12 @@ static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exh res = R_SUCCEEDED(res) ? 0 : res; // check for plugin - if (!res && !isHomebrew && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)) + if (!res && ((u32)((titleId >> 0x20) & 0xFFFFFFEDULL) == 0x00040000)) { u32 processID; assertSuccess(svcGetProcessId(&processID, *outProcessHandle)); assertSuccess(plgldrInit()); - assertSuccess(PLGLDR_LoadPlugin(processID)); + assertSuccess(PLGLDR_LoadPlugin(processID, isHomebrew)); plgldrExit(); } } diff --git a/sysmodules/loader/source/main.c b/sysmodules/loader/source/main.c index 9ad83aa0c..775a3aab1 100644 --- a/sysmodules/loader/source/main.c +++ b/sysmodules/loader/source/main.c @@ -10,7 +10,7 @@ #include "hbldr.h" u32 config, multiConfig, bootConfig; -bool isN3DS, isSdMode, nextGamePatchDisabled; +bool isN3DS, isSdMode, nextGamePatchDisabled, isLumaWithKext; // MAKE SURE fsreg has been init before calling this static Result fsldrPatchPermissions(void) @@ -33,7 +33,7 @@ static inline void loadCFWInfo(void) s64 out; u64 hbldrTid = 0; - bool isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1; + isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1; if (isLumaWithKext) { svcGetSystemInfo(&out, 0x10000, 3); @@ -60,7 +60,8 @@ static inline void loadCFWInfo(void) panic(0xDEADCAFE); #ifndef BUILD_FOR_EXPLOIT_DEV - config = 1u << PATCHVERSTRING; // all options 0, except maybe the MSET version display patch + // Most options 0, except select ones + config = BIT(PATCHVERSTRING) | BIT(PATCHGAMES) | BIT(LOADEXTFIRMSANDMODULES); #else config = 0; #endif diff --git a/sysmodules/loader/source/patcher.c b/sysmodules/loader/source/patcher.c index 59bc4c0e6..7e51f9619 100644 --- a/sysmodules/loader/source/patcher.c +++ b/sysmodules/loader/source/patcher.c @@ -966,7 +966,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 ro countryId, stateId; - if(loadTitleLocaleConfig(progId, &mask, ®ionId, &languageId, &countryId, &stateId)) + if(isLumaWithKext && loadTitleLocaleConfig(progId, &mask, ®ionId, &languageId, &countryId, &stateId)) svcKernelSetState(0x10001, ((u32)stateId << 24) | ((u32)countryId << 16) | ((u32)languageId << 8) | ((u32)regionId << 4) | (u32)mask , progId); if(!patchLayeredFs(progId, code, size, textSize, roSize, dataSize, roAddress, dataAddress)) goto error; } diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 62539ff05..c07e216c9 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -43,7 +43,7 @@ enum singleOptions }; extern u32 config, multiConfig, bootConfig; -extern bool isN3DS, isSdMode, nextGamePatchDisabled; +extern bool isN3DS, isSdMode, nextGamePatchDisabled, isLumaWithKext; void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress); bool loadTitleCodeSection(u64 progId, u8 *code, u32 size); diff --git a/sysmodules/rosalina/include/plugin/3gx.h b/sysmodules/rosalina/include/plugin/3gx.h index d14030953..068610c5b 100644 --- a/sysmodules/rosalina/include/plugin/3gx.h +++ b/sysmodules/rosalina/include/plugin/3gx.h @@ -24,7 +24,8 @@ typedef struct CTR_PACKED u32 eventsSelfManaged : 1; u32 swapNotNeeded : 1; u32 usePrivateMemory : 1; - u32 unused : 23; + u32 allowHomebrewLoad : 1; + u32 unused : 22; }; }; u32 exeLoadChecksum; diff --git a/sysmodules/rosalina/include/plugin/plgloader.h b/sysmodules/rosalina/include/plugin/plgloader.h index c7bdeab32..23b3767f1 100644 --- a/sysmodules/rosalina/include/plugin/plgloader.h +++ b/sysmodules/rosalina/include/plugin/plgloader.h @@ -34,7 +34,7 @@ u32 saveSwapFunc(void* startAddr, void* endAddr, void* args); u32 loadSwapFunc(void* startAddr, void* endAddr, void* args); u32 loadExeFunc(void* startAddr, void* endAddr, void* args); -bool TryToLoadPlugin(Handle process); +bool TryToLoadPlugin(Handle process, bool isHomebrew); void PLG__NotifyEvent(PLG_Event event, bool signal); void PLG__SetConfigMemoryStatus(u32 status); u32 PLG__GetConfigMemoryStatus(void); diff --git a/sysmodules/rosalina/source/plugin/file_loader.c b/sysmodules/rosalina/source/plugin/file_loader.c index 354080eda..82cb3f8b6 100644 --- a/sysmodules/rosalina/source/plugin/file_loader.c +++ b/sysmodules/rosalina/source/plugin/file_loader.c @@ -599,7 +599,7 @@ static char *memstr(char *haystack, const char *needle, int size) return NULL; } -bool TryToLoadPlugin(Handle process) +bool TryToLoadPlugin(Handle process, bool isHomebrew) { u64 tid; u64 fileSize; @@ -835,7 +835,14 @@ bool TryToLoadPlugin(Handle process) if (!res && fileHeader.infos.compatibility == PLG_COMPAT_EMULATOR) { ctx->error.message = "Plugin is only compatible with emulators"; - return false; + res = -1; + } + + // Check if plugin can load on homebrew + if (!res && (isHomebrew && !fileHeader.infos.allowHomebrewLoad)) { + // Do not display message as this is a common case + ctx->error.message = NULL; + res = -1; } // Flags diff --git a/sysmodules/rosalina/source/plugin/plgloader.c b/sysmodules/rosalina/source/plugin/plgloader.c index 3810f8d07..6b0876025 100644 --- a/sysmodules/rosalina/source/plugin/plgloader.c +++ b/sysmodules/rosalina/source/plugin/plgloader.c @@ -196,7 +196,7 @@ void PluginLoader__HandleCommands(void *_ctx) { case 1: // Load plugin { - if (cmdbuf[0] != IPC_MakeHeader(1, 1, 0)) + if (cmdbuf[0] != IPC_MakeHeader(1, 2, 0)) { error(cmdbuf, 0xD9001830); break; @@ -209,7 +209,7 @@ void PluginLoader__HandleCommands(void *_ctx) TaskRunner_RunTask(j_PluginLoader__SetMode3AppMode, NULL, 0); bool flash = !(ctx->useUserLoadParameters && ctx->userLoadParameters.noFlash); - if (ctx->isEnabled && TryToLoadPlugin(ctx->target)) + if (ctx->isEnabled && TryToLoadPlugin(ctx->target, cmdbuf[2])) { if (flash) {