Implemented FIRM booting

This commit is contained in:
Wolfvak 2017-07-29 13:17:31 +02:00 committed by d0k3
parent 15882e7111
commit e121e72f1c
6 changed files with 206 additions and 6 deletions

4
source/common/bootfirm.h Normal file
View 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
View 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 .

View File

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

View File

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

View File

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

View File

@ -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)) {