diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index 7a7c38f8d..a6ae3eb22 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -27,7 +27,8 @@ CFLAGS = \ -std=gnu11 \ -Werror \ -Wall \ - -fstrict-volatile-bitfields + -fstrict-volatile-bitfields \ + -DFUSEE_STAGE1_SRC LDFLAGS = -specs=linker.specs -g $(ARCH) diff --git a/fusee/fusee-primary/linker.ld b/fusee/fusee-primary/linker.ld index 8dc2a6098..7483c12df 100644 --- a/fusee/fusee-primary/linker.ld +++ b/fusee/fusee-primary/linker.ld @@ -3,19 +3,57 @@ OUTPUT_ARCH(arm) ENTRY(_start) /* Mostly copied from https://github.com/devkitPro/buildscripts/blob/master/dkarm-eabi/crtls/3dsx.ld */ +MEMORY +{ + NULL : ORIGIN = 0x00000000, LENGTH = 0x1000 + main : ORIGIN = 0x40010000, LENGTH = 0x20000 + low_iram : ORIGIN = 0x40003000, LENGTH = 0x8000 +} SECTIONS { - PROVIDE(__start__ = 0x40010000); + PROVIDE(__start__ = 0x40010000); + PROVIDE(__stack_top__ = 0x40010000); + PROVIDE(__stack_bottom__ = 0x4000C000); + PROVIDE(__heap_start__ = 0); + PROVIDE(__heap_end__ = 0); + . = __start__; . = ALIGN(32); - .text : + + .crt0 : { . = ALIGN(32); - /* .init */ KEEP( *(.text.start) ) KEEP( *(.init) ) . = ALIGN(4); + } >main + + .chainloader_loadable : + { + . = ALIGN(32); + PROVIDE (__chainloader_start__ = .); + PROVIDE (__chainloader_lma__ = LOADADDR(.chainloader_loadable)); + KEEP(*(.chainloader.text.start)) + build/chainloader.o(.text*) + build/chainloader.o(.rodata*) + build/chainloader.o(.data*) + . = ALIGN(8); + + } >low_iram AT>main + + .chainloader_bss : + { + . = ALIGN(8); + PROVIDE (__chainloader_bss_start__ = .); + build/chainloader.o(.bss* COMMON) + . = ALIGN(8); + PROVIDE (__chainloader_end__ = .); + } >low_iram AT>main + + .text : + { + . = ALIGN(4); /* .text */ *(.text) @@ -30,7 +68,7 @@ SECTIONS /* .fini */ KEEP( *(.fini) ) . = ALIGN(4); - } + } >main .rodata : { @@ -41,14 +79,14 @@ SECTIONS *(.gnu.linkonce.r*) SORT(CONSTRUCTORS) . = ALIGN(4); - } + } >main .preinit_array ALIGN(4) : { PROVIDE (__preinit_array_start = .); KEEP (*(.preinit_array)) PROVIDE (__preinit_array_end = .); - } + } >main .init_array ALIGN(4) : { @@ -56,7 +94,7 @@ SECTIONS KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) PROVIDE (__init_array_end = .); - } + } >main .fini_array ALIGN(4) : { @@ -64,7 +102,7 @@ SECTIONS KEEP (*(.fini_array)) KEEP (*(SORT(.fini_array.*))) PROVIDE (__fini_array_end = .); - } + } >main .ctors ALIGN(4) : { @@ -72,7 +110,7 @@ SECTIONS KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) - } + } >main .dtors ALIGN(4) : { @@ -80,11 +118,11 @@ SECTIONS KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) - } + } >main - .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >main __exidx_start = .; - ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } + ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >main __exidx_end = .; .data : @@ -94,19 +132,19 @@ SECTIONS *(.gnu.linkonce.d*) CONSTRUCTORS . = ALIGN(4); - } + } >main - __bss_start__ = ALIGN(32); .bss : { + __bss_start__ = ALIGN(32); *(.dynbss) *(.bss) *(.bss.*) *(.gnu.linkonce.b*) *(COMMON) . = ALIGN(8); - } - __bss_end__ = .; + __bss_end__ = .; + } >main __end__ = ABSOLUTE(.) ; /* ================== diff --git a/fusee/fusee-primary/src/chainloader.c b/fusee/fusee-primary/src/chainloader.c new file mode 100644 index 000000000..4caa70792 --- /dev/null +++ b/fusee/fusee-primary/src/chainloader.c @@ -0,0 +1,34 @@ +#include "chainloader.h" + +char g_chainloader_arg_data[CHAINLOADER_ARG_DATA_MAX_SIZE] = {0}; +chainloader_entry_t g_chainloader_entries[CHAINLOADER_MAX_ENTRIES] = {0}; /* keep them sorted */ +size_t g_chainloader_num_entries = 0; +uintptr_t g_chainloader_entrypoint = 0; + +#pragma GCC optimize (3) + +static void *xmemmove(void *dst, const void *src, size_t len) +{ + const uint8_t *src8 = (const uint8_t *)src; + uint8_t *dst8 = (uint8_t *)dst; + + if (dst8 < src8) { + for (size_t i = 0; i < len; i++) { + dst8[i] = src8[i]; + } + } else if (src8 > dst8) { + for (size_t i = len; len > 0; len--) + dst8[i - 1] = src8[i - 1]; + } + + return dst; +} + +void relocate_and_chainload_main(int argc) { + for(size_t i = 0; i < g_chainloader_num_entries; i++) { + chainloader_entry_t *entry = &g_chainloader_entries[i]; + xmemmove((void *)entry->load_address, (const void *)entry->src_address, entry->size); + } + + ((void (*)(int, void *))g_chainloader_entrypoint)(argc, g_chainloader_arg_data); +} diff --git a/fusee/fusee-primary/src/chainloader.h b/fusee/fusee-primary/src/chainloader.h new file mode 100644 index 000000000..9d9e44347 --- /dev/null +++ b/fusee/fusee-primary/src/chainloader.h @@ -0,0 +1,25 @@ +#ifndef FUSEE_CHAINLOADER_H +#define FUSEE_CHAINLOADER_H + +#include +#include + +#define CHAINLOADER_ARG_DATA_MAX_SIZE 0x6200 +#define CHAINLOADER_MAX_ENTRIES 128 + +typedef struct chainloader_entry_t { + uintptr_t load_address; + uintptr_t src_address; + size_t size; + size_t num; +} chainloader_entry_t; + +extern chainloader_entry_t g_chainloader_entries[CHAINLOADER_MAX_ENTRIES]; /* keep them sorted */ +extern size_t g_chainloader_num_entries; +extern uintptr_t g_chainloader_entrypoint; + +extern char g_chainloader_arg_data[CHAINLOADER_ARG_DATA_MAX_SIZE]; + +void relocate_and_chainload(int argc); + +#endif diff --git a/fusee/fusee-primary/src/hwinit.h b/fusee/fusee-primary/src/hwinit.h index ce0e50663..2e7994bca 100644 --- a/fusee/fusee-primary/src/hwinit.h +++ b/fusee/fusee-primary/src/hwinit.h @@ -28,7 +28,7 @@ void clock_enable_fuse(u32 enable); void display_color_screen(u32 color); /*! Init display in full 1280x720 resolution (32bpp, line stride 768, framebuffer size = 1280*768*4 bytes). */ -u32 *display_init_framebuffer(); +u32 *display_init_framebuffer(void *address); /*! Enable or disable the backlight. Should only be called when the screen is completely set up, to avoid flickering. */ void display_enable_backlight(bool on); diff --git a/fusee/fusee-primary/src/hwinit/di.c b/fusee/fusee-primary/src/hwinit/di.c index 3b117c67a..5cb8d8b88 100644 --- a/fusee/fusee-primary/src/hwinit/di.c +++ b/fusee/fusee-primary/src/hwinit/di.c @@ -188,9 +188,9 @@ void display_enable_backlight(bool on) { } -u32 *display_init_framebuffer(void) +u32 *display_init_framebuffer(void *address) { - u32 *lfb_addr = (u32 *)0xC0000000; + u32 *lfb_addr = (u32 *)address; //This configures the framebuffer @ 0xC0000000 with a resolution of 1280x720 (line stride 768). exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer, 32); diff --git a/fusee/fusee-primary/src/hwinit/di.h b/fusee/fusee-primary/src/hwinit/di.h index a1058823c..9d482e072 100644 --- a/fusee/fusee-primary/src/hwinit/di.h +++ b/fusee/fusee-primary/src/hwinit/di.h @@ -52,7 +52,7 @@ void display_end(); void display_color_screen(u32 color); /*! Init display in full 1280x720 resolution (32bpp, line stride 768, framebuffer size = 1280*768*4 bytes). */ -u32 *display_init_framebuffer(void); +u32 *display_init_framebuffer(void *address); /*! Enable or disable the backlight. Should only be called when the screen is completely set up, to avoid flickering. */ void display_enable_backlight(bool on); diff --git a/fusee/fusee-primary/src/init.c b/fusee/fusee-primary/src/init.c new file mode 100644 index 000000000..a0221aba0 --- /dev/null +++ b/fusee/fusee-primary/src/init.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include "utils.h" + +void __libc_init_array(void); +void __libc_fini_array(void); + +extern uint8_t __bss_start__[], __bss_end__[]; +extern uint8_t __heap_start__[], __heap_end__[]; + +extern char *fake_heap_start; +extern char *fake_heap_end; + +int __program_argc; +void **__program_argv; + +void __attribute__((noreturn)) __program_exit(int rc); +void __attribute__((noreturn)) (*__program_exit_callback)(int rc) = NULL; + +static void __program_parse_argc_argv(int argc, char *argdata); +static void __program_cleanup_argv(void); + +static void __program_init_heap(void) { + fake_heap_start = (char*)__heap_start__; + fake_heap_end = (char*)__heap_end__; +} + +static void __program_init_newlib_hooks(void) { + __syscalls.exit = __program_exit; /* For exit, etc. */ +} + +static void __program_move_additional_sections(void) { +#if defined(FUSEE_STAGE1_SRC) || defined(FUSEE_STAGE2_SRC) + extern uint8_t __chainloader_lma__[], __chainloader_start__[], __chainloader_bss_start__[], __chainloader_end__[]; + memcpy(__chainloader_start__, __chainloader_lma__, __chainloader_bss_start__ - __chainloader_start__); + memset(__chainloader_bss_start__, 0, __chainloader_end__ - __chainloader_bss_start__); +#endif +} + +void __program_init(int argc, char *argdata) { + /* Zero-fill the .bss section */ + memset(__bss_start__, 0, __bss_end__ - __bss_start__); + + __program_init_heap(); + __program_init_newlib_hooks(); + __program_parse_argc_argv(argc, argdata); + + /* Once argv is parsed, we can discard the low IRAM region */ + __program_move_additional_sections(); + __libc_init_array(); +} + +void __program_exit(int rc) { + __libc_fini_array(); + __program_cleanup_argv(); + if (__program_exit_callback == NULL) { + /* Default callback */ + generic_panic(); + } else { + __program_exit_callback(rc); + } + for (;;); +} + +#ifdef FUSEE_STAGE1_SRC +static void __program_parse_argc_argv(int argc, char *argdata) { + __program_argc = 0; + __program_argv = NULL; +} +#elif defined(FUSEE_STAGE2_SRC) +#include "stage2.h" +static void __program_parse_argc_argv(int argc, char *argdata) { + size_t pos = 0, len; + + __program_argc = argc; + + __program_argv = malloc(argc * sizeof(void **)); + if (__program_argv == NULL) { + generic_panic(); + } + + len = strlen(argdata); + __program_argv[0] = malloc(len + 1); + if (__program_argv[0] == NULL) { + generic_panic(); + } + strcpy((char *)__program_argv[0], argdata); + pos += len + 1; + + __program_argv[1] = malloc(len + 1); + if (__program_argv[1] == NULL) { + generic_panic(); + } + memcpy(__program_argv[1], argdata + pos, sizeof(stage2_args_t)); +} +#else +static void __program_parse_argc_argv(int argc, char *argdata) { + size_t pos = 0, len; + + __program_argc = argc; + + __program_argv = malloc(argc * sizeof(void **)); + if (__program_argv == NULL) { + generic_panic(); + } + + for (int i = 0; i < argc; i++) { + len = strlen(argdata + pos); + __program_argv[i] = malloc(len + 1); + if (__program_argv[i] == NULL) { + generic_panic(); + } + strcpy((char *)__program_argv[i], argdata + pos); + pos += len + 1; + } +} +#endif + +static void __program_cleanup_argv(void) { +#ifndef FUSEE_STAGE1_SRC + for (int i = 0; i < __program_argc; i++) { + free(__program_argv[i]); + __program_argv[i] = NULL; + } + free(__program_argv); +#endif +} diff --git a/fusee/fusee-primary/src/lib/fatfs/ffconf.h b/fusee/fusee-primary/src/lib/fatfs/ffconf.h index 381b2419e..d12bb8e2f 100644 --- a/fusee/fusee-primary/src/lib/fatfs/ffconf.h +++ b/fusee/fusee-primary/src/lib/fatfs/ffconf.h @@ -15,7 +15,7 @@ / and optional writing functions as well. */ -#define FF_FS_MINIMIZE 3 +#define FF_FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / / 0: Basic functions are fully enabled. diff --git a/fusee/fusee-primary/src/main.c b/fusee/fusee-primary/src/main.c index 565f5f085..53a3fea03 100644 --- a/fusee/fusee-primary/src/main.c +++ b/fusee/fusee-primary/src/main.c @@ -4,15 +4,13 @@ #include "se.h" #include "sd_utils.h" #include "stage2.h" +#include "chainloader.h" #include "sdmmc.h" #include "lib/fatfs/ff.h" #include "lib/printk.h" #include "display/video_fb.h" -/* TODO: Should we allow more than 32K for the BCT0? */ -#define BCT0_LOAD_ADDRESS (uintptr_t)(0x40038000) -#define BCT0_LOAD_END_ADDRESS (uintptr_t)(0x4003F000) -#define MAGIC_BCT0 0x30544342 +static char g_bct0_buffer[BCTO_MAX_SIZE]; #define DEFAULT_BCT0_FOR_DEBUG \ "BCT0\n"\ @@ -22,21 +20,21 @@ "stage2_entrypoint = 0xFFF00000\n" const char *load_config(void) { - if (!read_sd_file((void *)BCT0_LOAD_ADDRESS, BCT0_LOAD_END_ADDRESS - BCT0_LOAD_ADDRESS, "BCT.ini")) { + if (!read_sd_file(g_bct0_buffer, BCTO_MAX_SIZE, "BCT.ini")) { printk("Failed to read BCT0 from SD!\n"); printk("[DEBUG] Using default BCT0!\n"); - memcpy((void *)BCT0_LOAD_ADDRESS, DEFAULT_BCT0_FOR_DEBUG, sizeof(DEFAULT_BCT0_FOR_DEBUG)); + memcpy(g_bct0_buffer, DEFAULT_BCT0_FOR_DEBUG, sizeof(DEFAULT_BCT0_FOR_DEBUG)); /* TODO: Stop using default. */ /* printk("Error: Failed to load BCT.ini!\n"); * generic_panic(); */ } - if ((*((u32 *)(BCT0_LOAD_ADDRESS))) != MAGIC_BCT0) { + if (memcmp(g_bct0_buffer, "BCT0", 4) != 0) { printk("Error: Unexpected magic in BCT.ini!\n"); generic_panic(); } /* Return pointer to first line of the ini. */ - const char *bct0 = (const char *)BCT0_LOAD_ADDRESS; + const char *bct0 = g_bct0_buffer; while (*bct0 && *bct0 != '\n') { bct0++; } @@ -60,12 +58,10 @@ void load_sbk(void) { } int main(void) { - stage2_entrypoint_t stage2_entrypoint; - void **stage2_argv = (void **)(BCT0_LOAD_END_ADDRESS); const char *bct0; u32 *lfb_base; - char buf[0x400]; - memset(buf, 0xCC, 0x400); + const char *stage2_path; + stage2_args_t stage2_args = {0}; /* Initialize DRAM. */ /* TODO: What can be stripped out to make this minimal? */ @@ -75,7 +71,7 @@ int main(void) { display_init(); /* Register the display as a printk provider. */ - lfb_base = display_init_framebuffer(); + lfb_base = display_init_framebuffer((void *)0xC0000000); video_init(lfb_base); /* Turn on the backlight after initializing the lfb */ @@ -102,23 +98,21 @@ int main(void) { bct0 = load_config(); /* Load the loader payload into DRAM. */ - stage2_entrypoint = load_stage2(bct0); + load_stage2(bct0); - /* Setup argv. */ - memset(stage2_argv, 0, STAGE2_ARGC * sizeof(*stage2_argv)); - stage2_argv[STAGE2_ARGV_PROGRAM_PATH] = (void *)stage2_get_program_path(); - stage2_argv[STAGE2_ARGV_ARGUMENT_STRUCT] = &stage2_argv[STAGE2_ARGC]; - stage2_args_t *args = (stage2_args_t *)stage2_argv[STAGE2_ARGV_ARGUMENT_STRUCT]; - - /* Setup arguments struct. */ - args->version = 0; - args->bct0 = bct0; - args->lfb = (uint32_t *)lfb_base; - args->console_col = video_get_col(); - args->console_row = video_get_row(); f_unmount(""); + display_enable_backlight(false); + display_end(); + + /* Setup argument data. */ + stage2_path = stage2_get_program_path(); + stage2_args.version = 0; + strcpy(stage2_args.bct0, bct0); + strcpy(g_chainloader_arg_data, stage2_path); + memcpy(g_chainloader_arg_data + strlen(stage2_path) + 1, &stage2_args, sizeof(stage2_args_t)); + /* Jump to Stage 2. */ - stage2_entrypoint(STAGE2_ARGC, stage2_argv); + relocate_and_chainload(STAGE2_ARGC); return 0; } diff --git a/fusee/fusee-primary/src/stage2.c b/fusee/fusee-primary/src/stage2.c index 557953c27..f58bd3592 100644 --- a/fusee/fusee-primary/src/stage2.c +++ b/fusee/fusee-primary/src/stage2.c @@ -1,12 +1,15 @@ #include "utils.h" #include +#include "display/video_fb.h" #include "sd_utils.h" #include "stage2.h" +#include "chainloader.h" #include "lib/printk.h" #include "lib/vsprintf.h" #include "lib/ini.h" +#include "lib/fatfs/ff.h" -char g_stage2_path[0x300] = {0}; +char g_stage2_path[0x100] = {0}; const char *stage2_get_program_path(void) { return g_stage2_path; @@ -22,13 +25,13 @@ static int stage2_ini_handler(void *user, const char *section, const char *name, /* Read in load address as a hex string. */ sscanf(value, "%x", &x); config->load_address = x; - if (config->entrypoint == NULL) { - config->entrypoint = (stage2_entrypoint_t)config->load_address; + if (config->entrypoint == 0) { + config->entrypoint = config->load_address; } } else if (strcmp(name, STAGE2_ENTRYPOINT_KEY) == 0) { /* Read in entrypoint as a hex string. */ sscanf(value, "%x", &x); - config->entrypoint = (stage2_entrypoint_t)x; + config->entrypoint = x; } else { return 0; } @@ -38,8 +41,11 @@ static int stage2_ini_handler(void *user, const char *section, const char *name, return 1; } -stage2_entrypoint_t load_stage2(const char *bct0) { +void load_stage2(const char *bct0) { stage2_config_t config = {0}; + FILINFO info; + size_t size; + uintptr_t tmp_addr; if (ini_parse_string(bct0, stage2_ini_handler, &config) < 0) { printk("Error: Failed to parse BCT.ini!\n"); @@ -51,17 +57,65 @@ stage2_entrypoint_t load_stage2(const char *bct0) { generic_panic(); } + if (strlen(config.path) + 1 + sizeof(stage2_args_t) > CHAINLOADER_ARG_DATA_MAX_SIZE) { + printk("Error: Stage2's path name is too big!\n"); + } + + if (!check_32bit_address_loadable(config.entrypoint)) { + printk("Error: Stage2's entrypoint is invalid!\n"); + generic_panic(); + } + + if (!check_32bit_address_loadable(config.load_address)) { + printk("Error: Stage2's load address is invalid!\n"); + generic_panic(); + } + printk("[DEBUG] Stage 2 Config:\n"); printk(" File Path: %s\n", config.path); printk(" Load Address: 0x%08x\n", config.load_address); printk(" Entrypoint: 0x%p\n", config.entrypoint); - if (!read_sd_file((void *)config.load_address, 0x100000, config.path)) { + if (f_stat(config.path, &info) != FR_OK) { + printk("Error: Failed to stat stage2 (%s)!\n", config.path); + generic_panic(); + } + + size = (size_t)info.fsize; + + /* the LFB is located at 0xC0000000 atm */ + if (size > 0xC0000000u - 0x80000000u) { + printk("Error: Stage2 is way too big!\n"); + generic_panic(); + } + + if (!check_32bit_address_range_loadable(config.load_address, size)) { + printk("Error: Stage2 has an invalid load address & size combination!\n"); + generic_panic(); + } + + if (config.entrypoint < config.load_address || config.entrypoint >= config.load_address + size) { + printk("Error: Stage2's entrypoint is outside Stage2!\n"); + generic_panic(); + } + + if (check_32bit_address_range_in_program(config.load_address, size)) { + tmp_addr = 0x80000000u; + } else { + tmp_addr = config.load_address; + } + + if (read_sd_file((void *)tmp_addr, size, config.path) != size) { printk("Error: Failed to read stage2 (%s)!\n", config.path); generic_panic(); } - strncpy(g_stage2_path, config.path, sizeof(g_stage2_path)); + g_chainloader_num_entries = 1; + g_chainloader_entries[0].load_address = config.load_address; + g_chainloader_entries[0].src_address = tmp_addr; + g_chainloader_entries[0].size = size; + g_chainloader_entries[0].num = 0; + g_chainloader_entrypoint = config.entrypoint; - return config.entrypoint; + strncpy(g_stage2_path, config.path, sizeof(g_stage2_path)); } diff --git a/fusee/fusee-primary/src/stage2.h b/fusee/fusee-primary/src/stage2.h index 8bb5c6119..f5fc22b34 100644 --- a/fusee/fusee-primary/src/stage2.h +++ b/fusee/fusee-primary/src/stage2.h @@ -11,24 +11,23 @@ #define STAGE2_NAME_KEY "stage2_path" #define STAGE2_ADDRESS_KEY "stage2_addr" #define STAGE2_ENTRYPOINT_KEY "stage2_entrypoint" - -typedef void (*stage2_entrypoint_t)(int argc, void **argv); +#define BCTO_MAX_SIZE 0x6000 typedef struct { - char path[0x300]; + char path[0x100]; uintptr_t load_address; - stage2_entrypoint_t entrypoint; + uintptr_t entrypoint; } stage2_config_t; typedef struct { uint32_t version; - const char *bct0; uint32_t *lfb; uint32_t console_row; uint32_t console_col; + char bct0[BCTO_MAX_SIZE]; } stage2_args_t; const char *stage2_get_program_path(void); -stage2_entrypoint_t load_stage2(const char *bct0); +void load_stage2(const char *bct0); #endif diff --git a/fusee/fusee-primary/src/start.s b/fusee/fusee-primary/src/start.s index 961542f45..2b4fd7694 100644 --- a/fusee/fusee-primary/src/start.s +++ b/fusee/fusee-primary/src/start.s @@ -2,30 +2,27 @@ mov r\@, #0 .endm -.section .text.start +.section .text.start, "ax", %progbits .arm .align 5 .global _start +.type _start, %function _start: - /* Insert NOPs for convenience (i.e. to use Nintendo's BCTs, for example) */ - .rept 16 - nop - .endr - /* Switch to supervisor mode, mask all interrupts, clear all flags */ + /* Switch to system mode, mask all interrupts, clear all flags */ msr cpsr_cxsf, #0xDF /* Relocate ourselves if necessary */ - ldr r0, =__start__ - adr r1, _start - cmp r0, r1 + ldr r2, =__start__ + adr r3, _start + cmp r2, r3 bne _relocation_loop_end - ldr r2, =__bss_start__ - sub r2, r2, r0 /* size >= 32, obviously */ + ldr r4, =__bss_start__ + sub r4, r4, r2 /* size >= 32, obviously, and we've declared 32-byte-alignment */ _relocation_loop: - ldmia r1!, {r3-r10} - stmia r0!, {r3-r10} - subs r2, #0x20 + ldmia r3!, {r5-r12} + stmia r2!, {r5-r12} + subs r4, #0x20 bne _relocation_loop ldr r12, =_relocation_loop_end @@ -33,22 +30,25 @@ _start: _relocation_loop_end: /* Set the stack pointer */ - ldr sp, =0x40008000 - mov fp, #0 + ldr sp, =__stack_top__ + mov fp, #0 + bl __program_init - /* Clear .bss */ - ldr r0, =__bss_start__ - mov r1, #0 - ldr r2, =__bss_end__ - sub r2, r2, r0 - bl memset - - /* Call global constructors */ - bl __libc_init_array - - /* Set r0 to r12 to 0 (because why not?) & call main */ + /* Set r0 to r12 to 0 (for debugging) & call main */ .rept 13 CLEAR_GPR_REG_ITER .endr - bl main - b . + ldr r0, =__program_argc + ldr r1, =__program_argv + ldr lr, =__program_exit + b main + +/* No need to include this in normal programs: */ +.section .chainloader.text.start, "ax", %progbits +.arm +.align 5 +.global relocate_and_chainload +.type relocate_and_chainload, %function +relocate_and_chainload: + ldr sp, =__stack_top__ + b relocate_and_chainload_main diff --git a/fusee/fusee-primary/src/utils.h b/fusee/fusee-primary/src/utils.h index 43f2634ba..3d60797b9 100644 --- a/fusee/fusee-primary/src/utils.h +++ b/fusee/fusee-primary/src/utils.h @@ -71,9 +71,38 @@ static inline bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { return __builtin_add_overflow_p(a, b, (uint32_t)0); } +static inline bool check_32bit_address_loadable(uintptr_t addr) { + /* FWIW the bootROM forbids loading anything between 0x40000000 and 0x40010000, using it for itself... */ + return (addr >= 0x40010000u && addr < 0x40040000u) || addr >= 0x80000000u; +} + +static inline bool check_32bit_address_range_loadable(uintptr_t addr, size_t size) { + return + __builtin_add_overflow_p(addr, size, (uintptr_t)0) && /* the range doesn't overflow */ + check_32bit_address_loadable(addr) && check_32bit_address_loadable(addr + size) && /* bounds are valid */ + !(addr >= 0x40010000u && addr + size >= 0x40040000u) /* the range doesn't cross MMIO */ + ; +} + +bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); +static inline bool overlaps_a(const void *as, const void *ae, const void *bs, const void *be) { + return overlaps((uint64_t)(uintptr_t)as, (uint64_t)(uintptr_t)ae, (uint64_t)(uintptr_t)bs, (uint64_t)(uintptr_t)be); +} + +static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t size) { + extern uint8_t __chainloader_start__[], __chainloader_end__[]; + extern uint8_t __stack_bottom__[], __stack_top__[]; + extern uint8_t __start__[], __end__[]; + uint8_t *start = (uint8_t *)addr, *end = start + size; + + return overlaps_a(start, end, __chainloader_start__, __chainloader_end__) || + overlaps_a(start, end, __stack_bottom__, __stack_top__) || + overlaps_a(start, end, (void *)0xC0000000, (void *)0xC03C0000) || /* framebuffer */ + overlaps_a(start, end, __start__, __end__); +} + void panic(uint32_t code); void generic_panic(void); void panic_predefined(uint32_t which); -bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); #endif diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index a511f127f..34bfea6d6 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -27,7 +27,8 @@ CFLAGS = \ -std=gnu11 \ -Werror \ -Wall \ - -fstrict-volatile-bitfields + -fstrict-volatile-bitfields \ + -DFUSEE_STAGE2_SRC LDFLAGS = -specs=linker.specs -g $(ARCH) diff --git a/fusee/fusee-secondary/linker.ld b/fusee/fusee-secondary/linker.ld index e9fc76e24..5252357c3 100644 --- a/fusee/fusee-secondary/linker.ld +++ b/fusee/fusee-secondary/linker.ld @@ -7,12 +7,17 @@ MEMORY { NULL : ORIGIN = 0x00000000, LENGTH = 0x1000 main : ORIGIN = 0xFFF00000, LENGTH = 0x00100000 - low_iram : ORIGIN = 0x40003000, LENGTH = 0x4000 + low_iram : ORIGIN = 0x40003000, LENGTH = 0x8000 } SECTIONS { - PROVIDE(__start__ = 0xFFF00000); + PROVIDE(__start__ = 0xF0000000); + PROVIDE(__stack_top__ = 0x40010000); + PROVIDE(__stack_bottom__ = 0x4000C000); + PROVIDE(__heap_start__ = 0xE0000000); + PROVIDE(__heap_end__ = 0xF0000000); + . = __start__; . = ALIGN(32); @@ -24,18 +29,26 @@ SECTIONS . = ALIGN(4); } >main - .chainloader : + .chainloader_loadable : { . = ALIGN(32); - PROVIDE (__chainloader_start = .); + PROVIDE (__chainloader_start__ = .); + PROVIDE (__chainloader_lma__ = LOADADDR(.chainloader_loadable)); KEEP(*(.chainloader.text.start)) build/chainloader.o(.text*) build/chainloader.o(.rodata*) build/chainloader.o(.data*) . = ALIGN(8); + + } >low_iram AT>main + + .chainloader_bss : + { + . = ALIGN(8); + PROVIDE (__chainloader_bss_start__ = .); build/chainloader.o(.bss* COMMON) . = ALIGN(8); - PROVIDE (__chainloader_end = .); + PROVIDE (__chainloader_end__ = .); } >low_iram AT>main .text : diff --git a/fusee/fusee-secondary/src/chainloader.c b/fusee/fusee-secondary/src/chainloader.c index 6b13a1607..4caa70792 100644 --- a/fusee/fusee-secondary/src/chainloader.c +++ b/fusee/fusee-secondary/src/chainloader.c @@ -1,12 +1,34 @@ #include "chainloader.h" -uint8_t __attribute__((used)) g_payload_arg_data[PAYLOAD_ARG_DATA_MAX_SIZE] = {0}; +char g_chainloader_arg_data[CHAINLOADER_ARG_DATA_MAX_SIZE] = {0}; +chainloader_entry_t g_chainloader_entries[CHAINLOADER_MAX_ENTRIES] = {0}; /* keep them sorted */ +size_t g_chainloader_num_entries = 0; +uintptr_t g_chainloader_entrypoint = 0; #pragma GCC optimize (3) -void relocate_and_chainload_main(uintptr_t load_address, uintptr_t src_address, size_t size, int argc) { - for(size_t i = 0; i < size; i++) { - *(uint8_t *)(load_address + i) = *(uint8_t *)(src_address + i); + +static void *xmemmove(void *dst, const void *src, size_t len) +{ + const uint8_t *src8 = (const uint8_t *)src; + uint8_t *dst8 = (uint8_t *)dst; + + if (dst8 < src8) { + for (size_t i = 0; i < len; i++) { + dst8[i] = src8[i]; + } + } else if (src8 > dst8) { + for (size_t i = len; len > 0; len--) + dst8[i - 1] = src8[i - 1]; } - ((void (*)(int, void *))load_address)(argc, g_payload_arg_data); + return dst; +} + +void relocate_and_chainload_main(int argc) { + for(size_t i = 0; i < g_chainloader_num_entries; i++) { + chainloader_entry_t *entry = &g_chainloader_entries[i]; + xmemmove((void *)entry->load_address, (const void *)entry->src_address, entry->size); + } + + ((void (*)(int, void *))g_chainloader_entrypoint)(argc, g_chainloader_arg_data); } diff --git a/fusee/fusee-secondary/src/chainloader.h b/fusee/fusee-secondary/src/chainloader.h index 19010d284..9d9e44347 100644 --- a/fusee/fusee-secondary/src/chainloader.h +++ b/fusee/fusee-secondary/src/chainloader.h @@ -4,10 +4,22 @@ #include #include -#define PAYLOAD_ARG_DATA_MAX_SIZE 0x1000 +#define CHAINLOADER_ARG_DATA_MAX_SIZE 0x6200 +#define CHAINLOADER_MAX_ENTRIES 128 -extern uint8_t g_payload_arg_data[PAYLOAD_ARG_DATA_MAX_SIZE]; +typedef struct chainloader_entry_t { + uintptr_t load_address; + uintptr_t src_address; + size_t size; + size_t num; +} chainloader_entry_t; -void relocate_and_chainload(uintptr_t load_address, uintptr_t src_address, size_t size, int argc); +extern chainloader_entry_t g_chainloader_entries[CHAINLOADER_MAX_ENTRIES]; /* keep them sorted */ +extern size_t g_chainloader_num_entries; +extern uintptr_t g_chainloader_entrypoint; + +extern char g_chainloader_arg_data[CHAINLOADER_ARG_DATA_MAX_SIZE]; + +void relocate_and_chainload(int argc); #endif diff --git a/fusee/fusee-secondary/src/hwinit.h b/fusee/fusee-secondary/src/hwinit.h index ce0e50663..2e7994bca 100644 --- a/fusee/fusee-secondary/src/hwinit.h +++ b/fusee/fusee-secondary/src/hwinit.h @@ -28,7 +28,7 @@ void clock_enable_fuse(u32 enable); void display_color_screen(u32 color); /*! Init display in full 1280x720 resolution (32bpp, line stride 768, framebuffer size = 1280*768*4 bytes). */ -u32 *display_init_framebuffer(); +u32 *display_init_framebuffer(void *address); /*! Enable or disable the backlight. Should only be called when the screen is completely set up, to avoid flickering. */ void display_enable_backlight(bool on); diff --git a/fusee/fusee-secondary/src/hwinit/di.c b/fusee/fusee-secondary/src/hwinit/di.c index 3b117c67a..5cb8d8b88 100644 --- a/fusee/fusee-secondary/src/hwinit/di.c +++ b/fusee/fusee-secondary/src/hwinit/di.c @@ -188,9 +188,9 @@ void display_enable_backlight(bool on) { } -u32 *display_init_framebuffer(void) +u32 *display_init_framebuffer(void *address) { - u32 *lfb_addr = (u32 *)0xC0000000; + u32 *lfb_addr = (u32 *)address; //This configures the framebuffer @ 0xC0000000 with a resolution of 1280x720 (line stride 768). exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer, 32); diff --git a/fusee/fusee-secondary/src/hwinit/di.h b/fusee/fusee-secondary/src/hwinit/di.h index a1058823c..9d482e072 100644 --- a/fusee/fusee-secondary/src/hwinit/di.h +++ b/fusee/fusee-secondary/src/hwinit/di.h @@ -52,7 +52,7 @@ void display_end(); void display_color_screen(u32 color); /*! Init display in full 1280x720 resolution (32bpp, line stride 768, framebuffer size = 1280*768*4 bytes). */ -u32 *display_init_framebuffer(void); +u32 *display_init_framebuffer(void *address); /*! Enable or disable the backlight. Should only be called when the screen is completely set up, to avoid flickering. */ void display_enable_backlight(bool on); diff --git a/fusee/fusee-secondary/src/init.c b/fusee/fusee-secondary/src/init.c new file mode 100644 index 000000000..a0221aba0 --- /dev/null +++ b/fusee/fusee-secondary/src/init.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include "utils.h" + +void __libc_init_array(void); +void __libc_fini_array(void); + +extern uint8_t __bss_start__[], __bss_end__[]; +extern uint8_t __heap_start__[], __heap_end__[]; + +extern char *fake_heap_start; +extern char *fake_heap_end; + +int __program_argc; +void **__program_argv; + +void __attribute__((noreturn)) __program_exit(int rc); +void __attribute__((noreturn)) (*__program_exit_callback)(int rc) = NULL; + +static void __program_parse_argc_argv(int argc, char *argdata); +static void __program_cleanup_argv(void); + +static void __program_init_heap(void) { + fake_heap_start = (char*)__heap_start__; + fake_heap_end = (char*)__heap_end__; +} + +static void __program_init_newlib_hooks(void) { + __syscalls.exit = __program_exit; /* For exit, etc. */ +} + +static void __program_move_additional_sections(void) { +#if defined(FUSEE_STAGE1_SRC) || defined(FUSEE_STAGE2_SRC) + extern uint8_t __chainloader_lma__[], __chainloader_start__[], __chainloader_bss_start__[], __chainloader_end__[]; + memcpy(__chainloader_start__, __chainloader_lma__, __chainloader_bss_start__ - __chainloader_start__); + memset(__chainloader_bss_start__, 0, __chainloader_end__ - __chainloader_bss_start__); +#endif +} + +void __program_init(int argc, char *argdata) { + /* Zero-fill the .bss section */ + memset(__bss_start__, 0, __bss_end__ - __bss_start__); + + __program_init_heap(); + __program_init_newlib_hooks(); + __program_parse_argc_argv(argc, argdata); + + /* Once argv is parsed, we can discard the low IRAM region */ + __program_move_additional_sections(); + __libc_init_array(); +} + +void __program_exit(int rc) { + __libc_fini_array(); + __program_cleanup_argv(); + if (__program_exit_callback == NULL) { + /* Default callback */ + generic_panic(); + } else { + __program_exit_callback(rc); + } + for (;;); +} + +#ifdef FUSEE_STAGE1_SRC +static void __program_parse_argc_argv(int argc, char *argdata) { + __program_argc = 0; + __program_argv = NULL; +} +#elif defined(FUSEE_STAGE2_SRC) +#include "stage2.h" +static void __program_parse_argc_argv(int argc, char *argdata) { + size_t pos = 0, len; + + __program_argc = argc; + + __program_argv = malloc(argc * sizeof(void **)); + if (__program_argv == NULL) { + generic_panic(); + } + + len = strlen(argdata); + __program_argv[0] = malloc(len + 1); + if (__program_argv[0] == NULL) { + generic_panic(); + } + strcpy((char *)__program_argv[0], argdata); + pos += len + 1; + + __program_argv[1] = malloc(len + 1); + if (__program_argv[1] == NULL) { + generic_panic(); + } + memcpy(__program_argv[1], argdata + pos, sizeof(stage2_args_t)); +} +#else +static void __program_parse_argc_argv(int argc, char *argdata) { + size_t pos = 0, len; + + __program_argc = argc; + + __program_argv = malloc(argc * sizeof(void **)); + if (__program_argv == NULL) { + generic_panic(); + } + + for (int i = 0; i < argc; i++) { + len = strlen(argdata + pos); + __program_argv[i] = malloc(len + 1); + if (__program_argv[i] == NULL) { + generic_panic(); + } + strcpy((char *)__program_argv[i], argdata + pos); + pos += len + 1; + } +} +#endif + +static void __program_cleanup_argv(void) { +#ifndef FUSEE_STAGE1_SRC + for (int i = 0; i < __program_argc; i++) { + free(__program_argv[i]); + __program_argv[i] = NULL; + } + free(__program_argv); +#endif +} diff --git a/fusee/fusee-secondary/src/loader.c b/fusee/fusee-secondary/src/loader.c index 42097d64b..72d7eaed3 100644 --- a/fusee/fusee-secondary/src/loader.c +++ b/fusee/fusee-secondary/src/loader.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "utils.h" #include "loader.h" #include "sd_utils.h" @@ -47,10 +50,12 @@ bool validate_load_address(uintptr_t load_addr) { void load_list_entry(const char *key) { load_file_t load_file_ctx = {0}; + struct stat st; + size_t size; + size_t entry_num; + chainloader_entry_t *entry; load_file_ctx.key = key; - printf("Loading %s\n", key); - if (ini_parse_string(get_loader_ctx()->bct0, loadlist_entry_ini_handler, &load_file_ctx) < 0) { printf("Error: Failed to parse BCT.ini!\n"); generic_panic(); @@ -61,20 +66,30 @@ void load_list_entry(const char *key) { generic_panic(); } - printf("Loading %s from %s to 0x%08x\n", key, load_file_ctx.path, load_file_ctx.load_address); - - if (!validate_load_address(load_file_ctx.load_address)) { - printf("Error: Load address 0x%08x is invalid!\n", load_file_ctx.load_address); + if (!check_32bit_address_loadable(load_file_ctx.load_address)) { + printf("Error: Load address 0x%08x is invalid (%s)!\n", load_file_ctx.load_address, key); generic_panic(); } - /* Read file off of SD. */ - load_file_ctx.load_size = read_sd_file((void *)load_file_ctx.load_address, LOADER_FILESIZE_MAX, load_file_ctx.path); - if (load_file_ctx.load_size == 0) { - printf("Error: Failed to read %s!\n", load_file_ctx.path); + if (stat(load_file_ctx.path, &st) == -1) { + printf("Error: Failed to stat file %s: %s!\n", load_file_ctx.path, strerror(errno)); generic_panic(); } + size = (size_t)st.st_size; + if (!check_32bit_address_range_loadable(load_file_ctx.load_address, size)) { + printf("Error: %s has an invalid load address & size combination!\n", key); + generic_panic(); + } + + entry_num = g_chainloader_num_entries++; + entry = &g_chainloader_entries[entry_num]; + entry->load_address = load_file_ctx.load_address; + entry->size = size; + entry->num = entry_num; + strcpy(get_loader_ctx()->file_paths[entry_num], load_file_ctx.path); + get_loader_ctx()->nb_files++; + /* Check for special keys. */ if (strcmp(key, LOADER_PACKAGE2_KEY) == 0) { get_loader_ctx()->package2_loadfile = load_file_ctx; @@ -136,7 +151,7 @@ static int loadlist_ini_handler(void *user, const char *section, const char *nam } else if (strcmp(name, LOADER_ENTRYPOINT_KEY) == 0) { /* Read in entrypoint as a hex string. */ sscanf(value, "%x", &x); - loader_ctx->chainload_entrypoint = (entrypoint_t)x; + loader_ctx->chainload_entrypoint = x; } else if (strcmp(name, LOADER_CUSTOMSPLASH_KEY) == 0) { strncpy(loader_ctx->custom_splash_path, value, sizeof(loader_ctx->custom_splash_path)); } else { @@ -148,8 +163,52 @@ static int loadlist_ini_handler(void *user, const char *section, const char *nam return 1; } +static int chainloader_entry_comparator(const void *a, const void *b) { + const chainloader_entry_t *e1 = (const chainloader_entry_t *)a; + const chainloader_entry_t *e2 = (const chainloader_entry_t *)b; + + return (int)(e1->load_address - e2->load_address); +} + +static uintptr_t find_carveout(chainloader_entry_t *entries, size_t num_entries, size_t requested) { + for(size_t i = 0; i < num_entries; i++) { + uintptr_t lbound = entries[i].load_address + entries[i].size; + if (check_32bit_address_range_loadable(lbound, requested)) { + if (i == num_entries - 1 || lbound + requested <= entries[i + 1].load_address) + return lbound; + } + } + + return 0; +} + void load_payload(const char *bct0) { loader_ctx_t *ctx = get_loader_ctx(); + bool can_load_in_place = true; + + extern uint8_t __chainloader_start__[], __chainloader_end__[]; + extern uint8_t __stack_bottom__[], __stack_top__[]; + extern uint8_t __heap_start__[], __heap_end__[]; + extern uint8_t __start__[], __end__[]; + + static chainloader_entry_t nonallocatable_entries[] = { + {.load_address = 0x00000000, .size = 0x40010000}, /* Unknown/Invalid + Low IRAM */ + {.load_address = 0x40040000, .size = 0x80000000 - 0x40040000}, /* Invalid/MMIO */ + {.load_address = (uintptr_t)__chainloader_start__, .size = 0}, + {.load_address = (uintptr_t)__stack_bottom__, .size = 0}, + {.load_address = (uintptr_t)__heap_start__, .size = 0}, + {.load_address = (uintptr_t)__start__, .size = 0}, + {.load_address = 0, .size = 0}, /* Min to max loaded address */ + }; + static const size_t num_nonallocatable_entries = sizeof(nonallocatable_entries) / sizeof(nonallocatable_entries[0]); + if (nonallocatable_entries[2].size == 0) { + /* Initializers aren't computable at load time */ + nonallocatable_entries[2].size = __chainloader_end__ - __chainloader_start__; + nonallocatable_entries[3].size = __stack_top__ - __stack_bottom__; + nonallocatable_entries[4].size = __heap_end__ - __heap_start__; + nonallocatable_entries[5].size = __end__ - __start__; + } + /* Set BCT0 global. */ ctx->bct0 = bct0; @@ -158,4 +217,97 @@ void load_payload(const char *bct0) { printf("Error: Failed to parse BCT.ini!\n"); generic_panic(); } + + /* Sort the entries by ascending load addresses */ + qsort(g_chainloader_entries, g_chainloader_num_entries, sizeof(chainloader_entry_t), chainloader_entry_comparator); + + /* Check for possible overlap and find the entrypoint, also determine whether we can load in-place */ + ctx->file_id_of_entrypoint = ctx->nb_files; + for (size_t i = 0; i < ctx->nb_files; i++) { + if (i != ctx->nb_files - 1 && + overlaps( + g_chainloader_entries[i].load_address, + g_chainloader_entries[i].load_address + g_chainloader_entries[i].size, + g_chainloader_entries[i + 1].load_address, + g_chainloader_entries[i + 1].load_address + g_chainloader_entries[i + 1].size + ) + ) { + printf( + "Error: Loading ranges for files %s and %s overlap!\n", + ctx->file_paths[g_chainloader_entries[i].num], + ctx->file_paths[g_chainloader_entries[i + 1].num] + ); + generic_panic(); + } + + if(ctx->chainload_entrypoint >= g_chainloader_entries[i].load_address && + ctx->chainload_entrypoint < g_chainloader_entries[i].load_address + g_chainloader_entries[i].size) { + ctx->file_id_of_entrypoint = g_chainloader_entries[i].num; + } + + can_load_in_place = can_load_in_place && + check_32bit_address_range_in_program( + g_chainloader_entries[i].load_address, + g_chainloader_entries[i].load_address + g_chainloader_entries[i].size + ); + } + + if (ctx->chainload_entrypoint != 0 && ctx->file_id_of_entrypoint >= ctx->nb_files) { + printf("Error: Entrypoint not in range of any of the files!\n"); + generic_panic(); + } + + if (ctx->chainload_entrypoint == 0 && !can_load_in_place) { + printf("Error: Files have to be loaded in place when booting Horizon!\n"); + generic_panic(); + } + + if (can_load_in_place) { + for (size_t i = 0; i < ctx->nb_files; i++) { + chainloader_entry_t *entry = &g_chainloader_entries[i]; + entry->src_address = entry->load_address; + if (read_sd_file((void *)entry->src_address, entry->size, ctx->file_paths[entry->num]) != entry->size) { + printf("Error: Failed to read file %s: %s!\n", ctx->file_paths[entry->num], strerror(errno)); + generic_panic(); + } + } + } else { + size_t total_size = 0; + uintptr_t carveout, min_addr, max_addr, pos; + + for (size_t i = 0; i < ctx->nb_files; i++) { + total_size += g_chainloader_entries[i].size; + } + + min_addr = g_chainloader_entries[g_chainloader_num_entries - 1].load_address; + max_addr = g_chainloader_entries[g_chainloader_num_entries - 1].load_address + + g_chainloader_entries[g_chainloader_num_entries - 1].size; + + nonallocatable_entries[num_nonallocatable_entries - 1].load_address = min_addr; + nonallocatable_entries[num_nonallocatable_entries - 1].size = max_addr - min_addr; + + /* We need to find a carveout, first outside the loaded range, then inside */ + qsort(nonallocatable_entries, num_nonallocatable_entries, sizeof(chainloader_entry_t), chainloader_entry_comparator); + + carveout = find_carveout(nonallocatable_entries, num_nonallocatable_entries, total_size); + if (carveout == 0) { + carveout = find_carveout(g_chainloader_entries, g_chainloader_num_entries, total_size); + } + if (carveout == 0) { + printf("Error: Failed to find a carveout!\n"); + generic_panic(); + } + + /* Finally, read the files into the carveout */ + pos = carveout; + for (size_t i = 0; i < ctx->nb_files; i++) { + chainloader_entry_t *entry = &g_chainloader_entries[i]; + entry->src_address = pos; + if (read_sd_file((void *)entry->src_address, entry->size, ctx->file_paths[entry->num]) != entry->size) { + printf("Error: Failed to read file %s: %s!\n", ctx->file_paths[entry->num], strerror(errno)); + generic_panic(); + } + pos += entry->size; + } + } } diff --git a/fusee/fusee-secondary/src/loader.h b/fusee/fusee-secondary/src/loader.h index 7e7a48cfa..b332ba159 100644 --- a/fusee/fusee-secondary/src/loader.h +++ b/fusee/fusee-secondary/src/loader.h @@ -2,11 +2,10 @@ #define FUSEE_LOADER_H #include "utils.h" - -typedef void (*entrypoint_t)(int argc, void **argv); +#include "chainloader.h" typedef struct { - char path[0x300]; + char path[0x100]; const char *key; uintptr_t load_address; size_t load_size; @@ -14,12 +13,15 @@ typedef struct { typedef struct { const char *bct0; - entrypoint_t chainload_entrypoint; + uintptr_t chainload_entrypoint; + size_t file_id_of_entrypoint; + size_t nb_files; load_file_t package2_loadfile; load_file_t exosphere_loadfile; load_file_t tsecfw_loadfile; load_file_t warmboot_loadfile; - char custom_splash_path[0x300]; + char custom_splash_path[0x100]; + char file_paths[CHAINLOADER_MAX_ENTRIES][0x100]; } loader_ctx_t; #define LOADER_ENTRYPOINT_KEY "entrypoint" @@ -32,10 +34,10 @@ typedef struct { #define LOADER_WARMBOOT_KEY "warmboot" /* TODO: Should we allow files bigger than 16 MB? */ -#define LOADER_FILESIZE_MAX 0x01000000 +//#define LOADER_FILESIZE_MAX 0x01000000 loader_ctx_t *get_loader_ctx(void); void load_payload(const char *bct0); -#endif \ No newline at end of file +#endif diff --git a/fusee/fusee-secondary/src/main.c b/fusee/fusee-secondary/src/main.c index bdb3ce174..f05a66e30 100644 --- a/fusee/fusee-secondary/src/main.c +++ b/fusee/fusee-secondary/src/main.c @@ -1,40 +1,50 @@ #include #include #include +#include #include "utils.h" #include "hwinit.h" #include "loader.h" +#include "chainloader.h" #include "stage2.h" #include "nxboot.h" #include "console.h" #include "sd_utils.h" #include "fs_dev.h" +#include "display/video_fb.h" -/* TODO: Add a #define for this size, somewhere. Also, primary can only actually load 0x7000. */ -static char g_bct0[0x8000]; - +static stage2_args_t *g_stage2_args; /* Allow for main(int argc, void **argv) signature. */ #pragma GCC diagnostic ignored "-Wmain" -void __init_heap(void) { - extern char* fake_heap_start; - extern char* fake_heap_end; - - fake_heap_start = (char*)0xF0000000; - fake_heap_end = (char*)0xFFF00000; -} - int main(int argc, void **argv) { - stage2_args_t args = {0}; loader_ctx_t *loader_ctx = get_loader_ctx(); + void *framebuffer = memalign(0x1000, CONFIG_VIDEO_VISIBLE_ROWS * CONFIG_VIDEO_COLS * CONFIG_VIDEO_PIXEL_SIZE); - if (argc != STAGE2_ARGC || ((args = *((stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT])).version != 0)) { + /* Initialize the display. */ + display_init(); + + if (framebuffer == NULL) { generic_panic(); } - /* Setup console/stdout. */ - console_resume(args.lfb, args.console_row, args.console_col); + /* Initalize the framebuffer and console/stdout */ + display_init_framebuffer(framebuffer); + console_init(framebuffer); + + /* Turn on the backlight after initializing the lfb */ + /* to avoid flickering. */ + display_enable_backlight(true); + + if (argc != STAGE2_ARGC) { + generic_panic(); + } + g_stage2_args = (stage2_args_t *)argv[STAGE2_ARGV_ARGUMENT_STRUCT]; + + if(g_stage2_args->version != 0) { + generic_panic(); + } initialize_sd(); if(fsdev_mount_all() == -1) { @@ -42,29 +52,32 @@ int main(int argc, void **argv) { } fsdev_set_default_device("sdmc"); - /* Copy the BCT0 from unsafe primary memory into our memory. */ - strncpy(g_bct0, args.bct0, sizeof(g_bct0)); - /* TODO: What other hardware init should we do here? */ printf(u8"Welcome to Atmosphère Fusée Stage 2!\n"); printf("Stage 2 executing from: %s\n", (const char *)argv[STAGE2_ARGV_PROGRAM_PATH]); /* This will load all remaining binaries off of the SD. */ - load_payload(g_bct0); + load_payload(g_stage2_args->bct0); printf("Loaded payloads!\n"); /* Unmount everything (this causes all open files to be flushed and closed) */ fsdev_unmount_all(); - if (loader_ctx->chainload_entrypoint != NULL) { - /* TODO: What do we want to do in terms of argc/argv? */ - loader_ctx->chainload_entrypoint(0, NULL); + /* Deinitialize the framebuffer and display */ + display_enable_backlight(false); + display_end(); + free(framebuffer); + + if (loader_ctx->chainload_entrypoint != 0) { + /* TODO: What else do we want to do in terms of argc/argv? */ + const char *path = get_loader_ctx()->file_paths[get_loader_ctx()->file_id_of_entrypoint]; + strcpy(g_chainloader_arg_data, path); + relocate_and_chainload(1); } else { /* If we don't have a chainload entrypoint set, we're booting Horizon. */ nxboot_main(); } return 0; } - diff --git a/fusee/fusee-secondary/src/stage2.h b/fusee/fusee-secondary/src/stage2.h index 886311c7f..7ec36e9ba 100644 --- a/fusee/fusee-secondary/src/stage2.h +++ b/fusee/fusee-secondary/src/stage2.h @@ -8,12 +8,11 @@ #define STAGE2_ARGV_ARGUMENT_STRUCT 1 #define STAGE2_ARGC 2 +#define BCTO_MAX_SIZE 0x6000 + typedef struct { uint32_t version; - const char *bct0; - uint32_t *lfb; - uint32_t console_row; - uint32_t console_col; + char bct0[BCTO_MAX_SIZE]; } stage2_args_t; #endif diff --git a/fusee/fusee-secondary/src/start.s b/fusee/fusee-secondary/src/start.s index b90b9b12f..2b4fd7694 100644 --- a/fusee/fusee-secondary/src/start.s +++ b/fusee/fusee-secondary/src/start.s @@ -8,11 +8,7 @@ .global _start .type _start, %function _start: - /* Insert NOPs for convenience (i.e. to use Nintendo's BCTs, for example) */ - .rept 16 - nop - .endr - /* Switch to supervisor mode, mask all interrupts, clear all flags */ + /* Switch to system mode, mask all interrupts, clear all flags */ msr cpsr_cxsf, #0xDF /* Relocate ourselves if necessary */ @@ -22,7 +18,7 @@ _start: bne _relocation_loop_end ldr r4, =__bss_start__ - sub r4, r4, r2 /* size >= 32, obviously */ + sub r4, r4, r2 /* size >= 32, obviously, and we've declared 32-byte-alignment */ _relocation_loop: ldmia r3!, {r5-r12} stmia r2!, {r5-r12} @@ -34,36 +30,25 @@ _start: _relocation_loop_end: /* Set the stack pointer */ - ldr sp, =0x40010000 - mov fp, #0 - stmfd sp!, {r0, r1} + ldr sp, =__stack_top__ + mov fp, #0 + bl __program_init - /* Clear .bss */ - ldr r0, =__bss_start__ - mov r1, #0 - ldr r2, =__bss_end__ - sub r2, r2, r0 - bl memset - - /* Initialize the heap */ - bl __init_heap - - /* Call global constructors */ - bl __libc_init_array - - /* Set r0 to r12 to 0 (because why not?) & call main */ + /* Set r0 to r12 to 0 (for debugging) & call main */ .rept 13 CLEAR_GPR_REG_ITER .endr - ldmfd sp!, {r0, r1} - bl main - b . + ldr r0, =__program_argc + ldr r1, =__program_argv + ldr lr, =__program_exit + b main +/* No need to include this in normal programs: */ .section .chainloader.text.start, "ax", %progbits .arm .align 5 .global relocate_and_chainload .type relocate_and_chainload, %function relocate_and_chainload: - ldr sp, =0x40010000 + ldr sp, =__stack_top__ b relocate_and_chainload_main diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c index 417632ee0..e592d110d 100644 --- a/fusee/fusee-secondary/src/stratosphere.c +++ b/fusee/fusee-secondary/src/stratosphere.c @@ -41,7 +41,7 @@ ini1_header_t *stratosphere_get_ini1(void) { /* Merges some number of INI1s into a single INI1. It's assumed that the INIs are in order of preference. */ size_t stratosphere_merge_inis(void *dst, ini1_header_t **inis, unsigned int num_inis) { - char sd_path[0x300] = {0}; + char sd_path[0x100] = {0}; /* Validate all ini headers. */ for (unsigned int i = 0; i < num_inis; i++) { if (inis[i] == NULL || inis[i]->magic != MAGIC_INI1 || inis[i]->num_processes > INI1_MAX_KIPS) { diff --git a/fusee/fusee-secondary/src/utils.h b/fusee/fusee-secondary/src/utils.h index 43f2634ba..fca9039aa 100644 --- a/fusee/fusee-secondary/src/utils.h +++ b/fusee/fusee-secondary/src/utils.h @@ -71,9 +71,39 @@ static inline bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { return __builtin_add_overflow_p(a, b, (uint32_t)0); } +static inline bool check_32bit_address_loadable(uintptr_t addr) { + /* FWIW the bootROM forbids loading anything between 0x40000000 and 0x40010000, using it for itself... */ + return (addr >= 0x40010000u && addr < 0x40040000u) || addr >= 0x80000000u; +} + +static inline bool check_32bit_address_range_loadable(uintptr_t addr, size_t size) { + return + __builtin_add_overflow_p(addr, size, (uintptr_t)0) && /* the range doesn't overflow */ + check_32bit_address_loadable(addr) && check_32bit_address_loadable(addr + size) && /* bounds are valid */ + !(addr >= 0x40010000u && addr + size >= 0x40040000u) /* the range doesn't cross MMIO */ + ; +} + +bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); +static inline bool overlaps_a(const void *as, const void *ae, const void *bs, const void *be) { + return overlaps((uint64_t)(uintptr_t)as, (uint64_t)(uintptr_t)ae, (uint64_t)(uintptr_t)bs, (uint64_t)(uintptr_t)be); +} + +static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t size) { + extern uint8_t __chainloader_start__[], __chainloader_end__[]; + extern uint8_t __stack_bottom__[], __stack_top__[]; + extern uint8_t __heap_start__[], __heap_end__[]; + extern uint8_t __start__[], __end__[]; + uint8_t *start = (uint8_t *)addr, *end = start + size; + + return overlaps_a(start, end, __chainloader_start__, __chainloader_end__) || + overlaps_a(start, end, __stack_bottom__, __stack_top__) || + overlaps_a(start, end, __heap_start__, __heap_end__) || + overlaps_a(start, end, __start__, __end__); +} + void panic(uint32_t code); void generic_panic(void); void panic_predefined(uint32_t which); -bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); #endif