1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-20 11:32:04 +00:00

feat(core/rust): bootloader implementation in rust

This commit is contained in:
tychovrahe 2022-05-05 13:47:19 +02:00
parent a5dd838883
commit c8871aac1a
55 changed files with 1853 additions and 355 deletions

View File

@ -0,0 +1 @@
Error screens redesign

View File

@ -29,10 +29,10 @@ if TREZOR_MODEL in ('1', 'R'):
FONT_BOLD=None
FONT_MONO='Font_PixelOperatorMono_Regular_8'
if TREZOR_MODEL in ('T', ):
FONT_NORMAL='Font_Roboto_Regular_20'
FONT_NORMAL='Font_TTHoves_Regular_18'
FONT_DEMIBOLD=None
FONT_BOLD=None
FONT_MONO='Font_RobotoMono_Regular_20'
FONT_BOLD='Font_TTHoves_Bold_16'
FONT_MONO=None
# modtrezorcrypto
CCFLAGS_MOD += '-Wno-sequence-point '
@ -44,6 +44,9 @@ CPPDEFINES_MOD += [
'AES_192',
'USE_KECCAK',
'ED25519_NO_PRECOMP',
'TREZOR_UI2',
'USE_RUST_LOADER',
'FANCY_FATAL_ERROR'
]
SOURCE_MOD += [
'vendor/trezor-crypto/blake2s.c',

View File

@ -194,8 +194,12 @@ SOURCE_MOD += [
if UI2:
CPPDEFINES_MOD += [
'TREZOR_UI2',
'USE_RUST_LOADER'
'USE_RUST_LOADER',
]
if TREZOR_MODEL not in ('1', ):
CPPDEFINES_MOD += [
'FANCY_FATAL_ERROR',
]
# modtrezorutils
SOURCE_MOD += [

View File

@ -190,8 +190,12 @@ SOURCE_MOD += [
if UI2:
CPPDEFINES_MOD += [
'TREZOR_UI2',
'USE_RUST_LOADER'
'USE_RUST_LOADER',
]
if TREZOR_MODEL not in ('1', ):
CPPDEFINES_MOD += [
'FANCY_FATAL_ERROR',
]
if FROZEN:
CPPDEFINES_MOD += ['TREZOR_EMULATOR_FROZEN']
if RASPI:

BIN
core/assets/erase_big.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
core/assets/success_bld.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
core/assets/warn_bld.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1 @@
Bootloader redesign

View File

@ -34,6 +34,8 @@
#include "mini_printf.h"
#include "version.h"
#include "rust_ui.h"
#if defined TREZOR_MODEL_T
#include "touch.h"
#elif defined TREZOR_MODEL_R
@ -64,29 +66,26 @@
// common shared functions
static void ui_confirm_cancel_buttons(void) {
display_bar_radius(9, 184, 108, 50, COLOR_BL_FAIL, COLOR_BL_BG, 4);
display_icon(9 + (108 - 16) / 2, 184 + (50 - 16) / 2, 16, 16,
toi_icon_cancel + 12, sizeof(toi_icon_cancel) - 12, COLOR_BL_BG,
COLOR_BL_FAIL);
display_bar_radius(123, 184, 108, 50, COLOR_BL_DONE, COLOR_BL_BG, 4);
display_icon(123 + (108 - 19) / 2, 184 + (50 - 16) / 2, 20, 16,
toi_icon_confirm + 12, sizeof(toi_icon_confirm) - 12,
COLOR_BL_BG, COLOR_BL_DONE);
static void format_ver_bfr(const char *format, uint32_t version, char *bfr,
size_t bfr_len) {
mini_snprintf(bfr, bfr_len, format, (int)(version & 0xFF),
(int)((version >> 8) & 0xFF), (int)((version >> 16) & 0xFF)
// ignore build field (int)((version >> 24) & 0xFF)
);
}
static const char *format_ver(const char *format, uint32_t version) {
static char ver_str[64];
mini_snprintf(ver_str, sizeof(ver_str), format, (int)(version & 0xFF),
(int)((version >> 8) & 0xFF), (int)((version >> 16) & 0xFF)
// ignore build field (int)((version >> 24) & 0xFF)
);
format_ver_bfr(format, version, ver_str, sizeof(ver_str));
return ver_str;
}
// boot UI
static uint16_t boot_background;
static bool initial_setup = true;
void ui_set_initial_setup(bool initial) { initial_setup = initial; }
void ui_screen_boot(const vendor_header *const vhdr,
const image_header *const hdr) {
@ -159,160 +158,77 @@ void ui_screen_welcome_third(void) {
COLOR_WELCOME_FG, COLOR_WELCOME_BG);
}
// info UI
static int display_vendor_string(const char *text, int textlen,
uint16_t fgcolor) {
int split = display_text_split(text, textlen, FONT_NORMAL, DISPLAY_RESX - 55);
if (split >= textlen) {
display_text(55, 95, text, textlen, FONT_NORMAL, fgcolor, COLOR_BL_BG);
return 120;
} else {
display_text(55, 95, text, split, FONT_NORMAL, fgcolor, COLOR_BL_BG);
if (text[split] == ' ') {
split++;
}
display_text(55, 120, text + split, textlen - split, FONT_NORMAL, fgcolor,
COLOR_BL_BG);
return 145;
}
uint32_t ui_screen_intro(const vendor_header *const vhdr,
const image_header *const hdr) {
char bld_ver[32];
format_ver_bfr("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
const char *ver_str = format_ver("Firmware %d.%d.%d by", hdr->version);
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str);
}
void ui_screen_firmware_info(const vendor_header *const vhdr,
const image_header *const hdr) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
const char *ver_str = format_ver("Bootloader %d.%d.%d", VERSION_UINT32);
display_text(16, 32, ver_str, -1, FONT_NORMAL, COLOR_BL_FG, COLOR_BL_BG);
display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG);
display_icon(16, 54, 32, 32, toi_icon_info + 12, sizeof(toi_icon_info) - 12,
COLOR_BL_GRAY, COLOR_BL_BG);
if (vhdr && hdr) {
ver_str = format_ver("Firmware %d.%d.%d by", (hdr->version));
display_text(55, 70, ver_str, -1, FONT_NORMAL, COLOR_BL_GRAY, COLOR_BL_BG);
display_vendor_string(vhdr->vstr, vhdr->vstr_len, COLOR_BL_GRAY);
} else {
display_text(55, 70, "No Firmware", -1, FONT_NORMAL, COLOR_BL_GRAY,
COLOR_BL_BG);
}
display_text_center(120, 220, "Go to trezor.io/start", -1, FONT_NORMAL,
COLOR_BL_FG, COLOR_BL_BG);
uint32_t ui_screen_menu(void) {
char bld_ver[32];
format_ver_bfr("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
return screen_menu(bld_ver);
}
void ui_screen_firmware_fingerprint(const image_header *const hdr) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_text(16, 32, "Firmware fingerprint", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG);
uint32_t ui_screen_firmware_fingerprint(const image_header *const hdr) {
static const char *hexdigits = "0123456789abcdef";
char fingerprint_str[64];
char fingerprint_str[65];
for (int i = 0; i < 32; i++) {
fingerprint_str[i * 2] = hexdigits[(hdr->fingerprint[i] >> 4) & 0xF];
fingerprint_str[i * 2 + 1] = hexdigits[hdr->fingerprint[i] & 0xF];
}
for (int i = 0; i < 4; i++) {
display_text_center(120, 70 + i * 25, fingerprint_str + i * 16, 16,
FONT_MONO, COLOR_BL_FG, COLOR_BL_BG);
}
display_bar_radius(9, 184, 222, 50, COLOR_BL_DONE, COLOR_BL_BG, 4);
display_icon(9 + (222 - 19) / 2, 184 + (50 - 16) / 2, 20, 16,
toi_icon_confirm + 12, sizeof(toi_icon_confirm) - 12,
COLOR_BL_BG, COLOR_BL_DONE);
fingerprint_str[64] = 0;
return screen_fwinfo(fingerprint_str);
}
// install UI
void ui_screen_install_confirm_upgrade(const vendor_header *const vhdr,
const image_header *const hdr) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_text(16, 32, "Firmware update", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG);
display_icon(16, 54, 32, 32, toi_icon_info + 12, sizeof(toi_icon_info) - 12,
COLOR_BL_FG, COLOR_BL_BG);
display_text(55, 70, "Update firmware by", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
int next_y = display_vendor_string(vhdr->vstr, vhdr->vstr_len, COLOR_BL_FG);
uint32_t ui_screen_install_confirm_upgrade(const vendor_header *const vhdr,
const image_header *const hdr) {
const char *ver_str = format_ver("to version %d.%d.%d?", hdr->version);
display_text(55, next_y, ver_str, -1, FONT_NORMAL, COLOR_BL_FG, COLOR_BL_BG);
ui_confirm_cancel_buttons();
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str, false,
false);
}
void ui_screen_install_confirm_newvendor_or_downgrade_wipe(
uint32_t ui_screen_install_confirm_newvendor_or_downgrade_wipe(
const vendor_header *const vhdr, const image_header *const hdr,
secbool downgrade_wipe) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_text(
16, 32,
(sectrue == downgrade_wipe) ? "Firmware downgrade" : "Vendor change", -1,
FONT_NORMAL, COLOR_BL_FG, COLOR_BL_BG);
display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG);
display_icon(16, 54, 32, 32, toi_icon_info + 12, sizeof(toi_icon_info) - 12,
COLOR_BL_FG, COLOR_BL_BG);
display_text(55, 70, "Install firmware by", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
int next_y = display_vendor_string(vhdr->vstr, vhdr->vstr_len, COLOR_BL_FG);
const char *ver_str = format_ver("(version %d.%d.%d)?", hdr->version);
display_text(55, next_y, ver_str, -1, FONT_NORMAL, COLOR_BL_FG, COLOR_BL_BG);
display_text_center(120, 170, "Seed will be erased!", -1, FONT_NORMAL,
COLOR_BL_FAIL, COLOR_BL_BG);
ui_confirm_cancel_buttons();
if (downgrade_wipe) {
const char *ver_str = format_ver("to version %d.%d.%d?", hdr->version);
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str, true,
false);
} else {
const char *ver_str = format_ver("version %d.%d.%d?", hdr->version);
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str, false,
true);
}
}
void ui_screen_install_start(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_loader(0, false, -20, COLOR_BL_PROCESS, COLOR_BL_BG, toi_icon_install,
sizeof(toi_icon_install), COLOR_BL_FG);
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24,
"Installing firmware", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
void ui_screen_install_start() {
screen_install_progress(0, true, initial_setup);
}
void ui_screen_install_progress_erase(int pos, int len) {
display_loader(250 * pos / len, false, -20, COLOR_BL_PROCESS, COLOR_BL_BG,
toi_icon_install, sizeof(toi_icon_install), COLOR_BL_FG);
screen_install_progress(250 * pos / len, false, initial_setup);
}
void ui_screen_install_progress_upload(int pos) {
display_loader(pos, false, -20, COLOR_BL_PROCESS, COLOR_BL_BG,
toi_icon_install, sizeof(toi_icon_install), COLOR_BL_FG);
screen_install_progress(pos, false, initial_setup);
}
// wipe UI
void ui_screen_wipe_confirm(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_text(16, 32, "Wipe device", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
display_bar(16, 44, DISPLAY_RESX - 14 * 2, 1, COLOR_BL_FG);
display_icon(16, 54, 32, 32, toi_icon_info + 12, sizeof(toi_icon_info) - 12,
COLOR_BL_FG, COLOR_BL_BG);
display_text(55, 70, "Do you want to", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
display_text(55, 95, "wipe the device?", -1, FONT_NORMAL, COLOR_BL_FG,
COLOR_BL_BG);
uint32_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); }
display_text_center(120, 170, "Seed will be erased!", -1, FONT_NORMAL,
COLOR_BL_FAIL, COLOR_BL_BG);
ui_confirm_cancel_buttons();
}
void ui_screen_wipe(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_loader(0, false, -20, COLOR_BL_PROCESS, COLOR_BL_BG, toi_icon_wipe,
sizeof(toi_icon_wipe), COLOR_BL_FG);
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Wiping device", -1,
FONT_NORMAL, COLOR_BL_FG, COLOR_BL_BG);
}
void ui_screen_wipe(void) { screen_wipe_progress(0, true); }
void ui_screen_wipe_progress(int pos, int len) {
display_loader(1000 * pos / len, false, -20, COLOR_BL_PROCESS, COLOR_BL_BG,
toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BL_FG);
screen_wipe_progress(1000 * pos / len, false);
}
// done UI
void ui_screen_done(int restart_seconds, secbool full_redraw) {
const char *str;
char count_str[24];
@ -323,28 +239,18 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) {
} else {
str = "Done! Unplug the device.";
}
if (sectrue == full_redraw) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
}
display_loader(1000, false, -20, COLOR_BL_DONE, COLOR_BL_BG, toi_icon_done,
sizeof(toi_icon_done), COLOR_BL_FG);
if (secfalse == full_redraw) {
display_bar(0, DISPLAY_RESY - 24 - 18, 240, 23, COLOR_BL_BG);
}
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, str, -1, FONT_NORMAL,
COLOR_BL_FG, COLOR_BL_BG);
screen_install_success(str, initial_setup, full_redraw);
}
void ui_screen_boot_empty() {
screen_boot_empty();
display_refresh();
display_backlight(BACKLIGHT_NORMAL);
}
// error UI
void ui_screen_fail(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_BL_BG);
display_loader(1000, false, -20, COLOR_BL_FAIL, COLOR_BL_BG, toi_icon_fail,
sizeof(toi_icon_fail), COLOR_BL_FG);
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24,
"Failed! Please, reconnect.", -1, FONT_NORMAL,
COLOR_BL_FG, COLOR_BL_BG);
}
void ui_screen_fail(void) { screen_install_fail(); }
// general functions
@ -354,43 +260,3 @@ void ui_fadeout(void) {
display_fade(BACKLIGHT_NORMAL, 0, 500);
display_clear();
}
int ui_user_input(int zones) {
for (;;) {
#if defined TREZOR_MODEL_T
uint32_t evt = touch_click();
uint16_t x = touch_unpack_x(evt);
uint16_t y = touch_unpack_y(evt);
// clicked on Cancel button
if ((zones & INPUT_CANCEL) && x >= 9 && x < 9 + 108 && y > 184 &&
y < 184 + 50) {
return INPUT_CANCEL;
}
// clicked on Confirm button
if ((zones & INPUT_CONFIRM) && x >= 123 && x < 123 + 108 && y > 184 &&
y < 184 + 50) {
return INPUT_CONFIRM;
}
// clicked on Long Confirm button
if ((zones & INPUT_LONG_CONFIRM) && x >= 9 && x < 9 + 222 && y > 184 &&
y < 184 + 50) {
return INPUT_LONG_CONFIRM;
}
// clicked on Info icon
if ((zones & INPUT_INFO) && x >= 16 && x < 16 + 32 && y > 54 &&
y < 54 + 32) {
return INPUT_INFO;
}
#elif defined TREZOR_MODEL_R
uint32_t evt = button_read();
if (evt == (BTN_LEFT | BTN_EVT_DOWN)) {
return INPUT_CANCEL;
}
if (evt == (BTN_RIGHT | BTN_EVT_DOWN)) {
return INPUT_CONFIRM;
}
#else
#error Unknown Trezor model
#endif
}
}

View File

@ -22,6 +22,15 @@
#include "image.h"
#include "secbool.h"
#include "stdbool.h"
typedef enum {
SCREEN_INTRO = 0,
SCREEN_MENU = 1,
SCREEN_WIPE_CONFIRM = 2,
SCREEN_FINGER_PRINT = 3,
SCREEN_WAIT_FOR_HOST = 4,
} screen_t;
void ui_screen_boot(const vendor_header* const vhdr,
const image_header* const hdr);
@ -32,20 +41,23 @@ void ui_screen_welcome_first(void);
void ui_screen_welcome_second(void);
void ui_screen_welcome_third(void);
void ui_screen_firmware_info(const vendor_header* const vhdr,
const image_header* const hdr);
void ui_screen_firmware_fingerprint(const image_header* const hdr);
uint32_t ui_screen_intro(const vendor_header* const vhdr,
const image_header* const hdr);
void ui_screen_install_confirm_upgrade(const vendor_header* const vhdr,
const image_header* const hdr);
void ui_screen_install_confirm_newvendor_or_downgrade_wipe(
uint32_t ui_screen_menu(void);
uint32_t ui_screen_firmware_fingerprint(const image_header* const hdr);
uint32_t ui_screen_install_confirm_upgrade(const vendor_header* const vhdr,
const image_header* const hdr);
uint32_t ui_screen_install_confirm_newvendor_or_downgrade_wipe(
const vendor_header* const vhdr, const image_header* const hdr,
secbool downgrade_wipe);
void ui_screen_install_start(void);
void ui_screen_install_start();
void ui_screen_install_progress_erase(int pos, int len);
void ui_screen_install_progress_upload(int pos);
void ui_screen_wipe_confirm(void);
uint32_t ui_screen_wipe_confirm(void);
void ui_screen_wipe(void);
void ui_screen_wipe_progress(int pos, int len);
@ -55,6 +67,9 @@ void ui_screen_fail(void);
void ui_fadein(void);
void ui_fadeout(void);
void ui_set_initial_setup(bool initial);
void ui_screen_boot_empty();
// clang-format off
#define INPUT_CANCEL 0x01 // Cancel button

View File

@ -42,7 +42,7 @@
#include "bootui.h"
#include "messages.h"
// #include "mpu.h"
#include "rust_ui.h"
const uint8_t BOOTLOADER_KEY_M = 2;
const uint8_t BOOTLOADER_KEY_N = 3;
@ -94,8 +94,8 @@ static void usb_init_all(secbool usb21_landing) {
usb_start();
}
static secbool bootloader_usb_loop(const vendor_header *const vhdr,
const image_header *const hdr) {
secbool bootloader_usb_loop(const vendor_header *const vhdr,
const image_header *const hdr) {
// if both are NULL, we don't have a firmware installed
// let's show a webusb landing page in this case
usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
@ -123,13 +123,10 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
break;
case 5: // WipeDevice
ui_fadeout();
ui_screen_wipe_confirm();
ui_fadein();
int response = ui_user_input(INPUT_CONFIRM | INPUT_CANCEL);
uint32_t response = ui_screen_wipe_confirm();
if (INPUT_CANCEL == response) {
ui_fadeout();
ui_screen_firmware_info(vhdr, hdr);
ui_fadein();
screen_connect();
send_user_abort(USB_IFACE_NUM, "Wipe cancelled");
break;
}
@ -139,14 +136,14 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
if (r < 0) { // error
ui_fadeout();
ui_screen_fail();
screen_wipe_fail();
ui_fadein();
usb_stop();
usb_deinit();
return secfalse; // shutdown
} else { // success
ui_fadeout();
ui_screen_done(0, sectrue);
screen_wipe_success();
ui_fadein();
usb_stop();
usb_deinit();
@ -179,6 +176,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
usb_stop();
usb_deinit();
ui_fadeout();
ui_screen_boot_empty();
return sectrue; // jump to firmware
}
break;
@ -198,7 +196,7 @@ secbool load_vendor_header_keys(const uint8_t *const data,
BOOTLOADER_KEYS, vhdr);
}
static secbool check_vendor_header_lock(const vendor_header *const vhdr) {
secbool check_vendor_header_lock(const vendor_header *const vhdr) {
uint8_t lock[FLASH_OTP_BLOCK_SIZE];
ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock,
FLASH_OTP_BLOCK_SIZE),
@ -256,6 +254,11 @@ int main(void) {
#if defined TREZOR_MODEL_T
display_set_little_endian();
#endif
ui_screen_boot_empty();
#if defined TREZOR_MODEL_T
touch_power_on();
touch_init();
#endif
@ -271,8 +274,6 @@ int main(void) {
check_bootloader_version();
#endif
display_clear();
// was there reboot with request to stay in bootloader?
secbool stay_in_bootloader = secfalse;
if (stay_in_bootloader_flag == STAY_IN_BOOTLOADER_FLAG) {
@ -301,7 +302,7 @@ int main(void) {
vendor_header vhdr;
image_header hdr;
// detect whether the devices contains a valid firmware
// detect whether the device contains a valid firmware
secbool firmware_present =
load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr);
@ -321,9 +322,14 @@ int main(void) {
// start the bootloader if no or broken firmware found ...
if (firmware_present != sectrue) {
// ignore stay in bootloader
stay_in_bootloader = secfalse;
// show intro animation
// no ui_fadeout(); - we already start from black screen
ui_set_initial_setup(true);
ui_fadeout();
ui_screen_welcome_first();
ui_fadein();
@ -349,16 +355,88 @@ int main(void) {
}
}
ensure(load_vendor_header_keys((const uint8_t *)FIRMWARE_START, &vhdr),
"invalid vendor header");
ensure(load_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen),
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE,
vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub, &hdr),
"invalid firmware header");
// ... or if user touched the screen on start
// ... or we have stay_in_bootloader flag to force it
if (touched || stay_in_bootloader == sectrue) {
// no ui_fadeout(); - we already start from black screen
ui_screen_firmware_info(&vhdr, &hdr);
ui_fadein();
ui_set_initial_setup(false);
// and start the usb loop
if (bootloader_usb_loop(&vhdr, &hdr) != sectrue) {
return 1;
screen_t screen = SCREEN_INTRO;
while (true) {
bool continue_to_firmware = false;
uint32_t ui_result = 0;
switch (screen) {
case SCREEN_INTRO:
ui_result = ui_screen_intro(&vhdr, &hdr);
if (ui_result == 1) {
screen = SCREEN_MENU;
}
if (ui_result == 2) {
screen = SCREEN_WAIT_FOR_HOST;
}
break;
case SCREEN_MENU:
ui_result = ui_screen_menu();
if (ui_result == 1) { // exit menu
screen = SCREEN_INTRO;
}
if (ui_result == 2) { // reboot
ui_fadeout();
ui_screen_boot_empty();
continue_to_firmware = true;
}
if (ui_result == 3) { // wipe
screen = SCREEN_WIPE_CONFIRM;
}
break;
case SCREEN_WIPE_CONFIRM:
ui_result = screen_wipe_confirm();
if (ui_result == INPUT_CANCEL) {
// canceled
screen = SCREEN_MENU;
}
if (ui_result == INPUT_CONFIRM) {
ui_fadeout();
ui_screen_wipe();
ui_fadein();
secbool r = bootloader_WipeDevice();
if (r != sectrue) { // error
ui_fadeout();
screen_wipe_fail();
ui_fadein();
return 1;
} else { // success
ui_fadeout();
screen_wipe_success();
ui_fadein();
return 1;
}
}
break;
case SCREEN_WAIT_FOR_HOST:
screen_connect();
if (sectrue == bootloader_usb_loop(&vhdr, &hdr)) {
continue_to_firmware = true;
} else {
return 1;
}
default:
break;
}
if (continue_to_firmware) {
break;
} else {
ui_fadeout();
}
}
}
@ -379,7 +457,7 @@ int main(void) {
// if all VTRUST flags are unset = ultimate trust => skip the procedure
if ((vhdr.vtrust & VTRUST_ALL) != VTRUST_ALL) {
// ui_fadeout(); // no fadeout - we start from black screen
ui_fadeout();
ui_screen_boot(&vhdr, &hdr);
ui_fadein();
@ -429,3 +507,5 @@ int main(void) {
return 0;
}
void HardFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(HF)"); }

View File

@ -33,6 +33,7 @@
#include "bootui.h"
#include "messages.h"
#include "rust_ui.h"
#include "memzero.h"
@ -199,8 +200,8 @@ static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) {
// only timeout => let's try again
} else {
// error
error_shutdown("Error reading", "from USB.", "Try different",
"USB cable.");
error_shutdown("USB ERROR",
"Error reading from USB. Try different USB cable.");
}
}
return; // success
@ -507,33 +508,32 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
detect_installation(&current_vhdr, &current_hdr, &vhdr, &hdr, &is_new,
&is_upgrade, &is_downgrade_wipe);
int response = INPUT_CANCEL;
uint32_t response = INPUT_CANCEL;
if (sectrue == is_new) {
// new installation - auto confirm
response = INPUT_CONFIRM;
} else if (sectrue == is_upgrade) {
// firmware upgrade
ui_fadeout();
ui_screen_install_confirm_upgrade(&vhdr, &hdr);
ui_fadein();
response = ui_user_input(INPUT_CONFIRM | INPUT_CANCEL);
response = ui_screen_install_confirm_upgrade(&vhdr, &hdr);
} else {
// downgrade with wipe or new firmware vendor
ui_fadeout();
ui_screen_install_confirm_newvendor_or_downgrade_wipe(
response = ui_screen_install_confirm_newvendor_or_downgrade_wipe(
&vhdr, &hdr, is_downgrade_wipe);
ui_fadein();
response = ui_user_input(INPUT_CONFIRM | INPUT_CANCEL);
}
if (INPUT_CANCEL == response) {
ui_fadeout();
ui_screen_firmware_info(&current_vhdr, &current_hdr);
ui_fadein();
screen_connect();
send_user_abort(iface_num, "Firmware install cancelled");
return -4;
}
ui_fadeout();
ui_screen_install_start();
ui_fadein();
headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
read_offset = IMAGE_INIT_CHUNK_SIZE;
@ -549,11 +549,6 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
} else {
// first block with the headers parsed -> the first chunk is now complete
read_offset = 0;
ui_fadeout();
ui_screen_install_start();
ui_fadein();
// if firmware is not upgrade, erase storage
if (sectrue != is_upgrade) {
ensure(
@ -625,7 +620,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
return (int)firmware_remaining;
}
int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
secbool bootloader_WipeDevice(void) {
static const uint8_t sectors[] = {
FLASH_SECTOR_STORAGE_1,
FLASH_SECTOR_STORAGE_2,
@ -648,8 +643,12 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
22,
FLASH_SECTOR_FIRMWARE_EXTRA_END,
};
if (sectrue !=
flash_erase_sectors(sectors, sizeof(sectors), ui_screen_wipe_progress)) {
return flash_erase_sectors(sectors, sizeof(sectors), ui_screen_wipe_progress);
}
int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
int wipe_result = bootloader_WipeDevice();
if (sectrue != wipe_result) {
MSG_SEND_INIT(Failure);
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
MSG_SEND_ASSIGN_STRING(message, "Could not erase flash");

View File

@ -48,4 +48,6 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf);
void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf);
secbool bootloader_WipeDevice(void);
#endif

View File

@ -0,0 +1 @@
Bootloader redesign

View File

@ -199,8 +199,8 @@ static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) {
// only timeout => let's try again
} else {
// error
error_shutdown("Error reading", "from USB.", "Try different",
"USB cable.");
error_shutdown("USB ERROR",
"Error reading from USB. Try different USB cable.");
}
}
return; // success

View File

@ -176,7 +176,7 @@ int main(void) {
// MicroPython default exception handler
void __attribute__((noreturn)) nlr_jump_fail(void *val) {
error_shutdown("Internal error", "(UE)", NULL, NULL);
error_shutdown("INTERNAL ERROR!", "(UE)");
}
// interrupt handlers
@ -184,29 +184,19 @@ void __attribute__((noreturn)) nlr_jump_fail(void *val) {
void NMI_Handler(void) {
// Clock Security System triggered NMI
if ((RCC->CIR & RCC_CIR_CSSF) != 0) {
error_shutdown("Internal error", "(CS)", NULL, NULL);
error_shutdown("INTERNAL ERROR!", "(CS)");
}
}
void HardFault_Handler(void) {
error_shutdown("Internal error", "(HF)", NULL, NULL);
}
void HardFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(HF)"); }
void MemManage_Handler_MM(void) {
error_shutdown("Internal error", "(MM)", NULL, NULL);
}
void MemManage_Handler_MM(void) { error_shutdown("INTERNAL ERROR!", "(MM)"); }
void MemManage_Handler_SO(void) {
error_shutdown("Internal error", "(SO)", NULL, NULL);
}
void MemManage_Handler_SO(void) { error_shutdown("INTERNAL ERROR!", "(SO)"); }
void BusFault_Handler(void) {
error_shutdown("Internal error", "(BF)", NULL, NULL);
}
void BusFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(BF)"); }
void UsageFault_Handler(void) {
error_shutdown("Internal error", "(UF)", NULL, NULL);
}
void UsageFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(UF)"); }
__attribute__((noreturn)) void reboot_to_bootloader() {
jump_to_with_flag(BOOTLOADER_START + IMAGE_HEADER_SIZE,

View File

@ -4,3 +4,24 @@ void loader_uncompress_r(int32_t y_offset, uint16_t fg_color, uint16_t bg_color,
uint16_t icon_color, int32_t progress,
int32_t indeterminate, const uint8_t* icon_data,
uint32_t icon_data_size);
uint32_t screen_install_confirm(const char* vendor_str, uint8_t vendor_str_len,
const char* version_str, bool downgrade,
bool vendor);
uint32_t screen_wipe_confirm(void);
uint32_t screen_install_progress(int16_t progress, bool initialize,
bool initial_setup);
uint32_t 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(const char* bld_version_str);
uint32_t screen_connect(void);
uint32_t screen_fwinfo(const char* fingerprint);
uint32_t screen_fatal_error(const char* msg, const char* file);
uint32_t screen_error_shutdown(const char* label, const char* msg);
uint32_t screen_wipe_success(void);
uint32_t screen_wipe_fail(void);
uint32_t screen_install_success(const char* reboot_msg, bool initial_setup,
bool complete_draw);
uint32_t screen_install_fail(void);
void screen_boot_empty(void);

View File

@ -1,12 +1,11 @@
use core::{
convert::{Infallible, TryInto},
num::TryFromIntError,
};
use cstr_core::CStr;
use core::{convert::Infallible, num::TryFromIntError};
#[cfg(feature = "micropython")]
use crate::micropython::{ffi, obj::Obj, qstr::Qstr};
use {
crate::micropython::{ffi, obj::Obj, qstr::Qstr},
core::convert::TryInto,
cstr_core::CStr,
};
#[allow(clippy::enum_variant_names)] // We mimic the Python exception classnames here.
#[derive(Clone, Copy, Debug)]

View File

@ -200,7 +200,7 @@ pub fn toif_info(data: &[u8]) -> Option<(Offset, ToifFormat)> {
}
}
/// Aborts if the TOIF file does not have the correct grayscale flag, do not use
/// Aborts if the TOIF file does not have the correct format, do not use
/// with user-supplied inputs.
fn toif_info_ensure(data: &[u8], format: ToifFormat) -> (Offset, &[u8]) {
let info = unwrap!(display::toif_info(data), "Invalid TOIF data");
@ -951,6 +951,19 @@ pub fn text_right(baseline: Point, text: &str, font: Font, fg_color: Color, bg_c
);
}
pub fn text_top_left(position: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
// let w = font.text_width(text);
let h = font.text_height();
display::text(
position.x,
position.y + h,
text,
font.into(),
fg_color.into(),
bg_color.into(),
);
}
#[inline(always)]
pub fn pixeldata(color: Color) {
display::pixeldata(color.into());

View File

@ -352,6 +352,15 @@ impl Rect {
self.inset(Insets::uniform(size))
}
pub const fn outset(&self, insets: Insets) -> Self {
Self {
x0: self.x0 - insets.left,
y0: self.y0 - insets.top,
x1: self.x1 + insets.right,
y1: self.y1 + insets.bottom,
}
}
/// Leave just the left side of a certain `width`.
pub const fn cut_from_left(&self, width: i16) -> Self {
Self {

View File

@ -0,0 +1,123 @@
use crate::ui::{
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Pad},
constant::screen,
display,
display::Color,
geometry::{Offset, Point, Rect},
model_tt::{
component::{Button, ButtonMsg::Clicked},
constant::{HEIGHT, WIDTH},
theme::WHITE,
},
};
use super::ReturnToC;
#[derive(Copy, Clone)]
pub enum ConfirmMsg {
Cancel = 1,
Confirm = 2,
}
impl ReturnToC for ConfirmMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Confirm {
bg: Pad,
bg_color: Color,
icon: Option<&'static [u8]>,
message: Child<Paragraphs<&'static str>>,
left: Child<Button<&'static str>>,
right: Child<Button<&'static str>>,
confirm_left: bool,
}
impl Confirm {
pub fn new(
bg_color: Color,
icon: Option<&'static [u8]>,
message: Paragraphs<&'static str>,
left: Button<&'static str>,
right: Button<&'static str>,
confirm_left: bool,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(bg_color),
bg_color,
icon,
message: Child::new(message),
left: Child::new(left),
right: Child::new(right),
confirm_left,
};
instance.bg.clear();
instance
}
}
impl Component for Confirm {
type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.message.place(Rect::new(
Point::new(15, 59),
Point::new(WIDTH - 15, HEIGHT - 64),
));
let button_size = Offset::new(102, 48);
self.left.place(Rect::from_top_left_and_size(
Point::new(15, 176),
button_size,
));
self.right.place(Rect::from_top_left_and_size(
Point::new(123, 176),
button_size,
));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.left.event(ctx, event) {
return if self.confirm_left {
Some(Self::Msg::Confirm)
} else {
Some(Self::Msg::Cancel)
};
};
if let Some(Clicked) = self.right.event(ctx, event) {
return if self.confirm_left {
Some(Self::Msg::Cancel)
} else {
Some(Self::Msg::Confirm)
};
};
None
}
fn paint(&mut self) {
self.bg.paint();
if let Some(icon) = self.icon {
display::icon(
Point::new(screen().center().x, 45),
icon,
WHITE,
self.bg_color,
);
}
self.message.paint();
self.left.paint();
self.right.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.left.bounds(sink);
self.right.bounds(sink);
}
}

View File

@ -0,0 +1,55 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never, Pad},
constant::screen,
display::{self, Font},
geometry::{Offset, Point, Rect},
model_tt::bootloader::theme::{BLD_BG, BLD_TITLE_COLOR},
};
use crate::ui::model_tt::constant::{HEIGHT, WIDTH};
pub struct Connect {
bg: Pad,
message: &'static str,
}
impl Connect {
pub fn new(message: &'static str) -> Self {
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
message,
};
instance.bg.clear();
instance
}
}
impl Component for Connect {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
let font = Font::NORMAL;
self.bg.paint();
display::text_center(
screen().center() + Offset::y(font.text_height() / 2),
self.message,
Font::NORMAL,
BLD_TITLE_COLOR,
BLD_BG,
);
}
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}

View File

@ -0,0 +1,78 @@
use crate::ui::{
component::{Child, Component, Event, EventCtx, Pad},
display::{self, Font},
geometry::{Point, Rect},
model_tt::{
bootloader::theme::{button_bld_menu_item, BLD_BG, BLD_FG, BLD_TITLE_COLOR},
component::{Button, ButtonMsg::Clicked},
},
};
use crate::ui::model_tt::constant::{HEIGHT, WIDTH};
pub struct FwInfo {
bg: Pad,
message: &'static str,
fingerprint: &'static str,
exit: Child<Button<&'static str>>,
}
impl FwInfo {
pub fn new(fingerprint: &'static str) -> Self {
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
message: "FIRMWARE FINGERPRINT",
fingerprint,
exit: Child::new(Button::with_text("Return to menu").styled(button_bld_menu_item())),
};
instance.bg.clear();
instance
}
}
impl Component for FwInfo {
type Msg = ();
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.exit.place(Rect::new(
Point::new(16, 178),
Point::new(16 + 209, 178 + 48),
));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.exit.event(ctx, event) {
return Some(());
};
None
}
fn paint(&mut self) {
self.bg.paint();
self.exit.paint();
display::rect_fill(
Rect::new(Point::new(16, 44), Point::new(WIDTH - 12, 45)),
BLD_FG,
);
display::text_top_left(
Point::new(15, 24),
self.message,
Font::BOLD,
BLD_TITLE_COLOR,
BLD_BG,
);
for i in 0..4usize {
let ypos = (60 + i * 20) as i16;
let idx = i * 16;
let part = self.fingerprint.get(idx..idx + 16).unwrap_or("");
display::text_top_left(Point::new(15, ypos), part, Font::MEDIUM, BLD_FG, BLD_BG);
}
}
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}

View File

@ -0,0 +1,105 @@
use crate::ui::{
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Pad},
geometry::{Insets, LinearPlacement, Point, Rect},
model_tt::{
bootloader::{
theme::{button_bld_menu, button_bld_menu_item, BLD_BG, MENU, TEXT_NORMAL},
title::Title,
ReturnToC,
},
component::ButtonMsg::Clicked,
},
};
use crate::ui::model_tt::{
component::Button,
constant::{HEIGHT, WIDTH},
};
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum IntroMsg {
Menu = 1,
Host = 2,
}
impl ReturnToC for IntroMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Intro {
bg: Pad,
title: Child<Title>,
menu: Child<Button<&'static str>>,
host: Child<Button<&'static str>>,
text: Child<Paragraphs<&'static str>>,
}
impl Intro {
pub fn new(bld_version: &'static str, vendor: &'static str, version: &'static str) -> Self {
let p1 = Paragraphs::new()
.add(TEXT_NORMAL, version)
.add(TEXT_NORMAL, vendor)
.with_placement(LinearPlacement::vertical().align_at_start());
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Title::new(bld_version)),
menu: Child::new(
Button::with_icon(MENU)
.styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)),
),
host: Child::new(Button::with_text("CONNECT TO HOST").styled(button_bld_menu_item())),
text: Child::new(p1),
};
instance.bg.clear();
instance
}
}
impl Component for Intro {
type Msg = IntroMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.title
.place(Rect::new(Point::new(15, 24), Point::new(180, 40)));
self.menu.place(Rect::new(
Point::new(187, 15),
Point::new(187 + 38, 15 + 38),
));
self.host.place(Rect::new(
Point::new(16, 178),
Point::new(16 + 209, 178 + 48),
));
self.text
.place(Rect::new(Point::new(15, 75), Point::new(225, 200)));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.menu.event(ctx, event) {
return Some(Self::Msg::Menu);
};
if let Some(Clicked) = self.host.event(ctx, event) {
return Some(Self::Msg::Host);
};
None
}
fn paint(&mut self) {
self.bg.paint();
self.title.paint();
self.text.paint();
self.host.paint();
self.menu.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.menu.bounds(sink);
}
}

View File

@ -0,0 +1,109 @@
use crate::ui::{
component::{Child, Component, Event, EventCtx, Pad},
geometry::{Insets, Point, Rect},
model_tt::{
bootloader::{
theme::{button_bld_menu, button_bld_menu_item, BLD_BG, CLOSE, ERASE, REBOOT},
title::Title,
ReturnToC,
},
component::{Button, ButtonMsg::Clicked, IconText},
constant::{HEIGHT, WIDTH},
},
};
#[repr(u32)]
#[derive(Copy, Clone)]
pub enum MenuMsg {
Close = 1,
Reboot = 2,
FactoryReset = 3,
}
impl ReturnToC for MenuMsg {
fn return_to_c(self) -> u32 {
self as u32
}
}
pub struct Menu {
bg: Pad,
title: Child<Title>,
close: Child<Button<&'static str>>,
reboot: Child<Button<&'static str>>,
reset: Child<Button<&'static str>>,
}
impl Menu {
pub fn new(bld_version: &'static str) -> Self {
let content_reboot = IconText::new("REBOOT", REBOOT);
let content_reset = IconText::new("FACTORY RESET", ERASE);
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Title::new(bld_version)),
close: Child::new(
Button::with_icon(CLOSE)
.styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)),
),
reboot: Child::new(
Button::with_icon_and_text(content_reboot).styled(button_bld_menu_item()),
),
reset: Child::new(
Button::with_icon_and_text(content_reset).styled(button_bld_menu_item()),
),
};
instance.bg.clear();
instance
}
}
impl Component for Menu {
type Msg = MenuMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.title
.place(Rect::new(Point::new(15, 24), Point::new(180, 40)));
self.close.place(Rect::new(
Point::new(187, 15),
Point::new(187 + 38, 15 + 38),
));
self.reboot
.place(Rect::new(Point::new(16, 66), Point::new(16 + 209, 66 + 48)));
self.reset.place(Rect::new(
Point::new(16, 122),
Point::new(16 + 209, 122 + 48),
));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(Clicked) = self.close.event(ctx, event) {
return Some(Self::Msg::Close);
}
if let Some(Clicked) = self.reboot.event(ctx, event) {
return Some(Self::Msg::Reboot);
}
if let Some(Clicked) = self.reset.event(ctx, event) {
return Some(Self::Msg::FactoryReset);
}
None
}
fn paint(&mut self) {
self.bg.paint();
self.title.paint();
self.close.paint();
self.reboot.paint();
self.reset.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.close.bounds(sink);
self.reboot.bounds(sink);
self.reset.bounds(sink);
}
}

View File

@ -0,0 +1,421 @@
use crate::{
trezorhal::io::{io_touch_read, io_touch_unpack_x, io_touch_unpack_y},
ui::{
component::{Component, Event, EventCtx, Never},
display::{self, Font},
event::TouchEvent,
geometry::Point,
model_tt::constant,
},
};
pub mod confirm;
mod connect;
mod fwinfo;
pub mod intro;
pub mod menu;
mod theme;
mod title;
use crate::ui::{
component::text::paragraphs::Paragraphs,
display::Color,
geometry::LinearPlacement,
model_tt::{
bootloader::{
connect::Connect,
theme::{
button_install_cancel, button_install_confirm, button_wipe_cancel,
button_wipe_confirm, BLD_BG, BLD_COLOR_INITIAL_INSTALL_BG,
BLD_COLOR_INITIAL_INSTALL_SUCCESS, BLD_FG, BLD_WIPE_COLOR, ERASE_BIG, LOGO_EMPTY,
RECEIVE,
},
},
component::{Button, ResultScreen},
theme::{
BACKLIGHT_DIM, BACKLIGHT_NORMAL, BLACK, ICON_SUCCESS_SMALL, ICON_WARN_SMALL,
TEXT_ERROR_BOLD, TEXT_ERROR_NORMAL, WHITE,
},
},
util::{from_c_array, from_c_str},
};
use confirm::Confirm;
use fwinfo::FwInfo;
use intro::Intro;
use menu::Menu;
pub trait ReturnToC {
fn return_to_c(self) -> u32;
}
impl ReturnToC for Never {
fn return_to_c(self) -> u32 {
unreachable!()
}
}
impl ReturnToC for () {
fn return_to_c(self) -> u32 {
0
}
}
fn fadein() {
display::fade_backlight_duration(BACKLIGHT_NORMAL, 500);
}
fn fadeout() {
display::fade_backlight_duration(BACKLIGHT_DIM, 500);
}
fn run<F>(frame: &mut F) -> u32
where
F: Component,
F::Msg: ReturnToC,
{
frame.place(constant::screen());
frame.paint();
fadein();
loop {
let event = touch_eval();
if let Some(e) = event {
let mut ctx = EventCtx::new();
let msg = frame.event(&mut ctx, Event::Touch(e));
frame.paint();
if let Some(message) = msg {
return message.return_to_c();
}
}
}
}
fn touch_eval() -> Option<TouchEvent> {
let event = io_touch_read();
if event == 0 {
return None;
}
let event_type = event >> 24;
let x = io_touch_unpack_x(event) as u32;
let y = io_touch_unpack_y(event) as u32;
TouchEvent::new(event_type, x, y).ok()
}
#[no_mangle]
extern "C" fn screen_install_confirm(
vendor_str: *const cty::c_char,
vendor_str_len: u8,
version: *const cty::c_char,
downgrade: bool,
vendor: bool,
) -> u32 {
let text = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
let version = unwrap!(unsafe { from_c_str(version) });
const ICON: Option<&'static [u8]> = Some(RECEIVE);
let msg = if downgrade {
"Downgrade firmware by"
} else if vendor {
"Change vendor to"
} else {
"Update firmware by"
};
let mut message = Paragraphs::new()
.add(theme::TEXT_NORMAL, msg)
.centered()
.add(theme::TEXT_NORMAL, text)
.centered()
.add(theme::TEXT_NORMAL, version)
.centered();
if vendor || downgrade {
message = message
.add(theme::TEXT_BOLD, "Seed will be erased!")
.centered();
}
message = message.with_placement(LinearPlacement::vertical().align_at_center());
let left = Button::with_text("CANCEL").styled(button_install_cancel());
let right = Button::with_text("INSTALL").styled(button_install_confirm());
let mut frame = Confirm::new(BLD_BG, ICON, message, left, right, false);
run(&mut frame)
}
#[no_mangle]
extern "C" fn screen_wipe_confirm() -> u32 {
const ICON: Option<&'static [u8]> = Some(ERASE_BIG);
let message = Paragraphs::new()
.add(TEXT_ERROR_NORMAL, "Do you really want to wipe the device?")
.centered()
.add(TEXT_ERROR_BOLD, "Seed will be erased!")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let left = Button::with_text("WIPE").styled(button_wipe_confirm());
let right = Button::with_text("CANCEL").styled(button_wipe_cancel());
let mut frame = Confirm::new(BLD_WIPE_COLOR, ICON, message, left, right, true);
run(&mut frame)
}
#[no_mangle]
extern "C" fn screen_menu(bld_version: *const cty::c_char) -> u32 {
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
run(&mut Menu::new(bld_version))
}
#[no_mangle]
extern "C" fn screen_intro(
bld_version: *const cty::c_char,
vendor_str: *const cty::c_char,
vendor_str_len: u8,
version: *const cty::c_char,
) -> u32 {
display::set_backlight(0);
let vendor = unwrap!(unsafe { from_c_array(vendor_str, vendor_str_len as usize) });
let version = unwrap!(unsafe { from_c_str(version) });
let bld_version = unwrap!(unsafe { from_c_str(bld_version) });
run(&mut Intro::new(bld_version, vendor, version))
}
fn screen_progress(
text: &str,
progress: u16,
initialize: bool,
fg_color: Color,
bg_color: Color,
icon: Option<(&[u8], Color)>,
) -> u32 {
if initialize {
display::rect_fill(constant::screen(), bg_color);
}
display::text_center(
Point::new(constant::WIDTH / 2, 214),
text,
Font::NORMAL,
fg_color,
bg_color,
);
display::loader(progress, -20, fg_color, bg_color, icon);
0
}
const INITIAL_INSTALL_LOADER_COLOR: Color = Color::rgb(0x4A, 0x90, 0xE2);
#[no_mangle]
extern "C" fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) -> u32 {
let bg_color = if initial_setup { WHITE } else { BLD_BG };
let icon_color = if initial_setup { BLACK } else { BLD_FG };
let fg_color = if initial_setup {
INITIAL_INSTALL_LOADER_COLOR
} else {
BLD_FG
};
screen_progress(
"Installing firmware...",
progress,
initialize,
fg_color,
bg_color,
Some((theme::RECEIVE, icon_color)),
)
}
#[no_mangle]
extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) -> u32 {
screen_progress(
"Wiping device...",
progress,
initialize,
theme::BLD_FG,
BLD_WIPE_COLOR,
Some((theme::ERASE_BIG, theme::BLD_FG)),
)
}
#[no_mangle]
extern "C" fn screen_connect() -> u32 {
let mut frame = Connect::new("Waiting for host...");
frame.place(constant::screen());
frame.paint();
fadein();
0
}
#[no_mangle]
extern "C" fn screen_fwinfo(fingerprint: *const cty::c_char) -> u32 {
let fingerprint = unwrap!(unsafe { from_c_str(fingerprint) });
run(&mut FwInfo::new(fingerprint))
}
//
// fn screen_result(message_top: &'static str, message_bottom: &'static str,
// icon: &'static [u8], bg: Color) -> u32 {
//
// let m_top = Paragraphs::new()
// .add(
// theme::TEXT_BOLD,message_top,
// ).centered()
// .with_placement(LinearPlacement::vertical().align_at_center());
//
// let m_bottom = Paragraphs::new()
// .add(
// theme::TEXT_SUBMSG,message_bottom,
// ).centered()
// .with_placement(LinearPlacement::vertical().align_at_center());
//
// let mut frame = Result::new(bg, icon, m_top, m_bottom);
// frame.place(constant::screen());
// frame.paint();
// 0
// }
#[no_mangle]
extern "C" fn screen_wipe_success() -> u32 {
let m_top = Paragraphs::new()
.add(theme::TEXT_BOLD, "Device wiped")
.centered()
.add(theme::TEXT_BOLD, "successfully.")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let m_bottom = Paragraphs::new()
.add(theme::TEXT_SUBMSG, "PLEASE RECONNECT")
.centered()
.add(theme::TEXT_SUBMSG, "THE DEVICE")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, BLD_BG, ICON_SUCCESS_SMALL, m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
0
}
#[no_mangle]
extern "C" fn screen_wipe_fail() -> u32 {
let m_top = Paragraphs::new()
.add(theme::TEXT_BOLD, "Device wipe was")
.centered()
.add(theme::TEXT_BOLD, "not successful.")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let m_bottom = Paragraphs::new()
.add(theme::TEXT_SUBMSG, "PLEASE RECONNECT")
.centered()
.add(theme::TEXT_SUBMSG, "THE DEVICE")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, BLD_BG, ICON_WARN_SMALL, m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
0
}
#[no_mangle]
extern "C" fn screen_boot_empty() {
display::icon(constant::screen().center(), LOGO_EMPTY, WHITE, BLACK);
}
#[no_mangle]
extern "C" fn screen_install_fail() -> u32 {
let m_top = Paragraphs::new()
.add(theme::TEXT_BOLD, "Firmware installation was")
.centered()
.add(theme::TEXT_BOLD, "not successful.")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let m_bottom = Paragraphs::new()
.add(theme::TEXT_SUBMSG, "PLEASE RECONNECT")
.centered()
.add(theme::TEXT_SUBMSG, "THE DEVICE")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, BLD_BG, ICON_WARN_SMALL, m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
0
}
fn screen_install_success_bld(msg: &'static str, complete_draw: bool) -> u32 {
let m_top = Paragraphs::new()
.add(theme::TEXT_BOLD, "Firmware installed")
.centered()
.add(theme::TEXT_BOLD, "successfully.")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let m_bottom = Paragraphs::new()
.add(theme::TEXT_SUBMSG, msg)
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
BLD_BG,
ICON_SUCCESS_SMALL,
m_top,
m_bottom,
complete_draw,
);
frame.place(constant::screen());
frame.paint();
0
}
fn screen_install_success_initial(msg: &'static str, complete_draw: bool) -> u32 {
let m_top = Paragraphs::new()
.add(theme::TEXT_INITIAL_INSTALL_SUCCESS, "Firmware installed")
.centered()
.add(theme::TEXT_INITIAL_INSTALL_SUCCESS, "successfully.")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let m_bottom = Paragraphs::new()
.add(theme::TEXT_INITIAL_INSTALL_SUCCESS, msg)
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
BLD_COLOR_INITIAL_INSTALL_SUCCESS,
BLD_COLOR_INITIAL_INSTALL_BG,
ICON_SUCCESS_SMALL,
m_top,
m_bottom,
complete_draw,
);
frame.place(constant::screen());
frame.paint();
0
}
#[no_mangle]
extern "C" fn screen_install_success(
reboot_msg: *const cty::c_char,
initial_setup: bool,
complete_draw: bool,
) -> u32 {
let msg = unwrap!(unsafe { from_c_str(reboot_msg) });
if initial_setup {
screen_install_success_initial(msg, complete_draw)
} else {
screen_install_success_bld(msg, complete_draw)
}
}

View File

@ -0,0 +1,255 @@
use crate::{
alpha,
ui::{
component::text::TextStyle,
display::{Color, Font},
model_tt::{
component::{ButtonStyle, ButtonStyleSheet},
theme::{FG, GREY_DARK, GREY_LIGHT, WHITE},
},
},
};
pub const BLD_BG: Color = Color::rgb(0x00, 0x17, 0xA3);
pub const BLD_FG: Color = WHITE;
pub const BLD_WIPE_COLOR: Color = Color::rgb(0xAD, 0x2B, 0x2B);
pub const BLD_WIPE_BTN_COLOR: Color = Color::alpha(BLD_WIPE_COLOR, alpha!(0.3));
pub const BLD_WIPE_BTN_COLOR_ACTIVE: Color = Color::alpha(BLD_WIPE_COLOR, alpha!(0.15));
pub const BLD_COLOR_SUBMSG: Color = Color::rgb(0x80, 0x8B, 0xD1);
pub const BLD_COLOR_INITIAL_INSTALL_SUCCESS: Color = Color::rgb(0x39, 0xA8, 0x14);
pub const BLD_COLOR_INITIAL_INSTALL_BG: Color = Color::rgb(0xDE, 0xDE, 0xDE);
pub const BLD_BTN_MENU_COLOR: Color = Color::alpha(BLD_BG, alpha!(0.22));
pub const BLD_BTN_MENU_COLOR_ACTIVE: Color = Color::alpha(BLD_BG, alpha!(0.11));
pub const BLD_BTN_MENUITEM_COLOR: Color = Color::alpha(BLD_BG, alpha!(0.33));
pub const BLD_BTN_MENUITEM_COLOR_ACTIVE: Color =
Color::rgba(BLD_BG, 0xFF, 0xFF, 0xFF, alpha!(0.11));
pub const BLD_TITLE_COLOR: Color = Color::rgba(BLD_BG, 0xFF, 0xFF, 0xFF, alpha!(0.75));
// Commonly used corner radius (i.e. for buttons).
pub const RADIUS: u8 = 2;
// Size of icons in the UI (i.e. inside buttons).
pub const ICON_SIZE: i32 = 16;
// UI icons.
pub const ICON_CANCEL: &[u8] = include_res!("model_tt/res/cancel.toif");
pub const ICON_CONFIRM: &[u8] = include_res!("model_tt/res/confirm.toif");
// BLD icons
pub const CLOSE: &[u8] = include_res!("model_tt/res/close.toif");
pub const ERASE: &[u8] = include_res!("model_tt/res/erase.toif");
pub const ERASE_BIG: &[u8] = include_res!("model_tt/res/erase_big.toif");
pub const REBOOT: &[u8] = include_res!("model_tt/res/reboot.toif");
pub const MENU: &[u8] = include_res!("model_tt/res/menu.toif");
pub const RECEIVE: &[u8] = include_res!("model_tt/res/receive.toif");
pub const LOGO_EMPTY: &[u8] = include_res!("model_tt/res/trezor_empty.toif");
pub fn button_install_cancel() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_BTN_MENUITEM_COLOR,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: RADIUS,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_BTN_MENU_COLOR_ACTIVE,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: RADIUS,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: GREY_LIGHT,
button_color: GREY_DARK,
background_color: WHITE,
border_color: WHITE,
border_radius: RADIUS,
border_width: 0,
},
}
}
pub fn button_install_confirm() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_BG,
button_color: WHITE,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: RADIUS,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_BTN_MENU_COLOR_ACTIVE,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: RADIUS,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: FG,
button_color: GREY_DARK,
background_color: FG,
border_color: FG,
border_radius: RADIUS,
border_width: 0,
},
}
}
pub fn button_wipe_cancel() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_WIPE_COLOR,
button_color: WHITE,
background_color: BLD_WIPE_COLOR,
border_color: BLD_WIPE_COLOR,
border_radius: RADIUS,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_WIPE_BTN_COLOR,
background_color: BLD_WIPE_COLOR,
border_color: BLD_WIPE_COLOR,
border_radius: RADIUS,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: GREY_LIGHT,
button_color: GREY_DARK,
background_color: WHITE,
border_color: WHITE,
border_radius: RADIUS,
border_width: 0,
},
}
}
pub fn button_wipe_confirm() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_WIPE_BTN_COLOR,
background_color: BLD_WIPE_COLOR,
border_color: BLD_WIPE_COLOR,
border_radius: RADIUS,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: WHITE,
button_color: BLD_WIPE_BTN_COLOR_ACTIVE,
background_color: BLD_WIPE_COLOR,
border_color: BLD_WIPE_COLOR,
border_radius: RADIUS,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: FG,
button_color: GREY_DARK,
background_color: FG,
border_color: FG,
border_radius: RADIUS,
border_width: 0,
},
}
}
pub fn button_bld_menu() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_FG,
button_color: BLD_BTN_MENU_COLOR,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_FG,
button_color: BLD_BTN_MENU_COLOR_ACTIVE,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: GREY_LIGHT,
button_color: BLD_BTN_MENU_COLOR,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
}
}
pub fn button_bld_menu_item() -> ButtonStyleSheet {
ButtonStyleSheet {
normal: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_FG,
button_color: BLD_BTN_MENUITEM_COLOR,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
active: &ButtonStyle {
font: Font::BOLD,
text_color: BLD_FG,
button_color: BLD_BTN_MENUITEM_COLOR_ACTIVE,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
disabled: &ButtonStyle {
font: Font::BOLD,
text_color: GREY_LIGHT,
button_color: BLD_BTN_MENUITEM_COLOR,
background_color: BLD_BG,
border_color: BLD_BG,
border_radius: 4,
border_width: 0,
},
}
}
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, 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_SUBMSG: TextStyle = TextStyle::new(
Font::BOLD,
BLD_COLOR_SUBMSG,
BLD_BG,
BLD_COLOR_SUBMSG,
BLD_COLOR_SUBMSG,
);
pub const TEXT_INITIAL_INSTALL_SUCCESS: TextStyle = TextStyle::new(
Font::BOLD,
BLD_COLOR_INITIAL_INSTALL_SUCCESS,
BLD_COLOR_INITIAL_INSTALL_BG,
BLD_COLOR_INITIAL_INSTALL_SUCCESS,
BLD_COLOR_INITIAL_INSTALL_SUCCESS,
);

View File

@ -0,0 +1,52 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never},
display::{self, Font},
geometry::{Point, Rect},
model_tt::bootloader::theme::{BLD_BG, BLD_TITLE_COLOR},
};
pub struct Title {
version: &'static str,
area: Rect,
}
impl Title {
pub fn new(version: &'static str) -> Self {
Self {
version,
area: Rect::zero(),
}
}
}
impl Component for Title {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
display::text_top_left(
self.area.top_left(),
"BOOTLOADER",
Font::BOLD,
BLD_TITLE_COLOR,
BLD_BG,
);
display::text_top_left(
Point::new(self.area.top_left().x + 115, self.area.top_left().y),
self.version,
Font::BOLD,
BLD_TITLE_COLOR,
BLD_BG,
);
}
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}

View File

@ -6,7 +6,7 @@ use crate::{
},
display::{self, Color, Font},
event::TouchEvent,
geometry::{Insets, Offset, Rect},
geometry::{Insets, Offset, Point, Rect},
},
};
@ -21,6 +21,7 @@ pub enum ButtonMsg {
pub struct Button<T> {
area: Rect,
touch_expand: Option<Insets>,
content: ButtonContent<T>,
styles: ButtonStyleSheet,
state: State,
@ -37,6 +38,7 @@ impl<T> Button<T> {
Self {
content,
area: Rect::zero(),
touch_expand: None,
styles: theme::button_default(),
state: State::Initial,
long_press: None,
@ -52,6 +54,10 @@ impl<T> Button<T> {
Self::new(ButtonContent::Icon(image))
}
pub fn with_icon_and_text(content: IconText) -> Self {
Self::new(ButtonContent::IconAndText(content))
}
pub fn empty() -> Self {
Self::new(ButtonContent::Empty)
}
@ -61,6 +67,11 @@ impl<T> Button<T> {
self
}
pub fn with_expanded_touch_area(mut self, expand: Insets) -> Self {
self.touch_expand = Some(expand);
self
}
pub fn with_long_press(mut self, duration: Duration) -> Self {
self.long_press = Some(duration);
self
@ -196,6 +207,9 @@ impl<T> Button<T> {
style.button_color,
);
}
ButtonContent::IconAndText(child) => {
child.paint(self.area, self.style(), Self::BASELINE_OFFSET);
}
}
}
}
@ -212,6 +226,12 @@ where
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
let touch_area = if let Some(expand) = self.touch_expand {
self.area.outset(expand)
} else {
self.area
};
match event {
Event::Touch(TouchEvent::TouchStart(pos)) => {
match self.state {
@ -220,7 +240,7 @@ where
}
_ => {
// Touch started in our area, transform to `Pressed` state.
if self.area.contains(pos) {
if touch_area.contains(pos) {
self.set(ctx, State::Pressed);
if let Some(duration) = self.long_press {
self.long_timer = Some(ctx.request_timer(duration));
@ -232,12 +252,12 @@ where
}
Event::Touch(TouchEvent::TouchMove(pos)) => {
match self.state {
State::Released if self.area.contains(pos) => {
State::Released if touch_area.contains(pos) => {
// Touch entered our area, transform to `Pressed` state.
self.set(ctx, State::Pressed);
return Some(ButtonMsg::Pressed);
}
State::Pressed if !self.area.contains(pos) => {
State::Pressed if !touch_area.contains(pos) => {
// Touch is leaving our area, transform to `Released` state.
self.set(ctx, State::Released);
return Some(ButtonMsg::Released);
@ -252,7 +272,7 @@ where
State::Initial | State::Disabled => {
// Do nothing.
}
State::Pressed if self.area.contains(pos) => {
State::Pressed if touch_area.contains(pos) => {
// Touch finished in our area, we got clicked.
self.set(ctx, State::Initial);
return Some(ButtonMsg::Clicked);
@ -300,6 +320,7 @@ where
ButtonContent::Empty => {}
ButtonContent::Text(text) => t.field("text", text),
ButtonContent::Icon(_) => t.symbol("icon"),
ButtonContent::IconAndText(_) => {}
}
t.close();
}
@ -318,6 +339,7 @@ pub enum ButtonContent<T> {
Empty,
Text(T),
Icon(&'static [u8]),
IconAndText(IconText),
}
#[derive(PartialEq, Eq)]
@ -517,3 +539,55 @@ pub enum CancelInfoConfirmMsg {
pub enum SelectWordMsg {
Selected(usize),
}
#[derive(PartialEq, Eq)]
pub struct IconText {
text: &'static str,
icon: &'static [u8],
}
impl IconText {
pub fn new(text: &'static str, icon: &'static [u8]) -> Self {
Self { text, icon }
}
pub fn paint(&self, area: Rect, style: &ButtonStyle, baseline_offset: i16) {
let width = style.font.text_width(self.text);
let height = style.font.text_height();
let mut use_icon = false;
let mut use_text = false;
let mut icon_pos = Point::new(area.top_left().x + 25, area.center().y);
let mut text_pos =
area.center() + Offset::new(-width / 2, height / 2) + Offset::y(baseline_offset);
if area.width() > (46 + 10 + width) {
//display both icon and text
let start_of_baseline = area.center() + Offset::new(-width / 2, height / 2);
text_pos = Point::new(area.top_left().x + 46, start_of_baseline.y);
use_text = true;
use_icon = true;
} else if area.width() > (width + 10) {
use_text = true;
} else {
//if we can't fit the text, retreat to centering the icon
icon_pos = area.center();
use_icon = true;
}
if use_text {
display::text(
text_pos,
self.text,
style.font,
style.text_color,
style.button_color,
);
}
if use_icon {
display::icon(icon_pos, self.icon, style.text_color, style.button_color);
}
}
}

View File

@ -6,12 +6,13 @@ mod keyboard;
mod loader;
mod number_input;
mod page;
mod result;
mod scroll;
mod swipe;
pub use button::{
Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, CancelConfirmMsg,
CancelInfoConfirmMsg, SelectWordMsg,
CancelInfoConfirmMsg, IconText, SelectWordMsg,
};
pub use dialog::{Dialog, DialogMsg, IconDialog};
pub use frame::{Frame, NotificationFrame};
@ -27,6 +28,7 @@ pub use keyboard::{
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
pub use number_input::{NumberInputDialog, NumberInputDialogMsg};
pub use page::{SwipeHoldPage, SwipePage};
pub use result::ResultScreen;
pub use scroll::ScrollBar;
pub use swipe::{Swipe, SwipeDirection};

View File

@ -0,0 +1,92 @@
use crate::{
alpha,
ui::{
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Never, Pad},
constant::screen,
display::{self, Color},
geometry::{Offset, Point, Rect},
},
};
use crate::ui::model_tt::constant::{HEIGHT, WIDTH};
pub struct ResultScreen {
bg: Pad,
small_pad: Pad,
fg_color: Color,
bg_color: Color,
icon: &'static [u8],
message_top: Child<Paragraphs<&'static str>>,
message_bottom: Child<Paragraphs<&'static str>>,
}
impl ResultScreen {
pub fn new(
fg_color: Color,
bg_color: Color,
icon: &'static [u8],
message_top: Paragraphs<&'static str>,
message_bottom: Paragraphs<&'static str>,
complete_draw: bool,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(bg_color),
small_pad: Pad::with_background(bg_color),
fg_color,
bg_color,
icon,
message_top: Child::new(message_top),
message_bottom: Child::new(message_bottom),
};
if complete_draw {
instance.bg.clear();
} else {
instance.small_pad.clear();
}
instance
}
}
impl Component for ResultScreen {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg
.place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT)));
self.message_top
.place(Rect::new(Point::new(15, 59), Point::new(WIDTH - 15, 149)));
let bottom_area = Rect::new(Point::new(15, 151), Point::new(WIDTH - 15, HEIGHT));
self.small_pad.place(bottom_area);
self.message_bottom.place(bottom_area);
bounds
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
self.bg.paint();
self.small_pad.paint();
display::icon(
Point::new(screen().center().x, 45),
self.icon,
self.fg_color,
self.bg_color,
);
display::rect_fill(
Rect::from_top_left_and_size(Point::new(12, 149), Offset::new(216, 1)),
Color::alpha(self.bg_color, alpha!(0.2)),
);
self.message_top.paint();
self.message_bottom.paint();
}
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
}

View File

@ -9,6 +9,8 @@ pub const LOADER_OUTER: f32 = 60_f32;
pub const LOADER_INNER: f32 = 42_f32;
pub const LOADER_ICON_MAX_SIZE: i16 = 64;
pub const BACKLIGHT_NORMAL: i32 = 150;
pub const fn size() -> Offset {
Offset::new(WIDTH, HEIGHT)
}

View File

@ -1,3 +1,5 @@
#[cfg(feature = "bootloader")]
pub mod bootloader;
pub mod component;
pub mod constant;
pub mod event;
@ -5,3 +7,4 @@ pub mod theme;
#[cfg(feature = "micropython")]
pub mod layout;
pub mod screens;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,76 @@
use crate::ui::{
component::{text::paragraphs::Paragraphs, Component},
geometry::LinearPlacement,
model_tt::{
component::ResultScreen,
constant,
theme::{ERROR_COLOR, ICON_WARN_SMALL, TEXT_ERROR_BOLD, TEXT_ERROR_NORMAL, WHITE},
},
util::from_c_str,
};
#[no_mangle]
extern "C" fn screen_fatal_error(msg: *const cty::c_char, file: *const cty::c_char) -> u32 {
let m_top = if msg.is_null() {
Paragraphs::new()
.add(TEXT_ERROR_BOLD, "FATAL ERROR!")
.centered()
// .add(theme::TEXT_WIPE_NORMAL, unwrap!(unsafe { from_c_str(expr) }))
// .centered()
.add(TEXT_ERROR_NORMAL, unwrap!(unsafe { from_c_str(file) }))
.centered()
.with_placement(LinearPlacement::vertical().align_at_center())
} else {
let msg = unwrap!(unsafe { from_c_str(msg) });
Paragraphs::new()
.add(TEXT_ERROR_BOLD, "FATAL ERROR!")
.centered()
.add(TEXT_ERROR_NORMAL, msg)
.centered()
.with_placement(LinearPlacement::vertical().align_at_center())
};
let m_bottom = Paragraphs::new()
.add(TEXT_ERROR_BOLD, "PLEASE CONTACT")
.centered()
.add(TEXT_ERROR_BOLD, "TREZOR SUPPORT")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, ERROR_COLOR, ICON_WARN_SMALL, m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
0
}
#[no_mangle]
extern "C" fn screen_error_shutdown(label: *const cty::c_char, msg: *const cty::c_char) -> u32 {
let label = unwrap!(unsafe { from_c_str(label) });
let m_top = if msg.is_null() {
Paragraphs::new()
.add(TEXT_ERROR_BOLD, label)
.centered()
.with_placement(LinearPlacement::vertical().align_at_center())
} else {
let msg = unwrap!(unsafe { from_c_str(msg) });
Paragraphs::new()
.add(TEXT_ERROR_BOLD, label)
.centered()
.add(TEXT_ERROR_NORMAL, msg)
.centered()
.with_placement(LinearPlacement::vertical().align_at_center())
};
let m_bottom = Paragraphs::new()
.add(TEXT_ERROR_BOLD, "PLEASE UNPLUG")
.centered()
.add(TEXT_ERROR_BOLD, "THE DEVICE")
.centered()
.with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(WHITE, ERROR_COLOR, ICON_WARN_SMALL, m_top, m_bottom, true);
frame.place(constant::screen());
frame.paint();
0
}

View File

@ -35,6 +35,8 @@ pub const GREY_LIGHT: Color = Color::rgb(0xA8, 0xA8, 0xA8); // greyish
pub const GREY_MEDIUM: Color = Color::rgb(0x64, 0x64, 0x64);
pub const GREY_DARK: Color = Color::rgb(0x33, 0x33, 0x33); // greyer
pub const ERROR_COLOR: Color = Color::rgb(0xAD, 0x2B, 0x2B);
// Commonly used corner radius (i.e. for buttons).
pub const RADIUS: u8 = 2;
@ -54,6 +56,8 @@ pub const ICON_NEXT: &[u8] = include_res!("model_tt/res/next.toif");
pub const ICON_WARN: &[u8] = include_res!("model_tt/res/warn-icon.toif");
pub const ICON_LIST_CURRENT: &[u8] = include_res!("model_tt/res/current.toif");
pub const ICON_LIST_CHECK: &[u8] = include_res!("model_tt/res/check.toif");
pub const ICON_SUCCESS_SMALL: &[u8] = include_res!("model_tt/res/success_bld.toif");
pub const ICON_WARN_SMALL: &[u8] = include_res!("model_tt/res/warn_bld.toif");
// Large, three-color icons.
pub const WARN_COLOR: Color = YELLOW;
@ -391,6 +395,10 @@ pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, FG, BG, GREY_LIG
pub const TEXT_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_ERROR_NORMAL: TextStyle =
TextStyle::new(Font::NORMAL, FG, ERROR_COLOR, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_ERROR_BOLD: TextStyle =
TextStyle::new(Font::BOLD, FG, ERROR_COLOR, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_NORMAL_OFF_WHITE: TextStyle =
TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT);

View File

@ -1,5 +1,7 @@
use heapless::String;
use cstr_core::CStr;
pub trait ResultExt {
fn assert_if_debugging_ui(self, message: &str);
}
@ -33,6 +35,28 @@ pub fn u32_to_str(num: u32, buffer: &mut [u8]) -> Option<&str> {
}
}
pub unsafe fn from_c_str(c_str: *const cty::c_char) -> Option<&'static str> {
unsafe {
let bytes = CStr::from_ptr(c_str).to_bytes();
if bytes.is_ascii() {
Some(core::str::from_utf8_unchecked(bytes))
} else {
None
}
}
}
pub unsafe fn from_c_array(c_str: *const cty::c_char, len: usize) -> Option<&'static str> {
unsafe {
let slice = core::slice::from_raw_parts(c_str as *const u8, len);
if slice.is_ascii() {
Some(core::str::from_utf8_unchecked(slice))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -23,10 +23,14 @@
#include "common.h"
#include "display.h"
#ifdef FANCY_FATAL_ERROR
#include "rust_ui.h"
#endif
#include "flash.h"
#include "rand.h"
#include "supervise.h"
#include "mini_printf.h"
#include "stm32f4xx_ll_utils.h"
#ifdef RGB16
@ -52,6 +56,12 @@ __fatal_error(const char *expr, const char *msg, const char *file, int line,
const char *func) {
display_orientation(0);
display_backlight(255);
#ifdef FANCY_FATAL_ERROR
char buf[256] = {0};
mini_snprintf(buf, sizeof(buf), "%s, line %d", file, line);
screen_fatal_error(msg, buf);
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
display_printf("\nFATAL ERROR:\n");
if (expr) {
@ -72,51 +82,24 @@ __fatal_error(const char *expr, const char *msg, const char *file, int line,
rev[4]);
#endif
display_printf("\nPlease contact Trezor support.\n");
#endif
shutdown();
for (;;)
;
}
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4) {
error_shutdown(const char *label, const char *msg) {
display_orientation(0);
#ifdef TREZOR_FONT_NORMAL_ENABLE
display_clear();
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_FATAL_ERROR);
int y = 32;
if (line1) {
display_text(8, y, line1, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
y += 32;
}
if (line2) {
display_text(8, y, line2, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
y += 32;
}
if (line3) {
display_text(8, y, line3, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
y += 32;
}
if (line4) {
display_text(8, y, line4, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
y += 32;
}
y += 32;
display_text(8, y, "Please unplug the device.", -1, FONT_NORMAL, COLOR_WHITE,
COLOR_FATAL_ERROR);
#ifdef FANCY_FATAL_ERROR
screen_error_shutdown(label, msg);
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
if (line1) {
display_printf("%s\n", line1);
if (label) {
display_printf("%s\n", label);
}
if (line2) {
display_printf("%s\n", line2);
}
if (line3) {
display_printf("%s\n", line3);
}
if (line4) {
display_printf("%s\n", line4);
if (msg) {
display_printf("%s\n", msg);
}
display_printf("\nPlease unplug the device.\n");
#endif
@ -156,7 +139,7 @@ void clear_otg_hs_memory(void) {
uint32_t __stack_chk_guard = 0;
void __attribute__((noreturn)) __stack_chk_fail(void) {
error_shutdown("Internal error", "(SS)", NULL, NULL);
error_shutdown("Internal error", "(SS)");
}
uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
@ -193,3 +176,13 @@ void ensure_compatible_settings(void) {
display_set_big_endian();
#endif
}
void show_wipe_code_screen(void) {
error_shutdown(
"DEVICE WIPED!",
"You have entered the wipe code. All private data has been erased.");
}
void show_pin_too_many_screen(void) {
error_shutdown("DEVICE WIPED!",
"Too many wrong PIN attempts. Storage has been wiped.");
}

View File

@ -57,8 +57,10 @@ void __attribute__((noreturn))
__fatal_error(const char *expr, const char *msg, const char *file, int line,
const char *func);
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4);
error_shutdown(const char *label, const char *msg);
void show_wipe_code_screen(void);
void show_pin_too_many_screen(void);
#define ensure(expr, msg) \
(((expr) == sectrue) \

View File

@ -132,7 +132,7 @@ static void _i2c_init(void) {
i2c_handle.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_OK != HAL_I2C_Init(&i2c_handle)) {
ensure(secfalse, NULL);
ensure(secfalse, "Touch screen panel was not loaded properly.");
return;
}
}
@ -210,7 +210,7 @@ void touch_set_mode(void) {
sectrue * (HAL_OK == HAL_I2C_Master_Transmit(
&i2c_handle, TOUCH_ADDRESS, touch_panel_config,
sizeof(touch_panel_config), 10)),
NULL);
"Touch screen panel was not loaded properly.");
}
void touch_power_on(void) {

View File

@ -25,6 +25,9 @@
#include "common.h"
#include "display.h"
#ifdef FANCY_FATAL_ERROR
#include "rust_ui.h"
#endif
#include "memzero.h"
extern void main_clean_exit();
@ -46,6 +49,12 @@ __fatal_error(const char *expr, const char *msg, const char *file, int line,
const char *func) {
display_orientation(0);
display_backlight(255);
#ifdef FANCY_FATAL_ERROR
char buf[256] = {0};
snprintf(buf, sizeof(buf), "%s, line %d", file, line);
screen_fatal_error(msg, buf);
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
display_printf("\nFATAL ERROR:\n");
printf("\nFATAL ERROR:\n");
@ -74,42 +83,36 @@ __fatal_error(const char *expr, const char *msg, const char *file, int line,
#endif
display_printf("\n\n\nHint:\nIsn't the emulator already running?\n");
printf("Hint:\nIsn't the emulator already running?\n");
#endif
hal_delay(3000);
__shutdown();
//__shutdown();
for (;;)
;
}
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4) {
error_shutdown(const char *label, const char *msg) {
#ifdef FANCY_FATAL_ERROR
screen_error_shutdown(label, msg);
#else
display_clear();
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_FATAL_ERROR);
int y = 32;
if (line1) {
display_text(8, y, line1, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", line1);
if (label) {
display_text(8, y, label, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", label);
y += 32;
}
if (line2) {
display_text(8, y, line2, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", line2);
y += 32;
}
if (line3) {
display_text(8, y, line3, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", line3);
y += 32;
}
if (line4) {
display_text(8, y, line4, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", line4);
if (msg) {
display_text(8, y, msg, -1, FONT_NORMAL, COLOR_WHITE, COLOR_FATAL_ERROR);
printf("%s\n", msg);
y += 32;
}
y += 32;
display_text(8, y, "Please unplug the device.", -1, FONT_NORMAL, COLOR_WHITE,
COLOR_FATAL_ERROR);
printf("\nPlease unplug the device.\n");
#endif
display_backlight(255);
hal_delay(5000);
exit(4);
@ -153,3 +156,13 @@ void emulator_poll_events(void) {
uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
void collect_hw_entropy(void) { memzero(HW_ENTROPY_DATA, HW_ENTROPY_LEN); }
void show_wipe_code_screen(void) {
error_shutdown(
"DEVICE WIPED!",
"You have entered the wipe code. All private data has been erased.");
}
void show_pin_too_many_screen(void) {
error_shutdown("DEVICE WIPED!",
"Too many wrong PIN attempts. Storage has been wiped.");
}

View File

@ -44,8 +44,9 @@ void __attribute__((noreturn))
__fatal_error(const char *expr, const char *msg, const char *file, int line,
const char *func);
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4);
error_shutdown(const char *label, const char *msg);
void show_wipe_code_screen(void);
void show_pin_too_many_screen(void);
#define ensure(expr, msg) \
(((expr) == sectrue) \

View File

@ -115,3 +115,12 @@ uint32_t drbg_random32(void) {
drbg_generate((uint8_t *)&value, sizeof(value));
return value;
}
void show_wipe_code_screen(void) {
error_shutdown("You have entered the", "wipe code. All private",
"data has been erased.", NULL);
}
void show_pin_too_many_screen(void) {
error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.",
NULL);
}

View File

@ -33,6 +33,8 @@ __fatal_error(const char *expr, const char *msg, const char *file, int line,
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4);
void show_wipe_code_screen(void);
void show_pin_too_many_screen(void);
#define ensure(expr, msg) \
(((expr) == sectrue) \

View File

@ -992,8 +992,7 @@ static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) {
static void ensure_not_wipe_code(const uint8_t *pin, size_t pin_len) {
if (sectrue != is_not_wipe_code(pin, pin_len)) {
storage_wipe();
error_shutdown("You have entered the", "wipe code. All private",
"data has been erased.", NULL);
show_wipe_code_screen();
}
}
@ -1028,8 +1027,7 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
wait_random();
if (ctr >= PIN_MAX_TRIES) {
storage_wipe();
error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.",
NULL);
show_pin_too_many_screen();
return secfalse;
}
@ -1091,8 +1089,7 @@ static secbool unlock(const uint8_t *pin, size_t pin_len,
wait_random();
if (ctr + 1 >= PIN_MAX_TRIES) {
storage_wipe();
error_shutdown("Too many wrong PIN", "attempts. Storage has",
"been wiped.", NULL);
show_pin_too_many_screen();
}
return secfalse;
}

View File

@ -46,12 +46,5 @@ void __fatal_error(const char *expr, const char *msg, const char *file,
__shutdown();
}
void error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4) {
// For testing do not treat pin_fails_check_max as a fatal error.
(void)line1;
(void)line2;
(void)line3;
(void)line4;
return;
}
void show_wipe_code_screen(void);
void show_pin_too_many_screen(void);

View File

@ -24,8 +24,9 @@
void __fatal_error(const char *expr, const char *msg, const char *file,
int line, const char *func);
void error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4);
void show_wipe_code_screen(void);
void show_pin_too_many_screen(void);
#define ensure(expr, msg) \
(((expr) == sectrue) \