diff --git a/loader/source/main.c b/loader/source/main.c index 97f4756..8573d0b 100644 --- a/loader/source/main.c +++ b/loader/source/main.c @@ -23,6 +23,7 @@ static u32 loadPayload(const char *path) void main(void) { FATFS fs; + f_mount(&fs, "0:", 1); //Get pressed buttons diff --git a/patches/reboot.s b/patches/reboot.s index b6c1aa6..f3cf0b9 100644 --- a/patches/reboot.s +++ b/patches/reboot.s @@ -1,221 +1,232 @@ .arm.little -byteswritten equ 0x2000E000 -kernelCode equ 0x080F0000 -buffer equ 0x24000000 -fileOpen equ 0x4E45504F ;dummy +firm_addr equ 0x24000000 ; Temporary location where we'll load the FIRM to +firm_maxsize equ 0x200000 ; Random value that's bigger than any of the currently known firm's sizes. +kernel_code equ 0x080F0000 .create "reboot.bin", 0 .arm -//Code jumps here right after the sprintf call -process9Reboot: - doPxi: - ldr r4, =0x44846 - ldr r0, =0x10008000 - readPxiLoop1: - ldrh r1, [r0,#4] - .word 0xE1B01B81 //lsls r1, r1, #0x17 - bmi readPxiLoop1 - ldr r0, [r0,#0xC] - cmp r0, r4 - bne doPxi - - GetFirmPath: - add r0, sp, #0x3A8-0x70+0x24 - ldr r1, [r0], #4 - ldr r2, =0x00300030 - cmp r1, r2 - ldreq r1, [r0], #4 - ldreq r2, =0x002F0032 - cmpeq r1, r2 - - OpenFirm: - ldreq r1, =(FileName - OpenFirm - 12) - addeq r1, pc - addne r1, sp, #0x3A8-0x70 - moveq r2, #1 - movne r2, #0 - str r2, [externalFirm] - mov r2, #1 - add r0, r7, #8 - ldr r6, =fileOpen - blx r6 - - SeekFirm: - ldr r0, [externalFirm] - cmp r0, #1 - moveq r0, r7 - ldreq r1, =byteswritten - ldreq r2, =buffer - ldreq r3, =0x0 - ldreq r6, [sp,#0x3A8-0x198] - ldreq r6, [r6,#0x28] //fread function stored here - blxeq r6 - - ReadFirm: + ; Interesting registers and locations to keep in mind, set before this code is ran: + ; - sp + 0x3A8 - 0x70: FIRM path in exefs. + ; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle + ; - *(sp + 0x3A8 - 0x198) + 0x28: fread function. + + pxi_wait_recv: + ldr r2, =0x44846 + ldr r0, =0x10008000 + readPxiLoop1: + ldrh r1, [r0, #4] + lsls r1, #0x17 + bmi readPxiLoop1 + ldr r0, [r0, #0xC] + cmp r0, r2 + bne pxi_wait_recv + + ; Convert 2 bytes of the path string + ; This will be the method of getting the lower 2 bytes of the title ID + ; until someone bothers figuring out where the value is derived from. + mov r0, #0 ; Result + add r1, sp, #0x3A8 - 0x70 + add r1, #0x22 ; The significant bytes + mov r2, #4 ; Maximum loops (amount of bytes * 2) + + hex_string_to_int_loop: + ldr r3, [r1], #2 ; 2 because it's a utf-16 string. + and r3, #0xFF + + ; Check if it"s a number + cmp r3, #'0' + blo hex_string_to_int_end + sub r3, #'0' + cmp r3, #9 + bls hex_string_to_int_calc + + ; Check if it"s a capital letter + cmp r3, #'A' - '0' + blo hex_string_to_int_end + sub r3, #'A' - '0' - 0xA ; Make the correct value: 0xF >= al >= 0xA + cmp r3, #0xF + bls hex_string_to_int_calc + + ; Incorrect value: x > "A" + bhi hex_string_to_int_end + + hex_string_to_int_calc: + orr r0, r3, r0, lsl #4 + subs r2, #1 + bne hex_string_to_int_loop + hex_string_to_int_end: + + ; Get the FIRM path + cmp r0, #0x0002 ; NATIVE_FIRM + adreq r1, firm_fname + beq load_firm + ldr r5, =0x0102 ; TWL_FIRM + cmp r0, r5 + adreq r1, twlfirm_fname + beq load_firm + ldr r5, =0x0202 ; AGB_FIRM + cmp r0, r5 + adreq r1, agbfirm_fname + beq load_firm + bne fallback ; TODO: Stubbed + + fallback: + ; Fallback: Load specified FIRM from exefs + add r1, sp, #0x3A8-0x70 ; Location of exefs string. + b load_firm + + load_firm: + ; Open file + add r0, r7, #8 + mov r2, #1 + ldr r6, [fopen] + orr r6, 1 + blx r6 + + cmp r0, #0 ; Check if we were able to load the FIRM + bne fallback ; Otherwise, try again with the FIRM from exefs. + ; This will loop indefinitely if the exefs FIRM fails to load, but whatever. + + ; Read file mov r0, r7 - ldr r1, =byteswritten - ldr r2, =buffer - ldr r3, =0x200000 - ldr r6, [sp,#0x3A8-0x198] - ldr r6, [r6,#0x28] //fread function stored here + adr r1, bytes_read + mov r2, firm_addr + mov r3, firm_maxsize + ldr r6, [sp, #0x3A8-0x198] + ldr r6, [r6, #0x28] blx r6 - KernelSetState: - mov r2, #0 - mov r3, r2 - mov r1, r2 - mov r0, r2 - .word 0xEF00007C //SVC 0x7C + ; Set kernel state + mov r0, #0 + mov r1, #0 + mov r2, #0 + mov r3, #0 + swi 0x7C - GoToReboot: - ldr r0, =(KernelCodeStart - GoToReboot - 12) - add r0, pc - ldr r1, =kernelCode - ldr r2, =0x300 - bl Memcpy - - ldr r0, =kernelCode - .word 0xEF00007B //SVC 0x7B + goto_reboot: + ; Jump to reboot code + ldr r0, =(kernelcode_start - goto_reboot - 12) + add r0, pc + ldr r1, =kernel_code + ldr r2, =0x300 + bl memcpy32 + ldr r0, =kernel_code + swi 0x7B - InfiniteLoop: - b InfiniteLoop + die: + b die -Memcpy: - MOV R12, LR - STMFD SP!, {R0-R4} - ADD R2, R2, R0 - - memcpyLoop: - LDR R3, [R0],#4 - STR R3, [R1],#4 - CMP R0, R2 - BLT memcpyLoop - LDMFD SP!, {R0-R4} - MOV LR, R12 - BX LR - -FileName: - .dcw "sdmc:/aurei/patched_firmware_sys.bin" - .word 0x0 - -externalFirm: - .word 0x2000A000 +memcpy32: ; memcpy32(void *src, void *dst, unsigned int size) + add r2, r0 + memcpy32_loop: + ldmia r0!, {r3} + stmia r1!, {r3} + cmp r0, r2 + blo memcpy32_loop + bx lr +bytes_read: .word 0 +fopen: .ascii "OPEN" .pool +firm_fname: .dcw "sdmc:/aurei/patched_firmware_sys.bin" + .word 0x0 +.pool +twlfirm_fname: .dcw "sdmc:/aurei/patched_firmware_twl.bin" + .word 0x0 +.pool +agbfirm_fname: .dcw "sdmc:/aurei/patched_firmware_agb.bin" + .word 0x0 -// Kernel Code .align 4 -KernelCodeStart: - memorySetting: - MRC p15, 0, R0,c2,c0, 0 - MRC p15, 0, R12,c2,c0, 1 - MRC p15, 0, R1,c3,c0, 0 - MRC p15, 0, R2,c5,c0, 2 - MRC p15, 0, R3,c5,c0, 3 - LDR R4, =0x18000035 - BIC R2, R2, #0xF0000 - BIC R3, R3, #0xF0000 - ORR R0, R0, #0x10 - ORR R2, R2, #0x30000 - ORR R3, R3, #0x30000 - ORR R12, R12, #0x10 - ORR R1, R1, #0x10 - MCR p15, 0, R0,c2,c0, 0 - MCR p15, 0, R12,c2,c0, 1 - MCR p15, 0, R1,c3,c0, 0 - MCR p15, 0, R2,c5,c0, 2 - MCR p15, 0, R3,c5,c0, 3 - MCR p15, 0, R4,c6,c4, 0 - MRC p15, 0, R0,c2,c0, 0 - MRC p15, 0, R1,c2,c0, 1 - MRC p15, 0, R2,c3,c0, 0 - ORR R0, R0, #0x20 - ORR R1, R1, #0x20 - ORR R2, R2, #0x20 - MCR p15, 0, R0,c2,c0, 0 - MCR p15, 0, R1,c2,c0, 1 - MCR p15, 0, R2,c3,c0, 0 + kernelcode_start: + ; Set MPU settings + mrc p15, 0, r0, c2, c0, 0 ; dcacheable + mrc p15, 0, r12, c2, c0, 1 ; icacheable + mrc p15, 0, r1, c3, c0, 0 ; write bufferable + mrc p15, 0, r2, c5, c0, 2 ; daccess + mrc p15, 0, r3, c5, c0, 3 ; iaccess + ldr r4, =0x18000035 ; 0x18000000 128M + bic r2, r2, #0xF0000 ; unprotect region 4 + bic r3, r3, #0xF0000 ; unprotect region 4 + orr r0, r0, #0x10 ; dcacheable region 4 + orr r2, r2, #0x30000 ; region 4 r/w + orr r3, r3, #0x30000 ; region 4 r/w + orr r12, r12, #0x10 ; icacheable region 4 + orr r1, r1, #0x10 ; write bufferable region 4 + mcr p15, 0, r0, c2, c0, 0 + mcr p15, 0, r12, c2, c0, 1 + mcr p15, 0, r1, c3, c0, 0 ; write bufferable + mcr p15, 0, r2, c5, c0, 2 ; daccess + mcr p15, 0, r3, c5, c0, 3 ; iaccess + mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm) - copyFirmPartitions: - LDR R4, =buffer - ADD R3, R4, #0x40 - LDR R0, [R3] - ADD R0, R0, R4 - LDR R1, [R3,#4] - LDR R2, [R3,#8] - bl KernelMemcpy - - ADD R3, R4, #0x70 - LDR R0, [R3] - ADD R0, R0, R4 - LDR R1, [R3,#4] - LDR R2, [R3,#8] - bl KernelMemcpy - - ADD R3, R4, #0xA0 - LDR R0, [R3] - ADD R0, R0, R4 - LDR R1, [R3,#4] - LDR R2, [R3,#8] - bl KernelMemcpy - - ADD R3, R4, #0xD0 - LDR R0, [R3] - CMP R0, #0 - BEQ invalidateDataCache - ADD R0, R0, R4 - LDR R1, [R3,#4] - LDR R2, [R3,#8] - bl KernelMemcpy - - invalidateDataCache: - MOV R2, #0 - MOV R1, R2 - loc_809460C: - MOV R0, #0 - MOV R3, R2,LSL#30 - loc_8094614: - ORR R12, R3, R0,LSL#5 - MCR p15, 0, R1,c7,c10, 4 - MCR p15, 0, R12,c7,c14, 2 - ADD R0, R0, #1 - CMP R0, #0x20 - BCC loc_8094614 - ADD R2, R2, #1 - CMP R2, #4 - BCC loc_809460C + mrc p15, 0, r0, c2, c0, 0 ; dcacheable + mrc p15, 0, r1, c2, c0, 1 ; icacheable + mrc p15, 0, r2, c3, c0, 0 ; write bufferable + orr r0, r0, #0x20 ; dcacheable region 5 + orr r1, r1, #0x20 ; icacheable region 5 + orr r2, r2, #0x20 ; write bufferable region 5 + mcr p15, 0, r0, c2, c0, 0 ; dcacheable + mcr p15, 0, r1, c2, c0, 1 ; icacheable + mcr p15, 0, r2, c3, c0, 0 ; write bufferable - jumpToEntrypoint: - MCR p15, 0, R1,c7,c10, 4 - LDR R0, =0x42078 - MCR p15, 0, R0,c1,c0, 0 - MCR p15, 0, R1,c7,c5, 0 - MCR p15, 0, R1,c7,c6, 0 - MCR p15, 0, R1,c7,c10, 4 - LDR R4, =buffer - MOV R1, #0x1FFFFFFC - LDR R2, [R4,#8] - STR R2, [R1] - LDR R0, [R4,#0xC] - BX R0 + ; Copy the firmware + mov r4, firm_addr + add r5, r4, #0x40 ; Start of loop + add r6, r5, #0x30 * 3 ; End of loop (scan 4 entries) + + copy_firm_loop: + ldr r0, [r5] + cmp r0, #0 + addne r0, r4 ; src + ldrne r1, [r5, #4] ; dest + ldrne r2, [r5, #8] ; size + blne kernelmemcpy32 + + cmp r5, r6 + addlo r5, #0x30 + blo copy_firm_loop + + ; Flush cache + mov r2, #0 + mov r1, r2 + flush_cache: + mov r0, #0 + mov r3, r2, lsl #30 + flush_cache_inner_loop: + orr r12, r3, r0, lsl#5 + mcr p15, 0, r1, c7, c10, 4 ; drain write buffer + mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment) + add r0, #1 + cmp r0, #0x20 + bcc flush_cache_inner_loop + add r2, #1 + cmp r2, #4 + bcc flush_cache + + ; Enable MPU + ldr r0, =0x42078 ; alt vector select, enable itcm + mcr p15, 0, r0, c1, c0, 0 + mcr p15, 0, r1, c7, c5, 0 ; flush dcache + mcr p15, 0, r1, c7, c6, 0 ; flush icache + mcr p15, 0, r1, c7, c10, 4 ; drain write buffer + mov r0, firm_addr + + ; Boot FIRM + mov r1, #0x1FFFFFFC + ldr r2, [r0, #8] ; arm11 entry + str r2, [r1] + ldr r0, [r0, #0xC] ; arm9 entry + bx r0 .pool -KernelMemcpy: - MOV R12, LR - STMFD SP!, {R0-R4} - ADD R2, R2, R0 - - kmemcpyLoop: - LDR R3, [R0],#4 - STR R3, [R1],#4 - CMP R0, R2 - BLT kmemcpyLoop - LDMFD SP!, {R0-R4} - MOV LR, R12 - BX LR -.pool - -KernelCodeEnd: - +kernelmemcpy32: ; memcpy32(void *src, void *dst, unsigned int size) + add r2, r0 + kmemcpy32_loop: + ldmia r0!, {r3} + stmia r1!, {r3} + cmp r0, r2 + blo kmemcpy32_loop + bx lr .close diff --git a/source/firm.c b/source/firm.c index e84446a..2cdcce8 100755 --- a/source/firm.c +++ b/source/firm.c @@ -26,7 +26,9 @@ static u8 *arm9Section; static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin", "/aurei/patched_firmware_emu.bin", "/aurei/patched_firmware_em2.bin", - "/aurei/patched_firmware90.bin" }; + "/aurei/patched_firmware90.bin", + "/aurei/patched_firmware_twl.bin", + "/aurei/patched_firmware_agb.bin" }; static u32 firmSize, console, @@ -212,6 +214,80 @@ void loadFirm(void) if(console && !usePatchedFirm) decryptArm9Bin(arm9Section, mode); } +static inline void patchTwlAgb(u32 mode) +{ + static firmHeader *const twlAgbFirm = (firmHeader *)0x25000000; + + const char *path = mode ? "aurei/firmware_agb.bin" : "aurei/firmware_twl.bin"; + u32 size = fileSize(path); + + //Skip patching + if(!size) return; + + fileRead(twlAgbFirm, path, size); + + const firmSectionHeader *twlAgbSection = twlAgbFirm->section; + + //Check that the loaded FIRM matches the console + if((((u32)twlAgbSection[3].address >> 8) & 0xFF) != (console ? 0x60 : 0x68)) + error(mode ? "aurei/firmware_agb.bin doesn't match this\nconsole, or it's encrypted" : + "aurei/firmware_twl.bin doesn't match this\nconsole, or it's encrypted"); + + if(console) decryptArm9Bin((u8 *)twlAgbFirm + twlAgbSection[3].offset, 0); + + struct patchData { + u32 offset[2]; + union { + u8 type0[8]; + u16 type1; + } patch; + u32 type; + }; + + const struct patchData twlPatches[] = { + {{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, + {{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1}, + {{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2}, + {{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2}, + {{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2}, + {{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2}, + {{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2}, + {{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1}, + {{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1} + }; + + const struct patchData agbPatches[] = { + {{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, + {{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1} + }; + + //Calculate the amount of patches to apply + u32 numPatches = mode ? (sizeof(agbPatches) / sizeof(struct patchData)) : (sizeof(twlPatches) / sizeof(struct patchData)); + const struct patchData *patches = mode ? agbPatches : twlPatches; + + //Patch + for(u32 i = 0; i < numPatches; i++) + { + switch(patches[i].type) + { + case 0: + memcpy((u8 *)twlAgbFirm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]); + break; + case 2: + *(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console] + 2) = 0; + case 1: + *(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console]) = patches[i].patch.type1; + break; + } + } + + //Patch ARM9 entrypoint on N3DS to skip arm9loader + if(console) + twlAgbFirm->arm9Entry = (u8 *)0x801301C; + + fileWrite(twlAgbFirm, mode ? patchedFirms[5] : patchedFirms[4], size); +} + //NAND redirection static inline void patchEmuNAND(u8 *proc9Offset) { @@ -297,6 +373,10 @@ static inline void injectLoader(void) //Patches void patchFirm(void) { + //Only patch AGB_FIRM/TWL_FIRM if the patched ones don't already exist + if(!fileExists(patchedFirms[4])) patchTwlAgb(0); + if(!fileExists(patchedFirms[5])) patchTwlAgb(1); + //Skip patching if(usePatchedFirm) return;