feat(core/rust): bootloader implementation in rust

grdddj/different_colors_for_old_display
tychovrahe 2 years ago committed by Martin Milata
parent f9612898ab
commit 5a991f3244

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

@ -32,10 +32,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 '
@ -47,6 +47,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',

@ -192,6 +192,11 @@ CPPDEFINES_MOD += [
'USE_RUST_LOADER'
]
if TREZOR_MODEL not in ('1', ):
CPPDEFINES_MOD += [
'FANCY_FATAL_ERROR',
]
# modtrezorutils
SOURCE_MOD += [
'embed/extmod/modtrezorutils/modtrezorutils.c',

@ -184,6 +184,11 @@ CPPDEFINES_MOD += [
'TREZOR_UI2',
'USE_RUST_LOADER'
]
if TREZOR_MODEL not in ('1', ):
CPPDEFINES_MOD += [
'FANCY_FATAL_ERROR',
]
if FROZEN:
CPPDEFINES_MOD += ['TREZOR_EMULATOR_FROZEN']
if RASPI:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

@ -21,27 +21,10 @@
#include "bootui.h"
#include "display.h"
#include "icon_cancel.h"
#include "icon_confirm.h"
#include "icon_done.h"
#include "icon_fail.h"
#include "icon_info.h"
#include "icon_install.h"
#include "icon_logo.h"
#include "icon_safeplace.h"
#include "icon_welcome.h"
#include "icon_wipe.h"
#include "mini_printf.h"
#include "rust_ui.h"
#include "version.h"
#if defined USE_TOUCH
#include "touch/touch.h"
#elif defined USE_BUTTON
#include "button.h"
#else
#error No input method defined
#endif
#define BACKLIGHT_NORMAL 150
#define COLOR_BL_BG COLOR_WHITE // background
@ -59,34 +42,22 @@
#define COLOR_BL_GRAY COLOR_BL_FG
#endif
#define COLOR_WELCOME_BG COLOR_WHITE // welcome background
#define COLOR_WELCOME_FG COLOR_BLACK // welcome foreground
// 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 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),
static void format_ver(const char *format, uint32_t version, char *buffer,
size_t buffer_len) {
mini_snprintf(buffer, buffer_len, format, (int)(version & 0xFF),
(int)((version >> 8) & 0xFF), (int)((version >> 16) & 0xFF)
// ignore build field (int)((version >> 24) & 0xFF)
);
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) {
@ -112,10 +83,11 @@ void ui_screen_boot(const vendor_header *const vhdr,
}
if (show_string) {
char ver_str[64];
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 5 - 50, vhdr->vstr,
vhdr->vstr_len, FONT_NORMAL, COLOR_BL_BG,
boot_background);
const char *ver_str = format_ver("%d.%d.%d", fw_version);
format_ver("%d.%d.%d", fw_version, ver_str, sizeof(ver_str));
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 5 - 25, ver_str, -1,
FONT_NORMAL, COLOR_BL_BG, boot_background);
}
@ -145,187 +117,75 @@ void ui_screen_boot_click(void) {
// welcome UI
void ui_screen_welcome_first(void) {
display_icon(0, 0, 240, 240, toi_icon_logo + 12, sizeof(toi_icon_logo) - 12,
COLOR_WELCOME_FG, COLOR_WELCOME_BG);
PIXELDATA_DIRTY();
display_refresh();
}
void ui_screen_welcome(void) { screen_welcome(); }
void ui_screen_welcome_second(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WELCOME_BG);
display_icon((DISPLAY_RESX - 200) / 2, (DISPLAY_RESY - 60) / 2, 200, 60,
toi_icon_safeplace + 12, sizeof(toi_icon_safeplace) - 12,
COLOR_WELCOME_FG, COLOR_WELCOME_BG);
PIXELDATA_DIRTY();
display_refresh();
}
uint32_t ui_screen_intro(const vendor_header *const vhdr,
const image_header *const hdr) {
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));
void ui_screen_welcome_third(void) {
display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WELCOME_BG);
display_icon((DISPLAY_RESX - 180) / 2, (DISPLAY_RESY - 30) / 2 - 5, 180, 30,
toi_icon_welcome + 12, sizeof(toi_icon_welcome) - 12,
COLOR_WELCOME_FG, COLOR_WELCOME_BG);
display_text_center(120, 220, "Go to trezor.io/start", -1, FONT_NORMAL,
COLOR_WELCOME_FG, COLOR_WELCOME_BG);
PIXELDATA_DIRTY();
display_refresh();
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str);
}
// 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;
}
PIXELDATA_DIRTY();
display_refresh();
}
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);
PIXELDATA_DIRTY();
display_refresh();
uint32_t ui_screen_menu(void) {
char bld_ver[32];
format_ver("%d.%d.%d", VERSION_UINT32, bld_ver, sizeof(bld_ver));
return screen_menu(bld_ver);
}
// 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);
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();
PIXELDATA_DIRTY();
display_refresh();
uint32_t ui_screen_install_confirm_upgrade(const vendor_header *const vhdr,
const image_header *const hdr) {
uint8_t fingerprint[32];
char ver_str[64];
get_image_fingerprint(hdr, fingerprint);
format_ver("%d.%d.%d", hdr->version, ver_str, sizeof(ver_str));
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str,
fingerprint, 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();
PIXELDATA_DIRTY();
display_refresh();
uint8_t fingerprint[32];
char ver_str[64];
get_image_fingerprint(hdr, fingerprint);
format_ver("%d.%d.%d", hdr->version, ver_str, sizeof(ver_str));
if (downgrade_wipe) {
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str,
fingerprint, true, false);
} else {
return screen_install_confirm(vhdr->vstr, vhdr->vstr_len, ver_str,
fingerprint, 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);
PIXELDATA_DIRTY();
display_refresh();
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);
PIXELDATA_DIRTY();
display_refresh();
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);
PIXELDATA_DIRTY();
display_refresh();
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);
display_text_center(120, 170, "Seed will be erased!", -1, FONT_NORMAL,
COLOR_BL_FAIL, COLOR_BL_BG);
ui_confirm_cancel_buttons();
PIXELDATA_DIRTY();
display_refresh();
}
uint32_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); }
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);
PIXELDATA_DIRTY();
display_refresh();
}
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);
PIXELDATA_DIRTY();
display_refresh();
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];
@ -336,35 +196,17 @@ 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);
PIXELDATA_DIRTY();
display_refresh();
screen_install_success(str, initial_setup, full_redraw);
}
// 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);
PIXELDATA_DIRTY();
display_refresh();
void ui_screen_boot_empty(bool firmware_present, bool fading) {
screen_boot_empty(firmware_present, fading);
}
// error UI
void ui_screen_fail(void) { screen_install_fail(); }
// general functions
void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 1000); }
@ -373,43 +215,3 @@ void ui_fadeout(void) {
display_fade(BACKLIGHT_NORMAL, 0, 500);
display_clear();
}
int ui_user_input(int zones) {
for (;;) {
#if defined USE_TOUCH
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 USE_BUTTON
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 No input method defined
#endif
}
}

@ -22,29 +22,38 @@
#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);
void ui_screen_boot_wait(int wait_seconds);
void ui_screen_boot_click(void);
void ui_screen_welcome_first(void);
void ui_screen_welcome_second(void);
void ui_screen_welcome_third(void);
void ui_screen_welcome(void);
uint32_t ui_screen_intro(const vendor_header* const vhdr,
const image_header* const hdr);
void ui_screen_firmware_info(const vendor_header* const vhdr,
const image_header* const hdr);
uint32_t ui_screen_menu(void);
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_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);
@ -54,6 +63,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(bool firmware_present, bool fading);
// clang-format off
#define INPUT_CANCEL 0x01 // Cancel button

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_cancel[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x10, 0x00, 0x10, 0x00,
// compressed data length (32-bit)
0x52, 0x00, 0x00, 0x00,
// compressed data
0x45, 0x4d, 0xc1, 0x09, 0x80, 0x30, 0x10, 0x4b, 0xcf, 0x05, 0xba, 0x80, 0xd0, 0x01, 0x2c, 0xb8, 0xff, 0x47, 0x70, 0x01, 0xb1, 0x0b, 0xdc, 0x20, 0xb6, 0xc4, 0x48, 0x5b, 0x0c, 0x1c, 0x09, 0xe1, 0x92, 0xe0, 0x0c, 0x40, 0xdc, 0x90, 0x5a, 0x06, 0x8a, 0x5b, 0xa1, 0x5b, 0x6a, 0xcc, 0x0f, 0xb9, 0xde, 0xe4, 0xa1, 0xf3, 0x26, 0x9d, 0x2a, 0x85, 0xcb, 0x50, 0x3e, 0xd6, 0x6f, 0x94, 0xeb, 0xe1, 0xe7, 0xe1, 0x2b, 0x2b, 0xb8, 0xcd, 0x5c, 0xed, 0x3d, 0xd7, 0xec, 0xdd, 0xfb, 0xce, 0x82, 0xb1, 0xfb, 0x02,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_confirm[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x14, 0x00, 0x10, 0x00,
// compressed data length (32-bit)
0x69, 0x00, 0x00, 0x00,
// compressed data
0x63, 0x60, 0x80, 0x80, 0x48, 0x28, 0xcd, 0x30, 0xe1, 0x3f, 0x2b, 0x94, 0xf5, 0xe3, 0x7f, 0x3c, 0x84, 0xd1, 0xf0, 0xff, 0xff, 0x7a, 0x08, 0xeb, 0xfb, 0xff, 0xff, 0xdc, 0x60, 0x46, 0xc1, 0xff, 0xff, 0xfb, 0x19, 0x18, 0x1c, 0x38, 0x19, 0x18, 0xbe, 0x81, 0x85, 0xbe, 0xcc, 0x67, 0x48, 0x00, 0x0a, 0x31, 0x32, 0x38, 0xfc, 0xff, 0x2f, 0xf1, 0xec, 0xff, 0x7f, 0x69, 0x06, 0x86, 0x05, 0x40, 0xfe, 0x3f, 0x90, 0x10, 0x83, 0xc0, 0xef, 0xff, 0x40, 0xa0, 0x0d, 0xd2, 0xb7, 0x11, 0xc8, 0xd8, 0xcf, 0x04, 0x62, 0x81, 0x04, 0x75, 0x20, 0xa6, 0x6e, 0xfa, 0x7f, 0x9e, 0x19, 0xc2, 0x52, 0xf8, 0x6d, 0x03, 0x73, 0xc7, 0x62, 0x16, 0x30, 0x05, 0x00,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_done[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x40, 0x00, 0x40, 0x00,
// compressed data length (32-bit)
0xce, 0x00, 0x00, 0x00,
// compressed data
0x63, 0x60, 0x18, 0x05, 0x23, 0x0f, 0x4c, 0xe0, 0xc6, 0x2f, 0xff, 0xfb, 0x3e, 0x7e, 0xed, 0xff, 0xff, 0x4b, 0xe3, 0x93, 0xff, 0xf5, 0xff, 0xff, 0x79, 0xfc, 0xda, 0xff, 0xff, 0xe7, 0xc1, 0x2d, 0xff, 0x03, 0x24, 0xbf, 0x1f, 0xa7, 0xf4, 0x82, 0xff, 0x60, 0xc0, 0x89, 0x57, 0xfb, 0xff, 0xfb, 0x78, 0x6d, 0xff, 0xff, 0x5f, 0x17, 0xbf, 0xf6, 0xf7, 0x8c, 0x38, 0xa4, 0x1b, 0x88, 0xd3, 0xce, 0x84, 0x5f, 0xbb, 0x2c, 0x86, 0xab, 0xa4, 0xf1, 0x6b, 0x87, 0xc6, 0x08, 0xd4, 0xf1, 0x72, 0x58, 0x3c, 0x25, 0x03, 0xa2, 0xbf, 0x43, 0xfc, 0x8e, 0xa1, 0xfd, 0x27, 0x24, 0x46, 0xa0, 0xb6, 0xeb, 0x61, 0x0d, 0x52, 0x59, 0x98, 0xf6, 0xf7, 0xd8, 0xb4, 0x83, 0x62, 0xa4, 0x00, 0x87, 0xdf, 0xa1, 0xc6, 0xf2, 0x7d, 0x83, 0xd0, 0xcc, 0xe8, 0xf2, 0x06, 0xff, 0x20, 0xce, 0xc2, 0x61, 0x3b, 0x03, 0xc3, 0xe7, 0xff, 0x48, 0x80, 0x19, 0x53, 0xde, 0x00, 0x49, 0x5a, 0x1e, 0x5b, 0xb0, 0x3e, 0x86, 0x4b, 0xbf, 0x67, 0xc6, 0x26, 0x8f, 0x30, 0x40, 0x1f, 0x7b, 0xbc, 0x3c, 0xc5, 0xab, 0x1d, 0x61, 0x80, 0x3d, 0xae, 0x78, 0xff, 0x8a, 0xd3, 0xf1, 0xc8, 0x06, 0xd8, 0xe3, 0x4e, 0xf5, 0x9f, 0xf1, 0x6a, 0x67, 0x60, 0x08, 0x00, 0x4a, 0xfb, 0x33, 0xe0, 0x01, 0x4f, 0xfe, 0xff, 0x67, 0xc1, 0x27, 0x9f, 0x80, 0x5f, 0x3b, 0x03, 0xc3, 0x15, 0x16, 0x86, 0x51, 0x30, 0xc2, 0x00, 0x00,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_fail[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x40, 0x00, 0x40, 0x00,
// compressed data length (32-bit)
0x21, 0x01, 0x00, 0x00,
// compressed data
0xed, 0x52, 0x39, 0x12, 0x82, 0x40, 0x10, 0x44, 0x17, 0x88, 0x7d, 0x82, 0x1f, 0xd0, 0xe2, 0x05, 0x2e, 0x0f, 0xb0, 0x84, 0x1f, 0xc0, 0x0f, 0x7c, 0xa2, 0x89, 0xb9, 0x55, 0xfe, 0xc0, 0x23, 0xdf, 0xc0, 0x23, 0xd1, 0x62, 0x14, 0xd8, 0x65, 0xf6, 0xe8, 0x22, 0x36, 0xb0, 0xa3, 0xae, 0x69, 0x66, 0xd8, 0x9e, 0xe9, 0x28, 0xfa, 0xe3, 0x27, 0x30, 0x5f, 0x33, 0xdf, 0x4f, 0x43, 0xfd, 0x44, 0xa9, 0xa1, 0x35, 0x2d, 0xc3, 0xf6, 0x86, 0x0a, 0xc3, 0xef, 0xa4, 0x82, 0x01, 0x47, 0x22, 0x4a, 0x7a, 0x5a, 0x7e, 0xe9, 0x22, 0x6c, 0x27, 0xaa, 0x7a, 0xfe, 0xf8, 0x52, 0x7f, 0x40, 0xdb, 0x4e, 0x14, 0xb7, 0x34, 0xef, 0xa8, 0x3b, 0x60, 0xd6, 0x74, 0xc5, 0x6d, 0xcb, 0x9f, 0x1d, 0x55, 0x13, 0x5b, 0x3f, 0x13, 0x99, 0x01, 0xb9, 0xa6, 0x8e, 0x85, 0x97, 0x2e, 0x16, 0xed, 0xe3, 0x7b, 0xec, 0x6c, 0xfd, 0xa6, 0x8b, 0x94, 0xd6, 0x86, 0x49, 0x5b, 0x37, 0x43, 0xa9, 0x30, 0xed, 0xfd, 0x5b, 0x07, 0x5c, 0x74, 0x55, 0x35, 0x9a, 0xac, 0x5c, 0x7f, 0x19, 0x79, 0x10, 0xde, 0x82, 0xae, 0xae, 0xbc, 0xf1, 0xf7, 0x9b, 0x8d, 0xb7, 0xb3, 0x2f, 0xe3, 0x33, 0x18, 0xd0, 0xb0, 0xac, 0x04, 0x08, 0xc8, 0x8d, 0x75, 0x89, 0x02, 0x94, 0x0f, 0x03, 0x54, 0x0c, 0x13, 0x66, 0x76, 0xe0, 0x7b, 0x0f, 0x2c, 0x6e, 0xa0, 0x5c, 0xf3, 0xff, 0x53, 0xa4, 0xdf, 0x47, 0xed, 0xf1, 0x89, 0xc2, 0xe3, 0x44, 0x1c, 0x1b, 0x8d, 0xed, 0x78, 0x3b, 0x18, 0xf0, 0x70, 0xf5, 0xca, 0x93, 0x4b, 0xff, 0xbe, 0x31, 0x5e, 0xee, 0x90, 0x0f, 0x89, 0xdb, 0xe5, 0x70, 0x85, 0x04, 0x7a, 0x4f, 0x4a, 0xb8, 0x83, 0x77, 0x98, 0xef, 0x03, 0xfa, 0xbd, 0xe0, 0x20, 0x49, 0x90, 0xae, 0xca, 0x72, 0x2a, 0xc0, 0x6d, 0x05, 0x7f, 0x8b, 0xf2, 0x5d, 0x59, 0xab, 0x12, 0x20, 0x1c, 0x31, 0xaf, 0x7a, 0x05, 0xf2, 0x2d, 0xad, 0xd7, 0x0a, 0x10, 0xcf, 0x84, 0xb7, 0x05, 0x02, 0x9a, 0xad, 0x99, 0xef, 0x45, 0xf4, 0xc7, 0x6f, 0xe0, 0x03,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_info[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x20, 0x00, 0x20, 0x00,
// compressed data length (32-bit)
0xde, 0x00, 0x00, 0x00,
// compressed data
0x7d, 0x91, 0x3d, 0x0a, 0xc2, 0x40, 0x10, 0x85, 0x27, 0x10, 0xfc, 0x05, 0xe3, 0x0d, 0xbc, 0x82, 0xa0, 0x07, 0x10, 0x3c, 0x80, 0xde, 0x20, 0xf6, 0x22, 0xd8, 0x5b, 0xc4, 0xde, 0x42, 0x6f, 0x60, 0x67, 0x91, 0x46, 0x4b, 0x3b, 0xc5, 0x0b, 0xe4, 0x06, 0xba, 0x17, 0x10, 0x21, 0x10, 0x45, 0x89, 0x3e, 0x37, 0x3b, 0xbb, 0xa8, 0xab, 0x38, 0xc5, 0xce, 0x7e, 0x30, 0x3b, 0xb3, 0xf3, 0x1e, 0x91, 0x8c, 0xfa, 0xfa, 0xb8, 0x9f, 0xb8, 0x64, 0x62, 0x0c, 0x15, 0x45, 0x8d, 0x3d, 0xe8, 0x28, 0x30, 0xa7, 0xc0, 0xa8, 0xd9, 0x5e, 0x00, 0x07, 0x85, 0x02, 0xc8, 0xeb, 0xb2, 0x5a, 0x96, 0x1f, 0x28, 0x73, 0xdd, 0x0c, 0x70, 0x88, 0x56, 0x08, 0x4c, 0xdf, 0x04, 0x15, 0xa2, 0xb3, 0xaa, 0x4e, 0x96, 0xf2, 0xe8, 0x62, 0x4a, 0x84, 0x8d, 0xbc, 0x55, 0x81, 0x9c, 0x4c, 0x57, 0x38, 0x43, 0x78, 0xf2, 0xd2, 0xe2, 0xe9, 0x02, 0x45, 0xc1, 0x53, 0x01, 0x57, 0x3d, 0xf0, 0x62, 0xa8, 0x56, 0xbb, 0x81, 0x4a, 0xf0, 0x2f, 0x11, 0xbd, 0xc5, 0x6d, 0x7e, 0x0b, 0xde, 0x39, 0x89, 0x52, 0x9f, 0x5e, 0xfd, 0x29, 0x3e, 0xdd, 0x3b, 0xff, 0xd9, 0xae, 0xe7, 0x7e, 0x86, 0x93, 0x88, 0xe7, 0x19, 0xbe, 0xce, 0xf9, 0x3f, 0x86, 0xe1, 0x0b, 0xb5, 0x9e, 0x66, 0xf9, 0x5f, 0xde, 0x47, 0xb3, 0x40, 0xc9, 0xde, 0x57, 0xeb, 0x11, 0x86, 0xae, 0xd6, 0x63, 0x6b, 0xe9, 0x65, 0xeb, 0xf9, 0xa5, 0x77, 0xe6, 0x47, 0xbf, 0xd9, 0x90, 0x7e, 0x9c, 0x9c, 0x9f, 0x7e, 0xd9, 0x7e, 0x7e, 0xf8, 0xfd, 0x04,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_install[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x40, 0x00, 0x40, 0x00,
// compressed data length (32-bit)
0xb8, 0x00, 0x00, 0x00,
// compressed data
0x63, 0x60, 0x18, 0x05, 0xa3, 0x80, 0x58, 0xe0, 0xec, 0xe2, 0x62, 0x82, 0x4f, 0xfe, 0xcf, 0xff, 0xff, 0xe7, 0x47, 0xe5, 0xc9, 0x93, 0x3f, 0x85, 0x90, 0x5f, 0x83, 0x45, 0x7a, 0xc1, 0x7f, 0x5e, 0x98, 0xfc, 0x85, 0xff, 0x5c, 0x98, 0xf2, 0xbf, 0xfe, 0xef, 0x87, 0xc9, 0xff, 0xfe, 0xbf, 0x1e, 0x43, 0xfa, 0xc0, 0xff, 0xff, 0x40, 0x03, 0xc0, 0xf2, 0x17, 0x80, 0x4c, 0x1e, 0x74, 0x79, 0x07, 0xa0, 0xe0, 0x79, 0x46, 0x90, 0xbc, 0x00, 0x90, 0xf8, 0xcf, 0x82, 0x61, 0xc0, 0x17, 0xa0, 0xa8, 0x2e, 0x48, 0xfe, 0x12, 0x90, 0xe1, 0x8f, 0x69, 0xbf, 0x02, 0x50, 0xf8, 0xfd, 0x3f, 0x08, 0xfe, 0xcf, 0x84, 0xc5, 0x03, 0x20, 0x03, 0xa0, 0xc0, 0x1f, 0x9b, 0xff, 0x15, 0x10, 0xf2, 0x4c, 0x58, 0x03, 0xe8, 0x13, 0x4c, 0x5a, 0x1f, 0x7b, 0x00, 0x1a, 0xc0, 0xe4, 0x99, 0x71, 0x84, 0xf0, 0x63, 0x88, 0xb4, 0x1e, 0xae, 0x18, 0x50, 0x00, 0x39, 0xfd, 0xff, 0x7b, 0x26, 0x9c, 0xd1, 0x77, 0x11, 0x24, 0x2f, 0x8b, 0x3b, 0x7a, 0x05, 0xfe, 0xfe, 0xff, 0x7f, 0x9f, 0x11, 0x4f, 0xfc, 0x1f, 0xfc, 0xff, 0x5f, 0x86, 0x01, 0x6f, 0x02, 0xc1, 0x9b, 0x7c, 0x80, 0x06, 0xe0, 0xd7, 0xce, 0x20, 0xc0, 0xc8, 0x30, 0x0a, 0x46, 0x01, 0xf1, 0x00, 0x00,
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_welcome[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0xb4, 0x00, 0x1e, 0x00,
// compressed data length (32-bit)
0x32, 0x03, 0x00, 0x00,
// compressed data
0xc5, 0x52, 0xc1, 0x4e, 0x13, 0x51, 0x14, 0xbd, 0x50, 0xa0, 0x50, 0x2c, 0x74, 0xa7, 0x0b, 0x11, 0xbe, 0x80, 0xf2, 0x07, 0xf0, 0x07, 0xfa, 0x03, 0x06, 0x56, 0x6c, 0xdb, 0xb8, 0xc2, 0x15, 0x26, 0x2e, 0xd8, 0x90, 0x40, 0x22, 0x89, 0x1b, 0x05, 0x34, 0x26, 0xec, 0x6c, 0xf5, 0x03, 0x04, 0x75, 0xe1, 0xb2, 0xc4, 0x95, 0x3b, 0x4a, 0xe2, 0xc6, 0x28, 0x4e, 0x29, 0x50, 0x5a, 0xa0, 0x73, 0x3c, 0xf7, 0xbe, 0xe9, 0x74, 0x3a, 0x40, 0x62, 0xa4, 0x89, 0x77, 0x31, 0x73, 0xdf, 0x7b, 0xf7, 0x9c, 0x77, 0xdf, 0xb9, 0x47, 0xe4, 0x3f, 0x05, 0x30, 0x7b, 0x13, 0xf8, 0x11, 0xd0, 0xc7, 0xdf, 0x05, 0xb2, 0xfc, 0x56, 0x80, 0xc1, 0x9b, 0x31, 0x47, 0x30, 0x24, 0x4b, 0x86, 0x3b, 0x55, 0xa0, 0xbf, 0x6b, 0xcc, 0x3b, 0x40, 0x4a, 0x64, 0x06, 0xd8, 0xe0, 0xe2, 0x04, 0xe8, 0xe9, 0x1a, 0xf3, 0x2a, 0x90, 0x16, 0xc9, 0x03, 0x25, 0x2e, 0xea, 0xf0, 0xa4, 0x6b, 0xcc, 0x73, 0xc0, 0xa8, 0x48, 0x11, 0x00, 0x17, 0xe7, 0x28, 0x74, 0x8f, 0x79, 0x02, 0x98, 0x16, 0x29, 0x93, 0x39, 0x21, 0xe2, 0x63, 0xb1, 0x7b, 0xcc, 0xcc, 0x73, 0x36, 0x3a, 0x0e, 0x32, 0x63, 0xb7, 0x74, 0x8d, 0xf9, 0x5c, 0x67, 0xc7, 0xd1, 0x61, 0x58, 0xe7, 0x48, 0x65, 0xe4, 0xe9, 0x2f, 0x6f, 0x39, 0xd1, 0xae, 0x5a, 0x3b, 0xf0, 0x5e, 0x24, 0xac, 0x94, 0x07, 0x96, 0xd1, 0x4f, 0x3d, 0x6f, 0x9a, 0x7b, 0x77, 0x24, 0xf3, 0xc5, 0x7f, 0x1b, 0x1e, 0x45, 0x31, 0x16, 0x75, 0x9d, 0x5d, 0x83, 0xcc, 0x23, 0x3a, 0x47, 0x4e, 0xf3, 0x15, 0x73, 0xd3, 0xdb, 0x55, 0x7d, 0xb2, 0xa5, 0x5a, 0xe6, 0x9d, 0x66, 0xdb, 0x3d, 0xc6, 0xfc, 0x9a, 0xa9, 0x97, 0xd0, 0x86, 0x56, 0x94, 0x24, 0x86, 0x71, 0x71, 0xac, 0xb3, 0x6b, 0xa2, 0x89, 0xac, 0x6c, 0x02, 0x43, 0x92, 0x69, 0x6a, 0x15, 0xee, 0x06, 0x55, 0x53, 0xbe, 0x2d, 0xc7, 0x38, 0x11, 0x97, 0x4d, 0x1a, 0xb3, 0xe5, 0x0f, 0x6d, 0x63, 0x40, 0xe2, 0x98, 0x20, 0x0e, 0x81, 0x5e, 0x6e, 0xd4, 0xb8, 0xb5, 0xab, 0x65, 0xbb, 0x56, 0xa4, 0x0d, 0x58, 0xd5, 0xbe, 0x5b, 0x6e, 0x4b, 0xeb, 0x60, 0xcf, 0x98, 0x23, 0x31, 0x22, 0x71, 0x4c, 0x10, 0xb4, 0x45, 0x3f, 0x05, 0xfe, 0x4a, 0xb9, 0x2b, 0x7a, 0x49, 0x1d, 0x58, 0x5f, 0xf2, 0xd5, 0x2a, 0x56, 0x45, 0x9d, 0x96, 0xb7, 0x88, 0xea, 0xd3, 0x6c, 0xfd, 0xb9, 0xaf, 0xcf, 0x52, 0xe6, 0xc7, 0x3f, 0x54, 0x8f, 0x85, 0x33, 0xe0, 0xbe, 0xc4, 0x31, 0x41, 0x50, 0x82, 0xc1, 0x3c, 0xbc, 0x55, 0x36, 0x53, 0x55, 0x61, 0xac, 0xbf, 0x1d, 0x20, 0x65, 0x55, 0xb4, 0xcb, 0x86, 0x29, 0x96, 0x62, 0xc6, 0x81, 0x7c, 0x04, 0xc6, 0x95, 0x79, 0x52, 0x15, 0xc4, 0x2d, 0x99, 0x82, 0x1a, 0xb5, 0x13, 0xd3, 0x0a, 0x8e, 0x2d, 0x55, 0x44, 0xe1, 0x01, 0x59, 0x4f, 0x88, 0x9d, 0x53, 0xa8, 0x02, 0x46, 0xad, 0x2a, 0x6f, 0x76, 0xe1, 0xed, 0x69, 0x1e, 0x64, 0xed, 0x20, 0xa7, 0xcc, 0x14, 0xf7, 0x54, 0x5f, 0x48, 0xfe, 0x95, 0x38, 0xa6, 0x15, 0xdc, 0x48, 0x97, 0x91, 0x63, 0x4b, 0x7d, 0x75, 0x56, 0x6d, 0xea, 0xcd, 0xda, 0x45, 0xd6, 0xaa, 0x8a, 0xea, 0x46, 0x85, 0x8e, 0x6c, 0x6a, 0x8b, 0xca, 0x54, 0x50, 0xe6, 0x7e, 0x91, 0x9a, 0xbe, 0xd0, 0x4c, 0x1b, 0xc3, 0x44, 0xcc, 0x3d, 0x5a, 0xc5, 0xb4, 0xf8, 0x48, 0x9e, 0xbb, 0x29, 0xba, 0xb8, 0x6f, 0x55, 0x9c, 0xc2, 0xa0, 0x2b, 0xdb, 0x75, 0x59, 0x9d, 0xaa, 0x39, 0xe6, 0x93, 0x80, 0xb9, 0x10, 0xc7, 0x84, 0x71, 0x81, 0xe9, 0x1a, 0xdf, 0x71, 0x86, 0x61, 0x9f, 0x8f, 0x2a, 0xb7, 0xaa, 0x66, 0xad, 0xca, 0x3d, 0x5c, 0x83, 0x59, 0x52, 0x4c, 0x83, 0x4b, 0xcc, 0x31, 0x4c, 0x18, 0x0d, 0xe4, 0x1a, 0x7c, 0x72, 0x0d, 0xf7, 0xd4, 0x41, 0x95, 0x4b, 0xcc, 0xfd, 0x21, 0xb3, 0x66, 0x27, 0x57, 0x30, 0x57, 0xae, 0x61, 0xae, 0x61, 0xa3, 0xc9, 0x76, 0xaa, 0x58, 0x54, 0xb9, 0x2a, 0xff, 0xd0, 0xf3, 0x75, 0xcc, 0x55, 0xec, 0xa9, 0x5d, 0xcb, 0xfa, 0x4b, 0xea, 0xcb, 0x86, 0xc2, 0x01, 0xfc, 0xa5, 0xce, 0x11, 0x4c, 0x47, 0xd8, 0x8d, 0xa2, 0x2e, 0xd0, 0x0b, 0x8a, 0x2a, 0x49, 0x9b, 0xd9, 0x79, 0x63, 0x62, 0x7e, 0x7e, 0xa0, 0xe8, 0xbc, 0x71, 0x11, 0x7a, 0xa3, 0xcd, 0x1c, 0xc1, 0x74, 0x84, 0x52, 0x96, 0xcc, 0xd7, 0x5a, 0x9a, 0x57, 0xc3, 0x92, 0x6b, 0x40, 0x22, 0x7e, 0x7e, 0x42, 0x68, 0xde, 0xfc, 0x3c, 0x01, 0x2c, 0x5e, 0x62, 0x8e, 0x60, 0x3a, 0x82, 0x30, 0xfa, 0x58, 0x7d, 0xcd, 0x87, 0x4a, 0xc6, 0x3a, 0x97, 0xef, 0xb3, 0x8e, 0x39, 0x63, 0x67, 0x47, 0xec, 0x3c, 0x63, 0xf7, 0xef, 0x80, 0xfe, 0x89, 0x33, 0x47, 0x30, 0xab, 0xbf, 0x23, 0xba, 0xcc, 0xc0, 0xc9, 0xce, 0xdf, 0x86, 0x09, 0x89, 0xd2, 0xc2, 0x37, 0x2d, 0xb5, 0xed, 0x33, 0x60, 0x79, 0x0b, 0x4a, 0xd5, 0x00, 0xd6, 0x97, 0x7c, 0x95, 0x34, 0xce, 0x1c, 0xc1, 0x34, 0x8c, 0x23, 0x08, 0xbd, 0x71, 0xdc, 0x14, 0xb4, 0x37, 0xb5, 0x6c, 0x9f, 0x72, 0xcc, 0xfb, 0x6e, 0x55, 0x0a, 0x0f, 0x3c, 0xb9, 0xcc, 0xdc, 0xc6, 0xf8, 0x7a, 0x1e, 0x06, 0xfb, 0x48, 0xbb, 0x66, 0xa7, 0xf5, 0xa2, 0xa6, 0x15, 0x6d, 0x3b, 0x35, 0x4c, 0x24, 0xc6, 0x18, 0x65, 0xf4, 0x2d, 0x9b, 0xbc, 0x82, 0xb9, 0x8d, 0xe9, 0x64, 0x3e, 0x73, 0xa6, 0x39, 0xb6, 0x61, 0x51, 0x2a, 0xab, 0x4a, 0x06, 0xcc, 0xf2, 0x59, 0x57, 0x05, 0x3d, 0x78, 0x6f, 0xe8, 0xde, 0x2b, 0x98, 0xdb, 0x98, 0x0e, 0x35, 0xe4, 0x14, 0xd0, 0xa9, 0x1e, 0x3a, 0x5b, 0x89, 0x3c, 0xfa, 0x89, 0x0f, 0xb7, 0xa5, 0xc5, 0x2c, 0x6b, 0x07, 0xde, 0x72, 0xaf, 0x1d, 0x3c, 0x3b, 0xf0, 0x5e, 0xf6, 0xc9, 0x55, 0xcc, 0x21, 0x26, 0x98, 0xe0, 0x1f,
};

@ -1,11 +0,0 @@
// clang-format off
static const uint8_t toi_icon_wipe[] = {
// magic
'T', 'O', 'I', 'G',
// width (16-bit), height (16-bit)
0x40, 0x00, 0x40, 0x00,
// compressed data length (32-bit)
0x2f, 0x01, 0x00, 0x00,
// compressed data
0xed, 0xd2, 0x31, 0x4e, 0x02, 0x41, 0x14, 0x06, 0xe0, 0xc7, 0x62, 0x58, 0x41, 0x5d, 0x48, 0xac, 0x35, 0x14, 0xf6, 0x5a, 0x98, 0x58, 0xe2, 0x0d, 0xc6, 0x1b, 0x48, 0x6d, 0x21, 0x5a, 0xd8, 0x99, 0xc8, 0x0d, 0xbc, 0x82, 0x37, 0xc0, 0x23, 0x10, 0x2f, 0x00, 0x89, 0x85, 0xa5, 0xdb, 0x6b, 0x82, 0x80, 0x68, 0x24, 0xe0, 0x73, 0xde, 0xcc, 0x12, 0x27, 0xd9, 0x7f, 0x86, 0x9a, 0x84, 0x3f, 0xd9, 0xc9, 0x66, 0xbf, 0xec, 0xec, 0xec, 0x7b, 0x8f, 0x68, 0x9d, 0xd5, 0xc9, 0xf5, 0x12, 0x1f, 0xb6, 0xc2, 0x3e, 0xe2, 0x4a, 0xd0, 0x3f, 0xb9, 0x13, 0xf4, 0x6f, 0xe6, 0xcd, 0x90, 0x4f, 0x99, 0xd5, 0x6e, 0xc0, 0x67, 0xcc, 0xbd, 0xb4, 0xe8, 0xe5, 0x3a, 0xeb, 0xdc, 0x28, 0x8f, 0x46, 0xd4, 0x14, 0x4f, 0xd8, 0xb3, 0xc1, 0x09, 0x75, 0xc5, 0xd5, 0x6c, 0x0f, 0xfb, 0x05, 0x0d, 0xc5, 0x5f, 0xa7, 0xf7, 0xd8, 0xdf, 0xe4, 0xf8, 0x3a, 0xbf, 0x9e, 0x0f, 0xf0, 0x01, 0x67, 0xd9, 0x41, 0x5c, 0xe3, 0xdb, 0x85, 0xc3, 0x3f, 0x68, 0xf3, 0x60, 0xe1, 0xb0, 0xc8, 0x7d, 0xfe, 0x4f, 0x01, 0xf8, 0xd8, 0xf1, 0x18, 0xf8, 0x8f, 0xe3, 0xdb, 0xe8, 0xf8, 0x4e, 0xaa, 0x79, 0x6e, 0xba, 0x7e, 0x98, 0xf7, 0xae, 0xeb, 0xe0, 0x07, 0x3f, 0xac, 0x5c, 0x9a, 0xb5, 0x85, 0x46, 0xcf, 0x24, 0x99, 0xc8, 0x7a, 0x97, 0xf7, 0x49, 0x56, 0xda, 0x07, 0x59, 0xcf, 0xe1, 0xe8, 0x49, 0xb6, 0x8e, 0x64, 0x6d, 0xe4, 0xfd, 0x4b, 0x9e, 0xbf, 0xf3, 0x86, 0x69, 0x62, 0x15, 0x8d, 0xb6, 0xce, 0xf3, 0xc0, 0xde, 0x80, 0x06, 0xca, 0x6c, 0x74, 0xfa, 0xca, 0xde, 0xc4, 0xb8, 0x3d, 0x8d, 0x54, 0x17, 0x36, 0xc5, 0xfd, 0x91, 0xfa, 0x25, 0xa3, 0x82, 0x71, 0x38, 0x60, 0x73, 0xe6, 0xf2, 0x31, 0x19, 0xdf, 0x27, 0xdc, 0xdf, 0x52, 0x76, 0x90, 0x08, 0xf9, 0x55, 0x76, 0xac, 0x31, 0xaa, 0x8e, 0xad, 0x60, 0xc5, 0x16, 0xaa, 0x84, 0xfd, 0xcc, 0x96, 0x65, 0xae, 0xc8, 0x93, 0x27, 0xd9, 0xf8, 0xb4, 0x17, 0xf9, 0x9c, 0x5e, 0xf4, 0xf5, 0x18, 0x7b, 0x99, 0x6a, 0xfa, 0xd5, 0x3a, 0xad, 0xb3, 0x42, 0xf9, 0x03,
};

@ -47,7 +47,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;
@ -63,6 +63,12 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
#define USB_IFACE_NUM 0
typedef enum {
CONTINUE = 0,
RETURN = 1,
SHUTDOWN = 2,
} usb_result_t;
static void usb_init_all(secbool usb21_landing) {
usb_dev_info_t dev_info = {
.device_class = 0x00,
@ -99,8 +105,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) {
static usb_result_t 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);
@ -115,6 +121,7 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
}
uint16_t msg_id;
uint32_t msg_size;
uint32_t response;
if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) {
// invalid header -> discard
continue;
@ -127,35 +134,26 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
process_msg_Ping(USB_IFACE_NUM, msg_size, buf);
break;
case 5: // WipeDevice
ui_fadeout();
ui_screen_wipe_confirm();
ui_fadein();
int response = ui_user_input(INPUT_CONFIRM | INPUT_CANCEL);
response = ui_screen_wipe_confirm();
if (INPUT_CANCEL == response) {
ui_fadeout();
ui_screen_firmware_info(vhdr, hdr);
ui_fadein();
send_user_abort(USB_IFACE_NUM, "Wipe cancelled");
break;
hal_delay(100);
usb_stop();
usb_deinit();
return RETURN;
}
ui_fadeout();
ui_screen_wipe();
ui_fadein();
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
if (r < 0) { // error
ui_fadeout();
ui_screen_fail();
ui_fadein();
screen_wipe_fail();
usb_stop();
usb_deinit();
return secfalse; // shutdown
} else { // success
ui_fadeout();
ui_screen_done(0, sectrue);
ui_fadein();
return SHUTDOWN;
} else { // success
screen_wipe_success();
usb_stop();
usb_deinit();
return secfalse; // shutdown
return SHUTDOWN;
}
break;
case 6: // FirmwareErase
@ -164,17 +162,18 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
case 7: // FirmwareUpload
r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf);
if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort
ui_fadeout();
ui_screen_fail();
ui_fadein();
usb_stop();
usb_deinit();
return secfalse; // shutdown
return SHUTDOWN;
} else if (r == UPLOAD_ERR_USER_ABORT) {
hal_delay(100);
usb_stop();
usb_deinit();
return RETURN;
} else if (r == 0) { // last chunk received
ui_screen_install_progress_upload(1000);
ui_fadeout();
ui_screen_done(4, sectrue);
ui_fadein();
ui_screen_done(3, secfalse);
hal_delay(1000);
ui_screen_done(2, secfalse);
@ -183,8 +182,8 @@ static secbool bootloader_usb_loop(const vendor_header *const vhdr,
hal_delay(1000);
usb_stop();
usb_deinit();
ui_fadeout();
return sectrue; // jump to firmware
ui_screen_boot_empty(true, true);
return CONTINUE;
}
break;
case 55: // GetFeatures
@ -260,10 +259,52 @@ int main(void) {
display_reinit();
mpu_config_bootloader();
const image_header *hdr = NULL;
vendor_header vhdr;
// detect whether the device contains a valid firmware
secbool firmware_present = sectrue;
if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) {
firmware_present = secfalse;
}
if (sectrue == firmware_present) {
firmware_present = check_vendor_header_keys(&vhdr);
}
if (sectrue == firmware_present) {
firmware_present = check_vendor_header_lock(&vhdr);
}
if (sectrue == firmware_present) {
hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen),
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE);
if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) {
firmware_present = secfalse;
}
}
if (sectrue == firmware_present) {
firmware_present = check_image_model(hdr);
}
if (sectrue == firmware_present) {
firmware_present =
check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub);
}
if (sectrue == firmware_present) {
firmware_present =
check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen,
FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT);
}
#if defined TREZOR_MODEL_T
set_core_clock(CLOCK_180_MHZ);
display_set_little_endian();
#endif
ui_screen_boot_empty(firmware_present == sectrue, false);
#ifdef USE_TOUCH
touch_power_on();
touch_init();
@ -276,14 +317,10 @@ int main(void) {
rgb_led_init();
#endif
mpu_config_bootloader();
#if PRODUCTION
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) {
@ -310,69 +347,24 @@ int main(void) {
}
#endif
const image_header *hdr = NULL;
vendor_header vhdr;
// detect whether the device contains a valid firmware
secbool firmware_present = sectrue;
if (sectrue != read_vendor_header((const uint8_t *)FIRMWARE_START, &vhdr)) {
firmware_present = secfalse;
}
if (sectrue == firmware_present) {
firmware_present = check_vendor_header_keys(&vhdr);
}
if (sectrue == firmware_present) {
firmware_present = check_vendor_header_lock(&vhdr);
}
if (sectrue == firmware_present) {
hdr = read_image_header((const uint8_t *)(FIRMWARE_START + vhdr.hdrlen),
FIRMWARE_IMAGE_MAGIC, FIRMWARE_IMAGE_MAXSIZE);
if (hdr != (const image_header *)(FIRMWARE_START + vhdr.hdrlen)) {
firmware_present = secfalse;
}
}
if (sectrue == firmware_present) {
firmware_present = check_image_model(hdr);
}
if (sectrue == firmware_present) {
firmware_present =
check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub);
}
if (sectrue == firmware_present) {
firmware_present =
check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen,
FIRMWARE_SECTORS, FIRMWARE_SECTORS_COUNT);
}
// start the bootloader if no or broken firmware found ...
if (firmware_present != sectrue) {
// show intro animation
// no ui_fadeout(); - we already start from black screen
ui_screen_welcome_first();
ui_fadein();
hal_delay(1000);
// ignore stay in bootloader
stay_in_bootloader = secfalse;
touched = false;
ui_fadeout();
ui_screen_welcome_second();
ui_fadein();
// show intro animation
hal_delay(1000);
ui_set_initial_setup(true);
ui_fadeout();
ui_screen_welcome_third();
ui_fadein();
ui_screen_welcome();
// erase storage
ensure(flash_erase_sectors(STORAGE_SECTORS, STORAGE_SECTORS_COUNT, NULL),
NULL);
// and start the usb loop
if (bootloader_usb_loop(NULL, NULL) != sectrue) {
if (bootloader_usb_loop(NULL, NULL) != CONTINUE) {
return 1;
}
}
@ -380,13 +372,78 @@ int main(void) {
// ... 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);
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_screen_boot_empty(true, true);
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_screen_wipe();
secbool r = bootloader_WipeDevice();
if (r != sectrue) { // error
screen_wipe_fail();
return 1;
} else { // success
screen_wipe_success();
return 1;
}
}
break;
case SCREEN_WAIT_FOR_HOST:
screen_connect();
switch (bootloader_usb_loop(&vhdr, hdr)) {
case CONTINUE:
continue_to_firmware = true;
break;
case RETURN:
screen = SCREEN_INTRO;
break;
case SHUTDOWN:
return 1;
break;
default:
break;
}
break;
default:
break;
}
// and start the usb loop
if (bootloader_usb_loop(&vhdr, hdr) != sectrue) {
return 1;
if (continue_to_firmware) {
break;
}
}
}
@ -416,7 +473,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();
@ -466,3 +523,5 @@ int main(void) {
return 0;
}
void HardFault_Handler(void) { error_shutdown("INTERNAL ERROR!", "(HF)"); }

@ -23,7 +23,7 @@ ccmram_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
sram_start = ORIGIN(SRAM);
sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
_codelen = SIZEOF(.flash) + SIZEOF(.data) + SIZEOF(.exidx) + SIZEOF(.align);
_codelen = SIZEOF(.flash) + SIZEOF(.data);
SECTIONS {
.header : ALIGN(4) {
@ -36,15 +36,6 @@ SECTIONS {
*(.text*);
. = ALIGN(4);
*(.rodata*);
. = ALIGN(4);
} >FLASH AT>FLASH
/* exception handling info generated by llvm which should consist of 8 bytes of "cantunwind" */
.exidx : ALIGN(4) {
*(.ARM.exidx*);
} >FLASH AT>FLASH
.align : ALIGN(4) {
. = ALIGN(512);
} >FLASH AT>FLASH
@ -57,6 +48,10 @@ SECTIONS {
. = ALIGN(512);
} >CCMRAM AT>FLASH
/DISCARD/ : {
*(.ARM.exidx*);
}
.bss : ALIGN(4) {
*(.bss*);
. = ALIGN(4);

@ -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
@ -559,33 +560,26 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
&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();
send_user_abort(iface_num, "Firmware install cancelled");
return UPLOAD_ERR_USER_ABORT;
}
ui_screen_install_start();
headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
read_offset = IMAGE_INIT_CHUNK_SIZE;
@ -601,11 +595,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(
@ -677,7 +666,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,
@ -700,8 +689,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) {
secbool 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");

@ -66,4 +66,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

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

@ -186,7 +186,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
@ -194,29 +194,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,

@ -4,3 +4,25 @@ 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,
const uint8_t* fingerprint, bool downgrade,
bool vendor);
uint32_t screen_wipe_confirm(void);
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(const char* bld_version_str);
void screen_connect(void);
void screen_fatal_error_c(const char* msg, const char* file);
void screen_error_shutdown_c(const char* label, const char* msg);
void screen_wipe_success(void);
void 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_welcome(void);
void screen_boot_empty(bool firmware_present, bool fading);

@ -1,12 +1,11 @@
use core::{
convert::{Infallible, TryInto},
num::TryFromIntError,
};
use core::{convert::Infallible, num::TryFromIntError};
use cstr_core::CStr;
#[cfg(feature = "micropython")]
use crate::micropython::{ffi, obj::Obj, qstr::Qstr};
use {
crate::micropython::{ffi, obj::Obj, qstr::Qstr},
core::convert::TryInto,
};
#[allow(clippy::enum_variant_names)] // We mimic the Python exception classnames here.
#[derive(Clone, Copy, Debug)]
@ -22,7 +21,6 @@ pub enum Error {
KeyError(Obj),
#[cfg(feature = "micropython")]
AttributeError(Qstr),
#[cfg(feature = "micropython")]
ValueError(&'static CStr),
#[cfg(feature = "micropython")]
ValueErrorParam(&'static CStr, Obj),

@ -26,6 +26,7 @@ mod trace;
#[cfg(feature = "ui")]
#[macro_use]
pub mod ui;
pub mod strutil;
#[cfg(feature = "debug")]
#[panic_handler]

@ -1,6 +1,6 @@
use core::{convert::TryFrom, ops::Deref, ptr, slice, str};
use crate::{error::Error, micropython::obj::Obj};
use crate::{error::Error, micropython::obj::Obj, strutil::hexlify};
use super::ffi;
@ -231,18 +231,6 @@ pub unsafe fn get_buffer_mut<'a>(obj: Obj) -> Result<&'a mut [u8], Error> {
}
}
fn hexlify(data: &[u8], buffer: &mut [u8]) {
const HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
let mut i: usize = 0;
for b in data.iter().take(buffer.len() / 2) {
let hi: usize = ((b & 0xf0) >> 4).into();
let lo: usize = (b & 0x0f).into();
buffer[i] = HEX_LOWER[hi];
buffer[i + 1] = HEX_LOWER[lo];
i += 2;
}
}
pub fn hexlify_bytes(obj: Obj, offset: usize, max_len: usize) -> Result<StrBuffer, Error> {
if !obj.is_bytes() {
return Err(Error::TypeError);

@ -0,0 +1,11 @@
pub fn hexlify(data: &[u8], buffer: &mut [u8]) {
const HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
let mut i: usize = 0;
for b in data.iter().take(buffer.len() / 2) {
let hi: usize = ((b & 0xf0) >> 4).into();
let lo: usize = (b & 0x0f).into();
buffer[i] = HEX_LOWER[hi];
buffer[i + 1] = HEX_LOWER[lo];
i += 2;
}
}

@ -175,3 +175,9 @@ pub fn sync() {
ffi::display_sync();
}
}
pub fn refresh() {
unsafe {
ffi::display_refresh();
}
}

@ -36,6 +36,13 @@ pub trait ParagraphStrType: AsRef<str> {
fn skip_prefix(&self, bytes: usize) -> Self;
}
#[cfg(feature = "bootloader")]
impl ParagraphStrType for &str {
fn skip_prefix(&self, chars: usize) -> Self {
&self[chars..]
}
}
pub trait ParagraphSource {
/// Determines the output type produced.
type StrType: ParagraphStrType;
@ -258,6 +265,8 @@ pub struct Paragraph<T> {
/// Try to keep this and the next paragraph on the same page. NOTE: doesn't
/// work if two or more subsequent paragraphs have this flag.
no_break: bool,
padding_top: i16,
padding_bottom: i16,
}
impl<T> Paragraph<T> {
@ -268,6 +277,8 @@ impl<T> Paragraph<T> {
align: Alignment::Start,
break_after: false,
no_break: false,
padding_top: PARAGRAPH_TOP_SPACE,
padding_bottom: PARAGRAPH_BOTTOM_SPACE,
}
}
@ -286,6 +297,16 @@ impl<T> Paragraph<T> {
self
}
pub const fn with_top_padding(mut self, padding: i16) -> Self {
self.padding_top = padding;
self
}
pub const fn with_bottom_padding(mut self, padding: i16) -> Self {
self.padding_bottom = padding;
self
}
pub fn content(&self) -> &T {
&self.content
}
@ -302,13 +323,15 @@ impl<T> Paragraph<T> {
align: self.align,
break_after: self.break_after,
no_break: self.no_break,
padding_top: self.padding_top,
padding_bottom: self.padding_bottom,
}
}
fn layout(&self, area: Rect) -> TextLayout {
TextLayout {
padding_top: PARAGRAPH_TOP_SPACE,
padding_bottom: PARAGRAPH_BOTTOM_SPACE,
padding_top: self.padding_top,
padding_bottom: self.padding_bottom,
..TextLayout::new(*self.style)
.with_align(self.align)
.with_bounds(area)
@ -604,3 +627,42 @@ where
self
}
}
impl<T: ParagraphStrType, const N: usize> ParagraphSource for Vec<Paragraph<T>, N> {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
let para = &self[index];
para.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
self.len()
}
}
impl<T: ParagraphStrType, const N: usize> ParagraphSource for [Paragraph<T>; N] {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
let para = &self[index];
para.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
self.len()
}
}
impl<T: ParagraphStrType> ParagraphSource for Paragraph<T> {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
assert_eq!(index, 0);
self.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
1
}
}

@ -23,10 +23,12 @@ use crate::{
error::Error,
time::Duration,
trezorhal::{buffers::get_text_buffer, display, qr, time, uzlib::UzlibContext},
ui::{component::image::Image, lerp::Lerp},
ui::lerp::Lerp,
};
use core::slice;
#[cfg(feature = "dma2d")]
use crate::ui::component::image::Image;
pub use crate::ui::display::toif::Icon;
#[cfg(any(feature = "model_tt", feature = "model_tr"))]
pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN};
@ -833,6 +835,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());
@ -860,6 +875,10 @@ pub fn sync() {
display::sync();
}
pub fn refresh() {
display::refresh();
}
pub fn get_color_table(fg_color: Color, bg_color: Color) -> [Color; 16] {
let mut table: [Color; 16] = [Color::from_u16(0); 16];

@ -326,6 +326,15 @@ impl Rect {
}
}
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,
}
}
pub const fn cut_from_left(&self, width: i16) -> Self {
Self {
x0: self.x0,

@ -197,45 +197,6 @@ impl ParagraphSource for PropsList {
}
}
impl<T: ParagraphStrType, const N: usize> ParagraphSource for Vec<Paragraph<T>, N> {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
let para = &self[index];
para.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
self.len()
}
}
impl<T: ParagraphStrType, const N: usize> ParagraphSource for [Paragraph<T>; N] {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
let para = &self[index];
para.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
self.len()
}
}
impl<T: ParagraphStrType> ParagraphSource for Paragraph<T> {
type StrType = T;
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
assert_eq!(index, 0);
self.map(|content| content.skip_prefix(offset))
}
fn size(&self) -> usize {
1
}
}
impl ParagraphStrType for StrBuffer {
fn skip_prefix(&self, chars: usize) -> Self {
self.offset(chars)

@ -8,7 +8,8 @@ pub mod display;
pub mod event;
pub mod geometry;
pub mod lerp;
mod util;
pub mod screens;
pub mod util;
#[cfg(feature = "micropython")]
pub mod layout;

@ -0,0 +1,195 @@
use crate::ui::{
component::{
text::paragraphs::{ParagraphVecShort, Paragraphs},
Child, Component, ComponentExt, Event, EventCtx, Label, Pad,
},
constant,
constant::screen,
display::{Color, Icon},
geometry::{Alignment, Insets, Offset, Point, Rect, TOP_CENTER},
model_tt::{
bootloader::theme::{
button_bld_menu, BUTTON_AREA_START, CLOSE, CONTENT_PADDING, CORNER_BUTTON_AREA,
INFO_SMALL, TEXT_TITLE, TITLE_AREA,
},
component::{Button, ButtonMsg::Clicked},
constant::WIDTH,
theme::WHITE,
},
};
#[derive(Copy, Clone, ToPrimitive)]
pub enum ConfirmMsg {
Cancel = 1,
Confirm = 2,
}
pub struct Confirm<'a> {
bg: Pad,
content_pad: Pad,
bg_color: Color,
icon: Option<Icon>,
title: Option<Child<Label<&'static str>>>,
message: Child<Paragraphs<ParagraphVecShort<&'a str>>>,
left: Child<Button<&'static str>>,
right: Child<Button<&'static str>>,
info_button: Option<Button<&'static str>>,
close_button: Option<Button<&'static str>>,
info_title: Option<Child<Label<&'static str>>>,
info_text: Option<Paragraphs<ParagraphVecShort<&'a str>>>,
show_info: bool,
confirm_left: bool,
}
impl<'a> Confirm<'a> {
pub fn new(
bg_color: Color,
icon: Option<Icon>,
left: Button<&'static str>,
right: Button<&'static str>,
confirm_left: bool,
confirm: (Option<&'static str>, Paragraphs<ParagraphVecShort<&'a str>>),
info: Option<(&'static str, Paragraphs<ParagraphVecShort<&'a str>>)>,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(bg_color),
content_pad: Pad::with_background(bg_color),
bg_color,
icon,
message: Child::new(confirm.1),
left: Child::new(left),
right: Child::new(right),
close_button: None,
info_button: None,
info_title: None,
info_text: None,
confirm_left,
show_info: false,
title: confirm
.0
.map(|title| Child::new(Label::new(title, Alignment::Start, TEXT_TITLE))),
};
if let Some((title, text)) = info {
instance.info_title = Some(Child::new(Label::new(title, Alignment::Start, TEXT_TITLE)));
instance.info_text = Some(text);
instance.info_button = Some(
Button::with_icon(Icon::new(INFO_SMALL))
.styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)),
);
instance.close_button = Some(
Button::with_icon(Icon::new(CLOSE))
.styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)),
);
}
instance.bg.clear();
instance
}
}
impl<'a> Component for Confirm<'a> {
type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.bg.place(constant::screen());
self.content_pad.place(Rect::new(
Point::zero(),
Point::new(WIDTH, BUTTON_AREA_START),
));
let icon_height = if let Some(icon) = self.icon {
icon.toif.height()
} else {
0
};
self.message.place(Rect::new(
Point::new(CONTENT_PADDING, 32 + icon_height),
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START),
));
let button_size = Offset::new(106, 38);
self.left.place(Rect::from_top_left_and_size(
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
button_size,
));
self.right.place(Rect::from_top_left_and_size(
Point::new(124, BUTTON_AREA_START),
button_size,
));
self.info_button.place(CORNER_BUTTON_AREA);
self.close_button.place(CORNER_BUTTON_AREA);
self.info_title.place(TITLE_AREA);
self.title.place(TITLE_AREA);
self.info_text.place(Rect::new(
Point::new(CONTENT_PADDING, TITLE_AREA.y1),
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START),
));
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if self.show_info {
if let Some(Clicked) = self.close_button.event(ctx, event) {
self.show_info = false;
self.content_pad.clear();
self.title.request_complete_repaint(ctx);
self.message.request_complete_repaint(ctx);
return None;
}
} else if let Some(Clicked) = self.info_button.event(ctx, event) {
self.show_info = true;
self.info_text.request_complete_repaint(ctx);
self.info_title.request_complete_repaint(ctx);
self.content_pad.clear();
return None;
}
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();
self.content_pad.paint();
if self.show_info {
self.close_button.paint();
self.info_title.paint();
self.info_text.paint();
self.left.paint();
self.right.paint();
} else {
self.info_button.paint();
self.title.paint();
self.message.paint();
self.left.paint();
self.right.paint();
if let Some(icon) = self.icon {
icon.draw(
Point::new(screen().center().x, 32),
TOP_CENTER,
WHITE,
self.bg_color,
);
}
}
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.left.bounds(sink);
self.right.bounds(sink);
}
}

@ -0,0 +1,50 @@
use crate::ui::{
component::{Component, Event, EventCtx, Never, Pad},
constant::screen,
display::{self, Font},
geometry::{Offset, Rect},
model_tt::bootloader::theme::{BLD_BG, BLD_TITLE_COLOR},
};
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(screen());
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,
);
}
}

@ -0,0 +1,100 @@
use crate::ui::{
component::{
text::paragraphs::{ParagraphVecShort, Paragraphs},
Child, Component, Event, EventCtx, Label, Pad,
},
constant::screen,
display::Icon,
geometry::{Alignment, Insets, Point, Rect},
model_tt::{
bootloader::theme::{button_bld_menu, button_bld_menu_item, BLD_BG, MENU},
component::ButtonMsg::Clicked,
},
};
use heapless::String;
use crate::ui::model_tt::{
bootloader::theme::{CONTENT_PADDING, CORNER_BUTTON_AREA, TEXT_TITLE, TITLE_AREA},
component::Button,
constant::WIDTH,
};
#[repr(u32)]
#[derive(Copy, Clone, ToPrimitive)]
pub enum IntroMsg {
Menu = 1,
Host = 2,
}
pub struct Intro<'a> {
bg: Pad,
title: Child<Label<String<32>>>,
menu: Child<Button<&'static str>>,
host: Child<Button<&'static str>>,
text: Child<Paragraphs<ParagraphVecShort<&'a str>>>,
}
impl<'a> Intro<'a> {
pub fn new(bld_version: &'static str, content: Paragraphs<ParagraphVecShort<&'a str>>) -> Self {
let mut title: String<32> = String::new();
unwrap!(title.push_str("BOOTLOADER "));
unwrap!(title.push_str(bld_version));
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Label::new(title, Alignment::Start, TEXT_TITLE)),
menu: Child::new(
Button::with_icon(Icon::new(MENU))
.styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)),
),
host: Child::new(Button::with_text("INSTALL FIRMWARE").styled(button_bld_menu_item())),
text: Child::new(content),
};
instance.bg.clear();
instance
}
}
impl<'a> Component for Intro<'a> {
type Msg = IntroMsg;
fn place(&mut self, bounds: Rect) -> Rect {
const BUTTON_AREA_START: i16 = 188;
self.bg.place(screen());
self.title.place(TITLE_AREA);
self.menu.place(CORNER_BUTTON_AREA);
self.host.place(Rect::new(
Point::new(10, BUTTON_AREA_START),
Point::new(10 + 220, BUTTON_AREA_START + 38),
));
self.text.place(Rect::new(
Point::new(CONTENT_PADDING, 75),
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START),
));
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);
}
}

@ -0,0 +1,106 @@
use crate::ui::{
component::{Child, Component, Event, EventCtx, Label, Pad},
constant::{screen, WIDTH},
display::Icon,
geometry::{Alignment, Insets, Point, Rect},
model_tt::{
bootloader::theme::{
button_bld_menu, button_bld_menu_item, BLD_BG, CLOSE, CONTENT_PADDING,
CORNER_BUTTON_AREA, ERASE, REBOOT, TEXT_TITLE, TITLE_AREA,
},
component::{Button, ButtonMsg::Clicked, IconText},
},
};
use heapless::String;
#[repr(u32)]
#[derive(Copy, Clone, ToPrimitive)]
pub enum MenuMsg {
Close = 1,
Reboot = 2,
FactoryReset = 3,
}
pub struct Menu {
bg: Pad,
title: Child<Label<String<32>>>,
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 TREZOR", Icon::new(REBOOT));
let content_reset = IconText::new("FACTORY RESET", Icon::new(ERASE));
let mut title: String<32> = String::new();
unwrap!(title.push_str("BOOTLOADER "));
unwrap!(title.push_str(bld_version));
let mut instance = Self {
bg: Pad::with_background(BLD_BG),
title: Child::new(Label::new(title, Alignment::Start, TEXT_TITLE)),
close: Child::new(
Button::with_icon(Icon::new(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(screen());
self.title.place(TITLE_AREA);
self.close.place(CORNER_BUTTON_AREA);
self.reboot.place(Rect::new(
Point::new(CONTENT_PADDING, 64),
Point::new(WIDTH - CONTENT_PADDING, 64 + 38),
));
self.reset.place(Rect::new(
Point::new(CONTENT_PADDING, 110),
Point::new(WIDTH - CONTENT_PADDING, 110 + 38),
));
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);
}
}

@ -0,0 +1,513 @@
use crate::{
trezorhal::io::io_touch_read,
ui::{
component::{Component, Event, EventCtx, Never},
display::{self, Font},
event::TouchEvent,
geometry::Point,
model_tt::constant,
},
};
use heapless::String;
use num_traits::ToPrimitive;
pub mod confirm;
mod connect;
pub mod intro;
pub mod menu;
pub mod theme;
use crate::{
strutil::hexlify,
ui::{
component::text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
constant::screen,
display::{Color, Icon},
geometry::{LinearPlacement, CENTER},
model_tt::{
bootloader::{
connect::Connect,
theme::{
button_install_cancel, button_install_confirm, button_wipe_cancel,
button_wipe_confirm, BLD_BG, BLD_FG, BLD_WIPE_COLOR, ERASE_BIG, LOGO_EMPTY,
RECEIVE, TEXT_WIPE_BOLD, WELCOME_COLOR,
},
},
component::{Button, ResultScreen},
theme::{
BACKLIGHT_DIM, BACKLIGHT_NORMAL, BG, BLACK, FG, GREY_DARK, ICON_SUCCESS_SMALL,
ICON_WARN_SMALL, TEXT_ERROR_BOLD, TEXT_ERROR_NORMAL, WHITE,
},
},
util::{from_c_array, from_c_str},
},
};
use confirm::Confirm;
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<T> ReturnToC for T
where
T: ToPrimitive,
{
fn return_to_c(self) -> u32 {
self.to_u32().unwrap()
}
}
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());
fadeout();
display::sync();
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));
if let Some(message) = msg {
return message.return_to_c();
}
display::sync();
frame.paint();
}
}
}
fn show<F>(frame: &mut F, fading: bool)
where
F: Component,
{
frame.place(screen());
if fading {
fadeout()
};
display::sync();
frame.paint();
if fading {
fadein()
};
}
fn touch_eval() -> Option<TouchEvent> {
let event = io_touch_read();
if event == 0 {
return None;
}
let event_type = event >> 24;
let ex = ((event >> 12) & 0xFFF) as i16;
let ey = (event & 0xFFF) as i16;
TouchEvent::new(event_type, ex as _, ey as _).ok()
}
#[no_mangle]
extern "C" fn screen_install_confirm(
vendor_str: *const cty::c_char,
vendor_str_len: u8,
version: *const cty::c_char,
fingerprint: *const cty::uint8_t,
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) });
let mut fingerprint_buffer: [u8; 64] = [0; 64];
let fingerprint_str = unsafe {
let fingerprint_slice = core::slice::from_raw_parts(fingerprint as *const u8, 32);
hexlify(fingerprint_slice, &mut fingerprint_buffer);
core::str::from_utf8_unchecked(fingerprint_buffer.as_ref())
};
let mut version_str: String<64> = String::new();
unwrap!(version_str.push_str("Firmware version "));
unwrap!(version_str.push_str(version));
let mut vendor_str: String<64> = String::new();
unwrap!(vendor_str.push_str("by "));
unwrap!(vendor_str.push_str(text));
let title = if downgrade {
"DOWNGRADE FW"
} else if vendor {
"CHANGE FW VENDOR"
} else {
"UPDATE FIRMWARE"
};
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, version_str.as_ref()));
messages.add(Paragraph::new(&theme::TEXT_NORMAL, vendor_str.as_ref()));
if vendor || downgrade {
messages
.add(Paragraph::new(&theme::TEXT_BOLD, "Seed will be erased!").with_top_padding(16));
}
let message =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_FINGERPRINT, fingerprint_str));
let fingerprint =
Paragraphs::new(messages).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,
None,
left,
right,
false,
(Some(title), message),
Some(("FW FINGERPRINT", fingerprint)),
);
run(&mut frame)
}
#[no_mangle]
extern "C" fn screen_wipe_confirm() -> u32 {
let icon = Some(Icon::new(ERASE_BIG));
let mut messages = ParagraphVecShort::new();
messages.add(
Paragraph::new(
&TEXT_ERROR_NORMAL,
"Are you sure you want to factory reset the device?",
)
.centered(),
);
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "Seed and firmware\nwill be erased!").centered());
let message =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let left = Button::with_text("RESET").styled(button_wipe_confirm());
let right = Button::with_text("CANCEL").styled(button_wipe_cancel());
let mut frame = Confirm::new(
BLD_WIPE_COLOR,
icon,
left,
right,
true,
(None, message),
None,
);
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 {
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) });
let mut fw: String<64> = String::new();
unwrap!(fw.push_str("Firmware "));
unwrap!(fw.push_str(version));
let mut vendor_: String<64> = String::new();
unwrap!(vendor_.push_str("by "));
unwrap!(vendor_.push_str(vendor));
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_NORMAL, fw.as_ref()));
messages.add(Paragraph::new(&theme::TEXT_NORMAL, vendor_.as_ref()));
let p = Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_start());
let mut frame = Intro::new(bld_version, p);
run(&mut frame)
}
fn screen_progress(
text: &str,
progress: u16,
initialize: bool,
fg_color: Color,
bg_color: Color,
icon: Option<(Icon, Color)>,
) {
if initialize {
fadeout();
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);
if initialize {
fadein();
}
}
#[no_mangle]
extern "C" fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
let bg_color = if initial_setup { WELCOME_COLOR } else { BG };
let fg_color = if initial_setup { FG } else { BLD_FG };
screen_progress(
"Installing firmware...",
progress,
initialize,
fg_color,
bg_color,
Some((Icon::new(RECEIVE), fg_color)),
)
}
#[no_mangle]
extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
screen_progress(
"Resetting Trezor...",
progress,
initialize,
theme::BLD_FG,
BLD_WIPE_COLOR,
Some((Icon::new(ERASE_BIG), theme::BLD_FG)),
)
}
#[no_mangle]
extern "C" fn screen_connect() {
let mut frame = Connect::new("Waiting for host...");
show(&mut frame, true);
}
#[no_mangle]
extern "C" fn screen_wipe_success() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "Trezor reset").centered());
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_WIPE_BOLD, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&TEXT_WIPE_BOLD, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
BLD_WIPE_COLOR,
Icon::new(ICON_SUCCESS_SMALL),
m_top,
m_bottom,
true,
);
show(&mut frame, true);
}
#[no_mangle]
extern "C" fn screen_wipe_fail() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "Trezor reset was").centered());
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "not successful.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_WIPE_BOLD, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&TEXT_WIPE_BOLD, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
BLD_WIPE_COLOR,
Icon::new(ICON_WARN_SMALL),
m_top,
m_bottom,
true,
);
show(&mut frame, true);
}
#[no_mangle]
extern "C" fn screen_boot_empty(firmware_present: bool, fading: bool) {
if fading {
fadeout();
}
let fg = if firmware_present { GREY_DARK } else { WHITE };
let bg = if firmware_present {
BLACK
} else {
WELCOME_COLOR
};
display::rect_fill(constant::screen(), bg);
let icon = Icon::new(LOGO_EMPTY);
icon.draw(screen().center(), CENTER, fg, bg);
if fading {
fadein();
} else {
display::set_backlight(BACKLIGHT_NORMAL);
}
}
#[no_mangle]
extern "C" fn screen_install_fail() {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Firmware installation was").centered());
messages.add(Paragraph::new(&theme::TEXT_BOLD, "not successful.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_SUBMSG, "PLEASE RECONNECT").centered());
messages.add(Paragraph::new(&theme::TEXT_SUBMSG, "THE DEVICE").centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
BLD_BG,
Icon::new(ICON_WARN_SMALL),
m_top,
m_bottom,
true,
);
show(&mut frame, true);
}
fn screen_install_success_bld(msg: &'static str, complete_draw: bool) {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_BOLD, "Firmware installed").centered());
messages.add(Paragraph::new(&theme::TEXT_BOLD, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_SUBMSG, msg).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
BLD_BG,
Icon::new(ICON_SUCCESS_SMALL),
m_top,
m_bottom,
complete_draw,
);
show(&mut frame, complete_draw);
}
fn screen_install_success_initial(msg: &'static str, complete_draw: bool) {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_WELCOME_BOLD, "Firmware installed").centered());
messages.add(Paragraph::new(&theme::TEXT_WELCOME_BOLD, "successfully.").centered());
let m_top =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_SUBMSG_INITIAL, msg).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
FG,
WELCOME_COLOR,
Icon::new(ICON_SUCCESS_SMALL),
m_top,
m_bottom,
complete_draw,
);
show(&mut frame, complete_draw);
}
#[no_mangle]
extern "C" fn screen_install_success(
reboot_msg: *const cty::c_char,
initial_setup: bool,
complete_draw: bool,
) {
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)
}
}
#[no_mangle]
extern "C" fn screen_welcome() {
fadeout();
display::rect_fill(screen(), WELCOME_COLOR);
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&theme::TEXT_WELCOME, "Get started with").centered());
messages.add(Paragraph::new(&theme::TEXT_WELCOME, "your trezor at").centered());
messages.add(
Paragraph::new(&theme::TEXT_WELCOME_BOLD, "trezor.io/start")
.centered()
.with_top_padding(2),
);
let mut frame =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
show(&mut frame, false);
fadein();
}

@ -0,0 +1,304 @@
use crate::{
alpha,
ui::{
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
constant::WIDTH,
display::{Color, Font},
geometry::{Offset, Point, Rect},
model_tt::{
component::{ButtonStyle, ButtonStyleSheet},
theme::{BLACK, FG, GREY_DARK, GREY_LIGHT, GREY_MEDIUM, 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_TEXT_COLOR: Color = Color::rgb(0xD6, 0x95, 0x95);
pub const BLD_WIPE_BTN_COLOR: Color = Color::alpha(BLD_WIPE_COLOR, alpha!(0.3));
pub const BLD_WIPE_BTN_COLOR_ACTIVE: Color = Color::rgb(0xB9, 0x4B, 0x4B);
pub const BLD_WIPE_CANCEL_BTN_COLOR_ACTIVE: Color = Color::rgb(0xF3, 0xDF, 0xDF);
pub const BLD_INSTALL_BTN_COLOR: Color = Color::alpha(BLD_BG, alpha!(0.3));
pub const BLD_INSTALL_BTN_COLOR_ACTIVE: Color = Color::rgb(0xD9, 0xDC, 0xF1);
pub const BLD_INSTALL_CANCEL_BTN_COLOR_ACTIVE: Color = Color::rgb(0x26, 0x3A, 0xB1);
pub const BLD_COLOR_SUBMSG: Color = Color::rgb(0x80, 0x8B, 0xD1);
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));
pub const WELCOME_COLOR: Color = BLACK;
// Commonly used corner radius (i.e. for buttons).
pub const RADIUS: u8 = 2;
// Commonly used constants for UI elements.
pub const CONTENT_PADDING: i16 = 10;
pub const TITLE_AREA: Rect = Rect::new(Point::new(15, 14), Point::new(200, 30));
pub const CORNER_BUTTON_SIZE: i16 = 32;
pub const CORNER_BUTTON_PADDING: i16 = 8;
pub const CORNER_BUTTON_AREA: Rect = Rect::from_top_left_and_size(
Point::new(
WIDTH - CORNER_BUTTON_SIZE - CORNER_BUTTON_PADDING,
CORNER_BUTTON_PADDING,
),
Offset::uniform(CORNER_BUTTON_SIZE),
);
pub const TITLE_AREA_HEIGHT: i16 = 16;
pub const TITLE_AREA_START_Y: i16 = 8;
pub const BUTTON_AREA_START: i16 = 188;
// 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 const INFO_SMALL: &[u8] = include_res!("model_tt/res/info_small.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_INSTALL_CANCEL_BTN_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: BLD_BG,
button_color: BLD_INSTALL_BTN_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: BLD_WIPE_COLOR,
button_color: BLD_WIPE_CANCEL_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: 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_WELCOME: TextStyle = TextStyle::new(
Font::NORMAL,
GREY_MEDIUM,
WELCOME_COLOR,
GREY_MEDIUM,
GREY_MEDIUM,
);
pub const TEXT_WELCOME_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, WELCOME_COLOR, FG, FG);
pub const TEXT_TITLE: TextStyle = TextStyle::new(
Font::BOLD,
BLD_TITLE_COLOR,
BLD_BG,
BLD_TITLE_COLOR,
BLD_TITLE_COLOR,
);
pub const TEXT_SUBMSG_INITIAL: TextStyle = TextStyle::new(
Font::BOLD,
GREY_MEDIUM,
WELCOME_COLOR,
GREY_MEDIUM,
GREY_MEDIUM,
);
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
pub const TEXT_FINGERPRINT: TextStyle =
TextStyle::new(Font::NORMAL, BLD_FG, BLD_BG, BLD_FG, BLD_FG)
.with_line_breaking(BreakWordsNoHyphen);
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, BLD_FG, BLD_BG, BLD_FG, BLD_FG);
pub const TEXT_WIPE_BOLD: TextStyle = TextStyle::new(
Font::BOLD,
BLD_WIPE_TEXT_COLOR,
BLD_WIPE_COLOR,
BLD_WIPE_TEXT_COLOR,
BLD_WIPE_TEXT_COLOR,
);
pub const TEXT_SUBMSG: TextStyle = TextStyle::new(
Font::BOLD,
BLD_COLOR_SUBMSG,
BLD_BG,
BLD_COLOR_SUBMSG,
BLD_COLOR_SUBMSG,
);

@ -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,
@ -69,6 +71,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
@ -234,6 +241,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 {
@ -242,7 +255,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));
@ -254,7 +267,7 @@ where
}
Event::Touch(TouchEvent::TouchMove(pos)) => {
match self.state {
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);
@ -269,7 +282,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);
@ -554,7 +567,7 @@ impl IconText {
Self { text, icon }
}
pub fn paint(&self, area: Rect, style: &ButtonStyle, baseline_offset: i32) {
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();
@ -570,8 +583,7 @@ impl IconText {
if area.width() > (Self::ICON_SPACE + Self::TEXT_MARGIN + 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 + Self::ICON_SPACE, start_of_baseline.y);
text_pos = Point::new(area.top_left().x + Self::ICON_SPACE, text_pos.y);
use_text = true;
use_icon = true;
} else if area.width() > (width + Self::TEXT_MARGIN) {

@ -5,24 +5,27 @@ mod fido;
mod fido_icons;
mod frame;
mod hold_to_confirm;
#[cfg(feature = "dma2d")]
mod homescreen;
mod keyboard;
mod loader;
mod number_input;
mod page;
mod progress;
mod result;
mod scroll;
mod swipe;
mod welcome_screen;
pub use button::{
Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, CancelConfirmMsg,
CancelInfoConfirmMsg, SelectWordMsg,
CancelInfoConfirmMsg, IconText, SelectWordMsg,
};
pub use dialog::{Dialog, DialogMsg, IconDialog};
pub use fido::{FidoConfirm, FidoMsg};
pub use frame::{Frame, NotificationFrame};
pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg};
#[cfg(feature = "dma2d")]
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};
pub use keyboard::{
bip39::Bip39Input,
@ -36,6 +39,7 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
pub use number_input::{NumberInputDialog, NumberInputDialogMsg};
pub use page::{SwipeHoldPage, SwipePage};
pub use progress::Progress;
pub use result::ResultScreen;
pub use scroll::ScrollBar;
pub use swipe::{Swipe, SwipeDirection};
pub use welcome_screen::WelcomeScreen;

@ -0,0 +1,93 @@
use crate::{
alpha,
ui::{
component::{
text::paragraphs::{ParagraphStrType, ParagraphVecShort, Paragraphs},
Child, Component, Event, EventCtx, Never, Pad,
},
constant::screen,
display::{self, Color, Icon},
geometry::{Offset, Point, Rect, CENTER},
},
};
use crate::ui::model_tt::constant::{HEIGHT, WIDTH};
pub struct ResultScreen<T> {
bg: Pad,
small_pad: Pad,
fg_color: Color,
bg_color: Color,
icon: Icon,
message_top: Child<Paragraphs<ParagraphVecShort<T>>>,
message_bottom: Child<Paragraphs<ParagraphVecShort<T>>>,
}
impl<T: ParagraphStrType> ResultScreen<T> {
pub fn new(
fg_color: Color,
bg_color: Color,
icon: Icon,
message_top: Paragraphs<ParagraphVecShort<T>>,
message_bottom: Paragraphs<ParagraphVecShort<T>>,
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<T: ParagraphStrType> Component for ResultScreen<T> {
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();
self.icon.draw(
Point::new(screen().center().x, 45),
CENTER,
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();
}
}

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

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

@ -0,0 +1,104 @@
#[cfg(feature = "micropython")]
use crate::micropython::buffer::StrBuffer;
use crate::ui::{
component::{
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
Component,
},
display::Icon,
geometry::LinearPlacement,
model_tt::{
component::ResultScreen,
constant,
theme::{FATAL_ERROR_COLOR, ICON_WARN_SMALL, TEXT_ERROR_BOLD, TEXT_ERROR_NORMAL, WHITE},
},
};
#[cfg(not(feature = "micropython"))]
// SAFETY: Actually safe but see below
unsafe fn get_str(text: &str) -> &str {
text
}
#[cfg(feature = "micropython")]
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not
// escape the lifetime of the original &str.
unsafe fn get_str(text: &str) -> StrBuffer {
unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) }
}
pub fn screen_fatal_error(msg: Option<&str>, file: &str) {
// SAFETY: these will get placed into `frame` which does not outlive this
// function
let msg = msg.map(|s| unsafe { get_str(s) });
let file = unsafe { get_str(file) };
let m_top = if let Some(msg) = msg {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "FATAL ERROR!".into()).centered());
messages.add(Paragraph::new(&TEXT_ERROR_NORMAL, msg).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
} else {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "FATAL ERROR!".into()).centered());
messages.add(Paragraph::new(&TEXT_ERROR_NORMAL, file).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
};
let mut messages = ParagraphVecShort::new();
messages
.add(Paragraph::new(&TEXT_ERROR_BOLD, "PLEASE CONTACT\nTREZOR SUPPORT".into()).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
FATAL_ERROR_COLOR,
Icon::new(ICON_WARN_SMALL),
m_top,
m_bottom,
true,
);
frame.place(constant::screen());
frame.paint();
}
pub fn screen_error_shutdown(label: &str, msg: Option<&str>) {
// SAFETY: these will get placed into `frame` which does not outlive this
// function
let msg = msg.map(|s| unsafe { get_str(s) });
let label = unsafe { get_str(label) };
let m_top = if let Some(msg) = msg {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, label).centered());
messages.add(Paragraph::new(&TEXT_ERROR_NORMAL, msg).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
} else {
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, label).centered());
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center())
};
let mut messages = ParagraphVecShort::new();
messages.add(Paragraph::new(&TEXT_ERROR_BOLD, "PLEASE UNPLUG\nTHE DEVICE".into()).centered());
let m_bottom =
Paragraphs::new(messages).with_placement(LinearPlacement::vertical().align_at_center());
let mut frame = ResultScreen::new(
WHITE,
FATAL_ERROR_COLOR,
Icon::new(ICON_WARN_SMALL),
m_top,
m_bottom,
true,
);
frame.place(constant::screen());
frame.paint();
}

@ -42,6 +42,8 @@ pub const GREY_MEDIUM: Color = Color::rgb(0x45, 0x45, 0x45); // button pressed
pub const GREY_DARK: Color = Color::rgb(0x1A, 0x1A, 0x1A); // button
pub const VIOLET: Color = Color::rgb(0x95, 0x00, 0xCA);
pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xAD, 0x2B, 0x2B);
// Commonly used corner radius (i.e. for buttons).
pub const RADIUS: u8 = 2;
@ -64,6 +66,8 @@ 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_LOCK: &[u8] = include_res!("model_tt/res/lock.toif");
pub const ICON_LOGO: &[u8] = include_res!("model_tt/res/logo.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;
@ -397,6 +401,10 @@ pub fn textstyle_number(num: i32) -> &'static TextStyle {
_ => &TEXT_NORMAL,
}
}
pub const TEXT_ERROR_NORMAL: TextStyle =
TextStyle::new(Font::NORMAL, FG, FATAL_ERROR_COLOR, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_ERROR_BOLD: TextStyle =
TextStyle::new(Font::BOLD, FG, FATAL_ERROR_COLOR, GREY_LIGHT, GREY_LIGHT);
pub const TEXT_NORMAL_OFF_WHITE: TextStyle =
TextStyle::new(Font::NORMAL, OFF_WHITE, BG, GREY_LIGHT, GREY_LIGHT);

@ -0,0 +1,40 @@
//! Reexporting the `screens` module according to the
//! current feature (Trezor model)
#[cfg(all(feature = "model_tr", not(feature = "model_tt")))]
pub use super::model_tr::screens::*;
#[cfg(feature = "model_tt")]
pub use super::model_tt::screens::*;
use crate::ui::util::from_c_str;
#[no_mangle]
extern "C" fn screen_fatal_error_c(msg: *const cty::c_char, file: *const cty::c_char) {
let msg = if msg.is_null() {
None
} else {
unsafe { from_c_str(msg) }
};
let file = if file.is_null() {
""
} else {
unwrap!(unsafe { from_c_str(file) })
};
screen_fatal_error(msg, file);
}
#[no_mangle]
extern "C" fn screen_error_shutdown_c(msg: *const cty::c_char, file: *const cty::c_char) {
let msg = if msg.is_null() {
None
} else {
unsafe { from_c_str(msg) }
};
let file = if file.is_null() {
""
} else {
unwrap!(unsafe { from_c_str(file) })
};
screen_fatal_error(msg, file);
}

@ -5,6 +5,8 @@ use crate::ui::{
geometry::{Offset, Point, CENTER},
};
use cstr_core::CStr;
pub trait ResultExt {
fn assert_if_debugging_ui(self, message: &str);
}
@ -38,6 +40,46 @@ pub fn u32_to_str(num: u32, buffer: &mut [u8]) -> Option<&str> {
}
}
/// Constructs a string from a C string.
///
/// # Safety
///
/// The caller is responsible that the pointer is valid, which means that:
/// (a) it is not null,
/// (b) it points to a memory containing a valid C string (zero-terminated
/// sequence of characters), and
/// (c) that the pointer has appropriate lifetime.
pub unsafe fn from_c_str<'a>(c_str: *const cty::c_char) -> Option<&'a str> {
unsafe {
let bytes = CStr::from_ptr(c_str).to_bytes();
if bytes.is_ascii() {
Some(core::str::from_utf8_unchecked(bytes))
} else {
None
}
}
}
/// Construct str from a C array.
///
/// # Safety
///
/// The caller is responsible that the pointer is valid, which means that:
/// (a) it is not null,
/// (b) it points to a memory containing array of characters, with length `len`,
/// and
/// (c) that the pointer has appropriate lifetime.
pub unsafe fn from_c_array<'a>(c_str: *const cty::c_char, len: usize) -> Option<&'a 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(feature = "ui_debug")]
static mut DISABLE_ANIMATION: bool = false;

@ -23,11 +23,15 @@
#include "common.h"
#include "display.h"
#ifdef FANCY_FATAL_ERROR
#include "rust_ui.h"
#endif
#include "flash.h"
#include "rand.h"
#include "stm32.h"
#include "supervise.h"
#include "mini_printf.h"
#include "stm32f4xx_ll_utils.h"
#ifdef RGB16
@ -39,13 +43,16 @@
// from util.s
extern void shutdown_privileged(void);
void shutdown(void) {
void __attribute__((noreturn)) shutdown(void) {
#ifdef USE_SVC_SHUTDOWN
svc_shutdown();
#else
// It won't work properly unless called from the privileged mode
shutdown_privileged();
#endif
for (;;)
;
}
void __attribute__((noreturn))
@ -53,6 +60,13 @@ __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: %d", file, line);
screen_fatal_error_c(msg, buf);
display_refresh();
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
display_printf("\nFATAL ERROR:\n");
if (expr) {
@ -73,58 +87,28 @@ __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_c(label, msg);
display_refresh();
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
if (line1) {
display_printf("%s\n", line1);
}
if (line2) {
display_printf("%s\n", line2);
if (label) {
display_printf("%s\n", label);
}
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
display_backlight(255);
shutdown();
for (;;)
;
}
#ifndef NDEBUG
@ -157,7 +141,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];
@ -197,3 +181,13 @@ void ensure_compatible_settings(void) {
display_set_slow_pwm();
#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.");
}

@ -51,14 +51,16 @@
#define STAY_IN_BOOTLOADER_FLAG 0x0FC35A96
void shutdown(void);
void __attribute__((noreturn)) shutdown(void);
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) \

@ -103,19 +103,22 @@ secbool check_image_model(const image_header *const hdr) {
return sectrue;
}
secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m,
uint8_t key_n, const uint8_t *const *keys) {
// check header signature
void get_image_fingerprint(const image_header *const hdr, uint8_t *const out) {
BLAKE2S_CTX ctx;
uint8_t fingerprint[32];
blake2s_Init(&ctx, BLAKE2S_DIGEST_LENGTH);
blake2s_Update(&ctx, hdr, IMAGE_HEADER_SIZE - IMAGE_SIG_SIZE);
for (int i = 0; i < IMAGE_SIG_SIZE; i++) {
blake2s_Update(&ctx, (const uint8_t *)"\x00", 1);
}
blake2s_Final(&ctx, out, BLAKE2S_DIGEST_LENGTH);
}
blake2s_Final(&ctx, fingerprint, BLAKE2S_DIGEST_LENGTH);
secbool check_image_header_sig(const image_header *const hdr, uint8_t key_m,
uint8_t key_n, const uint8_t *const *keys) {
// check header signature
uint8_t fingerprint[32];
get_image_fingerprint(hdr, fingerprint);
ed25519_public_key pub;
if (sectrue != compute_pubkey(key_m, key_n, keys, hdr->sigmask, pub))

@ -107,4 +107,6 @@ secbool __wur check_image_contents(const image_header *const hdr,
uint32_t firstskip, const uint8_t *sectors,
int blocks);
void get_image_fingerprint(const image_header *const hdr, uint8_t *const out);
#endif

@ -133,7 +133,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;
}
}
@ -211,7 +211,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) {

@ -25,15 +25,22 @@
#include "common.h"
#include "display.h"
#ifdef FANCY_FATAL_ERROR
#include "rust_ui.h"
#endif
#include "memzero.h"
extern void main_clean_exit();
void __shutdown(void) {
void __attribute__((noreturn)) __shutdown(void) {
printf("SHUTDOWN\n");
main_clean_exit(3);
for (;;)
;
}
void __attribute__((noreturn)) shutdown(void) { __shutdown(); }
#ifdef RGB16
#define COLOR_FATAL_ERROR RGB16(0x7F, 0x00, 0x00)
#else
@ -46,6 +53,13 @@ __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: %d", file, line);
screen_fatal_error_c(msg, buf);
display_refresh();
#else
display_print_color(COLOR_WHITE, COLOR_FATAL_ERROR);
display_printf("\nFATAL ERROR:\n");
printf("\nFATAL ERROR:\n");
@ -74,42 +88,35 @@ __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();
for (;;)
;
shutdown();
}
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_c(label, msg);
display_refresh();
#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 +160,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.");
}

@ -40,12 +40,15 @@
})
#endif
void __attribute__((noreturn)) shutdown(void);
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) \

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

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

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

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

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

Loading…
Cancel
Save