From 337d4daa45d84e86698553e63b892cdc7eaf69c9 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Fri, 31 Jan 2025 17:11:40 +0100 Subject: [PATCH 1/5] refactor(core): complete bootloader refactoring --- core/SConscript.bootloader | 22 +- core/SConscript.bootloader_ci | 2 +- core/SConscript.bootloader_emu | 24 +- .../bootloader/.changelog.d/4572.changed | 1 + core/embed/projects/bootloader/bootui.c | 39 +- core/embed/projects/bootloader/bootui.h | 31 +- core/embed/projects/bootloader/emulator.c | 10 +- core/embed/projects/bootloader/emulator.h | 24 +- core/embed/projects/bootloader/main.c | 328 +------ core/embed/projects/bootloader/messages.c | 884 ------------------ core/embed/projects/bootloader/messages.h | 83 -- core/embed/projects/bootloader/poll.c | 62 ++ core/embed/projects/bootloader/poll.h | 40 + .../bootloader/protob/{ => pb}/.gitignore | 0 .../bootloader/protob/{ => pb}/Makefile | 0 .../protob/{ => pb}/messages.options | 0 .../bootloader/protob/{ => pb}/messages.pb.c | 0 .../bootloader/protob/{ => pb}/messages.pb.h | 0 .../bootloader/protob/{ => pb}/messages.proto | 0 .../embed/projects/bootloader/protob/protob.c | 280 ++++++ .../embed/projects/bootloader/protob/protob.h | 70 ++ .../embed/projects/bootloader/wire/codec_v1.c | 248 +++++ .../embed/projects/bootloader/wire/codec_v1.h | 50 + .../projects/bootloader/wire/wire_iface_usb.c | 111 +++ .../projects/bootloader/wire/wire_iface_usb.h | 24 + .../bootloader/workflow/wf_auto_update.c | 40 + .../bootloader/workflow/wf_bootloader.c | 93 ++ .../bootloader/workflow/wf_empty_device.c | 60 ++ .../bootloader/workflow/wf_firmware_update.c | 553 +++++++++++ .../bootloader/workflow/wf_get_features.c | 34 + .../bootloader/workflow/wf_host_control.c | 125 +++ .../bootloader/workflow/wf_initialize.c | 34 + .../projects/bootloader/workflow/wf_ping.c | 32 + .../workflow/wf_unlock_bootloader.c | 43 + .../bootloader/workflow/wf_wipe_device.c | 49 + .../projects/bootloader/workflow/workflow.c | 53 ++ .../projects/bootloader/workflow/workflow.h | 74 ++ .../bootloader/workflow/workflow_internal.h | 31 + core/embed/projects/bootloader_ci/messages.c | 2 +- tools/style.c.exclude | 2 +- 40 files changed, 2228 insertions(+), 1330 deletions(-) create mode 100644 core/embed/projects/bootloader/.changelog.d/4572.changed delete mode 100644 core/embed/projects/bootloader/messages.c delete mode 100644 core/embed/projects/bootloader/messages.h create mode 100644 core/embed/projects/bootloader/poll.c create mode 100644 core/embed/projects/bootloader/poll.h rename core/embed/projects/bootloader/protob/{ => pb}/.gitignore (100%) rename core/embed/projects/bootloader/protob/{ => pb}/Makefile (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.options (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.pb.c (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.pb.h (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.proto (100%) create mode 100644 core/embed/projects/bootloader/protob/protob.c create mode 100644 core/embed/projects/bootloader/protob/protob.h create mode 100644 core/embed/projects/bootloader/wire/codec_v1.c create mode 100644 core/embed/projects/bootloader/wire/codec_v1.h create mode 100644 core/embed/projects/bootloader/wire/wire_iface_usb.c create mode 100644 core/embed/projects/bootloader/wire/wire_iface_usb.h create mode 100644 core/embed/projects/bootloader/workflow/wf_auto_update.c create mode 100644 core/embed/projects/bootloader/workflow/wf_bootloader.c create mode 100644 core/embed/projects/bootloader/workflow/wf_empty_device.c create mode 100644 core/embed/projects/bootloader/workflow/wf_firmware_update.c create mode 100644 core/embed/projects/bootloader/workflow/wf_get_features.c create mode 100644 core/embed/projects/bootloader/workflow/wf_host_control.c create mode 100644 core/embed/projects/bootloader/workflow/wf_initialize.c create mode 100644 core/embed/projects/bootloader/workflow/wf_ping.c create mode 100644 core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c create mode 100644 core/embed/projects/bootloader/workflow/wf_wipe_device.c create mode 100644 core/embed/projects/bootloader/workflow/workflow.c create mode 100644 core/embed/projects/bootloader/workflow/workflow.h create mode 100644 core/embed/projects/bootloader/workflow/workflow_internal.h diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 95c9cffec55..cd16128e6af 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -105,11 +105,29 @@ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader/header.S', 'embed/projects/bootloader/bootui.c', 'embed/projects/bootloader/main.c', - 'embed/projects/bootloader/messages.c', - 'embed/projects/bootloader/protob/messages.pb.c', + 'embed/projects/bootloader/poll.c', + 'embed/projects/bootloader/workflow/workflow.c', + 'embed/projects/bootloader/workflow/wf_firmware_update.c', + 'embed/projects/bootloader/workflow/wf_wipe_device.c', + 'embed/projects/bootloader/workflow/wf_get_features.c', + 'embed/projects/bootloader/workflow/wf_initialize.c', + 'embed/projects/bootloader/workflow/wf_ping.c', + 'embed/projects/bootloader/workflow/wf_bootloader.c', + 'embed/projects/bootloader/workflow/wf_empty_device.c', + 'embed/projects/bootloader/workflow/wf_auto_update.c', + 'embed/projects/bootloader/workflow/wf_host_control.c', + 'embed/projects/bootloader/wire/codec_v1.c', + 'embed/projects/bootloader/wire/wire_iface_usb.c', + 'embed/projects/bootloader/protob/protob.c', + 'embed/projects/bootloader/protob/pb/messages.pb.c', 'embed/projects/bootloader/version_check.c', ] +if 'optiga' in FEATURES_AVAILABLE: + SOURCE_BOOTLOADER += [ + 'embed/projects/bootloader/workflow/wf_unlock_bootloader.c', + ] + env.Replace( CAT='cat', diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index cf0717ac7d3..980dfc4c0ee 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -100,7 +100,7 @@ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader_ci/bootui.c', 'embed/projects/bootloader_ci/main.c', 'embed/projects/bootloader_ci/messages.c', - 'embed/projects/bootloader_ci/protob/messages.pb.c', + 'embed/projects/bootloader_ci/protob/pb/messages.pb.c', 'embed/projects/bootloader_ci/version_check.c', ] diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 80669a5d85f..eba7237d4dd 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -100,10 +100,23 @@ SOURCE_NANOPB = [ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader/bootui.c', 'embed/projects/bootloader/main.c', - 'embed/projects/bootloader/messages.c', - 'embed/projects/bootloader/emulator.c', + 'embed/projects/bootloader/poll.c', + 'embed/projects/bootloader/workflow/workflow.c', + 'embed/projects/bootloader/workflow/wf_firmware_update.c', + 'embed/projects/bootloader/workflow/wf_wipe_device.c', + 'embed/projects/bootloader/workflow/wf_get_features.c', + 'embed/projects/bootloader/workflow/wf_initialize.c', + 'embed/projects/bootloader/workflow/wf_ping.c', + 'embed/projects/bootloader/workflow/wf_bootloader.c', + 'embed/projects/bootloader/workflow/wf_empty_device.c', + 'embed/projects/bootloader/workflow/wf_auto_update.c', + 'embed/projects/bootloader/workflow/wf_host_control.c', + 'embed/projects/bootloader/wire/codec_v1.c', + 'embed/projects/bootloader/wire/wire_iface_usb.c', + 'embed/projects/bootloader/protob/protob.c', + 'embed/projects/bootloader/protob/pb/messages.pb.c', 'embed/projects/bootloader/version_check.c', - 'embed/projects/bootloader/protob/messages.pb.c', + 'embed/projects/bootloader/emulator.c', ] SOURCE_TREZORHAL = [ @@ -138,6 +151,11 @@ env = Environment( FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_UNIX, PATH_HAL) +if 'optiga' in FEATURES_AVAILABLE: + SOURCE_BOOTLOADER += [ + 'embed/projects/bootloader/workflow/wf_unlock_bootloader.c', + ] + env.Replace( CP='cp', AS='as', diff --git a/core/embed/projects/bootloader/.changelog.d/4572.changed b/core/embed/projects/bootloader/.changelog.d/4572.changed new file mode 100644 index 00000000000..1fc71073848 --- /dev/null +++ b/core/embed/projects/bootloader/.changelog.d/4572.changed @@ -0,0 +1 @@ +Major code clean-up and refactoring. diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c index 45a71055d38..51ac3da0565 100644 --- a/core/embed/projects/bootloader/bootui.c +++ b/core/embed/projects/bootloader/bootui.c @@ -19,39 +19,16 @@ #include -#include #include #include #include + #include "bootui.h" #include "rust_ui.h" #include "version.h" #define BACKLIGHT_NORMAL 150 -#define COLOR_BL_BG COLOR_WHITE // background -#define COLOR_BL_FG COLOR_BLACK // foreground - -#ifdef RGB16 -#define COLOR_BL_FAIL RGB16(0xFF, 0x00, 0x00) // red -#define COLOR_BL_DONE RGB16(0x00, 0xAE, 0x0B) // green -#define COLOR_BL_PROCESS RGB16(0x4A, 0x90, 0xE2) // blue -#define COLOR_BL_GRAY RGB16(0x99, 0x99, 0x99) // gray -#else -#define COLOR_BL_FAIL COLOR_BL_FG -#define COLOR_BL_DONE COLOR_BL_FG -#define COLOR_BL_PROCESS COLOR_BL_FG -#define COLOR_BL_GRAY COLOR_BL_FG -#endif - -#if !defined TREZOR_MODEL_T2B1 && !defined TREZOR_MODEL_T3B1 -#define BOOT_WAIT_HEIGHT 25 -#define BOOT_WAIT_Y_TOP (DISPLAY_RESY - BOOT_WAIT_HEIGHT) -#else -#define BOOT_WAIT_HEIGHT 12 -#define BOOT_WAIT_Y_TOP (DISPLAY_RESY - BOOT_WAIT_HEIGHT) -#endif - #define TOIF_LENGTH(ptr) ((*(uint32_t *)((ptr) + 8)) + 12) // common shared functions @@ -140,13 +117,15 @@ uint32_t ui_screen_menu(secbool firmware_present) { return screen_menu(firmware_present); } +void ui_screen_connect(void) { screen_connect(initial_setup); } + // install UI -uint32_t ui_screen_install_confirm(const vendor_header *const vhdr, - const image_header *const hdr, - secbool should_keep_seed, - secbool is_newvendor, secbool is_newinstall, - int version_cmp) { +ui_result_t ui_screen_install_confirm(const vendor_header *const vhdr, + const image_header *const hdr, + secbool should_keep_seed, + secbool is_newvendor, + secbool is_newinstall, int version_cmp) { uint8_t fingerprint[32]; char ver_str[64]; get_image_fingerprint(hdr, fingerprint); @@ -172,7 +151,7 @@ void ui_screen_install_progress_upload(int pos) { // wipe UI -uint32_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); } +ui_result_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); } void ui_screen_wipe(void) { screen_wipe_progress(0, true); } diff --git a/core/embed/projects/bootloader/bootui.h b/core/embed/projects/bootloader/bootui.h index b3fc7e082ad..9713f84f371 100644 --- a/core/embed/projects/bootloader/bootui.h +++ b/core/embed/projects/bootloader/bootui.h @@ -17,13 +17,17 @@ * along with this program. If not, see . */ -#ifndef __BOOTUI_H__ -#define __BOOTUI_H__ +#pragma once #include #include +typedef enum { + UI_RESULT_CANCEL = 1, + UI_RESULT_CONFIRM = 2, +} ui_result_t; + typedef enum { SCREEN_INTRO = 0, SCREEN_MENU = 1, @@ -58,16 +62,18 @@ uint32_t ui_screen_intro(const vendor_header* const vhdr, uint32_t ui_screen_menu(secbool firmware_present); -uint32_t ui_screen_install_confirm(const vendor_header* const vhdr, - const image_header* const hdr, - secbool shold_keep_seed, - secbool is_newvendor, secbool is_newinstall, - int version_cmp); +void ui_screen_connect(void); + +ui_result_t ui_screen_install_confirm(const vendor_header* const vhdr, + const image_header* const hdr, + secbool shold_keep_seed, + secbool is_newvendor, + secbool is_newinstall, int version_cmp); void ui_screen_install_start(); void ui_screen_install_progress_erase(int pos, int len); void ui_screen_install_progress_upload(int pos); -uint32_t ui_screen_wipe_confirm(void); +ui_result_t ui_screen_wipe_confirm(void); void ui_screen_wipe(void); void ui_screen_wipe_progress(int pos, int len); @@ -84,12 +90,3 @@ void ui_screen_boot_stage_1(bool fading); #ifdef USE_OPTIGA uint32_t ui_screen_unlock_bootloader_confirm(void); #endif - -// clang-format off -#define INPUT_CANCEL 0x01 // Cancel button -#define INPUT_CONFIRM 0x02 // Confirm button -#define INPUT_LONG_CONFIRM 0x04 // Long Confirm button -#define INPUT_INFO 0x08 // Info icon -// clang-format on - -#endif diff --git a/core/embed/projects/bootloader/emulator.c b/core/embed/projects/bootloader/emulator.c index 8f46770f5a1..0c9bbb8df46 100644 --- a/core/embed/projects/bootloader/emulator.c +++ b/core/embed/projects/bootloader/emulator.c @@ -8,11 +8,9 @@ #include #include #include -#include #include #include #include "bootui.h" -#include "rust_ui.h" #ifdef USE_OPTIGA #include @@ -188,10 +186,12 @@ int main(int argc, char **argv) { (void)!flash_otp_write(FLASH_OTP_BLOCK_DEVICE_VARIANT, 0, otp_data, sizeof(otp_data)); - bootloader_main(); - hal_delay(3000); - jump_to_next_stage(0); + int exit_code = bootloader_main(); + char msg[64]; + snprintf(msg, sizeof(msg), "Exit code: %d", exit_code); + + error_shutdown_ex("BOOTLOADER ERROR", msg, "UNEXPECTED EXIT"); return 0; } diff --git a/core/embed/projects/bootloader/emulator.h b/core/embed/projects/bootloader/emulator.h index 721490e912a..e3d362cde4d 100644 --- a/core/embed/projects/bootloader/emulator.h +++ b/core/embed/projects/bootloader/emulator.h @@ -1,10 +1,26 @@ -#ifndef __EMULATOR_H__ -#define __EMULATOR_H__ +/* + * 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 #include #undef FIRMWARE_START extern uint8_t *FIRMWARE_START; - -#endif diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c index e29fe9bc409..35d2909d26e 100644 --- a/core/embed/projects/bootloader/main.c +++ b/core/embed/projects/bootloader/main.c @@ -20,27 +20,20 @@ #include #include -#include - -#include #include #include -#include #include #include #include #include -#include #include #include -#include -#include +#include #include #include #include #include #include -#include "messages.pb.h" #ifdef USE_PVD #include @@ -64,12 +57,9 @@ #include #endif -#include -#include "version.h" +#include #include "bootui.h" -#include "messages.h" -#include "rust_ui.h" #include "version_check.h" #ifdef TREZOR_EMULATOR @@ -77,14 +67,6 @@ #include "emulator.h" #endif -#define USB_IFACE_NUM 0 - -typedef enum { - SHUTDOWN = 0, - CONTINUE_TO_FIRMWARE = 0xAABBCCDD, - RETURN_TO_MENU = 0x55667788, -} usb_result_t; - void failed_jump_to_firmware(void); CONFIDENTIAL volatile secbool dont_optimize_out_true = sectrue; @@ -143,158 +125,6 @@ static void drivers_deinit(void) { display_deinit(DISPLAY_JUMP_BEHAVIOR); } -static void usb_init_all(secbool usb21_landing) { - usb_dev_info_t dev_info = { - .device_class = 0x00, - .device_subclass = 0x00, - .device_protocol = 0x00, - .vendor_id = 0x1209, - .product_id = 0x53C0, - .release_num = 0x0200, - .manufacturer = MODEL_USB_MANUFACTURER, - .product = MODEL_USB_PRODUCT, - .serial_number = "000000000000000000000000", - .interface = "TREZOR Interface", - .usb21_enabled = sectrue, - .usb21_landing = usb21_landing, - }; - - static uint8_t rx_buffer[USB_PACKET_SIZE]; - - static const usb_webusb_info_t webusb_info = { - .iface_num = USB_IFACE_NUM, -#ifdef TREZOR_EMULATOR - .emu_port = 21324, -#else - .ep_in = 0x01, - .ep_out = 0x01, -#endif - .subclass = 0, - .protocol = 0, - .max_packet_len = sizeof(rx_buffer), - .rx_buffer = rx_buffer, - .polling_interval = 1, - }; - - ensure(usb_init(&dev_info), NULL); - - ensure(usb_webusb_add(&webusb_info), NULL); - - ensure(usb_start(), NULL); -} - -static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, - const image_header *const hdr) { - // if both are NULL, we don't have a firmware installed - // let's show a webusb landing page in this case - usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse); - - uint8_t buf[USB_PACKET_SIZE]; - - for (;;) { -#ifdef TREZOR_EMULATOR - // Ensures that SDL events are processed. This prevents the emulator from - // freezing when the user interacts with the window. - SDL_PumpEvents(); -#endif - int r = usb_webusb_read_blocking(USB_IFACE_NUM, buf, USB_PACKET_SIZE, - USB_TIMEOUT); - if (r != USB_PACKET_SIZE) { - continue; - } - uint16_t msg_id; - uint32_t msg_size; - uint32_t response; - if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) { - // invalid header -> discard - continue; - } - switch (msg_id) { - case MessageType_MessageType_Initialize: - process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; - case MessageType_MessageType_Ping: - process_msg_Ping(USB_IFACE_NUM, msg_size, buf); - break; - case MessageType_MessageType_WipeDevice: - response = ui_screen_wipe_confirm(); - if (INPUT_CANCEL == response) { - send_user_abort(USB_IFACE_NUM, "Wipe cancelled"); - hal_delay(100); - usb_deinit(); - return RETURN_TO_MENU; - } - ui_screen_wipe(); - r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf); - if (r < 0) { // error - screen_wipe_fail(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - } else { // success - screen_wipe_success(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - } - break; - case MessageType_MessageType_FirmwareErase: - process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf); - break; - case MessageType_MessageType_FirmwareUpload: - r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort - if (r == UPLOAD_ERR_BOOTLOADER_LOCKED) { - // This function does not return - show_install_restricted_screen(); - } else { - ui_screen_fail(); - } - usb_deinit(); - return SHUTDOWN; - } else if (r == UPLOAD_ERR_USER_ABORT) { - hal_delay(100); - usb_deinit(); - return RETURN_TO_MENU; - } else if (r == 0) { // last chunk received - ui_screen_install_progress_upload(1000); - ui_screen_done(4, sectrue); - ui_screen_done(3, secfalse); - hal_delay(1000); - ui_screen_done(2, secfalse); - hal_delay(1000); - ui_screen_done(1, secfalse); - hal_delay(1000); - usb_deinit(); - return CONTINUE_TO_FIRMWARE; - } - break; - case MessageType_MessageType_GetFeatures: - process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; -#if defined USE_OPTIGA - case MessageType_MessageType_UnlockBootloader: - response = ui_screen_unlock_bootloader_confirm(); - if (INPUT_CANCEL == response) { - send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled"); - hal_delay(100); - usb_deinit(); - return RETURN_TO_MENU; - } - process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf); - screen_unlock_bootloader_success(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - break; -#endif - default: - process_msg_unknown(USB_IFACE_NUM, msg_size, buf); - break; - } - } -} - static secbool check_vendor_header_lock(const vendor_header *const vhdr) { uint8_t lock[FLASH_OTP_BLOCK_SIZE]; ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock, @@ -392,7 +222,6 @@ void real_jump_to_firmware(void) { #ifdef USE_RESET_TO_BOOT __attribute__((noreturn)) void jump_to_fw_through_reset(void) { display_fade(display_get_backlight(), 0, 200); - reboot_device(); } #endif @@ -540,143 +369,44 @@ int bootloader_main(void) { // ... or there is no valid firmware if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue || auto_upgrade == sectrue) { - screen_t screen; - ui_set_initial_setup(true); - if (header_present == sectrue) { - if (auto_upgrade == sectrue) { - screen = SCREEN_WAIT_FOR_HOST; + workflow_result_t result; + + do { + if (header_present == sectrue) { + if (auto_upgrade == sectrue) { + result = workflow_auto_update(&vhdr, hdr); + } else { + result = workflow_bootloader(&vhdr, hdr, firmware_present); + } } else { - ui_set_initial_setup(false); - screen = SCREEN_INTRO; + result = workflow_empty_device(); } + } while (result == WF_STAY || result == WF_RETURN); - } else { - screen = SCREEN_WELCOME; - -#ifdef USE_STORAGE_HWKEY - secret_bhk_regenerate(); -#endif - ensure(erase_storage(NULL), NULL); - - // keep the model screen up for a while -#ifndef USE_BACKLIGHT - hal_delay(1500); + switch (result) { + case WF_CONTINUE_TO_FIRMWARE: { + ensure(dont_optimize_out_true * (workflow_is_jump_allowed_1() == + workflow_is_jump_allowed_2()), + NULL); +#ifdef USE_RESET_TO_BOOT + firmware_jump_fn = jump_to_fw_through_reset; #else - // backlight fading takes some time so the explicit delay here is - // shorter - hal_delay(1000); -#endif - } - - while (true) { - volatile secbool continue_to_firmware = secfalse; - volatile secbool continue_to_firmware_backup = secfalse; - uint32_t ui_result = 0; - - switch (screen) { - case SCREEN_WELCOME: - - ui_screen_welcome(); - - // and start the usb loop - switch (bootloader_usb_loop(NULL, NULL)) { - case CONTINUE_TO_FIRMWARE: - continue_to_firmware = sectrue; - continue_to_firmware_backup = sectrue; - break; - case RETURN_TO_MENU: - break; - default: - case SHUTDOWN: - return 1; - break; - } - break; - - case SCREEN_INTRO: - ui_result = ui_screen_intro(&vhdr, hdr, firmware_present); - if (ui_result == 1) { - screen = SCREEN_MENU; - } - if (ui_result == 2) { - screen = SCREEN_WAIT_FOR_HOST; - } - break; - case SCREEN_MENU: - ui_result = ui_screen_menu(firmware_present); - if (ui_result == 0xAABBCCDD) { // exit menu - screen = SCREEN_INTRO; - } - if (ui_result == 0x11223344) { // reboot -#ifndef USE_HASH_PROCESSOR - ui_screen_boot_stage_1(true); + ui_screen_boot_stage_1(true); + firmware_jump_fn = real_jump_to_firmware; #endif - continue_to_firmware = firmware_present; - continue_to_firmware_backup = firmware_present_backup; - } - if (ui_result == 0x55667788) { // wipe - screen = SCREEN_WIPE_CONFIRM; - } - break; - case SCREEN_WIPE_CONFIRM: - ui_result = screen_wipe_confirm(); - if (ui_result == INPUT_CANCEL) { - // canceled - screen = SCREEN_MENU; - } - if (ui_result == INPUT_CONFIRM) { - ui_screen_wipe(); - secbool r = bootloader_WipeDevice(); - if (r != sectrue) { // error - screen_wipe_fail(); - return 1; - } else { // success - screen_wipe_success(); - return 1; - } - } - break; - case SCREEN_WAIT_FOR_HOST: - screen_connect(auto_upgrade == sectrue); - switch (bootloader_usb_loop(&vhdr, hdr)) { - case CONTINUE_TO_FIRMWARE: - continue_to_firmware = sectrue; - continue_to_firmware_backup = sectrue; - break; - case RETURN_TO_MENU: - screen = SCREEN_INTRO; - break; - case SHUTDOWN: - return 1; - break; - default: - break; - } - break; - default: - break; - } - - if (continue_to_firmware != continue_to_firmware_backup) { + } break; + case WF_SHUTDOWN: + reboot_or_halt_after_rsod(); + return 0; + case WF_WIPE_AND_SHUTDOWN: + default: { // erase storage if we saw flips randomly flip, most likely due to // glitch - #ifdef USE_STORAGE_HWKEY secret_bhk_regenerate(); #endif ensure(erase_storage(NULL), NULL); - } - ensure(dont_optimize_out_true * - (continue_to_firmware == continue_to_firmware_backup), - NULL); - if (sectrue == continue_to_firmware) { -#ifdef USE_RESET_TO_BOOT - firmware_jump_fn = jump_to_fw_through_reset; -#else - ui_screen_boot_stage_1(true); - firmware_jump_fn = real_jump_to_firmware; -#endif - break; + return 1; } } } diff --git a/core/embed/projects/bootloader/messages.c b/core/embed/projects/bootloader/messages.c deleted file mode 100644 index f1371721d86..00000000000 --- a/core/embed/projects/bootloader/messages.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - * 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 -#include -#include "messages.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include "version.h" - -#include "bootui.h" -#include "messages.h" -#include "rust_ui.h" -#include "version_check.h" - -#include "memzero.h" - -#ifdef TREZOR_EMULATOR -#include "emulator.h" -#endif - -#if USE_OPTIGA -#include -#endif - -#define MSG_HEADER1_LEN 9 -#define MSG_HEADER2_LEN 1 - -secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, - uint32_t *msg_size) { - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { - return secfalse; - } - *msg_id = (buf[3] << 8) + buf[4]; - *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; - return sectrue; -} - -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t buf[USB_PACKET_SIZE]; -} usb_write_state; - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count) { - usb_write_state *state = (usb_write_state *)(stream->state); - - size_t written = 0; - // while we have data left - while (written < count) { - size_t remaining = count - written; - // if all remaining data fit into our packet - if (state->packet_pos + remaining <= USB_PACKET_SIZE) { - // append data from buf to state->buf - memcpy(state->buf + state->packet_pos, buf + written, remaining); - // advance position - state->packet_pos += remaining; - // and return - return true; - } else { - // append data that fits - memcpy(state->buf + state->packet_pos, buf + written, - USB_PACKET_SIZE - state->packet_pos); - written += USB_PACKET_SIZE - state->packet_pos; - // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); - // prepare new packet - state->packet_index++; - memzero(state->buf, USB_PACKET_SIZE); - state->buf[0] = '?'; - state->packet_pos = MSG_HEADER2_LEN; - } - } - - return true; -} - -static void _usb_write_flush(usb_write_state *state) { - // if packet is not filled up completely - if (state->packet_pos < USB_PACKET_SIZE) { - // pad it with zeroes - memzero(state->buf + state->packet_pos, - USB_PACKET_SIZE - state->packet_pos); - } - // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); -} - -static secbool _send_msg(uint8_t iface_num, uint16_t msg_id, - const pb_msgdesc_t *fields, const void *msg) { - // determine message size by serializing it into a dummy stream - pb_ostream_t sizestream = {.callback = NULL, - .state = NULL, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - if (false == pb_encode(&sizestream, fields, msg)) { - return secfalse; - } - const uint32_t msg_size = sizestream.bytes_written; - - usb_write_state state = { - .iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = - { - '?', - '#', - '#', - (msg_id >> 8) & 0xFF, - msg_id & 0xFF, - (msg_size >> 24) & 0xFF, - (msg_size >> 16) & 0xFF, - (msg_size >> 8) & 0xFF, - msg_size & 0xFF, - }, - }; - - pb_ostream_t stream = {.callback = &_usb_write, - .state = &state, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - - if (false == pb_encode(&stream, fields, msg)) { - return secfalse; - } - - _usb_write_flush(&state); - - return sectrue; -} - -#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default -#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ - { msg_send.FIELD = VALUE; } -#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - msg_send.FIELD = VALUE; \ - } -#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ - } -#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ - } -#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ - memcpy(msg_send.FIELD.bytes, VALUE, \ - MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ - msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ - } -#define MSG_SEND(TYPE) \ - _send_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, &msg_send) - -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t *buf; -} usb_read_state; - -static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) { - for (int retry = 0;; retry++) { - int r = - usb_webusb_read_blocking(iface_num, buf, USB_PACKET_SIZE, USB_TIMEOUT); - if (r != USB_PACKET_SIZE) { // reading failed - if (r == 0 && retry < 10) { - // only timeout => let's try again - continue; - } else { - // error - error_shutdown_ex("USB ERROR", - "Error reading from USB. Try different USB cable.", - NULL); - } - } - return; // success - } -} - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { - usb_read_state *state = (usb_read_state *)(stream->state); - - size_t read = 0; - // while we have data left - while (read < count) { - size_t remaining = count - read; - // if all remaining data fit into our packet - if (state->packet_pos + remaining <= USB_PACKET_SIZE) { - // append data from buf to state->buf - memcpy(buf + read, state->buf + state->packet_pos, remaining); - // advance position - state->packet_pos += remaining; - // and return - return true; - } else { - // append data that fits - memcpy(buf + read, state->buf + state->packet_pos, - USB_PACKET_SIZE - state->packet_pos); - read += USB_PACKET_SIZE - state->packet_pos; - // read next packet (with retry) - _usb_webusb_read_retry(state->iface_num, state->buf); - // prepare next packet - state->packet_index++; - state->packet_pos = MSG_HEADER2_LEN; - } - } - - return true; -} - -static void _usb_read_flush(usb_read_state *state) { (void)state; } - -static secbool _recv_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const pb_msgdesc_t *fields, void *msg) { - usb_read_state state = {.iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = buf}; - - pb_istream_t stream = {.callback = &_usb_read, - .state = &state, - .bytes_left = msg_size, - .errmsg = NULL}; - - if (false == pb_decode_noinit(&stream, fields, msg)) { - return secfalse; - } - - _usb_read_flush(&state); - - return sectrue; -} - -#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default -#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ - { \ - msg_recv.FIELD.funcs.decode = &CALLBACK; \ - msg_recv.FIELD.arg = (void *)ARGUMENT; \ - } -#define MSG_RECV(TYPE) \ - _recv_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv) - -void send_user_abort(uint8_t iface_num, const char *msg) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); - MSG_SEND_ASSIGN_STRING(message, msg); - MSG_SEND(Failure); -} - -static void send_msg_features(uint8_t iface_num, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_SEND_INIT(Features); - MSG_SEND_ASSIGN_STRING(vendor, "trezor.io"); - MSG_SEND_ASSIGN_REQUIRED_VALUE(major_version, VERSION_MAJOR); - MSG_SEND_ASSIGN_REQUIRED_VALUE(minor_version, VERSION_MINOR); - MSG_SEND_ASSIGN_REQUIRED_VALUE(patch_version, VERSION_PATCH); - MSG_SEND_ASSIGN_VALUE(bootloader_mode, true); - MSG_SEND_ASSIGN_STRING(model, MODEL_NAME); - MSG_SEND_ASSIGN_STRING(internal_model, MODEL_INTERNAL_NAME); - if (vhdr && hdr) { - MSG_SEND_ASSIGN_VALUE(firmware_present, true); - MSG_SEND_ASSIGN_VALUE(fw_major, (hdr->version & 0xFF)); - MSG_SEND_ASSIGN_VALUE(fw_minor, ((hdr->version >> 8) & 0xFF)); - MSG_SEND_ASSIGN_VALUE(fw_patch, ((hdr->version >> 16) & 0xFF)); - MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len); - } else { - MSG_SEND_ASSIGN_VALUE(firmware_present, false); - } - if (unit_properties()->color_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_color, unit_properties()->color); - } - if (unit_properties()->packaging_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_packaging, unit_properties()->packaging); - } - if (unit_properties()->btconly_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_properties()->btconly); - } - -#if USE_OPTIGA - MSG_SEND_ASSIGN_VALUE(bootloader_locked, - (secret_bootloader_locked() == sectrue)); -#endif - MSG_SEND(Features); -} - -void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_RECV_INIT(Initialize); - MSG_RECV(Initialize); - send_msg_features(iface_num, vhdr, hdr); -} - -void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_RECV_INIT(GetFeatures); - MSG_RECV(GetFeatures); - send_msg_features(iface_num, vhdr, hdr); -} - -void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - MSG_RECV_INIT(Ping); - MSG_RECV(Ping); - - MSG_SEND_INIT(Success); - MSG_SEND_ASSIGN_STRING(message, msg_recv.message); - MSG_SEND(Success); -} - -static uint32_t firmware_remaining; -static uint32_t firmware_block; -static uint32_t chunk_requested; -static uint32_t erase_offset; - -void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - firmware_remaining = 0; - firmware_block = 0; - chunk_requested = 0; - erase_offset = 0; - - MSG_RECV_INIT(FirmwareErase); - MSG_RECV(FirmwareErase); - - firmware_remaining = msg_recv.has_length ? msg_recv.length : 0; - if ((firmware_remaining > 0) && - ((firmware_remaining % sizeof(uint32_t)) == 0) && - (firmware_remaining <= FIRMWARE_MAXSIZE)) { - // request new firmware - chunk_requested = (firmware_remaining > IMAGE_INIT_CHUNK_SIZE) - ? IMAGE_INIT_CHUNK_SIZE - : firmware_remaining; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, 0); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - } else { - // invalid firmware size - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong firmware size"); - MSG_SEND(Failure); - } -} - -static uint32_t chunk_size = 0; - -#ifndef TREZOR_EMULATOR -__attribute__((section(".buf"))) -#endif -uint32_t chunk_buffer[IMAGE_CHUNK_SIZE / 4]; - -#define CHUNK_BUFFER_PTR ((const uint8_t *const)&chunk_buffer) - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, - void **arg) { -#define BUFSIZE 32768 - - size_t offset = (size_t)(*arg); - - if (stream->bytes_left > IMAGE_CHUNK_SIZE) { - chunk_size = 0; - return false; - } - - if (offset == 0) { - // clear chunk buffer - memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); - } - - uint32_t chunk_written = offset; - chunk_size = offset + stream->bytes_left; - - while (stream->bytes_left) { - // update loader but skip first block - if (firmware_block > 0) { - ui_screen_install_progress_upload( - 1000 * (firmware_block * IMAGE_CHUNK_SIZE + chunk_written) / - (firmware_block * IMAGE_CHUNK_SIZE + firmware_remaining)); - } - // read data - if (!pb_read( - stream, (pb_byte_t *)(CHUNK_BUFFER_PTR + chunk_written), - (stream->bytes_left > BUFSIZE) ? BUFSIZE : stream->bytes_left)) { - chunk_size = 0; - return false; - } - chunk_written += BUFSIZE; - } - - return true; -} - -static int version_compare(uint32_t vera, uint32_t verb) { - /* Explicit casts so that we control how compiler does the unsigned shift - * and correctly then promote uint8_t to int without possibility of - * having implementation-defined right shift on negative int - * in case compiler promoted the wrong unsinged int - */ - int a, b; - a = (uint8_t)vera & 0xFF; - b = (uint8_t)verb & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 8) & 0xFF; - b = (uint8_t)(verb >> 8) & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 16) & 0xFF; - b = (uint8_t)(verb >> 16) & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 24) & 0xFF; - b = (uint8_t)(verb >> 24) & 0xFF; - return a - b; -} - -static void detect_installation(const vendor_header *current_vhdr, - const image_header *current_hdr, - const vendor_header *const new_vhdr, - const image_header *const new_hdr, - secbool *is_new, secbool *keep_seed, - secbool *is_newvendor, secbool *is_upgrade) { - *is_new = secfalse; - *keep_seed = secfalse; - *is_newvendor = secfalse; - *is_upgrade = secfalse; - if (sectrue != check_vendor_header_keys(current_vhdr)) { - *is_new = sectrue; - return; - } - if (sectrue != check_image_model(current_hdr)) { - *is_new = sectrue; - return; - } - if (sectrue != check_firmware_min_version(current_hdr->monotonic)) { - *is_new = sectrue; - return; - } - if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, - current_vhdr->vsig_n, - current_vhdr->vpub)) { - *is_new = sectrue; - return; - } - uint8_t hash1[32], hash2[32]; - vendor_header_hash(new_vhdr, hash1); - vendor_header_hash(current_vhdr, hash2); - if (0 != memcmp(hash1, hash2, 32)) { - *is_newvendor = sectrue; - return; - } - if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) { - return; - } - if (version_compare(new_hdr->version, current_hdr->version) > 0) { - *is_upgrade = sectrue; - } - - *keep_seed = sectrue; -} - -static int firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; -static size_t headers_offset = 0; -static size_t read_offset = 0; - -int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - MSG_RECV_INIT(FirmwareUpload); - MSG_RECV_CALLBACK(payload, _read_payload, read_offset); - const secbool r = MSG_RECV(FirmwareUpload); - - if (sectrue != r || chunk_size != (chunk_requested + read_offset)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_CHUNK_SIZE; - } - - static image_header hdr; - - if (firmware_block == 0) { - if (headers_offset == 0) { - // first block and headers are not yet parsed - vendor_header vhdr; - - if (sectrue != read_vendor_header(CHUNK_BUFFER_PTR, &vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER; - } - - if (sectrue != check_vendor_header_model(&vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong model"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL; - } - - if (sectrue != check_vendor_header_keys(&vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; - } - - const image_header *received_hdr = - read_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); - - if (received_hdr != - (const image_header *)(CHUNK_BUFFER_PTR + vhdr.hdrlen)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER; - } - - if (sectrue != check_image_model(received_hdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_MODEL; - } - - if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; - } - - if (sectrue != check_firmware_min_version(received_hdr->monotonic)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Cannot downgrade to this version"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION; - } - - memcpy(&hdr, received_hdr, sizeof(hdr)); - - vendor_header current_vhdr; - - secbool is_new = secfalse; - - if (sectrue != - read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { - is_new = sectrue; - } - - const image_header *current_hdr = NULL; - - if (is_new == secfalse) { - current_hdr = read_image_header( - (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); - - if (current_hdr != - (const image_header *)(FIRMWARE_START + current_vhdr.hdrlen)) { - is_new = sectrue; - } - } - - secbool should_keep_seed = secfalse; - secbool is_newvendor = secfalse; - secbool is_upgrade = secfalse; - if (is_new == secfalse) { - detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, - &should_keep_seed, &is_newvendor, &is_upgrade); - } - - secbool is_ilu = secfalse; // interaction-less update - - if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) { - IMAGE_HASH_CTX ctx; - uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; - IMAGE_HASH_INIT(&ctx); - IMAGE_HASH_UPDATE(&ctx, CHUNK_BUFFER_PTR, - vhdr.hdrlen + received_hdr->hdrlen); - IMAGE_HASH_FINAL(&ctx, hash); - - // the firmware must be the same as confirmed by the user - boot_args_t args = {0}; - bootargs_get_args(&args); - - if (memcmp(args.hash, hash, sizeof(hash)) != 0) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Firmware mismatch"); - MSG_SEND(Failure); - return UPLOAD_ERR_FIRMWARE_MISMATCH; - } - - // the firmware must be from the same vendor - // the firmware must be newer - if (is_upgrade != sectrue || is_newvendor != secfalse) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Not a firmware upgrade"); - MSG_SEND(Failure); - return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE; - } - - if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Not a full-trust image"); - MSG_SEND(Failure); - return UPLOAD_ERR_NOT_FULLTRUST_IMAGE; - } - - // upload the firmware without confirmation - is_ilu = sectrue; - } - -#if defined USE_OPTIGA - if (secfalse != secret_optiga_present() && - ((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Install restricted"); - MSG_SEND(Failure); - return UPLOAD_ERR_BOOTLOADER_LOCKED; - } -#endif - - uint32_t response = INPUT_CANCEL; - if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) && - (sectrue == is_new || sectrue == is_ilu)) { - // new installation or interaction less updated - auto confirm - // only allowed for full-trust images - response = INPUT_CONFIRM; - } else { - if (sectrue != is_new) { - int version_cmp = version_compare(hdr.version, current_hdr->version); - response = ui_screen_install_confirm( - &vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp); - } else { - response = ui_screen_install_confirm(&vhdr, &hdr, sectrue, - is_newvendor, is_new, 0); - } - } - - if (INPUT_CANCEL == response) { - send_user_abort(iface_num, "Firmware install cancelled"); - return UPLOAD_ERR_USER_ABORT; - } - - ui_screen_install_start(); - - // if firmware is not upgrade, erase storage - if (sectrue != should_keep_seed) { -#ifdef USE_STORAGE_HWKEY - secret_bhk_regenerate(); -#endif - ensure(erase_storage(NULL), NULL); - } - - headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; - read_offset = IMAGE_INIT_CHUNK_SIZE; - - // request the rest of the first chunk - MSG_SEND_INIT(FirmwareRequest); - uint32_t chunk_limit = (firmware_remaining > IMAGE_CHUNK_SIZE) - ? IMAGE_CHUNK_SIZE - : firmware_remaining; - chunk_requested = chunk_limit - read_offset; - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, read_offset); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - - firmware_remaining -= read_offset; - return (int)firmware_remaining; - } else { - // first block with the headers parsed -> the first chunk is now complete - read_offset = 0; - } - } - - // should not happen, but double-check - if (flash_area_get_address(&FIRMWARE_AREA, firmware_block * IMAGE_CHUNK_SIZE, - 0) == NULL) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); - MSG_SEND(Failure); - return UPLOAD_ERR_FIRMWARE_TOO_BIG; - } - - if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, - CHUNK_BUFFER_PTR + headers_offset, - chunk_size - headers_offset)) { - if (firmware_upload_chunk_retry > 0) { - --firmware_upload_chunk_retry; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - return (int)firmware_remaining; - } - - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_CHUNK_HASH; - } - - // buffer with the received data - const uint32_t *src = (const uint32_t *)CHUNK_BUFFER_PTR; - // number of received bytes - uint32_t bytes_remaining = chunk_size; - // offset into the FIRMWARE_AREA part of the flash - uint32_t write_offset = firmware_block * IMAGE_CHUNK_SIZE; - - ensure((chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL); - - while (bytes_remaining > 0) { - // erase flash before writing - uint32_t bytes_erased = 0; - - if (write_offset >= erase_offset) { - // erase the next flash section - ensure( - flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased), - NULL); - erase_offset += bytes_erased; - } else { - // some erased space left from the previous round => use it - bytes_erased = erase_offset - write_offset; - } - - // write the received data - uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining); - ensure(flash_unlock_write(), NULL); - ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src, - bytes_to_write), - NULL); - ensure(flash_lock_write(), NULL); - - write_offset += bytes_to_write; - src += bytes_to_write / sizeof(uint32_t); - - bytes_remaining -= bytes_to_write; - } - - firmware_remaining -= chunk_requested; - - if (firmware_remaining == 0) { - // erase the rest (unused part) of the FIRMWARE_AREA - uint32_t bytes_erased = 0; - do { - ensure( - flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased), - NULL); - erase_offset += bytes_erased; - } while (bytes_erased > 0); - } - - headers_offset = 0; - firmware_block++; - firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; - - if (firmware_remaining > 0) { - chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE) - ? IMAGE_CHUNK_SIZE - : firmware_remaining; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - } else { - MSG_SEND_INIT(Success); - MSG_SEND(Success); - } - return (int)firmware_remaining; -} - -secbool bootloader_WipeDevice(void) { - return erase_device(ui_screen_wipe_progress); -} - -int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - secbool wipe_result = bootloader_WipeDevice(); - if (sectrue != wipe_result) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); - MSG_SEND(Failure); - return WIPE_ERR_CANNOT_ERASE; - } else { - MSG_SEND_INIT(Success); - MSG_SEND(Success); - return WIPE_OK; - } -} - -void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - // consume remaining message - int remaining_chunks = 0; - - if (msg_size > (USB_PACKET_SIZE - MSG_HEADER1_LEN)) { - // calculate how many blocks need to be read to drain the message (rounded - // up to not leave any behind) - remaining_chunks = (msg_size - (USB_PACKET_SIZE - MSG_HEADER1_LEN) + - ((USB_PACKET_SIZE - MSG_HEADER2_LEN) - 1)) / - (USB_PACKET_SIZE - MSG_HEADER2_LEN); - } - - for (int i = 0; i < remaining_chunks; i++) { - // read next packet (with retry) - _usb_webusb_read_retry(iface_num, buf); - } - - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage); - MSG_SEND_ASSIGN_STRING(message, "Unexpected message"); - MSG_SEND(Failure); -} - -#if defined USE_OPTIGA -void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - secret_optiga_erase(); - MSG_SEND_INIT(Success); - MSG_SEND(Success); -} -#endif diff --git a/core/embed/projects/bootloader/messages.h b/core/embed/projects/bootloader/messages.h deleted file mode 100644 index 5623ff3b5f5..00000000000 --- a/core/embed/projects/bootloader/messages.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 __MESSAGES_H__ -#define __MESSAGES_H__ - -#include - -#include - -#define USB_TIMEOUT 500 -#define USB_PACKET_SIZE 64 - -#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 - -enum { - UPLOAD_OK = 0, - UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, - UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, - UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, - UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL = -15, - UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, - UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, - UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, - UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION = -16, - UPLOAD_ERR_USER_ABORT = -7, - UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, - UPLOAD_ERR_INVALID_CHUNK_HASH = -9, - UPLOAD_ERR_BOOTLOADER_LOCKED = -10, - UPLOAD_ERR_FIRMWARE_MISMATCH = -11, - UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12, - UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, - UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, -}; - -enum { - WIPE_OK = 0, - WIPE_ERR_CANNOT_ERASE = -1, -}; - -secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, - uint32_t *msg_size); - -void send_user_abort(uint8_t iface_num, const char *msg); - -void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr); -void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr); -void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); -void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); -void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); - -#ifdef USE_OPTIGA -void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -#endif - -secbool bootloader_WipeDevice(void); - -#endif diff --git a/core/embed/projects/bootloader/poll.c b/core/embed/projects/bootloader/poll.c new file mode 100644 index 00000000000..20bc612694c --- /dev/null +++ b/core/embed/projects/bootloader/poll.c @@ -0,0 +1,62 @@ +/* + * 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 "poll.h" + +#include +#include + +#ifdef TREZOR_EMULATOR +#include "SDL.h" +#endif + +uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, + poll_event_t* event, uint32_t timeout_ms) { + uint32_t start = systick_ms(); + + while (systick_ms() - start < timeout_ms) { +#ifdef TREZOR_EMULATOR + // Ensures that SDL events are processed. This prevents the emulator from + // freezing when the user interacts with the window. + SDL_PumpEvents(); +#endif + + for (size_t i = 0; i < ifaces_num; i++) { + uint8_t iface_num = ifaces[i] & 0xFF; + if (iface_num < IFACE_USB_MAX) { + if ((ifaces[i] & MODE_READ) == MODE_READ) { + // check if USB can read + if (sectrue == usb_webusb_can_read(iface_num)) { + event->type = EVENT_USB_CAN_READ; + return iface_num; + } + } + } + } + +#ifndef TREZOR_EMULATOR + __WFI(); +#endif + } + + event->type = EVENT_NONE; + return 0; +} diff --git a/core/embed/projects/bootloader/poll.h b/core/embed/projects/bootloader/poll.h new file mode 100644 index 00000000000..1f0c504621f --- /dev/null +++ b/core/embed/projects/bootloader/poll.h @@ -0,0 +1,40 @@ +/* + * 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 + +#include + +#define IFACE_USB_MAX (15) // 0-15 reserved for USB + +#define MODE_READ 0x0000 +#define MODE_WRITE 0x0100 + +typedef enum { + EVENT_NONE = 0, + EVENT_USB_CAN_READ = 0x01, +} poll_event_type_t; + +typedef struct { + poll_event_type_t type; + uint8_t data[16]; +} poll_event_t; + +uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, + poll_event_t* event, uint32_t timeout_ms); diff --git a/core/embed/projects/bootloader/protob/.gitignore b/core/embed/projects/bootloader/protob/pb/.gitignore similarity index 100% rename from core/embed/projects/bootloader/protob/.gitignore rename to core/embed/projects/bootloader/protob/pb/.gitignore diff --git a/core/embed/projects/bootloader/protob/Makefile b/core/embed/projects/bootloader/protob/pb/Makefile similarity index 100% rename from core/embed/projects/bootloader/protob/Makefile rename to core/embed/projects/bootloader/protob/pb/Makefile diff --git a/core/embed/projects/bootloader/protob/messages.options b/core/embed/projects/bootloader/protob/pb/messages.options similarity index 100% rename from core/embed/projects/bootloader/protob/messages.options rename to core/embed/projects/bootloader/protob/pb/messages.options diff --git a/core/embed/projects/bootloader/protob/messages.pb.c b/core/embed/projects/bootloader/protob/pb/messages.pb.c similarity index 100% rename from core/embed/projects/bootloader/protob/messages.pb.c rename to core/embed/projects/bootloader/protob/pb/messages.pb.c diff --git a/core/embed/projects/bootloader/protob/messages.pb.h b/core/embed/projects/bootloader/protob/pb/messages.pb.h similarity index 100% rename from core/embed/projects/bootloader/protob/messages.pb.h rename to core/embed/projects/bootloader/protob/pb/messages.pb.h diff --git a/core/embed/projects/bootloader/protob/messages.proto b/core/embed/projects/bootloader/protob/pb/messages.proto similarity index 100% rename from core/embed/projects/bootloader/protob/messages.proto rename to core/embed/projects/bootloader/protob/pb/messages.proto diff --git a/core/embed/projects/bootloader/protob/protob.c b/core/embed/projects/bootloader/protob/protob.c new file mode 100644 index 00000000000..df7de10b4a1 --- /dev/null +++ b/core/embed/projects/bootloader/protob/protob.c @@ -0,0 +1,280 @@ +/* + * 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 + +#include +#include + +#if USE_OPTIGA +#include +#endif + +#include "memzero.h" +#include "pb/messages.pb.h" +#include "protob.h" +#include "version.h" +#include "wire/codec_v1.h" + +#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default +#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ + { msg_send.FIELD = VALUE; } +#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + msg_send.FIELD = VALUE; \ + } +#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ + } +#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ + } +#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ + memcpy(msg_send.FIELD.bytes, VALUE, \ + MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ + msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ + } +#define MSG_SEND(TYPE) \ + codec_send_msg(iface->wire, MessageType_MessageType_##TYPE, TYPE##_fields, \ + &msg_send) + +#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default +#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ + { \ + msg_recv.FIELD.funcs.decode = &CALLBACK; \ + msg_recv.FIELD.arg = (void *)ARGUMENT; \ + } +#define MSG_RECV(TYPE) \ + codec_recv_message(iface->wire, msg_size, buf, TYPE##_fields, &msg_recv) + +void send_user_abort(protob_iface_t *iface, const char *msg) { + if (iface == NULL) { + return; + } + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); + MSG_SEND_ASSIGN_STRING(message, msg); + MSG_SEND(Failure); +} + +void send_msg_failure(protob_iface_t *iface, FailureType type, + const char *msg) { + if (iface == NULL) { + return; + } + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, type); + MSG_SEND_ASSIGN_STRING(message, msg); + MSG_SEND(Failure); +} + +void send_msg_success(protob_iface_t *iface, const char *msg) { + if (iface == NULL) { + return; + } + MSG_SEND_INIT(Success); + if (msg != NULL) { + MSG_SEND_ASSIGN_STRING(message, msg); + } + MSG_SEND(Success); +} + +void send_msg_features(protob_iface_t *iface, const vendor_header *const vhdr, + const image_header *const hdr) { + if (iface == NULL) { + return; + } + MSG_SEND_INIT(Features); + MSG_SEND_ASSIGN_STRING(vendor, "trezor.io"); + MSG_SEND_ASSIGN_REQUIRED_VALUE(major_version, VERSION_MAJOR); + MSG_SEND_ASSIGN_REQUIRED_VALUE(minor_version, VERSION_MINOR); + MSG_SEND_ASSIGN_REQUIRED_VALUE(patch_version, VERSION_PATCH); + MSG_SEND_ASSIGN_VALUE(bootloader_mode, true); + MSG_SEND_ASSIGN_STRING(model, MODEL_NAME); + MSG_SEND_ASSIGN_STRING(internal_model, MODEL_INTERNAL_NAME); + if (vhdr && hdr) { + MSG_SEND_ASSIGN_VALUE(firmware_present, true); + MSG_SEND_ASSIGN_VALUE(fw_major, (hdr->version & 0xFF)); + MSG_SEND_ASSIGN_VALUE(fw_minor, ((hdr->version >> 8) & 0xFF)); + MSG_SEND_ASSIGN_VALUE(fw_patch, ((hdr->version >> 16) & 0xFF)); + MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len); + } else { + MSG_SEND_ASSIGN_VALUE(firmware_present, false); + } + if (unit_properties()->color_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_color, unit_properties()->color); + } + if (unit_properties()->packaging_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_packaging, unit_properties()->packaging); + } + if (unit_properties()->btconly_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_properties()->btconly); + } + +#if USE_OPTIGA + MSG_SEND_ASSIGN_VALUE(bootloader_locked, + (secret_bootloader_locked() == sectrue)); +#endif + MSG_SEND(Features); +} + +secbool recv_msg_initialize(protob_iface_t *iface, Initialize *msg, + uint8_t *buf, uint32_t msg_size) { + if (iface == NULL) { + return secfalse; + } + MSG_RECV_INIT(Initialize); + secbool result = MSG_RECV(Initialize); + memcpy(msg, &msg_recv, sizeof(Initialize)); + return result; +} + +secbool recv_msg_get_features(protob_iface_t *iface, GetFeatures *msg, + uint8_t *buf, uint32_t msg_size) { + if (iface == NULL) { + return secfalse; + } + MSG_RECV_INIT(GetFeatures); + secbool result = MSG_RECV(GetFeatures); + memcpy(msg, &msg_recv, sizeof(GetFeatures)); + return result; +} + +secbool recv_msg_ping(protob_iface_t *iface, Ping *msg, uint8_t *buf, + uint32_t msg_size) { + if (iface == NULL) { + return secfalse; + } + MSG_RECV_INIT(Ping); + secbool result = MSG_RECV(Ping); + memcpy(msg, &msg_recv, sizeof(Ping)); + return result; +} + +secbool recv_msg_firmware_erase(protob_iface_t *iface, FirmwareErase *msg, + uint8_t *buf, uint32_t msg_size) { + if (iface == NULL) { + return secfalse; + } + + MSG_RECV_INIT(FirmwareErase); + secbool result = MSG_RECV(FirmwareErase); + memcpy(msg, &msg_recv, sizeof(FirmwareErase)); + return result; +} + +void send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, + uint32_t length) { + if (iface == NULL) { + return; + } + + MSG_SEND_INIT(FirmwareRequest); + MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, offset); + MSG_SEND_ASSIGN_REQUIRED_VALUE(length, length); + MSG_SEND(FirmwareRequest); +} + +typedef struct { + void (*cb)(size_t len, void *ctx); + void *ctx; + uint8_t *buffer; + size_t buffer_size; +} payload_ctx_t; + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool read_payload(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + payload_ctx_t *payload_ctx = (payload_ctx_t *)*arg; +#define BUFSIZE 32768 + + if (stream->bytes_left > payload_ctx->buffer_size) { + return false; + } + + uint32_t bytes_written = 0; + + while (stream->bytes_left) { + uint32_t received = + stream->bytes_left > BUFSIZE ? BUFSIZE : stream->bytes_left; + + // notify of received data + payload_ctx->cb(received, payload_ctx->ctx); + + // read data + if (!pb_read(stream, (pb_byte_t *)(payload_ctx->buffer + bytes_written), + (received))) { + return false; + } + bytes_written += received; + } + + return true; +} + +secbool recv_msg_firmware_upload(protob_iface_t *iface, FirmwareUpload *msg, + uint8_t *buf, uint32_t msg_size, void *ctx, + void (*data_cb)(size_t len, void *ctx), + uint8_t *buffer, size_t buffer_size) { + payload_ctx_t payload_ctx = { + .cb = data_cb, .ctx = ctx, .buffer = buffer, .buffer_size = buffer_size}; + + MSG_RECV_INIT(FirmwareUpload); + MSG_RECV_CALLBACK(payload, read_payload, &payload_ctx); + secbool result = MSG_RECV(FirmwareUpload); + memcpy(msg, &msg_recv, sizeof(FirmwareUpload)); + return result; +} + +void recv_msg_unknown(protob_iface_t *iface, uint32_t msg_size, uint8_t *buf) { + codec_flush(iface->wire, msg_size, buf); + send_msg_failure(iface, FailureType_Failure_UnexpectedMessage, + "Unexpected message"); +} + +void protob_init(protob_iface_t *iface, wire_iface_t *wire) { + iface->wire = wire; +} + +uint32_t protob_get_iface_flag(protob_iface_t *iface) { + return iface->wire->iface_num; +} + +secbool protob_get_msg_header(protob_iface_t *iface, uint8_t *buf, + uint16_t *msg_id, uint32_t *msg_size) { + if (iface != NULL) { + iface->wire->read(buf, iface->wire->rx_len); + return codec_parse_header(buf, msg_id, msg_size); + } + return secfalse; +} diff --git a/core/embed/projects/bootloader/protob/protob.h b/core/embed/projects/bootloader/protob/protob.h new file mode 100644 index 00000000000..083ddce7854 --- /dev/null +++ b/core/embed/projects/bootloader/protob/protob.h @@ -0,0 +1,70 @@ +/* + * 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 + +#include + +#include + +#include "pb/messages.pb.h" + +#include "wire/codec_v1.h" + +typedef struct { + wire_iface_t *wire; +} protob_iface_t; + +void send_user_abort(protob_iface_t *iface, const char *msg); + +void send_msg_features(protob_iface_t *iface, const vendor_header *const vhdr, + const image_header *const hdr); + +void send_msg_failure(protob_iface_t *iface, FailureType type, const char *msg); + +void send_msg_success(protob_iface_t *iface, const char *msg); + +void send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, + uint32_t length); + +secbool recv_msg_initialize(protob_iface_t *iface, Initialize *msg, + uint8_t *buf, uint32_t msg_size); + +secbool recv_msg_get_features(protob_iface_t *iface, GetFeatures *msg, + uint8_t *buf, uint32_t msg_size); + +secbool recv_msg_ping(protob_iface_t *iface, Ping *msg, uint8_t *buf, + uint32_t msg_size); + +secbool recv_msg_firmware_erase(protob_iface_t *iface, FirmwareErase *msg, + uint8_t *buf, uint32_t msg_size); + +secbool recv_msg_firmware_upload(protob_iface_t *iface, FirmwareUpload *msg, + uint8_t *buf, uint32_t msg_size, void *ctx, + void (*data_cb)(size_t len, void *ctx), + uint8_t *buffer, size_t buffer_size); + +void recv_msg_unknown(protob_iface_t *iface, uint32_t msg_size, uint8_t *buf); + +void protob_init(protob_iface_t *iface, wire_iface_t *wire); + +uint32_t protob_get_iface_flag(protob_iface_t *iface); + +secbool protob_get_msg_header(protob_iface_t *iface, uint8_t *buf, + uint16_t *msg_id, uint32_t *msg_size); diff --git a/core/embed/projects/bootloader/wire/codec_v1.c b/core/embed/projects/bootloader/wire/codec_v1.c new file mode 100644 index 00000000000..6fbcf6c0fb9 --- /dev/null +++ b/core/embed/projects/bootloader/wire/codec_v1.c @@ -0,0 +1,248 @@ +/* + * 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 +#include +#include "memzero.h" + +#include "codec_v1.h" + +#define MSG_HEADER1_LEN 9 +#define MSG_HEADER2_LEN 1 + +typedef struct { + wire_iface_t *iface; + + uint8_t packet_index; + uint8_t packet_pos; + uint8_t buf[MAX_PACKET_SIZE]; + +} packet_write_state_t; + +typedef struct { + wire_iface_t *iface; + uint8_t packet_index; + uint8_t packet_pos; + uint8_t *buf; +} packet_read_state_t; + +secbool codec_parse_header(const uint8_t *buf, uint16_t *msg_id, + uint32_t *msg_size) { + if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { + return secfalse; + } + *msg_id = (buf[3] << 8) + buf[4]; + *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + return sectrue; +} + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { + packet_write_state_t *state = (packet_write_state_t *)(stream->state); + + size_t tx_len = state->iface->tx_len; + uint8_t *tx_buf = state->buf; + + size_t written = 0; + // while we have data left + while (written < count) { + size_t remaining = count - written; + // if all remaining data fit into our packet + if (state->packet_pos + remaining <= tx_len) { + // append data from buf to state->buf + memcpy(tx_buf + state->packet_pos, buf + written, remaining); + // advance position + state->packet_pos += remaining; + // and return + return true; + } else { + // append data that fits + memcpy(tx_buf + state->packet_pos, buf + written, + tx_len - state->packet_pos); + written += tx_len - state->packet_pos; + // send packet + bool ok = state->iface->write(tx_buf, tx_len); + ensure(sectrue * ok, NULL); + // prepare new packet + state->packet_index++; + memzero(tx_buf, tx_len); + tx_buf[0] = '?'; + state->packet_pos = MSG_HEADER2_LEN; + } + } + + return true; +} + +static void write_flush(packet_write_state_t *state) { + size_t packet_size = state->iface->tx_len; + + // if packet is not filled up completely + if (state->packet_pos < packet_size) { + // pad it with zeroes + memzero(state->buf + state->packet_pos, packet_size - state->packet_pos); + } + // send packet + bool ok = state->iface->write(state->buf, packet_size); + ensure(sectrue * (ok), NULL); +} + +secbool codec_send_msg(wire_iface_t *iface, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg) { + // determine message size by serializing it into a dummy stream + pb_ostream_t sizestream = {.callback = NULL, + .state = NULL, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + if (false == pb_encode(&sizestream, fields, msg)) { + return secfalse; + } + const uint32_t msg_size = sizestream.bytes_written; + + packet_write_state_t state = { + .iface = iface, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .buf = + { + '?', + '#', + '#', + (msg_id >> 8) & 0xFF, + msg_id & 0xFF, + (msg_size >> 24) & 0xFF, + (msg_size >> 16) & 0xFF, + (msg_size >> 8) & 0xFF, + msg_size & 0xFF, + }, + }; + + pb_ostream_t stream = {.callback = &write, + .state = &state, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + + if (false == pb_encode(&stream, fields, msg)) { + return secfalse; + } + + write_flush(&state); + + return sectrue; +} + +static void read_retry(wire_iface_t *iface, uint8_t *buf) { + size_t packet_size = iface->rx_len; + + for (int retry = 0;; retry++) { + int r = iface->read(buf, packet_size); + if (r != packet_size) { // reading failed + if (r == 0 && retry < 10) { + // only timeout => let's try again + continue; + } else { + iface->error(); + } + } + return; // success + } +} + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool read(pb_istream_t *stream, uint8_t *buf, size_t count) { + packet_read_state_t *state = (packet_read_state_t *)(stream->state); + + size_t packet_size = state->iface->rx_len; + + size_t read = 0; + // while we have data left + while (read < count) { + size_t remaining = count - read; + // if all remaining data fit into our packet + if (state->packet_pos + remaining <= packet_size) { + // append data from buf to state->buf + memcpy(buf + read, state->buf + state->packet_pos, remaining); + // advance position + state->packet_pos += remaining; + // and return + return true; + } else { + // append data that fits + memcpy(buf + read, state->buf + state->packet_pos, + packet_size - state->packet_pos); + read += packet_size - state->packet_pos; + // read next packet (with retry) + read_retry(state->iface, state->buf); + // prepare next packet + state->packet_index++; + state->packet_pos = MSG_HEADER2_LEN; + } + } + + return true; +} + +static void read_flush(packet_read_state_t *state) { (void)state; } + +secbool codec_recv_message(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg) { + packet_read_state_t state = {.iface = iface, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .buf = buf}; + + pb_istream_t stream = {.callback = &read, + .state = &state, + .bytes_left = msg_size, + .errmsg = NULL}; + + if (false == pb_decode_noinit(&stream, fields, msg)) { + return secfalse; + } + + read_flush(&state); + + return sectrue; +} + +void codec_flush(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf) { + // consume remaining message + int remaining_chunks = 0; + + size_t packet_size = iface->rx_len; + + if (msg_size > (packet_size - MSG_HEADER1_LEN)) { + // calculate how many blocks need to be read to drain the message (rounded + // up to not leave any behind) + remaining_chunks = (msg_size - (packet_size - MSG_HEADER1_LEN) + + ((packet_size - MSG_HEADER2_LEN) - 1)) / + (packet_size - MSG_HEADER2_LEN); + } + + for (int i = 0; i < remaining_chunks; i++) { + // read next packet (with retry) + read_retry(iface, buf); + } +} diff --git a/core/embed/projects/bootloader/wire/codec_v1.h b/core/embed/projects/bootloader/wire/codec_v1.h new file mode 100644 index 00000000000..4e9448b24b9 --- /dev/null +++ b/core/embed/projects/bootloader/wire/codec_v1.h @@ -0,0 +1,50 @@ +/* + * 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 + +#include + +#include + +#define MAX_PACKET_SIZE 256 + +typedef struct { + uint8_t iface_num; + size_t tx_len; + size_t rx_len; + + // write function pointer + bool (*write)(uint8_t *data, size_t size); + // read function pointer + int (*read)(uint8_t *buffer, size_t buffer_size); + + void (*error)(void); +} wire_iface_t; + +secbool codec_parse_header(const uint8_t *buf, uint16_t *msg_id, + uint32_t *msg_size); + +secbool codec_send_msg(wire_iface_t *iface, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg); + +secbool codec_recv_message(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg); + +void codec_flush(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf); diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.c b/core/embed/projects/bootloader/wire/wire_iface_usb.c new file mode 100644 index 00000000000..5a562bb7d31 --- /dev/null +++ b/core/embed/projects/bootloader/wire/wire_iface_usb.c @@ -0,0 +1,111 @@ +/* + * 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 "wire_iface_usb.h" + +#include + +#define USB_TIMEOUT 500 +#define USB_PACKET_SIZE 64 +#define USB_IFACE_NUM 0 + +static bool usb_write(uint8_t* data, size_t size) { + if (size != USB_PACKET_SIZE) { + return false; + } + + int r = usb_webusb_write_blocking(USB_IFACE_NUM, data, size, USB_TIMEOUT); + + return r == size; +} + +static int usb_read(uint8_t* buffer, size_t buffer_size) { + if (buffer_size != USB_PACKET_SIZE) { + return -1; + } + + int r = usb_webusb_read_blocking(USB_IFACE_NUM, buffer, USB_PACKET_SIZE, + USB_TIMEOUT); + + return r; +} + +static void usb_error(void) { + error_shutdown_ex("USB ERROR", + "Error reading from USB. Try different USB cable.", NULL); +} + +static void usb_init_all(secbool usb21_landing) { + usb_dev_info_t dev_info = { + .device_class = 0x00, + .device_subclass = 0x00, + .device_protocol = 0x00, + .vendor_id = 0x1209, + .product_id = 0x53C0, + .release_num = 0x0200, + .manufacturer = MODEL_USB_MANUFACTURER, + .product = MODEL_USB_PRODUCT, + .serial_number = "000000000000000000000000", + .interface = "TREZOR Interface", + .usb21_enabled = sectrue, + .usb21_landing = usb21_landing, + }; + + static uint8_t rx_buffer[USB_PACKET_SIZE]; + + static const usb_webusb_info_t webusb_info = { + .iface_num = USB_IFACE_NUM, +#ifdef TREZOR_EMULATOR + .emu_port = 21324, +#else + .ep_in = 0x01, + .ep_out = 0x01, +#endif + .subclass = 0, + .protocol = 0, + .max_packet_len = sizeof(rx_buffer), + .rx_buffer = rx_buffer, + .polling_interval = 1, + }; + + ensure(usb_init(&dev_info), NULL); + + ensure(usb_webusb_add(&webusb_info), NULL); + + ensure(usb_start(), NULL); +} + +void usb_iface_init(wire_iface_t* iface, secbool no_fw) { + usb_init_all(no_fw); + + iface->iface_num = USB_IFACE_NUM; + iface->tx_len = USB_PACKET_SIZE; + iface->rx_len = USB_PACKET_SIZE; + iface->write = &usb_write; + iface->read = &usb_read; + iface->error = &usb_error; +} + +void usb_iface_deinit(wire_iface_t* iface) { + memset(iface, 0, sizeof(wire_iface_t)); + usb_deinit(); +} diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.h b/core/embed/projects/bootloader/wire/wire_iface_usb.h new file mode 100644 index 00000000000..8fe76866ecd --- /dev/null +++ b/core/embed/projects/bootloader/wire/wire_iface_usb.h @@ -0,0 +1,24 @@ +/* + * 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 + +#include "codec_v1.h" + +void usb_iface_init(wire_iface_t* iface, secbool no_fw); diff --git a/core/embed/projects/bootloader/workflow/wf_auto_update.c b/core/embed/projects/bootloader/workflow/wf_auto_update.c new file mode 100644 index 00000000000..25a6c69bea8 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_auto_update.c @@ -0,0 +1,40 @@ +/* + * 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 "bootui.h" +#include "workflow.h" +#include "workflow_internal.h" + +workflow_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr) { + workflow_reset_jump(); + ui_set_initial_setup(true); + ui_screen_connect(); + + workflow_result_t res = WF_RETURN; + while (res == WF_RETURN || res == WF_STAY) { + res = workflow_host_control(vhdr, hdr, ui_screen_connect); + } + return workflow_exit_common(res); +} diff --git a/core/embed/projects/bootloader/workflow/wf_bootloader.c b/core/embed/projects/bootloader/workflow/wf_bootloader.c new file mode 100644 index 00000000000..f252962a21c --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c @@ -0,0 +1,93 @@ +/* + * 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 + +#ifdef TREZOR_EMULATOR +#include "SDL.h" +#endif + +#include "bootui.h" +#include "workflow.h" +#include "workflow_internal.h" + +workflow_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present) { + workflow_reset_jump(); + ui_set_initial_setup(false); + + screen_t screen = SCREEN_INTRO; + uint32_t ui_result = 0; + + while (true) { + switch (screen) { + case SCREEN_INTRO: + ui_result = ui_screen_intro(vhdr, hdr, firmware_present); + if (ui_result == 1) { + screen = SCREEN_MENU; + } + if (ui_result == 2) { + screen = SCREEN_WAIT_FOR_HOST; + } + break; + case SCREEN_MENU: + ui_result = ui_screen_menu(firmware_present); + if (ui_result == 0xAABBCCDD) { // exit menu + screen = SCREEN_INTRO; + } + if (ui_result == 0x11223344) { // reboot +#ifndef USE_HASH_PROCESSOR + ui_screen_boot_stage_1(true); +#endif + workflow_allow_jump_1(); + workflow_allow_jump_2(); + return WF_CONTINUE_TO_FIRMWARE; + } + if (ui_result == 0x55667788) { // wipe + workflow_result_t r = workflow_wipe_device(NULL, 0, NULL); + if (r == WF_SHUTDOWN) { + return r; + } + } + break; + case SCREEN_WAIT_FOR_HOST: + ui_screen_connect(); + workflow_result_t res = + workflow_host_control(vhdr, hdr, ui_screen_connect); + switch (res) { + case WF_STAY: + break; + case WF_RETURN: + screen = SCREEN_INTRO; + break; + default: + return workflow_exit_common(res); + } + break; + default: + return WF_WIPE_AND_SHUTDOWN; + break; + } + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_empty_device.c b/core/embed/projects/bootloader/workflow/wf_empty_device.c new file mode 100644 index 00000000000..41a3903a927 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_empty_device.c @@ -0,0 +1,60 @@ +/* + * 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 +#include +#include + +#ifdef USE_STORAGE_HWKEY +#include +#endif + +#include "bootui.h" +#include "workflow.h" +#include "workflow_internal.h" + +workflow_result_t workflow_empty_device(void) { + workflow_reset_jump(); + ui_set_initial_setup(true); + +#ifdef USE_STORAGE_HWKEY + secret_bhk_regenerate(); +#endif + ensure(erase_storage(NULL), NULL); + + // keep the model screen up for a while +#ifndef USE_BACKLIGHT + systick_delay_ms(1500); +#else + // backlight fading takes some time so the explicit delay here is + // shorter + systick_delay_ms(1000); +#endif + + workflow_result_t res = WF_RETURN; + while (res == WF_RETURN || res == WF_STAY) { + ui_screen_welcome(); + res = workflow_host_control(NULL, NULL, ui_screen_welcome); + } + return workflow_exit_common(res); +} diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c new file mode 100644 index 00000000000..f255d57617a --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c @@ -0,0 +1,553 @@ +/* + * 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 +#include +#include + +#if USE_OPTIGA +#include +#endif + +#include + +#include "bootui.h" +#include "protob/protob.h" +#include "version_check.h" +#include "workflow.h" +#include "workflow_internal.h" + +#ifdef TREZOR_EMULATOR +#include "emulator.h" +#endif + +typedef enum { + UPLOAD_OK = 0, + UPLOAD_IN_PROGRESS = 1, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL = -15, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION = -16, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, + UPLOAD_ERR_BOOTLOADER_LOCKED = -10, + UPLOAD_ERR_FIRMWARE_MISMATCH = -11, + UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12, + UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, + UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, +} upload_status_t; + +#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 + +#ifndef TREZOR_EMULATOR +__attribute__((section(".buf"))) +#endif +uint32_t chunk_buffer[IMAGE_CHUNK_SIZE / 4]; + +typedef struct { + uint32_t firmware_remaining; + uint32_t firmware_block; + uint32_t chunk_requested; + uint32_t erase_offset; + int32_t firmware_upload_chunk_retry; + size_t headers_offset; + size_t read_offset; + uint32_t chunk_size; +} firmware_update_ctx_t; + +static int version_compare(uint32_t vera, uint32_t verb) { + /* Explicit casts so that we control how compiler does the unsigned shift + * and correctly then promote uint8_t to int without possibility of + * having implementation-defined right shift on negative int + * in case compiler promoted the wrong unsigned int + */ + int a, b; + a = (uint8_t)vera & 0xFF; + b = (uint8_t)verb & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 8) & 0xFF; + b = (uint8_t)(verb >> 8) & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 16) & 0xFF; + b = (uint8_t)(verb >> 16) & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 24) & 0xFF; + b = (uint8_t)(verb >> 24) & 0xFF; + return a - b; +} + +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, + const vendor_header *const new_vhdr, + const image_header *const new_hdr, + secbool *is_new, secbool *keep_seed, + secbool *is_newvendor, secbool *is_upgrade) { + *is_new = secfalse; + *keep_seed = secfalse; + *is_newvendor = secfalse; + *is_upgrade = secfalse; + if (sectrue != check_vendor_header_keys(current_vhdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_firmware_min_version(current_hdr->monotonic)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { + *is_new = sectrue; + return; + } + uint8_t hash1[32], hash2[32]; + vendor_header_hash(new_vhdr, hash1); + vendor_header_hash(current_vhdr, hash2); + if (0 != memcmp(hash1, hash2, 32)) { + *is_newvendor = sectrue; + return; + } + if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) { + return; + } + if (version_compare(new_hdr->version, current_hdr->version) > 0) { + *is_upgrade = sectrue; + } + + *keep_seed = sectrue; +} + +static void fw_data_received(size_t len, void *ctx) { + firmware_update_ctx_t *context = (firmware_update_ctx_t *)ctx; + + context->chunk_size += len; + // update loader but skip first block + if (context->firmware_block > 0) { + ui_screen_install_progress_upload( + 1000 * + (context->firmware_block * IMAGE_CHUNK_SIZE + context->chunk_size) / + (context->firmware_block * IMAGE_CHUNK_SIZE + + context->firmware_remaining)); + } +} + +static upload_status_t process_msg_FirmwareUpload(protob_iface_t *iface, + uint32_t msg_size, + uint8_t *buf, + firmware_update_ctx_t *ctx) { + FirmwareUpload msg; + + const secbool r = recv_msg_firmware_upload( + iface, &msg, buf, msg_size, ctx, fw_data_received, + &((uint8_t *)chunk_buffer)[ctx->read_offset], + sizeof(chunk_buffer) - ctx->read_offset); + + if (sectrue != r || + ctx->chunk_size != (ctx->chunk_requested + ctx->read_offset)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid chunk size"); + return UPLOAD_ERR_INVALID_CHUNK_SIZE; + } + + static image_header hdr; + + if (ctx->firmware_block == 0) { + if (ctx->headers_offset == 0) { + // first block and headers are not yet parsed + vendor_header vhdr; + + if (sectrue != read_vendor_header((uint8_t *)chunk_buffer, &vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid vendor header"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER; + } + + if (sectrue != check_vendor_header_model(&vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong model"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL; + } + + if (sectrue != check_vendor_header_keys(&vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid vendor header signature"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header((uint8_t *)chunk_buffer + vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); + + if (received_hdr != + (const image_header *)((uint8_t *)chunk_buffer + vhdr.hdrlen)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid firmware header"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER; + } + + if (sectrue != check_image_model(received_hdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong firmware model"); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid firmware signature"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + if (sectrue != check_firmware_min_version(received_hdr->monotonic)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware downgrade protection"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + + vendor_header current_vhdr; + + secbool is_new = secfalse; + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); + + if (current_hdr != + (const image_header *)(void *)(FIRMWARE_START + + current_vhdr.hdrlen)) { + is_new = sectrue; + } + } + + secbool should_keep_seed = secfalse; + secbool is_newvendor = secfalse; + secbool is_upgrade = secfalse; + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &should_keep_seed, &is_newvendor, &is_upgrade); + } + + secbool is_ilu = secfalse; // interaction-less update + + if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) { + IMAGE_HASH_CTX ctx; + uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; + IMAGE_HASH_INIT(&ctx); + IMAGE_HASH_UPDATE(&ctx, (uint8_t *)chunk_buffer, + vhdr.hdrlen + received_hdr->hdrlen); + IMAGE_HASH_FINAL(&ctx, hash); + + // the firmware must be the same as confirmed by the user + boot_args_t args = {0}; + bootargs_get_args(&args); + + if (memcmp(args.hash, hash, sizeof(hash)) != 0) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware mismatch"); + return UPLOAD_ERR_FIRMWARE_MISMATCH; + } + + // the firmware must be from the same vendor + // the firmware must be newer + if (is_upgrade != sectrue || is_newvendor != secfalse) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Not a firmware upgrade"); + return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE; + } + + if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Not a full-trust image"); + return UPLOAD_ERR_NOT_FULLTRUST_IMAGE; + } + + // upload the firmware without confirmation + is_ilu = sectrue; + } + +#if defined USE_OPTIGA + if (secfalse != secret_optiga_present() && + ((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Install restricted"); + return UPLOAD_ERR_BOOTLOADER_LOCKED; + } +#endif + + ui_result_t response = UI_RESULT_CANCEL; + if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) && + (sectrue == is_new || sectrue == is_ilu)) { + // new installation or interaction less updated - auto confirm + // only allowed for full-trust images + response = UI_RESULT_CONFIRM; + } else { + if (sectrue != is_new) { + int version_cmp = version_compare(hdr.version, current_hdr->version); + response = ui_screen_install_confirm( + &vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp); + } else { + response = ui_screen_install_confirm(&vhdr, &hdr, sectrue, + is_newvendor, is_new, 0); + } + } + + if (UI_RESULT_CONFIRM != response) { + send_user_abort(iface, "Firmware install cancelled"); + return UPLOAD_ERR_USER_ABORT; + } + + ui_screen_install_start(); + + // if firmware is not upgrade, erase storage + if (sectrue != should_keep_seed) { +#ifdef USE_STORAGE_HWKEY + secret_bhk_regenerate(); +#endif + ensure(erase_storage(NULL), NULL); + } + + ctx->headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; + ctx->read_offset = IMAGE_INIT_CHUNK_SIZE; + + // request the rest of the first chunk + uint32_t chunk_limit = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE) + ? IMAGE_CHUNK_SIZE + : ctx->firmware_remaining; + ctx->chunk_requested = chunk_limit - ctx->read_offset; + + send_msg_request_firmware(iface, ctx->read_offset, ctx->chunk_requested); + + ctx->firmware_remaining -= ctx->read_offset; + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; + } else { + // first block with the headers parsed -> the first chunk is now complete + ctx->read_offset = 0; + } + } + + // should not happen, but double-check + if (flash_area_get_address( + &FIRMWARE_AREA, ctx->firmware_block * IMAGE_CHUNK_SIZE, 0) == NULL) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware too big"); + return UPLOAD_ERR_FIRMWARE_TOO_BIG; + } + + if (sectrue != + check_single_hash(hdr.hashes + ctx->firmware_block * 32, + (uint8_t *)chunk_buffer + ctx->headers_offset, + ctx->chunk_size - ctx->headers_offset)) { + if (ctx->firmware_upload_chunk_retry > 0) { + --ctx->firmware_upload_chunk_retry; + + // clear chunk buffer + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + ctx->chunk_size = 0; + + send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested); + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; + } + + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid chunk hash"); + return UPLOAD_ERR_INVALID_CHUNK_HASH; + } + + // buffer with the received data + const uint32_t *src = (const uint32_t *)chunk_buffer; + // number of received bytes + uint32_t bytes_remaining = ctx->chunk_size; + // offset into the FIRMWARE_AREA part of the flash + uint32_t write_offset = ctx->firmware_block * IMAGE_CHUNK_SIZE; + + ensure((ctx->chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL); + + while (bytes_remaining > 0) { + // erase flash before writing + uint32_t bytes_erased = 0; + + if (write_offset >= ctx->erase_offset) { + // erase the next flash section + ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset, + &bytes_erased), + NULL); + ctx->erase_offset += bytes_erased; + } else { + // some erased space left from the previous round => use it + bytes_erased = ctx->erase_offset - write_offset; + } + + // write the received data + uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining); + ensure(flash_unlock_write(), NULL); + ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src, + bytes_to_write), + NULL); + ensure(flash_lock_write(), NULL); + + write_offset += bytes_to_write; + src += bytes_to_write / sizeof(uint32_t); + + bytes_remaining -= bytes_to_write; + } + + ctx->firmware_remaining -= ctx->chunk_requested; + + if (ctx->firmware_remaining == 0) { + // erase the rest (unused part) of the FIRMWARE_AREA + uint32_t bytes_erased = 0; + do { + ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset, + &bytes_erased), + NULL); + ctx->erase_offset += bytes_erased; + } while (bytes_erased > 0); + } + + ctx->headers_offset = 0; + ctx->firmware_block++; + ctx->firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; + + if (ctx->firmware_remaining > 0) { + ctx->chunk_requested = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE) + ? IMAGE_CHUNK_SIZE + : ctx->firmware_remaining; + + // clear chunk buffer + ctx->chunk_size = 0; + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested); + } else { + send_msg_success(iface, NULL); + } + + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; +} + +workflow_result_t workflow_firmware_update(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf) { + firmware_update_ctx_t ctx = { + .firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT, + }; + + FirmwareErase msg; + secbool res = recv_msg_firmware_erase(iface, &msg, buf, msg_size); + + if (res != sectrue) { + return WF_RETURN; + } + + ctx.firmware_remaining = msg.has_length ? msg.length : 0; + if ((ctx.firmware_remaining > 0) && + ((ctx.firmware_remaining % sizeof(uint32_t)) == 0) && + (ctx.firmware_remaining <= FIRMWARE_MAXSIZE)) { + // clear chunk buffer + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + ctx.chunk_size = 0; + + // request new firmware + ctx.chunk_requested = (ctx.firmware_remaining > IMAGE_INIT_CHUNK_SIZE) + ? IMAGE_INIT_CHUNK_SIZE + : ctx.firmware_remaining; + send_msg_request_firmware(iface, 0, ctx.chunk_requested); + } else { + // invalid firmware size + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong firmware size"); + return WF_RETURN; + } + + upload_status_t s = UPLOAD_IN_PROGRESS; + + while (true) { + uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ}; + poll_event_t e = {0}; + uint8_t i = poll_events(ifaces, 1, &e, 100); + + if (e.type == EVENT_NONE || i != protob_get_iface_flag(iface)) { + continue; + } + + uint16_t msg_id = 0; + msg_size = 0; + + if (sectrue != protob_get_msg_header(iface, buf, &msg_id, &msg_size)) { + // invalid header -> discard + return WF_RETURN; + } + s = process_msg_FirmwareUpload(iface, msg_size, buf, &ctx); + + if (s < 0 && s != UPLOAD_ERR_USER_ABORT) { // error, but not user abort + if (s == UPLOAD_ERR_BOOTLOADER_LOCKED) { + // This function does not return + show_install_restricted_screen(); + } else { + ui_screen_fail(); + } + return WF_SHUTDOWN; + } else if (s == UPLOAD_ERR_USER_ABORT) { + systick_delay_ms(100); + return WF_RETURN; + } else if (s == UPLOAD_OK) { // last chunk received + ui_screen_install_progress_upload(1000); + ui_screen_done(4, sectrue); + ui_screen_done(3, secfalse); + systick_delay_ms(1000); + ui_screen_done(2, secfalse); + systick_delay_ms(1000); + ui_screen_done(1, secfalse); + systick_delay_ms(1000); + return WF_CONTINUE_TO_FIRMWARE; + } + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_get_features.c b/core/embed/projects/bootloader/workflow/wf_get_features.c new file mode 100644 index 00000000000..8749b39183f --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_get_features.c @@ -0,0 +1,34 @@ +/* + * 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 "protob.h" +#include "workflow.h" + +workflow_result_t workflow_get_features(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf, + const vendor_header *const vhdr, + const image_header *const hdr) { + GetFeatures msg_recv; + recv_msg_get_features(iface, &msg_recv, buf, msg_size); + send_msg_features(iface, vhdr, hdr); + return WF_STAY; +} diff --git a/core/embed/projects/bootloader/workflow/wf_host_control.c b/core/embed/projects/bootloader/workflow/wf_host_control.c new file mode 100644 index 00000000000..c0fd4d2259d --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_host_control.c @@ -0,0 +1,125 @@ +/* + * 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 +#include +#include + +#include "poll.h" +#include "protob/protob.h" +#include "wire/wire_iface_usb.h" +#include "workflow.h" +#include "workflow_internal.h" + +workflow_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)) { + wire_iface_t usb_iface = {0}; + protob_iface_t protob_usb_iface = {0}; + + // if both are NULL, we don't have a firmware installed + // let's show a webusb landing page in this case + usb_iface_init(&usb_iface, + (vhdr == NULL && hdr == NULL) ? sectrue : secfalse); + + protob_init(&protob_usb_iface, &usb_iface); + + uint8_t buf[MAX_PACKET_SIZE] = {0}; + + workflow_result_t result = WF_STAY; + + for (;;) { + uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ}; + poll_event_t e = {0}; + + uint8_t i = poll_events(ifaces, 1, &e, 100); + + uint16_t msg_id = 0; + uint32_t msg_size = 0; + protob_iface_t *active_iface = NULL; + + switch (e.type) { + case EVENT_USB_CAN_READ: + if (i == protob_get_iface_flag(&protob_usb_iface) && + sectrue == protob_get_msg_header(&protob_usb_iface, buf, &msg_id, + &msg_size)) { + active_iface = &protob_usb_iface; + } else { + continue; + } + break; + case EVENT_NONE: + default: + continue; + } + + switch (msg_id) { + case MessageType_MessageType_Initialize: + result = workflow_initialize(active_iface, msg_size, buf, vhdr, hdr); + break; + case MessageType_MessageType_Ping: + result = workflow_ping(active_iface, msg_size, buf); + break; + case MessageType_MessageType_WipeDevice: + result = workflow_wipe_device(active_iface, msg_size, buf); + break; + case MessageType_MessageType_FirmwareErase: + result = workflow_firmware_update(active_iface, msg_size, buf); + break; + case MessageType_MessageType_GetFeatures: + result = workflow_get_features(active_iface, msg_size, buf, vhdr, hdr); + break; +#if defined USE_OPTIGA + case MessageType_MessageType_UnlockBootloader: + result = workflow_unlock_bootloader(active_iface, msg_size, buf); + break; +#endif + default: + recv_msg_unknown(active_iface, msg_size, buf); + break; + } + + switch (result) { + case WF_CONTINUE_TO_FIRMWARE: + workflow_allow_jump_1(); + systick_delay_ms(100); + usb_deinit(); + return WF_CONTINUE_TO_FIRMWARE; + case WF_SHUTDOWN: + systick_delay_ms(100); + usb_deinit(); + return WF_SHUTDOWN; + case WF_STAY: + break; + case WF_RETURN: + systick_delay_ms(100); + usb_deinit(); + return WF_RETURN; + default: + // todo show some error? + systick_delay_ms(100); + usb_deinit(); + return WF_SHUTDOWN; + } + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_initialize.c b/core/embed/projects/bootloader/workflow/wf_initialize.c new file mode 100644 index 00000000000..1cdc024fc24 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_initialize.c @@ -0,0 +1,34 @@ +/* + * 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 "protob.h" +#include "workflow.h" + +workflow_result_t workflow_initialize(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf, + const vendor_header *const vhdr, + const image_header *const hdr) { + Initialize msg_recv; + recv_msg_initialize(iface, &msg_recv, buf, msg_size); + send_msg_features(iface, vhdr, hdr); + return WF_STAY; +} diff --git a/core/embed/projects/bootloader/workflow/wf_ping.c b/core/embed/projects/bootloader/workflow/wf_ping.c new file mode 100644 index 00000000000..f27d8499a5c --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_ping.c @@ -0,0 +1,32 @@ +/* + * 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 "protob.h" +#include "workflow.h" + +workflow_result_t workflow_ping(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf) { + Ping msg_recv; + recv_msg_ping(iface, &msg_recv, buf, msg_size); + send_msg_success(iface, msg_recv.message); + return WF_STAY; +} diff --git a/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c new file mode 100644 index 00000000000..cd7d3e4d096 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c @@ -0,0 +1,43 @@ +/* + * 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 "bootui.h" +#include "protob.h" +#include "rust_ui.h" +#include "workflow.h" + +workflow_result_t workflow_unlock_bootloader(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf) { + ui_result_t response = ui_screen_unlock_bootloader_confirm(); + if (UI_RESULT_CONFIRM != response) { + send_user_abort(iface, "Bootloader unlock cancelled"); + return WF_RETURN; + } + + secret_optiga_erase(); + send_msg_success(iface, NULL); + + screen_unlock_bootloader_success(); + return WF_SHUTDOWN; +} diff --git a/core/embed/projects/bootloader/workflow/wf_wipe_device.c b/core/embed/projects/bootloader/workflow/wf_wipe_device.c new file mode 100644 index 00000000000..6ce45169cbc --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c @@ -0,0 +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 . + */ + +#include +#include + +#include + +#include "bootui.h" +#include "protob.h" +#include "rust_ui.h" +#include "workflow.h" + +workflow_result_t workflow_wipe_device(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf) { + ui_result_t response = ui_screen_wipe_confirm(); + if (UI_RESULT_CONFIRM != response) { + send_user_abort(iface, "Wipe cancelled"); + return WF_RETURN; + } + ui_screen_wipe(); + secbool wipe_result = erase_device(ui_screen_wipe_progress); + + if (sectrue != wipe_result) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Could not erase flash"); + screen_wipe_fail(); + } else { + send_msg_success(iface, NULL); + screen_wipe_success(); + } + return WF_SHUTDOWN; +} diff --git a/core/embed/projects/bootloader/workflow/workflow.c b/core/embed/projects/bootloader/workflow/workflow.c new file mode 100644 index 00000000000..806d0b65fb5 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/workflow.c @@ -0,0 +1,53 @@ +/* + * 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 "protob/protob.h" +#include "workflow.h" +#include "workflow_internal.h" + +// anti-glitch +volatile secbool continue_to_firmware = secfalse; +volatile secbool continue_to_firmware_backup = secfalse; + +workflow_result_t workflow_exit_common(workflow_result_t result) { + switch (result) { + case WF_SHUTDOWN: + return WF_SHUTDOWN; + case WF_RETURN: + return WF_STAY; + case WF_CONTINUE_TO_FIRMWARE: + workflow_allow_jump_2(); + return WF_CONTINUE_TO_FIRMWARE; + default: + return WF_WIPE_AND_SHUTDOWN; + } +} + +secbool workflow_is_jump_allowed_1(void) { return continue_to_firmware; } +secbool workflow_is_jump_allowed_2(void) { return continue_to_firmware_backup; } + +void workflow_allow_jump_1(void) { continue_to_firmware = sectrue; } +void workflow_allow_jump_2(void) { continue_to_firmware_backup = sectrue; } + +void workflow_reset_jump(void) { + continue_to_firmware_backup = secfalse; + continue_to_firmware = secfalse; +} diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h new file mode 100644 index 00000000000..1d4fa12759d --- /dev/null +++ b/core/embed/projects/bootloader/workflow/workflow.h @@ -0,0 +1,74 @@ +/* + * 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 + +#include + +#include + +#include "protob/protob.h" + +typedef enum { + WF_WIPE_AND_SHUTDOWN = 0, + WF_SHUTDOWN = 0x11223344, + WF_CONTINUE_TO_FIRMWARE = 0xAABBCCDD, + WF_RETURN = 0x55667788, + WF_STAY = 0xEEFF0011, +} workflow_result_t; + +workflow_result_t workflow_firmware_update(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf); + +workflow_result_t workflow_wipe_device(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf); + +#ifdef USE_OPTIGA +workflow_result_t workflow_unlock_bootloader(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf); +#endif + +workflow_result_t workflow_ping(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf); + +workflow_result_t workflow_initialize(protob_iface_t *iface, uint32_t msg_size, + uint8_t *buf, + const vendor_header *const vhdr, + const image_header *const hdr); + +workflow_result_t workflow_get_features(protob_iface_t *iface, + uint32_t msg_size, uint8_t *buf, + const vendor_header *const vhdr, + const image_header *const hdr); + +workflow_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present); + +workflow_result_t workflow_empty_device(void); + +workflow_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)); + +workflow_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr); + +secbool workflow_is_jump_allowed_1(void); +secbool workflow_is_jump_allowed_2(void); diff --git a/core/embed/projects/bootloader/workflow/workflow_internal.h b/core/embed/projects/bootloader/workflow/workflow_internal.h new file mode 100644 index 00000000000..5e5b7e8e2c2 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/workflow_internal.h @@ -0,0 +1,31 @@ +/* + * 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 + +#include + +#include "workflow.h" + +void workflow_allow_jump_1(void); +void workflow_allow_jump_2(void); + +void workflow_reset_jump(void); + +workflow_result_t workflow_exit_common(workflow_result_t result); diff --git a/core/embed/projects/bootloader_ci/messages.c b/core/embed/projects/bootloader_ci/messages.c index 9e3251f7a46..67f91cf77fa 100644 --- a/core/embed/projects/bootloader_ci/messages.c +++ b/core/embed/projects/bootloader_ci/messages.c @@ -23,7 +23,7 @@ #include #include #include -#include "messages.pb.h" +#include "pb/messages.pb.h" #include #include diff --git a/tools/style.c.exclude b/tools/style.c.exclude index 64872317868..41486c838fa 100644 --- a/tools/style.c.exclude +++ b/tools/style.c.exclude @@ -1,4 +1,4 @@ -^\./core/embed/projects/bootloader/protob/ +^\./core/embed/projects/bootloader/protob/pb/ ^\./crypto/aes/ ^\./crypto/chacha20poly1305/ ^\./crypto/ed25519-donna/ From 8913d03af4978727a112b06267906744697e7e91 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Thu, 6 Feb 2025 16:05:08 +0100 Subject: [PATCH 2/5] fix(core): fix emulator usb deinitialization [no changelog] --- core/embed/io/usb/unix/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/embed/io/usb/unix/usb.c b/core/embed/io/usb/unix/usb.c index 94cb44711e1..5bde28f6fe5 100644 --- a/core/embed/io/usb/unix/usb.c +++ b/core/embed/io/usb/unix/usb.c @@ -69,7 +69,7 @@ secbool usb_init(const usb_dev_info_t *dev_info) { return sectrue; } -void usb_deinit(void) {} +void usb_deinit(void) { usb_stop(); } secbool usb_start(void) { const char *ip = getenv("TREZOR_UDP_IP"); From 71863fcd6ae97b418dfc29fc4087983ede6d274f Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Thu, 6 Feb 2025 16:05:25 +0100 Subject: [PATCH 3/5] fix(core): fix emulator usb polling [no changelog] --- core/embed/io/usb/unix/usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/embed/io/usb/unix/usb.c b/core/embed/io/usb/unix/usb.c index 5bde28f6fe5..d18f6024178 100644 --- a/core/embed/io/usb/unix/usb.c +++ b/core/embed/io/usb/unix/usb.c @@ -141,6 +141,10 @@ secbool usb_vcp_add(const usb_vcp_info_t *info) { } static secbool usb_emulated_poll_read(uint8_t iface_num) { + if (usb_ifaces[iface_num].msg_len > 0) { + return sectrue; + } + struct pollfd fds[] = { {usb_ifaces[iface_num].sock, POLLIN, 0}, }; From 8e4557e2e9e71b2bd5dd7e9742c649528f86c5c2 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Fri, 7 Feb 2025 21:50:07 +0100 Subject: [PATCH 4/5] fix(core): handle communication errors in bootloader [no changelog] --- .../embed/projects/bootloader/protob/protob.c | 37 ++++++++++--------- .../embed/projects/bootloader/protob/protob.h | 16 ++++---- .../bootloader/workflow/wf_firmware_update.c | 25 ++++++++++--- .../projects/bootloader/workflow/wf_ping.c | 4 +- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/core/embed/projects/bootloader/protob/protob.c b/core/embed/projects/bootloader/protob/protob.c index df7de10b4a1..237f8fa8ed4 100644 --- a/core/embed/projects/bootloader/protob/protob.c +++ b/core/embed/projects/bootloader/protob/protob.c @@ -77,42 +77,43 @@ #define MSG_RECV(TYPE) \ codec_recv_message(iface->wire, msg_size, buf, TYPE##_fields, &msg_recv) -void send_user_abort(protob_iface_t *iface, const char *msg) { +secbool send_user_abort(protob_iface_t *iface, const char *msg) { if (iface == NULL) { - return; + return sectrue; } MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); MSG_SEND_ASSIGN_STRING(message, msg); - MSG_SEND(Failure); + return MSG_SEND(Failure); } -void send_msg_failure(protob_iface_t *iface, FailureType type, - const char *msg) { +secbool send_msg_failure(protob_iface_t *iface, FailureType type, + const char *msg) { if (iface == NULL) { - return; + return sectrue; } MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, type); MSG_SEND_ASSIGN_STRING(message, msg); - MSG_SEND(Failure); + return MSG_SEND(Failure); } -void send_msg_success(protob_iface_t *iface, const char *msg) { +secbool send_msg_success(protob_iface_t *iface, const char *msg) { if (iface == NULL) { - return; + return sectrue; } MSG_SEND_INIT(Success); if (msg != NULL) { MSG_SEND_ASSIGN_STRING(message, msg); } - MSG_SEND(Success); + return MSG_SEND(Success); } -void send_msg_features(protob_iface_t *iface, const vendor_header *const vhdr, - const image_header *const hdr) { +secbool send_msg_features(protob_iface_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr) { if (iface == NULL) { - return; + return sectrue; } MSG_SEND_INIT(Features); MSG_SEND_ASSIGN_STRING(vendor, "trezor.io"); @@ -145,7 +146,7 @@ void send_msg_features(protob_iface_t *iface, const vendor_header *const vhdr, MSG_SEND_ASSIGN_VALUE(bootloader_locked, (secret_bootloader_locked() == sectrue)); #endif - MSG_SEND(Features); + return MSG_SEND(Features); } secbool recv_msg_initialize(protob_iface_t *iface, Initialize *msg, @@ -193,16 +194,16 @@ secbool recv_msg_firmware_erase(protob_iface_t *iface, FirmwareErase *msg, return result; } -void send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, - uint32_t length) { +secbool send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, + uint32_t length) { if (iface == NULL) { - return; + return sectrue; } MSG_SEND_INIT(FirmwareRequest); MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, offset); MSG_SEND_ASSIGN_REQUIRED_VALUE(length, length); - MSG_SEND(FirmwareRequest); + return MSG_SEND(FirmwareRequest); } typedef struct { diff --git a/core/embed/projects/bootloader/protob/protob.h b/core/embed/projects/bootloader/protob/protob.h index 083ddce7854..cdd95c29fa4 100644 --- a/core/embed/projects/bootloader/protob/protob.h +++ b/core/embed/projects/bootloader/protob/protob.h @@ -31,17 +31,19 @@ typedef struct { wire_iface_t *wire; } protob_iface_t; -void send_user_abort(protob_iface_t *iface, const char *msg); +secbool send_user_abort(protob_iface_t *iface, const char *msg); -void send_msg_features(protob_iface_t *iface, const vendor_header *const vhdr, - const image_header *const hdr); +secbool send_msg_features(protob_iface_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr); -void send_msg_failure(protob_iface_t *iface, FailureType type, const char *msg); +secbool send_msg_failure(protob_iface_t *iface, FailureType type, + const char *msg); -void send_msg_success(protob_iface_t *iface, const char *msg); +secbool send_msg_success(protob_iface_t *iface, const char *msg); -void send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, - uint32_t length); +secbool send_msg_request_firmware(protob_iface_t *iface, uint32_t offset, + uint32_t length); secbool recv_msg_initialize(protob_iface_t *iface, Initialize *msg, uint8_t *buf, uint32_t msg_size); diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c index f255d57617a..2e822ef4961 100644 --- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c +++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c @@ -60,6 +60,7 @@ typedef enum { UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12, UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, + UPLOAD_ERR_COMMUNICATION = -17, } upload_status_t; #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 @@ -354,7 +355,10 @@ static upload_status_t process_msg_FirmwareUpload(protob_iface_t *iface, : ctx->firmware_remaining; ctx->chunk_requested = chunk_limit - ctx->read_offset; - send_msg_request_firmware(iface, ctx->read_offset, ctx->chunk_requested); + if (sectrue != send_msg_request_firmware(iface, ctx->read_offset, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } ctx->firmware_remaining -= ctx->read_offset; if (ctx->firmware_remaining > 0) { @@ -386,8 +390,11 @@ static upload_status_t process_msg_FirmwareUpload(protob_iface_t *iface, memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); ctx->chunk_size = 0; - send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, - ctx->chunk_requested); + if (sectrue != send_msg_request_firmware( + iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } if (ctx->firmware_remaining > 0) { return UPLOAD_IN_PROGRESS; } @@ -462,8 +469,11 @@ static upload_status_t process_msg_FirmwareUpload(protob_iface_t *iface, // clear chunk buffer ctx->chunk_size = 0; memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); - send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, - ctx->chunk_requested); + if (sectrue != + send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } } else { send_msg_success(iface, NULL); } @@ -499,7 +509,10 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, ctx.chunk_requested = (ctx.firmware_remaining > IMAGE_INIT_CHUNK_SIZE) ? IMAGE_INIT_CHUNK_SIZE : ctx.firmware_remaining; - send_msg_request_firmware(iface, 0, ctx.chunk_requested); + if (sectrue != send_msg_request_firmware(iface, 0, ctx.chunk_requested)) { + ui_screen_fail(); + return WF_SHUTDOWN; + } } else { // invalid firmware size send_msg_failure(iface, FailureType_Failure_ProcessError, diff --git a/core/embed/projects/bootloader/workflow/wf_ping.c b/core/embed/projects/bootloader/workflow/wf_ping.c index f27d8499a5c..c22fde0d53f 100644 --- a/core/embed/projects/bootloader/workflow/wf_ping.c +++ b/core/embed/projects/bootloader/workflow/wf_ping.c @@ -26,7 +26,9 @@ workflow_result_t workflow_ping(protob_iface_t *iface, uint32_t msg_size, uint8_t *buf) { Ping msg_recv; - recv_msg_ping(iface, &msg_recv, buf, msg_size); + if (sectrue != recv_msg_ping(iface, &msg_recv, buf, msg_size)) { + return WF_STAY; + } send_msg_success(iface, msg_recv.message); return WF_STAY; } From d35437daac733e8d27e5c310381cb68d6b868f79 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Tue, 11 Feb 2025 17:34:46 +0100 Subject: [PATCH 5/5] fixup! refactor(core): complete bootloader refactoring --- core/embed/projects/bootloader/main.c | 13 ++-- .../bootloader/workflow/wf_auto_update.c | 10 +-- .../bootloader/workflow/wf_bootloader.c | 32 ++++++---- .../bootloader/workflow/wf_empty_device.c | 8 +-- .../bootloader/workflow/wf_firmware_update.c | 14 ++--- .../bootloader/workflow/wf_get_features.c | 2 +- .../bootloader/workflow/wf_host_control.c | 62 ++++++++++++------- .../bootloader/workflow/wf_initialize.c | 2 +- .../projects/bootloader/workflow/wf_ping.c | 4 +- .../workflow/wf_unlock_bootloader.c | 5 +- .../bootloader/workflow/wf_wipe_device.c | 11 ++-- .../projects/bootloader/workflow/workflow.c | 26 ++++---- .../projects/bootloader/workflow/workflow.h | 37 ++++++----- .../bootloader/workflow/workflow_internal.h | 2 +- 14 files changed, 130 insertions(+), 98 deletions(-) diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c index 35d2909d26e..a1d9eb92206 100644 --- a/core/embed/projects/bootloader/main.c +++ b/core/embed/projects/bootloader/main.c @@ -369,7 +369,7 @@ int bootloader_main(void) { // ... or there is no valid firmware if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue || auto_upgrade == sectrue) { - workflow_result_t result; + hc_result_t result; do { if (header_present == sectrue) { @@ -381,10 +381,11 @@ int bootloader_main(void) { } else { result = workflow_empty_device(); } - } while (result == WF_STAY || result == WF_RETURN); + } while (result == HC_CANCELLED); switch (result) { - case WF_CONTINUE_TO_FIRMWARE: { + case HC_REBOOT: + case HC_FIRMWARE_INSTALLED: { ensure(dont_optimize_out_true * (workflow_is_jump_allowed_1() == workflow_is_jump_allowed_2()), NULL); @@ -395,10 +396,12 @@ int bootloader_main(void) { firmware_jump_fn = real_jump_to_firmware; #endif } break; - case WF_SHUTDOWN: + case HC_DEVICE_WIPED: + case HC_BOOTLOADER_UNLOCKED: + case HC_ERROR: reboot_or_halt_after_rsod(); return 0; - case WF_WIPE_AND_SHUTDOWN: + case HC_ERROR_FATAL: default: { // erase storage if we saw flips randomly flip, most likely due to // glitch diff --git a/core/embed/projects/bootloader/workflow/wf_auto_update.c b/core/embed/projects/bootloader/workflow/wf_auto_update.c index 25a6c69bea8..06438571632 100644 --- a/core/embed/projects/bootloader/workflow/wf_auto_update.c +++ b/core/embed/projects/bootloader/workflow/wf_auto_update.c @@ -26,15 +26,15 @@ #include "workflow.h" #include "workflow_internal.h" -workflow_result_t workflow_auto_update(const vendor_header *const vhdr, - const image_header *const hdr) { +hc_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr) { workflow_reset_jump(); ui_set_initial_setup(true); ui_screen_connect(); - workflow_result_t res = WF_RETURN; - while (res == WF_RETURN || res == WF_STAY) { + hc_result_t res = HC_CANCELLED; + while (res == HC_CANCELLED) { res = workflow_host_control(vhdr, hdr, ui_screen_connect); } - return workflow_exit_common(res); + return res; } diff --git a/core/embed/projects/bootloader/workflow/wf_bootloader.c b/core/embed/projects/bootloader/workflow/wf_bootloader.c index f252962a21c..c856958465a 100644 --- a/core/embed/projects/bootloader/workflow/wf_bootloader.c +++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c @@ -31,9 +31,9 @@ #include "workflow.h" #include "workflow_internal.h" -workflow_result_t workflow_bootloader(const vendor_header *const vhdr, - const image_header *const hdr, - secbool firmware_present) { +hc_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present) { workflow_reset_jump(); ui_set_initial_setup(false); @@ -62,31 +62,37 @@ workflow_result_t workflow_bootloader(const vendor_header *const vhdr, #endif workflow_allow_jump_1(); workflow_allow_jump_2(); - return WF_CONTINUE_TO_FIRMWARE; + return HC_REBOOT; } if (ui_result == 0x55667788) { // wipe workflow_result_t r = workflow_wipe_device(NULL, 0, NULL); - if (r == WF_SHUTDOWN) { + if (r == WF_ERROR) { return r; } + if (r == WF_OK) { + return HC_DEVICE_WIPED; + } + + if (r == WF_CANCELLED) { + screen = SCREEN_MENU; + continue; + } + return WF_ERROR_FATAL; } break; case SCREEN_WAIT_FOR_HOST: ui_screen_connect(); - workflow_result_t res = - workflow_host_control(vhdr, hdr, ui_screen_connect); + hc_result_t res = workflow_host_control(vhdr, hdr, ui_screen_connect); switch (res) { - case WF_STAY: - break; - case WF_RETURN: + case HC_CANCELLED: screen = SCREEN_INTRO; - break; + continue; default: - return workflow_exit_common(res); + return res; } break; default: - return WF_WIPE_AND_SHUTDOWN; + return HC_ERROR_FATAL; break; } } diff --git a/core/embed/projects/bootloader/workflow/wf_empty_device.c b/core/embed/projects/bootloader/workflow/wf_empty_device.c index 41a3903a927..5cd240980b0 100644 --- a/core/embed/projects/bootloader/workflow/wf_empty_device.c +++ b/core/embed/projects/bootloader/workflow/wf_empty_device.c @@ -33,7 +33,7 @@ #include "workflow.h" #include "workflow_internal.h" -workflow_result_t workflow_empty_device(void) { +hc_result_t workflow_empty_device(void) { workflow_reset_jump(); ui_set_initial_setup(true); @@ -51,10 +51,10 @@ workflow_result_t workflow_empty_device(void) { systick_delay_ms(1000); #endif - workflow_result_t res = WF_RETURN; - while (res == WF_RETURN || res == WF_STAY) { + hc_result_t res = HC_CANCELLED; + while (res == HC_CANCELLED) { ui_screen_welcome(); res = workflow_host_control(NULL, NULL, ui_screen_welcome); } - return workflow_exit_common(res); + return res; } diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c index 2e822ef4961..8515e73447b 100644 --- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c +++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c @@ -494,7 +494,7 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, secbool res = recv_msg_firmware_erase(iface, &msg, buf, msg_size); if (res != sectrue) { - return WF_RETURN; + return WF_ERROR; } ctx.firmware_remaining = msg.has_length ? msg.length : 0; @@ -511,13 +511,13 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, : ctx.firmware_remaining; if (sectrue != send_msg_request_firmware(iface, 0, ctx.chunk_requested)) { ui_screen_fail(); - return WF_SHUTDOWN; + return WF_ERROR; } } else { // invalid firmware size send_msg_failure(iface, FailureType_Failure_ProcessError, "Wrong firmware size"); - return WF_RETURN; + return WF_ERROR; } upload_status_t s = UPLOAD_IN_PROGRESS; @@ -536,7 +536,7 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, if (sectrue != protob_get_msg_header(iface, buf, &msg_id, &msg_size)) { // invalid header -> discard - return WF_RETURN; + return WF_ERROR; } s = process_msg_FirmwareUpload(iface, msg_size, buf, &ctx); @@ -547,10 +547,10 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, } else { ui_screen_fail(); } - return WF_SHUTDOWN; + return WF_ERROR; } else if (s == UPLOAD_ERR_USER_ABORT) { systick_delay_ms(100); - return WF_RETURN; + return WF_CANCELLED; } else if (s == UPLOAD_OK) { // last chunk received ui_screen_install_progress_upload(1000); ui_screen_done(4, sectrue); @@ -560,7 +560,7 @@ workflow_result_t workflow_firmware_update(protob_iface_t *iface, systick_delay_ms(1000); ui_screen_done(1, secfalse); systick_delay_ms(1000); - return WF_CONTINUE_TO_FIRMWARE; + return WF_OK; } } } diff --git a/core/embed/projects/bootloader/workflow/wf_get_features.c b/core/embed/projects/bootloader/workflow/wf_get_features.c index 8749b39183f..3981bb94d1a 100644 --- a/core/embed/projects/bootloader/workflow/wf_get_features.c +++ b/core/embed/projects/bootloader/workflow/wf_get_features.c @@ -30,5 +30,5 @@ workflow_result_t workflow_get_features(protob_iface_t *iface, GetFeatures msg_recv; recv_msg_get_features(iface, &msg_recv, buf, msg_size); send_msg_features(iface, vhdr, hdr); - return WF_STAY; + return WF_OK; } diff --git a/core/embed/projects/bootloader/workflow/wf_host_control.c b/core/embed/projects/bootloader/workflow/wf_host_control.c index c0fd4d2259d..bdb5cfa5566 100644 --- a/core/embed/projects/bootloader/workflow/wf_host_control.c +++ b/core/embed/projects/bootloader/workflow/wf_host_control.c @@ -31,9 +31,9 @@ #include "workflow.h" #include "workflow_internal.h" -workflow_result_t workflow_host_control(const vendor_header *const vhdr, - const image_header *const hdr, - void (*redraw_wait_screen)(void)) { +hc_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)) { wire_iface_t usb_iface = {0}; protob_iface_t protob_usb_iface = {0}; @@ -46,7 +46,7 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr, uint8_t buf[MAX_PACKET_SIZE] = {0}; - workflow_result_t result = WF_STAY; + workflow_result_t result = WF_ERROR_FATAL; for (;;) { uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ}; @@ -75,51 +75,65 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr, switch (msg_id) { case MessageType_MessageType_Initialize: - result = workflow_initialize(active_iface, msg_size, buf, vhdr, hdr); - break; + workflow_initialize(active_iface, msg_size, buf, vhdr, hdr); + // whatever the result, we stay here and continue + continue; case MessageType_MessageType_Ping: - result = workflow_ping(active_iface, msg_size, buf); - break; + workflow_ping(active_iface, msg_size, buf); + // whatever the result, we stay here and continue + continue; case MessageType_MessageType_WipeDevice: result = workflow_wipe_device(active_iface, msg_size, buf); + if (result == WF_OK) { + workflow_allow_jump_1(); + systick_delay_ms(100); + usb_deinit(); + return HC_DEVICE_WIPED; + } break; case MessageType_MessageType_FirmwareErase: result = workflow_firmware_update(active_iface, msg_size, buf); + if (result == WF_OK) { + workflow_allow_jump_1(); + systick_delay_ms(100); + usb_deinit(); + return HC_FIRMWARE_INSTALLED; + } break; case MessageType_MessageType_GetFeatures: - result = workflow_get_features(active_iface, msg_size, buf, vhdr, hdr); - break; + workflow_get_features(active_iface, msg_size, buf, vhdr, hdr); + // whatever the result, we stay here and continue + continue; #if defined USE_OPTIGA case MessageType_MessageType_UnlockBootloader: result = workflow_unlock_bootloader(active_iface, msg_size, buf); + if (result == WF_OK) { + workflow_allow_jump_1(); + systick_delay_ms(100); + usb_deinit(); + return HC_BOOTLOADER_UNLOCKED; + } break; #endif default: recv_msg_unknown(active_iface, msg_size, buf); - break; + continue; } switch (result) { - case WF_CONTINUE_TO_FIRMWARE: - workflow_allow_jump_1(); + case WF_CANCELLED: systick_delay_ms(100); usb_deinit(); - return WF_CONTINUE_TO_FIRMWARE; - case WF_SHUTDOWN: - systick_delay_ms(100); - usb_deinit(); - return WF_SHUTDOWN; - case WF_STAY: - break; - case WF_RETURN: + return HC_CANCELLED; + case WF_ERROR: systick_delay_ms(100); usb_deinit(); - return WF_RETURN; + return HC_ERROR; + case WF_ERROR_FATAL: default: - // todo show some error? systick_delay_ms(100); usb_deinit(); - return WF_SHUTDOWN; + return WF_ERROR_FATAL; } } } diff --git a/core/embed/projects/bootloader/workflow/wf_initialize.c b/core/embed/projects/bootloader/workflow/wf_initialize.c index 1cdc024fc24..be5471705ff 100644 --- a/core/embed/projects/bootloader/workflow/wf_initialize.c +++ b/core/embed/projects/bootloader/workflow/wf_initialize.c @@ -30,5 +30,5 @@ workflow_result_t workflow_initialize(protob_iface_t *iface, uint32_t msg_size, Initialize msg_recv; recv_msg_initialize(iface, &msg_recv, buf, msg_size); send_msg_features(iface, vhdr, hdr); - return WF_STAY; + return WF_OK; } diff --git a/core/embed/projects/bootloader/workflow/wf_ping.c b/core/embed/projects/bootloader/workflow/wf_ping.c index c22fde0d53f..b89c3173105 100644 --- a/core/embed/projects/bootloader/workflow/wf_ping.c +++ b/core/embed/projects/bootloader/workflow/wf_ping.c @@ -27,8 +27,8 @@ workflow_result_t workflow_ping(protob_iface_t *iface, uint32_t msg_size, uint8_t *buf) { Ping msg_recv; if (sectrue != recv_msg_ping(iface, &msg_recv, buf, msg_size)) { - return WF_STAY; + return WF_ERROR; } send_msg_success(iface, msg_recv.message); - return WF_STAY; + return WF_OK; } diff --git a/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c index cd7d3e4d096..554fc0fa543 100644 --- a/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c +++ b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c @@ -32,12 +32,11 @@ workflow_result_t workflow_unlock_bootloader(protob_iface_t *iface, ui_result_t response = ui_screen_unlock_bootloader_confirm(); if (UI_RESULT_CONFIRM != response) { send_user_abort(iface, "Bootloader unlock cancelled"); - return WF_RETURN; + return WF_CANCELLED; } - secret_optiga_erase(); send_msg_success(iface, NULL); screen_unlock_bootloader_success(); - return WF_SHUTDOWN; + return WF_OK; } diff --git a/core/embed/projects/bootloader/workflow/wf_wipe_device.c b/core/embed/projects/bootloader/workflow/wf_wipe_device.c index 6ce45169cbc..988e438eec0 100644 --- a/core/embed/projects/bootloader/workflow/wf_wipe_device.c +++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c @@ -32,7 +32,7 @@ workflow_result_t workflow_wipe_device(protob_iface_t *iface, uint32_t msg_size, ui_result_t response = ui_screen_wipe_confirm(); if (UI_RESULT_CONFIRM != response) { send_user_abort(iface, "Wipe cancelled"); - return WF_RETURN; + return WF_CANCELLED; } ui_screen_wipe(); secbool wipe_result = erase_device(ui_screen_wipe_progress); @@ -41,9 +41,10 @@ workflow_result_t workflow_wipe_device(protob_iface_t *iface, uint32_t msg_size, send_msg_failure(iface, FailureType_Failure_ProcessError, "Could not erase flash"); screen_wipe_fail(); - } else { - send_msg_success(iface, NULL); - screen_wipe_success(); + return WF_ERROR; } - return WF_SHUTDOWN; + + send_msg_success(iface, NULL); + screen_wipe_success(); + return WF_OK; } diff --git a/core/embed/projects/bootloader/workflow/workflow.c b/core/embed/projects/bootloader/workflow/workflow.c index 806d0b65fb5..6725f42f314 100644 --- a/core/embed/projects/bootloader/workflow/workflow.c +++ b/core/embed/projects/bootloader/workflow/workflow.c @@ -27,19 +27,19 @@ volatile secbool continue_to_firmware = secfalse; volatile secbool continue_to_firmware_backup = secfalse; -workflow_result_t workflow_exit_common(workflow_result_t result) { - switch (result) { - case WF_SHUTDOWN: - return WF_SHUTDOWN; - case WF_RETURN: - return WF_STAY; - case WF_CONTINUE_TO_FIRMWARE: - workflow_allow_jump_2(); - return WF_CONTINUE_TO_FIRMWARE; - default: - return WF_WIPE_AND_SHUTDOWN; - } -} +// workflow_result_t workflow_exit_common(workflow_result_t result) { +// switch (result) { +// case WF_SHUTDOWN: +// return WF_SHUTDOWN; +// case WF_RETURN: +// return WF_STAY; +// case WF_CONTINUE_TO_FIRMWARE: +// workflow_allow_jump_2(); +// return WF_CONTINUE_TO_FIRMWARE; +// default: +// return WF_WIPE_AND_SHUTDOWN; +// } +// } secbool workflow_is_jump_allowed_1(void) { return continue_to_firmware; } secbool workflow_is_jump_allowed_2(void) { return continue_to_firmware_backup; } diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h index 1d4fa12759d..1ea79e4de1c 100644 --- a/core/embed/projects/bootloader/workflow/workflow.h +++ b/core/embed/projects/bootloader/workflow/workflow.h @@ -26,13 +26,22 @@ #include "protob/protob.h" typedef enum { - WF_WIPE_AND_SHUTDOWN = 0, - WF_SHUTDOWN = 0x11223344, - WF_CONTINUE_TO_FIRMWARE = 0xAABBCCDD, - WF_RETURN = 0x55667788, - WF_STAY = 0xEEFF0011, + WF_ERROR_FATAL = 0, + WF_ERROR = 0x11223344, + WF_OK = 0xAABBCCDD, + WF_CANCELLED = 0x55667788, } workflow_result_t; +typedef enum { + HC_ERROR_FATAL = 0, + HC_ERROR = 0x11223344, + HC_CANCELLED, + HC_REBOOT, + HC_FIRMWARE_INSTALLED, + HC_DEVICE_WIPED, + HC_BOOTLOADER_UNLOCKED, +} hc_result_t; + workflow_result_t workflow_firmware_update(protob_iface_t *iface, uint32_t msg_size, uint8_t *buf); @@ -57,18 +66,18 @@ workflow_result_t workflow_get_features(protob_iface_t *iface, const vendor_header *const vhdr, const image_header *const hdr); -workflow_result_t workflow_bootloader(const vendor_header *const vhdr, - const image_header *const hdr, - secbool firmware_present); +hc_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present); -workflow_result_t workflow_empty_device(void); +hc_result_t workflow_empty_device(void); -workflow_result_t workflow_host_control(const vendor_header *const vhdr, - const image_header *const hdr, - void (*redraw_wait_screen)(void)); +hc_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)); -workflow_result_t workflow_auto_update(const vendor_header *const vhdr, - const image_header *const hdr); +hc_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr); secbool workflow_is_jump_allowed_1(void); secbool workflow_is_jump_allowed_2(void); diff --git a/core/embed/projects/bootloader/workflow/workflow_internal.h b/core/embed/projects/bootloader/workflow/workflow_internal.h index 5e5b7e8e2c2..09921694bf1 100644 --- a/core/embed/projects/bootloader/workflow/workflow_internal.h +++ b/core/embed/projects/bootloader/workflow/workflow_internal.h @@ -28,4 +28,4 @@ void workflow_allow_jump_2(void); void workflow_reset_jump(void); -workflow_result_t workflow_exit_common(workflow_result_t result); +// workflow_result_t workflow_exit_common(workflow_result_t result);