mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-24 14:20:57 +00:00
fix(core/bootloader): fix erroneous shutdown when rejecting firmware upload
This commit is contained in:
parent
01a7de5b98
commit
a6d0842663
@ -206,7 +206,7 @@ env.Replace(
|
|||||||
'TREZOR_EMULATOR',
|
'TREZOR_EMULATOR',
|
||||||
CPU_MODEL,
|
CPU_MODEL,
|
||||||
'HW_MODEL=' + MODEL_AS_NUMBER,
|
'HW_MODEL=' + MODEL_AS_NUMBER,
|
||||||
'HW_REVISION=' + ('6' if TREZOR_MODEL in ('R',) else '0'),
|
'HW_REVISION=' + ('10' if TREZOR_MODEL in ('R',) else '0'),
|
||||||
'TREZOR_MODEL_'+TREZOR_MODEL,
|
'TREZOR_MODEL_'+TREZOR_MODEL,
|
||||||
'TREZOR_BOARD=\\"boards/board-unix.h\\"',
|
'TREZOR_BOARD=\\"boards/board-unix.h\\"',
|
||||||
'MCU_TYPE='+CPU_MODEL,
|
'MCU_TYPE='+CPU_MODEL,
|
||||||
|
1
core/embed/bootloader/.changelog.d/3122.changed
Normal file
1
core/embed/bootloader/.changelog.d/3122.changed
Normal file
@ -0,0 +1 @@
|
|||||||
|
No longer erases seed when firmware is corrupted but firmware header is correct and signed. Added firmware corrupted info to bootloader screen.
|
@ -192,16 +192,18 @@ void ui_screen_boot_click(void) {
|
|||||||
void ui_screen_welcome(void) { screen_welcome(); }
|
void ui_screen_welcome(void) { screen_welcome(); }
|
||||||
|
|
||||||
uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr) {
|
const image_header *const hdr, bool fw_ok) {
|
||||||
char bld_ver[32];
|
char bld_ver[32];
|
||||||
char ver_str[64];
|
char ver_str[64];
|
||||||
format_ver("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
|
format_ver("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
|
||||||
format_ver("%d.%d.%d", hdr->version, ver_str, sizeof(ver_str));
|
format_ver("%d.%d.%d", hdr->version, ver_str, sizeof(ver_str));
|
||||||
|
|
||||||
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str);
|
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str, fw_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ui_screen_menu(void) { return screen_menu(); }
|
uint32_t ui_screen_menu(secbool firmware_present) {
|
||||||
|
return screen_menu(firmware_present);
|
||||||
|
}
|
||||||
|
|
||||||
// install UI
|
// install UI
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ typedef enum {
|
|||||||
SCREEN_WIPE_CONFIRM = 2,
|
SCREEN_WIPE_CONFIRM = 2,
|
||||||
SCREEN_FINGER_PRINT = 3,
|
SCREEN_FINGER_PRINT = 3,
|
||||||
SCREEN_WAIT_FOR_HOST = 4,
|
SCREEN_WAIT_FOR_HOST = 4,
|
||||||
|
SCREEN_WELCOME = 5,
|
||||||
} screen_t;
|
} screen_t;
|
||||||
|
|
||||||
void ui_screen_boot(const vendor_header* const vhdr,
|
void ui_screen_boot(const vendor_header* const vhdr,
|
||||||
@ -42,9 +43,9 @@ void ui_click(void);
|
|||||||
void ui_screen_welcome(void);
|
void ui_screen_welcome(void);
|
||||||
|
|
||||||
uint32_t ui_screen_intro(const vendor_header* const vhdr,
|
uint32_t ui_screen_intro(const vendor_header* const vhdr,
|
||||||
const image_header* const hdr);
|
const image_header* const hdr, bool fw_ok);
|
||||||
|
|
||||||
uint32_t ui_screen_menu(void);
|
uint32_t ui_screen_menu(secbool firmware_present);
|
||||||
|
|
||||||
uint32_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
uint32_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
||||||
const image_header* const hdr,
|
const image_header* const hdr,
|
||||||
|
@ -81,9 +81,9 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
|
|||||||
#define USB_IFACE_NUM 0
|
#define USB_IFACE_NUM 0
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CONTINUE = 0,
|
SHUTDOWN = 0,
|
||||||
RETURN = 1,
|
CONTINUE_TO_FIRMWARE = 0xAABBCCDD,
|
||||||
SHUTDOWN = 2,
|
RETURN_TO_MENU = 0x55667788,
|
||||||
} usb_result_t;
|
} usb_result_t;
|
||||||
|
|
||||||
static void usb_init_all(secbool usb21_landing) {
|
static void usb_init_all(secbool usb21_landing) {
|
||||||
@ -164,7 +164,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
|||||||
hal_delay(100);
|
hal_delay(100);
|
||||||
usb_stop();
|
usb_stop();
|
||||||
usb_deinit();
|
usb_deinit();
|
||||||
return RETURN;
|
return RETURN_TO_MENU;
|
||||||
}
|
}
|
||||||
ui_screen_wipe();
|
ui_screen_wipe();
|
||||||
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
|
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
|
||||||
@ -200,7 +200,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
|||||||
hal_delay(100);
|
hal_delay(100);
|
||||||
usb_stop();
|
usb_stop();
|
||||||
usb_deinit();
|
usb_deinit();
|
||||||
return RETURN;
|
return RETURN_TO_MENU;
|
||||||
} else if (r == 0) { // last chunk received
|
} else if (r == 0) { // last chunk received
|
||||||
ui_screen_install_progress_upload(1000);
|
ui_screen_install_progress_upload(1000);
|
||||||
ui_screen_done(4, sectrue);
|
ui_screen_done(4, sectrue);
|
||||||
@ -213,7 +213,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
|||||||
usb_stop();
|
usb_stop();
|
||||||
usb_deinit();
|
usb_deinit();
|
||||||
ui_screen_boot_empty(true);
|
ui_screen_boot_empty(true);
|
||||||
return CONTINUE;
|
return CONTINUE_TO_FIRMWARE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MessageType_MessageType_GetFeatures:
|
case MessageType_MessageType_GetFeatures:
|
||||||
@ -227,7 +227,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
|||||||
hal_delay(100);
|
hal_delay(100);
|
||||||
usb_stop();
|
usb_stop();
|
||||||
usb_deinit();
|
usb_deinit();
|
||||||
return RETURN;
|
return RETURN_TO_MENU;
|
||||||
}
|
}
|
||||||
process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf);
|
process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf);
|
||||||
screen_unlock_bootloader_success();
|
screen_unlock_bootloader_success();
|
||||||
@ -315,39 +315,51 @@ int bootloader_main(void) {
|
|||||||
|
|
||||||
mpu_config_bootloader();
|
mpu_config_bootloader();
|
||||||
|
|
||||||
|
#ifdef TREZOR_EMULATOR
|
||||||
|
// wait a bit so that the empty lock icon is visible
|
||||||
|
// (on a real device, we are waiting for touch init which takes longer)
|
||||||
|
hal_delay(400);
|
||||||
|
#endif
|
||||||
|
|
||||||
const image_header *hdr = NULL;
|
const image_header *hdr = NULL;
|
||||||
vendor_header vhdr;
|
vendor_header vhdr;
|
||||||
|
|
||||||
// detect whether the device contains a valid firmware
|
// detect whether the device contains a valid firmware
|
||||||
secbool firmware_present = sectrue;
|
volatile secbool vhdr_present = secfalse;
|
||||||
|
volatile secbool vhdr_keys_ok = secfalse;
|
||||||
|
volatile secbool vhdr_lock_ok = secfalse;
|
||||||
|
volatile secbool img_hdr_ok = secfalse;
|
||||||
|
volatile secbool model_ok = secfalse;
|
||||||
|
volatile secbool header_present = secfalse;
|
||||||
|
volatile secbool firmware_present = secfalse;
|
||||||
|
|
||||||
if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) {
|
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
|
||||||
firmware_present = secfalse;
|
|
||||||
|
if (sectrue == vhdr_present) {
|
||||||
|
vhdr_keys_ok = check_vendor_header_keys(&vhdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sectrue == firmware_present) {
|
if (sectrue == vhdr_keys_ok) {
|
||||||
firmware_present = check_vendor_header_keys(&vhdr);
|
vhdr_lock_ok = check_vendor_header_lock(&vhdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sectrue == firmware_present) {
|
if (sectrue == vhdr_lock_ok) {
|
||||||
firmware_present = check_vendor_header_lock(&vhdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectrue == firmware_present) {
|
|
||||||
hdr = read_image_header(
|
hdr = read_image_header(
|
||||||
(const uint8_t *)(size_t)(FIRMWARE_START + vhdr.hdrlen),
|
(const uint8_t *)(size_t)(FIRMWARE_START + vhdr.hdrlen),
|
||||||
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE);
|
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE);
|
||||||
if (hdr != (const image_header *)(size_t)(FIRMWARE_START + vhdr.hdrlen)) {
|
if (hdr == (const image_header *)(size_t)(FIRMWARE_START + vhdr.hdrlen)) {
|
||||||
firmware_present = secfalse;
|
img_hdr_ok = sectrue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sectrue == firmware_present) {
|
if (sectrue == img_hdr_ok) {
|
||||||
firmware_present = check_image_model(hdr);
|
model_ok = check_image_model(hdr);
|
||||||
}
|
}
|
||||||
if (sectrue == firmware_present) {
|
if (sectrue == model_ok) {
|
||||||
firmware_present =
|
header_present =
|
||||||
check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub);
|
check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub);
|
||||||
}
|
}
|
||||||
if (sectrue == firmware_present) {
|
|
||||||
|
if (sectrue == header_present) {
|
||||||
firmware_present = check_image_contents(
|
firmware_present = check_image_contents(
|
||||||
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
|
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
|
||||||
}
|
}
|
||||||
@ -414,16 +426,21 @@ int bootloader_main(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// start the bootloader if no or broken firmware found ...
|
// start the bootloader ...
|
||||||
if (firmware_present != sectrue) {
|
// ... if user touched the screen on start
|
||||||
#ifdef TREZOR_EMULATOR
|
// ... or we have stay_in_bootloader flag to force it
|
||||||
// wait a bit so that the empty lock icon is visible
|
// ... or there is no valid firmware
|
||||||
// (on a real device, we are waiting for touch init which takes longer)
|
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue) {
|
||||||
hal_delay(400);
|
screen_t screen;
|
||||||
#endif
|
if (header_present == sectrue) {
|
||||||
// ignore stay in bootloader
|
ui_set_initial_setup(false);
|
||||||
stay_in_bootloader = secfalse;
|
screen = SCREEN_INTRO;
|
||||||
touched = false;
|
} else {
|
||||||
|
screen = SCREEN_WELCOME;
|
||||||
|
|
||||||
|
// erase storage
|
||||||
|
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
|
||||||
|
NULL);
|
||||||
|
|
||||||
ui_set_initial_setup(true);
|
ui_set_initial_setup(true);
|
||||||
|
|
||||||
@ -431,37 +448,37 @@ int bootloader_main(void) {
|
|||||||
#ifndef USE_BACKLIGHT
|
#ifndef USE_BACKLIGHT
|
||||||
hal_delay(1500);
|
hal_delay(1500);
|
||||||
#else
|
#else
|
||||||
// backlight fading takes some time so the explicit delay here is shorter
|
// backlight fading takes some time so the explicit delay here is
|
||||||
|
// shorter
|
||||||
hal_delay(1000);
|
hal_delay(1000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// show welcome screen
|
|
||||||
ui_screen_welcome();
|
|
||||||
|
|
||||||
// erase storage
|
|
||||||
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
// and start the usb loop
|
|
||||||
if (bootloader_usb_loop(NULL, NULL) != CONTINUE) {
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ... or if user touched the screen on start
|
|
||||||
// ... or we have stay_in_bootloader flag to force it
|
|
||||||
if (touched || stay_in_bootloader == sectrue) {
|
|
||||||
ui_set_initial_setup(false);
|
|
||||||
|
|
||||||
screen_t screen = SCREEN_INTRO;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
bool continue_to_firmware = false;
|
secbool continue_to_firmware = secfalse;
|
||||||
uint32_t ui_result = 0;
|
uint32_t ui_result = 0;
|
||||||
|
|
||||||
switch (screen) {
|
switch (screen) {
|
||||||
|
case SCREEN_WELCOME:
|
||||||
|
|
||||||
|
ui_screen_welcome();
|
||||||
|
|
||||||
|
// and start the usb loop
|
||||||
|
switch (bootloader_usb_loop(NULL, NULL)) {
|
||||||
|
case CONTINUE_TO_FIRMWARE:
|
||||||
|
continue_to_firmware = sectrue;
|
||||||
|
break;
|
||||||
|
case RETURN_TO_MENU:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case SHUTDOWN:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SCREEN_INTRO:
|
case SCREEN_INTRO:
|
||||||
ui_result = ui_screen_intro(&vhdr, hdr);
|
ui_result = ui_screen_intro(&vhdr, hdr, firmware_present);
|
||||||
if (ui_result == 1) {
|
if (ui_result == 1) {
|
||||||
screen = SCREEN_MENU;
|
screen = SCREEN_MENU;
|
||||||
}
|
}
|
||||||
@ -470,15 +487,15 @@ int bootloader_main(void) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SCREEN_MENU:
|
case SCREEN_MENU:
|
||||||
ui_result = ui_screen_menu();
|
ui_result = ui_screen_menu(firmware_present);
|
||||||
if (ui_result == 1) { // exit menu
|
if (ui_result == 0xAABBCCDD) { // exit menu
|
||||||
screen = SCREEN_INTRO;
|
screen = SCREEN_INTRO;
|
||||||
}
|
}
|
||||||
if (ui_result == 2) { // reboot
|
if (ui_result == 0x11223344) { // reboot
|
||||||
ui_screen_boot_empty(true);
|
ui_screen_boot_empty(true);
|
||||||
continue_to_firmware = true;
|
continue_to_firmware = firmware_present;
|
||||||
}
|
}
|
||||||
if (ui_result == 3) { // wipe
|
if (ui_result == 0x55667788) { // wipe
|
||||||
screen = SCREEN_WIPE_CONFIRM;
|
screen = SCREEN_WIPE_CONFIRM;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -503,10 +520,10 @@ int bootloader_main(void) {
|
|||||||
case SCREEN_WAIT_FOR_HOST:
|
case SCREEN_WAIT_FOR_HOST:
|
||||||
screen_connect();
|
screen_connect();
|
||||||
switch (bootloader_usb_loop(&vhdr, hdr)) {
|
switch (bootloader_usb_loop(&vhdr, hdr)) {
|
||||||
case CONTINUE:
|
case CONTINUE_TO_FIRMWARE:
|
||||||
continue_to_firmware = true;
|
continue_to_firmware = sectrue;
|
||||||
break;
|
break;
|
||||||
case RETURN:
|
case RETURN_TO_MENU:
|
||||||
screen = SCREEN_INTRO;
|
screen = SCREEN_INTRO;
|
||||||
break;
|
break;
|
||||||
case SHUTDOWN:
|
case SHUTDOWN:
|
||||||
@ -520,7 +537,7 @@ int bootloader_main(void) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (continue_to_firmware) {
|
if (sectrue == continue_to_firmware) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,9 @@ void screen_install_progress(int16_t progress, bool initialize,
|
|||||||
bool initial_setup);
|
bool initial_setup);
|
||||||
void screen_wipe_progress(int16_t progress, bool initialize);
|
void screen_wipe_progress(int16_t progress, bool initialize);
|
||||||
uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
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,
|
||||||
uint32_t screen_menu(void);
|
bool fw_ok);
|
||||||
|
uint32_t screen_menu(secbool firmware_present);
|
||||||
void screen_connect(void);
|
void screen_connect(void);
|
||||||
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);
|
||||||
|
@ -18,6 +18,8 @@ pub mod uzlib;
|
|||||||
pub mod wordlist;
|
pub mod wordlist;
|
||||||
|
|
||||||
pub mod buffers;
|
pub mod buffers;
|
||||||
|
pub mod secbool;
|
||||||
|
|
||||||
#[cfg(not(feature = "micropython"))]
|
#[cfg(not(feature = "micropython"))]
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
|
3
core/embed/rust/src/trezorhal/secbool.rs
Normal file
3
core/embed/rust/src/trezorhal/secbool.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
use super::ffi;
|
||||||
|
|
||||||
|
pub use ffi::{secbool, secfalse, sectrue};
|
@ -1,6 +1,6 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||||
geometry::{Alignment2D, Rect},
|
geometry::{Alignment, Alignment2D, Rect},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -32,10 +32,11 @@ pub struct Intro<'a> {
|
|||||||
title: Child<Label<&'a str>>,
|
title: Child<Label<&'a str>>,
|
||||||
buttons: Child<ButtonController<&'static str>>,
|
buttons: Child<ButtonController<&'static str>>,
|
||||||
text: Child<Label<&'a str>>,
|
text: Child<Label<&'a str>>,
|
||||||
|
warn: Option<Child<Label<&'a str>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Intro<'a> {
|
impl<'a> Intro<'a> {
|
||||||
pub fn new(title: &'a str, content: &'a str) -> 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::centered(title, TEXT_NORMAL).vertically_centered()),
|
title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()),
|
||||||
@ -44,6 +45,10 @@ impl<'a> Intro<'a> {
|
|||||||
RIGHT_BUTTON_TEXT,
|
RIGHT_BUTTON_TEXT,
|
||||||
))),
|
))),
|
||||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
||||||
|
warn: (!fw_ok).then_some(Child::new(
|
||||||
|
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_NORMAL)
|
||||||
|
.vertically_centered(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,6 +66,15 @@ impl<'a> Component for Intro<'a> {
|
|||||||
self.title.place(title_area);
|
self.title.place(title_area);
|
||||||
self.buttons.place(buttons_area);
|
self.buttons.place(buttons_area);
|
||||||
self.text.place(text_area);
|
self.text.place(text_area);
|
||||||
|
|
||||||
|
if self.warn.is_some() {
|
||||||
|
let (warn_area, text_area) = text_area.split_top(10);
|
||||||
|
self.warn.place(warn_area);
|
||||||
|
self.text.place(text_area);
|
||||||
|
} else {
|
||||||
|
self.text.place(text_area);
|
||||||
|
}
|
||||||
|
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +96,7 @@ impl<'a> Component for Intro<'a> {
|
|||||||
let area = self.bg.area;
|
let area = self.bg.area;
|
||||||
ICON_WARN_TITLE.draw(area.top_left(), Alignment2D::TOP_LEFT, BLD_FG, BLD_BG);
|
ICON_WARN_TITLE.draw(area.top_left(), Alignment2D::TOP_LEFT, BLD_FG, BLD_BG);
|
||||||
ICON_WARN_TITLE.draw(area.top_right(), Alignment2D::TOP_RIGHT, BLD_FG, BLD_BG);
|
ICON_WARN_TITLE.draw(area.top_right(), Alignment2D::TOP_RIGHT, BLD_FG, BLD_BG);
|
||||||
|
self.warn.paint();
|
||||||
self.text.paint();
|
self.text.paint();
|
||||||
self.buttons.paint();
|
self.buttons.paint();
|
||||||
}
|
}
|
||||||
@ -89,6 +104,7 @@ impl<'a> Component for Intro<'a> {
|
|||||||
#[cfg(feature = "ui_bounds")]
|
#[cfg(feature = "ui_bounds")]
|
||||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
self.title.bounds(sink);
|
self.title.bounds(sink);
|
||||||
|
self.warn.bounds(sink);
|
||||||
self.text.bounds(sink);
|
self.text.bounds(sink);
|
||||||
self.buttons.bounds(sink);
|
self.buttons.bounds(sink);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
use crate::trace::{Trace, Tracer};
|
use crate::trace::{Trace, Tracer};
|
||||||
use crate::ui::{
|
use crate::{
|
||||||
|
trezorhal::secbool::{secbool, sectrue},
|
||||||
|
ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Pad},
|
component::{Child, Component, Event, EventCtx, Pad},
|
||||||
constant::screen,
|
constant::screen,
|
||||||
display,
|
display,
|
||||||
display::{Font, Icon},
|
display::{Font, Icon},
|
||||||
geometry::{Alignment2D, Offset, Point, Rect},
|
geometry::{Alignment2D, Offset, Point, Rect},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -17,9 +20,9 @@ use super::{
|
|||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum MenuMsg {
|
pub enum MenuMsg {
|
||||||
Close = 1,
|
Close = 0xAABBCCDD,
|
||||||
Reboot = 2,
|
Reboot = 0x11223344,
|
||||||
FactoryReset = 3,
|
FactoryReset = 0x55667788,
|
||||||
}
|
}
|
||||||
impl ReturnToC for MenuMsg {
|
impl ReturnToC for MenuMsg {
|
||||||
fn return_to_c(self) -> u32 {
|
fn return_to_c(self) -> u32 {
|
||||||
@ -74,17 +77,19 @@ impl Trace for MenuChoice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MenuChoiceFactory;
|
pub struct MenuChoiceFactory {
|
||||||
|
firmware_present: secbool,
|
||||||
|
}
|
||||||
|
|
||||||
impl MenuChoiceFactory {
|
impl MenuChoiceFactory {
|
||||||
const CHOICES: [(&'static str, &'static str, Icon); CHOICE_LENGTH] = [
|
const CHOICES: [(&'static str, &'static str, Icon); CHOICE_LENGTH] = [
|
||||||
("Factory", "reset", ICON_TRASH),
|
("Factory", "reset", ICON_TRASH),
|
||||||
("Reboot", "Trezor", ICON_REDO),
|
|
||||||
("Exit", "menu", ICON_EXIT),
|
("Exit", "menu", ICON_EXIT),
|
||||||
|
("Reboot", "Trezor", ICON_REDO),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new(firmware_present: secbool) -> Self {
|
||||||
Self {}
|
Self { firmware_present }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +98,11 @@ impl ChoiceFactory<&'static str> for MenuChoiceFactory {
|
|||||||
type Item = MenuChoice;
|
type Item = MenuChoice;
|
||||||
|
|
||||||
fn count(&self) -> usize {
|
fn count(&self) -> usize {
|
||||||
|
if self.firmware_present == sectrue {
|
||||||
CHOICE_LENGTH
|
CHOICE_LENGTH
|
||||||
|
} else {
|
||||||
|
CHOICE_LENGTH - 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
fn get(&self, choice_index: usize) -> (Self::Item, Self::Action) {
|
||||||
@ -104,8 +113,8 @@ impl ChoiceFactory<&'static str> for MenuChoiceFactory {
|
|||||||
);
|
);
|
||||||
let action = match choice_index {
|
let action = match choice_index {
|
||||||
0 => MenuMsg::FactoryReset,
|
0 => MenuMsg::FactoryReset,
|
||||||
1 => MenuMsg::Reboot,
|
1 => MenuMsg::Close,
|
||||||
2 => MenuMsg::Close,
|
2 if self.firmware_present == sectrue => MenuMsg::Reboot,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
(choice_item, action)
|
(choice_item, action)
|
||||||
@ -118,8 +127,8 @@ pub struct Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
pub fn new() -> Self {
|
pub fn new(firmware_present: secbool) -> Self {
|
||||||
let choices = MenuChoiceFactory::new();
|
let choices = MenuChoiceFactory::new(firmware_present);
|
||||||
Self {
|
Self {
|
||||||
pad: Pad::with_background(BLD_BG).with_clear(),
|
pad: Pad::with_background(BLD_BG).with_clear(),
|
||||||
choice_page: Child::new(
|
choice_page: Child::new(
|
||||||
|
@ -21,11 +21,14 @@ mod menu;
|
|||||||
mod theme;
|
mod theme;
|
||||||
mod welcome;
|
mod welcome;
|
||||||
|
|
||||||
use crate::ui::{
|
use crate::{
|
||||||
|
trezorhal::secbool::secbool,
|
||||||
|
ui::{
|
||||||
constant,
|
constant,
|
||||||
constant::HEIGHT,
|
constant::HEIGHT,
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
model_tr::theme::{ICON_ARM_LEFT, ICON_ARM_RIGHT, WHITE},
|
model_tr::theme::{ICON_ARM_LEFT, ICON_ARM_RIGHT, WHITE},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use confirm::Confirm;
|
use confirm::Confirm;
|
||||||
use connect::Connect;
|
use connect::Connect;
|
||||||
@ -192,8 +195,8 @@ extern "C" fn screen_unlock_bootloader_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn screen_menu(_bld_version: *const cty::c_char) -> u32 {
|
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||||
run(&mut Menu::new())
|
run(&mut Menu::new(firmware_present))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -202,6 +205,7 @@ extern "C" fn screen_intro(
|
|||||||
vendor_str: *const cty::c_char,
|
vendor_str: *const cty::c_char,
|
||||||
vendor_str_len: u8,
|
vendor_str_len: u8,
|
||||||
version: *const cty::c_char,
|
version: *const cty::c_char,
|
||||||
|
fw_ok: bool,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
||||||
let version = unwrap!(unsafe { from_c_str(version) });
|
let version = unwrap!(unsafe { from_c_str(version) });
|
||||||
@ -217,7 +221,7 @@ extern "C" fn screen_intro(
|
|||||||
unwrap!(version_str.push_str("\nby "));
|
unwrap!(version_str.push_str("\nby "));
|
||||||
unwrap!(version_str.push_str(vendor));
|
unwrap!(version_str.push_str(vendor));
|
||||||
|
|
||||||
let mut frame = Intro::new(title_str.as_str(), version_str.as_str());
|
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok);
|
||||||
run(&mut frame)
|
run(&mut frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ use crate::ui::{
|
|||||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||||
constant::screen,
|
constant::screen,
|
||||||
display::Icon,
|
display::Icon,
|
||||||
geometry::{Insets, Point, Rect},
|
geometry::{Alignment, Insets, Point, Rect},
|
||||||
model_tt::{
|
model_tt::{
|
||||||
bootloader::theme::{
|
bootloader::theme::{
|
||||||
button_bld, button_bld_menu, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
button_bld, button_bld_menu, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||||
CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_TITLE, TITLE_AREA,
|
CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_TITLE, TEXT_WARNING, TITLE_AREA,
|
||||||
},
|
},
|
||||||
component::{Button, ButtonMsg::Clicked},
|
component::{Button, ButtonMsg::Clicked},
|
||||||
constant::WIDTH,
|
constant::WIDTH,
|
||||||
@ -26,10 +26,11 @@ pub struct Intro<'a> {
|
|||||||
menu: Child<Button<&'static str>>,
|
menu: Child<Button<&'static str>>,
|
||||||
host: Child<Button<&'static str>>,
|
host: Child<Button<&'static str>>,
|
||||||
text: Child<Label<&'a str>>,
|
text: Child<Label<&'a str>>,
|
||||||
|
warn: Option<Child<Label<&'a str>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Intro<'a> {
|
impl<'a> Intro<'a> {
|
||||||
pub fn new(title: &'a str, content: &'a str) -> 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).vertically_centered()),
|
||||||
@ -40,6 +41,10 @@ impl<'a> Intro<'a> {
|
|||||||
),
|
),
|
||||||
host: Child::new(Button::with_text("INSTALL FIRMWARE").styled(button_bld())),
|
host: Child::new(Button::with_text("INSTALL FIRMWARE").styled(button_bld())),
|
||||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
||||||
|
warn: (!fw_ok).then_some(Child::new(
|
||||||
|
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_WARNING)
|
||||||
|
.vertically_centered(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,10 +61,25 @@ impl<'a> Component for Intro<'a> {
|
|||||||
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
||||||
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
||||||
));
|
));
|
||||||
|
if self.warn.is_some() {
|
||||||
|
self.warn.place(Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING),
|
||||||
|
Point::new(
|
||||||
|
WIDTH - CONTENT_PADDING,
|
||||||
|
TITLE_AREA.y1 + CONTENT_PADDING + 30,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
self.text.place(Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING + 30),
|
||||||
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
self.text.place(Rect::new(
|
self.text.place(Rect::new(
|
||||||
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING),
|
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING),
|
||||||
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +97,7 @@ impl<'a> Component for Intro<'a> {
|
|||||||
self.bg.paint();
|
self.bg.paint();
|
||||||
self.title.paint();
|
self.title.paint();
|
||||||
self.text.paint();
|
self.text.paint();
|
||||||
|
self.warn.paint();
|
||||||
self.host.paint();
|
self.host.paint();
|
||||||
self.menu.paint();
|
self.menu.paint();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
|
trezorhal::secbool::{secbool, sectrue},
|
||||||
|
ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||||
constant::{screen, WIDTH},
|
constant::{screen, WIDTH},
|
||||||
display::Icon,
|
display::Icon,
|
||||||
@ -11,6 +13,7 @@ use crate::ui::{
|
|||||||
},
|
},
|
||||||
component::{Button, ButtonMsg::Clicked, IconText},
|
component::{Button, ButtonMsg::Clicked, IconText},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUTTON_AREA_START: i16 = 56;
|
const BUTTON_AREA_START: i16 = 56;
|
||||||
@ -19,9 +22,9 @@ const BUTTON_SPACING: i16 = 8;
|
|||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Copy, Clone, ToPrimitive)]
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
pub enum MenuMsg {
|
pub enum MenuMsg {
|
||||||
Close = 1,
|
Close = 0xAABBCCDD,
|
||||||
Reboot = 2,
|
Reboot = 0x11223344,
|
||||||
FactoryReset = 3,
|
FactoryReset = 0x55667788,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Menu {
|
pub struct Menu {
|
||||||
@ -33,7 +36,7 @@ pub struct Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
pub fn new() -> Self {
|
pub fn new(firmware_present: secbool) -> Self {
|
||||||
let content_reboot = IconText::new("REBOOT TREZOR", Icon::new(REFRESH24));
|
let content_reboot = IconText::new("REBOOT TREZOR", Icon::new(REFRESH24));
|
||||||
let content_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
let content_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
||||||
|
|
||||||
@ -45,7 +48,11 @@ impl Menu {
|
|||||||
.styled(button_bld_menu())
|
.styled(button_bld_menu())
|
||||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||||
),
|
),
|
||||||
reboot: Child::new(Button::with_icon_and_text(content_reboot).styled(button_bld())),
|
reboot: Child::new(
|
||||||
|
Button::with_icon_and_text(content_reboot)
|
||||||
|
.styled(button_bld())
|
||||||
|
.initially_enabled(sectrue == firmware_present),
|
||||||
|
),
|
||||||
reset: Child::new(Button::with_icon_and_text(content_reset).styled(button_bld())),
|
reset: Child::new(Button::with_icon_and_text(content_reset).styled(button_bld())),
|
||||||
};
|
};
|
||||||
instance.bg.clear();
|
instance.bg.clear();
|
||||||
|
@ -35,7 +35,7 @@ pub mod menu;
|
|||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod welcome;
|
pub mod welcome;
|
||||||
|
|
||||||
use crate::ui::model_tt::theme::BLACK;
|
use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK};
|
||||||
use confirm::Confirm;
|
use confirm::Confirm;
|
||||||
use intro::Intro;
|
use intro::Intro;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
@ -220,8 +220,8 @@ extern "C" fn screen_wipe_confirm() -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn screen_menu() -> u32 {
|
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||||
run(&mut Menu::new())
|
run(&mut Menu::new(firmware_present))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -230,6 +230,7 @@ extern "C" fn screen_intro(
|
|||||||
vendor_str: *const cty::c_char,
|
vendor_str: *const cty::c_char,
|
||||||
vendor_str_len: u8,
|
vendor_str_len: u8,
|
||||||
version: *const cty::c_char,
|
version: *const cty::c_char,
|
||||||
|
fw_ok: bool,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
||||||
let version = unwrap!(unsafe { from_c_str(version) });
|
let version = unwrap!(unsafe { from_c_str(version) });
|
||||||
@ -245,7 +246,7 @@ extern "C" fn screen_intro(
|
|||||||
unwrap!(version_str.push_str("\nby "));
|
unwrap!(version_str.push_str("\nby "));
|
||||||
unwrap!(version_str.push_str(vendor));
|
unwrap!(version_str.push_str(vendor));
|
||||||
|
|
||||||
let mut frame = Intro::new(title_str.as_str(), version_str.as_str());
|
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok);
|
||||||
|
|
||||||
run(&mut frame)
|
run(&mut frame)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ pub const BLD_FG: Color = WHITE;
|
|||||||
pub const BLD_WIPE_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
pub const BLD_WIPE_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
||||||
pub const BLD_WIPE_TEXT_COLOR: Color = WHITE;
|
pub const BLD_WIPE_TEXT_COLOR: Color = WHITE;
|
||||||
|
|
||||||
|
pub const BLD_WARN_COLOR: Color = Color::rgb(0xFF, 0x00, 0x00);
|
||||||
|
|
||||||
pub const BLD_WIPE_BTN_COLOR: Color = WHITE;
|
pub const BLD_WIPE_BTN_COLOR: Color = WHITE;
|
||||||
pub const BLD_WIPE_BTN_COLOR_ACTIVE: Color = Color::rgb(0xFA, 0xCF, 0xCF);
|
pub const BLD_WIPE_BTN_COLOR_ACTIVE: Color = Color::rgb(0xFA, 0xCF, 0xCF);
|
||||||
|
|
||||||
@ -238,6 +240,13 @@ pub const TEXT_TITLE: TextStyle = TextStyle::new(
|
|||||||
);
|
);
|
||||||
|
|
||||||
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(
|
||||||
|
Font::BOLD,
|
||||||
|
BLD_WARN_COLOR,
|
||||||
|
BLD_BG,
|
||||||
|
BLD_WARN_COLOR,
|
||||||
|
BLD_WARN_COLOR,
|
||||||
|
);
|
||||||
pub const TEXT_FINGERPRINT: TextStyle =
|
pub const TEXT_FINGERPRINT: TextStyle =
|
||||||
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG)
|
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG)
|
||||||
.with_line_breaking(BreakWordsNoHyphen);
|
.with_line_breaking(BreakWordsNoHyphen);
|
||||||
|
Loading…
Reference in New Issue
Block a user