mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-21 23:18:13 +00:00
feat(core): introduce interaction-less upgrade
This commit is contained in:
parent
804874c7b9
commit
ba83a7e644
1
core/.changelog.d/2919.added
Normal file
1
core/.changelog.d/2919.added
Normal file
@ -0,0 +1 @@
|
||||
Support interaction-less upgrade
|
@ -445,6 +445,7 @@ env.Replace(
|
||||
'FIRMWARE',
|
||||
'TREZOR_MODEL_'+TREZOR_MODEL,
|
||||
'USE_HAL_DRIVER',
|
||||
'ARM_USER_MODE',
|
||||
UI_LAYOUT,
|
||||
] + CPPDEFINES_MOD + CPPDEFINES_HAL,
|
||||
ASFLAGS=env.get('ENV')['CPU_ASFLAGS'],
|
||||
|
@ -109,7 +109,7 @@ tools.add_font('DEMIBOLD', FONT_DEMIBOLD, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
tools.add_font('BIG', FONT_BIG, CPPDEFINES_MOD, SOURCE_MOD)
|
||||
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')), CONSTRAINTS=["limited_util_s"])
|
||||
env = Environment(ENV=os.environ, CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')))
|
||||
|
||||
FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
||||
|
||||
@ -151,6 +151,7 @@ env.Replace(
|
||||
CPPDEFINES=[
|
||||
'TREZOR_PRODTEST',
|
||||
'TREZOR_MODEL_'+TREZOR_MODEL,
|
||||
'ARM_USER_MODE',
|
||||
'USE_HAL_DRIVER',
|
||||
] + CPPDEFINES_MOD + CPPDEFINES_HAL,
|
||||
ASFLAGS=env.get('ENV')['CPU_ASFLAGS'],
|
||||
|
@ -3,9 +3,9 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 48K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 48K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(CCMRAM) + LENGTH(CCMRAM); /* 8-byte aligned full descending stack */
|
||||
|
1
core/embed/bootloader/.changelog.d/2794.added
Normal file
1
core/embed/bootloader/.changelog.d/2794.added
Normal file
@ -0,0 +1 @@
|
||||
Minimize risk of losing seed when upgrading firmware
|
1
core/embed/bootloader/.changelog.d/2919.added
Normal file
1
core/embed/bootloader/.changelog.d/2919.added
Normal file
@ -0,0 +1 @@
|
||||
Support interaction-less upgrade
|
21
core/embed/bootloader/boot_internal.h
Normal file
21
core/embed/bootloader/boot_internal.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef BOOT_INTERNAL_H
|
||||
#define BOOT_INTERNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <boot_args.h>
|
||||
|
||||
// The 'g_boot_command' variable stores the 'command' passed to the
|
||||
// function 'svc_reboot_to_bootloader()'. It may be one of the
|
||||
// 'BOOT_COMMAND_xxx' values defined in the enumeration, or it could
|
||||
// be any other value that should be treated as a non-special action,
|
||||
// in which case the bootloader should behave as if the device was
|
||||
// just powered up. The variable is set before the main() is called.
|
||||
extern boot_command_t g_boot_command;
|
||||
|
||||
|
||||
// The 'g_boot_args' array stores extra arguments passed
|
||||
// function 'svc_reboot_to_bootloader()'
|
||||
extern uint8_t g_boot_args[BOOT_ARGS_SIZE];
|
||||
|
||||
|
||||
#endif // BOOT_INTERNAL_H
|
@ -2,6 +2,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include TREZOR_BOARD
|
||||
#include "boot_internal.h"
|
||||
#include "bootui.h"
|
||||
#include "common.h"
|
||||
#include "display.h"
|
||||
@ -17,7 +18,12 @@
|
||||
#undef FIRMWARE_START
|
||||
|
||||
uint8_t *FIRMWARE_START = 0;
|
||||
uint32_t stay_in_bootloader_flag;
|
||||
|
||||
// Simulation of a boot command normally grabbed during reset processing
|
||||
boot_command_t g_boot_command = BOOT_COMMAND_NONE;
|
||||
// Simulation of a boot args normally sitting at the BOOT_ARGS region
|
||||
uint8_t g_boot_args[BOOT_ARGS_SIZE];
|
||||
|
||||
|
||||
void set_core_clock(int) {}
|
||||
|
||||
@ -47,7 +53,7 @@ __attribute__((noreturn)) int main(int argc, char **argv) {
|
||||
if (argc == 2) {
|
||||
if (argv[1][0] == 's') {
|
||||
// Run the firmware
|
||||
stay_in_bootloader_flag = STAY_IN_BOOTLOADER_FLAG;
|
||||
g_boot_command = BOOT_COMMAND_STOP_AND_WAIT;
|
||||
}
|
||||
#ifdef USE_OPTIGA
|
||||
else if (argv[1][0] == 'l') {
|
||||
|
@ -2,7 +2,6 @@
|
||||
#define __EMULATOR_H__
|
||||
|
||||
#define CLOCK_180_MHZ 0
|
||||
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
|
||||
#define mini_snprintf snprintf
|
||||
|
||||
#undef FIRMWARE_START
|
||||
@ -11,7 +10,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern uint8_t *FIRMWARE_START;
|
||||
extern uint32_t stay_in_bootloader_flag;
|
||||
|
||||
void emulator_poll_events(void);
|
||||
void set_core_clock(int);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "boot_internal.h"
|
||||
#include "common.h"
|
||||
#include "display.h"
|
||||
#include "flash.h"
|
||||
@ -68,19 +69,6 @@
|
||||
#include "platform.h"
|
||||
#endif
|
||||
|
||||
const uint8_t BOOTLOADER_KEY_M = 2;
|
||||
const uint8_t BOOTLOADER_KEY_N = 3;
|
||||
static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
#if !PRODUCTION
|
||||
/*** DEVEL/QA KEYS ***/
|
||||
(const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
|
||||
(const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
|
||||
(const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
|
||||
#else
|
||||
MODEL_BOOTLOADER_KEYS
|
||||
#endif
|
||||
};
|
||||
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
typedef enum {
|
||||
@ -251,11 +239,6 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
}
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr) {
|
||||
return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
|
||||
BOOTLOADER_KEYS);
|
||||
}
|
||||
|
||||
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,
|
||||
@ -376,12 +359,10 @@ void real_jump_to_firmware(void) {
|
||||
|
||||
#ifndef TREZOR_EMULATOR
|
||||
int main(void) {
|
||||
// grab "stay in bootloader" flag as soon as possible
|
||||
register uint32_t r11 __asm__("r11");
|
||||
volatile uint32_t stay_in_bootloader_flag = r11;
|
||||
#else
|
||||
int bootloader_main(void) {
|
||||
#endif
|
||||
secbool stay_in_bootloader = secfalse;
|
||||
|
||||
random_delays_init();
|
||||
// display_init_seq();
|
||||
@ -413,6 +394,7 @@ int bootloader_main(void) {
|
||||
volatile secbool header_present = secfalse;
|
||||
volatile secbool firmware_present = secfalse;
|
||||
volatile secbool firmware_present_backup = secfalse;
|
||||
volatile secbool auto_upgrade = secfalse;
|
||||
|
||||
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
|
||||
|
||||
@ -482,10 +464,19 @@ int bootloader_main(void) {
|
||||
check_bootloader_version();
|
||||
#endif
|
||||
|
||||
// was there reboot with request to stay in bootloader?
|
||||
secbool stay_in_bootloader = secfalse;
|
||||
if (stay_in_bootloader_flag == STAY_IN_BOOTLOADER_FLAG) {
|
||||
stay_in_bootloader = sectrue;
|
||||
switch (g_boot_command) {
|
||||
case BOOT_COMMAND_STOP_AND_WAIT:
|
||||
// firmare requested to stay in bootloader
|
||||
stay_in_bootloader = sectrue;
|
||||
break;
|
||||
case BOOT_COMMAND_INSTALL_UPGRADE:
|
||||
if (firmware_present == sectrue) {
|
||||
// continue without user interaction
|
||||
auto_upgrade = sectrue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup),
|
||||
@ -521,12 +512,20 @@ int bootloader_main(void) {
|
||||
// start the bootloader ...
|
||||
// ... if user touched the screen on start
|
||||
// ... or we have stay_in_bootloader flag to force it
|
||||
// ... or strict upgrade was confirmed in the firmware (auto_upgrade flag)
|
||||
// ... or there is no valid firmware
|
||||
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue) {
|
||||
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue ||
|
||||
auto_upgrade == sectrue) {
|
||||
screen_t screen;
|
||||
ui_set_initial_setup(true);
|
||||
if (header_present == sectrue) {
|
||||
ui_set_initial_setup(false);
|
||||
screen = SCREEN_INTRO;
|
||||
if (auto_upgrade == sectrue) {
|
||||
screen = SCREEN_WAIT_FOR_HOST;
|
||||
} else {
|
||||
ui_set_initial_setup(false);
|
||||
screen = SCREEN_INTRO;
|
||||
}
|
||||
|
||||
} else {
|
||||
screen = SCREEN_WELCOME;
|
||||
|
||||
@ -534,8 +533,6 @@ int bootloader_main(void) {
|
||||
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
|
||||
NULL);
|
||||
|
||||
ui_set_initial_setup(true);
|
||||
|
||||
// keep the model screen up for a while
|
||||
#ifndef USE_BACKLIGHT
|
||||
hal_delay(1500);
|
||||
@ -613,7 +610,7 @@ int bootloader_main(void) {
|
||||
}
|
||||
break;
|
||||
case SCREEN_WAIT_FOR_HOST:
|
||||
screen_connect();
|
||||
screen_connect(auto_upgrade == sectrue);
|
||||
switch (bootloader_usb_loop(&vhdr, hdr)) {
|
||||
case CONTINUE_TO_FIRMWARE:
|
||||
continue_to_firmware = sectrue;
|
||||
|
@ -3,9 +3,10 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
|
||||
BOOT_ARGS (wal) : ORIGIN = 0x1000FF00, LENGTH = 0x100
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(CCMRAM) + SIZEOF(.stack) ; /* 8-byte aligned full descending stack */
|
||||
@ -23,8 +24,10 @@ ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
/* reserve 256 bytes for bootloader arguments */
|
||||
boot_args_start = ORIGIN(BOOT_ARGS);
|
||||
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
|
||||
g_boot_args = boot_args_start;
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <pb_encode.h>
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include "boot_internal.h"
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "image.h"
|
||||
@ -426,8 +427,6 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
|
||||
return true;
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr);
|
||||
|
||||
static int version_compare(uint32_t vera, uint32_t verb) {
|
||||
int a, b;
|
||||
a = vera & 0xFF;
|
||||
@ -448,11 +447,12 @@ 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 *is_upgrade,
|
||||
secbool *is_newvendor) {
|
||||
secbool *is_new, secbool *keep_seed,
|
||||
secbool *is_newvendor, secbool *is_upgrade) {
|
||||
*is_new = secfalse;
|
||||
*is_upgrade = secfalse;
|
||||
*keep_seed = secfalse;
|
||||
*is_newvendor = secfalse;
|
||||
*is_upgrade = secfalse;
|
||||
if (sectrue != check_vendor_header_keys(current_vhdr)) {
|
||||
*is_new = sectrue;
|
||||
return;
|
||||
@ -477,7 +477,11 @@ static void detect_installation(const vendor_header *current_vhdr,
|
||||
if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) {
|
||||
return;
|
||||
}
|
||||
*is_upgrade = sectrue;
|
||||
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;
|
||||
@ -577,9 +581,51 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
|
||||
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);
|
||||
&should_keep_seed, &is_newvendor, &is_upgrade);
|
||||
}
|
||||
|
||||
secbool is_ilu = secfalse; // interaction-less update
|
||||
|
||||
if (g_boot_command == BOOT_COMMAND_INSTALL_UPGRADE) {
|
||||
BLAKE2S_CTX ctx;
|
||||
uint8_t hash[BLAKE2S_DIGEST_LENGTH];
|
||||
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
|
||||
blake2s_Update(&ctx, CHUNK_BUFFER_PTR,
|
||||
vhdr.hdrlen + received_hdr->hdrlen);
|
||||
blake2s_Final(&ctx, hash, BLAKE2S_DIGEST_LENGTH);
|
||||
|
||||
// the firmware must be the same as confirmed by the user
|
||||
if (memcmp(&g_boot_args[0], 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_ALL) != VTRUST_ALL) {
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
@ -593,8 +639,8 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
#endif
|
||||
|
||||
uint32_t response = INPUT_CANCEL;
|
||||
if (sectrue == is_new) {
|
||||
// new installation - auto confirm
|
||||
if (sectrue == is_new || sectrue == is_ilu) {
|
||||
// new installation or interaction less updated - auto confirm
|
||||
response = INPUT_CONFIRM;
|
||||
} else {
|
||||
int version_cmp = version_compare(hdr.version, current_hdr->version);
|
||||
|
@ -42,6 +42,9 @@ enum {
|
||||
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,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -7,7 +7,7 @@
|
||||
reset_handler:
|
||||
// setup environment for subsequent stage of code
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =firmware_header_start // r1 - point to byte where firmware image header might start
|
||||
ldr r1, =ccmram_end // r1 - point to byte where BOOT_ARGS region starts
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
@ -33,9 +33,22 @@ reset_handler:
|
||||
// subsequent operations, it is not necessary to insert a memory barrier instruction."
|
||||
cpsie f
|
||||
|
||||
// r11 contains argument passed to reboot_to_bootloader()
|
||||
// function called when the firmware rebooted to the bootloader
|
||||
ldr r0, =g_boot_command
|
||||
str r11, [r0]
|
||||
|
||||
// enter the application code
|
||||
bl main
|
||||
|
||||
b shutdown_privileged
|
||||
|
||||
.bss
|
||||
|
||||
.global g_boot_command
|
||||
g_boot_command:
|
||||
.word 0
|
||||
|
||||
|
||||
.end
|
||||
|
||||
|
@ -40,18 +40,6 @@
|
||||
#include "model.h"
|
||||
// #include "mpu.h"
|
||||
|
||||
const uint8_t BOOTLOADER_KEY_M = 2;
|
||||
const uint8_t BOOTLOADER_KEY_N = 3;
|
||||
static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
(const uint8_t *)"\xc2\xc8\x7a\x49\xc5\xa3\x46\x09\x77\xfb\xb2\xec\x9d\xfe\x60\xf0\x6b\xd6\x94\xdb\x82\x44\xbd\x49\x81\xfe\x3b\x7a\x26\x30\x7f\x3f",
|
||||
(const uint8_t *)"\x80\xd0\x36\xb0\x87\x39\xb8\x46\xf4\xcb\x77\x59\x30\x78\xde\xb2\x5d\xc9\x48\x7a\xed\xcf\x52\xe3\x0b\x4f\xb7\xcd\x70\x24\x17\x8a",
|
||||
(const uint8_t *)"\xb8\x30\x7a\x71\xf5\x52\xc6\x0a\x4c\xbb\x31\x7f\xf4\x8b\x82\xcd\xbf\x6b\x6b\xb5\xf0\x4c\x92\x0f\xec\x7b\xad\xf0\x17\x88\x37\x51",
|
||||
// comment the lines above and uncomment the lines below to use a custom signed vendorheader
|
||||
// (const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
|
||||
// (const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
|
||||
// (const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
|
||||
};
|
||||
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
static void usb_init_all(secbool usb21_landing) {
|
||||
@ -166,11 +154,6 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
}
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(vendor_header *const vhdr) {
|
||||
return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
|
||||
BOOTLOADER_KEYS);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -3,9 +3,10 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
|
||||
BOOT_ARGS (wal) : ORIGIN = 0x1000FF00, LENGTH = 0x100
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(CCMRAM) + LENGTH(CCMRAM); /* 8-byte aligned full descending stack */
|
||||
@ -23,8 +24,9 @@ ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
/* reserve 256 bytes for bootloader arguments */
|
||||
boot_args_start = ORIGIN(BOOT_ARGS);
|
||||
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
||||
|
||||
|
@ -402,8 +402,6 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
|
||||
return true;
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr);
|
||||
|
||||
static int version_compare(uint32_t vera, uint32_t verb) {
|
||||
int a, b;
|
||||
a = vera & 0xFF;
|
||||
|
@ -7,7 +7,7 @@
|
||||
reset_handler:
|
||||
// setup environment for subsequent stage of code
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =firmware_header_start // r1 - point to byte where firmware header starts
|
||||
ldr r1, =ccmram_end // r1 - point to byte where BOOT_ARGS region starts
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "supervise.h"
|
||||
#endif
|
||||
|
||||
#include "image.h"
|
||||
#include "version.h"
|
||||
|
||||
#if MICROPY_PY_TREZORUTILS
|
||||
@ -245,18 +246,90 @@ STATIC mp_obj_t mod_trezorutils_unit_btconly(void) {
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_unit_btconly_obj,
|
||||
mod_trezorutils_unit_btconly);
|
||||
|
||||
/// def reboot_to_bootloader() -> None:
|
||||
/// def reboot_to_bootloader(
|
||||
/// boot_command : int = 0,
|
||||
/// boot_args : bytes | None = None,
|
||||
/// ) -> None:
|
||||
/// """
|
||||
/// Reboots to bootloader.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader() {
|
||||
STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader(size_t n_args,
|
||||
const mp_obj_t *args) {
|
||||
#ifndef TREZOR_EMULATOR
|
||||
svc_reboot_to_bootloader();
|
||||
boot_command_t boot_command = BOOT_COMMAND_NONE;
|
||||
mp_buffer_info_t boot_args = {0};
|
||||
|
||||
if (n_args > 0 && args[0] != mp_const_none) {
|
||||
mp_int_t value = mp_obj_get_int(args[0]);
|
||||
|
||||
switch (value) {
|
||||
case 0:
|
||||
boot_command = BOOT_COMMAND_STOP_AND_WAIT;
|
||||
break;
|
||||
case 1:
|
||||
boot_command = BOOT_COMMAND_INSTALL_UPGRADE;
|
||||
break;
|
||||
default:
|
||||
mp_raise_ValueError("Invalid value.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_args > 1 && args[1] != mp_const_none) {
|
||||
mp_get_buffer_raise(args[1], &boot_args, MP_BUFFER_READ);
|
||||
}
|
||||
|
||||
svc_reboot_to_bootloader(boot_command, boot_args.buf, boot_args.len);
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_reboot_to_bootloader_obj,
|
||||
mod_trezorutils_reboot_to_bootloader);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
|
||||
mod_trezorutils_reboot_to_bootloader_obj, 0, 2,
|
||||
mod_trezorutils_reboot_to_bootloader);
|
||||
|
||||
/// def check_firmware_header(
|
||||
/// header : bytes
|
||||
/// ) -> dict:
|
||||
/// """
|
||||
/// Checks firmware image and vendor header and returns
|
||||
/// { "version": (major, minor, patch),
|
||||
/// "vendor": string,
|
||||
/// "fingerprint": bytes,
|
||||
/// "hash": bytes
|
||||
/// }
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorutils_check_firmware_header(mp_obj_t header) {
|
||||
mp_buffer_info_t header_buf = {0};
|
||||
mp_get_buffer_raise(header, &header_buf, MP_BUFFER_READ);
|
||||
|
||||
firmware_header_info_t info;
|
||||
|
||||
if (sectrue == check_firmware_header(header_buf.buf, header_buf.len, &info)) {
|
||||
mp_obj_t version[3] = {mp_obj_new_int(info.ver_major),
|
||||
mp_obj_new_int(info.ver_minor),
|
||||
mp_obj_new_int(info.ver_patch)};
|
||||
|
||||
mp_obj_t result = mp_obj_new_dict(4);
|
||||
mp_obj_dict_store(result, MP_ROM_QSTR(MP_QSTR_version),
|
||||
mp_obj_new_tuple(MP_ARRAY_SIZE(version), version));
|
||||
mp_obj_dict_store(
|
||||
result, MP_ROM_QSTR(MP_QSTR_vendor),
|
||||
mp_obj_new_str_copy(&mp_type_str, info.vstr, info.vstr_len));
|
||||
mp_obj_dict_store(
|
||||
result, MP_ROM_QSTR(MP_QSTR_fingerprint),
|
||||
mp_obj_new_bytes(info.fingerprint, sizeof(info.fingerprint)));
|
||||
mp_obj_dict_store(result, MP_ROM_QSTR(MP_QSTR_hash),
|
||||
mp_obj_new_bytes(info.hash, sizeof(info.hash)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
mp_raise_ValueError("Invalid value.");
|
||||
}
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorutils_check_firmware_header_obj,
|
||||
mod_trezorutils_check_firmware_header);
|
||||
|
||||
/// def bootloader_locked() -> bool | None:
|
||||
/// """
|
||||
@ -328,6 +401,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
|
||||
MP_ROM_PTR(&mod_trezorutils_firmware_vendor_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader),
|
||||
MP_ROM_PTR(&mod_trezorutils_reboot_to_bootloader_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_check_firmware_header),
|
||||
MP_ROM_PTR(&mod_trezorutils_check_firmware_header_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_bootloader_locked),
|
||||
MP_ROM_PTR(&mod_trezorutils_bootloader_locked_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_unit_color),
|
||||
|
@ -244,83 +244,6 @@ void BusFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(BF)"); }
|
||||
|
||||
void UsageFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(UF)"); }
|
||||
|
||||
__attribute__((noreturn)) void reboot_to_bootloader() {
|
||||
mpu_config_bootloader();
|
||||
jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE,
|
||||
STAY_IN_BOOTLOADER_FLAG);
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
void copy_image_header_for_bootloader(const uint8_t *image_header) {
|
||||
memcpy(&firmware_header_start, image_header, IMAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void SVC_C_Handler(uint32_t *stack) {
|
||||
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
||||
bool clear_firmware_header = true;
|
||||
switch (svc_number) {
|
||||
case SVC_ENABLE_IRQ:
|
||||
HAL_NVIC_EnableIRQ(stack[0]);
|
||||
break;
|
||||
case SVC_DISABLE_IRQ:
|
||||
HAL_NVIC_DisableIRQ(stack[0]);
|
||||
break;
|
||||
case SVC_SET_PRIORITY:
|
||||
NVIC_SetPriority(stack[0], stack[1]);
|
||||
break;
|
||||
#ifdef SYSTEM_VIEW
|
||||
case SVC_GET_DWT_CYCCNT:
|
||||
cyccnt_cycles = *DWT_CYCCNT_ADDR;
|
||||
break;
|
||||
#endif
|
||||
case SVC_SHUTDOWN:
|
||||
shutdown_privileged();
|
||||
for (;;)
|
||||
;
|
||||
break;
|
||||
case SVC_REBOOT_COPY_IMAGE_HEADER:
|
||||
copy_image_header_for_bootloader((uint8_t *)stack[0]);
|
||||
clear_firmware_header = false;
|
||||
// break is omitted here because we want to continue to reboot below
|
||||
case SVC_REBOOT_TO_BOOTLOADER:
|
||||
// if not going from copy image header & reboot, clean preventively this
|
||||
// part of CCMRAM
|
||||
if (clear_firmware_header) {
|
||||
explicit_bzero(&firmware_header_start, IMAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
ensure_compatible_settings();
|
||||
|
||||
__asm__ volatile("msr control, %0" ::"r"(0x0));
|
||||
__asm__ volatile("isb");
|
||||
// See stack layout in
|
||||
// https://developer.arm.com/documentation/ka004005/latest We are changing
|
||||
// return address in PC to land into reboot to avoid any bug with ROP and
|
||||
// raising privileges.
|
||||
stack[6] = (uintptr_t)reboot_to_bootloader;
|
||||
return;
|
||||
case SVC_GET_SYSTICK_VAL: {
|
||||
systick_val_copy = SysTick->VAL;
|
||||
} break;
|
||||
default:
|
||||
stack[0] = 0xffffffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile(
|
||||
" tst lr, #4 \n" // Test Bit 3 to see which stack pointer we should
|
||||
// use.
|
||||
" ite eq \n" // Tell the assembler that the nest 2 instructions
|
||||
// are if-then-else
|
||||
" mrseq r0, msp \n" // Make R0 point to main stack pointer
|
||||
" mrsne r0, psp \n" // Make R0 point to process stack pointer
|
||||
" b SVC_C_Handler \n" // Off to C land
|
||||
);
|
||||
}
|
||||
|
||||
// MicroPython builtin stubs
|
||||
|
||||
mp_import_stat_t mp_import_stat(const char *path) {
|
||||
|
@ -3,10 +3,11 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
FLASH2 (r) : ORIGIN = 0x08120000, LENGTH = 896K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
FLASH2 (r) : ORIGIN = 0x08120000, LENGTH = 896K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
|
||||
BOOT_ARGS (wal) : ORIGIN = 0x1000FF00, LENGTH = 0x100
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(SRAM) + SIZEOF(.stack); /* 8-byte aligned full descending stack */
|
||||
@ -22,8 +23,9 @@ data_size = SIZEOF(.data);
|
||||
ccmram_start = ORIGIN(CCMRAM);
|
||||
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
|
||||
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */
|
||||
firmware_header_start = ccmram_end - 0x400;
|
||||
/* reserve 256 bytes for bootloader arguments */
|
||||
boot_args_start = ORIGIN(BOOT_ARGS);
|
||||
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
|
||||
|
||||
/* used by the startup code to wipe memory */
|
||||
sram_start = ORIGIN(SRAM);
|
||||
|
@ -28,6 +28,11 @@ reset_handler:
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =boot_args_start // r0 - point to beginning of BOOT_ARGS
|
||||
ldr r1, =boot_args_end // r1 - point to byte after the end of BOOT_ARGS
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =sram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
|
@ -25,6 +25,20 @@
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "image.h"
|
||||
#include "model.h"
|
||||
|
||||
const uint8_t BOOTLOADER_KEY_M = 2;
|
||||
const uint8_t BOOTLOADER_KEY_N = 3;
|
||||
static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
#if !PRODUCTION
|
||||
/*** DEVEL/QA KEYS ***/
|
||||
(const uint8_t *)"\xd7\x59\x79\x3b\xbc\x13\xa2\x81\x9a\x82\x7c\x76\xad\xb6\xfb\xa8\xa4\x9a\xee\x00\x7f\x49\xf2\xd0\x99\x2d\x99\xb8\x25\xad\x2c\x48",
|
||||
(const uint8_t *)"\x63\x55\x69\x1c\x17\x8a\x8f\xf9\x10\x07\xa7\x47\x8a\xfb\x95\x5e\xf7\x35\x2c\x63\xe7\xb2\x57\x03\x98\x4c\xf7\x8b\x26\xe2\x1a\x56",
|
||||
(const uint8_t *)"\xee\x93\xa4\xf6\x6f\x8d\x16\xb8\x19\xbb\x9b\xeb\x9f\xfc\xcd\xfc\xdc\x14\x12\xe8\x7f\xee\x6a\x32\x4c\x2a\x99\xa1\xe0\xe6\x71\x48",
|
||||
#else
|
||||
MODEL_BOOTLOADER_KEYS
|
||||
#endif
|
||||
};
|
||||
|
||||
static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
|
||||
const uint8_t *const *pub, uint8_t sigmask,
|
||||
@ -203,6 +217,11 @@ secbool check_vendor_header_sig(const vendor_header *const vhdr, uint8_t key_m,
|
||||
*(const ed25519_signature *)vhdr->sig));
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr) {
|
||||
return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
|
||||
BOOTLOADER_KEYS);
|
||||
}
|
||||
|
||||
void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash) {
|
||||
BLAKE2S_CTX ctx;
|
||||
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
|
||||
@ -257,3 +276,48 @@ secbool check_image_contents(const image_header *const hdr, uint32_t firstskip,
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
secbool check_firmware_header(const uint8_t *header, size_t header_size,
|
||||
firmware_header_info_t *info) {
|
||||
// parse and check vendor header
|
||||
vendor_header vhdr;
|
||||
if (sectrue != read_vendor_header(header, &vhdr)) {
|
||||
return secfalse;
|
||||
}
|
||||
if (sectrue != check_vendor_header_keys(&vhdr)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// parse and check image header
|
||||
const image_header *ihdr;
|
||||
if ((ihdr = read_image_header(header + vhdr.hdrlen, FIRMWARE_IMAGE_MAGIC,
|
||||
FIRMWARE_IMAGE_MAXSIZE)) == NULL) {
|
||||
return secfalse;
|
||||
}
|
||||
if (sectrue !=
|
||||
check_image_header_sig(ihdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
// copy vendor string
|
||||
info->vstr_len = MIN(sizeof(info->vstr), vhdr.vstr_len);
|
||||
if (info->vstr_len > 0) {
|
||||
memcpy(info->vstr, vhdr.vstr, info->vstr_len);
|
||||
}
|
||||
|
||||
// copy firmware version
|
||||
info->ver_major = ihdr->version & 0xFF;
|
||||
info->ver_minor = (ihdr->version >> 8) & 0xFF;
|
||||
info->ver_patch = (ihdr->version >> 16) & 0xFF;
|
||||
|
||||
// calculate and copy the image fingerprint
|
||||
get_image_fingerprint(ihdr, info->fingerprint);
|
||||
|
||||
// calculate hash of both vendor and image headers
|
||||
BLAKE2S_CTX ctx;
|
||||
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
|
||||
blake2s_Update(&ctx, header, vhdr.hdrlen + ihdr->hdrlen);
|
||||
blake2s_Final(&ctx, &info->hash, BLAKE2S_DIGEST_LENGTH);
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define __TREZORHAL_IMAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "blake2s.h"
|
||||
#include "flash.h"
|
||||
#include "model.h"
|
||||
#include "secbool.h"
|
||||
@ -78,6 +79,21 @@ typedef struct {
|
||||
const uint8_t *origin; // pointer to the underlying data
|
||||
} vendor_header;
|
||||
|
||||
typedef struct {
|
||||
// vendor string
|
||||
uint8_t vstr[64];
|
||||
// vendor string length
|
||||
size_t vstr_len;
|
||||
// firmware version
|
||||
uint8_t ver_major;
|
||||
uint8_t ver_minor;
|
||||
uint8_t ver_patch;
|
||||
// firmware fingerprint
|
||||
uint8_t fingerprint[BLAKE2S_DIGEST_LENGTH];
|
||||
// hash of vendor and image header
|
||||
uint8_t hash[BLAKE2S_DIGEST_LENGTH];
|
||||
} firmware_header_info_t;
|
||||
|
||||
const image_header *read_image_header(const uint8_t *const data,
|
||||
const uint32_t magic,
|
||||
const uint32_t maxsize);
|
||||
@ -95,6 +111,8 @@ secbool __wur check_vendor_header_sig(const vendor_header *const vhdr,
|
||||
uint8_t key_m, uint8_t key_n,
|
||||
const uint8_t *const *keys);
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr);
|
||||
|
||||
void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash);
|
||||
|
||||
secbool __wur check_single_hash(const uint8_t *const hash,
|
||||
@ -106,4 +124,7 @@ secbool __wur check_image_contents(const image_header *const hdr,
|
||||
|
||||
void get_image_fingerprint(const image_header *const hdr, uint8_t *const out);
|
||||
|
||||
secbool check_firmware_header(const uint8_t *header, size_t header_size,
|
||||
firmware_header_info_t *info);
|
||||
|
||||
#endif
|
||||
|
@ -667,28 +667,4 @@ int main(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SVC_C_Handler(uint32_t *stack) {
|
||||
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
||||
switch (svc_number) {
|
||||
case SVC_GET_SYSTICK_VAL: {
|
||||
systick_val_copy = SysTick->VAL;
|
||||
} break;
|
||||
default:
|
||||
stack[0] = 0xffffffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile(
|
||||
" tst lr, #4 \n" // Test Bit 3 to see which stack pointer we should
|
||||
// use.
|
||||
" ite eq \n" // Tell the assembler that the nest 2 instructions
|
||||
// are if-then-else
|
||||
" mrseq r0, msp \n" // Make R0 point to main stack pointer
|
||||
" mrsne r0, psp \n" // Make R0 point to process stack pointer
|
||||
" b SVC_C_Handler \n" // Off to C land
|
||||
);
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(HF)"); }
|
||||
|
@ -3,9 +3,10 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
|
||||
BOOT_ARGS (wal) : ORIGIN = 0x1000FF00, LENGTH = 0x100
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(SRAM) + LENGTH(SRAM); /* 8-byte aligned full descending stack */
|
||||
@ -20,6 +21,10 @@ data_size = SIZEOF(.data);
|
||||
ccmram_start = ORIGIN(CCMRAM);
|
||||
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
|
||||
/* reserve 256 bytes for bootloader arguments */
|
||||
boot_args_start = ORIGIN(BOOT_ARGS);
|
||||
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
|
||||
|
||||
/* used by the startup code to wipe memory */
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
@ -11,6 +11,11 @@ reset_handler:
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =boot_args_start // r0 - point to beginning of BOOT_ARGS
|
||||
ldr r1, =boot_args_end // r1 - point to byte after the end of BOOT_ARGS
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =sram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
|
@ -3,9 +3,10 @@
|
||||
ENTRY(reset_handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
|
||||
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
|
||||
BOOT_ARGS (wal) : ORIGIN = 0x1000FF00, LENGTH = 0x100
|
||||
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
|
||||
}
|
||||
|
||||
main_stack_base = ORIGIN(SRAM) + LENGTH(SRAM); /* 8-byte aligned full descending stack */
|
||||
@ -20,6 +21,10 @@ data_size = SIZEOF(.data);
|
||||
ccmram_start = ORIGIN(CCMRAM);
|
||||
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
|
||||
|
||||
/* reserve 256 bytes for bootloader arguments */
|
||||
boot_args_start = ORIGIN(BOOT_ARGS);
|
||||
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
|
||||
|
||||
/* used by the startup code to wipe memory */
|
||||
sram_start = ORIGIN(SRAM);
|
||||
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
|
@ -11,6 +11,11 @@ reset_handler:
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =boot_args_start // r0 - point to beginning of BOOT_ARGS
|
||||
ldr r1, =boot_args_end // r1 - point to byte after the end of BOOT_ARGS
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
ldr r1, =sram_end // r1 - point to byte after the end of SRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
|
@ -46,6 +46,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_confirm_emphasized;
|
||||
MP_QSTR_confirm_ethereum_tx;
|
||||
MP_QSTR_confirm_fido;
|
||||
MP_QSTR_confirm_firmware_update;
|
||||
MP_QSTR_confirm_homescreen;
|
||||
MP_QSTR_confirm_joint_total;
|
||||
MP_QSTR_confirm_modify_fee;
|
||||
@ -73,6 +74,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_fee_amount;
|
||||
MP_QSTR_fee_label;
|
||||
MP_QSTR_fee_rate_amount;
|
||||
MP_QSTR_fingerprint;
|
||||
MP_QSTR_hold;
|
||||
MP_QSTR_hold_danger;
|
||||
MP_QSTR_horizontal;
|
||||
|
@ -15,7 +15,7 @@ uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
||||
uint8_t vendor_str_len, const char* version_str,
|
||||
bool fw_ok);
|
||||
uint32_t screen_menu(secbool firmware_present);
|
||||
void screen_connect(void);
|
||||
void screen_connect(bool initial_setup);
|
||||
void screen_fatal_error_rust(const char* title, const char* msg,
|
||||
const char* footer);
|
||||
void screen_wipe_success(void);
|
||||
|
@ -5,7 +5,7 @@ use crate::ui::{
|
||||
|
||||
pub struct Pad {
|
||||
pub area: Rect,
|
||||
color: Color,
|
||||
pub color: Color,
|
||||
clear: bool,
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::ui::{
|
||||
geometry::{Offset, Rect},
|
||||
};
|
||||
|
||||
use super::theme::{BLD_BG, BLD_FG};
|
||||
use super::super::theme::bootloader::{BLD_BG, BLD_FG};
|
||||
|
||||
pub struct Connect {
|
||||
bg: Pad,
|
||||
|
@ -6,9 +6,11 @@ use crate::ui::{
|
||||
use super::{
|
||||
super::{
|
||||
component::{ButtonController, ButtonControllerMsg::Triggered, ButtonLayout, ButtonPos},
|
||||
theme::{BUTTON_HEIGHT, ICON_WARN_TITLE, TITLE_AREA_HEIGHT},
|
||||
theme::{
|
||||
bootloader::{BLD_BG, BLD_FG, TEXT_NORMAL},
|
||||
BUTTON_HEIGHT, ICON_WARN_TITLE, TITLE_AREA_HEIGHT,
|
||||
},
|
||||
},
|
||||
theme::{BLD_BG, BLD_FG, TEXT_NORMAL},
|
||||
ReturnToC,
|
||||
};
|
||||
|
||||
|
@ -12,8 +12,10 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::component::{Choice, ChoiceFactory, ChoicePage},
|
||||
theme::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH},
|
||||
super::{
|
||||
component::{Choice, ChoiceFactory, ChoicePage},
|
||||
theme::bootloader::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH},
|
||||
},
|
||||
ReturnToC,
|
||||
};
|
||||
|
||||
|
@ -14,11 +14,9 @@ use heapless::String;
|
||||
|
||||
use super::component::{ResultScreen, WelcomeScreen};
|
||||
|
||||
mod confirm;
|
||||
mod connect;
|
||||
mod intro;
|
||||
mod menu;
|
||||
mod theme;
|
||||
mod welcome;
|
||||
|
||||
use crate::{
|
||||
@ -27,14 +25,18 @@ use crate::{
|
||||
constant,
|
||||
constant::HEIGHT,
|
||||
geometry::Point,
|
||||
model_tr::theme::{ICON_ARM_LEFT, ICON_ARM_RIGHT, WHITE},
|
||||
model_tr::{
|
||||
component::bl_confirm::{Confirm, ConfirmMsg},
|
||||
theme::{
|
||||
bootloader::{BLD_BG, BLD_FG, ICON_ALERT, ICON_SPINNER, ICON_SUCCESS},
|
||||
ICON_ARM_LEFT, ICON_ARM_RIGHT, TEXT_BOLD, TEXT_NORMAL, WHITE,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
use confirm::Confirm;
|
||||
use connect::Connect;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
use theme::{BLD_BG, BLD_FG, ICON_ALERT, ICON_SPINNER, ICON_SUCCESS};
|
||||
use welcome::Welcome;
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
@ -55,6 +57,12 @@ impl ReturnToC for () {
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnToC for ConfirmMsg {
|
||||
fn return_to_c(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
fn button_eval() -> Option<ButtonEvent> {
|
||||
let event = io_button_read();
|
||||
if event == 0 {
|
||||
@ -145,18 +153,15 @@ extern "C" fn screen_install_confirm(
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
|
||||
let message =
|
||||
Label::left_aligned(version_str.as_str(), theme::TEXT_NORMAL).vertically_centered();
|
||||
let message = Label::left_aligned(version_str.as_str(), TEXT_NORMAL).vertically_centered();
|
||||
let fingerprint = Label::left_aligned(
|
||||
fingerprint_str,
|
||||
theme::TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
|
||||
TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
|
||||
)
|
||||
.vertically_centered();
|
||||
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"Seed will be erased!",
|
||||
theme::TEXT_NORMAL,
|
||||
));
|
||||
let alert =
|
||||
(!should_keep_seed).then_some(Label::left_aligned("Seed will be erased!", TEXT_NORMAL));
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false)
|
||||
.with_info_screen("FW FINGERPRINT", fingerprint);
|
||||
@ -165,8 +170,8 @@ extern "C" fn screen_install_confirm(
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
let message = Label::left_aligned("Seed and firmware will be erased!", theme::TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
let message =
|
||||
Label::left_aligned("Seed and firmware will be erased!", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET", false);
|
||||
|
||||
@ -175,8 +180,8 @@ extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_unlock_bootloader_confirm() -> u32 {
|
||||
let message = Label::left_aligned("This action cannot be undone!", theme::TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
let message =
|
||||
Label::left_aligned("This action cannot be undone!", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, "UNLOCK BOOTLOADER?", message, None, "UNLOCK", true);
|
||||
|
||||
@ -185,10 +190,10 @@ extern "C" fn screen_unlock_bootloader_confirm() -> u32 {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_unlock_bootloader_success() {
|
||||
let title = Label::centered("Bootloader unlocked", theme::TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Bootloader unlocked", TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect the\ndevice", theme::TEXT_NORMAL).vertically_centered();
|
||||
Label::centered("Please reconnect the\ndevice", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
|
||||
show(&mut frame);
|
||||
@ -295,17 +300,17 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_connect() {
|
||||
extern "C" fn screen_connect(_initial_setup: bool) {
|
||||
let mut frame = Connect::new("Waiting for host...");
|
||||
show(&mut frame);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_success() {
|
||||
let title = Label::centered("Trezor Reset", theme::TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Trezor Reset", TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", theme::TEXT_NORMAL).vertically_centered();
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
|
||||
show(&mut frame);
|
||||
@ -313,10 +318,10 @@ extern "C" fn screen_wipe_success() {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_wipe_fail() {
|
||||
let title = Label::centered("Reset failed", theme::TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Reset failed", TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", theme::TEXT_NORMAL).vertically_centered();
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame);
|
||||
@ -332,10 +337,10 @@ extern "C" fn screen_boot_empty(_fading: bool) {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_install_fail() {
|
||||
let title = Label::centered("Install failed", theme::TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Install failed", TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", theme::TEXT_NORMAL).vertically_centered();
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame);
|
||||
@ -358,9 +363,9 @@ extern "C" fn screen_install_success(
|
||||
unwrap!(reboot_msg.push_str("Reconnect the device"));
|
||||
}
|
||||
|
||||
let title = Label::centered("Firmware installed", theme::TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Firmware installed", TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content = Label::centered(reboot_msg.as_str(), theme::TEXT_NORMAL).vertically_centered();
|
||||
let content = Label::centered(reboot_msg.as_str(), TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw);
|
||||
show(&mut frame);
|
||||
|
@ -4,7 +4,7 @@ use crate::ui::{
|
||||
geometry::{Offset, Rect},
|
||||
};
|
||||
|
||||
use super::theme::{BLD_BG, BLD_FG};
|
||||
use super::super::theme::bootloader::{BLD_BG, BLD_FG};
|
||||
|
||||
pub struct Welcome {
|
||||
bg: Pad,
|
||||
|
@ -1,16 +1,15 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
||||
display::{self, Color, Font},
|
||||
geometry::{Point, Rect},
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
||||
display::{self, Color, Font},
|
||||
geometry::{Point, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::{
|
||||
component::{ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos},
|
||||
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT},
|
||||
},
|
||||
theme::WHITE,
|
||||
ReturnToC,
|
||||
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT, WHITE},
|
||||
ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos,
|
||||
};
|
||||
|
||||
const ALERT_AREA_START: i16 = 39;
|
||||
@ -21,38 +20,36 @@ pub enum ConfirmMsg {
|
||||
Confirm = 2,
|
||||
}
|
||||
|
||||
impl ReturnToC for ConfirmMsg {
|
||||
fn return_to_c(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Confirm<'a> {
|
||||
pub struct Confirm<T: StringType, U> {
|
||||
bg: Pad,
|
||||
bg_color: Color,
|
||||
title: &'static str,
|
||||
message: Child<Label<&'a str>>,
|
||||
alert: Option<Label<&'a str>>,
|
||||
info_title: Option<&'static str>,
|
||||
info_text: Option<Label<&'a str>>,
|
||||
button_text: &'static str,
|
||||
buttons: ButtonController<&'static str>,
|
||||
message: Child<Label<U>>,
|
||||
alert: Option<Label<T>>,
|
||||
info_title: Option<T>,
|
||||
info_text: Option<Label<U>>,
|
||||
button_text: T,
|
||||
buttons: ButtonController<T>,
|
||||
/// Whether we are on the info screen (optional extra screen)
|
||||
showing_info_screen: bool,
|
||||
two_btn_confirm: bool,
|
||||
}
|
||||
|
||||
impl<'a> Confirm<'a> {
|
||||
impl<T, U> Confirm<T, U>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
pub fn new(
|
||||
bg_color: Color,
|
||||
title: &'static str,
|
||||
message: Label<&'a str>,
|
||||
alert: Option<Label<&'a str>>,
|
||||
button_text: &'static str,
|
||||
message: Label<U>,
|
||||
alert: Option<Label<T>>,
|
||||
button_text: T,
|
||||
two_btn_confirm: bool,
|
||||
) -> Self {
|
||||
let btn_layout =
|
||||
Self::get_button_layout_general(false, button_text, false, two_btn_confirm);
|
||||
Self::get_button_layout_general(false, button_text.clone(), false, two_btn_confirm);
|
||||
Self {
|
||||
bg: Pad::with_background(bg_color).with_clear(),
|
||||
bg_color,
|
||||
@ -69,7 +66,7 @@ impl<'a> Confirm<'a> {
|
||||
}
|
||||
|
||||
/// Adding optional info screen
|
||||
pub fn with_info_screen(mut self, info_title: &'static str, info_text: Label<&'a str>) -> Self {
|
||||
pub fn with_info_screen(mut self, info_title: T, info_text: Label<U>) -> Self {
|
||||
self.info_title = Some(info_title);
|
||||
self.info_text = Some(info_text);
|
||||
self.buttons = ButtonController::new(self.get_button_layout());
|
||||
@ -80,10 +77,10 @@ impl<'a> Confirm<'a> {
|
||||
self.info_title.is_some()
|
||||
}
|
||||
|
||||
fn get_button_layout(&self) -> ButtonLayout<&'static str> {
|
||||
fn get_button_layout(&self) -> ButtonLayout<T> {
|
||||
Self::get_button_layout_general(
|
||||
self.showing_info_screen,
|
||||
self.button_text,
|
||||
self.button_text.clone(),
|
||||
self.has_info_screen(),
|
||||
self.two_btn_confirm,
|
||||
)
|
||||
@ -92,10 +89,10 @@ impl<'a> Confirm<'a> {
|
||||
/// Not relying on self here, to call it in constructor.
|
||||
fn get_button_layout_general(
|
||||
showing_info_screen: bool,
|
||||
button_text: &'static str,
|
||||
button_text: T,
|
||||
has_info_screen: bool,
|
||||
two_btn_confirm: bool,
|
||||
) -> ButtonLayout<&'static str> {
|
||||
) -> ButtonLayout<T> {
|
||||
if showing_info_screen {
|
||||
ButtonLayout::arrow_none_none()
|
||||
} else if has_info_screen {
|
||||
@ -124,7 +121,11 @@ impl<'a> Confirm<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Component for Confirm<'a> {
|
||||
impl<T, U> Component for Confirm<T, U>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
type Msg = ConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -209,7 +210,7 @@ impl<'a> Component for Confirm<'a> {
|
||||
|
||||
// We are either on the info screen or on the "main" screen
|
||||
if self.showing_info_screen {
|
||||
display_top_left(unwrap!(self.info_title));
|
||||
display_top_left(unwrap!(self.info_title.clone()).as_ref());
|
||||
self.info_text.paint();
|
||||
} else {
|
||||
display_top_left(self.title);
|
||||
@ -224,3 +225,14 @@ impl<'a> Component for Confirm<'a> {
|
||||
self.buttons.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for Confirm<T, U>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("BlConfirm");
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod bl_confirm;
|
||||
mod button;
|
||||
mod button_controller;
|
||||
mod common;
|
||||
|
@ -30,7 +30,7 @@ use crate::{
|
||||
},
|
||||
TextStyle,
|
||||
},
|
||||
ComponentExt, FormattedText, Timeout,
|
||||
ComponentExt, FormattedText, Label, LineBreaking, Timeout,
|
||||
},
|
||||
display, geometry,
|
||||
layout::{
|
||||
@ -263,6 +263,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for super::component::bl_confirm::Confirm<T, U>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
|
||||
super::component::bl_confirm::ConfirmMsg::Confirm => Ok(CONFIRMED.as_obj()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to create and call a `ButtonPage` dialog based on paginable content
|
||||
/// (e.g. `Paragraphs` or `FormattedText`).
|
||||
/// Has optional title (supply empty `StrBuffer` for that) and hold-to-confirm
|
||||
@ -1609,6 +1622,33 @@ extern "C" fn draw_welcome_screen() -> Obj {
|
||||
Obj::const_none()
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_firmware_update(
|
||||
n_args: usize,
|
||||
args: *const Obj,
|
||||
kwargs: *mut Map,
|
||||
) -> Obj {
|
||||
use super::component::bl_confirm::Confirm;
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
|
||||
|
||||
let title = "UPDATE FIRMWARE";
|
||||
let message = Label::left_aligned(description, theme::TEXT_NORMAL).vertically_centered();
|
||||
let fingerprint = Label::left_aligned(
|
||||
fingerprint,
|
||||
theme::TEXT_NORMAL.with_line_breaking(LineBreaking::BreakWordsNoHyphen),
|
||||
)
|
||||
.vertically_centered();
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Confirm::new(theme::BG, title, message, None, "INSTALL".into(), false)
|
||||
.with_info_screen(StrBuffer::from("FW FINGERPRINT"), fingerprint),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mp_module_trezorui2: Module = obj_module! {
|
||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
||||
@ -2019,4 +2059,12 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// def draw_welcome_screen() -> None:
|
||||
/// """Show logo icon with the model name at the bottom and return."""
|
||||
Qstr::MP_QSTR_draw_welcome_screen => obj_fn_0!(draw_welcome_screen).as_obj(),
|
||||
|
||||
/// def confirm_firmware_update(
|
||||
/// *,
|
||||
/// description: str,
|
||||
/// fingerprint: str,
|
||||
/// ) -> None:
|
||||
/// """Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
|
||||
Qstr::MP_QSTR_confirm_firmware_update => obj_fn_kw!(0, new_confirm_firmware_update).as_obj(),
|
||||
};
|
||||
|
@ -9,6 +9,8 @@ use crate::ui::{
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
pub mod bootloader;
|
||||
|
||||
// Color palette.
|
||||
pub const WHITE: Color = Color::white();
|
||||
pub const BLACK: Color = Color::black();
|
@ -1,9 +1,9 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never, Pad},
|
||||
constant::screen,
|
||||
display::{self, Font},
|
||||
display::{self, Color, Font},
|
||||
geometry::{Offset, Rect},
|
||||
model_tt::bootloader::theme::{BLD_BG, BLD_TITLE_COLOR},
|
||||
model_tt::theme::bootloader::BLD_TITLE_COLOR,
|
||||
};
|
||||
|
||||
pub struct Connect {
|
||||
@ -12,9 +12,9 @@ pub struct Connect {
|
||||
}
|
||||
|
||||
impl Connect {
|
||||
pub fn new(message: &'static str) -> Self {
|
||||
pub fn new(message: &'static str, bg: Color) -> Self {
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
bg: Pad::with_background(bg),
|
||||
message,
|
||||
};
|
||||
|
||||
@ -44,7 +44,7 @@ impl Component for Connect {
|
||||
self.message,
|
||||
Font::NORMAL,
|
||||
BLD_TITLE_COLOR,
|
||||
BLD_BG,
|
||||
self.bg.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ use crate::ui::{
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld, button_bld_menu, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_TITLE, TEXT_WARNING, TITLE_AREA,
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked},
|
||||
constant::WIDTH,
|
||||
theme::bootloader::{
|
||||
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT,
|
||||
CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_WARNING, TITLE_AREA,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -33,7 +33,7 @@ impl<'a> Intro<'a> {
|
||||
pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(Label::left_aligned(title, TEXT_TITLE).vertically_centered()),
|
||||
title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()),
|
||||
menu: Child::new(
|
||||
Button::with_icon(Icon::new(MENU32))
|
||||
.styled(button_bld_menu())
|
||||
|
@ -6,12 +6,12 @@ use crate::{
|
||||
display::Icon,
|
||||
geometry::{Insets, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld, button_bld_menu, BLD_BG, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, FIRE24, REFRESH24, TEXT_TITLE,
|
||||
TITLE_AREA, X32,
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked, IconText},
|
||||
theme::bootloader::{
|
||||
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, FIRE24, REFRESH24, TITLE_AREA,
|
||||
X32,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -42,7 +42,9 @@ impl Menu {
|
||||
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(Label::left_aligned("BOOTLOADER", TEXT_TITLE).vertically_centered()),
|
||||
title: Child::new(
|
||||
Label::left_aligned("BOOTLOADER", text_title(BLD_BG)).vertically_centered(),
|
||||
),
|
||||
close: Child::new(
|
||||
Button::with_icon(Icon::new(X32))
|
||||
.styled(button_bld_menu())
|
||||
|
@ -8,19 +8,22 @@ use crate::{
|
||||
event::TouchEvent,
|
||||
geometry::Point,
|
||||
model_tt::{
|
||||
bootloader::{
|
||||
confirm::ConfirmTitle,
|
||||
connect::Connect,
|
||||
theme::{
|
||||
button_bld, button_confirm, button_wipe_cancel, button_wipe_confirm, BLD_BG,
|
||||
BLD_FG, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD32, FIRE32, FIRE40,
|
||||
TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||
},
|
||||
welcome::Welcome,
|
||||
bootloader::{connect::Connect, welcome::Welcome},
|
||||
component::{
|
||||
bl_confirm::{Confirm, ConfirmTitle},
|
||||
Button, ResultScreen, WelcomeScreen,
|
||||
},
|
||||
component::{Button, ResultScreen, WelcomeScreen},
|
||||
constant,
|
||||
theme::{BACKLIGHT_DIM, BACKLIGHT_NORMAL, FG, WHITE},
|
||||
theme::{
|
||||
bootloader::{
|
||||
button_bld, button_bld_menu, button_confirm, button_wipe_cancel,
|
||||
button_wipe_confirm, BLD_BG, BLD_FG, BLD_WIPE_COLOR, CHECK24, CHECK40,
|
||||
DOWNLOAD32, FIRE32, FIRE40, RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE,
|
||||
TEXT_BOLD, TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40,
|
||||
WELCOME_COLOR, X24,
|
||||
},
|
||||
BACKLIGHT_DIM, BACKLIGHT_NORMAL, FG, WHITE,
|
||||
},
|
||||
},
|
||||
util::{from_c_array, from_c_str},
|
||||
},
|
||||
@ -28,20 +31,15 @@ use crate::{
|
||||
use heapless::String;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
pub mod confirm;
|
||||
mod connect;
|
||||
pub mod intro;
|
||||
pub mod menu;
|
||||
pub mod theme;
|
||||
pub mod welcome;
|
||||
|
||||
use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK};
|
||||
use confirm::Confirm;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
|
||||
use self::theme::{RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE};
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
|
||||
@ -163,12 +161,10 @@ extern "C" fn screen_install_confirm(
|
||||
} else {
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(version_str.as_ref(), theme::TEXT_NORMAL);
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"SEED WILL BE ERASED!",
|
||||
theme::TEXT_BOLD,
|
||||
));
|
||||
let title = Label::left_aligned(title_str, TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(version_str.as_ref(), TEXT_NORMAL);
|
||||
let alert =
|
||||
(!should_keep_seed).then_some(Label::left_aligned("SEED WILL BE ERASED!", TEXT_BOLD));
|
||||
|
||||
let (left, right) = if should_keep_seed {
|
||||
let l = Button::with_text("CANCEL").styled(button_bld());
|
||||
@ -184,6 +180,7 @@ extern "C" fn screen_install_confirm(
|
||||
BLD_BG,
|
||||
left,
|
||||
right,
|
||||
button_bld_menu(),
|
||||
ConfirmTitle::Text(title),
|
||||
msg,
|
||||
alert,
|
||||
@ -210,6 +207,7 @@ extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
BLD_WIPE_COLOR,
|
||||
left,
|
||||
right,
|
||||
button_bld_menu(),
|
||||
ConfirmTitle::Icon(icon),
|
||||
msg,
|
||||
Some(alert),
|
||||
@ -298,15 +296,16 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
|
||||
"Resetting Trezor",
|
||||
progress,
|
||||
initialize,
|
||||
theme::BLD_FG,
|
||||
BLD_FG,
|
||||
BLD_WIPE_COLOR,
|
||||
Some((Icon::new(FIRE32), theme::BLD_FG)),
|
||||
Some((Icon::new(FIRE32), BLD_FG)),
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_connect() {
|
||||
let mut frame = Connect::new("Waiting for host...");
|
||||
extern "C" fn screen_connect(initial_setup: bool) {
|
||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
let mut frame = Connect::new("Waiting for host...", bg);
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,9 @@ use crate::ui::{
|
||||
constant::screen,
|
||||
display::{self, Font, Icon},
|
||||
geometry::{Alignment2D, Offset, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{START_URL, WELCOME_COLOR},
|
||||
theme::{BLACK, GREY_MEDIUM, WHITE},
|
||||
model_tt::theme::{
|
||||
bootloader::{START_URL, WELCOME_COLOR},
|
||||
BLACK, GREY_MEDIUM, WHITE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -5,13 +5,15 @@ use crate::ui::{
|
||||
display::{Color, Icon},
|
||||
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld_menu, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING, CORNER_BUTTON_AREA,
|
||||
CORNER_BUTTON_TOUCH_EXPANSION, INFO32, TEXT_FINGERPRINT, TEXT_TITLE, TITLE_AREA, X32,
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked},
|
||||
component::{Button, ButtonMsg::Clicked, ButtonStyleSheet},
|
||||
constant::WIDTH,
|
||||
theme::WHITE,
|
||||
theme::{
|
||||
bootloader::{
|
||||
text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32, TITLE_AREA, X32,
|
||||
},
|
||||
WHITE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -29,40 +31,44 @@ pub enum ConfirmMsg {
|
||||
Confirm = 2,
|
||||
}
|
||||
|
||||
pub enum ConfirmTitle<'a> {
|
||||
Text(Label<&'a str>),
|
||||
pub enum ConfirmTitle<T> {
|
||||
Text(Label<T>),
|
||||
Icon(Icon),
|
||||
}
|
||||
|
||||
pub struct ConfirmInfo<'a> {
|
||||
pub title: Child<Label<&'a str>>,
|
||||
pub text: Child<Label<&'a str>>,
|
||||
pub struct ConfirmInfo<T> {
|
||||
pub title: Child<Label<T>>,
|
||||
pub text: Child<Label<T>>,
|
||||
pub info_button: Child<Button<&'static str>>,
|
||||
pub close_button: Child<Button<&'static str>>,
|
||||
}
|
||||
|
||||
pub struct Confirm<'a> {
|
||||
pub struct Confirm<T> {
|
||||
bg: Pad,
|
||||
content_pad: Pad,
|
||||
bg_color: Color,
|
||||
title: ConfirmTitle<'a>,
|
||||
message: Child<Label<&'a str>>,
|
||||
alert: Option<Child<Label<&'a str>>>,
|
||||
title: ConfirmTitle<T>,
|
||||
message: Child<Label<T>>,
|
||||
alert: Option<Child<Label<T>>>,
|
||||
left_button: Child<Button<&'static str>>,
|
||||
right_button: Child<Button<&'static str>>,
|
||||
info: Option<ConfirmInfo<'a>>,
|
||||
info: Option<ConfirmInfo<T>>,
|
||||
show_info: bool,
|
||||
}
|
||||
|
||||
impl<'a> Confirm<'a> {
|
||||
impl<T> Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
pub fn new(
|
||||
bg_color: Color,
|
||||
left_button: Button<&'static str>,
|
||||
right_button: Button<&'static str>,
|
||||
title: ConfirmTitle<'a>,
|
||||
message: Label<&'a str>,
|
||||
alert: Option<Label<&'a str>>,
|
||||
info: Option<(&'a str, &'a str)>,
|
||||
menu_button: ButtonStyleSheet,
|
||||
title: ConfirmTitle<T>,
|
||||
message: Label<T>,
|
||||
alert: Option<Label<T>>,
|
||||
info: Option<(T, T)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(bg_color).with_clear(),
|
||||
@ -74,16 +80,20 @@ impl<'a> Confirm<'a> {
|
||||
left_button: Child::new(left_button),
|
||||
right_button: Child::new(right_button),
|
||||
info: info.map(|(title, text)| ConfirmInfo {
|
||||
title: Child::new(Label::left_aligned(title, TEXT_TITLE).vertically_centered()),
|
||||
text: Child::new(Label::left_aligned(text, TEXT_FINGERPRINT).vertically_centered()),
|
||||
title: Child::new(
|
||||
Label::left_aligned(title, text_title(bg_color)).vertically_centered(),
|
||||
),
|
||||
text: Child::new(
|
||||
Label::left_aligned(text, text_fingerprint(bg_color)).vertically_centered(),
|
||||
),
|
||||
info_button: Child::new(
|
||||
Button::with_icon(Icon::new(INFO32))
|
||||
.styled(button_bld_menu())
|
||||
.styled(menu_button)
|
||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||
),
|
||||
close_button: Child::new(
|
||||
Button::with_icon(Icon::new(X32))
|
||||
.styled(button_bld_menu())
|
||||
.styled(menu_button)
|
||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||
),
|
||||
}),
|
||||
@ -92,7 +102,10 @@ impl<'a> Confirm<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Component for Confirm<'a> {
|
||||
impl<T> Component for Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
type Msg = ConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -220,3 +233,13 @@ impl<'a> Component for Confirm<'a> {
|
||||
self.right_button.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("BlConfirm");
|
||||
}
|
||||
}
|
@ -357,7 +357,7 @@ pub enum ButtonContent<T> {
|
||||
IconBlend(Icon, Icon, Offset),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ButtonStyleSheet {
|
||||
pub normal: &'static ButtonStyle,
|
||||
pub active: &'static ButtonStyle,
|
||||
|
@ -15,7 +15,7 @@ const TITLE_AREA_START: i16 = 70;
|
||||
const MESSAGE_AREA_START: i16 = 116;
|
||||
|
||||
#[cfg(feature = "bootloader")]
|
||||
const STYLE: &ResultStyle = &crate::ui::model_tt::bootloader::theme::RESULT_WIPE;
|
||||
const STYLE: &ResultStyle = &crate::ui::model_tt::theme::bootloader::RESULT_WIPE;
|
||||
#[cfg(not(feature = "bootloader"))]
|
||||
const STYLE: &ResultStyle = &super::theme::RESULT_ERROR;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
mod address_details;
|
||||
pub mod bl_confirm;
|
||||
mod button;
|
||||
mod coinjoin_progress;
|
||||
mod dialog;
|
||||
|
@ -4,7 +4,7 @@ use crate::ui::{
|
||||
model_tt::theme,
|
||||
};
|
||||
#[cfg(feature = "bootloader")]
|
||||
use crate::ui::{display::Icon, model_tt::bootloader::theme::DEVICE_NAME};
|
||||
use crate::ui::{display::Icon, model_tt::theme::bootloader::DEVICE_NAME};
|
||||
|
||||
const TEXT_BOTTOM_MARGIN: i16 = 24; // matching the homescreen label margin
|
||||
const ICON_TOP_MARGIN: i16 = 48;
|
||||
|
@ -31,7 +31,7 @@ use crate::{
|
||||
},
|
||||
TextStyle,
|
||||
},
|
||||
Border, Component, Empty, FormattedText, Never, Qr, Timeout,
|
||||
Border, Component, Empty, FormattedText, Label, Never, Qr, Timeout,
|
||||
},
|
||||
display::{self, tjpgd::jpeg_info},
|
||||
geometry,
|
||||
@ -363,6 +363,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for super::component::bl_confirm::Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
|
||||
super::component::bl_confirm::ConfirmMsg::Confirm => Ok(CONFIRMED.as_obj()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
@ -1586,6 +1598,39 @@ extern "C" fn draw_welcome_screen() -> Obj {
|
||||
Obj::const_none()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn new_confirm_firmware_update(
|
||||
n_args: usize,
|
||||
args: *const Obj,
|
||||
kwargs: *mut Map,
|
||||
) -> Obj {
|
||||
use super::component::bl_confirm::{Confirm, ConfirmTitle};
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
|
||||
|
||||
let title_str = StrBuffer::from("UPDATE FIRMWARE");
|
||||
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(description, theme::TEXT_NORMAL);
|
||||
|
||||
let left = Button::with_text("CANCEL").styled(theme::button_default());
|
||||
let right = Button::with_text("INSTALL").styled(theme::button_confirm());
|
||||
|
||||
let obj = LayoutObj::new(Confirm::new(
|
||||
theme::BG,
|
||||
left,
|
||||
right,
|
||||
theme::button_moreinfo(),
|
||||
ConfirmTitle::Text(title),
|
||||
msg,
|
||||
None,
|
||||
Some(("FW FINGERPRINT".into(), fingerprint)),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mp_module_trezorui2: Module = obj_module! {
|
||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
||||
@ -2006,6 +2051,14 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// def draw_welcome_screen() -> None:
|
||||
/// """Show logo icon with the model name at the bottom and return."""
|
||||
Qstr::MP_QSTR_draw_welcome_screen => obj_fn_0!(draw_welcome_screen).as_obj(),
|
||||
|
||||
/// def confirm_firmware_update(
|
||||
/// *,
|
||||
/// description: str,
|
||||
/// fingerprint: str,
|
||||
/// ) -> None:
|
||||
/// """Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
|
||||
Qstr::MP_QSTR_confirm_firmware_update => obj_fn_kw!(0, new_confirm_firmware_update).as_obj(),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -231,13 +231,15 @@ pub fn button_bld() -> ButtonStyleSheet {
|
||||
}
|
||||
}
|
||||
|
||||
pub const TEXT_TITLE: TextStyle = TextStyle::new(
|
||||
Font::BOLD,
|
||||
BLD_TITLE_COLOR,
|
||||
BLD_BG,
|
||||
BLD_TITLE_COLOR,
|
||||
BLD_TITLE_COLOR,
|
||||
);
|
||||
pub const fn text_title(bg: Color) -> TextStyle {
|
||||
TextStyle::new(
|
||||
Font::BOLD,
|
||||
BLD_TITLE_COLOR,
|
||||
bg,
|
||||
BLD_TITLE_COLOR,
|
||||
BLD_TITLE_COLOR,
|
||||
)
|
||||
}
|
||||
|
||||
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
|
||||
pub const TEXT_WARNING: TextStyle = TextStyle::new(
|
||||
@ -247,9 +249,9 @@ pub const TEXT_WARNING: TextStyle = TextStyle::new(
|
||||
BLD_WARN_COLOR,
|
||||
BLD_WARN_COLOR,
|
||||
);
|
||||
pub const TEXT_FINGERPRINT: TextStyle =
|
||||
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG)
|
||||
.with_line_breaking(BreakWordsNoHyphen);
|
||||
pub const fn text_fingerprint(bg: Color) -> TextStyle {
|
||||
TextStyle::new(Font::NORMAL, BLD_FG, bg, BLD_FG, BLD_FG).with_line_breaking(BreakWordsNoHyphen)
|
||||
}
|
||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
|
||||
pub const TEXT_WIPE_BOLD: TextStyle = TextStyle::new(
|
||||
Font::BOLD,
|
@ -1,3 +1,5 @@
|
||||
pub mod bootloader;
|
||||
|
||||
use crate::{
|
||||
time::Duration,
|
||||
ui::{
|
20
core/embed/trezorhal/boot_args.h
Normal file
20
core/embed/trezorhal/boot_args.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef TREZORHAL_BOOT_ARGS_H
|
||||
#define TREZORHAL_BOOT_ARGS_H
|
||||
|
||||
|
||||
// Defines boot command for 'svc_reboot_to_bootloader()' function
|
||||
typedef enum {
|
||||
// Normal boot sequence
|
||||
BOOT_COMMAND_NONE = 0x00000000,
|
||||
// Stop and wait for further instructions
|
||||
BOOT_COMMAND_STOP_AND_WAIT = 0x0FC35A96,
|
||||
// Do not ask anything, install an upgrade
|
||||
BOOT_COMMAND_INSTALL_UPGRADE = 0xFA4A5C8D,
|
||||
} boot_command_t;
|
||||
|
||||
// Maximum size of extra arguments passed to
|
||||
// 'svc_reboot_to_bootloader()' function
|
||||
#define BOOT_ARGS_SIZE 256
|
||||
|
||||
|
||||
#endif // TREZORHAL_BOOT_ARGS_H
|
@ -51,12 +51,7 @@
|
||||
})
|
||||
#endif
|
||||
|
||||
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
|
||||
|
||||
// from linker script
|
||||
extern uint8_t firmware_header_start;
|
||||
extern uint8_t ccmram_start;
|
||||
extern uint8_t ccmram_end;
|
||||
|
||||
void __attribute__((noreturn)) trezor_shutdown(void);
|
||||
|
||||
|
112
core/embed/trezorhal/stm32f4/supervise.c
Normal file
112
core/embed/trezorhal/stm32f4/supervise.c
Normal file
@ -0,0 +1,112 @@
|
||||
#include STM32_HAL_H
|
||||
|
||||
#include <model.h>
|
||||
|
||||
#include "../mpu.h"
|
||||
#include "common.h"
|
||||
#include "supervise.h"
|
||||
|
||||
#ifdef ARM_USER_MODE
|
||||
|
||||
|
||||
// Saves extra parameters for the bootloader
|
||||
static void _copy_boot_args(const void *args, size_t args_size) {
|
||||
|
||||
// symbols imported from the linker script
|
||||
extern uint8_t boot_args_start;
|
||||
extern uint8_t boot_args_end;
|
||||
|
||||
uint8_t *p = &boot_args_start;
|
||||
|
||||
if (args != NULL && args_size > 0) {
|
||||
size_t max_size = &boot_args_end - &boot_args_start;
|
||||
size_t copy_size = MIN(args_size, max_size);
|
||||
memcpy(p, args, copy_size);
|
||||
p += args_size;
|
||||
}
|
||||
|
||||
if (p < &boot_args_end) {
|
||||
memset(p, 0, &boot_args_end - p);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) static void _reboot_to_bootloader(
|
||||
boot_command_t boot_command) {
|
||||
mpu_config_bootloader();
|
||||
jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE, boot_command);
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
void svc_reboot_to_bootloader(boot_command_t boot_command, const void *args,
|
||||
size_t args_size) {
|
||||
_copy_boot_args(args, args_size);
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
register uint32_t r0 __asm__("r0") = boot_command;
|
||||
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_TO_BOOTLOADER), "r"(r0)
|
||||
: "memory");
|
||||
} else {
|
||||
ensure_compatible_settings();
|
||||
_reboot_to_bootloader(boot_command);
|
||||
}
|
||||
}
|
||||
|
||||
void SVC_C_Handler(uint32_t *stack) {
|
||||
uint8_t svc_number = ((uint8_t *)stack[6])[-2];
|
||||
switch (svc_number) {
|
||||
case SVC_ENABLE_IRQ:
|
||||
HAL_NVIC_EnableIRQ(stack[0]);
|
||||
break;
|
||||
case SVC_DISABLE_IRQ:
|
||||
HAL_NVIC_DisableIRQ(stack[0]);
|
||||
break;
|
||||
case SVC_SET_PRIORITY:
|
||||
NVIC_SetPriority(stack[0], stack[1]);
|
||||
break;
|
||||
#ifdef SYSTEM_VIEW
|
||||
case SVC_GET_DWT_CYCCNT:
|
||||
cyccnt_cycles = *DWT_CYCCNT_ADDR;
|
||||
break;
|
||||
#endif
|
||||
case SVC_SHUTDOWN:
|
||||
shutdown_privileged();
|
||||
for (;;)
|
||||
;
|
||||
break;
|
||||
case SVC_REBOOT_TO_BOOTLOADER:
|
||||
ensure_compatible_settings();
|
||||
|
||||
__asm__ volatile("msr control, %0" ::"r"(0x0));
|
||||
__asm__ volatile("isb");
|
||||
|
||||
__asm__ volatile(
|
||||
"mov r0, %[boot_command]" ::[boot_command] "r"(stack[0]));
|
||||
|
||||
// See stack layout in
|
||||
// https://developer.arm.com/documentation/ka004005/latest We are changing
|
||||
// return address in PC to land into reboot to avoid any bug with ROP and
|
||||
// raising privileges.
|
||||
stack[6] = (uintptr_t)_reboot_to_bootloader;
|
||||
return;
|
||||
case SVC_GET_SYSTICK_VAL:
|
||||
systick_val_copy = SysTick->VAL;
|
||||
break;
|
||||
default:
|
||||
stack[0] = 0xffffffff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile(
|
||||
" tst lr, #4 \n" // Test Bit 3 to see which stack pointer we should
|
||||
// use.
|
||||
" ite eq \n" // Tell the assembler that the nest 2 instructions
|
||||
// are if-then-else
|
||||
" mrseq r0, msp \n" // Make R0 point to main stack pointer
|
||||
" mrsne r0, psp \n" // Make R0 point to process stack pointer
|
||||
" b SVC_C_Handler \n" // Off to C land
|
||||
);
|
||||
}
|
||||
|
||||
#endif // ARM_USER_MODE
|
@ -5,10 +5,10 @@
|
||||
#define SVC_SET_PRIORITY 2
|
||||
#define SVC_SHUTDOWN 4
|
||||
#define SVC_REBOOT_TO_BOOTLOADER 5
|
||||
#define SVC_REBOOT_COPY_IMAGE_HEADER 6
|
||||
#define SVC_GET_SYSTICK_VAL 7
|
||||
#define SVC_GET_SYSTICK_VAL 6
|
||||
|
||||
#include <string.h>
|
||||
#include "boot_args.h"
|
||||
#include "common.h"
|
||||
#include "image.h"
|
||||
|
||||
@ -17,8 +17,6 @@ extern uint32_t systick_val_copy;
|
||||
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
extern void reboot_to_bootloader(void);
|
||||
extern void copy_image_header_for_bootloader(const uint8_t *image_header);
|
||||
extern void ensure_compatible_settings(void);
|
||||
|
||||
static inline uint32_t is_mode_unprivileged(void) {
|
||||
@ -70,27 +68,8 @@ static inline void svc_shutdown(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void svc_reboot_to_bootloader(void) {
|
||||
explicit_bzero(&firmware_header_start, IMAGE_HEADER_SIZE);
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_TO_BOOTLOADER) : "memory");
|
||||
} else {
|
||||
ensure_compatible_settings();
|
||||
reboot_to_bootloader();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void svc_reboot_copy_image_header(const uint8_t *image_address) {
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
register const uint8_t *r0 __asm__("r0") = image_address;
|
||||
__asm__ __volatile__("svc %0" ::"i"(SVC_REBOOT_COPY_IMAGE_HEADER), "r"(r0)
|
||||
: "memory");
|
||||
} else {
|
||||
copy_image_header_for_bootloader(image_address);
|
||||
ensure_compatible_settings();
|
||||
reboot_to_bootloader();
|
||||
}
|
||||
}
|
||||
void svc_reboot_to_bootloader(boot_command_t boot_command, const void* args,
|
||||
size_t args_size);
|
||||
|
||||
static inline uint32_t svc_get_systick_val(void) {
|
||||
if (is_mode_unprivileged() && !is_mode_handler()) {
|
||||
|
@ -43,7 +43,7 @@ jump_to_with_flag:
|
||||
// wipe memory at the end of the current stage of code
|
||||
bl clear_otg_hs_memory
|
||||
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM
|
||||
ldr r1, =firmware_header_start // r1 - point to byte after the end of CCMRAM
|
||||
ldr r1, =boot_args_start // r1 - point to byte after the end of CCMRAM
|
||||
ldr r2, =0 // r2 - the word-sized value to be written
|
||||
bl memset_reg
|
||||
ldr r0, =sram_start // r0 - point to beginning of SRAM
|
||||
|
@ -442,6 +442,15 @@ def show_lockscreen(
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def draw_welcome_screen() -> None:
|
||||
"""Show logo icon with the model name at the bottom and return."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def confirm_firmware_update(
|
||||
*,
|
||||
description: str,
|
||||
fingerprint: str,
|
||||
) -> None:
|
||||
"""Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
|
||||
CONFIRMED: object
|
||||
CANCELLED: object
|
||||
INFO: object
|
||||
@ -895,3 +904,12 @@ def show_lockscreen(
|
||||
# rust/src/ui/model_tt/layout.rs
|
||||
def draw_welcome_screen() -> None:
|
||||
"""Show logo icon with the model name at the bottom and return."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tt/layout.rs
|
||||
def confirm_firmware_update(
|
||||
*,
|
||||
description: str,
|
||||
fingerprint: str,
|
||||
) -> None:
|
||||
"""Ask whether to update firmware, optionally show fingerprint. Shared with bootloader."""
|
||||
|
@ -75,12 +75,30 @@ def unit_btconly() -> bool | None:
|
||||
|
||||
|
||||
# extmod/modtrezorutils/modtrezorutils.c
|
||||
def reboot_to_bootloader() -> None:
|
||||
def reboot_to_bootloader(
|
||||
boot_command : int = 0,
|
||||
boot_args : bytes | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Reboots to bootloader.
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorutils/modtrezorutils.c
|
||||
def check_firmware_header(
|
||||
header : bytes
|
||||
) -> dict:
|
||||
"""
|
||||
Checks firmware image and vendor header and returns
|
||||
{ "version": (major, minor, patch),
|
||||
"vendor": string,
|
||||
"full_trust": bool,
|
||||
"fingerprint": bytes,
|
||||
"hash": bytes
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorutils/modtrezorutils.c
|
||||
def bootloader_locked() -> bool | None:
|
||||
"""
|
||||
|
@ -43,6 +43,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
||||
"embed/trezorhal/stm32f4/mpu.c",
|
||||
"embed/trezorhal/stm32f4/platform.c",
|
||||
"embed/trezorhal/stm32f4/systick.c",
|
||||
"embed/trezorhal/stm32f4/supervise.c",
|
||||
"embed/trezorhal/stm32f4/random_delays.c",
|
||||
"embed/trezorhal/stm32f4/rng.c",
|
||||
"embed/trezorhal/stm32f4/vectortable.s",
|
||||
|
@ -93,6 +93,8 @@ trezor.enums.AmountUnit
|
||||
import trezor.enums.AmountUnit
|
||||
trezor.enums.BackupType
|
||||
import trezor.enums.BackupType
|
||||
trezor.enums.BootCommand
|
||||
import trezor.enums.BootCommand
|
||||
trezor.enums.ButtonRequestType
|
||||
import trezor.enums.ButtonRequestType
|
||||
trezor.enums.Capability
|
||||
|
@ -7,20 +7,60 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
async def reboot_to_bootloader(msg: RebootToBootloader) -> NoReturn:
|
||||
from trezor import io, loop, utils
|
||||
from ubinascii import hexlify
|
||||
|
||||
from trezor import io, loop, utils, wire
|
||||
from trezor.enums import BootCommand
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_action
|
||||
from trezor.ui.layouts import confirm_action, confirm_firmware_update
|
||||
from trezor.wire.context import get_context
|
||||
|
||||
await confirm_action(
|
||||
"reboot",
|
||||
"Go to bootloader",
|
||||
"Do you want to restart Trezor in bootloader mode?",
|
||||
verb="Restart",
|
||||
)
|
||||
if (
|
||||
msg.boot_command == BootCommand.INSTALL_UPGRADE
|
||||
and msg.firmware_header is not None
|
||||
):
|
||||
# check and parse received firmware header
|
||||
hdr = utils.check_firmware_header(msg.firmware_header)
|
||||
if hdr is None:
|
||||
raise wire.DataError("Invalid firmware header.")
|
||||
else:
|
||||
# vendor must be the same
|
||||
if hdr["vendor"] != utils.firmware_vendor():
|
||||
raise wire.DataError("Different firmware vendor.")
|
||||
|
||||
current_version = (
|
||||
int(utils.VERSION_MAJOR),
|
||||
int(utils.VERSION_MINOR),
|
||||
int(utils.VERSION_PATCH),
|
||||
)
|
||||
|
||||
# firmware must be newer
|
||||
if hdr["version"] <= current_version:
|
||||
raise wire.DataError("Not a firmware upgrade.")
|
||||
|
||||
version_str = ".".join(map(str, hdr["version"]))
|
||||
|
||||
await confirm_firmware_update(
|
||||
description=f"Firmware version {version_str}\nby {hdr['vendor']}",
|
||||
fingerprint=hexlify(hdr["fingerprint"]).decode(),
|
||||
)
|
||||
boot_command = BootCommand.INSTALL_UPGRADE
|
||||
boot_args = hdr["hash"]
|
||||
|
||||
else:
|
||||
await confirm_action(
|
||||
"reboot",
|
||||
"Go to bootloader",
|
||||
"Do you want to restart Trezor in bootloader mode?",
|
||||
verb="Restart",
|
||||
)
|
||||
boot_command = BootCommand.STOP_AND_WAIT
|
||||
boot_args = None
|
||||
|
||||
ctx = get_context()
|
||||
await ctx.write(Success(message="Rebooting"))
|
||||
# make sure the outgoing USB buffer is flushed
|
||||
await loop.wait(ctx.iface.iface_num() | io.POLL_WRITE)
|
||||
utils.reboot_to_bootloader()
|
||||
# reboot to the bootloader, pass the firmware header hash if any
|
||||
utils.reboot_to_bootloader(boot_command, boot_args)
|
||||
raise RuntimeError
|
||||
|
6
core/src/trezor/enums/BootCommand.py
Normal file
6
core/src/trezor/enums/BootCommand.py
Normal file
@ -0,0 +1,6 @@
|
||||
# Automatically generated by pb2py
|
||||
# fmt: off
|
||||
# isort:skip_file
|
||||
|
||||
STOP_AND_WAIT = 0
|
||||
INSTALL_UPGRADE = 1
|
@ -450,6 +450,10 @@ if TYPE_CHECKING:
|
||||
Matrix9 = 1
|
||||
Matrix6 = 2
|
||||
|
||||
class BootCommand(IntEnum):
|
||||
STOP_AND_WAIT = 0
|
||||
INSTALL_UPGRADE = 1
|
||||
|
||||
class DebugSwipeDirection(IntEnum):
|
||||
UP = 0
|
||||
DOWN = 1
|
||||
|
@ -21,6 +21,7 @@ if TYPE_CHECKING:
|
||||
from trezor.enums import BinanceOrderSide # noqa: F401
|
||||
from trezor.enums import BinanceOrderType # noqa: F401
|
||||
from trezor.enums import BinanceTimeInForce # noqa: F401
|
||||
from trezor.enums import BootCommand # noqa: F401
|
||||
from trezor.enums import ButtonRequestType # noqa: F401
|
||||
from trezor.enums import Capability # noqa: F401
|
||||
from trezor.enums import CardanoAddressType # noqa: F401
|
||||
@ -2617,6 +2618,16 @@ if TYPE_CHECKING:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class RebootToBootloader(protobuf.MessageType):
|
||||
boot_command: "BootCommand"
|
||||
firmware_header: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
boot_command: "BootCommand | None" = None,
|
||||
firmware_header: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["RebootToBootloader"]:
|
||||
|
@ -1349,3 +1349,17 @@ async def confirm_set_new_pin(
|
||||
"CONTINUE",
|
||||
br_code,
|
||||
)
|
||||
|
||||
|
||||
async def confirm_firmware_update(description: str, fingerprint: str) -> None:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.confirm_firmware_update(
|
||||
description=description, fingerprint=fingerprint
|
||||
)
|
||||
),
|
||||
"firmware_update",
|
||||
BR_TYPE_OTHER,
|
||||
)
|
||||
)
|
||||
|
@ -1368,3 +1368,17 @@ async def confirm_set_new_pin(
|
||||
br_code,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def confirm_firmware_update(description: str, fingerprint: str) -> None:
|
||||
await raise_if_not_confirmed(
|
||||
interact(
|
||||
RustLayout(
|
||||
trezorui2.confirm_firmware_update(
|
||||
description=description, fingerprint=fingerprint
|
||||
)
|
||||
),
|
||||
"firmware_update",
|
||||
BR_TYPE_OTHER,
|
||||
)
|
||||
)
|
||||
|
@ -15,6 +15,7 @@ from trezorutils import ( # noqa: F401
|
||||
VERSION_MINOR,
|
||||
VERSION_PATCH,
|
||||
bootloader_locked,
|
||||
check_firmware_header,
|
||||
consteq,
|
||||
firmware_hash,
|
||||
firmware_vendor,
|
||||
|
@ -238,8 +238,16 @@ def unlock_path(client: "TrezorClient", n: "Address") -> "MessageType":
|
||||
|
||||
@session
|
||||
@expect(messages.Success, field="message", ret_type=str)
|
||||
def reboot_to_bootloader(client: "TrezorClient") -> "MessageType":
|
||||
return client.call(messages.RebootToBootloader())
|
||||
def reboot_to_bootloader(
|
||||
client: "TrezorClient",
|
||||
boot_command: messages.BootCommand = messages.BootCommand.STOP_AND_WAIT,
|
||||
firmware_header: Optional[bytes] = None,
|
||||
) -> "MessageType":
|
||||
return client.call(
|
||||
messages.RebootToBootloader(
|
||||
boot_command=boot_command, firmware_header=firmware_header
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@session
|
||||
|
Loading…
Reference in New Issue
Block a user