mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-08 22:40:59 +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',
|
||||
CPU_MODEL,
|
||||
'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_BOARD=\\"boards/board-unix.h\\"',
|
||||
'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(); }
|
||||
|
||||
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 ver_str[64];
|
||||
format_ver("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
|
||||
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
|
||||
|
||||
|
@ -31,6 +31,7 @@ typedef enum {
|
||||
SCREEN_WIPE_CONFIRM = 2,
|
||||
SCREEN_FINGER_PRINT = 3,
|
||||
SCREEN_WAIT_FOR_HOST = 4,
|
||||
SCREEN_WELCOME = 5,
|
||||
} screen_t;
|
||||
|
||||
void ui_screen_boot(const vendor_header* const vhdr,
|
||||
@ -42,9 +43,9 @@ void ui_click(void);
|
||||
void ui_screen_welcome(void);
|
||||
|
||||
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,
|
||||
const image_header* const hdr,
|
||||
|
@ -81,9 +81,9 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
typedef enum {
|
||||
CONTINUE = 0,
|
||||
RETURN = 1,
|
||||
SHUTDOWN = 2,
|
||||
SHUTDOWN = 0,
|
||||
CONTINUE_TO_FIRMWARE = 0xAABBCCDD,
|
||||
RETURN_TO_MENU = 0x55667788,
|
||||
} usb_result_t;
|
||||
|
||||
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);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN;
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
ui_screen_wipe();
|
||||
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);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN;
|
||||
return RETURN_TO_MENU;
|
||||
} else if (r == 0) { // last chunk received
|
||||
ui_screen_install_progress_upload(1000);
|
||||
ui_screen_done(4, sectrue);
|
||||
@ -213,7 +213,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
ui_screen_boot_empty(true);
|
||||
return CONTINUE;
|
||||
return CONTINUE_TO_FIRMWARE;
|
||||
}
|
||||
break;
|
||||
case MessageType_MessageType_GetFeatures:
|
||||
@ -227,7 +227,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN;
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf);
|
||||
screen_unlock_bootloader_success();
|
||||
@ -315,39 +315,51 @@ int bootloader_main(void) {
|
||||
|
||||
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;
|
||||
vendor_header vhdr;
|
||||
|
||||
// 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)) {
|
||||
firmware_present = secfalse;
|
||||
vhdr_present = read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr);
|
||||
|
||||
if (sectrue == vhdr_present) {
|
||||
vhdr_keys_ok = check_vendor_header_keys(&vhdr);
|
||||
}
|
||||
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present = check_vendor_header_keys(&vhdr);
|
||||
if (sectrue == vhdr_keys_ok) {
|
||||
vhdr_lock_ok = check_vendor_header_lock(&vhdr);
|
||||
}
|
||||
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present = check_vendor_header_lock(&vhdr);
|
||||
}
|
||||
|
||||
if (sectrue == firmware_present) {
|
||||
if (sectrue == vhdr_lock_ok) {
|
||||
hdr = read_image_header(
|
||||
(const uint8_t *)(size_t)(FIRMWARE_START + vhdr.hdrlen),
|
||||
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE);
|
||||
if (hdr != (const image_header *)(size_t)(FIRMWARE_START + vhdr.hdrlen)) {
|
||||
firmware_present = secfalse;
|
||||
if (hdr == (const image_header *)(size_t)(FIRMWARE_START + vhdr.hdrlen)) {
|
||||
img_hdr_ok = sectrue;
|
||||
}
|
||||
}
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present = check_image_model(hdr);
|
||||
if (sectrue == img_hdr_ok) {
|
||||
model_ok = check_image_model(hdr);
|
||||
}
|
||||
if (sectrue == firmware_present) {
|
||||
firmware_present =
|
||||
if (sectrue == model_ok) {
|
||||
header_present =
|
||||
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(
|
||||
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
|
||||
}
|
||||
@ -414,54 +426,59 @@ int bootloader_main(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// start the bootloader if no or broken firmware found ...
|
||||
if (firmware_present != sectrue) {
|
||||
#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
|
||||
// ignore stay in bootloader
|
||||
stay_in_bootloader = secfalse;
|
||||
touched = false;
|
||||
|
||||
ui_set_initial_setup(true);
|
||||
|
||||
// keep the model screen up for a while
|
||||
#ifndef USE_BACKLIGHT
|
||||
hal_delay(1500);
|
||||
#else
|
||||
// backlight fading takes some time so the explicit delay here is shorter
|
||||
hal_delay(1000);
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// start the bootloader ...
|
||||
// ... 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);
|
||||
// ... or there is no valid firmware
|
||||
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue) {
|
||||
screen_t screen;
|
||||
if (header_present == sectrue) {
|
||||
ui_set_initial_setup(false);
|
||||
screen = SCREEN_INTRO;
|
||||
} else {
|
||||
screen = SCREEN_WELCOME;
|
||||
|
||||
screen_t screen = SCREEN_INTRO;
|
||||
// erase storage
|
||||
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
|
||||
NULL);
|
||||
|
||||
ui_set_initial_setup(true);
|
||||
|
||||
// keep the model screen up for a while
|
||||
#ifndef USE_BACKLIGHT
|
||||
hal_delay(1500);
|
||||
#else
|
||||
// backlight fading takes some time so the explicit delay here is
|
||||
// shorter
|
||||
hal_delay(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
while (true) {
|
||||
bool continue_to_firmware = false;
|
||||
secbool continue_to_firmware = secfalse;
|
||||
uint32_t ui_result = 0;
|
||||
|
||||
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:
|
||||
ui_result = ui_screen_intro(&vhdr, hdr);
|
||||
ui_result = ui_screen_intro(&vhdr, hdr, firmware_present);
|
||||
if (ui_result == 1) {
|
||||
screen = SCREEN_MENU;
|
||||
}
|
||||
@ -470,15 +487,15 @@ int bootloader_main(void) {
|
||||
}
|
||||
break;
|
||||
case SCREEN_MENU:
|
||||
ui_result = ui_screen_menu();
|
||||
if (ui_result == 1) { // exit menu
|
||||
ui_result = ui_screen_menu(firmware_present);
|
||||
if (ui_result == 0xAABBCCDD) { // exit menu
|
||||
screen = SCREEN_INTRO;
|
||||
}
|
||||
if (ui_result == 2) { // reboot
|
||||
if (ui_result == 0x11223344) { // reboot
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@ -503,10 +520,10 @@ int bootloader_main(void) {
|
||||
case SCREEN_WAIT_FOR_HOST:
|
||||
screen_connect();
|
||||
switch (bootloader_usb_loop(&vhdr, hdr)) {
|
||||
case CONTINUE:
|
||||
continue_to_firmware = true;
|
||||
case CONTINUE_TO_FIRMWARE:
|
||||
continue_to_firmware = sectrue;
|
||||
break;
|
||||
case RETURN:
|
||||
case RETURN_TO_MENU:
|
||||
screen = SCREEN_INTRO;
|
||||
break;
|
||||
case SHUTDOWN:
|
||||
@ -520,7 +537,7 @@ int bootloader_main(void) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (continue_to_firmware) {
|
||||
if (sectrue == continue_to_firmware) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ void screen_install_progress(int16_t progress, bool initialize,
|
||||
bool initial_setup);
|
||||
void screen_wipe_progress(int16_t progress, bool initialize);
|
||||
uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
||||
uint8_t vendor_str_len, const char* version_str);
|
||||
uint32_t screen_menu(void);
|
||||
uint8_t vendor_str_len, const char* version_str,
|
||||
bool fw_ok);
|
||||
uint32_t screen_menu(secbool firmware_present);
|
||||
void screen_connect(void);
|
||||
void screen_fatal_error_rust(const char* title, const char* msg,
|
||||
const char* footer);
|
||||
|
@ -18,6 +18,8 @@ pub mod uzlib;
|
||||
pub mod wordlist;
|
||||
|
||||
pub mod buffers;
|
||||
pub mod secbool;
|
||||
|
||||
#[cfg(not(feature = "micropython"))]
|
||||
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::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
geometry::{Alignment2D, Rect},
|
||||
geometry::{Alignment, Alignment2D, Rect},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -32,10 +32,11 @@ pub struct Intro<'a> {
|
||||
title: Child<Label<&'a str>>,
|
||||
buttons: Child<ButtonController<&'static str>>,
|
||||
text: Child<Label<&'a str>>,
|
||||
warn: Option<Child<Label<&'a str>>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()),
|
||||
@ -44,6 +45,10 @@ impl<'a> Intro<'a> {
|
||||
RIGHT_BUTTON_TEXT,
|
||||
))),
|
||||
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.buttons.place(buttons_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
|
||||
}
|
||||
|
||||
@ -82,6 +96,7 @@ impl<'a> Component for Intro<'a> {
|
||||
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_right(), Alignment2D::TOP_RIGHT, BLD_FG, BLD_BG);
|
||||
self.warn.paint();
|
||||
self.text.paint();
|
||||
self.buttons.paint();
|
||||
}
|
||||
@ -89,6 +104,7 @@ impl<'a> Component for Intro<'a> {
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.title.bounds(sink);
|
||||
self.warn.bounds(sink);
|
||||
self.text.bounds(sink);
|
||||
self.buttons.bounds(sink);
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::{Trace, Tracer};
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Pad},
|
||||
constant::screen,
|
||||
display,
|
||||
display::{Font, Icon},
|
||||
geometry::{Alignment2D, Offset, Point, Rect},
|
||||
use crate::{
|
||||
trezorhal::secbool::{secbool, sectrue},
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Pad},
|
||||
constant::screen,
|
||||
display,
|
||||
display::{Font, Icon},
|
||||
geometry::{Alignment2D, Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -17,9 +20,9 @@ use super::{
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum MenuMsg {
|
||||
Close = 1,
|
||||
Reboot = 2,
|
||||
FactoryReset = 3,
|
||||
Close = 0xAABBCCDD,
|
||||
Reboot = 0x11223344,
|
||||
FactoryReset = 0x55667788,
|
||||
}
|
||||
impl ReturnToC for MenuMsg {
|
||||
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 {
|
||||
const CHOICES: [(&'static str, &'static str, Icon); CHOICE_LENGTH] = [
|
||||
("Factory", "reset", ICON_TRASH),
|
||||
("Reboot", "Trezor", ICON_REDO),
|
||||
("Exit", "menu", ICON_EXIT),
|
||||
("Reboot", "Trezor", ICON_REDO),
|
||||
];
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
pub fn new(firmware_present: secbool) -> Self {
|
||||
Self { firmware_present }
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +98,11 @@ impl ChoiceFactory<&'static str> for MenuChoiceFactory {
|
||||
type Item = MenuChoice;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
CHOICE_LENGTH
|
||||
if self.firmware_present == sectrue {
|
||||
CHOICE_LENGTH
|
||||
} else {
|
||||
CHOICE_LENGTH - 1
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
0 => MenuMsg::FactoryReset,
|
||||
1 => MenuMsg::Reboot,
|
||||
2 => MenuMsg::Close,
|
||||
1 => MenuMsg::Close,
|
||||
2 if self.firmware_present == sectrue => MenuMsg::Reboot,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
(choice_item, action)
|
||||
@ -118,8 +127,8 @@ pub struct Menu {
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new() -> Self {
|
||||
let choices = MenuChoiceFactory::new();
|
||||
pub fn new(firmware_present: secbool) -> Self {
|
||||
let choices = MenuChoiceFactory::new(firmware_present);
|
||||
Self {
|
||||
pad: Pad::with_background(BLD_BG).with_clear(),
|
||||
choice_page: Child::new(
|
||||
|
@ -21,11 +21,14 @@ mod menu;
|
||||
mod theme;
|
||||
mod welcome;
|
||||
|
||||
use crate::ui::{
|
||||
constant,
|
||||
constant::HEIGHT,
|
||||
geometry::Point,
|
||||
model_tr::theme::{ICON_ARM_LEFT, ICON_ARM_RIGHT, WHITE},
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
constant,
|
||||
constant::HEIGHT,
|
||||
geometry::Point,
|
||||
model_tr::theme::{ICON_ARM_LEFT, ICON_ARM_RIGHT, WHITE},
|
||||
},
|
||||
};
|
||||
use confirm::Confirm;
|
||||
use connect::Connect;
|
||||
@ -192,8 +195,8 @@ extern "C" fn screen_unlock_bootloader_success() {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_menu(_bld_version: *const cty::c_char) -> u32 {
|
||||
run(&mut Menu::new())
|
||||
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -202,6 +205,7 @@ extern "C" fn screen_intro(
|
||||
vendor_str: *const cty::c_char,
|
||||
vendor_str_len: u8,
|
||||
version: *const cty::c_char,
|
||||
fw_ok: bool,
|
||||
) -> u32 {
|
||||
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
||||
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(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)
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,11 @@ use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
constant::screen,
|
||||
display::Icon,
|
||||
geometry::{Insets, Point, Rect},
|
||||
geometry::{Alignment, Insets, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld, button_bld_menu, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_TITLE, TITLE_AREA,
|
||||
CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_TITLE, TEXT_WARNING, TITLE_AREA,
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked},
|
||||
constant::WIDTH,
|
||||
@ -26,10 +26,11 @@ pub struct Intro<'a> {
|
||||
menu: Child<Button<&'static str>>,
|
||||
host: Child<Button<&'static str>>,
|
||||
text: Child<Label<&'a str>>,
|
||||
warn: Option<Child<Label<&'a str>>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
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())),
|
||||
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(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
||||
));
|
||||
self.text.place(Rect::new(
|
||||
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING),
|
||||
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
||||
));
|
||||
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(
|
||||
Point::new(CONTENT_PADDING, TITLE_AREA.y1 + CONTENT_PADDING),
|
||||
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
||||
));
|
||||
}
|
||||
|
||||
bounds
|
||||
}
|
||||
|
||||
@ -77,6 +97,7 @@ impl<'a> Component for Intro<'a> {
|
||||
self.bg.paint();
|
||||
self.title.paint();
|
||||
self.text.paint();
|
||||
self.warn.paint();
|
||||
self.host.paint();
|
||||
self.menu.paint();
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
constant::{screen, WIDTH},
|
||||
display::Icon,
|
||||
geometry::{Insets, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld, button_bld_menu, BLD_BG, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, FIRE24, REFRESH24, TEXT_TITLE,
|
||||
TITLE_AREA, X32,
|
||||
use crate::{
|
||||
trezorhal::secbool::{secbool, sectrue},
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
constant::{screen, WIDTH},
|
||||
display::Icon,
|
||||
geometry::{Insets, Point, Rect},
|
||||
model_tt::{
|
||||
bootloader::theme::{
|
||||
button_bld, button_bld_menu, BLD_BG, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, FIRE24, REFRESH24, TEXT_TITLE,
|
||||
TITLE_AREA, X32,
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked, IconText},
|
||||
},
|
||||
component::{Button, ButtonMsg::Clicked, IconText},
|
||||
},
|
||||
};
|
||||
|
||||
@ -19,9 +22,9 @@ const BUTTON_SPACING: i16 = 8;
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, ToPrimitive)]
|
||||
pub enum MenuMsg {
|
||||
Close = 1,
|
||||
Reboot = 2,
|
||||
FactoryReset = 3,
|
||||
Close = 0xAABBCCDD,
|
||||
Reboot = 0x11223344,
|
||||
FactoryReset = 0x55667788,
|
||||
}
|
||||
|
||||
pub struct Menu {
|
||||
@ -33,7 +36,7 @@ pub struct 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_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
||||
|
||||
@ -45,7 +48,11 @@ impl Menu {
|
||||
.styled(button_bld_menu())
|
||||
.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())),
|
||||
};
|
||||
instance.bg.clear();
|
||||
|
@ -35,7 +35,7 @@ pub mod menu;
|
||||
pub mod theme;
|
||||
pub mod welcome;
|
||||
|
||||
use crate::ui::model_tt::theme::BLACK;
|
||||
use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK};
|
||||
use confirm::Confirm;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
@ -220,8 +220,8 @@ extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_menu() -> u32 {
|
||||
run(&mut Menu::new())
|
||||
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -230,6 +230,7 @@ extern "C" fn screen_intro(
|
||||
vendor_str: *const cty::c_char,
|
||||
vendor_str_len: u8,
|
||||
version: *const cty::c_char,
|
||||
fw_ok: bool,
|
||||
) -> u32 {
|
||||
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
|
||||
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(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)
|
||||
}
|
||||
|
@ -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_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_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_WARNING: TextStyle = TextStyle::new(
|
||||
Font::BOLD,
|
||||
BLD_WARN_COLOR,
|
||||
BLD_BG,
|
||||
BLD_WARN_COLOR,
|
||||
BLD_WARN_COLOR,
|
||||
);
|
||||
pub const TEXT_FINGERPRINT: TextStyle =
|
||||
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG)
|
||||
.with_line_breaking(BreakWordsNoHyphen);
|
||||
|
Loading…
Reference in New Issue
Block a user