diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index 54c95fbcf5..6b9116e773 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/antiglitch.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 6de6dbe4e7..3a56880ab4 100644
--- a/core/SConscript.bootloader_ci
+++ b/core/SConscript.bootloader_ci
@@ -99,7 +99,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 3a727a3925..c338d3b0d8 100644
--- a/core/SConscript.bootloader_emu
+++ b/core/SConscript.bootloader_emu
@@ -99,10 +99,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/antiglitch.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 = [
@@ -137,6 +150,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 0000000000..1fc7107384
--- /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/antiglitch.c b/core/embed/projects/bootloader/antiglitch.c
new file mode 100644
index 0000000000..fa06bb2cb4
--- /dev/null
+++ b/core/embed/projects/bootloader/antiglitch.c
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+// anti-glitch
+static volatile secbool continue_to_firmware = secfalse;
+static volatile secbool continue_to_firmware_backup = secfalse;
+
+secbool jump_is_allowed_1(void) { return continue_to_firmware; }
+secbool jump_is_allowed_2(void) { return continue_to_firmware_backup; }
+
+void jump_allow_1(void) { continue_to_firmware = sectrue; }
+void jump_allow_2(void) { continue_to_firmware_backup = sectrue; }
+
+void jump_reset(void) {
+ continue_to_firmware_backup = secfalse;
+ continue_to_firmware = secfalse;
+}
diff --git a/core/embed/projects/bootloader/antiglitch.h b/core/embed/projects/bootloader/antiglitch.h
new file mode 100644
index 0000000000..60cf8a076f
--- /dev/null
+++ b/core/embed/projects/bootloader/antiglitch.h
@@ -0,0 +1,30 @@
+/*
+ * 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
+
+secbool jump_is_allowed_1(void);
+secbool jump_is_allowed_2(void);
+
+void jump_allow_1(void);
+void jump_allow_2(void);
+
+void jump_reset(void);
diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c
index c2aa9b24b9..51ac3da056 100644
--- a/core/embed/projects/bootloader/bootui.c
+++ b/core/embed/projects/bootloader/bootui.c
@@ -22,35 +22,13 @@
#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
@@ -139,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);
@@ -171,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 b3fc7e082a..8dd4c556d8 100644
--- a/core/embed/projects/bootloader/bootui.h
+++ b/core/embed/projects/bootloader/bootui.h
@@ -17,23 +17,32 @@
* along with this program. If not, see .
*/
-#ifndef __BOOTUI_H__
-#define __BOOTUI_H__
+#pragma once
#include
#include
+// todo: use bindgen to tie this to rust
typedef enum {
- SCREEN_INTRO = 0,
- SCREEN_MENU = 1,
- SCREEN_WIPE_CONFIRM = 2,
- SCREEN_FINGER_PRINT = 3,
- SCREEN_WAIT_FOR_HOST = 4,
- SCREEN_WELCOME = 5,
-} screen_t;
+ UI_RESULT_CANCEL = 1,
+ UI_RESULT_CONFIRM = 2,
+} ui_result_t;
-// Displays a warning screeen before jumping to the untrusted firmware
+// todo: use bindgen to tie this to rust
+typedef enum {
+ MENU_EXIT = 0xAABBCCDD,
+ MENU_REBOOT = 0x11223344,
+ MENU_WIPE = 0x55667788,
+} menu_result_t;
+
+// todo: use bindgen to tie this to rust
+typedef enum {
+ INTRO_MENU = 1,
+ INTRO_HOST = 2,
+} intro_result_t;
+
+// Displays a warning screen before jumping to the untrusted firmware
//
// Shows vendor image, vendor string and firmware version
// and optional message to the user (see `wait` argument)
@@ -58,16 +67,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 +95,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 8f46770f5a..0c9bbb8df4 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 721490e912..e3d362cde4 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 3f9c896e58..b34924182e 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
@@ -67,27 +60,16 @@
#include
#endif
-#include
-#include "version.h"
-
+#include "antiglitch.h"
#include "bootui.h"
-#include "messages.h"
-#include "rust_ui.h"
#include "version_check.h"
+#include "workflow/workflow.h"
#ifdef TREZOR_EMULATOR
#include "SDL.h"
#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;
@@ -154,158 +136,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,
@@ -400,13 +230,10 @@ void real_jump_to_firmware(void) {
IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE));
}
-#ifdef USE_RESET_TO_BOOT
__attribute__((noreturn)) void jump_to_fw_through_reset(void) {
display_fade(display_get_backlight(), 0, 200);
-
reboot_device();
}
-#endif
#ifndef TREZOR_EMULATOR
int main(void) {
@@ -551,143 +378,47 @@ 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);
+ workflow_result_t result;
+
+ jump_reset();
if (header_present == sectrue) {
if (auto_upgrade == sectrue) {
- screen = SCREEN_WAIT_FOR_HOST;
+ result = workflow_auto_update(&vhdr, hdr);
} else {
- ui_set_initial_setup(false);
- screen = SCREEN_INTRO;
+ result = workflow_bootloader(&vhdr, hdr, firmware_present);
}
-
} 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);
-#else
- // backlight fading takes some time so the explicit delay here is
- // shorter
- hal_delay(1000);
-#endif
+ result = workflow_empty_device();
}
- while (true) {
- volatile secbool continue_to_firmware = secfalse;
- volatile secbool continue_to_firmware_backup = secfalse;
- uint32_t ui_result = 0;
+ switch (result) {
+ case WF_OK_FIRMWARE_INSTALLED:
+ firmware_present = sectrue;
+ firmware_present_backup = sectrue;
+ case WF_OK_REBOOT_SELECTED:
+ ensure(dont_optimize_out_true *
+ (jump_is_allowed_1() == jump_is_allowed_2()),
+ NULL);
- 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);
-#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) {
+ ensure(dont_optimize_out_true *
+ (firmware_present == firmware_present_backup),
+ NULL);
+ jump_to_fw_through_reset();
+ break;
+ case WF_OK_DEVICE_WIPED:
+ case WF_OK_BOOTLOADER_UNLOCKED:
+ case WF_ERROR:
+ reboot_or_halt_after_rsod();
+ return 0;
+ case WF_ERROR_FATAL:
+ 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;
+ error_shutdown("Bootloader fatal error");
}
}
}
@@ -695,16 +426,9 @@ int bootloader_main(void) {
ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup),
NULL);
-#ifdef USE_RESET_TO_BOOT
- if (sectrue == firmware_present &&
- firmware_jump_fn != jump_to_fw_through_reset) {
- firmware_jump_fn = real_jump_to_firmware;
- }
-#else
if (sectrue == firmware_present) {
firmware_jump_fn = real_jump_to_firmware;
}
-#endif
firmware_jump_fn();
diff --git a/core/embed/projects/bootloader/messages.c b/core/embed/projects/bootloader/messages.c
deleted file mode 100644
index f1371721d8..0000000000
--- 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 5623ff3b5f..0000000000
--- 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 0000000000..6d6bc70e73
--- /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 deadline = ticks_timeout(timeout_ms);
+
+ while (!ticks_expired(deadline)) {
+#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 0000000000..61d7d4e79c
--- /dev/null
+++ b/core/embed/projects/bootloader/poll.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#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;
+} 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 0000000000..c0ee01d89d
--- /dev/null
+++ b/core/embed/projects/bootloader/protob/protob.c
@@ -0,0 +1,245 @@
+/*
+ * 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, iface->msg_size, iface->buf, TYPE##_fields, \
+ &msg_recv)
+
+secbool send_user_abort(protob_io_t *iface, const char *msg) {
+ MSG_SEND_INIT(Failure);
+ MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled);
+ MSG_SEND_ASSIGN_STRING(message, msg);
+ return MSG_SEND(Failure);
+}
+
+secbool send_msg_failure(protob_io_t *iface, FailureType type,
+ const char *msg) {
+ MSG_SEND_INIT(Failure);
+ MSG_SEND_ASSIGN_VALUE(code, type);
+ MSG_SEND_ASSIGN_STRING(message, msg);
+ return MSG_SEND(Failure);
+}
+
+secbool send_msg_success(protob_io_t *iface, const char *msg) {
+ MSG_SEND_INIT(Success);
+ if (msg != NULL) {
+ MSG_SEND_ASSIGN_STRING(message, msg);
+ }
+ return MSG_SEND(Success);
+}
+
+secbool send_msg_features(protob_io_t *iface, 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
+ return MSG_SEND(Features);
+}
+
+secbool recv_msg_initialize(protob_io_t *iface, Initialize *msg) {
+ MSG_RECV_INIT(Initialize);
+ secbool result = MSG_RECV(Initialize);
+ memcpy(msg, &msg_recv, sizeof(Initialize));
+ return result;
+}
+
+secbool recv_msg_get_features(protob_io_t *iface, GetFeatures *msg) {
+ MSG_RECV_INIT(GetFeatures);
+ secbool result = MSG_RECV(GetFeatures);
+ memcpy(msg, &msg_recv, sizeof(GetFeatures));
+ return result;
+}
+
+secbool recv_msg_ping(protob_io_t *iface, Ping *msg) {
+ MSG_RECV_INIT(Ping);
+ secbool result = MSG_RECV(Ping);
+ memcpy(msg, &msg_recv, sizeof(Ping));
+ return result;
+}
+
+secbool recv_msg_firmware_erase(protob_io_t *iface, FirmwareErase *msg) {
+ MSG_RECV_INIT(FirmwareErase);
+ secbool result = MSG_RECV(FirmwareErase);
+ memcpy(msg, &msg_recv, sizeof(FirmwareErase));
+ return result;
+}
+
+secbool send_msg_request_firmware(protob_io_t *iface, uint32_t offset,
+ uint32_t length) {
+ MSG_SEND_INIT(FirmwareRequest);
+ MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, offset);
+ MSG_SEND_ASSIGN_REQUIRED_VALUE(length, length);
+ return 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_io_t *iface, FirmwareUpload *msg,
+ 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_io_t *iface) {
+ codec_flush(iface->wire, iface->msg_size, iface->buf);
+ send_msg_failure(iface, FailureType_Failure_UnexpectedMessage,
+ "Unexpected message");
+}
+
+void protob_init(protob_io_t *iface, wire_iface_t *wire) {
+ memset(iface, 0, sizeof(protob_io_t));
+ iface->wire = wire;
+}
+
+uint32_t protob_get_iface_flag(protob_io_t *iface) {
+ return iface->wire->poll_iface_id;
+}
+
+secbool protob_get_msg_header(protob_io_t *iface, uint16_t *msg_id) {
+ iface->wire->read(iface->buf, iface->wire->rx_packet_size);
+ return codec_parse_header(iface->buf, msg_id, &iface->msg_size);
+}
diff --git a/core/embed/projects/bootloader/protob/protob.h b/core/embed/projects/bootloader/protob/protob.h
new file mode 100644
index 0000000000..bcf4687ac8
--- /dev/null
+++ b/core/embed/projects/bootloader/protob/protob.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+#include
+
+#include "pb/messages.pb.h"
+
+#include "wire/codec_v1.h"
+
+typedef struct {
+ wire_iface_t *wire;
+ uint8_t buf[MAX_PACKET_SIZE];
+ size_t msg_size;
+
+} protob_io_t;
+
+secbool send_user_abort(protob_io_t *iface, const char *msg);
+
+secbool send_msg_features(protob_io_t *iface, const vendor_header *const vhdr,
+ const image_header *const hdr);
+
+secbool send_msg_failure(protob_io_t *iface, FailureType type, const char *msg);
+
+secbool send_msg_success(protob_io_t *iface, const char *msg);
+
+secbool send_msg_request_firmware(protob_io_t *iface, uint32_t offset,
+ uint32_t length);
+
+secbool recv_msg_initialize(protob_io_t *iface, Initialize *msg);
+
+secbool recv_msg_get_features(protob_io_t *iface, GetFeatures *msg);
+
+secbool recv_msg_ping(protob_io_t *iface, Ping *msg);
+
+secbool recv_msg_firmware_erase(protob_io_t *iface, FirmwareErase *msg);
+
+secbool recv_msg_firmware_upload(protob_io_t *iface, FirmwareUpload *msg,
+ void *ctx,
+ void (*data_cb)(size_t len, void *ctx),
+ uint8_t *buffer, size_t buffer_size);
+
+void recv_msg_unknown(protob_io_t *iface);
+
+void protob_init(protob_io_t *iface, wire_iface_t *wire);
+
+uint32_t protob_get_iface_flag(protob_io_t *iface);
+
+secbool protob_get_msg_header(protob_io_t *iface, uint16_t *msg_id);
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 0000000000..d7dcf773f5
--- /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,
+ size_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_packet_size;
+ 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_packet_size;
+
+ // 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_packet_size;
+
+ 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_packet_size;
+
+ 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_packet_size;
+
+ 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 0000000000..82450250ac
--- /dev/null
+++ b/core/embed/projects/bootloader/wire/codec_v1.h
@@ -0,0 +1,54 @@
+/*
+ * 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 {
+ // identifier of the interface used for polling communication events
+ uint8_t poll_iface_id;
+ // size of TX packet
+ size_t tx_packet_size;
+ // size of RX packet
+ size_t rx_packet_size;
+
+ // write function pointer
+ bool (*write)(uint8_t *data, size_t size);
+ // read function pointer
+ int (*read)(uint8_t *buffer, size_t buffer_size);
+
+ // RSOD function pointer
+ void (*error)(void);
+} wire_iface_t;
+
+secbool codec_parse_header(const uint8_t *buf, uint16_t *msg_id,
+ size_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 0000000000..4ebb691cef
--- /dev/null
+++ b/core/embed/projects/bootloader/wire/wire_iface_usb.c
@@ -0,0 +1,113 @@
+/*
+ * 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 usb21_landing) {
+ usb_init_all(usb21_landing);
+
+ memset(iface, 0, sizeof(wire_iface_t));
+
+ iface->poll_iface_id = USB_IFACE_NUM;
+ iface->tx_packet_size = USB_PACKET_SIZE;
+ iface->rx_packet_size = 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 0000000000..6b2057071c
--- /dev/null
+++ b/core/embed/projects/bootloader/wire/wire_iface_usb.h
@@ -0,0 +1,26 @@
+/*
+ * 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 usb21_landing);
+
+void usb_iface_deinit(wire_iface_t* iface);
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 0000000000..d5a4e64260
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_auto_update.c
@@ -0,0 +1,37 @@
+/*
+ * 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"
+
+workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
+ const image_header *const hdr) {
+ ui_set_initial_setup(true);
+
+ workflow_result_t res = WF_CANCELLED;
+ while (res == WF_CANCELLED) {
+ res = workflow_host_control(vhdr, hdr, ui_screen_connect);
+ }
+ return 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 0000000000..f2191b73fa
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "antiglitch.h"
+#include "bootui.h"
+#include "workflow.h"
+
+typedef enum {
+ SCREEN_INTRO,
+ SCREEN_MENU,
+ SCREEN_WAIT_FOR_HOST,
+} screen_t;
+
+workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
+ const image_header *const hdr,
+ secbool firmware_present) {
+ ui_set_initial_setup(false);
+
+ screen_t screen = SCREEN_INTRO;
+
+ while (true) {
+ switch (screen) {
+ case SCREEN_INTRO:
+ intro_result_t ui_result = ui_screen_intro(vhdr, hdr, firmware_present);
+ if (ui_result == INTRO_MENU) {
+ screen = SCREEN_MENU;
+ }
+ if (ui_result == INTRO_HOST) {
+ screen = SCREEN_WAIT_FOR_HOST;
+ }
+ break;
+ case SCREEN_MENU:
+ menu_result_t menu_result = ui_screen_menu(firmware_present);
+ if (menu_result == MENU_EXIT) { // exit menu
+ screen = SCREEN_INTRO;
+ }
+ if (menu_result == MENU_REBOOT) { // reboot
+#ifndef USE_HASH_PROCESSOR
+ ui_screen_boot_stage_1(true);
+#endif
+ jump_allow_1();
+ jump_allow_2();
+ return WF_OK_REBOOT_SELECTED;
+ }
+ if (menu_result == MENU_WIPE) { // wipe
+ workflow_result_t r = workflow_wipe_device(NULL);
+ if (r == WF_ERROR) {
+ return r;
+ }
+ if (r == WF_OK) {
+ return WF_OK_DEVICE_WIPED;
+ }
+
+ if (r == WF_CANCELLED) {
+ screen = SCREEN_MENU;
+ continue;
+ }
+ return WF_ERROR_FATAL;
+ }
+ break;
+ case SCREEN_WAIT_FOR_HOST:
+ workflow_result_t res =
+ workflow_host_control(vhdr, hdr, ui_screen_connect);
+ switch (res) {
+ case WF_CANCELLED:
+ screen = SCREEN_INTRO;
+ continue;
+ default:
+ return res;
+ }
+ break;
+ default:
+ return WF_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
new file mode 100644
index 0000000000..136e6f5617
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_empty_device.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#ifdef USE_STORAGE_HWKEY
+#include
+#endif
+
+#include "bootui.h"
+#include "workflow.h"
+
+workflow_result_t workflow_empty_device(void) {
+ 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_CANCELLED;
+ while (res == WF_CANCELLED) {
+ res = workflow_host_control(NULL, NULL, ui_screen_welcome);
+ }
+ return 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 0000000000..b60dbcf9ce
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
@@ -0,0 +1,561 @@
+/*
+ * 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"
+
+#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_ERR_COMMUNICATION = -17,
+} 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_io_t *iface,
+ firmware_update_ctx_t *ctx) {
+ FirmwareUpload msg;
+
+ const secbool r =
+ recv_msg_firmware_upload(iface, &msg, 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;
+
+ 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) {
+ 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;
+
+ 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;
+ }
+ 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);
+ 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);
+ }
+
+ if (ctx->firmware_remaining > 0) {
+ return UPLOAD_IN_PROGRESS;
+ }
+ return UPLOAD_OK;
+}
+
+workflow_result_t workflow_firmware_update(protob_io_t *iface) {
+ firmware_update_ctx_t ctx = {
+ .firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT,
+ };
+
+ FirmwareErase msg;
+ secbool res = recv_msg_firmware_erase(iface, &msg);
+
+ if (res != sectrue) {
+ return WF_ERROR;
+ }
+
+ 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;
+ if (sectrue != send_msg_request_firmware(iface, 0, ctx.chunk_requested)) {
+ ui_screen_fail();
+ return WF_ERROR;
+ }
+ } else {
+ // invalid firmware size
+ send_msg_failure(iface, FailureType_Failure_ProcessError,
+ "Wrong firmware size");
+ return WF_ERROR;
+ }
+
+ 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;
+
+ if (sectrue != protob_get_msg_header(iface, &msg_id)) {
+ // invalid header -> discard
+ return WF_ERROR;
+ }
+ s = process_msg_FirmwareUpload(iface, &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_ERROR;
+ } else if (s == UPLOAD_ERR_USER_ABORT) {
+ systick_delay_ms(100);
+ return WF_CANCELLED;
+ } 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_OK;
+ }
+ }
+}
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 0000000000..5f65eed4bf
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_get_features.c
@@ -0,0 +1,33 @@
+/*
+ * 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_io_t *iface,
+ const vendor_header *const vhdr,
+ const image_header *const hdr) {
+ GetFeatures msg_recv;
+ recv_msg_get_features(iface, &msg_recv);
+ send_msg_features(iface, vhdr, hdr);
+ 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
new file mode 100644
index 0000000000..5ac20be205
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_host_control.c
@@ -0,0 +1,123 @@
+/*
+ * 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 "antiglitch.h"
+#include "poll.h"
+#include "protob/protob.h"
+#include "wire/wire_iface_usb.h"
+#include "workflow.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_io_t protob_usb_iface = {0};
+
+ redraw_wait_screen();
+
+ // 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);
+
+ workflow_result_t result = WF_ERROR_FATAL;
+
+ 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;
+ protob_io_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, &msg_id)) {
+ active_iface = &protob_usb_iface;
+ } else {
+ continue;
+ }
+ break;
+ case EVENT_NONE:
+ default:
+ continue;
+ }
+
+ switch (msg_id) {
+ case MessageType_MessageType_Initialize:
+ workflow_initialize(active_iface, vhdr, hdr);
+ // whatever the result, we stay here and continue
+ continue;
+ case MessageType_MessageType_Ping:
+ workflow_ping(active_iface);
+ // whatever the result, we stay here and continue
+ continue;
+ case MessageType_MessageType_GetFeatures:
+ workflow_get_features(active_iface, vhdr, hdr);
+ // whatever the result, we stay here and continue
+ continue;
+ case MessageType_MessageType_WipeDevice:
+ result = workflow_wipe_device(active_iface);
+ if (result == WF_OK) {
+ systick_delay_ms(100);
+ usb_iface_deinit(&usb_iface);
+ return WF_OK_DEVICE_WIPED;
+ }
+ break;
+ case MessageType_MessageType_FirmwareErase:
+ result = workflow_firmware_update(active_iface);
+ if (result == WF_OK) {
+ jump_allow_1();
+ jump_allow_2();
+ systick_delay_ms(100);
+ usb_iface_deinit(&usb_iface);
+ return WF_OK_FIRMWARE_INSTALLED;
+ }
+ break;
+#if defined USE_OPTIGA
+ case MessageType_MessageType_UnlockBootloader:
+ result = workflow_unlock_bootloader(active_iface);
+ if (result == WF_OK) {
+ systick_delay_ms(100);
+ usb_iface_deinit(&usb_iface);
+ return WF_OK_BOOTLOADER_UNLOCKED;
+ }
+ break;
+#endif
+ default:
+ recv_msg_unknown(active_iface);
+ continue;
+ }
+
+ systick_delay_ms(100);
+ usb_iface_deinit(&usb_iface);
+ return result;
+ }
+}
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 0000000000..c378cf1f72
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_initialize.c
@@ -0,0 +1,33 @@
+/*
+ * 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_io_t *iface,
+ const vendor_header *const vhdr,
+ const image_header *const hdr) {
+ Initialize msg_recv;
+ recv_msg_initialize(iface, &msg_recv);
+ send_msg_features(iface, vhdr, hdr);
+ return WF_OK;
+}
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 0000000000..60b5140e8f
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_ping.c
@@ -0,0 +1,33 @@
+/*
+ * 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_io_t *iface) {
+ Ping msg_recv;
+ if (sectrue != recv_msg_ping(iface, &msg_recv)) {
+ return WF_ERROR;
+ }
+ send_msg_success(iface, msg_recv.message);
+ 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
new file mode 100644
index 0000000000..d6e01e5cb0
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c
@@ -0,0 +1,41 @@
+/*
+ * 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_io_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_CANCELLED;
+ }
+ secret_optiga_erase();
+ send_msg_success(iface, NULL);
+
+ screen_unlock_bootloader_success();
+ 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
new file mode 100644
index 0000000000..51b483d06e
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c
@@ -0,0 +1,55 @@
+/*
+ * 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_io_t *iface) {
+ ui_result_t response = ui_screen_wipe_confirm();
+ if (UI_RESULT_CONFIRM != response) {
+ if (iface != NULL) {
+ send_user_abort(iface, "Wipe cancelled");
+ }
+ return WF_CANCELLED;
+ }
+ ui_screen_wipe();
+ secbool wipe_result = erase_device(ui_screen_wipe_progress);
+
+ if (sectrue != wipe_result) {
+ if (iface != NULL) {
+ send_msg_failure(iface, FailureType_Failure_ProcessError,
+ "Could not erase flash");
+ }
+ screen_wipe_fail();
+ return WF_ERROR;
+ }
+
+ if (iface != NULL) {
+ send_msg_success(iface, NULL);
+ }
+ screen_wipe_success();
+ return WF_OK;
+}
diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h
new file mode 100644
index 0000000000..2a45dac4b1
--- /dev/null
+++ b/core/embed/projects/bootloader/workflow/workflow.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+#include
+
+#include "protob/protob.h"
+
+typedef enum {
+ WF_ERROR_FATAL = 0,
+ WF_ERROR = 0x11223344,
+ WF_OK = 0x7ABBCCDD,
+ WF_OK_REBOOT_SELECTED = 0x68A4DABF,
+ WF_OK_FIRMWARE_INSTALLED = 0x04D9D07F,
+ WF_OK_DEVICE_WIPED = 0x30DC3841,
+ WF_OK_BOOTLOADER_UNLOCKED = 0x23FCBD03,
+ WF_CANCELLED = 0x55667788,
+} workflow_result_t;
+
+workflow_result_t workflow_firmware_update(protob_io_t *iface);
+
+workflow_result_t workflow_wipe_device(protob_io_t *iface);
+
+#ifdef USE_OPTIGA
+workflow_result_t workflow_unlock_bootloader(protob_io_t *iface);
+#endif
+
+workflow_result_t workflow_ping(protob_io_t *iface);
+
+workflow_result_t workflow_initialize(protob_io_t *iface,
+ const vendor_header *const vhdr,
+ const image_header *const hdr);
+
+workflow_result_t workflow_get_features(protob_io_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);
+
+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);
diff --git a/core/embed/projects/bootloader_ci/messages.c b/core/embed/projects/bootloader_ci/messages.c
index 9e3251f7a4..67f91cf77f 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/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py
index 1a1bc8ea33..f3499e8df8 100644
--- a/core/site_scons/models/D002/discovery2.py
+++ b/core/site_scons/models/D002/discovery2.py
@@ -102,7 +102,6 @@ def configure(
"USE_TAMPER=1",
"USE_FLASH_BURST=1",
"USE_OEM_KEYS_CHECK=1",
- "USE_RESET_TO_BOOT=1",
]
env.get("ENV")["LINKER_SCRIPT"] = linker_script
diff --git a/core/site_scons/models/T3B1/trezor_t3b1_revB.py b/core/site_scons/models/T3B1/trezor_t3b1_revB.py
index f307a852b3..fd8bc66bb8 100644
--- a/core/site_scons/models/T3B1/trezor_t3b1_revB.py
+++ b/core/site_scons/models/T3B1/trezor_t3b1_revB.py
@@ -106,7 +106,6 @@ def configure(
("USE_STORAGE_HWKEY", "1"),
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
- ("USE_RESET_TO_BOOT", "1"),
("USE_OEM_KEYS_CHECK", "1"),
("USE_PVD", "1"),
]
diff --git a/core/site_scons/models/T3T1/trezor_t3t1_revE.py b/core/site_scons/models/T3T1/trezor_t3t1_revE.py
index 6ed6184ad0..a2042f34d0 100644
--- a/core/site_scons/models/T3T1/trezor_t3t1_revE.py
+++ b/core/site_scons/models/T3T1/trezor_t3t1_revE.py
@@ -139,7 +139,6 @@ def configure(
("USE_STORAGE_HWKEY", "1"),
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
- ("USE_RESET_TO_BOOT", "1"),
("USE_OEM_KEYS_CHECK", "1"),
("USE_PVD", "1"),
]
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 92e0e8190a..8f94eca4bb 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -223,7 +223,6 @@ def configure(
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
("USE_OEM_KEYS_CHECK", "1"),
- ("USE_RESET_TO_BOOT", "1"),
]
sources += [
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index cbc89f833e..6d81b310fd 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -228,7 +228,6 @@ def configure(
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
("USE_OEM_KEYS_CHECK", "1"),
- ("USE_RESET_TO_BOOT", "1"),
]
sources += [
diff --git a/tools/style.c.exclude b/tools/style.c.exclude
index 8d9ca1e4ca..11fed97082 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/