1
0
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:
tychovrahe 2023-06-30 22:57:04 +02:00 committed by TychoVrahe
parent 01a7de5b98
commit a6d0842663
15 changed files with 230 additions and 136 deletions

View File

@ -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,

View 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.

View File

@ -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

View File

@ -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,

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1,3 @@
use super::ffi;
pub use ffi::{secbool, secfalse, sectrue};

View File

@ -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);
} }

View File

@ -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(

View File

@ -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)
} }

View File

@ -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();
} }

View File

@ -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();

View File

@ -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)
} }

View File

@ -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);