forked from Mirror/GodMode9
Implemented FIRM booting
This commit is contained in:
parent
15882e7111
commit
e121e72f1c
4
source/common/bootfirm.h
Normal file
4
source/common/bootfirm.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
void __attribute__((noreturn)) BootFirm(void *firm, char *path);
|
151
source/common/bootfirm.s
Normal file
151
source/common/bootfirm.s
Normal file
@ -0,0 +1,151 @@
|
||||
.section .text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.equ ARG_MAGIC, 0xBEEF
|
||||
.equ MPCORE_LD, 0x27FFFB00
|
||||
.equ STUB_LOC, 0x27FFFC00
|
||||
.equ FBPTR_LOC, 0x23FFFE00
|
||||
.equ ARGV_LOC, 0x23FFFE20
|
||||
|
||||
.cpu mpcore
|
||||
MPCore_stub:
|
||||
mov r0, #0x20000000
|
||||
mov r1, #0
|
||||
str r1, [r0, #-4]
|
||||
.Lcheckmpcentry:
|
||||
ldr r1, [r0, #-4]
|
||||
cmp r1, #0
|
||||
beq .Lcheckmpcentry
|
||||
bx r1
|
||||
.pool
|
||||
MPCore_stub_end:
|
||||
|
||||
@ Assume these functions ALWAYS clobber R0-R3 and R12 and don't use the stack
|
||||
.equ MEMCPY, 0xFFFF0374 @ void memcpy32(void *src, void *dest, u32 size)
|
||||
.equ INV_DC, 0xFFFF07F0 @ void invalidate_dcache(void)
|
||||
.equ WB_DC, 0xFFFF07FC @ void writeback_dcache(void)
|
||||
.equ WBINV_DC, 0xFFFF0830 @ void writeback_invalidate_dcache(void)
|
||||
.equ INV_IC, 0xFFFF0AB4 @ void invalidate_icache(void)
|
||||
.equ INITCP15, 0xFFFF0C58 @ void reset_cp15(void)
|
||||
|
||||
.cpu arm946e-s
|
||||
@ void BootFirm_stub(void *firm, char *path)
|
||||
@ r0-r8: scratch registers
|
||||
@ r9: FIRM path
|
||||
@ r10: FIRM header
|
||||
@ r11: current section header
|
||||
BootFirm_stub:
|
||||
mov r10, r0
|
||||
add r11, r0, #0x40
|
||||
mov r9, r1
|
||||
|
||||
mov r4, #4
|
||||
.LBootFirm_stub_copysect:
|
||||
@ Fetch source, destination and length
|
||||
ldmia r11, {r0-r2}
|
||||
|
||||
cmp r2, #0 @ If section is unused/zerolength, don't even bother
|
||||
addne r0, r10 @ Fix source address
|
||||
|
||||
ldrne r3, =MEMCPY
|
||||
blxne r3
|
||||
|
||||
subs r4, #1
|
||||
addne r11, #0x30 @ Advance to the next section
|
||||
bne .LBootFirm_stub_copysect
|
||||
|
||||
@ Boot state
|
||||
@ CPSR:
|
||||
@ ARM, Supervisor, IRQ/FIQs disabled
|
||||
@ Flags are undefined
|
||||
msr cpsr_c, #0xD3
|
||||
|
||||
@ CP15:
|
||||
@ MPU and Caches are off
|
||||
@ TCMs are on (location/configuration is undefined)
|
||||
@ Alternative exception vectors are enabled (0xFFFF0000)
|
||||
ldr r3, =WBINV_DC
|
||||
ldr r4, =INV_IC
|
||||
ldr r5, =INITCP15
|
||||
blx r3
|
||||
blx r4
|
||||
blx r5
|
||||
|
||||
@ Registers:
|
||||
@ R0 = 0x00000002
|
||||
@ R1 = 0x23FFFE10
|
||||
@ R2 = 0x0000BEEF
|
||||
@ R3-R14 are undefined
|
||||
|
||||
mov r0, #2
|
||||
ldr r1, =ARGV_LOC
|
||||
ldr r2, =ARG_MAGIC
|
||||
|
||||
|
||||
@ Setup argv
|
||||
str r9, [r1, #0x00] @ FIRM path / argv[0]
|
||||
|
||||
ldr r3, =FBPTR_LOC
|
||||
str r3, [r1, #0x04] @ Framebuffers / argv[1]
|
||||
|
||||
@ Fetch FIRM entrypoints
|
||||
ldr r3, [r10, #0x08] @ ARM11 entrypoint
|
||||
ldr r4, [r10, #0x0C] @ ARM9 entrypoint
|
||||
|
||||
@ Set the ARM11 entrypoint
|
||||
mov r5, #0x20000000
|
||||
str r3, [r5, #-4]
|
||||
|
||||
@ Branch to the ARM9 entrypoint
|
||||
bx r4
|
||||
|
||||
.pool
|
||||
BootFirm_stub_end:
|
||||
|
||||
@ void BootFirm(void *firm, char *path)
|
||||
@ BootFirm_stub wrapper
|
||||
@ No checks are performed on the data
|
||||
.global BootFirm
|
||||
.type BootFirm, %function
|
||||
BootFirm:
|
||||
mov r10, r0
|
||||
|
||||
@ Copy the FIRM path somewhere safe
|
||||
ldr r0, =(ARGV_LOC+8)
|
||||
mov r11, r0
|
||||
blx strcpy
|
||||
|
||||
@ Relocate the MPCore stub binary
|
||||
ldr r4, =MPCORE_LD
|
||||
adr r1, MPCore_stub
|
||||
adr r2, MPCore_stub_end
|
||||
sub r2, r1
|
||||
mov r0, r4
|
||||
blx memcpy
|
||||
|
||||
@ Make the ARM11 run the stub, wait until its done
|
||||
mov r1, #0x20000000
|
||||
mov r0, r4
|
||||
str r0, [r1, #-4]
|
||||
.Lwaitforsi:
|
||||
ldr r0, [r1, #-4]
|
||||
cmp r0, #0
|
||||
bne .Lwaitforsi
|
||||
|
||||
@ Relocate BootFirm
|
||||
ldr r4, =STUB_LOC
|
||||
adr r5, BootFirm_stub
|
||||
adr r6, BootFirm_stub_end
|
||||
sub r7, r6, r5
|
||||
|
||||
mov r0, r4
|
||||
mov r1, r5
|
||||
mov r2, r7
|
||||
blx memcpy
|
||||
|
||||
mov r0, r10
|
||||
mov r1, r11
|
||||
|
||||
bx r4
|
||||
b .
|
@ -4,4 +4,5 @@
|
||||
|
||||
#define PAYLOAD_MAX_SIZE 0xFFFE0
|
||||
|
||||
void Chainload(u8 *source, size_t size);
|
||||
void __attribute__((noreturn)) Chainload(u8 *source, size_t size);
|
||||
void __attribute__((noreturn)) BootFirm(FirmHeader *firm, char *path);
|
||||
|
@ -9,6 +9,14 @@
|
||||
// 0 -> pre 9.5 / 1 -> 9.5 / 2 -> post 9.5
|
||||
#define A9L_CRYPTO_TYPE(hdr) ((hdr->k9l[3] == 0xFF) ? 0 : (hdr->k9l[3] == '1') ? 1 : 2)
|
||||
|
||||
// valid addresses for FIRM section loading, pairs of start / end address, provided by Wolfvak
|
||||
#define FIRM_VALID_ADDRESS \
|
||||
0x08006000, 0x08800000, \
|
||||
0x18000000, 0x18600000, \
|
||||
0x1FFF0000, 0x1FFFFFFC, \
|
||||
0x20000000, 0x23FFFE00, \
|
||||
0x24000000, 0x27FFFB00
|
||||
|
||||
u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
|
||||
u8 magic[] = { FIRM_MAGIC };
|
||||
if (memcmp(header->magic, magic, sizeof(magic)) != 0)
|
||||
@ -21,6 +29,7 @@ u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
|
||||
FirmSectionHeader* section = header->sections + i;
|
||||
if (!section->size) continue;
|
||||
if (section->offset < firm_size) return 1;
|
||||
if ((section->offset % 512) || (section->address % 512) || (section->size % 512)) return 1;
|
||||
if ((header->entry_arm11 >= section->address) &&
|
||||
(header->entry_arm11 < section->address + section->size))
|
||||
section_arm11 = i;
|
||||
@ -50,6 +59,36 @@ u32 ValidateFirmA9LHeader(FirmA9LHeader* header) {
|
||||
return sha_cmp((IS_DEVKIT) ? enckeyX0x15devhash : enckeyX0x15hash, header->keyX0x15, 0x10, SHA256_MODE);
|
||||
}
|
||||
|
||||
u32 ValidateFirm(void* firm, u32 firm_size) {
|
||||
FirmHeader* header = (FirmHeader*) firm;
|
||||
|
||||
// validate firm header
|
||||
if (ValidateFirmHeader(header, firm_size) != 0)
|
||||
return 1;
|
||||
|
||||
// hash verify all available sections and check load address
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
u32 valid_address[] = { FIRM_VALID_ADDRESS };
|
||||
FirmSectionHeader* section = header->sections + i;
|
||||
if (!section->size) continue;
|
||||
if (sha_cmp(section->hash, (u8*) firm + section->offset, section->size, SHA256_MODE) != 0) {
|
||||
return 1;
|
||||
}
|
||||
bool is_valid_address = false;
|
||||
for (u32 a = 0; a < sizeof(valid_address) / (2*sizeof(u32)); a++) {
|
||||
if ((valid_address[2*a] >= section->address) && (valid_address[(2*a)+1] <= section->address + section->size))
|
||||
is_valid_address = true;
|
||||
}
|
||||
if (!is_valid_address) return 1;
|
||||
}
|
||||
|
||||
// section for arm9 entrypoint not found?
|
||||
if (!FindFirmArm9Section(header))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FirmSectionHeader* FindFirmArm9Section(FirmHeader* firm) {
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
FirmSectionHeader* section = firm->sections + i;
|
||||
|
@ -43,6 +43,7 @@ typedef struct {
|
||||
|
||||
u32 ValidateFirmHeader(FirmHeader* header, u32 data_size);
|
||||
u32 ValidateFirmA9LHeader(FirmA9LHeader* header);
|
||||
u32 ValidateFirm(void* firm, u32 firm_size);
|
||||
|
||||
FirmSectionHeader* FindFirmArm9Section(FirmHeader* firm);
|
||||
u32 GetArm9BinarySize(FirmA9LHeader* a9l);
|
||||
|
@ -1417,11 +1417,15 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
}
|
||||
return 0;
|
||||
} else if ((user_select == boot)) {
|
||||
size_t payload_size = FileGetSize(curr_entry->path);
|
||||
if (ShowUnlockSequence(3, "%s (%dkB)\nBoot FIRM via boot9strap?\n(requires boot9strap nightly)", pathstr, payload_size / 1024)) {
|
||||
u32 flags = OVERWRITE_ALL;
|
||||
if (PathMoveCopy("0:/bootonce.firm", curr_entry->path, &flags, false))
|
||||
Reboot();
|
||||
size_t firm_size = FileGetSize(curr_entry->path);
|
||||
if (firm_size > TEMP_BUFFER_SIZE) {
|
||||
ShowPrompt(false, "FIRM too big, can't launch"); // unlikely
|
||||
} else if (ShowUnlockSequence(3, "%s (%dkB)\nBoot FIRM via chainloader?", pathstr, firm_size / 1024)) {
|
||||
if ((FileGetData(curr_entry->path, TEMP_BUFFER, firm_size, 0) == firm_size) &&
|
||||
(ValidateFirm(TEMP_BUFFER, firm_size) != 0)) {
|
||||
BootFirm((FirmHeader*)(void*)TEMP_BUFFER, curr_entry->path);
|
||||
while(1);
|
||||
} else ShowPrompt(false, "Not a vaild FIRM, can't launch");
|
||||
}
|
||||
return 0;
|
||||
} else if ((user_select == script)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user