1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-16 17:42:02 +00:00

feat(core): introduce interaction-less upgrade

This commit is contained in:
cepetr 2023-10-20 14:58:32 +02:00 committed by matejcik
parent 804874c7b9
commit ba83a7e644
72 changed files with 956 additions and 396 deletions

View File

@ -0,0 +1 @@
Support interaction-less upgrade

View File

@ -445,6 +445,7 @@ env.Replace(
'FIRMWARE', 'FIRMWARE',
'TREZOR_MODEL_'+TREZOR_MODEL, 'TREZOR_MODEL_'+TREZOR_MODEL,
'USE_HAL_DRIVER', 'USE_HAL_DRIVER',
'ARM_USER_MODE',
UI_LAYOUT, UI_LAYOUT,
] + CPPDEFINES_MOD + CPPDEFINES_HAL, ] + CPPDEFINES_MOD + CPPDEFINES_HAL,
ASFLAGS=env.get('ENV')['CPU_ASFLAGS'], ASFLAGS=env.get('ENV')['CPU_ASFLAGS'],

View File

@ -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('MONO', FONT_MONO, CPPDEFINES_MOD, SOURCE_MOD)
tools.add_font('BIG', FONT_BIG, 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) FEATURES_AVAILABLE = tools.configure_board(TREZOR_MODEL, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
@ -151,6 +151,7 @@ env.Replace(
CPPDEFINES=[ CPPDEFINES=[
'TREZOR_PRODTEST', 'TREZOR_PRODTEST',
'TREZOR_MODEL_'+TREZOR_MODEL, 'TREZOR_MODEL_'+TREZOR_MODEL,
'ARM_USER_MODE',
'USE_HAL_DRIVER', 'USE_HAL_DRIVER',
] + CPPDEFINES_MOD + CPPDEFINES_HAL, ] + CPPDEFINES_MOD + CPPDEFINES_HAL,
ASFLAGS=env.get('ENV')['CPU_ASFLAGS'], ASFLAGS=env.get('ENV')['CPU_ASFLAGS'],

View File

@ -3,9 +3,9 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 48K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 48K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K
} }
main_stack_base = ORIGIN(CCMRAM) + LENGTH(CCMRAM); /* 8-byte aligned full descending stack */ main_stack_base = ORIGIN(CCMRAM) + LENGTH(CCMRAM); /* 8-byte aligned full descending stack */

View File

@ -0,0 +1 @@
Minimize risk of losing seed when upgrading firmware

View File

@ -0,0 +1 @@
Support interaction-less upgrade

View 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

View File

@ -2,6 +2,7 @@
#include <unistd.h> #include <unistd.h>
#include TREZOR_BOARD #include TREZOR_BOARD
#include "boot_internal.h"
#include "bootui.h" #include "bootui.h"
#include "common.h" #include "common.h"
#include "display.h" #include "display.h"
@ -17,7 +18,12 @@
#undef FIRMWARE_START #undef FIRMWARE_START
uint8_t *FIRMWARE_START = 0; 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) {} void set_core_clock(int) {}
@ -47,7 +53,7 @@ __attribute__((noreturn)) int main(int argc, char **argv) {
if (argc == 2) { if (argc == 2) {
if (argv[1][0] == 's') { if (argv[1][0] == 's') {
// Run the firmware // Run the firmware
stay_in_bootloader_flag = STAY_IN_BOOTLOADER_FLAG; g_boot_command = BOOT_COMMAND_STOP_AND_WAIT;
} }
#ifdef USE_OPTIGA #ifdef USE_OPTIGA
else if (argv[1][0] == 'l') { else if (argv[1][0] == 'l') {

View File

@ -2,7 +2,6 @@
#define __EMULATOR_H__ #define __EMULATOR_H__
#define CLOCK_180_MHZ 0 #define CLOCK_180_MHZ 0
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
#define mini_snprintf snprintf #define mini_snprintf snprintf
#undef FIRMWARE_START #undef FIRMWARE_START
@ -11,7 +10,6 @@
#include <stdio.h> #include <stdio.h>
extern uint8_t *FIRMWARE_START; extern uint8_t *FIRMWARE_START;
extern uint32_t stay_in_bootloader_flag;
void emulator_poll_events(void); void emulator_poll_events(void);
void set_core_clock(int); void set_core_clock(int);

View File

@ -20,6 +20,7 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include "boot_internal.h"
#include "common.h" #include "common.h"
#include "display.h" #include "display.h"
#include "flash.h" #include "flash.h"
@ -68,19 +69,6 @@
#include "platform.h" #include "platform.h"
#endif #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 #define USB_IFACE_NUM 0
typedef enum { 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) { static secbool check_vendor_header_lock(const vendor_header *const vhdr) {
uint8_t lock[FLASH_OTP_BLOCK_SIZE]; uint8_t lock[FLASH_OTP_BLOCK_SIZE];
ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock, 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 #ifndef TREZOR_EMULATOR
int main(void) { 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 #else
int bootloader_main(void) { int bootloader_main(void) {
#endif #endif
secbool stay_in_bootloader = secfalse;
random_delays_init(); random_delays_init();
// display_init_seq(); // display_init_seq();
@ -413,6 +394,7 @@ int bootloader_main(void) {
volatile secbool header_present = secfalse; volatile secbool header_present = secfalse;
volatile secbool firmware_present = secfalse; volatile secbool firmware_present = secfalse;
volatile secbool firmware_present_backup = secfalse; volatile secbool firmware_present_backup = secfalse;
volatile secbool auto_upgrade = secfalse;
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr); vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
@ -482,10 +464,19 @@ int bootloader_main(void) {
check_bootloader_version(); check_bootloader_version();
#endif #endif
// was there reboot with request to stay in bootloader? switch (g_boot_command) {
secbool stay_in_bootloader = secfalse; case BOOT_COMMAND_STOP_AND_WAIT:
if (stay_in_bootloader_flag == STAY_IN_BOOTLOADER_FLAG) { // firmare requested to stay in bootloader
stay_in_bootloader = sectrue; 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), ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup),
@ -521,12 +512,20 @@ int bootloader_main(void) {
// start the bootloader ... // start the bootloader ...
// ... if user touched the screen on start // ... if user touched the screen on start
// ... or we have stay_in_bootloader flag to force it // ... 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 // ... 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; screen_t screen;
ui_set_initial_setup(true);
if (header_present == sectrue) { if (header_present == sectrue) {
ui_set_initial_setup(false); if (auto_upgrade == sectrue) {
screen = SCREEN_INTRO; screen = SCREEN_WAIT_FOR_HOST;
} else {
ui_set_initial_setup(false);
screen = SCREEN_INTRO;
}
} else { } else {
screen = SCREEN_WELCOME; screen = SCREEN_WELCOME;
@ -534,8 +533,6 @@ int bootloader_main(void) {
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL), ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
NULL); NULL);
ui_set_initial_setup(true);
// keep the model screen up for a while // keep the model screen up for a while
#ifndef USE_BACKLIGHT #ifndef USE_BACKLIGHT
hal_delay(1500); hal_delay(1500);
@ -613,7 +610,7 @@ int bootloader_main(void) {
} }
break; break;
case SCREEN_WAIT_FOR_HOST: case SCREEN_WAIT_FOR_HOST:
screen_connect(); screen_connect(auto_upgrade == sectrue);
switch (bootloader_usb_loop(&vhdr, hdr)) { switch (bootloader_usb_loop(&vhdr, hdr)) {
case CONTINUE_TO_FIRMWARE: case CONTINUE_TO_FIRMWARE:
continue_to_firmware = sectrue; continue_to_firmware = sectrue;

View File

@ -3,9 +3,10 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K 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 */ 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_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM); sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */ /* reserve 256 bytes for bootloader arguments */
firmware_header_start = ccmram_end - 0x400; 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); _codelen = SIZEOF(.flash) + SIZEOF(.data);

View File

@ -24,6 +24,7 @@
#include <pb_encode.h> #include <pb_encode.h>
#include "messages.pb.h" #include "messages.pb.h"
#include "boot_internal.h"
#include "common.h" #include "common.h"
#include "flash.h" #include "flash.h"
#include "image.h" #include "image.h"
@ -426,8 +427,6 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
return true; return true;
} }
secbool check_vendor_header_keys(const vendor_header *const vhdr);
static int version_compare(uint32_t vera, uint32_t verb) { static int version_compare(uint32_t vera, uint32_t verb) {
int a, b; int a, b;
a = vera & 0xFF; a = vera & 0xFF;
@ -448,11 +447,12 @@ static void detect_installation(const vendor_header *current_vhdr,
const image_header *current_hdr, const image_header *current_hdr,
const vendor_header *const new_vhdr, const vendor_header *const new_vhdr,
const image_header *const new_hdr, const image_header *const new_hdr,
secbool *is_new, secbool *is_upgrade, secbool *is_new, secbool *keep_seed,
secbool *is_newvendor) { secbool *is_newvendor, secbool *is_upgrade) {
*is_new = secfalse; *is_new = secfalse;
*is_upgrade = secfalse; *keep_seed = secfalse;
*is_newvendor = secfalse; *is_newvendor = secfalse;
*is_upgrade = secfalse;
if (sectrue != check_vendor_header_keys(current_vhdr)) { if (sectrue != check_vendor_header_keys(current_vhdr)) {
*is_new = sectrue; *is_new = sectrue;
return; 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) { if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) {
return; 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; 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 should_keep_seed = secfalse;
secbool is_newvendor = secfalse; secbool is_newvendor = secfalse;
secbool is_upgrade = secfalse;
if (is_new == secfalse) { if (is_new == secfalse) {
detect_installation(&current_vhdr, current_hdr, &vhdr, &hdr, &is_new, detect_installation(&current_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 #ifdef USE_OPTIGA
@ -593,8 +639,8 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
#endif #endif
uint32_t response = INPUT_CANCEL; uint32_t response = INPUT_CANCEL;
if (sectrue == is_new) { if (sectrue == is_new || sectrue == is_ilu) {
// new installation - auto confirm // new installation or interaction less updated - auto confirm
response = INPUT_CONFIRM; response = INPUT_CONFIRM;
} else { } else {
int version_cmp = version_compare(hdr.version, current_hdr->version); int version_cmp = version_compare(hdr.version, current_hdr->version);

View File

@ -42,6 +42,9 @@ enum {
UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, UPLOAD_ERR_FIRMWARE_TOO_BIG = -8,
UPLOAD_ERR_INVALID_CHUNK_HASH = -9, UPLOAD_ERR_INVALID_CHUNK_HASH = -9,
UPLOAD_ERR_BOOTLOADER_LOCKED = -10, UPLOAD_ERR_BOOTLOADER_LOCKED = -10,
UPLOAD_ERR_FIRMWARE_MISMATCH = -11,
UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12,
UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13,
}; };
enum { enum {

View File

@ -7,7 +7,7 @@
reset_handler: reset_handler:
// setup environment for subsequent stage of code // setup environment for subsequent stage of code
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM 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 ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg bl memset_reg
@ -33,9 +33,22 @@ reset_handler:
// subsequent operations, it is not necessary to insert a memory barrier instruction." // subsequent operations, it is not necessary to insert a memory barrier instruction."
cpsie f 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 // enter the application code
bl main bl main
b shutdown_privileged b shutdown_privileged
.bss
.global g_boot_command
g_boot_command:
.word 0
.end .end

View File

@ -40,18 +40,6 @@
#include "model.h" #include "model.h"
// #include "mpu.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 #define USB_IFACE_NUM 0
static void usb_init_all(secbool usb21_landing) { 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) { static secbool check_vendor_header_lock(const vendor_header *const vhdr) {
uint8_t lock[FLASH_OTP_BLOCK_SIZE]; uint8_t lock[FLASH_OTP_BLOCK_SIZE];
ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock, ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock,

View File

@ -3,9 +3,10 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K 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 */ 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_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM); sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */ /* reserve 256 bytes for bootloader arguments */
firmware_header_start = ccmram_end - 0x400; boot_args_start = ORIGIN(BOOT_ARGS);
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
_codelen = SIZEOF(.flash) + SIZEOF(.data); _codelen = SIZEOF(.flash) + SIZEOF(.data);

View File

@ -402,8 +402,6 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
return true; return true;
} }
secbool check_vendor_header_keys(const vendor_header *const vhdr);
static int version_compare(uint32_t vera, uint32_t verb) { static int version_compare(uint32_t vera, uint32_t verb) {
int a, b; int a, b;
a = vera & 0xFF; a = vera & 0xFF;

View File

@ -7,7 +7,7 @@
reset_handler: reset_handler:
// setup environment for subsequent stage of code // setup environment for subsequent stage of code
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM 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 ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg bl memset_reg

View File

@ -23,6 +23,7 @@
#include "supervise.h" #include "supervise.h"
#endif #endif
#include "image.h"
#include "version.h" #include "version.h"
#if MICROPY_PY_TREZORUTILS #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, STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_unit_btconly_obj,
mod_trezorutils_unit_btconly); 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. /// 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 #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 #endif
return mp_const_none; 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: /// 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_PTR(&mod_trezorutils_firmware_vendor_obj)},
{MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader), {MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader),
MP_ROM_PTR(&mod_trezorutils_reboot_to_bootloader_obj)}, 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_QSTR(MP_QSTR_bootloader_locked),
MP_ROM_PTR(&mod_trezorutils_bootloader_locked_obj)}, MP_ROM_PTR(&mod_trezorutils_bootloader_locked_obj)},
{MP_ROM_QSTR(MP_QSTR_unit_color), {MP_ROM_QSTR(MP_QSTR_unit_color),

View File

@ -244,83 +244,6 @@ void BusFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(BF)"); }
void UsageFault_Handler(void) { error_shutdown("INTERNAL ERROR", "(UF)"); } 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 // MicroPython builtin stubs
mp_import_stat_t mp_import_stat(const char *path) { mp_import_stat_t mp_import_stat(const char *path) {

View File

@ -3,10 +3,11 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
FLASH2 (r) : ORIGIN = 0x08120000, LENGTH = 896K FLASH2 (r) : ORIGIN = 0x08120000, LENGTH = 896K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K 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 */ 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_start = ORIGIN(CCMRAM);
ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
/* IMAGE_HEADER_SIZE is 0x400, this is for interaction-less firmware update start */ /* reserve 256 bytes for bootloader arguments */
firmware_header_start = ccmram_end - 0x400; boot_args_start = ORIGIN(BOOT_ARGS);
boot_args_end = ORIGIN(BOOT_ARGS) + LENGTH(BOOT_ARGS);
/* used by the startup code to wipe memory */ /* used by the startup code to wipe memory */
sram_start = ORIGIN(SRAM); sram_start = ORIGIN(SRAM);

View File

@ -28,6 +28,11 @@ reset_handler:
ldr r2, =0 // r2 - the word-sized value to be written ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg 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 r0, =sram_start // r0 - point to beginning of SRAM
ldr r1, =sram_end // r1 - point to byte after the end 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 ldr r2, =0 // r2 - the word-sized value to be written

View File

@ -25,6 +25,20 @@
#include "common.h" #include "common.h"
#include "flash.h" #include "flash.h"
#include "image.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, static secbool compute_pubkey(uint8_t sig_m, uint8_t sig_n,
const uint8_t *const *pub, uint8_t sigmask, 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)); *(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) { void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash) {
BLAKE2S_CTX ctx; BLAKE2S_CTX ctx;
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH); blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
@ -257,3 +276,48 @@ secbool check_image_contents(const image_header *const hdr, uint32_t firstskip,
return sectrue; 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;
}

View File

@ -21,6 +21,7 @@
#define __TREZORHAL_IMAGE_H__ #define __TREZORHAL_IMAGE_H__
#include <stdint.h> #include <stdint.h>
#include "blake2s.h"
#include "flash.h" #include "flash.h"
#include "model.h" #include "model.h"
#include "secbool.h" #include "secbool.h"
@ -78,6 +79,21 @@ typedef struct {
const uint8_t *origin; // pointer to the underlying data const uint8_t *origin; // pointer to the underlying data
} vendor_header; } 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 image_header *read_image_header(const uint8_t *const data,
const uint32_t magic, const uint32_t magic,
const uint32_t maxsize); 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, uint8_t key_m, uint8_t key_n,
const uint8_t *const *keys); 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); void vendor_header_hash(const vendor_header *const vhdr, uint8_t *hash);
secbool __wur check_single_hash(const uint8_t *const 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); 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 #endif

View File

@ -667,28 +667,4 @@ int main(void) {
return 0; 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)"); } void HardFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(HF)"); }

View File

@ -3,9 +3,10 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K 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 */ 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_start = ORIGIN(CCMRAM);
ccmram_end = ORIGIN(CCMRAM) + LENGTH(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 */ /* used by the startup code to wipe memory */
sram_start = ORIGIN(SRAM); sram_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM); sram_end = ORIGIN(SRAM) + LENGTH(SRAM);

View File

@ -11,6 +11,11 @@ reset_handler:
ldr r2, =0 // r2 - the word-sized value to be written ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg 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 r0, =sram_start // r0 - point to beginning of SRAM
ldr r1, =sram_end // r1 - point to byte after the end 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 ldr r2, =0 // r2 - the word-sized value to be written

View File

@ -3,9 +3,10 @@
ENTRY(reset_handler) ENTRY(reset_handler)
MEMORY { MEMORY {
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 768K
CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K CCMRAM (wal) : ORIGIN = 0x10000000, LENGTH = 64K - 0x100
SRAM (wal) : ORIGIN = 0x20000000, LENGTH = 192K 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 */ 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_start = ORIGIN(CCMRAM);
ccmram_end = ORIGIN(CCMRAM) + LENGTH(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 */ /* used by the startup code to wipe memory */
sram_start = ORIGIN(SRAM); sram_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM); sram_end = ORIGIN(SRAM) + LENGTH(SRAM);

View File

@ -11,6 +11,11 @@ reset_handler:
ldr r2, =0 // r2 - the word-sized value to be written ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg 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 r0, =sram_start // r0 - point to beginning of SRAM
ldr r1, =sram_end // r1 - point to byte after the end 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 ldr r2, =0 // r2 - the word-sized value to be written

View File

@ -46,6 +46,7 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_emphasized; MP_QSTR_confirm_emphasized;
MP_QSTR_confirm_ethereum_tx; MP_QSTR_confirm_ethereum_tx;
MP_QSTR_confirm_fido; MP_QSTR_confirm_fido;
MP_QSTR_confirm_firmware_update;
MP_QSTR_confirm_homescreen; MP_QSTR_confirm_homescreen;
MP_QSTR_confirm_joint_total; MP_QSTR_confirm_joint_total;
MP_QSTR_confirm_modify_fee; MP_QSTR_confirm_modify_fee;
@ -73,6 +74,7 @@ static void _librust_qstrs(void) {
MP_QSTR_fee_amount; MP_QSTR_fee_amount;
MP_QSTR_fee_label; MP_QSTR_fee_label;
MP_QSTR_fee_rate_amount; MP_QSTR_fee_rate_amount;
MP_QSTR_fingerprint;
MP_QSTR_hold; MP_QSTR_hold;
MP_QSTR_hold_danger; MP_QSTR_hold_danger;
MP_QSTR_horizontal; MP_QSTR_horizontal;

View File

@ -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, uint8_t vendor_str_len, const char* version_str,
bool fw_ok); bool fw_ok);
uint32_t screen_menu(secbool firmware_present); 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, void screen_fatal_error_rust(const char* title, const char* msg,
const char* footer); const char* footer);
void screen_wipe_success(void); void screen_wipe_success(void);

View File

@ -5,7 +5,7 @@ use crate::ui::{
pub struct Pad { pub struct Pad {
pub area: Rect, pub area: Rect,
color: Color, pub color: Color,
clear: bool, clear: bool,
} }

View File

@ -4,7 +4,7 @@ use crate::ui::{
geometry::{Offset, Rect}, geometry::{Offset, Rect},
}; };
use super::theme::{BLD_BG, BLD_FG}; use super::super::theme::bootloader::{BLD_BG, BLD_FG};
pub struct Connect { pub struct Connect {
bg: Pad, bg: Pad,

View File

@ -6,9 +6,11 @@ use crate::ui::{
use super::{ use super::{
super::{ super::{
component::{ButtonController, ButtonControllerMsg::Triggered, ButtonLayout, ButtonPos}, 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, ReturnToC,
}; };

View File

@ -12,8 +12,10 @@ use crate::{
}; };
use super::{ use super::{
super::component::{Choice, ChoiceFactory, ChoicePage}, super::{
theme::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH}, component::{Choice, ChoiceFactory, ChoicePage},
theme::bootloader::{BLD_BG, BLD_FG, ICON_EXIT, ICON_REDO, ICON_TRASH},
},
ReturnToC, ReturnToC,
}; };

View File

@ -14,11 +14,9 @@ use heapless::String;
use super::component::{ResultScreen, WelcomeScreen}; use super::component::{ResultScreen, WelcomeScreen};
mod confirm;
mod connect; mod connect;
mod intro; mod intro;
mod menu; mod menu;
mod theme;
mod welcome; mod welcome;
use crate::{ use crate::{
@ -27,14 +25,18 @@ use crate::{
constant, constant,
constant::HEIGHT, constant::HEIGHT,
geometry::Point, 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 connect::Connect;
use intro::Intro; use intro::Intro;
use menu::Menu; use menu::Menu;
use theme::{BLD_BG, BLD_FG, ICON_ALERT, ICON_SPINNER, ICON_SUCCESS};
use welcome::Welcome; use welcome::Welcome;
pub type BootloaderString = String<128>; 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> { fn button_eval() -> Option<ButtonEvent> {
let event = io_button_read(); let event = io_button_read();
if event == 0 { if event == 0 {
@ -145,18 +153,15 @@ extern "C" fn screen_install_confirm(
"DOWNGRADE FW" "DOWNGRADE FW"
}; };
let message = let message = Label::left_aligned(version_str.as_str(), TEXT_NORMAL).vertically_centered();
Label::left_aligned(version_str.as_str(), theme::TEXT_NORMAL).vertically_centered();
let fingerprint = Label::left_aligned( let fingerprint = Label::left_aligned(
fingerprint_str, fingerprint_str,
theme::TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen), TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
) )
.vertically_centered(); .vertically_centered();
let alert = (!should_keep_seed).then_some(Label::left_aligned( let alert =
"Seed will be erased!", (!should_keep_seed).then_some(Label::left_aligned("Seed will be erased!", TEXT_NORMAL));
theme::TEXT_NORMAL,
));
let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false) let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false)
.with_info_screen("FW FINGERPRINT", fingerprint); .with_info_screen("FW FINGERPRINT", fingerprint);
@ -165,8 +170,8 @@ extern "C" fn screen_install_confirm(
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_confirm() -> u32 { extern "C" fn screen_wipe_confirm() -> u32 {
let message = Label::left_aligned("Seed and firmware will be erased!", theme::TEXT_NORMAL) let message =
.vertically_centered(); 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); 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] #[no_mangle]
extern "C" fn screen_unlock_bootloader_confirm() -> u32 { extern "C" fn screen_unlock_bootloader_confirm() -> u32 {
let message = Label::left_aligned("This action cannot be undone!", theme::TEXT_NORMAL) let message =
.vertically_centered(); 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); 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] #[no_mangle]
extern "C" fn screen_unlock_bootloader_success() { 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 = 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); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
show(&mut frame); show(&mut frame);
@ -295,17 +300,17 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_connect() { extern "C" fn screen_connect(_initial_setup: bool) {
let mut frame = Connect::new("Waiting for host..."); let mut frame = Connect::new("Waiting for host...");
show(&mut frame); show(&mut frame);
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_success() { 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 = 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); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
show(&mut frame); show(&mut frame);
@ -313,10 +318,10 @@ extern "C" fn screen_wipe_success() {
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_fail() { 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 = 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); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
show(&mut frame); show(&mut frame);
@ -332,10 +337,10 @@ extern "C" fn screen_boot_empty(_fading: bool) {
#[no_mangle] #[no_mangle]
extern "C" fn screen_install_fail() { 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 = 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); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
show(&mut frame); show(&mut frame);
@ -358,9 +363,9 @@ extern "C" fn screen_install_success(
unwrap!(reboot_msg.push_str("Reconnect the device")); 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); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw);
show(&mut frame); show(&mut frame);

View File

@ -4,7 +4,7 @@ use crate::ui::{
geometry::{Offset, Rect}, geometry::{Offset, Rect},
}; };
use super::theme::{BLD_BG, BLD_FG}; use super::super::theme::bootloader::{BLD_BG, BLD_FG};
pub struct Welcome { pub struct Welcome {
bg: Pad, bg: Pad,

View File

@ -1,16 +1,15 @@
use crate::ui::{ use crate::{
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad}, strutil::StringType,
display::{self, Color, Font}, ui::{
geometry::{Point, Rect}, component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
display::{self, Color, Font},
geometry::{Point, Rect},
},
}; };
use super::{ use super::{
super::{ theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT, WHITE},
component::{ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos}, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos,
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT},
},
theme::WHITE,
ReturnToC,
}; };
const ALERT_AREA_START: i16 = 39; const ALERT_AREA_START: i16 = 39;
@ -21,38 +20,36 @@ pub enum ConfirmMsg {
Confirm = 2, Confirm = 2,
} }
impl ReturnToC for ConfirmMsg { pub struct Confirm<T: StringType, U> {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Confirm<'a> {
bg: Pad, bg: Pad,
bg_color: Color, bg_color: Color,
title: &'static str, title: &'static str,
message: Child<Label<&'a str>>, message: Child<Label<U>>,
alert: Option<Label<&'a str>>, alert: Option<Label<T>>,
info_title: Option<&'static str>, info_title: Option<T>,
info_text: Option<Label<&'a str>>, info_text: Option<Label<U>>,
button_text: &'static str, button_text: T,
buttons: ButtonController<&'static str>, buttons: ButtonController<T>,
/// Whether we are on the info screen (optional extra screen) /// Whether we are on the info screen (optional extra screen)
showing_info_screen: bool, showing_info_screen: bool,
two_btn_confirm: 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( pub fn new(
bg_color: Color, bg_color: Color,
title: &'static str, title: &'static str,
message: Label<&'a str>, message: Label<U>,
alert: Option<Label<&'a str>>, alert: Option<Label<T>>,
button_text: &'static str, button_text: T,
two_btn_confirm: bool, two_btn_confirm: bool,
) -> Self { ) -> Self {
let btn_layout = 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 { Self {
bg: Pad::with_background(bg_color).with_clear(), bg: Pad::with_background(bg_color).with_clear(),
bg_color, bg_color,
@ -69,7 +66,7 @@ impl<'a> Confirm<'a> {
} }
/// Adding optional info screen /// 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_title = Some(info_title);
self.info_text = Some(info_text); self.info_text = Some(info_text);
self.buttons = ButtonController::new(self.get_button_layout()); self.buttons = ButtonController::new(self.get_button_layout());
@ -80,10 +77,10 @@ impl<'a> Confirm<'a> {
self.info_title.is_some() 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::get_button_layout_general(
self.showing_info_screen, self.showing_info_screen,
self.button_text, self.button_text.clone(),
self.has_info_screen(), self.has_info_screen(),
self.two_btn_confirm, self.two_btn_confirm,
) )
@ -92,10 +89,10 @@ impl<'a> Confirm<'a> {
/// Not relying on self here, to call it in constructor. /// Not relying on self here, to call it in constructor.
fn get_button_layout_general( fn get_button_layout_general(
showing_info_screen: bool, showing_info_screen: bool,
button_text: &'static str, button_text: T,
has_info_screen: bool, has_info_screen: bool,
two_btn_confirm: bool, two_btn_confirm: bool,
) -> ButtonLayout<&'static str> { ) -> ButtonLayout<T> {
if showing_info_screen { if showing_info_screen {
ButtonLayout::arrow_none_none() ButtonLayout::arrow_none_none()
} else if has_info_screen { } 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; type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { 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 // We are either on the info screen or on the "main" screen
if self.showing_info_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(); self.info_text.paint();
} else { } else {
display_top_left(self.title); display_top_left(self.title);
@ -224,3 +225,14 @@ impl<'a> Component for Confirm<'a> {
self.buttons.bounds(sink); 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");
}
}

View File

@ -1,3 +1,4 @@
pub mod bl_confirm;
mod button; mod button;
mod button_controller; mod button_controller;
mod common; mod common;

View File

@ -30,7 +30,7 @@ use crate::{
}, },
TextStyle, TextStyle,
}, },
ComponentExt, FormattedText, Timeout, ComponentExt, FormattedText, Label, LineBreaking, Timeout,
}, },
display, geometry, display, geometry,
layout::{ 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 /// Function to create and call a `ButtonPage` dialog based on paginable content
/// (e.g. `Paragraphs` or `FormattedText`). /// (e.g. `Paragraphs` or `FormattedText`).
/// Has optional title (supply empty `StrBuffer` for that) and hold-to-confirm /// 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() 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] #[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! { pub static mp_module_trezorui2: Module = obj_module! {
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(), 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: /// def draw_welcome_screen() -> None:
/// """Show logo icon with the model name at the bottom and return.""" /// """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(), 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(),
}; };

View File

@ -9,6 +9,8 @@ use crate::ui::{
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
pub mod bootloader;
// Color palette. // Color palette.
pub const WHITE: Color = Color::white(); pub const WHITE: Color = Color::white();
pub const BLACK: Color = Color::black(); pub const BLACK: Color = Color::black();

View File

@ -1,9 +1,9 @@
use crate::ui::{ use crate::ui::{
component::{Component, Event, EventCtx, Never, Pad}, component::{Component, Event, EventCtx, Never, Pad},
constant::screen, constant::screen,
display::{self, Font}, display::{self, Color, Font},
geometry::{Offset, Rect}, geometry::{Offset, Rect},
model_tt::bootloader::theme::{BLD_BG, BLD_TITLE_COLOR}, model_tt::theme::bootloader::BLD_TITLE_COLOR,
}; };
pub struct Connect { pub struct Connect {
@ -12,9 +12,9 @@ pub struct Connect {
} }
impl Connect { impl Connect {
pub fn new(message: &'static str) -> Self { pub fn new(message: &'static str, bg: Color) -> Self {
let mut instance = Self { let mut instance = Self {
bg: Pad::with_background(BLD_BG), bg: Pad::with_background(bg),
message, message,
}; };
@ -44,7 +44,7 @@ impl Component for Connect {
self.message, self.message,
Font::NORMAL, Font::NORMAL,
BLD_TITLE_COLOR, BLD_TITLE_COLOR,
BLD_BG, self.bg.color,
); );
} }
} }

View File

@ -4,12 +4,12 @@ use crate::ui::{
display::Icon, display::Icon,
geometry::{Alignment, Insets, Point, Rect}, geometry::{Alignment, Insets, Point, Rect},
model_tt::{ 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}, component::{Button, ButtonMsg::Clicked},
constant::WIDTH, 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 { pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self {
Self { Self {
bg: Pad::with_background(BLD_BG).with_clear(), 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( menu: Child::new(
Button::with_icon(Icon::new(MENU32)) Button::with_icon(Icon::new(MENU32))
.styled(button_bld_menu()) .styled(button_bld_menu())

View File

@ -6,12 +6,12 @@ use crate::{
display::Icon, display::Icon,
geometry::{Insets, Point, Rect}, geometry::{Insets, Point, Rect},
model_tt::{ 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}, 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 { let mut instance = Self {
bg: Pad::with_background(BLD_BG), 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( close: Child::new(
Button::with_icon(Icon::new(X32)) Button::with_icon(Icon::new(X32))
.styled(button_bld_menu()) .styled(button_bld_menu())

View File

@ -8,19 +8,22 @@ use crate::{
event::TouchEvent, event::TouchEvent,
geometry::Point, geometry::Point,
model_tt::{ model_tt::{
bootloader::{ bootloader::{connect::Connect, welcome::Welcome},
confirm::ConfirmTitle, component::{
connect::Connect, bl_confirm::{Confirm, ConfirmTitle},
theme::{ Button, ResultScreen, WelcomeScreen,
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,
}, },
component::{Button, ResultScreen, WelcomeScreen},
constant, 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}, util::{from_c_array, from_c_str},
}, },
@ -28,20 +31,15 @@ use crate::{
use heapless::String; use heapless::String;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
pub mod confirm;
mod connect; mod connect;
pub mod intro; pub mod intro;
pub mod menu; pub mod menu;
pub mod theme;
pub mod welcome; pub mod welcome;
use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK}; use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK};
use confirm::Confirm;
use intro::Intro; use intro::Intro;
use menu::Menu; use menu::Menu;
use self::theme::{RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE};
pub type BootloaderString = String<128>; pub type BootloaderString = String<128>;
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE"; const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
@ -163,12 +161,10 @@ extern "C" fn screen_install_confirm(
} else { } else {
"DOWNGRADE FW" "DOWNGRADE FW"
}; };
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered(); let title = Label::left_aligned(title_str, TEXT_BOLD).vertically_centered();
let msg = Label::left_aligned(version_str.as_ref(), theme::TEXT_NORMAL); let msg = Label::left_aligned(version_str.as_ref(), TEXT_NORMAL);
let alert = (!should_keep_seed).then_some(Label::left_aligned( let alert =
"SEED WILL BE ERASED!", (!should_keep_seed).then_some(Label::left_aligned("SEED WILL BE ERASED!", TEXT_BOLD));
theme::TEXT_BOLD,
));
let (left, right) = if should_keep_seed { let (left, right) = if should_keep_seed {
let l = Button::with_text("CANCEL").styled(button_bld()); let l = Button::with_text("CANCEL").styled(button_bld());
@ -184,6 +180,7 @@ extern "C" fn screen_install_confirm(
BLD_BG, BLD_BG,
left, left,
right, right,
button_bld_menu(),
ConfirmTitle::Text(title), ConfirmTitle::Text(title),
msg, msg,
alert, alert,
@ -210,6 +207,7 @@ extern "C" fn screen_wipe_confirm() -> u32 {
BLD_WIPE_COLOR, BLD_WIPE_COLOR,
left, left,
right, right,
button_bld_menu(),
ConfirmTitle::Icon(icon), ConfirmTitle::Icon(icon),
msg, msg,
Some(alert), Some(alert),
@ -298,15 +296,16 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
"Resetting Trezor", "Resetting Trezor",
progress, progress,
initialize, initialize,
theme::BLD_FG, BLD_FG,
BLD_WIPE_COLOR, BLD_WIPE_COLOR,
Some((Icon::new(FIRE32), theme::BLD_FG)), Some((Icon::new(FIRE32), BLD_FG)),
) )
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_connect() { extern "C" fn screen_connect(initial_setup: bool) {
let mut frame = Connect::new("Waiting for host..."); let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
let mut frame = Connect::new("Waiting for host...", bg);
show(&mut frame, true); show(&mut frame, true);
} }

View File

@ -3,9 +3,9 @@ use crate::ui::{
constant::screen, constant::screen,
display::{self, Font, Icon}, display::{self, Font, Icon},
geometry::{Alignment2D, Offset, Rect}, geometry::{Alignment2D, Offset, Rect},
model_tt::{ model_tt::theme::{
bootloader::theme::{START_URL, WELCOME_COLOR}, bootloader::{START_URL, WELCOME_COLOR},
theme::{BLACK, GREY_MEDIUM, WHITE}, BLACK, GREY_MEDIUM, WHITE,
}, },
}; };

View File

@ -5,13 +5,15 @@ use crate::ui::{
display::{Color, Icon}, display::{Color, Icon},
geometry::{Alignment2D, Insets, Offset, Point, Rect}, geometry::{Alignment2D, Insets, Offset, Point, Rect},
model_tt::{ model_tt::{
bootloader::theme::{ component::{Button, ButtonMsg::Clicked, ButtonStyleSheet},
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},
constant::WIDTH, 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, Confirm = 2,
} }
pub enum ConfirmTitle<'a> { pub enum ConfirmTitle<T> {
Text(Label<&'a str>), Text(Label<T>),
Icon(Icon), Icon(Icon),
} }
pub struct ConfirmInfo<'a> { pub struct ConfirmInfo<T> {
pub title: Child<Label<&'a str>>, pub title: Child<Label<T>>,
pub text: Child<Label<&'a str>>, pub text: Child<Label<T>>,
pub info_button: Child<Button<&'static str>>, pub info_button: Child<Button<&'static str>>,
pub close_button: Child<Button<&'static str>>, pub close_button: Child<Button<&'static str>>,
} }
pub struct Confirm<'a> { pub struct Confirm<T> {
bg: Pad, bg: Pad,
content_pad: Pad, content_pad: Pad,
bg_color: Color, bg_color: Color,
title: ConfirmTitle<'a>, title: ConfirmTitle<T>,
message: Child<Label<&'a str>>, message: Child<Label<T>>,
alert: Option<Child<Label<&'a str>>>, alert: Option<Child<Label<T>>>,
left_button: Child<Button<&'static str>>, left_button: Child<Button<&'static str>>,
right_button: Child<Button<&'static str>>, right_button: Child<Button<&'static str>>,
info: Option<ConfirmInfo<'a>>, info: Option<ConfirmInfo<T>>,
show_info: bool, show_info: bool,
} }
impl<'a> Confirm<'a> { impl<T> Confirm<T>
where
T: AsRef<str>,
{
pub fn new( pub fn new(
bg_color: Color, bg_color: Color,
left_button: Button<&'static str>, left_button: Button<&'static str>,
right_button: Button<&'static str>, right_button: Button<&'static str>,
title: ConfirmTitle<'a>, menu_button: ButtonStyleSheet,
message: Label<&'a str>, title: ConfirmTitle<T>,
alert: Option<Label<&'a str>>, message: Label<T>,
info: Option<(&'a str, &'a str)>, alert: Option<Label<T>>,
info: Option<(T, T)>,
) -> Self { ) -> Self {
Self { Self {
bg: Pad::with_background(bg_color).with_clear(), bg: Pad::with_background(bg_color).with_clear(),
@ -74,16 +80,20 @@ impl<'a> Confirm<'a> {
left_button: Child::new(left_button), left_button: Child::new(left_button),
right_button: Child::new(right_button), right_button: Child::new(right_button),
info: info.map(|(title, text)| ConfirmInfo { info: info.map(|(title, text)| ConfirmInfo {
title: Child::new(Label::left_aligned(title, TEXT_TITLE).vertically_centered()), title: Child::new(
text: Child::new(Label::left_aligned(text, TEXT_FINGERPRINT).vertically_centered()), 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( info_button: Child::new(
Button::with_icon(Icon::new(INFO32)) Button::with_icon(Icon::new(INFO32))
.styled(button_bld_menu()) .styled(menu_button)
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)), .with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
), ),
close_button: Child::new( close_button: Child::new(
Button::with_icon(Icon::new(X32)) Button::with_icon(Icon::new(X32))
.styled(button_bld_menu()) .styled(menu_button)
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)), .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; type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -220,3 +233,13 @@ impl<'a> Component for Confirm<'a> {
self.right_button.bounds(sink); 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");
}
}

View File

@ -357,7 +357,7 @@ pub enum ButtonContent<T> {
IconBlend(Icon, Icon, Offset), IconBlend(Icon, Icon, Offset),
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Clone, Copy)]
pub struct ButtonStyleSheet { pub struct ButtonStyleSheet {
pub normal: &'static ButtonStyle, pub normal: &'static ButtonStyle,
pub active: &'static ButtonStyle, pub active: &'static ButtonStyle,

View File

@ -15,7 +15,7 @@ const TITLE_AREA_START: i16 = 70;
const MESSAGE_AREA_START: i16 = 116; const MESSAGE_AREA_START: i16 = 116;
#[cfg(feature = "bootloader")] #[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"))] #[cfg(not(feature = "bootloader"))]
const STYLE: &ResultStyle = &super::theme::RESULT_ERROR; const STYLE: &ResultStyle = &super::theme::RESULT_ERROR;

View File

@ -1,4 +1,5 @@
mod address_details; mod address_details;
pub mod bl_confirm;
mod button; mod button;
mod coinjoin_progress; mod coinjoin_progress;
mod dialog; mod dialog;

View File

@ -4,7 +4,7 @@ use crate::ui::{
model_tt::theme, model_tt::theme,
}; };
#[cfg(feature = "bootloader")] #[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 TEXT_BOTTOM_MARGIN: i16 = 24; // matching the homescreen label margin
const ICON_TOP_MARGIN: i16 = 48; const ICON_TOP_MARGIN: i16 = 48;

View File

@ -31,7 +31,7 @@ use crate::{
}, },
TextStyle, TextStyle,
}, },
Border, Component, Empty, FormattedText, Never, Qr, Timeout, Border, Component, Empty, FormattedText, Label, Never, Qr, Timeout,
}, },
display::{self, tjpgd::jpeg_info}, display::{self, tjpgd::jpeg_info},
geometry, 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 { extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; 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() 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] #[no_mangle]
pub static mp_module_trezorui2: Module = obj_module! { pub static mp_module_trezorui2: Module = obj_module! {
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(), 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: /// def draw_welcome_screen() -> None:
/// """Show logo icon with the model name at the bottom and return.""" /// """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(), 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)] #[cfg(test)]

View File

@ -231,13 +231,15 @@ pub fn button_bld() -> ButtonStyleSheet {
} }
} }
pub const TEXT_TITLE: TextStyle = TextStyle::new( pub const fn text_title(bg: Color) -> TextStyle {
Font::BOLD, TextStyle::new(
BLD_TITLE_COLOR, Font::BOLD,
BLD_BG, BLD_TITLE_COLOR,
BLD_TITLE_COLOR, bg,
BLD_TITLE_COLOR, 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_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
pub const TEXT_WARNING: TextStyle = TextStyle::new( pub const TEXT_WARNING: TextStyle = TextStyle::new(
@ -247,9 +249,9 @@ pub const TEXT_WARNING: TextStyle = TextStyle::new(
BLD_WARN_COLOR, BLD_WARN_COLOR,
BLD_WARN_COLOR, BLD_WARN_COLOR,
); );
pub const TEXT_FINGERPRINT: TextStyle = pub const fn text_fingerprint(bg: Color) -> TextStyle {
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG) TextStyle::new(Font::NORMAL, BLD_FG, bg, BLD_FG, BLD_FG).with_line_breaking(BreakWordsNoHyphen)
.with_line_breaking(BreakWordsNoHyphen); }
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, BLD_FG, BLD_BG, BLD_FG, BLD_FG); 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( pub const TEXT_WIPE_BOLD: TextStyle = TextStyle::new(
Font::BOLD, Font::BOLD,

View File

@ -1,3 +1,5 @@
pub mod bootloader;
use crate::{ use crate::{
time::Duration, time::Duration,
ui::{ ui::{

View 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

View File

@ -51,12 +51,7 @@
}) })
#endif #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); void __attribute__((noreturn)) trezor_shutdown(void);

View 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

View File

@ -5,10 +5,10 @@
#define SVC_SET_PRIORITY 2 #define SVC_SET_PRIORITY 2
#define SVC_SHUTDOWN 4 #define SVC_SHUTDOWN 4
#define SVC_REBOOT_TO_BOOTLOADER 5 #define SVC_REBOOT_TO_BOOTLOADER 5
#define SVC_REBOOT_COPY_IMAGE_HEADER 6 #define SVC_GET_SYSTICK_VAL 6
#define SVC_GET_SYSTICK_VAL 7
#include <string.h> #include <string.h>
#include "boot_args.h"
#include "common.h" #include "common.h"
#include "image.h" #include "image.h"
@ -17,8 +17,6 @@ extern uint32_t systick_val_copy;
// from util.s // from util.s
extern void shutdown_privileged(void); 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); extern void ensure_compatible_settings(void);
static inline uint32_t is_mode_unprivileged(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) { void svc_reboot_to_bootloader(boot_command_t boot_command, const void* args,
explicit_bzero(&firmware_header_start, IMAGE_HEADER_SIZE); size_t args_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();
}
}
static inline uint32_t svc_get_systick_val(void) { static inline uint32_t svc_get_systick_val(void) {
if (is_mode_unprivileged() && !is_mode_handler()) { if (is_mode_unprivileged() && !is_mode_handler()) {

View File

@ -43,7 +43,7 @@ jump_to_with_flag:
// wipe memory at the end of the current stage of code // wipe memory at the end of the current stage of code
bl clear_otg_hs_memory bl clear_otg_hs_memory
ldr r0, =ccmram_start // r0 - point to beginning of CCMRAM 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 ldr r2, =0 // r2 - the word-sized value to be written
bl memset_reg bl memset_reg
ldr r0, =sram_start // r0 - point to beginning of SRAM ldr r0, =sram_start // r0 - point to beginning of SRAM

View File

@ -442,6 +442,15 @@ def show_lockscreen(
# rust/src/ui/model_tr/layout.rs # rust/src/ui/model_tr/layout.rs
def draw_welcome_screen() -> None: def draw_welcome_screen() -> None:
"""Show logo icon with the model name at the bottom and return.""" """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 CONFIRMED: object
CANCELLED: object CANCELLED: object
INFO: object INFO: object
@ -895,3 +904,12 @@ def show_lockscreen(
# rust/src/ui/model_tt/layout.rs # rust/src/ui/model_tt/layout.rs
def draw_welcome_screen() -> None: def draw_welcome_screen() -> None:
"""Show logo icon with the model name at the bottom and return.""" """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."""

View File

@ -75,12 +75,30 @@ def unit_btconly() -> bool | None:
# extmod/modtrezorutils/modtrezorutils.c # 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. 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 # extmod/modtrezorutils/modtrezorutils.c
def bootloader_locked() -> bool | None: def bootloader_locked() -> bool | None:
""" """

View File

@ -43,6 +43,7 @@ def stm32f4_common_files(env, defines, sources, paths):
"embed/trezorhal/stm32f4/mpu.c", "embed/trezorhal/stm32f4/mpu.c",
"embed/trezorhal/stm32f4/platform.c", "embed/trezorhal/stm32f4/platform.c",
"embed/trezorhal/stm32f4/systick.c", "embed/trezorhal/stm32f4/systick.c",
"embed/trezorhal/stm32f4/supervise.c",
"embed/trezorhal/stm32f4/random_delays.c", "embed/trezorhal/stm32f4/random_delays.c",
"embed/trezorhal/stm32f4/rng.c", "embed/trezorhal/stm32f4/rng.c",
"embed/trezorhal/stm32f4/vectortable.s", "embed/trezorhal/stm32f4/vectortable.s",

View File

@ -93,6 +93,8 @@ trezor.enums.AmountUnit
import trezor.enums.AmountUnit import trezor.enums.AmountUnit
trezor.enums.BackupType trezor.enums.BackupType
import trezor.enums.BackupType import trezor.enums.BackupType
trezor.enums.BootCommand
import trezor.enums.BootCommand
trezor.enums.ButtonRequestType trezor.enums.ButtonRequestType
import trezor.enums.ButtonRequestType import trezor.enums.ButtonRequestType
trezor.enums.Capability trezor.enums.Capability

View File

@ -7,20 +7,60 @@ if TYPE_CHECKING:
async def reboot_to_bootloader(msg: RebootToBootloader) -> NoReturn: 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.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 from trezor.wire.context import get_context
await confirm_action( if (
"reboot", msg.boot_command == BootCommand.INSTALL_UPGRADE
"Go to bootloader", and msg.firmware_header is not None
"Do you want to restart Trezor in bootloader mode?", ):
verb="Restart", # 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() ctx = get_context()
await ctx.write(Success(message="Rebooting")) await ctx.write(Success(message="Rebooting"))
# make sure the outgoing USB buffer is flushed # make sure the outgoing USB buffer is flushed
await loop.wait(ctx.iface.iface_num() | io.POLL_WRITE) 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 raise RuntimeError

View File

@ -0,0 +1,6 @@
# Automatically generated by pb2py
# fmt: off
# isort:skip_file
STOP_AND_WAIT = 0
INSTALL_UPGRADE = 1

View File

@ -450,6 +450,10 @@ if TYPE_CHECKING:
Matrix9 = 1 Matrix9 = 1
Matrix6 = 2 Matrix6 = 2
class BootCommand(IntEnum):
STOP_AND_WAIT = 0
INSTALL_UPGRADE = 1
class DebugSwipeDirection(IntEnum): class DebugSwipeDirection(IntEnum):
UP = 0 UP = 0
DOWN = 1 DOWN = 1

View File

@ -21,6 +21,7 @@ if TYPE_CHECKING:
from trezor.enums import BinanceOrderSide # noqa: F401 from trezor.enums import BinanceOrderSide # noqa: F401
from trezor.enums import BinanceOrderType # noqa: F401 from trezor.enums import BinanceOrderType # noqa: F401
from trezor.enums import BinanceTimeInForce # 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 ButtonRequestType # noqa: F401
from trezor.enums import Capability # noqa: F401 from trezor.enums import Capability # noqa: F401
from trezor.enums import CardanoAddressType # noqa: F401 from trezor.enums import CardanoAddressType # noqa: F401
@ -2617,6 +2618,16 @@ if TYPE_CHECKING:
return isinstance(msg, cls) return isinstance(msg, cls)
class RebootToBootloader(protobuf.MessageType): 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 @classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["RebootToBootloader"]: def is_type_of(cls, msg: Any) -> TypeGuard["RebootToBootloader"]:

View File

@ -1349,3 +1349,17 @@ async def confirm_set_new_pin(
"CONTINUE", "CONTINUE",
br_code, 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,
)
)

View File

@ -1368,3 +1368,17 @@ async def confirm_set_new_pin(
br_code, 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,
)
)

View File

@ -15,6 +15,7 @@ from trezorutils import ( # noqa: F401
VERSION_MINOR, VERSION_MINOR,
VERSION_PATCH, VERSION_PATCH,
bootloader_locked, bootloader_locked,
check_firmware_header,
consteq, consteq,
firmware_hash, firmware_hash,
firmware_vendor, firmware_vendor,

View File

@ -238,8 +238,16 @@ def unlock_path(client: "TrezorClient", n: "Address") -> "MessageType":
@session @session
@expect(messages.Success, field="message", ret_type=str) @expect(messages.Success, field="message", ret_type=str)
def reboot_to_bootloader(client: "TrezorClient") -> "MessageType": def reboot_to_bootloader(
return client.call(messages.RebootToBootloader()) 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 @session