Added TWL/AGB FIRM patching/SD loading for New and Old 3DS (thanks to mid-kid of CakesFW for making it possible!)

This commit is contained in:
Aurora 2016-04-03 17:56:09 +02:00
parent 993e564fbb
commit a181bba9f2
3 changed files with 294 additions and 202 deletions

View File

@ -23,6 +23,7 @@ static u32 loadPayload(const char *path)
void main(void)
{
FATFS fs;
f_mount(&fs, "0:", 1);
//Get pressed buttons

View File

@ -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
; 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.
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
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
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
; 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)
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
hex_string_to_int_loop:
ldr r3, [r1], #2 ; 2 because it's a utf-16 string.
and r3, #0xFF
ReadFirm:
; 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
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
ldr r0, =kernelCode
.word 0xEF00007B //SVC 0x7B
die:
b die
InfiniteLoop:
b InfiniteLoop
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
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
ADD R3, R4, #0x70
LDR R0, [R3]
ADD R0, R0, R4
LDR R1, [R3,#4]
LDR R2, [R3,#8]
bl KernelMemcpy
; 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)
ADD R3, R4, #0xA0
LDR R0, [R3]
ADD R0, R0, R4
LDR R1, [R3,#4]
LDR R2, [R3,#8]
bl KernelMemcpy
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
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
cmp r5, r6
addlo r5, #0x30
blo copy_firm_loop
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
; 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
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
; 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

View File

@ -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;