mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 14:16:06 +00:00
refactor(core): complete bootloader refactoring
This commit is contained in:
parent
29ed889375
commit
3b05b7603a
@ -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',
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
@ -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',
|
||||
|
1
core/embed/projects/bootloader/.changelog.d/4572.changed
Normal file
1
core/embed/projects/bootloader/.changelog.d/4572.changed
Normal file
@ -0,0 +1 @@
|
||||
Major code clean-up and refactoring.
|
35
core/embed/projects/bootloader/antiglitch.c
Normal file
35
core/embed/projects/bootloader/antiglitch.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
// 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;
|
||||
}
|
30
core/embed/projects/bootloader/antiglitch.h
Normal file
30
core/embed/projects/bootloader/antiglitch.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
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);
|
@ -22,35 +22,13 @@
|
||||
#include <io/display.h>
|
||||
#include <io/display_utils.h>
|
||||
#include <rtl/mini_printf.h>
|
||||
|
||||
#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,
|
||||
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) {
|
||||
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); }
|
||||
|
||||
|
@ -17,23 +17,32 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUI_H__
|
||||
#define __BOOTUI_H__
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
// 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,
|
||||
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);
|
||||
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
|
||||
|
@ -8,11 +8,9 @@
|
||||
#include <io/display.h>
|
||||
#include <sys/bootargs.h>
|
||||
#include <sys/bootutils.h>
|
||||
#include <sys/systick.h>
|
||||
#include <util/flash.h>
|
||||
#include <util/flash_otp.h>
|
||||
#include "bootui.h"
|
||||
#include "rust_ui.h"
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
#include <sec/secret.h>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#undef FIRMWARE_START
|
||||
|
||||
extern uint8_t *FIRMWARE_START;
|
||||
|
||||
#endif
|
||||
|
@ -20,27 +20,20 @@
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <gfx/gfx_bitblt.h>
|
||||
#include <io/display.h>
|
||||
#include <io/display_utils.h>
|
||||
#include <sec/monoctr.h>
|
||||
#include <sec/random_delays.h>
|
||||
#include <sec/secret.h>
|
||||
#include <sys/bootargs.h>
|
||||
#include <sys/bootutils.h>
|
||||
#include <sys/mpu.h>
|
||||
#include <sys/system.h>
|
||||
#include <sys/systick.h>
|
||||
#include <sys/systimer.h>
|
||||
#include <util/flash.h>
|
||||
#include <sys/types.h>
|
||||
#include <util/flash_otp.h>
|
||||
#include <util/flash_utils.h>
|
||||
#include <util/image.h>
|
||||
#include <util/rsod.h>
|
||||
#include <util/unit_properties.h>
|
||||
#include "messages.pb.h"
|
||||
|
||||
#ifdef USE_PVD
|
||||
#include <sys/pvd.h>
|
||||
@ -67,27 +60,16 @@
|
||||
#include <sys/tamper.h>
|
||||
#endif
|
||||
|
||||
#include <io/usb.h>
|
||||
#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;
|
||||
ensure(dont_optimize_out_true *
|
||||
(firmware_present == firmware_present_backup),
|
||||
NULL);
|
||||
jump_to_fw_through_reset();
|
||||
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) {
|
||||
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();
|
||||
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include <io/usb.h>
|
||||
#include <sec/secret.h>
|
||||
#include <sys/bootargs.h>
|
||||
#include <util/flash.h>
|
||||
#include <util/flash_utils.h>
|
||||
#include <util/image.h>
|
||||
#include <util/unit_properties.h>
|
||||
#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 <sec/secret.h>
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MESSAGES_H__
|
||||
#define __MESSAGES_H__
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
#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
|
62
core/embed/projects/bootloader/poll.c
Normal file
62
core/embed/projects/bootloader/poll.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_bsp.h>
|
||||
|
||||
#include "poll.h"
|
||||
|
||||
#include <io/usb.h>
|
||||
#include <sys/systick.h>
|
||||
|
||||
#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;
|
||||
}
|
39
core/embed/projects/bootloader/poll.h
Normal file
39
core/embed/projects/bootloader/poll.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#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);
|
245
core/embed/projects/bootloader/protob/protob.c
Normal file
245
core/embed/projects/bootloader/protob/protob.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
|
||||
#include <util/image.h>
|
||||
#include <util/unit_properties.h>
|
||||
|
||||
#if USE_OPTIGA
|
||||
#include <sec/secret.h>
|
||||
#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);
|
||||
}
|
68
core/embed/projects/bootloader/protob/protob.h
Normal file
68
core/embed/projects/bootloader/protob/protob.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
#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);
|
248
core/embed/projects/bootloader/wire/codec_v1.c
Normal file
248
core/embed/projects/bootloader/wire/codec_v1.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#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);
|
||||
}
|
||||
}
|
54
core/embed/projects/bootloader/wire/codec_v1.h
Normal file
54
core/embed/projects/bootloader/wire/codec_v1.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <pb.h>
|
||||
|
||||
#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);
|
113
core/embed/projects/bootloader/wire/wire_iface_usb.c
Normal file
113
core/embed/projects/bootloader/wire/wire_iface_usb.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include "wire_iface_usb.h"
|
||||
|
||||
#include <io/usb.h>
|
||||
|
||||
#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();
|
||||
}
|
26
core/embed/projects/bootloader/wire/wire_iface_usb.h
Normal file
26
core/embed/projects/bootloader/wire/wire_iface_usb.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
37
core/embed/projects/bootloader/workflow/wf_auto_update.c
Normal file
37
core/embed/projects/bootloader/workflow/wf_auto_update.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
#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;
|
||||
}
|
99
core/embed/projects/bootloader/workflow/wf_bootloader.c
Normal file
99
core/embed/projects/bootloader/workflow/wf_bootloader.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <util/image.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
57
core/embed/projects/bootloader/workflow/wf_empty_device.c
Normal file
57
core/embed/projects/bootloader/workflow/wf_empty_device.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/systick.h>
|
||||
#include <sys/types.h>
|
||||
#include <util/flash_utils.h>
|
||||
#include <util/image.h>
|
||||
|
||||
#ifdef USE_STORAGE_HWKEY
|
||||
#include <sec/secret.h>
|
||||
#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;
|
||||
}
|
561
core/embed/projects/bootloader/workflow/wf_firmware_update.c
Normal file
561
core/embed/projects/bootloader/workflow/wf_firmware_update.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/bootargs.h>
|
||||
#include <sys/systick.h>
|
||||
#include <util/flash.h>
|
||||
#include <util/flash_utils.h>
|
||||
|
||||
#if USE_OPTIGA
|
||||
#include <sec/secret.h>
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
33
core/embed/projects/bootloader/workflow/wf_get_features.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_get_features.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#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;
|
||||
}
|
123
core/embed/projects/bootloader/workflow/wf_host_control.c
Normal file
123
core/embed/projects/bootloader/workflow/wf_host_control.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sys/systick.h>
|
||||
#include <sys/types.h>
|
||||
#include <util/image.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
33
core/embed/projects/bootloader/workflow/wf_initialize.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_initialize.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#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;
|
||||
}
|
33
core/embed/projects/bootloader/workflow/wf_ping.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_ping.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <sec/secret.h>
|
||||
|
||||
#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;
|
||||
}
|
55
core/embed/projects/bootloader/workflow/wf_wipe_device.c
Normal file
55
core/embed/projects/bootloader/workflow/wf_wipe_device.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <util/flash_utils.h>
|
||||
|
||||
#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;
|
||||
}
|
68
core/embed/projects/bootloader/workflow/workflow.h
Normal file
68
core/embed/projects/bootloader/workflow/workflow.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
#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);
|
@ -23,7 +23,7 @@
|
||||
#include <pb.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include "messages.pb.h"
|
||||
#include "pb/messages.pb.h"
|
||||
|
||||
#include <io/usb.h>
|
||||
#include <util/flash.h>
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
]
|
||||
|
@ -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"),
|
||||
]
|
||||
|
@ -223,7 +223,6 @@ def configure(
|
||||
("USE_TAMPER", "1"),
|
||||
("USE_FLASH_BURST", "1"),
|
||||
("USE_OEM_KEYS_CHECK", "1"),
|
||||
("USE_RESET_TO_BOOT", "1"),
|
||||
]
|
||||
|
||||
sources += [
|
||||
|
@ -228,7 +228,6 @@ def configure(
|
||||
("USE_TAMPER", "1"),
|
||||
("USE_FLASH_BURST", "1"),
|
||||
("USE_OEM_KEYS_CHECK", "1"),
|
||||
("USE_RESET_TO_BOOT", "1"),
|
||||
]
|
||||
|
||||
sources += [
|
||||
|
@ -1,4 +1,4 @@
|
||||
^\./core/embed/projects/bootloader/protob/
|
||||
^\./core/embed/projects/bootloader/protob/pb/
|
||||
^\./crypto/aes/
|
||||
^\./crypto/chacha20poly1305/
|
||||
^\./crypto/ed25519-donna/
|
||||
|
Loading…
Reference in New Issue
Block a user