From a3cdadf96ce6970b99249ba879572a8553abf1ae Mon Sep 17 00:00:00 2001 From: cepetr Date: Tue, 14 Jan 2025 11:16:57 +0100 Subject: [PATCH] feat(core): implement rebooting after rsod, simplify shutdown code [no changelog] --- .../io/display/ltdc_dsi/display_driver.c | 9 - core/embed/projects/kernel/main.c | 14 +- core/embed/sys/startup/inc/sys/bootutils.h | 14 +- core/embed/sys/startup/stm32/bootutils.c | 69 +++-- .../sys/startup/stm32/bootutils_helpers.S | 269 ++++++++++++++++++ .../sys/startup/stm32/bootutils_helpers.h | 39 +++ core/embed/sys/startup/stm32f4/limited_util.S | 84 ------ core/embed/sys/startup/stm32f4/startup_init.c | 20 -- .../sys/startup/stm32f4/startup_stage_0.s | 2 +- core/embed/sys/startup/stm32f4/util.S | 97 ------- core/embed/sys/startup/stm32u5/limited_util.S | 82 ------ core/embed/sys/startup/stm32u5/util.S | 86 ------ core/embed/sys/startup/unix/bootutils.c | 12 +- core/embed/sys/task/stm32/systask.c | 5 +- core/embed/sys/task/stm32/system.c | 1 + core/embed/sys/task/unix/system.c | 12 +- core/embed/util/rsod/rsod.c | 5 +- core/site_scons/models/T3W1/__init__.py | 2 +- core/site_scons/models/stm32f4_common.py | 13 +- core/site_scons/models/stm32u5_common.py | 13 +- 20 files changed, 401 insertions(+), 447 deletions(-) create mode 100644 core/embed/sys/startup/stm32/bootutils_helpers.S create mode 100644 core/embed/sys/startup/stm32/bootutils_helpers.h delete mode 100644 core/embed/sys/startup/stm32f4/limited_util.S delete mode 100644 core/embed/sys/startup/stm32f4/util.S delete mode 100644 core/embed/sys/startup/stm32u5/limited_util.S delete mode 100644 core/embed/sys/startup/stm32u5/util.S diff --git a/core/embed/io/display/ltdc_dsi/display_driver.c b/core/embed/io/display/ltdc_dsi/display_driver.c index 3f21d1f46b4..9fe5bad48ae 100644 --- a/core/embed/io/display/ltdc_dsi/display_driver.c +++ b/core/embed/io/display/ltdc_dsi/display_driver.c @@ -425,15 +425,6 @@ bool display_init(display_content_mode_t mode) { void display_deinit(display_content_mode_t mode) { display_driver_t *drv = &g_display_driver; - if (mode == DISPLAY_RETAIN_CONTENT) { - // This is a temporary workaround for T3W1 to avoid clearing - // the display after drawing RSOD screen in `secure_shutdown()` - // function. The workaround should be removed once we have - // proper replacement for `secure_shutdown()` that resets the - // device instead of waiting for manual power off. - return; - } - GPIO_InitTypeDef GPIO_InitStructure = {0}; NVIC_DisableIRQ(LTDC_IRQn); diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c index e4ff5ed0e7d..4c4fab480ff 100644 --- a/core/embed/projects/kernel/main.c +++ b/core/embed/projects/kernel/main.c @@ -222,8 +222,8 @@ static void show_rsod(const systask_postmortem_t *pminfo) { applet_run(&coreapp); if (coreapp.task.pminfo.reason == TASK_TERM_REASON_EXIT) { - // If the RSOD was shown successfully, proceed to shutdown - secure_shutdown(); + // RSOD was shown successfully + return; } } #endif @@ -245,8 +245,9 @@ static void init_and_show_rsod(const systask_postmortem_t *pminfo) { // Show RSOD show_rsod(pminfo); - // Wait for the user to manually power off the device - secure_shutdown(); + // Wait for the user to read the RSOD and then reboots + // (or enters an infinite loop if RSOD_INFINITE_LOOP is defined) + reboot_after_rsod(); } // Kernel panic handler @@ -285,8 +286,9 @@ int main(void) { // Coreapp crashed, show RSOD show_rsod(&coreapp.task.pminfo); - // Wait for the user to manually power off the device - secure_shutdown(); + // Wait for the user to read the RSOD and then reboots + // (or enters an infinite loop if RSOD_INFINITE_LOOP is defined) + reboot_after_rsod(); return 0; } diff --git a/core/embed/sys/startup/inc/sys/bootutils.h b/core/embed/sys/startup/inc/sys/bootutils.h index 53409dfc879..a7624a885e0 100644 --- a/core/embed/sys/startup/inc/sys/bootutils.h +++ b/core/embed/sys/startup/inc/sys/bootutils.h @@ -38,19 +38,21 @@ void __attribute__((noreturn)) reboot_to_bootloader(void); // with the firmware installation. void __attribute__((noreturn)) reboot_and_upgrade(const uint8_t hash[32]); -// Allows the user to see the displayed error message and then -// safely shuts down the device (clears secrets, memory, etc.). +// Allows the user to read the displayed error message and then +// reboots the device or waits for power-off. // -// This function is called when the device enters an -// unrecoverable error state. -void __attribute__((noreturn)) secure_shutdown(void); +// The function's behavior depends on the `RSOD_INFINITE_LOOP` macro: +// 1) If `RSOD_INFINITE_LOOP` is defined, the function enters an infinite loop. +// 2) If `RSOD_INFINITE_LOOP` is not defined, the function waits for a +// specified duration and then resets the device. +void __attribute__((noreturn)) reboot_after_rsod(void); // Jumps to the next booting stage (e.g. bootloader to firmware). // `vectbl_address` points to the flash at the vector table of the next stage. // // Before jumping, the function disables all interrupts and clears the // memory and registers that could contain sensitive information. -void jump_to_next_stage(uint32_t vectbl_address); +void __attribute__((noreturn)) jump_to_next_stage(uint32_t vectbl_address); // Ensure compatible hardware settings before jumping to // the different booting stage. This function is used to diff --git a/core/embed/sys/startup/stm32/bootutils.c b/core/embed/sys/startup/stm32/bootutils.c index 1b69858749c..b18d2823f84 100644 --- a/core/embed/sys/startup/stm32/bootutils.c +++ b/core/embed/sys/startup/stm32/bootutils.c @@ -26,14 +26,23 @@ #include #include #include +#include #include +#include "bootutils_helpers.h" + #ifdef TREZOR_MODEL_T2T1 #include "../stm32f4/startup_init.h" #endif #ifdef KERNEL_MODE +// Battery powered devices (USE_POWERCTL) should not stall +// after showing RSOD, as it would drain the battery. +#ifndef USE_POWERCTL +#define RSOD_INFINITE_LOOP +#endif + #ifdef STM32U5 // Persistent variable that holds the 'command' for the next reboot. boot_command_t __attribute__((section(".boot_command"))) g_boot_command; @@ -86,7 +95,8 @@ void bootargs_get_args(boot_args_t* dest) { // Deletes all secrets and SRAM2 where stack is located // to prevent stack smashing error, do not return from function calling this #ifdef STM32U5 -static inline void __attribute__((always_inline)) delete_secrets(void) { +static inline void __attribute__((always_inline, no_stack_protector)) +delete_secrets(void) { __disable_irq(); // Disable SAES peripheral clock, so that we don't get tamper events @@ -96,6 +106,28 @@ static inline void __attribute__((always_inline)) delete_secrets(void) { } #endif // STM32U5 +// Clears USB FIFO memory to prevent data leakage of sensitive information +__attribute((used, no_stack_protector)) void clear_usb_fifo_memory(void) { +#ifdef STM32F4 +// reference RM0090 section 35.12.1 Figure 413 +#define USB_OTG_HS_DATA_FIFO_RAM (USB_OTG_HS_PERIPH_BASE + 0x20000U) +#define USB_OTG_HS_DATA_FIFO_SIZE (4096U) + + // use the HAL version due to section 2.1.6 of STM32F42xx Errata sheet + __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); // enable USB_OTG_HS peripheral clock so + // that the peripheral memory is + // accessible + __IO uint32_t* usb_fifo_ram = (__IO uint32_t*)USB_OTG_HS_DATA_FIFO_RAM; + + for (uint32_t i = 0; i < USB_OTG_HS_DATA_FIFO_SIZE / 4; i++) { + usb_fifo_ram[i] = 0; + } + + __HAL_RCC_USB_OTG_HS_CLK_DISABLE(); // disable USB OTG_HS peripheral clock as + // the peripheral is not needed right now +#endif // STM32F4 +} + #ifdef STM32F4 // Ensure that we are running in privileged thread mode. // @@ -162,20 +194,16 @@ reboot_with_args(boot_command_t command, const void* args, size_t args_size) { #ifdef STM32U5 delete_secrets(); - NVIC_SystemReset(); + wipe_all_and_reboot_with_args(); #else display_deinit(DISPLAY_RESET_CONTENT); ensure_compatible_settings(); - mpu_reconfig(MPU_MODE_DISABLED); + clear_usb_fifo_memory(); + mpu_reconfig(MPU_MODE_DISABLED); ensure_thread_mode(); - - // from util.s - extern void jump_to_with_flag(uint32_t address, uint32_t reset_flag); - jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE, g_boot_command); - for (;;) - ; + jump_to_bootloader(BOOTLOADER_START + IMAGE_HEADER_SIZE, g_boot_command); #endif } @@ -188,27 +216,24 @@ void reboot_and_upgrade(const uint8_t hash[32]) { } void reboot_device(void) { - bootargs_set(BOOT_COMMAND_NONE, NULL, 0); - + clear_usb_fifo_memory(); #ifdef STM32U5 delete_secrets(); #endif - - NVIC_SystemReset(); + wipe_all_and_reboot(); } -void __attribute__((noreturn)) secure_shutdown(void) { - display_deinit(DISPLAY_RETAIN_CONTENT); - +void __attribute__((noreturn)) reboot_after_rsod(void) { + clear_usb_fifo_memory(); #ifdef STM32U5 delete_secrets(); #endif - // from util.s - extern void shutdown_privileged(void); - shutdown_privileged(); - - for (;;) - ; +#ifdef RSOD_INFINITE_LOOP + wipe_all_and_stall(); +#else + systick_delay_ms(10 * 1000); + wipe_all_and_reboot(); +#endif } void ensure_compatible_settings(void) { diff --git a/core/embed/sys/startup/stm32/bootutils_helpers.S b/core/embed/sys/startup/stm32/bootutils_helpers.S new file mode 100644 index 00000000000..f28230dc615 --- /dev/null +++ b/core/embed/sys/startup/stm32/bootutils_helpers.S @@ -0,0 +1,269 @@ +/* + * 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 . + */ + + .syntax unified + + .text + +#ifdef KERNEL_MODE + +// --------------------------------------------------------------------------- +// jump_to_vector_table +// +// Jumps to address given in first argument R0 that points +// to the next stage's vector tanle +// +// Inputs: +// R0 - next stage's vector table +// R1 - argument passed to the next stage (set to 0 if not used) +// +// Outputs: +// -- +// +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + +jump_to_vector_table: + + CPSID F // Disable interrupts + + MOV R11, R1 // Argument is passed in R11 + MOV LR, R0 // Save for later use + + LDR R0, =0 // Clear all registers + MOV R1, R0 + MOV R2, R0 + MOV R3, R0 + MOV R4, R0 + MOV R5, R0 + MOV R6, R0 + MOV R7, R0 + MOV R8, R0 + MOV R9, R0 + MOV R10, R0 + MOV R12, R0 + + LDR R0, [LR] // Setup next stage's stack + MSR MSP, R0 + + // point to the next stage's exception handlers + // AN321, section 4.11: "a memory barrier is not required after a VTOR update" + .set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3 + + LDR R0, =SCB_VTOR + STR LR, [R0] + MOV R0, R1 // R0 <- 0 + + // go on to the next stage + LDR LR, [LR, 4] // Next stage's reset handler + BX LR + + +// --------------------------------------------------------------------------- +// jump_to_next_stage +// +// Jumps to the next booting stage (e.g. bootloader to firmware). +// `address` points to the flash at the vector table of the next stage. +// +// Before jumping, the function disables all interrupts and clears the +// memory and registers that could contain sensitive information. +// +// Inputs: +// R0 - next stage's vector table +// +// Outputs: +// -- +// +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + + .global jump_to_next_stage + .type jump_to_next_stage, STT_FUNC + +jump_to_next_stage: + + CPSID F // Disable interrupts + + MOV R10, R0 + + BL clear_usb_fifo_memory + + LDR R2, =0 + BL memory_wipe_exclude_bootargs + + MOV R0, R10 // Address of vectpr table + LDR R1, =0 // Argument passed to the next stage + B jump_to_vector_table + + +// --------------------------------------------------------------------------- +// jump_to_bootloader +// +// Jumps to the bootloader from the firmware. +// +// This functions is used only on STM32F4 based devices, since on STM32U5 +// we use wipe_all_and_reboot_with_args +// +// Before jumping, the function disables all interrupts and clears the +// memory and registers that could contain sensitive information. +// +// Inputs: +// R0 - bootloader's vector table +// R1 - 32-bit argument passed to bootloader +// +// Outputs: +// -- +// +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + + .global jump_to_bootloader + .type jump_to_bootloader, STT_FUNC + +jump_to_bootloader: + + CPSID F // Disable interrupts + + MOV R10, R0 // Save for future use + MOV R11, R1 + + LDR R2, =0 + BL memory_wipe_exclude_bootargs + + MOV R0, R10 // vect_table + MOV R1, R11 // Argument passed to the bootloader + B jump_to_vector_table + + +// --------------------------------------------------------------------------- +// system_reset +// +// Resets Cortex-M by setting SCB->AIRCR register +// +// Inputs: +// -- +// Outputs: +// -- +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + +system_reset: + + DSB + LDR R0, =0xE000ED0C // Address of SCB_AIRCR + LDR R1, =0x05FA0004 // VECTKEY (0x05FA) + SYSRESETREQ (bit 2) + STR R1, [R0] + DSB + + B . // Infinite loop (should not reach here) + + +// --------------------------------------------------------------------------- +// wipe_all_and_reboot +// +// Clears all RAM memory and reboots +// +// Inputs: +// -- +// Outputs: +// -- +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + + .global wipe_all_and_reboot + .type wipe_all_and_reboot, STT_FUNC + +wipe_all_and_reboot: + + CPSID F // Disable interrupts + + LDR R2, =0 + BL memory_wipe_all // Clear all accessible SRAM + + B system_reset // Reset CPU + +// --------------------------------------------------------------------------- +// wipe_all_and_stall +// +// Clears all RAM memory and enters infinite loop +// +// Inputs: +// -- +// Outputs: +// -- +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + + .global wipe_all_and_reboot + .global wipe_all_and_stall + .type wipe_all_and_stall, STT_FUNC + +wipe_all_and_stall: + + CPSID F // Disable interrupts + + LDR R2, =0 + BL memory_wipe_all // Clear all accessible SRAM + + B . // Infinite loop + + + +// --------------------------------------------------------------------------- +// wipe_all_and_reboot_with_args +// +// Clears all RAM memory excluding boot args area and reboots +// +// Inputs: +// -- +// Outputs: +// -- +// Modified registers: +// -- +// +// --------------------------------------------------------------------------- + + .global wipe_all_and_reboot_with_args + .type wipe_all_and_reboot_with_args, STT_FUNC + +wipe_all_and_reboot_with_args: + + CPSID F // Disable interrupts + + LDR R2, =0 + BL memory_wipe_exclude_bootargs + + B system_reset // Reset CPU + + + #endif // KERNEL_MODE + + .end diff --git a/core/embed/sys/startup/stm32/bootutils_helpers.h b/core/embed/sys/startup/stm32/bootutils_helpers.h new file mode 100644 index 00000000000..817017404b7 --- /dev/null +++ b/core/embed/sys/startup/stm32/bootutils_helpers.h @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +#pragma once + +// Wipes all SRAM and other sensitive information from the device and reboots +// it. +void __attribute__((noreturn)) wipe_all_and_reboot(void); + +// Wipes all SRAM and other sensitive information (excluding boot args area) +// from the device and reboots it. +void __attribute__((noreturn)) wipe_all_and_reboot_with_args(void); + +// Wipes all SRAM and other sensitive information from the device and +// enters and infinite loop. +void __attribute__((noreturn)) wipe_all_and_stall(void); + +// Wipe all SRAM and other sensition information from the device and +// jumps directly the the bootloader (without rebooting) +// +// This mechanism is used only on STM32F4 devices. +void __attribute__((noreturn)) +jump_to_bootloader(uint32_t vectbl_address, uint32_t command); diff --git a/core/embed/sys/startup/stm32f4/limited_util.S b/core/embed/sys/startup/stm32f4/limited_util.S deleted file mode 100644 index ddfd1f3096b..00000000000 --- a/core/embed/sys/startup/stm32f4/limited_util.S +++ /dev/null @@ -1,84 +0,0 @@ - .syntax unified - - .text - - .global jump_to - .type jump_to, STT_FUNC -jump_to: - mov r10, r0 // save input argument r0 (the address of the next stage's vector table) (r7 is callee save) - // this subroutine re-points the exception handlers before the C code - // that comprises them has been given a good environment to run. - // therefore, this code needs to disable interrupts before the VTOR - // update. then, the reset_handler of the next stage needs to re-enable interrupts. - // the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI). - // according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8: - // "there is no requirement to insert memory barrier instructions after CPSID". - cpsid f - // wipe memory at the end of the current stage of code - bl clear_otg_hs_memory - - ldr r2, =0 // r2 - the word-sized value to be written - bl memory_wipe_exclude_bootargs - - mov lr, r10 - // clear out the general purpose registers before the next stage's code can run (even the NMI exception handler) - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - // give the next stage a fresh main stack pointer - ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table - msr msp, r0 // give the next stage its main stack pointer - // point to the next stage's exception handlers - // AN321, section 4.11: "a memory barrier is not required after a VTOR update" - .set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3 - ldr r0, =SCB_VTOR - str lr, [r0] - mov r0, r1 // zero out r0 - // go on to the next stage - ldr lr, [lr, 4] // set lr to the next stage's reset_handler - bx lr - - .global shutdown_privileged - .type shutdown_privileged, STT_FUNC - // The function must be called from the privileged mode -shutdown_privileged: - cpsid f // disable all exceptions (except for NMI), the instruction is ignored in unprivileged mode - // if the exceptions weren't disabled, an exception handler (for example systick handler) - // could be called after the memory is erased, which would lead to another exception - - bl clear_otg_hs_memory - - ldr r2, =0 - bl memory_wipe_all - - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - ldr lr, =0xffffffff - - ldr r0, =1 - msr control, r0 // jump to unprivileged mode - ldr r0, =0 - b . // loop forever - - .end diff --git a/core/embed/sys/startup/stm32f4/startup_init.c b/core/embed/sys/startup/stm32f4/startup_init.c index f934632149a..980181cfe4f 100644 --- a/core/embed/sys/startup/stm32f4/startup_init.c +++ b/core/embed/sys/startup/stm32f4/startup_init.c @@ -230,24 +230,4 @@ void set_core_clock(clock_settings_t settings) { } #endif -// reference RM0090 section 35.12.1 Figure 413 -#define USB_OTG_HS_DATA_FIFO_RAM (USB_OTG_HS_PERIPH_BASE + 0x20000U) -#define USB_OTG_HS_DATA_FIFO_SIZE (4096U) - -// Clears USB FIFO memory to prevent data leakage of sensitive information -__attribute((used)) void clear_otg_hs_memory(void) { - // use the HAL version due to section 2.1.6 of STM32F42xx Errata sheet - __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); // enable USB_OTG_HS peripheral clock so - // that the peripheral memory is - // accessible - __IO uint32_t* usb_fifo_ram = (__IO uint32_t*)USB_OTG_HS_DATA_FIFO_RAM; - - for (uint32_t i = 0; i < USB_OTG_HS_DATA_FIFO_SIZE / 4; i++) { - usb_fifo_ram[i] = 0; - } - - __HAL_RCC_USB_OTG_HS_CLK_DISABLE(); // disable USB OTG_HS peripheral clock as - // the peripheral is not needed right now -} - #endif // KERNEL_MODE diff --git a/core/embed/sys/startup/stm32f4/startup_stage_0.s b/core/embed/sys/startup/stm32f4/startup_stage_0.s index 404968fff02..38fd10eb914 100644 --- a/core/embed/sys/startup/stm32f4/startup_stage_0.s +++ b/core/embed/sys/startup/stm32f4/startup_stage_0.s @@ -34,7 +34,7 @@ reset_handler: // Clear the USB FIFO memory to prevent data leakage of sensitive information. // This peripheral memory is not cleared by a device reset. - bl clear_otg_hs_memory + bl clear_usb_fifo_memory // enter the application code bl main diff --git a/core/embed/sys/startup/stm32f4/util.S b/core/embed/sys/startup/stm32f4/util.S deleted file mode 100644 index ced5954865f..00000000000 --- a/core/embed/sys/startup/stm32f4/util.S +++ /dev/null @@ -1,97 +0,0 @@ - .syntax unified - - .text - -#ifdef KERNEL_MODE - - // Jump to address given in first argument R0 that points to next's stage's VTOR - // Clear memory and all registers before jump - .global jump_to - .type jump_to, STT_FUNC -jump_to: - ldr r1, =0 - bl jump_to_with_flag - - // Jump to address given in first argument R0 that points to next's stage's VTOR - // Clear memory and all registers before jump. Second argument R1 is copied to R11 - // and kept after jump. - .global jump_to_with_flag - .type jump_to_with_flag, STT_FUNC -jump_to_with_flag: - mov r10, r0 // save input argument r0 (the address of the next stage's vector table) (r10 is callee save) - mov r11, r1 // save second argument in "flag" register because we'll be cleaning RAM - // this subroutine re-points the exception handlers before the C code - // that comprises them has been given a good environment to run. - // therefore, this code needs to disable interrupts before the VTOR - // update. then, the reset_handler of the next stage needs to re-enable interrupts. - // the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI). - // according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8: - // "there is no requirement to insert memory barrier instructions after CPSID". - cpsid f - // wipe memory at the end of the current stage of code - bl clear_otg_hs_memory - ldr r2, =0 // r2 - the word-sized value to be written - bl memory_wipe_exclude_bootargs - mov lr, r10 - // clear out the general purpose registers before the next stage's except the register with flag R11 - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r12, r0 - // give the next stage a fresh main stack pointer - ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table - msr msp, r0 // give the next stage its main stack pointer - // point to the next stage's exception handlers - // AN321, section 4.11: "a memory barrier is not required after a VTOR update" - .set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3 - ldr r0, =SCB_VTOR - str lr, [r0] - mov r0, r1 // zero out r0 - // go on to the next stage - ldr lr, [lr, 4] // set lr to the next stage's reset_handler - bx lr - - .global shutdown_privileged - .type shutdown_privileged, STT_FUNC - // The function must be called from the privileged mode -shutdown_privileged: - cpsid f // disable all exceptions (except for NMI), the instruction is ignored in unprivileged mode - // if the exceptions weren't disabled, an exception handler (for example systick handler) - // could be called after the memory is erased, which would lead to another exception - - bl clear_otg_hs_memory - - ldr r2, =0 - bl memory_wipe_all - - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - ldr lr, =0xffffffff - - ldr r0, =1 - msr control, r0 // jump to unprivileged mode - ldr r0, =0 - b . // loop forever - -#endif - - .end diff --git a/core/embed/sys/startup/stm32u5/limited_util.S b/core/embed/sys/startup/stm32u5/limited_util.S deleted file mode 100644 index 1b3f63c263a..00000000000 --- a/core/embed/sys/startup/stm32u5/limited_util.S +++ /dev/null @@ -1,82 +0,0 @@ - .syntax unified - - .text - - // Jump to address given in first argument R0 that points to next's stage's VTOR - // Clear memory and all registers before jump - .global jump_to - .type jump_to, STT_FUNC -jump_to: - mov r10, r0 // save input argument r0 (the address of the next stage's vector table) (r10 is callee save) - // this subroutine re-points the exception handlers before the C code - // that comprises them has been given a good environment to run. - // therefore, this code needs to disable interrupts before the VTOR - // update. then, the reset_handler of the next stage needs to re-enable interrupts. - // the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI). - // according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8: - // "there is no requirement to insert memory barrier instructions after CPSID". - cpsid f - // wipe memory at the end of the current stage of code - ldr r2, =0 // r2 - the word-sized value to be written - bl memory_wipe_exclude_bootargs - - mov lr, r10 - // clear out the general purpose registers before the next stage's code can run - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - // give the next stage a fresh main stack pointer - ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table - msr msp, r0 // give the next stage its main stack pointer - // point to the next stage's exception handlers - // AN321, section 4.11: "a memory barrier is not required after a VTOR update" - .set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3 - ldr r0, =SCB_VTOR - str lr, [r0] - mov r0, r1 // zero out r0 - // go on to the next stage - ldr lr, [lr, 4] // set lr to the next stage's reset_handler - bx lr - - .global shutdown_privileged - .type shutdown_privileged, STT_FUNC - // The function must be called from the privileged mode -shutdown_privileged: - cpsid f // disable all exceptions (except for NMI), the instruction is ignored in unprivileged mode - // if the exceptions weren't disabled, an exception handler (for example systick handler) - // could be called after the memory is erased, which would lead to another exception - - ldr r2, =0 - bl memory_wipe_all - - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - ldr lr, =0xffffffff - - ldr r0, =1 - msr control, r0 // jump to unprivileged mode - ldr r0, =0 - b . // loop forever - - .end diff --git a/core/embed/sys/startup/stm32u5/util.S b/core/embed/sys/startup/stm32u5/util.S deleted file mode 100644 index 1b83df5ad34..00000000000 --- a/core/embed/sys/startup/stm32u5/util.S +++ /dev/null @@ -1,86 +0,0 @@ - .syntax unified - - .text - -#ifdef KERNEL_MODE - - // Jump to address given in first argument R0 that points to next's stage's VTOR - // Clear memory and all registers before jump - .global jump_to - .type jump_to, STT_FUNC -jump_to: - mov r10, r0 // save input argument r0 (the address of the next stage's vector table) (r10 is callee save) - // this subroutine re-points the exception handlers before the C code - // that comprises them has been given a good environment to run. - // therefore, this code needs to disable interrupts before the VTOR - // update. then, the reset_handler of the next stage needs to re-enable interrupts. - // the following prevents activation of all exceptions except Non-Maskable Interrupt (NMI). - // according to "ARM Cortex-M Programming Guide to Memory Barrier Instructions" Application Note 321, section 4.8: - // "there is no requirement to insert memory barrier instructions after CPSID". - cpsid f - // wipe memory at the end of the current stage of code - ldr r2, =0 // r2 - the word-sized value to be written - bl memory_wipe_exclude_bootargs - - mov lr, r10 - // clear out the general purpose registers before the next stage's code can run - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - // give the next stage a fresh main stack pointer - ldr r0, [lr] // set r0 to the main stack pointer in the next stage's vector table - msr msp, r0 // give the next stage its main stack pointer - // point to the next stage's exception handlers - // AN321, section 4.11: "a memory barrier is not required after a VTOR update" - .set SCB_VTOR, 0xE000ED08 // reference "Cortex-M4 Devices Generic User Guide" section 4.3 - ldr r0, =SCB_VTOR - str lr, [r0] - mov r0, r1 // zero out r0 - // go on to the next stage - ldr lr, [lr, 4] // set lr to the next stage's reset_handler - bx lr - - .global shutdown_privileged - .type shutdown_privileged, STT_FUNC - // The function must be called from the privileged mode -shutdown_privileged: - cpsid f // disable all exceptions (except for NMI), the instruction is ignored in unprivileged mode - // if the exceptions weren't disabled, an exception handler (for example systick handler) - // could be called after the memory is erased, which would lead to another exception - - ldr r2, =0 - bl memory_wipe_all - - ldr r0, =0 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - ldr lr, =0xffffffff - - ldr r0, =1 - msr control, r0 // jump to unprivileged mode - ldr r0, =0 - b . // loop forever - -#endif - - .end diff --git a/core/embed/sys/startup/unix/bootutils.c b/core/embed/sys/startup/unix/bootutils.c index 12824d0c00a..eb3b65bea22 100644 --- a/core/embed/sys/startup/unix/bootutils.c +++ b/core/embed/sys/startup/unix/bootutils.c @@ -55,12 +55,18 @@ void bootargs_get_args(boot_args_t* dest) { memcpy(dest, &g_boot_args, sizeof(boot_args_t)); } -void __attribute__((noreturn)) secure_shutdown(void) { - printf("SHUTDOWN\n"); +void __attribute__((noreturn)) reboot_device(void) { + printf("reboot (normal)\n"); + + exit(3); +} + +void __attribute__((noreturn)) reboot_after_rsod(void) { + printf("reboot (with timeout)\n"); // Wait some time to let the user see the displayed // message before shutting down - hal_delay(3000); + systick_delay_ms(3000); exit(3); } diff --git a/core/embed/sys/task/stm32/systask.c b/core/embed/sys/task/stm32/systask.c index 2c459b67e65..b310c57059f 100644 --- a/core/embed/sys/task/stm32/systask.c +++ b/core/embed/sys/task/stm32/systask.c @@ -209,7 +209,10 @@ static void systask_kill(systask_t* task) { if (scheduler->error_handler != NULL) { scheduler->error_handler(&task->pminfo); } - secure_shutdown(); + + // We reach this point only if error_handler is NULL or + // if it returns. Neither is expected to happen. + reboot_device(); } else if (task == scheduler->active_task) { // Switch to the kernel task systask_yield_to(&scheduler->kernel_task); diff --git a/core/embed/sys/task/stm32/system.c b/core/embed/sys/task/stm32/system.c index 547ff0a8460..f957e7d4f17 100644 --- a/core/embed/sys/task/stm32/system.c +++ b/core/embed/sys/task/stm32/system.c @@ -257,6 +257,7 @@ __attribute((naked, no_stack_protector)) void system_emergency_rescue( // -------------------------------------------------------------- "BL emergency_reset \n" + "BL clear_usb_fifo_memory \n" "CPSIE I \n" // Re-enable interrupts diff --git a/core/embed/sys/task/unix/system.c b/core/embed/sys/task/unix/system.c index 48157962e9c..96c59853a86 100644 --- a/core/embed/sys/task/unix/system.c +++ b/core/embed/sys/task/unix/system.c @@ -48,7 +48,9 @@ void system_exit(int exitcode) { } } - secure_shutdown(); + // We reach this point only if g_error_handler is NULL or + // if it returns. Neither is expected to happen. + exit(3); } void system_exit_error_ex(const char* title, size_t title_len, @@ -77,7 +79,9 @@ void system_exit_error_ex(const char* title, size_t title_len, } } - secure_shutdown(); + // We reach this point only if g_error_handler is NULL or + // if it returns. Neither is expected to happen. + exit(3); } void system_exit_error(const char* title, const char* message, @@ -117,7 +121,9 @@ void system_exit_fatal_ex(const char* message, size_t message_len, } } - secure_shutdown(); + // We reach this point only if g_error_handler is NULL or + // if it returns. Neither is expected to happen. + exit(3); } void system_exit_fatal(const char* message, const char* file, int line) { diff --git a/core/embed/util/rsod/rsod.c b/core/embed/util/rsod/rsod.c index c23a91744b5..065e0eb1a19 100644 --- a/core/embed/util/rsod/rsod.c +++ b/core/embed/util/rsod/rsod.c @@ -177,8 +177,9 @@ static void init_and_show_rsod(const systask_postmortem_t* pminfo) { rsod_terminal(pminfo); #endif - // Wait for the user to manually power off the device - secure_shutdown(); + // Wait for the user to read the RSOD and then reboots + // (or enters an infinite loop if RSOD_INFINITE_LOOP is defined) + reboot_after_rsod(); } // Universal panic handler diff --git a/core/site_scons/models/T3W1/__init__.py b/core/site_scons/models/T3W1/__init__.py index b1cb7058c90..5e3accc67a8 100644 --- a/core/site_scons/models/T3W1/__init__.py +++ b/core/site_scons/models/T3W1/__init__.py @@ -14,7 +14,7 @@ def configure_board( paths: list[str], ): # Set default revision if None - revision = revision or "A" + revision = revision or "A0" # Mapping of revisions to their respective configurations revision_map = { diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py index ee658a340cd..e8f903831c8 100644 --- a/core/site_scons/models/stm32f4_common.py +++ b/core/site_scons/models/stm32f4_common.py @@ -69,6 +69,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/sys/mpu/stm32f4/mpu.c", "embed/sys/pvd/stm32/pvd.c", "embed/sys/startup/stm32/bootutils.c", + "embed/sys/startup/stm32/bootutils_helpers.S", "embed/sys/startup/stm32/memory_utils.S", "embed/sys/startup/stm32f4/reset_flags.c", "embed/sys/startup/stm32f4/startup_init.c", @@ -93,17 +94,5 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/util/unit_properties/stm32/unit_properties.c", ] - # boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks - # this helps to prevent making a bug in boardloader which may be hard to fix since it's locked with write-protect - env_constraints = env.get("CONSTRAINTS") - if env_constraints and "limited_util_s" in env_constraints: - sources += [ - "embed/sys/startup/stm32f4/limited_util.S", - ] - else: - sources += [ - "embed/sys/startup/stm32f4/util.S", - ] - env.get("ENV")["SUFFIX"] = "stm32f4" env.get("ENV")["LINKER_SCRIPT"] = """embed/sys/linker/stm32f4/{target}.ld""" diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py index e50a5e812a3..c0d6f027e50 100644 --- a/core/site_scons/models/stm32u5_common.py +++ b/core/site_scons/models/stm32u5_common.py @@ -86,6 +86,7 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/sys/mpu/stm32u5/mpu.c", "embed/sys/pvd/stm32/pvd.c", "embed/sys/startup/stm32/bootutils.c", + "embed/sys/startup/stm32/bootutils_helpers.S", "embed/sys/startup/stm32/memory_utils.S", "embed/sys/startup/stm32u5/reset_flags.c", "embed/sys/startup/stm32u5/startup_init.c", @@ -112,16 +113,4 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/util/unit_properties/stm32/unit_properties.c", ] - # boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks - # this helps to prevent making a bug in boardloader which may be hard to fix since it's locked with write-protect - env_constraints = env.get("CONSTRAINTS") - if env_constraints and "limited_util_s" in env_constraints: - sources += [ - "embed/sys/startup/stm32u5/limited_util.S", - ] - else: - sources += [ - "embed/sys/startup/stm32u5/util.S", - ] - env.get("ENV")["SUFFIX"] = "stm32u5"