diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader index 056d11ade..af532a192 100644 --- a/core/SConscript.boardloader +++ b/core/SConscript.boardloader @@ -79,6 +79,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', ] if NEW_RENDERING: diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index b79bd06f5..b8f039bbe 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -118,6 +118,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/unit_variant.c', 'vendor/micropython/lib/uzlib/adler32.c', diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index d01be32d2..841f48b1d 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -111,6 +111,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index fb3e3ed2f..a98ae71d5 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -113,6 +113,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/unit_variant.c', 'vendor/micropython/lib/uzlib/adler32.c', @@ -149,7 +150,6 @@ SOURCE_BOOTLOADER = [ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/bootutils.c', 'embed/trezorhal/unix/common.c', - 'embed/trezorhal/unix/fault_handlers.c', 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', 'embed/trezorhal/unix/mpu.c', @@ -157,6 +157,7 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/secret.c', + 'embed/trezorhal/unix/system.c', 'embed/trezorhal/unix/systick.c', 'embed/trezorhal/unix/systimer.c', 'embed/trezorhal/unix/usb.c', diff --git a/core/SConscript.coreapp b/core/SConscript.coreapp index 2550ef94e..a65908aac 100644 --- a/core/SConscript.coreapp +++ b/core/SConscript.coreapp @@ -235,6 +235,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/translations.c', 'embed/lib/unit_variant.c', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 74a2e634c..c201d098b 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -234,6 +234,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/translations.c', 'embed/lib/unit_variant.c', diff --git a/core/SConscript.kernel b/core/SConscript.kernel index 2be27fc7f..3b15bac4d 100644 --- a/core/SConscript.kernel +++ b/core/SConscript.kernel @@ -222,6 +222,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/translations.c', 'embed/lib/unit_variant.c', @@ -246,7 +247,7 @@ CPPDEFINES_MOD += [ if TREZOR_MODEL not in ('1', ): CPPDEFINES_MOD += [ - # 'FANCY_FATAL_ERROR', + 'FANCY_FATAL_ERROR', ] CPPDEFINES_MOD += ['USE_SVC_SHUTDOWN'] @@ -367,7 +368,7 @@ env.Replace( LINKFLAGS='-T embed/kernel/memory_${TREZOR_MODEL}%s.ld -Wl,--gc-sections -Wl,--print-memory-usage -Wl,-Map=build/kernel/kernel.map -Wl,--warn-common' % LD_VARIANT, CPPPATH=ALLPATHS, CPPDEFINES=[ - 'FIRMWARE', + 'KERNEL', 'TREZOR_MODEL_'+TREZOR_MODEL, 'USE_HAL_DRIVER', 'ARM_USER_MODE', diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index 3ae2a8c82..2302c2658 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -115,6 +115,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/qr-code-generator/qrcodegen.c', 'embed/lib/terminal.c', 'vendor/micropython/lib/uzlib/adler32.c', diff --git a/core/SConscript.reflash b/core/SConscript.reflash index 481450e22..dc6dfc18c 100644 --- a/core/SConscript.reflash +++ b/core/SConscript.reflash @@ -90,6 +90,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_rgba8888.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', diff --git a/core/SConscript.unix b/core/SConscript.unix index 43cf49360..f2071181b 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -243,6 +243,7 @@ SOURCE_MOD += [ 'embed/lib/gfx_bitblt_mono8.c', 'embed/lib/image.c', 'embed/lib/mini_printf.c', + 'embed/lib/rsod.c', 'embed/lib/terminal.c', 'embed/lib/translations.c', 'embed/lib/unit_variant.c', @@ -428,6 +429,7 @@ SOURCE_UNIX = [ 'embed/trezorhal/unix/mpu.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', + 'embed/trezorhal/unix/system.c', 'embed/trezorhal/unix/systick.c', 'embed/trezorhal/unix/systimer.c', 'embed/trezorhal/unix/time_estimate.c', diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index d63cca322..17eb8d4b7 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -21,19 +21,20 @@ #include TREZOR_BOARD #include "board_capabilities.h" +#include "bootutils.h" #include "buffers.h" #include "common.h" #include "compiler_traits.h" #include "display.h" #include "display_draw.h" -#include "fault_handlers.h" #include "flash.h" #include "flash_utils.h" #include "image.h" #include "model.h" #include "mpu.h" #include "rng.h" -#include "systimer.h" +#include "rsod.h" +#include "system.h" #include "terminal.h" #ifdef USE_SD_CARD @@ -234,8 +235,7 @@ static secbool copy_sdcard(void) { #endif int main(void) { - systick_init(); - systimer_init(); + system_init(&rsod_panic_handler); reset_flags_reset(); @@ -261,8 +261,6 @@ int main(void) { clear_otg_hs_memory(); #endif - fault_handlers_init(); - #ifdef USE_SDRAM sdram_init(); #endif diff --git a/core/embed/boardloader/memory_stm32u58.ld b/core/embed/boardloader/memory_stm32u58.ld index b92fcf303..51aa0845c 100644 --- a/core/embed/boardloader/memory_stm32u58.ld +++ b/core/embed/boardloader/memory_stm32u58.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index f2bea94e7..c417d8312 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -25,7 +25,6 @@ #include "common.h" #include "display.h" #include "display_utils.h" -#include "fault_handlers.h" #include "flash.h" #include "flash_otp.h" #include "flash_utils.h" @@ -33,8 +32,10 @@ #include "lowlevel.h" #include "messages.pb.h" #include "random_delays.h" +#include "rsod.h" #include "secbool.h" #include "secret.h" +#include "system.h" #include "systimer.h" #ifdef USE_DMA2D @@ -356,8 +357,7 @@ int bootloader_main(void) { #endif secbool stay_in_bootloader = secfalse; - systick_init(); - systimer_init(); + system_init(&rsod_panic_handler); rdi_init(); @@ -396,8 +396,6 @@ int bootloader_main(void) { ui_screen_boot_stage_1(false); - fault_handlers_init(); - #ifdef TREZOR_EMULATOR // wait a bit so that the empty lock icon is visible // (on a real device, we are waiting for touch init which takes longer) diff --git a/core/embed/bootloader/memory_stm32u58.ld b/core/embed/bootloader/memory_stm32u58.ld index 0812d1925..6711c5c4b 100644 --- a/core/embed/bootloader/memory_stm32u58.ld +++ b/core/embed/bootloader/memory_stm32u58.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 93e52fe5e..b43e06a6c 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -20,6 +20,7 @@ #include #include +#include "bootutils.h" #include "common.h" #include "display.h" #include "display_draw.h" @@ -30,7 +31,9 @@ #include "mpu.h" #include "random_delays.h" #include "rng.h" +#include "rsod.h" #include "secbool.h" +#include "system.h" #ifdef USE_TOUCH #include "touch.h" #endif @@ -176,8 +179,7 @@ static secbool check_vendor_header_lock(const vendor_header *const vhdr) { } int main(void) { - systick_init(); - systimer_init(); + system_init(&rsod_panic_handler); rdi_init(); #ifdef USE_TOUCH diff --git a/core/embed/bootloader_ci/memory_stm32u58.ld b/core/embed/bootloader_ci/memory_stm32u58.ld index 876807cb8..69f257018 100644 --- a/core/embed/bootloader_ci/memory_stm32u58.ld +++ b/core/embed/bootloader_ci/memory_stm32u58.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/coreapp/main.c b/core/embed/coreapp/main.c index 8ea6f269b..6a5161172 100644 --- a/core/embed/coreapp/main.c +++ b/core/embed/coreapp/main.c @@ -37,16 +37,28 @@ #include "ports/stm32/pendsv.h" #include "error_handling.h" +#include "rsod.h" #include "rust_ui_common.h" #include "secbool.h" +#include "systask.h" +#include "system.h" #ifdef USE_SECP256K1_ZKP #include "zkp_context.h" #endif -int main(void) { +int main(uint32_t cmd, void *arg) { + if (cmd == 1) { + systask_postmortem_t *info = (systask_postmortem_t *)arg; + rsod_gui(info); + system_exit(0); + } + screen_boot_stage_2(); + // uint32_t *p = 0; + // *p = 0; + #ifdef USE_SECP256K1_ZKP ensure(sectrue * (zkp_context_init() == 0), NULL); #endif diff --git a/core/embed/coreapp/memory_T3T1.ld b/core/embed/coreapp/memory_T3T1.ld index 3fc9d3fae..810877697 100644 --- a/core/embed/coreapp/memory_T3T1.ld +++ b/core/embed/coreapp/memory_T3T1.ld @@ -16,15 +16,13 @@ MEMORY { main_stack_base = ORIGIN(SRAM2) + SIZEOF(.stack); /* 8-byte aligned full descending stack */ _sstack = ORIGIN(SRAM2); _estack = main_stack_base; +_stack_size = SIZEOF(.stack); /* used by the startup code to populate variables used by the C code */ data_lma = LOADADDR(.data); data_vma = ADDR(.data); data_size = SIZEOF(.data); -bss_start = ADDR(.bss); -bss_end = ADDR(.bss) + SIZEOF(.bss); - /* used by the startup code to populate variables used by the C code */ confidential_lma = LOADADDR(.confidential); confidential_vma = ADDR(.confidential); @@ -43,6 +41,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/coreapp/startup_stm32f4.S b/core/embed/coreapp/startup_stm32f4.S index f1a8a6784..d1a921abb 100644 --- a/core/embed/coreapp/startup_stm32f4.S +++ b/core/embed/coreapp/startup_stm32f4.S @@ -6,21 +6,17 @@ .type reset_handler, STT_FUNC reset_handler: - // setup environment for subsequent stage of code - ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM - ldr r1, =ccmram_end // r1 - point to byte after the end of CCMRAM - ldr r2, =0 // r2 - the word-sized value to be written - bl memset_reg + push {r0, r1} - ldr r0, =boot_args_start // r0 - point to beginning of BOOT_ARGS - ldr r1, =boot_args_end // r1 - point to byte after the end of BOOT_ARGS - ldr r2, =0 // r2 - the word-sized value to be written - bl memset_reg + // setup the stack protector with provided random value + ldr r0, = __stack_chk_guard + str r2, [r0] - ldr r0, =sram_start // r0 - point to beginning of SRAM - ldr r1, =sram_end // r1 - point to byte after the end of SRAM - ldr r2, =0 // r2 - the word-sized value to be written - bl memset_reg + ldr r0, =bss_start + ldr r1, =0 + ldr r2, =bss_end + sub r2, r2, r0 + bl memset // copy data in from flash ldr r0, =data_vma // dst addr @@ -28,15 +24,14 @@ reset_handler: ldr r2, =data_size // size in bytes bl memcpy - // setup the stack protector (see build script "-fstack-protector-all") with an unpredictable value - bl rng_get - ldr r1, = __stack_chk_guard - str r0, [r1] + pop {r0, r1} // enter the application code + // returns exit code in r0 bl main - b secure_shutdown - + // terminate the application + // pass exit code in r0 + b system_exit .end diff --git a/core/embed/coreapp/startup_stm32u5.S b/core/embed/coreapp/startup_stm32u5.S index 6d94c656d..cc26b7470 100644 --- a/core/embed/coreapp/startup_stm32u5.S +++ b/core/embed/coreapp/startup_stm32u5.S @@ -6,53 +6,38 @@ .type reset_handler, STT_FUNC reset_handler: - // set the stack protection - ldr r0, =_sstack - add r0, r0, #128 // safety margin for the exception frame - msr PSPLIM, r0 + push {r0, r1} + + // setup the stack protector with provided random value + ldr r0, = __stack_chk_guard + str r2, [r0] ldr r0, =bss_start - ldr r1, =bss_end - ldr r2, =0 - bl memset_reg + ldr r1, =0 + ldr r2, =bss_end + sub r2, r2, r0 + bl memset // copy data in from flash - ldr r0, =data_vma // dst addr - ldr r1, =data_lma // src addr - ldr r2, =data_size // size in bytes + ldr r0, =data_vma + ldr r1, =data_lma + ldr r2, =data_size bl memcpy // copy confidential data in from flash - ldr r0, =confidential_vma // dst addr - ldr r1, =confidential_lma // src addr - ldr r2, =confidential_size // size in bytes + ldr r0, =confidential_vma + ldr r1, =confidential_lma + ldr r2, =confidential_size bl memcpy - // setup the stack protector (see build script "-fstack-protector-all") with an unpredictable value - bl rng_get - ldr r1, = __stack_chk_guard - str r0, [r1] + pop {r0, r1} // enter the application code + // returns exit code in r0 bl main - b secure_shutdown - - -memset_reg: - // call with the following (note that the arguments are not validated prior to use): - // r0 - address of first word to write (inclusive) - // r1 - address of first word following the address in r0 to NOT write (exclusive) - // r2 - word value to be written - // both addresses in r0 and r1 needs to be divisible by 4! - cmp r0, r1 - beq .L_loop_end - .L_loop_begin: - str r2, [r0], 4 // store the word in r2 to the address in r0, post-indexed - cmp r0, r1 - bne .L_loop_begin - .L_loop_end: - bx lr - + // terminate the application + // pass exit code in r0 + b system_exit .end diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index 07ffb1f6e..4ac0b79cc 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -42,15 +42,16 @@ #include "compiler_traits.h" #include "display.h" #include "entropy.h" -#include "fault_handlers.h" #include "flash.h" #include "image.h" #include "memzero.h" #include "model.h" #include "mpu.h" #include "random_delays.h" +#include "rsod.h" #include "rust_ui.h" #include "secure_aes.h" +#include "system.h" #include "systimer.h" #include TREZOR_BOARD @@ -125,8 +126,7 @@ static void optiga_log_hex(const char *prefix, const uint8_t *data, #endif int main(void) { - systick_init(); - systimer_init(); + system_init(&rsod_gui); rdi_init(); @@ -183,8 +183,6 @@ int main(void) { // Init peripherals - fault_handlers_init(); - #if defined TREZOR_MODEL_T set_core_clock(CLOCK_180_MHZ); #endif diff --git a/core/embed/firmware/memory_T3T1.ld b/core/embed/firmware/memory_T3T1.ld index 4e3a2a88c..9bbf39047 100644 --- a/core/embed/firmware/memory_T3T1.ld +++ b/core/embed/firmware/memory_T3T1.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/kernel/main.c b/core/embed/kernel/main.c index 4b3f97e3b..7ad6f8ce1 100644 --- a/core/embed/kernel/main.c +++ b/core/embed/kernel/main.c @@ -19,15 +19,14 @@ #include STM32_HAL_H -#include "image.h" -#include "irq.h" -#include "syscall.h" +#include +#include "applet.h" #include "board_capabilities.h" +#include "bootutils.h" #include "display.h" #include "dma2d.h" #include "entropy.h" -#include "fault_handlers.h" #include "haptic.h" #include "image.h" #include "irq.h" @@ -36,11 +35,12 @@ #include "optiga_commands.h" #include "optiga_transport.h" #include "random_delays.h" +#include "rsod.h" #include "sdcard.h" #include "secret.h" #include "secure_aes.h" +#include "system.h" #include "systick.h" -#include "systimer.h" #include "tamper.h" #include "touch.h" #include "unit_variant.h" @@ -66,15 +66,6 @@ static void optiga_log_hex(const char *prefix, const uint8_t *data, #endif void drivers_init() { - syscall_init(); - - systick_init(); - systimer_init(); - - fault_handlers_init(); - - systick_delay_ms(10); - #if defined TREZOR_MODEL_T set_core_clock(CLOCK_180_MHZ); #endif @@ -175,14 +166,92 @@ void drivers_init() { #endif } +// Initializes coreapp applet +static void coreapp_init(applet_t *applet) { + applet_header_t *coreapp_header = + (applet_header_t *)(COREAPP_START + IMAGE_HEADER_SIZE + 0x0400); + + applet_layout_t coreapp_layout = { + 0 + /* .data1_start = COREAPP_RAM1_START, + .data1_size = COREAPP_RAM1_SIZE, + .data2_start = COREAPP_RAM2_START, + .data2_size = COREAPP_RAM2_SIZE,*/ + }; + + applet_init(applet, coreapp_header, &coreapp_layout); +} + +// Shows RSOD (Red Screen of Death) +static void show_rsod(const systask_postmortem_t *pminfo) { +#ifdef FANCY_FATAL_ERROR + applet_t coreapp; + coreapp_init(&coreapp); + + // Reset and run the coreapp in RSOD mode + if (applet_reset(&coreapp, 1, pminfo, sizeof(systask_postmortem_t))) { + systask_yield_to(&coreapp.task); + + if (coreapp.task.pminfo.reason == TASK_TERM_REASON_EXIT) { + // If the RSOD was shown successfully, proceed to shutdown + secure_shutdown(); + } + } +#endif + + // If coreapp crashed, fallback to showing the error using a terminal + rsod_terminal(pminfo); +} + +// Initializes system in emergency mode and shows RSOD +static void init_and_show_rsod(const systask_postmortem_t *pminfo) { + // Initialize the system's core services + // (If the kernel crashes in emergency mode, we are out of options + // and show the RSOD without attempting to re-enter emergency mode) + system_init(&rsod_terminal); + + // Initialize necessary drivers + display_init(DISPLAY_RESET_CONTENT); + + // Show RSOD + show_rsod(pminfo); + + // Wait for the user to manually power off the device + secure_shutdown(); +} + +// Kernel panic handler +// (may be called from interrupt context) +static void kernel_panic(const systask_postmortem_t *pminfo) { + // Since the system state is unreliable, enter emergency mode + // and show the RSOD. + system_emergency_rescue(&init_and_show_rsod, pminfo); + // The previous function call never returns +} + int main(void) { - mpu_init(); + // Initialize system's core services + system_init(&kernel_panic); // Initialize hardware drivers drivers_init(); - // Start unprivileged application - start_unprivileged_app(); + // Initialize coreapp task + applet_t coreapp; + coreapp_init(&coreapp); + + // Reset and run the coreapp + if (!applet_reset(&coreapp, 0, NULL, 0)) { + error_shutdown("Cannot start coreapp"); + } + + systask_yield_to(&coreapp.task); + + // Coreapp crashed, show RSOD + show_rsod(&coreapp.task.pminfo); + + // Wait for the user to manually power off the device + secure_shutdown(); return 0; } diff --git a/core/embed/kernel/memory_T3T1.ld b/core/embed/kernel/memory_T3T1.ld index f799bf492..07a90ccc5 100644 --- a/core/embed/kernel/memory_T3T1.ld +++ b/core/embed/kernel/memory_T3T1.ld @@ -21,6 +21,9 @@ _estack = main_stack_base; data_lma = LOADADDR(.data); data_vma = ADDR(.data); data_size = SIZEOF(.data); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); + /* used by the startup code to populate variables used by the C code */ confidential_lma = LOADADDR(.confidential); diff --git a/core/embed/lib/error_handling.c b/core/embed/lib/error_handling.c index 1f2231760..cfaab4837 100644 --- a/core/embed/lib/error_handling.c +++ b/core/embed/lib/error_handling.c @@ -18,25 +18,10 @@ */ #include -#ifdef TREZOR_EMULATOR -#include -#endif +#include -#include "bootutils.h" -#include "display.h" #include "error_handling.h" -#include "mini_printf.h" -#ifdef FANCY_FATAL_ERROR -#include "rust_ui.h" -#else -#include "terminal.h" -#endif - -#ifdef RGB16 -#define COLOR_FATAL_ERROR RGB16(0x7F, 0x00, 0x00) -#else -#define COLOR_FATAL_ERROR COLOR_BLACK -#endif +#include "system.h" uint32_t __stack_chk_guard = 0; @@ -48,26 +33,9 @@ void __attribute__((noreturn, used)) __stack_chk_fail(void) { void __attribute__((noreturn)) error_shutdown_ex(const char *title, const char *message, const char *footer) { - if (title == NULL) { - title = "INTERNAL ERROR"; - } - if (footer == NULL) { - footer = "PLEASE VISIT\nTREZOR.IO/RSOD"; - } - -#ifdef FANCY_FATAL_ERROR - error_shutdown_rust(title, message, footer); -#else - display_orientation(0); - term_set_color(COLOR_WHITE, COLOR_FATAL_ERROR); - term_printf("%s\n", title); - if (message) { - term_printf("%s\n", message); - } - term_printf("\n%s\n", footer); - display_backlight(255); - secure_shutdown(); -#endif + system_exit_error(title, message, footer); + while (1) + ; } void __attribute__((noreturn)) error_shutdown(const char *message) { @@ -76,39 +44,9 @@ void __attribute__((noreturn)) error_shutdown(const char *message) { void __attribute__((noreturn)) __fatal_error(const char *msg, const char *file, int line) { -#ifdef TREZOR_EMULATOR - fprintf(stderr, "FATAL ERROR: %s\n", msg); - if (file) { - fprintf(stderr, "file: %s:%d\n", file, line); - } - fflush(stderr); -#endif -#ifdef FANCY_FATAL_ERROR - if (msg == NULL) { - char buf[128] = {0}; - mini_snprintf(buf, sizeof(buf), "%s:%d", file, line); - msg = buf; - } - error_shutdown(msg); -#else - display_orientation(0); - term_set_color(COLOR_WHITE, COLOR_FATAL_ERROR); - term_printf("\nINTERNAL ERROR:\n"); - if (msg) { - term_printf("msg : %s\n", msg); - } - if (file) { - term_printf("file: %s:%d\n", file, line); - } -#ifdef SCM_REVISION - const uint8_t *rev = (const uint8_t *)SCM_REVISION; - term_printf("rev : %02x%02x%02x%02x%02x\n", rev[0], rev[1], rev[2], rev[3], - rev[4]); -#endif - term_printf("\nPlease contact Trezor support.\n"); - display_backlight(255); - secure_shutdown(); -#endif + system_exit_fatal(msg, file, line); + while (1) + ; } void __attribute__((noreturn)) show_wipe_code_screen(void) { diff --git a/core/embed/lib/rsod.c b/core/embed/lib/rsod.c new file mode 100644 index 000000000..7be3da488 --- /dev/null +++ b/core/embed/lib/rsod.c @@ -0,0 +1,177 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "rsod.h" +#include "bootutils.h" +#include "display.h" +#include "mini_printf.h" +#include "system.h" +#include "terminal.h" + +#define RSOD_DEFAULT_TITLE "INTERNAL ERROR"; +#define RSOD_DEFAULT_MESSAGE "UNSPECIFIED"; +#define RSOD_DEFAULT_FOOTER "PLEASE VISIT TREZOR.IO/RSOD"; +#define RSOD_EXIT_MESSAGE "EXIT %d" + +#ifdef KERNEL_MODE + +#define RSOD_FG_COLOR COLOR_WHITE + +#ifdef USE_RGB_COLORS +#define RSOD_BG_COLOR RGB16(0x7F, 0x00, 0x00) +#else +#define RSOD_BG_COLOR COLOR_BLACK +#endif + +void rsod_terminal(const systask_postmortem_t* pminfo) { + display_orientation(0); + term_set_color(RSOD_FG_COLOR, RSOD_BG_COLOR); + + const char* title = RSOD_DEFAULT_TITLE; + const char* message = RSOD_DEFAULT_MESSAGE; + const char* footer = RSOD_DEFAULT_FOOTER; + const char* file = NULL; + char message_buf[32] = {0}; + int line = 0; + + switch (pminfo->reason) { + case TASK_TERM_REASON_EXIT: + mini_snprintf(message_buf, sizeof(message_buf), RSOD_EXIT_MESSAGE, + pminfo->exit.code); + message = message_buf; + break; + case TASK_TERM_REASON_ERROR: + title = pminfo->error.title; + message = pminfo->error.message; + footer = pminfo->error.footer; + break; + case TASK_TERM_REASON_FATAL: + message = pminfo->fatal.expr; + file = pminfo->fatal.file; + line = pminfo->fatal.line; + break; + case TASK_TERM_REASON_FAULT: + message = system_fault_message(&pminfo->fault); + break; + } + + if (title != NULL) { + term_printf("%s\n", title); + } + + if (message != NULL) { + term_printf("msg : %s\n", message); + } + + if (file) { + term_printf("file: %s:%d\n", file, line); + } + +#ifdef SCM_REVISION + const uint8_t* rev = (const uint8_t*)SCM_REVISION; + term_printf("rev : %02x%02x%02x%02x%02x\n", rev[0], rev[1], rev[2], rev[3], + rev[4]); +#endif + + if (footer != NULL) { + term_printf("\n%s\n", footer); + } + + display_backlight(255); +} + +#endif // KERNEL_MODE + +#if (defined(FIRMWARE) || defined(BOOTLOADER)) && defined(FANCY_FATAL_ERROR) + +#include "rust_ui.h" + +void rsod_gui(const systask_postmortem_t* pminfo) { + const char* title = RSOD_DEFAULT_TITLE; + const char* message = RSOD_DEFAULT_MESSAGE; + const char* footer = RSOD_DEFAULT_FOOTER; + char message_buf[128] = {0}; + + switch (pminfo->reason) { + case TASK_TERM_REASON_EXIT: + mini_snprintf(message_buf, sizeof(message_buf), RSOD_EXIT_MESSAGE, + pminfo->exit.code); + message = message_buf; + break; + + case TASK_TERM_REASON_ERROR: + title = pminfo->error.title; + message = pminfo->error.message; + footer = pminfo->error.footer; + break; + + case TASK_TERM_REASON_FATAL: + message = pminfo->fatal.expr; + if (message[0] == '\0') { + mini_snprintf(message_buf, sizeof(message_buf), "%s:%u", + pminfo->fatal.file, (unsigned int)pminfo->fatal.line); + message = message_buf; + } + break; + + case TASK_TERM_REASON_FAULT: + message = system_fault_message(&pminfo->fault); + break; + } + + // Render the RSOD in Rust + display_rsod_rust(title, message, footer); +} + +#endif + +#ifdef KERNEL_MODE + +// Initializes system in emergency mode and shows RSOD +static void init_and_show_rsod(const systask_postmortem_t* pminfo) { + // Initialize the system's core services + // (If the kernel crashes in emergency mode, we are out of options + // and show the RSOD without attempting to re-enter emergency mode) + system_init(&rsod_terminal); + + // Initialize necessary drivers + display_init(DISPLAY_RESET_CONTENT); + +#if (defined(FIRMWARE) || defined(BOOTLOADER)) && defined(FANCY_FATAL_ERROR) + // Show the RSOD using Rust GUI + rsod_gui(pminfo); +#else + // Show the RSOD using terminal + rsod_terminal(pminfo); +#endif + + // Wait for the user to manually power off the device + secure_shutdown(); +} + +// Universal panic handler +// (may be called from interrupt context) +void rsod_panic_handler(const systask_postmortem_t* pminfo) { + // Since the system state is unreliable, enter emergency mode + // and show the RSOD. + system_emergency_rescue(&init_and_show_rsod, pminfo); + // The previous function call never returns +} + +#endif // KERNEL_MODE diff --git a/core/embed/trezorhal/unix/fault_handlers.c b/core/embed/lib/rsod.h similarity index 56% rename from core/embed/trezorhal/unix/fault_handlers.c rename to core/embed/lib/rsod.h index 1e7754849..86f302d86 100644 --- a/core/embed/trezorhal/unix/fault_handlers.c +++ b/core/embed/lib/rsod.h @@ -17,6 +17,24 @@ * along with this program. If not, see . */ -#include +#ifndef LIB_RSOD_H +#define LIB_RSOD_H -void fault_handlers_init(void) {} +#include "systask.h" + +// Shows RSOD (Red Screen of Death) using terminal. +void rsod_terminal(const systask_postmortem_t* pminfo); + +// Shows RSOD (Red Screen of Death) using Rust GUI. +void rsod_gui(const systask_postmortem_t* pminfo); + +#ifdef KERNEL_MODE + +// Universal panic handler that can be passed to `system_init()` function +// to show RSOD screen describing the system error. +// (may be called from interrupt context) +void rsod_panic_handler(const systask_postmortem_t* pminfo); + +#endif // KERNEL_MODE + +#endif // LIB_RSOD_H diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index fc56a4c32..3ff73d873 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -31,7 +31,6 @@ #include "display.h" #include "display_draw.h" #include "display_utils.h" -#include "fault_handlers.h" #include "flash.h" #include "flash_otp.h" #include "fwutils.h" @@ -40,9 +39,11 @@ #include "mpu.h" #include "prodtest_common.h" #include "random_delays.h" +#include "rsod.h" #include "sbu.h" #include "sdcard.h" #include "secbool.h" +#include "system.h" #include "systimer.h" #include "touch.h" #include "usb.h" @@ -783,9 +784,8 @@ void cpuid_read(void) { #define BACKLIGHT_NORMAL 150 int main(void) { - systick_init(); - systimer_init(); - rdi_init(); + system_init(&rsod_panic_handler); + display_init(DISPLAY_RETAIN_CONTENT); #ifdef STM32U5 @@ -820,8 +820,6 @@ int main(void) { pair_optiga(); #endif - fault_handlers_init(); - display_clear(); draw_welcome_screen(); diff --git a/core/embed/prodtest/memory_stm32u58.ld b/core/embed/prodtest/memory_stm32u58.ld index eaa108f73..53c521686 100644 --- a/core/embed/prodtest/memory_stm32u58.ld +++ b/core/embed/prodtest/memory_stm32u58.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/reflash/main.c b/core/embed/reflash/main.c index 114870872..0e7ef4558 100644 --- a/core/embed/reflash/main.c +++ b/core/embed/reflash/main.c @@ -23,6 +23,7 @@ #include STM32_HAL_H +#include "bootutils.h" #include "common.h" #include "display.h" #include "display_draw.h" @@ -30,9 +31,11 @@ #include "image.h" #include "model.h" #include "rng.h" +#include "rsod.h" #include "sbu.h" #include "sdcard.h" #include "secbool.h" +#include "system.h" #include "systimer.h" #include "terminal.h" #include "touch.h" @@ -43,7 +46,7 @@ static void progress_callback(int pos, int len) { term_printf("."); } -static void flash_from_sdcard(const flash_area_t* area, uint32_t source, +static void flash_from_sdcard(const flash_area_t *area, uint32_t source, uint32_t length) { static uint32_t buf[SDCARD_BLOCK_SIZE / sizeof(uint32_t)]; @@ -69,8 +72,7 @@ static void flash_from_sdcard(const flash_area_t* area, uint32_t source, } int main(void) { - systick_init(); - systimer_init(); + system_init(&rsod_panic_handler); sdcard_init(); touch_init(); diff --git a/core/embed/reflash/memory_stm32u58.ld b/core/embed/reflash/memory_stm32u58.ld index e00c0df10..586970355 100644 --- a/core/embed/reflash/memory_stm32u58.ld +++ b/core/embed/reflash/memory_stm32u58.ld @@ -40,6 +40,8 @@ sram5_start = ORIGIN(SRAM5); sram5_end = ORIGIN(SRAM5) + LENGTH(SRAM5); sram6_start = ORIGIN(SRAM6); sram6_end = ORIGIN(SRAM6) + LENGTH(SRAM6); +bss_start = ADDR(.bss); +bss_end = ADDR(.bss) + SIZEOF(.bss); /* reserve 256 bytes for bootloader arguments */ boot_args_start = ORIGIN(BOOT_ARGS); diff --git a/core/embed/rust/rust_ui_common.h b/core/embed/rust/rust_ui_common.h index 11f03965a..6c8a8b7de 100644 --- a/core/embed/rust/rust_ui_common.h +++ b/core/embed/rust/rust_ui_common.h @@ -1,8 +1,7 @@ #include "common.h" -__attribute__((noreturn)) void error_shutdown_rust(const char* title, - const char* msg, - const char* footer); +void display_rsod_rust(const char* title, const char* message, + const char* footer); void screen_boot_stage_2(void); diff --git a/core/embed/rust/src/trezorhal/fatal_error.rs b/core/embed/rust/src/trezorhal/fatal_error.rs index 132c41eb1..dec821bd8 100644 --- a/core/embed/rust/src/trezorhal/fatal_error.rs +++ b/core/embed/rust/src/trezorhal/fatal_error.rs @@ -1,31 +1,28 @@ mod ffi { extern "C" { - // trezorhal/bootuils.c - pub fn secure_shutdown() -> !; + // error_handling.h + pub fn error_shutdown(msg: *const cty::c_char) -> !; } } -use crate::ui::{ - shape, - ui_features::{ModelUI, UIFeaturesCommon}, -}; +pub fn error_shutdown(msg: &str) -> ! { + const MAX_LEN: usize = 63; + let mut buffer: [u8; MAX_LEN + 1] = [0; MAX_LEN + 1]; -fn shutdown() -> ! { - unsafe { ffi::secure_shutdown() } -} + // Copy the message to the buffer + let msg_bytes = msg.as_bytes(); + let len = if msg_bytes.len() < MAX_LEN { + msg_bytes.len() + } else { + MAX_LEN + }; + buffer[..len].copy_from_slice(&msg_bytes[..len]); -/// Shows an error message and shuts down the device. -pub fn error_shutdown(title: &str, msg: &str, footer: &str) -> ! { - // SAFETY: - // This is the only situation we are allowed use this function - // to allow nested calls to `run_with_bumps`/`render_on_display`, - // because after the error message is displayed, the application will - // shut down. - unsafe { shape::unlock_bumps_on_failure() }; - - ModelUI::screen_fatal_error(title, msg, footer); - ModelUI::backlight_on(); - shutdown() + unsafe { + // SAFETY: `buffer` is a valid null-terminated string + // and the function never returns. + ffi::error_shutdown(buffer.as_ptr() as *const cty::c_char); + } } /// Shows an error message on the screen and shuts down the device. @@ -46,7 +43,7 @@ pub fn __fatal_error(msg: &str, _file: &str, _line: u32) -> ! { dbg_println!("==="); } - error_shutdown("INTERNAL_ERROR", msg, "PLEASE VISIT\nTREZOR.IO/RSOD"); + error_shutdown(msg); } pub trait UnwrapOrFatalError { diff --git a/core/embed/rust/src/ui/api/common_c.rs b/core/embed/rust/src/ui/api/common_c.rs index ccbed0d3a..7eafbef5a 100644 --- a/core/embed/rust/src/ui/api/common_c.rs +++ b/core/embed/rust/src/ui/api/common_c.rs @@ -10,19 +10,33 @@ use crate::ui::{ geometry::{Alignment2D, Point}, }; -use crate::{trezorhal::fatal_error, ui::util::from_c_str}; +#[cfg(feature = "new_rendering")] +use crate::ui::shape; + +use crate::ui::util::from_c_str; #[no_mangle] -extern "C" fn error_shutdown_rust( +extern "C" fn display_rsod_rust( title: *const cty::c_char, msg: *const cty::c_char, footer: *const cty::c_char, -) -> ! { +) { let title = unsafe { from_c_str(title) }.unwrap_or(""); let msg = unsafe { from_c_str(msg) }.unwrap_or(""); let footer = unsafe { from_c_str(footer) }.unwrap_or(""); - fatal_error::error_shutdown(title, msg, footer) + // SAFETY: + // This is the only situation we are allowed use this function + // to allow nested calls to `run_with_bumps`/`render_on_display`, + // because after the error message is displayed, the application will + // shut down. + #[cfg(feature = "new_rendering")] + unsafe { + shape::unlock_bumps_on_failure() + }; + + ModelUI::screen_fatal_error(title, msg, footer); + ModelUI::backlight_on(); } #[no_mangle] diff --git a/core/embed/trezorhal/applet.h b/core/embed/trezorhal/applet.h new file mode 100644 index 000000000..5309e7b5d --- /dev/null +++ b/core/embed/trezorhal/applet.h @@ -0,0 +1,81 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TREZORHAL_APPLET_H +#define TREZORHAL_APPLET_H + +#include +#include + +#ifdef SYSCALL_DISPATCH + +#include "systask.h" + +// Applet entry point +typedef void (*applet_startup_t)(const char* args, uint32_t random); + +// Applet header found at the beginning of the applet binary +typedef struct { + // Stack area + uint32_t stack_start; + uint32_t stack_size; + // Applet entry point + applet_startup_t startup; +} applet_header_t; + +// Applet memory layout +typedef struct { + // Data area 1 + uint32_t data1_start; + uint32_t data1_size; + // Data area 2 + uint32_t data2_start; + uint32_t data2_size; + +} applet_layout_t; + +typedef struct { + // Points to the applet header found at the beginning of the applet binary + applet_header_t* header; + // Applet memory layout describing the memory areas + // the applet is allowed to use + applet_layout_t layout; + // Applet task + systask_t task; + + // + privileges + +} applet_t; + +// Initializes the applet structure +void applet_init(applet_t* applet, applet_header_t* header, + applet_layout_t* layout); + +// Resets the applet and prepares it for execution from its entry point. +// +// Applet does not start immediately, it needs to be scheduled by +// `systask_yield_to(&applet->task)` after calling this function. +// +// Returns `true` if the applet was successfully reset. +bool applet_reset(applet_t* applet, uint32_t cmd, const void* arg, + size_t arg_size); + +#endif // SYSCALL_DISPATCH + +#endif // TREZORHAL_APPLET_H diff --git a/core/embed/trezorhal/fault_handlers.h b/core/embed/trezorhal/fault_handlers.h deleted file mode 100644 index 8372135f3..000000000 --- a/core/embed/trezorhal/fault_handlers.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef TREZORHAL_FAULT_HANDLERS_H -#define TREZORHAL_FAULT_HANDLERS_H - -// Initializes and enables fault handlers -void fault_handlers_init(void); - -#endif // TREZORHAL_FAULT_HANDLERS_H diff --git a/core/embed/trezorhal/stm32f4/applet.c b/core/embed/trezorhal/stm32f4/applet.c new file mode 100644 index 000000000..a36e2f9cf --- /dev/null +++ b/core/embed/trezorhal/stm32f4/applet.c @@ -0,0 +1,73 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "applet.h" +#include "mpu.h" +#include "rng.h" +#include "systask.h" + +#ifdef SYSCALL_DISPATCH + +void applet_init(applet_t* applet, applet_header_t* header, + applet_layout_t* layout) { + memset(applet, 0, sizeof(applet_t)); + + applet->header = header; + applet->layout = *layout; +} + +static void applet_clear_memory(applet_t* applet) { + if (applet->layout.data1_size > 0) { + memset((void*)applet->layout.data1_start, 0, applet->layout.data1_size); + } + if (applet->layout.data2_size > 0) { + memset((void*)applet->layout.data2_start, 0, applet->layout.data2_size); + } +} + +bool applet_reset(applet_t* applet, uint32_t cmd, const void* arg, + size_t arg_size) { + // Clear all memory the applet is allowed to use + applet_clear_memory(applet); + + // Reset the applet task (stack pointer, etc.) + systask_init(&applet->task, applet->header->stack_start, + applet->header->stack_size); + + // Copy the arguments onto the applet stack + void* arg_copy = NULL; + if (arg != NULL && arg_size > 0) { + arg_copy = systask_push_data(&applet->task, arg, arg_size); + if (arg_copy == NULL) { + return false; + } + } + + // Schedule the applet task run + uint32_t arg1 = cmd; + uint32_t arg2 = (uint32_t)arg_copy; + uint32_t arg3 = rng_get(); + + return systask_push_call(&applet->task, applet->header->startup, arg1, arg2, + arg3); +} + +#endif // SYSCALL_DISPATCH diff --git a/core/embed/trezorhal/stm32f4/fault_handlers.c b/core/embed/trezorhal/stm32f4/fault_handlers.c deleted file mode 100644 index 35fc0ed03..000000000 --- a/core/embed/trezorhal/stm32f4/fault_handlers.c +++ /dev/null @@ -1,58 +0,0 @@ -#include TREZOR_BOARD - -#include "common.h" -#include "mpu.h" - -#ifdef KERNEL_MODE - -void fault_handlers_init(void) { - // Enable BUS fault and USAGE fault handlers - SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk); -} - -void HardFault_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(HF)"); -} - -void MemManage_Handler_MM(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(MM)"); -} - -void MemManage_Handler_SO(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(SO)"); -} - -void BusFault_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(BF)"); -} - -void UsageFault_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(UF)"); -} - -void NMI_Handler(void) { - mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); - // Clock Security System triggered NMI - if ((RCC->CIR & RCC_CIR_CSSF) != 0) { - error_shutdown("(CS)"); - } - mpu_restore(mpu_mode); -} - -// from util.s -extern void shutdown_privileged(void); - -void PVD_IRQHandler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); -#ifdef BACKLIGHT_PWM_TIM - BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = 0; // turn off display backlight -#endif - shutdown_privileged(); -} - -#endif // KERNEL_MODE diff --git a/core/embed/trezorhal/stm32f4/syscall.c b/core/embed/trezorhal/stm32f4/syscall.c index 8fde6b42a..2846ba68e 100644 --- a/core/embed/trezorhal/stm32f4/syscall.c +++ b/core/embed/trezorhal/stm32f4/syscall.c @@ -17,40 +17,44 @@ * along with this program. If not, see . */ -#include STM32_HAL_H - #include "syscall.h" -#include "image.h" -#include "irq.h" #include "mpu.h" #ifdef SYSCALL_DISPATCH -void syscall_init(void) { - // SVCall priority should be the lowest since it is - // generally a blocking operation - NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_LOWEST); -} - __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( - uint32_t arg1, uint32_t arg2, uint32_t arg3, void *callback) { + uint32_t arg1, uint32_t arg2, uint32_t arg3, void* callback) { __asm__ volatile( "push {r1-r12, lr} \n" - "mrs r12, PSP \n" // reserved frame on unprivileged stack (!@# - // TODO check PSP value???) +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "mrs r12, PSPLIM \n" // Backup unprivileged stack limit "push {r12} \n" - "sub r12, r12, #32 \n" +#endif + "mrs r12, PSP \n" // Backup unprivileged stack pointer + "push {r12} \n" + + "sub r12, r12, #32 \n" // Reserve space for stack frame "msr PSP, r12 \n" - "str r0, [r12, #0] \n" // r0 - "str r1, [r12, #4] \n" // r1" - "str r2, [r12, #8] \n" // r2" + "str r0, [r12, #0] \n" // pass r0 + "str r1, [r12, #4] \n" // pass r1 + "str r2, [r12, #8] \n" // pass r2 "mov r1, #0 \n" - "str r1, [r12, #12] \n" // r3" - "str r1, [r12, #16] \n" // r12" - "str r1, [r12, #20] \n" // lr" + + "mov r4, r1 \n" // Clear registers r4-r11 + "mov r5, r1 \n" + "mov r6, r1 \n" + "mov r7, r1 \n" + "mov r8, r1 \n" + "mov r9, r1 \n" + "mov r10, r1 \n" + "mov r11, r1 \n" + + "str r1, [r12, #12] \n" // clear r3 + "str r1, [r12, #16] \n" // clear r12 + "str r1, [r12, #20] \n" // clear lr "bic r3, r3, #1 \n" "str r3, [r12, #24] \n" // return address @@ -58,14 +62,8 @@ __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( "ldr r1, = 0x01000000 \n" "str r1, [r12, #28] \n" // xPSR - "ldr r1, = 0xE000EF34 \n" // FPU->FPPCCR - "ldr r0, [r1] \n" - "bic r0, r0, #1 \n" // Clear LSPACT to suppress lazy stacking to - "str r0, [r1] \n" // avoid potential PSP stack overwrite. - - "mrs r1, CONTROL \n" - "bic r1, r1, #4 \n" // Clear FPCA to suppress lazy stacking to - "msr CONTROL, r1 \n" // avoid potential PSP stack overwrite. + "vmov r0, s0 \n" // Use FPU instruction to ensure lazy + // stacking // return to Secure Thread mode (use Secure PSP) "ldr lr, = 0xFFFFFFFD \n" @@ -73,133 +71,31 @@ __attribute__((naked, no_stack_protector)) static uint32_t _invoke_app_callback( } uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, - void *callback) { + void* callback) { mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_APP); uint32_t retval = _invoke_app_callback(args1, arg2, arg3, callback); mpu_reconfig(mpu_mode); return retval; } -// Jumps to reset vector of unprivileged application code -// -// Can be called only from an exception handler -__attribute__((naked, no_stack_protector)) static void start_app( - uint32_t app_start) { +__attribute__((naked, no_stack_protector)) void return_from_app_callback( + uint32_t retval, uint32_t* msp) { __asm__ volatile( - "ldr r12, [r0, #0] \n" // stack pointer - "sub r12, r12, #32 \n" - "msr PSP, r12 \n" - - "mov r1, #0 \n" - "str r1, [r12, #0] \n" // r0 - "str r1, [r12, #4] \n" // r1" - "str r1, [r12, #8] \n" // r2" - "str r1, [r12, #12] \n" // r3" - "str r1, [r12, #16] \n" // r12" - "str r1, [r12, #20] \n" // lr" - - "ldr r1, [r0, #4] \n" // reset vector - "bic r1, r1, #1 \n" - "str r1, [r12, #24] \n" // return address - - "ldr r1, = 0x01000000 \n" - "str r1, [r12, #28] \n" // xPSR - - "ldr r1, = 0xE000EF34 \n" // FPU->FPPCCR - "ldr r0, [r1] \n" - "bic r0, r0, #1 \n" // Clear LSPACT to suppress lazy stacking to - "str r0, [r1] \n" // avoid potential PSP stack overwrite. - - "mrs r1, CONTROL \n" - "bic r1, r1, #4 \n" // Clear FPCA to suppress lazy stacking to - "msr CONTROL, r1 \n" // avoid potential PSP stack overwrite. - - "mrs r1, CONTROL \n" // Switch thread mode to unprivileged - "orr r1, r1, #1 \n" // by setting nPRIV bit in CONTROL register. - "msr CONTROL, r1 \n" // This applies after return from this - // handler. - - // return to Secure Thread mode (use Secure PSP) - "ldr lr, = 0xFFFFFFFD \n" - "bx lr \n"); -} - -void SVC_C_Handler(uint32_t *stack, uint32_t r4, uint32_t r5, uint32_t r6) { - uint8_t svc_number = ((uint8_t *)stack[6])[-2]; - uint32_t args[6] = {stack[0], stack[1], stack[2], stack[3], r4, r5}; - - mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); - - switch (svc_number) { -#ifdef SYSTEM_VIEW - case SVC_GET_DWT_CYCCNT: - cyccnt_cycles = *DWT_CYCCNT_ADDR; - break; + "MSR MSP, R1 \n" + "POP {R1} \n" + "MSR PSP, R1 \n" +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "POP {R1} \n" + "MSR PSPLIM, R1 \n" #endif - case SVC_START_APP: - mpu_reconfig(MPU_MODE_APP); - start_app(args[0]); - break; - case SVC_SYSCALL: - syscall_handler(args, r6); - stack[0] = args[0]; - stack[1] = args[1]; - break; - default: - stack[0] = 0xffffffff; - stack[1] = 0xffffffff; - break; - } - mpu_restore(mpu_mode); -} + "LDR R1, = 0xE000EF34 \n" // FPU->FPCCR + "LDR R2, [R1] \n" + "BIC R2, R2, #1 \n" // Clear LSPACT to suppress repeated lazy + "STR R2, [R1] \n" // stacking that was already done -__attribute__((naked, no_stack_protector)) void SVC_Handler(void) { - __asm__ volatile( - " tst lr, #4 \n" // Bit #3 tells which stack pointer should we - // use - " ite eq \n" // Next 2 instructions are if-then-else - " mrseq r0, msp \n" // Make R0 point to main stack pointer - " mrsne r0, psp \n" // Make R0 point to process stack pointer - - " ldr r1, [r0, #24] \n" // Load the PC of the SVC handler - " ldrb r1, [r1, #-2] \n" // Load the instruction at the PC - " cmp r1, #2 \n" // SVC_CALLBACK_RETURN - " beq svc_callback_return \n" - - " mov r1, r4 \n" // pass R4 (arg5), R5 (arg6) and - " mov r2, r5 \n" // R6 (sycall_number) as arguments - " mov r3, r6 \n" // to SCV_C_Handler - " b SVC_C_Handler \n" // - - "svc_callback_return: \n" - - " ldr r0, [r0] \n" - " pop {r1} \n" - " msr PSP, r1 \n" - " pop {r1-r12, lr} \n" - " bx lr \n"); -} - -void __attribute__((no_stack_protector, noreturn)) -start_unprivileged_app(void) { - //!@# TODO calculate better - static const uint32_t app_start = COREAPP_START + IMAGE_HEADER_SIZE + 0x0400; - - mpu_reconfig(MPU_MODE_APP); - - register uint32_t ret __asm__("r0") = app_start; - - // SVC_START_APP is the only SVC that is allowed to be invoked from kernel - // itself and it is used to start the unprivileged application code - __asm__ volatile("svc %[svid]\n" - : "=r"(ret) - : [svid] "i"(SVC_START_APP), "r"(ret) - : "memory"); - - // We never get here, just to supress compiler warning - while (1) { - } + "POP {R1-R12, LR} \n" + "BX LR \n"); } #endif // SYSCALL_DISPATCH diff --git a/core/embed/trezorhal/stm32f4/syscall.h b/core/embed/trezorhal/stm32f4/syscall.h index 32da335d9..de64aaa90 100644 --- a/core/embed/trezorhal/stm32f4/syscall.h +++ b/core/embed/trezorhal/stm32f4/syscall.h @@ -22,18 +22,16 @@ #include +#include "applet.h" #include "syscall_numbers.h" // Reserved SVC numbers #define SVC_SYSCALL 0 -#define SVC_START_APP 1 +#define SVC_SYSTASK_YIELD 1 #define SVC_CALLBACK_RETURN 2 #ifdef KERNEL_MODE -// Initializes the SVC/Syscall handlers -void syscall_init(void); - // Handles all syscall requests. // // `args` points to an array of six 32-bit arguments. @@ -44,14 +42,20 @@ void syscall_init(void); // // Return values must be copied to `args[0]` and // `args[1]` (if returning a 64-bit value). -void syscall_handler(uint32_t *args, uint32_t syscall); +void syscall_handler(uint32_t* args, uint32_t syscall); -// Invokes application callback from the syscall handler +// Invokes the application callback from the syscall handler. +// +// This is a *temporary* helper function used to invoke application callbacks +// from the syscall handler. It will be removed once all callback arguments +// are eliminated from syscalls. uint32_t invoke_app_callback(uint32_t args1, uint32_t arg2, uint32_t arg3, - void *callback); + void* callback); -// Jumps to reset vector in the unprivileged application -void __attribute__((noreturn)) start_unprivileged_app(void); +// Internal function for returning from an application callback. +// This function is called from an unprivileged app via an SVC call. It restores +// the stack pointer and returns control to the privileged caller. +void return_from_app_callback(uint32_t retval, uint32_t* msp); #else // KERNEL_MODE diff --git a/core/embed/trezorhal/stm32f4/syscall_dispatch.c b/core/embed/trezorhal/stm32f4/syscall_dispatch.c index 3e4510f98..c4dafbb37 100644 --- a/core/embed/trezorhal/stm32f4/syscall_dispatch.c +++ b/core/embed/trezorhal/stm32f4/syscall_dispatch.c @@ -34,6 +34,8 @@ #include "rng.h" #include "sdcard.h" #include "secret.h" +#include "systask.h" +#include "system.h" #include "systick.h" #include "touch.h" #include "translations.h" @@ -61,8 +63,23 @@ static void firmware_hash_callback_wrapper(void *context, uint32_t progress, firmware_hash_callback); } -void syscall_handler(uint32_t *args, uint32_t syscall) { +__attribute((no_stack_protector)) void syscall_handler(uint32_t *args, + uint32_t syscall) { switch (syscall) { + case SYSCALL_SYSTEM_EXIT: { + systask_t *task = systask_active(); + systask_exit(task, (int)args[0]); + } break; + case SYSCALL_SYSTEM_EXIT_ERROR: { + systask_t *task = systask_active(); + systask_exit_error(task, (const char *)args[0], (const char *)args[1], + (const char *)args[2]); + } break; + case SYSCALL_SYSTEM_EXIT_FATAL: { + systask_t *task = systask_active(); + systask_exit_fatal(task, (const char *)args[0], (const char *)args[1], + (int)args[2]); + } break; case SYSCALL_SYSTICK_CYCLES: { uint64_t cycles = systick_cycles(); args[0] = cycles & 0xFFFFFFFF; diff --git a/core/embed/trezorhal/stm32f4/syscall_numbers.h b/core/embed/trezorhal/stm32f4/syscall_numbers.h index adc0d5b56..4e602032e 100644 --- a/core/embed/trezorhal/stm32f4/syscall_numbers.h +++ b/core/embed/trezorhal/stm32f4/syscall_numbers.h @@ -20,10 +20,14 @@ #ifndef SYSCALL_NUMBERS_H #define SYSCALL_NUMBERS_H +// Syscall identifiers typedef enum { - // Syscalls numbers - SYSCALL_SYSTICK_CYCLES = 1, + SYSCALL_SYSTEM_EXIT = 1, + SYSCALL_SYSTEM_EXIT_ERROR, + SYSCALL_SYSTEM_EXIT_FATAL, + + SYSCALL_SYSTICK_CYCLES, SYSCALL_SYSTICK_MS, SYSCALL_SYSTICK_US, SYSCALL_SYSTICK_US_TO_CYCLES, diff --git a/core/embed/trezorhal/stm32f4/syscall_stubs.c b/core/embed/trezorhal/stm32f4/syscall_stubs.c index 1da1d3ef1..10fc13cd3 100644 --- a/core/embed/trezorhal/stm32f4/syscall_stubs.c +++ b/core/embed/trezorhal/stm32f4/syscall_stubs.c @@ -21,6 +21,33 @@ #ifndef KERNEL_MODE +// ============================================================================= +// system.h +// ============================================================================= + +#include "system.h" + +void system_exit(int exit_code) { + syscall_invoke1(exit_code, SYSCALL_SYSTEM_EXIT); + while (1) + ; +} + +void system_exit_error(const char *title, const char *message, + const char *footer) { + syscall_invoke3((uint32_t)title, (uint32_t)message, (uint32_t)footer, + SYSCALL_SYSTEM_EXIT_ERROR); + while (1) + ; +} + +void system_exit_fatal(const char *message, const char *file, int line) { + syscall_invoke3((uint32_t)message, (uint32_t)file, line, + SYSCALL_SYSTEM_EXIT_FATAL); + while (1) + ; +} + // ============================================================================= // systick.h // ============================================================================= diff --git a/core/embed/trezorhal/stm32f4/systask.c b/core/embed/trezorhal/stm32f4/systask.c new file mode 100644 index 000000000..56456f7c0 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/systask.c @@ -0,0 +1,639 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include STM32_HAL_H + +#include +#include + +#include "bootutils.h" +#include "irq.h" +#include "mpu.h" +#include "syscall.h" +#include "systask.h" +#include "system.h" + +#ifdef KERNEL_MODE + +// Disable stack protector for this file since it may interfere +// with the stack manipulation and fault handling +#pragma GCC optimize("no-stack-protector") + +#define STK_FRAME_R0 0 +#define STK_FRAME_R1 1 +#define STK_FRAME_R2 2 +#define STK_FRAME_R3 3 +#define STK_FRAME_R12 4 +#define STK_FRAME_LR 5 +#define STK_FRAME_RET_ADDR 6 +#define STK_FRAME_XPSR 7 + +// Task manager state +typedef struct { + // Error handler called when a kernel task terminates + systask_error_handler_t error_handler; + // Background kernel task + systask_t kernel_task; + // Currently running task + systask_t* active_task; + // Task to be scheduled next + systask_t* waiting_task; +} systask_scheduler_t; + +// Kernel stack base pointer defined in linker script +extern uint8_t _sstack; +extern uint8_t _estack; + +// Global task manager state +static systask_scheduler_t g_systask_scheduler = { + // This static initialization is required for exception handling + // to function correctly before the scheduler is initialized. + .active_task = &g_systask_scheduler.kernel_task, + .waiting_task = &g_systask_scheduler.kernel_task, + .kernel_task = { + .sp_lim = (uint32_t)&_sstack, + }}; + +void systask_scheduler_init(systask_error_handler_t error_handler) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + memset(scheduler, 0, sizeof(systask_scheduler_t)); + + scheduler->error_handler = error_handler; + scheduler->active_task = &scheduler->kernel_task; + scheduler->waiting_task = scheduler->active_task; + + scheduler->kernel_task.sp_lim = (uint32_t)&_sstack; + + // SVCall priority should be the lowest since it is + // generally a blocking operation + NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_LOWEST); + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_LOWEST); + + // Enable BusFault and UsageFault handlers + SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk); +} + +systask_t* systask_active(void) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + return scheduler->active_task; +} + +static void systask_yield(void) { + bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0; + + if (handler_mode) { + SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; + __DSB(); + } else { + // SVC_SYSTASK_YIELD is the only SVC that is allowed to be invoked from + // kernel itself, and it is used to start the unprivileged application code. + __asm__ volatile("svc %[svid]\n" + : // no output + : [svid] "i"(SVC_SYSTASK_YIELD) + : "memory"); + } +} + +void systask_yield_to(systask_t* task) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + irq_key_t irq_key = irq_lock(); + scheduler->waiting_task = task; + irq_unlock(irq_key); + + systask_yield(); +} + +void systask_init(systask_t* task, uint32_t stack_ptr, uint32_t stack_size) { + memset(task, 0, sizeof(systask_t)); + task->sp = stack_ptr + stack_size; + task->sp_lim = stack_ptr + 256; + task->exc_return = 0xFFFFFFED; // Thread mode, use PSP, pop FP context + task->mpu_mode = MPU_MODE_APP; +} + +uint32_t* systask_push_data(systask_t* task, const void* data, size_t size) { + if (task->sp < task->sp_lim) { + // Stack overflow + return NULL; + } + + uint32_t stack_remaining = task->sp - task->sp_lim; + + if (stack_remaining < size) { + // Not enough space on the stack + return NULL; + } + + task->sp -= size; + + if (data != NULL) { + memcpy((void*)task->sp, data, size); + } else { + memset((void*)task->sp, 0, size); + } + + return (void*)task->sp; +} + +void systask_pop_data(systask_t* task, size_t size) { task->sp += size; } + +bool systask_push_call(systask_t* task, void* entrypoint, uint32_t arg1, + uint32_t arg2, uint32_t arg3) { + uint32_t original_sp = task->sp; + + // Align stack pointer to 8 bytes + task->sp &= ~7; + + // FP extension context + if (systask_push_data(task, NULL, 0x48) == NULL) { + goto cleanup; + } + + // Standard exception frame + uint32_t* stk_frame = systask_push_data(task, NULL, 0x20); + if (stk_frame == NULL) { + goto cleanup; + } + + // Registers r4-r11 + if (systask_push_data(task, NULL, 0x20) == NULL) { + goto cleanup; + } + // Registers s16-s31 + if (systask_push_data(task, NULL, 0x40) == NULL) { + goto cleanup; + } + + // Return to thread mode, use PSP, pop FP context + task->exc_return = 0xFFFFFFED; + + stk_frame[STK_FRAME_R0] = arg1; + stk_frame[STK_FRAME_R1] = arg2; + stk_frame[STK_FRAME_R2] = arg3; + stk_frame[STK_FRAME_RET_ADDR] = (uint32_t)entrypoint & ~1; + stk_frame[STK_FRAME_XPSR] = 0x01000000; // T (Thumb state) bit set + + return true; + +cleanup: + task->sp = original_sp; + return false; +} + +static void systask_kill(systask_t* task) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + task->killed = 1; + + if (task == &scheduler->kernel_task) { + // Call panic handler + if (scheduler->error_handler != NULL) { + scheduler->error_handler(&task->pminfo); + } + secure_shutdown(); + } else if (task == scheduler->active_task) { + // Switch to the kernel task + systask_yield_to(&scheduler->kernel_task); + } +} + +void systask_exit(systask_t* task, int exit_code) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + if (task == NULL) { + bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0; + task = handler_mode ? &scheduler->kernel_task : scheduler->active_task; + } + + systask_postmortem_t* pminfo = &task->pminfo; + + memset(pminfo, 0, sizeof(systask_postmortem_t)); + pminfo->reason = TASK_TERM_REASON_EXIT; + pminfo->privileged = (task == &scheduler->kernel_task); + pminfo->exit.code = exit_code; + + systask_kill(task); +} + +void systask_exit_error(systask_t* task, const char* title, const char* message, + const char* footer) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + if (task == NULL) { + bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0; + task = handler_mode ? &scheduler->kernel_task : scheduler->active_task; + } + + systask_postmortem_t* pminfo = &task->pminfo; + + memset(pminfo, 0, sizeof(systask_postmortem_t)); + pminfo->reason = TASK_TERM_REASON_ERROR; + pminfo->privileged = (task == &scheduler->kernel_task); + + if (title != NULL) { + strncpy(pminfo->error.title, title, sizeof(pminfo->error.title) - 1); + } + + if (message != NULL) { + strncpy(pminfo->error.message, message, sizeof(pminfo->error.message) - 1); + } + + if (footer != NULL) { + strncpy(pminfo->error.footer, footer, sizeof(pminfo->error.footer) - 1); + } + + systask_kill(task); +} + +void systask_exit_fatal(systask_t* task, const char* message, const char* file, + int line) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + if (task == NULL) { + bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0; + task = handler_mode ? &scheduler->kernel_task : scheduler->active_task; + } + + systask_postmortem_t* pminfo = &task->pminfo; + + memset(pminfo, 0, sizeof(systask_postmortem_t)); + pminfo->reason = TASK_TERM_REASON_FATAL; + pminfo->privileged = (task == &scheduler->kernel_task); + + if (message != NULL) { + strncpy(pminfo->fatal.expr, message, sizeof(pminfo->fatal.expr) - 1); + } + + if (file != NULL) { + strncpy(pminfo->fatal.file, file, sizeof(pminfo->fatal.file) - 1); + } + + pminfo->fatal.line = line; + + systask_kill(task); +} + +// Terminate active task from fault/exception handler +__attribute((used)) static void systask_exit_fault(bool privileged, + uint32_t sp) { + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); + + systask_scheduler_t* scheduler = &g_systask_scheduler; + + systask_t* task = + privileged ? &scheduler->kernel_task : scheduler->active_task; + + systask_postmortem_t* pminfo = &task->pminfo; + + // Do not overwrite the reason if it is already set to fault + // (exception handlers may call this function multiple times, and + // we want to preserve the first reason) + if (pminfo->reason != TASK_TERM_REASON_FAULT) { + pminfo->reason = TASK_TERM_REASON_FAULT; + pminfo->privileged = privileged; + pminfo->fault.sp = sp; +#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)) + pminfo->fault.sp_lim = task->sp_lim; +#endif + pminfo->fault.irqn = (__get_IPSR() & IPSR_ISR_Msk) - 16; + pminfo->fault.cfsr = SCB->CFSR; + pminfo->fault.mmfar = SCB->MMFAR; + pminfo->fault.bfar = SCB->BFAR; + pminfo->fault.hfsr = SCB->HFSR; + } + + systask_kill(task); + + mpu_restore(mpu_mode); +} + +// C part of PendSV handler that switches tasks +// +// `sp` is the stack pointer of the current task +// `sp_lim` is the stack pointer limit of the current task +// `exc_return` is the execution state of the current task +// +// Returns the context struct of the next task +__attribute((no_stack_protector, used)) static uint32_t scheduler_pendsv( + uint32_t sp, uint32_t sp_lim, uint32_t exc_return) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + // Save the current task context + systask_t* prev_task = scheduler->active_task; + prev_task->sp = sp; +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + // sp_lim is not valid on ARMv7-M + prev_task->sp_lim = sp_lim; +#endif + prev_task->exc_return = exc_return; + prev_task->mpu_mode = mpu_get_mode(); + + // Switch to the next task + scheduler->active_task = scheduler->waiting_task; + + // Load the scheduled task context + systask_t* next_task = scheduler->active_task; + + // Set task privilege level + uint32_t control = __get_CONTROL(); + if (next_task == &scheduler->kernel_task) { + control &= ~CONTROL_nPRIV_Msk; + } else { + control |= CONTROL_nPRIV_Msk; + } + __set_CONTROL(control); + + // Setup the MPU for the new task + mpu_reconfig(next_task->mpu_mode); + + return (uint32_t)next_task; +} + +__attribute__((naked, no_stack_protector)) void PendSV_Handler(void) { + __asm__ volatile( + "LDR R0, =%[active_task] \n" + "LDR R1, =%[waiting_task]\n" + "CMP R0, R1 \n" + "BEQ 3f \n" // No task switch needed + + "LDR R0, [R0] \n" // R0 = active_task + "LDR R0, [R0, #12] \n" // R0 = active_task->killed + "CMP R0, #0 \n" + "BEQ 1f \n" // =0 => normal processing + + // We are switching from a killed task to the kernel task. + // Since the reason might be a stack overflow, we must not + // attempt to save the task context. + + "LDR R1, = 0xE000EF34 \n" // FPU->FPCCR + "LDR R0, [R1] \n" + "BIC R0, R0, #1 \n" // Clear LSPACT to suppress later lazy + "STR R0, [R1] \n" // stacking to the killed task stack + + "MOV R0, #0 \n" // Skip context save + "MOV R1, R0 \n" // + "MOV R2, R0 \n" // + "B 2f \n" // + + "1: \n" + + // Save the current task context on its stack before switching + + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "ITTEE EQ \n" + "MRSEQ R0, MSP \n" // Get current SP + "MRSEQ R1, MSPLIM \n" // Get current SP Limit + "MRSNE R0, PSP \n" + "MRSNE R1, PSPLIM \n" +#else + "ITE EQ \n" + "MRSEQ R0, MSP \n" // Get current SP + "MRSNE R0, PSP \n" + "MOV R1, #0 \n" // (fake SPLIM) +#endif + "IT EQ \n" // If using main stack: + "SUBEQ SP, SP, #0x60 \n" // reserve space for R4-11 and S16-S31 + + "MOV R2, LR \n" // Get current EXC_RETURN + + "STMDB R0!, {R4-R11} \n" // Save R4-R11 to SP Frame Stack + "TST LR, #0x10 \n" // Check EXC_RETURN.Ftype bit to see if + // the current thread has a FP context + "IT EQ \n" + "VSTMDBEQ R0!, {S16-S31} \n" // If so, save S16-S31 FP addition + // context, that will also trigger lazy + // fp context preservation of S0-S15 + "2: \n" + + "BL scheduler_pendsv \n" // Save SP value of current task + "LDR LR, [R0, #8] \n" // Get the EXC_RETURN value + "LDR R1, [R0, #4] \n" // Get the SP_LIM value + "LDR R0, [R0, #0] \n" // Get the SP value + + "TST LR, #0x10 \n" // Check EXC_RETURN.Ftype bit to see if + // the next thread has a FP context + "IT EQ \n" + "VLDMIAEQ R0!, {S16-S31} \n" // If so, restore S16-S31 + "LDMIA R0!, {R4-R11} \n" // Restore R4-R11 + + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "ITEE EQ \n" + "MSREQ MSP, R0 \n" // Update MSP + "MSRNE PSPLIM, R1 \n" // Update PSPLIM & PSP + "MSRNE PSP, R0 \n" +#else + "ITE EQ \n" + "MSREQ MSP, R0 \n" // Update the MSP + "MSRNE PSP, R0 \n" // Update the PSP +#endif + "3: " + "BX LR \n" + : // No output + : [active_task] "i"(&g_systask_scheduler.active_task), // Input + [waiting_task] "i"(&g_systask_scheduler.waiting_task) // Input + : // Clobber + ); +} + +__attribute__((no_stack_protector, used)) static uint32_t svc_handler( + uint32_t* stack, uint32_t* msp, uint32_t exc_return, uint32_t r4, + uint32_t r5, uint32_t r6) { + uint8_t svc_number = ((uint8_t*)stack[6])[-2]; + +#ifdef SYSCALL_DISPATCH + uint32_t args[6] = {stack[0], stack[1], stack[2], stack[3], r4, r5}; +#endif + + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); + + switch (svc_number) { +#ifdef SYSTEM_VIEW + case SVC_GET_DWT_CYCCNT: + cyccnt_cycles = *DWT_CYCCNT_ADDR; + break; +#endif + case SVC_SYSTASK_YIELD: + // Yield to the waiting task + systask_yield(); + break; +#ifdef SYSCALL_DISPATCH + case SVC_SYSCALL: + syscall_handler(args, r6); + stack[0] = args[0]; + stack[1] = args[1]; + break; + case SVC_CALLBACK_RETURN: + // g_return_value = args[0] + // exc_return = return_from_callback; + mpu_restore(mpu_mode); + return_from_app_callback(args[0], msp); + break; +#endif + default: + break; + } + + mpu_restore(mpu_mode); + return exc_return; +} + +__attribute((naked, no_stack_protector)) void SVC_Handler(void) { + __asm__ volatile( + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + "ITE EQ \n" + "MRSEQ R0, MSP \n" // `stack` argument + "MRSNE R0, PSP \n" + "TST LR, #0x20 \n" + "IT EQ \n" + "ADDEQ R0, R0, #0x40 \n" + "MRS R1, MSP \n" // `msp` argument + "MOV R2, LR \n" // `exc_return` argument + "MOV R3, R4 \n" // 'r4' argument + "PUSH {R5, R6} \n" // 'r5' and 'r6' arguments on stack + "BL svc_handler \n" + "POP {R5, R6} \n" + "BX R0 \n" // Branch to the returned value + ); +} + +__attribute__((naked, no_stack_protector)) void HardFault_Handler(void) { + // A HardFault may also be caused by exception escalation. + // To ensure we have enough space to handle the exception, + // we set the stack pointer to the end of the stack. + + __asm__ volatile( + "MRS R1, MSP \n" // R1 = MSP + "LDR R0, =%[estack] \n" // Reset main stack + "MSR MSP, R0 \n" // + "MOV R0, #1 \n" // R0 = 1 (Privileged) + "B systask_exit_fault \n" // Exit task with fault + : + : [estack] "i"(&_estack) + : "memory"); +} + +__attribute__((naked, no_stack_protector)) void MemManage_Handler(void) { + __asm__ volatile( + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + "ITTEE EQ \n" + "MOVEQ R0, #1 \n" // R0 = 1 (Privileged) + "MRSEQ R1, MSP \n" // R1 = MSP + "MOVNE R0, #0 \n" // R0 = 0 (Unprivileged) + "MRSNE R1, PSP \n" // R1 = PSP +#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)) + "CMP R0, #0 \n" + "BEQ 1f \n" // Skip stack ptr checking for PSP + "LDR R2, =%[sstack] \n" + "CMP R1, R2 \n" // Check if PSP is below the stack + "ITT LO \n" // base + "LDRLO R2, =%[estack] \n" + "MSRLO MSP, R2 \n" // Reset MSP + "1: \n" +#endif + "B systask_exit_fault \n" // Exit task with fault + : + : [estack] "i"(&_estack), [sstack] "i"((uint32_t)&_sstack + 256) + : "memory"); +} + +__attribute__((naked, no_stack_protector)) void BusFault_Handler(void) { + __asm__ volatile( + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + "ITTEE EQ \n" + "MOVEQ R0, #1 \n" // R0 = 1 (Privileged) + "MRSEQ R1, MSP \n" // R1 = MSP + "MOVNE R0, #0 \n" // R0 = 0 (Unprivileged) + "MRSNE R1, PSP \n" // R1 = PSP + "B systask_exit_fault \n" // Exit task with fault + ); +} + +__attribute__((naked, no_stack_protector)) void UsageFault_Handler(void) { + __asm__ volatile( + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + "ITTT NE \n" + "MOVNE R0, #0 \n" // R0 = 0 (Unprivileged) + "MRSNE R1, PSP \n" // R1 = PSP + "BNE systask_exit_fault \n" // Exit task with fault +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "MRS R1, MSP \n" // R1 = MSP + "LDR R0, =0xE000ED28 \n" // SCB->CFSR + "LDR R0, [R0] \n" + "TST R0, #0x100000 \n" // STKOF bit set? + "ITT NE \n" + "LDRNE R0, =%[estack] \n" // Reset main stack in case of stack + "MSRNE MSP, R0 \n" // overflow +#endif + "MOV R0, #1 \n" // R0 = 1 (Privileged) + "B systask_exit_fault \n" // Exit task with fault + : + : [estack] "i"(&_estack) + : "memory"); +} + +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) +__attribute__((naked, no_stack_protector)) void SecureFault_Handler(void) { + __asm__ volatile( + "TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP) + "ITTEE EQ \n" + "MOVEQ R0, #1 \n" // R0 = 1 (Privileged) + "MRSEQ R1, MSP \n" // R1 = MSP + "MOVNE R0, #0 \n" // R0 = 0 (Unprivileged) + "MRSNE R1, PSP \n" // R1 = PSP + "B systask_exit_fault \n" // Exit task with fault + ); +} +#endif + +#ifdef STM32U5 +void GTZC_IRQHandler(void) { systask_exit_fault(true, __get_MSP()); } +#endif + +void NMI_Handler(void) { + mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); +#ifdef STM32U5 + if ((RCC->CIFR & RCC_CIFR_CSSF) != 0) { +#else + if ((RCC->CIR & RCC_CIR_CSSF) != 0) { +#endif + // Clock Security System triggered NMI + systask_exit_fault(true, __get_MSP()); + } + mpu_restore(mpu_mode); +} + +// from util.s +extern void shutdown_privileged(void); + +void PVD_PVM_IRQHandler(void) { + mpu_reconfig(MPU_MODE_DEFAULT); +#ifdef BACKLIGHT_PWM_TIM + // Turn off display backlight + BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = 0; +#endif + shutdown_privileged(); +} + +#endif // KERNEL_MODE diff --git a/core/embed/trezorhal/stm32f4/system.c b/core/embed/trezorhal/stm32f4/system.c new file mode 100644 index 000000000..351c359b0 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/system.c @@ -0,0 +1,310 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include STM32_HAL_H + +#include "system.h" +#include "bootutils.h" +#include "mpu.h" +#include "systask.h" +#include "systick.h" +#include "systimer.h" + +#ifdef KERNEL_MODE + +void system_init(systask_error_handler_t error_handler) { + mpu_init(); + mpu_reconfig(MPU_MODE_DEFAULT); + systask_scheduler_init(error_handler); + systick_init(); + systimer_init(); +} + +void system_exit(int exitcode) { systask_exit(NULL, exitcode); } + +void system_exit_error(const char* title, const char* message, + const char* footer) { + systask_exit_error(NULL, title, message, footer); +} + +void system_exit_fatal(const char* message, const char* file, int line) { + systask_exit_fatal(NULL, message, file, line); +} + +#endif // KERNEL_MODE + +#ifndef HardFault_IRQn +#define HardFault_IRQn (-13) // not defined in stm32lib/cmsis/stm32429xx.h +#endif + +#ifdef STM32U5 +const char* system_fault_message(const system_fault_t* fault) { + switch (fault->irqn) { + case HardFault_IRQn: + return "(HF)"; + case MemoryManagement_IRQn: + return "(MM)"; + case BusFault_IRQn: + return "(BF)"; + case UsageFault_IRQn: + return (fault->cfsr & SCB_CFSR_STKOF_Msk) ? "(SO)" : "(UF)"; + case SecureFault_IRQn: + return "(SF)"; + case GTZC_IRQn: + return "(IA)"; + case NonMaskableInt_IRQn: + return "(CS)"; + default: + return "(FAULT)"; + } +} +#else // STM32U5 +const char* system_fault_message(const system_fault_t* fault) { + switch (fault->irqn) { + case HardFault_IRQn: + return "(HF)"; + case MemoryManagement_IRQn: + return (fault->sp < fault->sp_lim) ? "(SO)" : "(MM)"; + case BusFault_IRQn: + return "(BF)"; + case UsageFault_IRQn: + return "(UF)"; + case NonMaskableInt_IRQn: + return "(CS)"; + default: + return "(FAULT)"; + } +} +#endif // STM32U5 + +__attribute__((used)) static void emergency_reset(void) { + // TODO: reset peripherals (at least DMA, DMA2D) + + // Disable all NVIC interrupts and clear pending flags + // so later the global interrupt can be re-enabled without + // firing any pending interrupt + for (int irqn = 0; irqn < 255; irqn++) { + NVIC_DisableIRQ(irqn); + NVIC_ClearPendingIRQ(irqn); + } + + // Disable SysTick + SysTick->CTRL = 0; + + // Clear PENDSV flag to prevent the PendSV_Handler call + SCB->ICSR &= ~SCB_ICSR_PENDSVSET_Msk; + + // Clear SCB->SHCSR exception flags so we can return back + // to thread mode without any exception active + + uint32_t preserved_flag = 0; + + switch ((__get_IPSR() & IPSR_ISR_Msk) - 16) { + case MemoryManagement_IRQn: + preserved_flag = SCB_SHCSR_MEMFAULTACT_Msk; + break; + case BusFault_IRQn: + preserved_flag = SCB_SHCSR_BUSFAULTACT_Msk; + break; + case UsageFault_IRQn: + preserved_flag = SCB_SHCSR_USGFAULTACT_Msk; + break; + case PendSV_IRQn: + preserved_flag = SCB_SHCSR_PENDSVACT_Msk; + break; + case SysTick_IRQn: + preserved_flag = SCB_SHCSR_SYSTICKACT_Msk; + break; + case SVCall_IRQn: + preserved_flag = SCB_SHCSR_SVCALLACT_Msk; + break; + case HardFault_IRQn: + default: + break; + } + + const uint32_t cleared_flags = + SCB_SHCSR_MEMFAULTACT_Msk | SCB_SHCSR_BUSFAULTACT_Msk | + SCB_SHCSR_USGFAULTACT_Msk | SCB_SHCSR_SVCALLACT_Msk | + SCB_SHCSR_MONITORACT_Msk | SCB_SHCSR_PENDSVACT_Msk | + SCB_SHCSR_SYSTICKACT_Msk; + + SCB->SHCSR &= ~(cleared_flags & ~preserved_flag); +} + +__attribute((naked, no_stack_protector)) void system_emergency_rescue( + systask_error_handler_t error_handler, const systask_postmortem_t* pminfo) { + extern uint32_t __stack_chk_guard; + + __asm__ volatile( + "MOV R5, R1 \n" // R5 = pminfo + "MOV R6, R0 \n" // R6 = error_handler + + "CPSID I \n" // Disable interrupts + + // -------------------------------------------------------------- + // Disable MPU + // -------------------------------------------------------------- + + "DMB 0xF \n" // Data memory barrier + "LDR R0, =0xE000ED94 \n" // MPU->CTRL + "MOV R1, #0 \n" + "STR R1, [R0] \n" // Disable MPU + + // -------------------------------------------------------------- + // Setup new stack + // -------------------------------------------------------------- + + "LDR R0, =_estack \n" // Setup new stack + "MSR MSP, R0 \n" // Set MSP +#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__) + "LDR R0, =_sstack \n" + "ADD R0, R0, #256 \n" // Add safety margin + "MSR MSPLIM, R0 \n" // Set MSPLIM +#endif + + // -------------------------------------------------------------- + // Copy pminfo to new stack + // -------------------------------------------------------------- + + "LDR R2, =%[PMINFO_SIZE] \n" // Copy pminfo to new stack + "SUB SP, R2 \n" // Allocate space for pminfo + "MOV R0, SP \n" // Destination + "MOV R1, R5 \n" // Source + "MOV R5, R0 \n" // R5 = pminfo on the new stack + "BL memcpy \n" + + // -------------------------------------------------------------- + // Save stack protector guard + // -------------------------------------------------------------- + + "LDR R0, =%[STK_GUARD] \n" // Save stack protector guard + "LDR R7, [R0] \n" // R7 = __stack_chk_guard + + // -------------------------------------------------------------- + // Clear .bss, initialize .data, ... + // -------------------------------------------------------------- + + "LDR R0, =bss_start \n" // Clear .bss + "MOV R1, #0 \n" + "LDR R2, =bss_end \n" + "SUB R2, R2, R0 \n" + "BL memset \n" + + "LDR R0, =data_vma \n" // Initialize .data + "LDR R1, =data_lma \n" + "LDR R2, =data_size \n" + "BL memcpy \n" + +#ifdef STM32U5 + "LDR R0, =confidential_vma \n" // Initialize .confidental + "LDR R1, =confidential_lma \n" + "LDR R2, =confidential_size \n" + "BL memcpy \n" +#endif + + // -------------------------------------------------------------- + // Restore stack protector guard + // -------------------------------------------------------------- + + "LDR R0, =%[STK_GUARD] \n" // Restore stack protector guard + "STR R7, [R0] \n" + + // -------------------------------------------------------------- + // Reset critical hardware so we can safely enable interrupts + // -------------------------------------------------------------- + + "BL emergency_reset \n" + + "CPSIE I \n" // Re-enable interrupts + + // -------------------------------------------------------------- + // Clear all VFP registers + // -------------------------------------------------------------- + + "LDR R1, = 0xE000EF34 \n" // FPU->FPCCR + "LDR R0, [R1] \n" + "BIC R0, R0, #1 \n" // Clear LSPACT to suppress lazy + // stacking + "STR R0, [R1] \n" + + // TODO: clear VFP registers (maybe for ARMV7-M only) + + // -------------------------------------------------------------- + // Clear R7-R11 registers + // -------------------------------------------------------------- + + "MOV R0, #0 \n" + "MOV R7, R0 \n" + "MOV R8, R0 \n" + "MOV R9, R0 \n" + "MOV R10, R0 \n" + "MOV R11, R0 \n" + + // -------------------------------------------------------------- + // Check if we are in thread mode and if yes, jump to error_handler + // -------------------------------------------------------------- + + "LDR R1, =0x1FF \n" // Get lower 9 bits of IPSR + "MRS R0, IPSR \n" + "ANDS R0, R0, R1 \n" + "CMP R0, #0 \n" // == 0 if in thread mode + "ITTT EQ \n" + "MOVEQ R0, R5 \n" // R0 = pminfo + "LDREQ LR, =secure_shutdown\n" + "BXEQ R6 \n" // jump to error_handler directly + + // -------------------------------------------------------------- + // Return from exception to thread mode + // -------------------------------------------------------------- + + "MOV R0, SP \n" // Align stack pointer to 8 bytes + "AND R0, R0, #~7 \n" + "MOV SP, R0 \n" + "SUB SP, SP, #32 \n" // Allocate space for the stack frame + + "MOV R0, #0 \n" + "STR R5, [SP, #0] \n" // future R0 = pminfo + "STR R0, [SP, #4] \n" // future R1 = 0 + "STR R0, [SP, #8] \n" // future R2 = 0 + "STR R0, [SP, #12] \n" // future R3 = 0 + "STR R0, [SP, #16] \n" // future R12 = 0 + "LDR R1, =secure_shutdown\n" + "STR R0, [SP, #20] \n" // future LR = secure_shutdown() + "BIC R6, R6, #1 \n" + "STR R6, [SP, #24] \n" // return address = error_handler() + "LDR R1, = 0x01000000 \n" // THUMB bit set + "STR R1, [SP, #28] \n" // future xPSR + + "MOV R4, R0 \n" // Clear registers R4-R6 + "MOV R5, R0 \n" // (R7-R11 are already cleared) + "MOV R6, R0 \n" + + "MRS R0, CONTROL \n" // Clear SPSEL to use MSP for thread + "BIC R0, R0, #3 \n" // Clear nPRIV to run in privileged mode + "MSR CONTROL, R1 \n" + + "LDR LR, = 0xFFFFFFF9 \n" // Return to Secure Thread mode, use MSP + "BX LR \n" + : // no output + : [PMINFO_SIZE] "i"(sizeof(systask_postmortem_t)), + [STK_GUARD] "i"(&__stack_chk_guard) + : // no clobber + ); +} diff --git a/core/embed/trezorhal/stm32f4/util.S b/core/embed/trezorhal/stm32f4/util.S index defc3c70f..94eae168a 100644 --- a/core/embed/trezorhal/stm32f4/util.S +++ b/core/embed/trezorhal/stm32f4/util.S @@ -112,16 +112,4 @@ shutdown_privileged: ldr r0, =0 b . // loop forever - .global MemManage_Handler - .type MemManage_Handler, STT_FUNC -MemManage_Handler: - ldr r2, =_sstack - mrs r1, msp - ldr r0, =_estack - msr msp, r0 - cmp r1, r2 - IT lt - bllt MemManage_Handler_SO - bl MemManage_Handler_MM - .end diff --git a/core/embed/trezorhal/stm32f4/vectortable.S b/core/embed/trezorhal/stm32f4/vectortable.S index 1cfec8c26..2f07d34fd 100644 --- a/core/embed/trezorhal/stm32f4/vectortable.S +++ b/core/embed/trezorhal/stm32f4/vectortable.S @@ -132,7 +132,8 @@ vector_table: .section .vector_table, "a" vector_table: - .word main_stack_base // defined in linker script + .word _sstack + .word _stack_size .word reset_handler diff --git a/core/embed/trezorhal/stm32u5/applet.c b/core/embed/trezorhal/stm32u5/applet.c new file mode 120000 index 000000000..0cdabf36a --- /dev/null +++ b/core/embed/trezorhal/stm32u5/applet.c @@ -0,0 +1 @@ +../stm32f4/applet.c \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/fault_handlers.c b/core/embed/trezorhal/stm32u5/fault_handlers.c deleted file mode 100644 index 2e4bbc78f..000000000 --- a/core/embed/trezorhal/stm32u5/fault_handlers.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "common.h" -#include "mpu.h" - -#ifdef KERNEL_MODE - -void fault_handlers_init(void) { - // Enable BUS fault and USAGE fault handlers - SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk); -} - -void HardFault_Handler(void) { - // A HardFault may also be caused by exception escalation. - // To ensure we have enough space to handle the exception, - // we set the stack pointer to the end of the stack. - extern uint8_t _estack; // linker script symbol - // Fix stack pointer - __set_MSP((uint32_t)&_estack); - - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(HF)"); -} - -void MemManage_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(MM)"); -} - -void BusFault_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(BF)"); -} - -void UsageFault_Handler(void) { - if (SCB->CFSR & SCB_CFSR_STKOF_Msk) { - // Stack overflow - extern uint8_t _estack; // linker script symbol - // Fix stack pointer - __set_MSP((uint32_t)&_estack); - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(SO)"); - } else { - // Other error - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(UF)"); - } -} - -void SecureFault_Handler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(SF)"); -} - -void GTZC_IRQHandler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - error_shutdown("(IA)"); -} - -void NMI_Handler(void) { - mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); - // Clock Security System triggered NMI - if ((RCC->CIFR & RCC_CIFR_CSSF) != 0) { - error_shutdown("(CS)"); - } - mpu_restore(mpu_mode); -} - -// from util.s -extern void shutdown_privileged(void); - -void PVD_PVM_IRQHandler(void) { - mpu_reconfig(MPU_MODE_DEFAULT); - TIM1->CCR1 = 0; // turn off display backlight - shutdown_privileged(); -} - -#endif // KERNEL_MODE diff --git a/core/embed/trezorhal/stm32u5/systask.c b/core/embed/trezorhal/stm32u5/systask.c new file mode 120000 index 000000000..7b6c139ae --- /dev/null +++ b/core/embed/trezorhal/stm32u5/systask.c @@ -0,0 +1 @@ +../stm32f4/systask.c \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/system.c b/core/embed/trezorhal/stm32u5/system.c new file mode 120000 index 000000000..36a84d685 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/system.c @@ -0,0 +1 @@ +../stm32f4/system.c \ No newline at end of file diff --git a/core/embed/trezorhal/stm32u5/vectortable.S b/core/embed/trezorhal/stm32u5/vectortable.S index 472414cae..d4f79d195 100644 --- a/core/embed/trezorhal/stm32u5/vectortable.S +++ b/core/embed/trezorhal/stm32u5/vectortable.S @@ -180,7 +180,8 @@ vector_table: .section .vector_table, "a" vector_table: - .word main_stack_base // defined in linker script + .word _sstack + .word _stack_size .word reset_handler #endif diff --git a/core/embed/trezorhal/systask.h b/core/embed/trezorhal/systask.h new file mode 100644 index 000000000..90170fdaf --- /dev/null +++ b/core/embed/trezorhal/systask.h @@ -0,0 +1,182 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TREZORHAL_SYSTASK_H +#define TREZORHAL_SYSTASK_H + +#include +#include +#include + +#include "mpu.h" + +// Termination reason for the task +typedef enum { + TASK_TERM_REASON_EXIT = 0, + TASK_TERM_REASON_ERROR, + TASK_TERM_REASON_FATAL, + TASK_TERM_REASON_FAULT, + +} systask_term_reason_t; + +typedef struct { + // Fault/exception number (-15..-1) + int irqn; + // Configurable Fault Status Register + // (combined UFSR/BFSR/MMFSR) + uint32_t cfsr; + // Hard Fault Status Register + uint32_t hfsr; + // Address associated with MemManage fault + uint32_t mmfar; + // Address associated with the BusFault + uint32_t bfar; + // Stack pointer at the time of the fault + // (MSP or PSP depending on the privilege level) + uint32_t sp; +#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)) + // Stack pointer limit (for the stack overflow detection) + uint32_t sp_lim; +#endif + +} system_fault_t; + +// Task post-mortem information +typedef struct { + // Reason for the task termination + systask_term_reason_t reason; + // Whether the error occurred in privileged mode + bool privileged; + + union { + // Argument passed to `systask_exit()` + struct { + int code; + } exit; + + // Fault information catched in `systask_exit_fault()` + system_fault_t fault; + + // Arguments passed to `systask_exit_fatal()` + struct { + uint32_t line; + char file[64]; + char expr[64]; + } fatal; + + // Arguments passed to `systask_exit_error()` + struct { + char title[64]; + char message[64]; + char footer[64]; + } error; + }; + +} systask_postmortem_t; + +// Error handler callback invoke when kernel task terminates. +// +// The purpose of this callbacks display RSOD (Red Screen of Death). +// +// The callback may be called from any context, including interrupt context. +typedef void (*systask_error_handler_t)(const systask_postmortem_t* pminfo); + +#ifdef KERNEL_MODE + +// Task context used by the kernel to save the state of each task +// when switching between them +typedef struct { + // `sp`, `sp_lim`, `exc_return` and `killed` should at the beginning + // and in this order to be compatible with the PendSV_Handler + // Stack pointer value + uint32_t sp; + // Stack pointer limit (ARMv8-M only) + uint32_t sp_lim; + // Exception return value + uint32_t exc_return; + // Set to nonzero, if the task is killed + uint32_t killed; + + // MPU mode the task is running in + mpu_mode_t mpu_mode; + // Task post-mortem information + systask_postmortem_t pminfo; + +} systask_t; + +// Initializes the scheduler for tasks +// +// No other task functions should be called before this function +void systask_scheduler_init(systask_error_handler_t error_handler); + +// Returns the currently running task +systask_t* systask_active(void); + +// Makes the given task the currently running task +void systask_yield_to(systask_t* task); + +// Initializes a task with the given stack pointer, stack size +// +// The task must be not be running when the function is called +void systask_init(systask_t* task, uint32_t stack_ptr, uint32_t stack_size); + +// Pushes data onto the stack of the task +// +// The task must be not be running when the function is called +uint32_t* systask_push_data(systask_t* task, const void* data, size_t size); + +// Pops data from the stack of the task +// +// The task must be not be running when the function is called +void systask_pop_data(systask_t* task, size_t size); + +// Runs the task with the given entrypoint and arguments +// +// The task must be not be running when the function is called +// Return `true` in case of success, `false` otherwise +bool systask_push_call(systask_t* task, void* fn, uint32_t arg1, uint32_t arg2, + uint32_t arg3); + +// Terminates the task with the given exit code +// +// If the task is not specified (NULL), it's automatically determined: +// 1) If the function is called in thread mode, the active task will be +// terminated. +// 2) If the function is called in handler mode, the kernel task will be +// terminated even if it is not the active task. +// +// If the terminated task is unprivileged, the kernel task will be scheduled +// next. +void systask_exit(systask_t* task, int exit_code); + +// Terminates the task with an error message +// +// (see `systask_exit()` for more details) +void systask_exit_error(systask_t* task, const char* title, const char* message, + const char* footer); + +// Terminates the task with a fatal error message +// +// (see `systask_exit()` for more details) +void systask_exit_fatal(systask_t* task, const char* message, const char* file, + int line); + +#endif // KERNEL_MODE + +#endif // TREZORHAL_SYSTASK_H diff --git a/core/embed/trezorhal/system.h b/core/embed/trezorhal/system.h new file mode 100644 index 000000000..074181112 --- /dev/null +++ b/core/embed/trezorhal/system.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TREZORHAL_SYSTEM_H +#define TREZORHAL_SYSTEM_H + +#include + +// Initializes the fundamental system services +// (MPU, SysTick, systimer and task scheduler). +// +// `error_handler` is a callback that is called when a kernel task terminates +// with an error +void system_init(systask_error_handler_t error_handler); + +// Terminates the current task normally with the given exit code. +// +// If the current task is the kernel task, the error handler is called with the +// postmortem information. If the task is not the kernel task, the task is +// terminated immediately and the kernel task is scheduled. +void system_exit(int exitcode); + +// Terminates the current task with an error message. +// +// See the notes for `system_exit` regarding the behavior of the error handler +void system_exit_error(const char* title, const char* message, + const char* footer); + +// Terminates the current task with a fatal error message. +// +// See the notes for `system_exit` regarding the behavior of the error handler +void system_exit_fatal(const char* message, const char* file, int line); + +// Returns string representation of the system fault. +const char* system_fault_message(const system_fault_t* fault); + +// Calls the error handler in the emergency mode. +// +// This function is called when the system encounters a critical error +// and needs to perform a useful action (such as displaying an error message) +// before it is reset or shut down. +// +// The function may be called from any context, including interrupt context. +// It completely resets stack pointers, clears the .bss segment, reinitializes +// the .data segment, and calls the `error_handler` callback. +// +// The system will be in a state similar to a reset when `main()` is called +// (but with some hardware peripherals still initialized and running). +__attribute__((noreturn)) void system_emergency_rescue( + systask_error_handler_t error_handler, const systask_postmortem_t* pminfo); + +#endif // TREZORHAL_SYSTEM_H diff --git a/core/embed/trezorhal/unix/system.c b/core/embed/trezorhal/unix/system.c new file mode 100644 index 000000000..728bce47e --- /dev/null +++ b/core/embed/trezorhal/unix/system.c @@ -0,0 +1,107 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "bootutils.h" +#include "system.h" +#include "systick.h" +#include "systimer.h" + +systask_error_handler_t g_error_handler = NULL; + +void system_init(systask_error_handler_t error_handler) { + g_error_handler = error_handler; + systick_init(); + systimer_init(); +} + +void system_exit(int exitcode) { + if (g_error_handler != NULL) { + systask_postmortem_t pminfo = {0}; + + pminfo.reason = TASK_TERM_REASON_EXIT; + pminfo.exit.code = exitcode; + + if (g_error_handler != NULL) { + g_error_handler(&pminfo); + } + } + + secure_shutdown(); +} + +void system_exit_error(const char* title, const char* message, + const char* footer) { + fprintf(stderr, "ERROR: %s\n", message); + fflush(stderr); + + if (g_error_handler != NULL) { + systask_postmortem_t pminfo = {0}; + + pminfo.reason = TASK_TERM_REASON_ERROR; + strncpy(pminfo.error.title, title, sizeof(pminfo.error.title) - 1); + strncpy(pminfo.error.message, message, sizeof(pminfo.error.message) - 1); + strncpy(pminfo.error.footer, footer, sizeof(pminfo.error.footer) - 1); + + if (g_error_handler != NULL) { + g_error_handler(&pminfo); + } + } + + secure_shutdown(); +} + +void system_exit_fatal(const char* message, const char* file, int line) { + fprintf(stderr, "ERROR: %s\n", message); + if (file) { + fprintf(stderr, "FILE: %s:%d\n", file, line); + } + fflush(stderr); + + if (g_error_handler != NULL) { + systask_postmortem_t pminfo = {0}; + + pminfo.reason = TASK_TERM_REASON_FATAL; + strncpy(pminfo.fatal.file, file, sizeof(pminfo.fatal.file) - 1); + strncpy(pminfo.fatal.expr, message, sizeof(pminfo.fatal.expr) - 1); + pminfo.fatal.line = line; + + if (g_error_handler != NULL) { + g_error_handler(&pminfo); + } + } + + secure_shutdown(); +} + +const char* system_fault_message(const system_fault_t* fault) { + // Not used in simulator + return "(FAULT)"; +} + +void system_emergency_rescue(systask_error_handler_t error_handler, + const systask_postmortem_t* pminfo) { + error_handler(pminfo); + + // We should never reach this point + exit(0); +} diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py index c38b12e01..e8e4cb1cd 100644 --- a/core/site_scons/models/stm32f4_common.py +++ b/core/site_scons/models/stm32f4_common.py @@ -40,11 +40,11 @@ def stm32f4_common_files(env, defines, sources, paths): ] sources += [ + "embed/trezorhal/stm32f4/applet.c", "embed/trezorhal/stm32f4/board_capabilities.c", "embed/trezorhal/stm32f4/bootutils.c", "embed/trezorhal/stm32f4/common.c", "embed/trezorhal/stm32f4/entropy.c", - "embed/trezorhal/stm32f4/fault_handlers.c", "embed/trezorhal/stm32f4/flash.c", "embed/trezorhal/stm32f4/flash_otp.c", "embed/trezorhal/stm32f4/fwutils.c", @@ -56,6 +56,8 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/syscall.c", "embed/trezorhal/stm32f4/syscall_dispatch.c", "embed/trezorhal/stm32f4/syscall_stubs.c", + "embed/trezorhal/stm32f4/system.c", + "embed/trezorhal/stm32f4/systask.c", "embed/trezorhal/stm32f4/systick.c", "embed/trezorhal/stm32f4/systimer.c", "embed/trezorhal/stm32f4/time_estimate.c", diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py index 0eea378cf..c62c066fe 100644 --- a/core/site_scons/models/stm32u5_common.py +++ b/core/site_scons/models/stm32u5_common.py @@ -49,11 +49,11 @@ def stm32u5_common_files(env, defines, sources, paths): ] sources += [ + "embed/trezorhal/stm32u5/applet.c", "embed/trezorhal/stm32u5/board_capabilities.c", "embed/trezorhal/stm32u5/bootutils.c", "embed/trezorhal/stm32u5/common.c", "embed/trezorhal/stm32u5/entropy.c", - "embed/trezorhal/stm32u5/fault_handlers.c", "embed/trezorhal/stm32u5/flash.c", "embed/trezorhal/stm32u5/flash_otp.c", "embed/trezorhal/stm32u5/fwutils.c", @@ -67,6 +67,8 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/trezorhal/stm32u5/syscall.c", "embed/trezorhal/stm32u5/syscall_dispatch.c", "embed/trezorhal/stm32u5/syscall_stubs.c", + "embed/trezorhal/stm32u5/system.c", + "embed/trezorhal/stm32u5/systask.c", "embed/trezorhal/stm32u5/systick.c", "embed/trezorhal/stm32u5/systimer.c", "embed/trezorhal/stm32u5/random_delays.c",