From e931661d4d27b96f4d449528c4124c1f8ba441ae Mon Sep 17 00:00:00 2001 From: cepetr Date: Fri, 30 Aug 2024 11:12:05 +0200 Subject: [PATCH] refactor(core/embed): simplify bootutils api [no changelog] --- core/embed/bootloader/emulator.c | 4 +- core/embed/bootloader/emulator.h | 2 +- core/embed/bootloader/main.c | 7 +- core/embed/bootloader/messages.c | 2 +- core/embed/bootloader/startup_stm32f4.s | 2 +- core/embed/bootloader/startup_stm32u5.s | 2 +- .../extmod/modtrezorutils/modtrezorutils.c | 28 ++-- core/embed/trezorhal/bootargs.h | 57 +++++++++ core/embed/trezorhal/bootutils.h | 75 ++++++----- core/embed/trezorhal/stm32f4/bootutils.c | 121 ++++++++++++------ core/embed/trezorhal/stm32u5/platform.h | 10 -- core/embed/trezorhal/stm32u5/secret.c | 4 +- core/embed/trezorhal/unix/bootutils.c | 44 ++++--- 13 files changed, 230 insertions(+), 128 deletions(-) create mode 100644 core/embed/trezorhal/bootargs.h diff --git a/core/embed/bootloader/emulator.c b/core/embed/bootloader/emulator.c index 24fbfd620..f1c8481ff 100644 --- a/core/embed/bootloader/emulator.c +++ b/core/embed/bootloader/emulator.c @@ -2,8 +2,8 @@ #include #include TREZOR_BOARD +#include "bootargs.h" #include "bootui.h" -#include "bootutils.h" #include "common.h" #include "display.h" #include "flash.h" @@ -174,7 +174,7 @@ void mpu_config_bootloader(void) {} void mpu_config_off(void) {} -__attribute__((noreturn)) void jump_to(uint32_t address) { +void jump_to(uint32_t address) { bool storage_is_erased = storage_empty(&STORAGE_AREAS[0]) && storage_empty(&STORAGE_AREAS[1]); diff --git a/core/embed/bootloader/emulator.h b/core/embed/bootloader/emulator.h index f9ab58037..9cf747075 100644 --- a/core/embed/bootloader/emulator.h +++ b/core/embed/bootloader/emulator.h @@ -15,6 +15,6 @@ void emulator_poll_events(void); void set_core_clock(int); void mpu_config_bootloader(void); void mpu_config_off(void); -void jump_to(void *addr); +__attribute__((noreturn)) void jump_to(uint32_t address); #endif diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 6f471d12b..5099001e3 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -20,6 +20,7 @@ #include #include +#include "bootargs.h" #include "bootutils.h" #include "common.h" #include "display.h" @@ -345,11 +346,7 @@ void real_jump_to_firmware(void) { __attribute__((noreturn)) void jump_to_fw_through_reset(void) { display_fade(display_backlight(-1), 0, 200); - __disable_irq(); - delete_secrets(); - NVIC_SystemReset(); - for (;;) - ; + reboot(); } #endif diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 2239880af..ddc94e5bf 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -24,7 +24,7 @@ #include #include "messages.pb.h" -#include "bootutils.h" +#include "bootargs.h" #include "common.h" #include "flash.h" #include "image.h" diff --git a/core/embed/bootloader/startup_stm32f4.s b/core/embed/bootloader/startup_stm32f4.s index a1fb0c7e5..96ad4d3ae 100644 --- a/core/embed/bootloader/startup_stm32f4.s +++ b/core/embed/bootloader/startup_stm32f4.s @@ -35,7 +35,7 @@ reset_handler: // r11 contains the command passed to bootargs_set() // function called when the firmware rebooted to the bootloader - ldr r0, =g_boot_command_shadow + ldr r0, =g_boot_command_saved str r11, [r0] // enter the application code diff --git a/core/embed/bootloader/startup_stm32u5.s b/core/embed/bootloader/startup_stm32u5.s index 6f5c36e73..ad97873e6 100644 --- a/core/embed/bootloader/startup_stm32u5.s +++ b/core/embed/bootloader/startup_stm32u5.s @@ -57,7 +57,7 @@ reset_handler: // copy & clear g_boot_command ldr r0, =g_boot_command ldr r1, [r0] - ldr r0, =g_boot_command_shadow + ldr r0, =g_boot_command_saved str r1, [r0] ldr r0, =g_boot_command mov r1, #0 diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 69d48673a..5babd7301 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -284,31 +284,37 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_sd_hotswap_enabled_obj, STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader(size_t n_args, const mp_obj_t *args) { #ifndef TREZOR_EMULATOR - boot_command_t boot_command = BOOT_COMMAND_NONE; - mp_buffer_info_t boot_args = {0}; - if (n_args > 0 && args[0] != mp_const_none) { mp_int_t value = mp_obj_get_int(args[0]); switch (value) { case 0: - boot_command = BOOT_COMMAND_STOP_AND_WAIT; + // Reboot and stay in bootloader + reboot_to_bootloader(); break; case 1: - boot_command = BOOT_COMMAND_INSTALL_UPGRADE; + // Reboot and continue with the firmware upgrade + mp_buffer_info_t hash = {0}; + + if (n_args > 1 && args[1] != mp_const_none) { + mp_get_buffer_raise(args[1], &hash, MP_BUFFER_READ); + } + + if (hash.len != 32) { + mp_raise_ValueError("Invalid value."); + } + + reboot_and_upgrade((uint8_t *)hash.buf); break; default: mp_raise_ValueError("Invalid value."); break; } + } else { + // Just reboot and go through the normal boot sequence + reboot(); } - if (n_args > 1 && args[1] != mp_const_none) { - mp_get_buffer_raise(args[1], &boot_args, MP_BUFFER_READ); - } - - bootargs_set(boot_command, boot_args.buf, boot_args.len); - reboot_to_bootloader(); #endif return mp_const_none; } diff --git a/core/embed/trezorhal/bootargs.h b/core/embed/trezorhal/bootargs.h new file mode 100644 index 000000000..45028e5f9 --- /dev/null +++ b/core/embed/trezorhal/bootargs.h @@ -0,0 +1,57 @@ +/* + * 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_BOOTARGS_H +#define TREZORHAL_BOOTARGS_H + +#include +#include + +// Defines boot command processed in bootloader on next reboot +typedef enum { + // Normal boot sequence + BOOT_COMMAND_NONE = 0x00000000, + // Stop and wait for further instructions + BOOT_COMMAND_STOP_AND_WAIT = 0x0FC35A96, + // Do not ask anything, install an upgrade + BOOT_COMMAND_INSTALL_UPGRADE = 0xFA4A5C8D, +} boot_command_t; + +// Maximum size boot_args array +#define BOOT_ARGS_MAX_SIZE (256 - 8) + +typedef union { + uint8_t raw[BOOT_ARGS_MAX_SIZE]; + + // firmware header hash, BOOT_COMMAND_INSTALL_UPGRADE + uint8_t hash[32]; + +} boot_args_t; + +// Configures the boot command and associated arguments for the next reboot. +// The arguments must adhere to the boot_args_t structure layout. +void bootargs_set(boot_command_t command, const void* args, size_t args_size); + +// Returns the last boot command saved during bootloader startup +boot_command_t bootargs_get_command(); + +// Returns the pointer to boot arguments +const boot_args_t* bootargs_get_args(); + +#endif // TREZORHAL_BOOTUTILS_H diff --git a/core/embed/trezorhal/bootutils.h b/core/embed/trezorhal/bootutils.h index acf892075..faa14a507 100644 --- a/core/embed/trezorhal/bootutils.h +++ b/core/embed/trezorhal/bootutils.h @@ -1,50 +1,49 @@ +/* + * 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_BOOTUTILS_H #define TREZORHAL_BOOTUTILS_H #include #include -// Defines boot command for 'reboot_to_bootloader()' function -typedef enum { - // Normal boot sequence - BOOT_COMMAND_NONE = 0x00000000, - // Stop and wait for further instructions - BOOT_COMMAND_STOP_AND_WAIT = 0x0FC35A96, - // Do not ask anything, install an upgrade - BOOT_COMMAND_INSTALL_UPGRADE = 0xFA4A5C8D, -} boot_command_t; - -// Maximum size boot_args array -#define BOOT_ARGS_MAX_SIZE (256 - 8) - -typedef union { - uint8_t raw[BOOT_ARGS_MAX_SIZE]; - - // firmware header hash, BOOT_COMMAND_INSTALL_UPGRADE - uint8_t hash[32]; - -} boot_args_t; - -// Sets boot command and arguments for the next reboot -// arguments have too respect boot_args_t structure layout -// (function can be called multiple times before reboting) -void bootargs_set(boot_command_t command, const void* args, size_t args_size); - -// Returns the last boot command set by bootargs_set_command() -boot_command_t bootargs_get_command(); - -// Returns the pointer to boot arguments -const boot_args_t* bootargs_get_args(); +// Immediately resets the device and initiates the normal boot sequence. +void __attribute__((noreturn)) reboot(void); -// Reboots the device into the bootloader. -// The bootloader will read the command set by `bootargs_set()`. +// Resets the device and enters the bootloader, +// halting there and waiting for further user instructions. void __attribute__((noreturn)) reboot_to_bootloader(void); -// Causes immediate reset of the device. -void __attribute__((noreturn)) reboot(void); - -// Safely shuts down the device (clears secrets, memory, etc.). -// This function is called when the device is in an unrecoverable state. +// Resets the device into the bootloader and automatically continues +// with the installation of new firmware (also known as an +// interaction-less upgrade). +// +// If the provided hash is NULL or invalid, the device will stop +// at the bootloader and will require user acknowledgment to proceed +// 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.). +// +// This function is called when the device eneters an +// unrecoverable error state. void __attribute__((noreturn)) secure_shutdown(void); #endif // TREZORHAL_BOOTUTILS_H diff --git a/core/embed/trezorhal/stm32f4/bootutils.c b/core/embed/trezorhal/stm32f4/bootutils.c index 4667fc5e8..ee01d3906 100644 --- a/core/embed/trezorhal/stm32f4/bootutils.c +++ b/core/embed/trezorhal/stm32f4/bootutils.c @@ -1,33 +1,46 @@ +/* + * 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 "../bootutils.h" -#include #include +#include "bootargs.h" +#include "bootutils.h" +#include "common.h" #include "display.h" #include "irq.h" #include "mpu.h" -// The 'g_boot_command_shadow' shadows a real boot command passed -// to the bootloader. -// 1. In the bootloader, its value is set in the startup code. -// 2. In the firmware it holds command for the next boot and it is used -// when reboot_to_bootloader() is called -boot_command_t g_boot_command_shadow; - #ifdef STM32U5 -// The 'g_boot_command' is persistent variable that holds the 'command' -// for the next reboot/jump to the bootloader. Its value is set to -// g_boot_command_shadow when 'reboot_to_bootloader()' is called. +// Persistent variable that holds the 'command' for the next reboot. boot_command_t __attribute__((section(".boot_command"))) g_boot_command; +#else +// Holds the 'command' for the next jump to the bootloader. +static boot_command_t g_boot_command = BOOT_COMMAND_NONE; #endif -// The 'g_boot_args' is persistent array that stores extra arguments passed -// to the function bootargs_set. +// Persistent array that holds extra arguments for the command passed +// to the bootloader. static boot_args_t __attribute__((section(".boot_args"))) g_boot_args; void bootargs_set(boot_command_t command, const void* args, size_t args_size) { // save boot command - g_boot_command_shadow = command; + g_boot_command = command; size_t copy_size = 0; // copy arguments up to BOOT_ARGS_MAX_SIZE @@ -43,44 +56,76 @@ void bootargs_set(boot_command_t command, const void* args, size_t args_size) { } } -boot_command_t bootargs_get_command() { return g_boot_command_shadow; } +#ifdef BOOTLOADER +// Contains the current boot command saved during bootloader startup. +boot_command_t g_boot_command_saved; + +boot_command_t bootargs_get_command() { return g_boot_command_saved; } const boot_args_t* bootargs_get_args() { return &g_boot_args; } +#endif -void __attribute__((noreturn)) secure_shutdown(void) { - display_deinit(DISPLAY_RETAIN_CONTENT); +// 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) { + __disable_irq(); -#if defined(STM32U5) + // Disable SAES peripheral clock, so that we don't get tamper events __HAL_RCC_SAES_CLK_DISABLE(); - // Erase all secrets - TAMP->CR2 |= TAMP_CR2_BKERASE; -#endif - // from util.s - extern void shutdown_privileged(void); - shutdown_privileged(); - for (;;) - ; + TAMP->CR2 |= TAMP_CR2_BKERASE; } +#endif // STM32U5 + +// Reboots the device with the given boot command and arguments +static void __attribute__((noreturn)) +reboot_with_args(boot_command_t command, const void* args, size_t args_size) { + bootargs_set(command, args, args_size); -void reboot_to_bootloader(void) { - boot_command_t boot_command = bootargs_get_command(); - display_deinit(DISPLAY_RESET_CONTENT); -#ifdef ENSURE_COMPATIBLE_SETTINGS - ensure_compatible_settings(); -#endif #ifdef STM32U5 - // extern uint32_t g_boot_command; - g_boot_command = boot_command; - __disable_irq(); delete_secrets(); NVIC_SystemReset(); #else + display_deinit(DISPLAY_RESET_CONTENT); +#ifdef ENSURE_COMPATIBLE_SETTINGS + ensure_compatible_settings(); +#endif mpu_config_bootloader(); - jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE, boot_command); + jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE, g_boot_command); for (;;) ; #endif } -void reboot(void) { NVIC_SystemReset(); } +void reboot_to_bootloader(void) { + reboot_with_args(BOOT_COMMAND_STOP_AND_WAIT, NULL, 0); +} + +void reboot_and_upgrade(const uint8_t hash[32]) { + reboot_with_args(BOOT_COMMAND_INSTALL_UPGRADE, hash, 32); +} + +void reboot(void) { + bootargs_set(BOOT_COMMAND_NONE, NULL, 0); + +#ifdef STM32U5 + delete_secrets(); +#endif + + NVIC_SystemReset(); +} + +void __attribute__((noreturn)) secure_shutdown(void) { + display_deinit(DISPLAY_RETAIN_CONTENT); + +#ifdef STM32U5 + delete_secrets(); +#endif + // from util.s + extern void shutdown_privileged(void); + shutdown_privileged(); + + for (;;) + ; +} diff --git a/core/embed/trezorhal/stm32u5/platform.h b/core/embed/trezorhal/stm32u5/platform.h index 55953cc50..d5c9ffb6e 100644 --- a/core/embed/trezorhal/stm32u5/platform.h +++ b/core/embed/trezorhal/stm32u5/platform.h @@ -42,16 +42,6 @@ void jump_to_with_flag(uint32_t address, uint32_t register_flag); extern uint32_t __stack_chk_guard; -// this deletes all secrets and SRAM2 where stack is located -// to prevent stack smashing error, do not return from function calling this -static inline void __attribute__((always_inline)) delete_secrets(void) { - // Disable SAES peripheral clock, so that we don't get tamper events - __HAL_RCC_SAES_CLK_DISABLE(); - - // Erase all - TAMP->CR2 |= TAMP_CR2_BKERASE; -} - void check_oem_keys(void); #endif // TREZORHAL_STM32_H diff --git a/core/embed/trezorhal/stm32u5/secret.c b/core/embed/trezorhal/stm32u5/secret.c index d148f0a54..104fdb5dd 100644 --- a/core/embed/trezorhal/stm32u5/secret.c +++ b/core/embed/trezorhal/stm32u5/secret.c @@ -1,6 +1,7 @@ #include "secret.h" #include #include +#include "bootutils.h" #include "common.h" #include "flash.h" #include "memzero.h" @@ -122,8 +123,7 @@ static secbool secret_present(uint32_t offset, uint32_t len) { // read access to it. static void secret_bhk_load(void) { if (sectrue == secret_bhk_locked()) { - delete_secrets(); - NVIC_SystemReset(); + reboot(); } uint32_t secret[SECRET_BHK_LEN / sizeof(uint32_t)] = {0}; diff --git a/core/embed/trezorhal/unix/bootutils.c b/core/embed/trezorhal/unix/bootutils.c index a80e560a5..1fe3afed8 100644 --- a/core/embed/trezorhal/unix/bootutils.c +++ b/core/embed/trezorhal/unix/bootutils.c @@ -1,26 +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 . + */ -#include "../bootutils.h" -#include #include #include #include -// The 'g_boot_command_shadow' variable stores the 'command' for the next -// reboot/jumping to the bootloadeer. It may be one of the -// 'BOOT_COMMAND_xxx' values defined in the enumeration, or it could -// be any other value that should be treated as a non-special action, -// in which case the bootloader should behave as if the device was -// just powered up. +#include +#include "bootargs.h" +#include "bootutils.h" -static boot_command_t g_boot_command_shadow; +// Holds the 'command' for the next reboot. +static boot_command_t g_boot_command; -// The 'g_boot_args' array stores extra arguments passed -// function boot_args. It sits at section that persists jump to the bootloader. +// Holds extra arguments for the command passed to the bootloader. static boot_args_t g_boot_args; void bootargs_set(boot_command_t command, const void* args, size_t args_size) { // save boot command - g_boot_command_shadow = command; + g_boot_command = command; size_t copy_size = 0; // copy arguments up to BOOT_ARGS_MAX_SIZE @@ -36,12 +49,7 @@ void bootargs_set(boot_command_t command, const void* args, size_t args_size) { } } -void bootargs_clear() { - g_boot_command_shadow = BOOT_COMMAND_NONE; - memset(&g_boot_args, 0, sizeof(g_boot_args)); -} - -boot_command_t bootargs_get_command() { return g_boot_command_shadow; } +boot_command_t bootargs_get_command() { return g_boot_command; } const boot_args_t* bootargs_get_args() { return &g_boot_args; }