From 5a991f324479d6c9e64d21ca433e7c8ab08aadd5 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Thu, 5 May 2022 13:47:19 +0200 Subject: [PATCH] feat(core/rust): bootloader implementation in rust --- core/.changelog.d/1049.added | 1 + core/SConscript.bootloader | 9 +- core/SConscript.firmware | 5 + core/SConscript.unix | 5 + core/assets/close.png | Bin 0 -> 4782 bytes core/assets/erase_big.png | Bin 0 -> 9543 bytes core/assets/info_small.png | Bin 0 -> 7583 bytes core/assets/menu.png | Bin 0 -> 5194 bytes core/assets/success_bld.png | Bin 0 -> 6451 bytes core/assets/warn_bld.png | Bin 0 -> 9462 bytes core/embed/bootloader/.changelog.d/1049.added | 1 + core/embed/bootloader/bootui.c | 306 ++--------- core/embed/bootloader/bootui.h | 32 +- core/embed/bootloader/icon_cancel.h | 11 - core/embed/bootloader/icon_confirm.h | 11 - core/embed/bootloader/icon_done.h | 11 - core/embed/bootloader/icon_fail.h | 11 - core/embed/bootloader/icon_info.h | 11 - core/embed/bootloader/icon_install.h | 11 - core/embed/bootloader/icon_logo.h | 11 - core/embed/bootloader/icon_safeplace.h | 11 - core/embed/bootloader/icon_welcome.h | 11 - core/embed/bootloader/icon_wipe.h | 11 - core/embed/bootloader/main.c | 243 +++++---- core/embed/bootloader/memory.ld | 15 +- core/embed/bootloader/messages.c | 37 +- core/embed/bootloader/messages.h | 2 + .../bootloader_ci/.changelog.d/1049.added | 1 + core/embed/bootloader_ci/messages.c | 4 +- core/embed/firmware/main.c | 24 +- core/embed/rust/rust_ui.h | 22 + core/embed/rust/src/error.rs | 12 +- core/embed/rust/src/lib.rs | 1 + core/embed/rust/src/micropython/buffer.rs | 14 +- core/embed/rust/src/strutil.rs | 11 + core/embed/rust/src/trezorhal/display.rs | 6 + .../rust/src/ui/component/text/paragraphs.rs | 66 ++- core/embed/rust/src/ui/display/mod.rs | 21 +- core/embed/rust/src/ui/geometry.rs | 9 + core/embed/rust/src/ui/layout/util.rs | 39 -- core/embed/rust/src/ui/mod.rs | 3 +- .../src/ui/model_tt/bootloader/confirm.rs | 195 +++++++ .../src/ui/model_tt/bootloader/connect.rs | 50 ++ .../rust/src/ui/model_tt/bootloader/intro.rs | 100 ++++ .../rust/src/ui/model_tt/bootloader/menu.rs | 106 ++++ .../rust/src/ui/model_tt/bootloader/mod.rs | 513 ++++++++++++++++++ .../rust/src/ui/model_tt/bootloader/theme.rs | 304 +++++++++++ .../rust/src/ui/model_tt/component/button.rs | 24 +- .../rust/src/ui/model_tt/component/mod.rs | 6 +- .../rust/src/ui/model_tt/component/result.rs | 93 ++++ core/embed/rust/src/ui/model_tt/constant.rs | 2 + core/embed/rust/src/ui/model_tt/mod.rs | 3 + .../embed/rust/src/ui/model_tt/res/close.toif | Bin 0 -> 94 bytes .../embed/rust/src/ui/model_tt/res/erase.toif | Bin 0 -> 148 bytes .../rust/src/ui/model_tt/res/erase_big.toif | Bin 0 -> 220 bytes .../rust/src/ui/model_tt/res/info_small.toif | Bin 0 -> 191 bytes core/embed/rust/src/ui/model_tt/res/menu.toif | Bin 0 -> 47 bytes .../rust/src/ui/model_tt/res/reboot.toif | Bin 0 -> 126 bytes .../rust/src/ui/model_tt/res/receive.toif | Bin 0 -> 203 bytes .../rust/src/ui/model_tt/res/success_bld.toif | Bin 0 -> 111 bytes .../src/ui/model_tt/res/trezor_empty.toif | Bin 0 -> 647 bytes .../rust/src/ui/model_tt/res/warn_bld.toif | Bin 0 -> 192 bytes core/embed/rust/src/ui/model_tt/screens.rs | 104 ++++ core/embed/rust/src/ui/model_tt/theme.rs | 8 + core/embed/rust/src/ui/screens.rs | 40 ++ core/embed/rust/src/ui/util.rs | 42 ++ core/embed/trezorhal/common.c | 76 ++- core/embed/trezorhal/common.h | 8 +- core/embed/trezorhal/image.c | 15 +- core/embed/trezorhal/image.h | 2 + core/embed/trezorhal/touch/ft6x36.c | 4 +- core/embed/unix/common.c | 61 ++- core/embed/unix/common.h | 7 +- legacy/common.c | 9 + legacy/common.h | 2 + storage/storage.c | 9 +- storage/tests/c/common.c | 11 +- storage/tests/c/common.h | 5 +- 78 files changed, 2117 insertions(+), 681 deletions(-) create mode 100644 core/.changelog.d/1049.added create mode 100644 core/assets/close.png create mode 100644 core/assets/erase_big.png create mode 100644 core/assets/info_small.png create mode 100644 core/assets/menu.png create mode 100644 core/assets/success_bld.png create mode 100644 core/assets/warn_bld.png create mode 100644 core/embed/bootloader/.changelog.d/1049.added delete mode 100644 core/embed/bootloader/icon_cancel.h delete mode 100644 core/embed/bootloader/icon_confirm.h delete mode 100644 core/embed/bootloader/icon_done.h delete mode 100644 core/embed/bootloader/icon_fail.h delete mode 100644 core/embed/bootloader/icon_info.h delete mode 100644 core/embed/bootloader/icon_install.h delete mode 100644 core/embed/bootloader/icon_logo.h delete mode 100644 core/embed/bootloader/icon_safeplace.h delete mode 100644 core/embed/bootloader/icon_welcome.h delete mode 100644 core/embed/bootloader/icon_wipe.h create mode 100644 core/embed/bootloader_ci/.changelog.d/1049.added create mode 100644 core/embed/rust/src/strutil.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/confirm.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/connect.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/intro.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/menu.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/mod.rs create mode 100644 core/embed/rust/src/ui/model_tt/bootloader/theme.rs create mode 100644 core/embed/rust/src/ui/model_tt/component/result.rs create mode 100644 core/embed/rust/src/ui/model_tt/res/close.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/erase.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/erase_big.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/info_small.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/menu.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/reboot.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/receive.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/success_bld.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/trezor_empty.toif create mode 100644 core/embed/rust/src/ui/model_tt/res/warn_bld.toif create mode 100644 core/embed/rust/src/ui/model_tt/screens.rs create mode 100644 core/embed/rust/src/ui/screens.rs diff --git a/core/.changelog.d/1049.added b/core/.changelog.d/1049.added new file mode 100644 index 000000000..97c386e6b --- /dev/null +++ b/core/.changelog.d/1049.added @@ -0,0 +1 @@ +Error screens redesign diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index ec8b33e53..56d41d4d1 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -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', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 62bb0bf03..06022f6fe 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -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', diff --git a/core/SConscript.unix b/core/SConscript.unix index e1b8724e8..dad147417 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -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: diff --git a/core/assets/close.png b/core/assets/close.png new file mode 100644 index 0000000000000000000000000000000000000000..e758076743f3258fbac1d092d44ad4953e463da1 GIT binary patch literal 4782 zcmeHKX;c$g7LG^@B21$#OUo7mPP3S#QrROB*$I+>hzlNEic|$lvY7-3(r(Zq1{FpW z6al*XGl^`S_68u^lE^||GuX@XVL?DdE)k-LzbqRh0PVHb3GJnq$4L+b4NqR*o7_+byMx_|EOymDFfuGfj1=WQuJ z_2rDLmMhk^KiW_22@LsrE2(BYET0(J)?DV4>XtrxuAkF`<8G$i3ktS-)$N}hvyHmx zmoi8G8oskL9(Xsfd-}isvvT=%v#Zym+AgoJbX?p2bnx}zy$2zF!20%DcK_r23f+{i z{0d(N-pM`slE|*Bhx*37y^5Z&@syIkzEE+t7G5^-4Xkmp zJ9^w}PR5EQot6{LH%!|Ssc+c5&3vPV;HfN}@yybsxY0@kZx1tZC~({T)71S7cJ87% zLfg>^Q*^&#D{@K*3%BY9#aAje>vTa2gsUFqSGfsjcxc1b5Z#6PSf~n>$ZzU0gBOL% ze<`X(n^TJqaIs58YRVU^NesItinTk|6Wzm!-~F?+vL%^fbG!7XgUkhomZ{0x_A7S@Lgp#n|_XcBJ&Y)Gcw?_rs|vw?&=ikK8zGvpt@?uB(1@+NS&LKxR_uZKZqP zpniL2c;~=BTsoRIhhIPTrt3y<-V>k9?^0^}MYF>DtP=W7fBSAx=Pi9v+@==4->;?9 zGdgbGbP4u|sT^v5H*Zzmt${r*%jchOc};(L#cWUSH*j~(n~ts_|5i%Rey_MbIx}v^ zki(<%Ro2TJf)xjc;@|Z4J$P60?elb>t4}9Me)oFoGo#~Yrl+z!mp~Xt!u|at1pfZ- zHyqez1%=raq4jg728Qc9!tHJ865PWPy3A-&QaC@}mM+J*HxPmH$nQ%wxg@o*&Y2CW$780mn%Z7Dm_oZU4dE zl)DqlAO31{sdw&mZB@%)vhDq1T`_%uvkhj|cKQUnd{Uh!OK$e=hu6MO>Z6@A_tno@ zZR^0QS(hu)yM(LvK5#q3VXwW$qFgMv-wC1Tckj$U!dUb$!#O1~Z%1~DCkYSL-;JU( z+4@is#Z133q#AFyz3Q2FiRA-YZl2(TYffoH-S7RcZe5J7#AVI8e_HNsD`<4yc5iU< zLX*Yo(@P%b9&YZhu4;Oozi0jFr;{?CM=+1+3NC}TH4O*tX|XVjCsxQP2&#z1C|a2k zv@Zg|+efQJ#POJh7>mW>az3fQ<_w95qkPgLrVtV;{jrsJ$Ql(ke~lo1MU+$;1)|Di)X^4d zO<*D*842i5E!0t<%TpsUwIV?!#sU*DxyE%g1uFhvuS`%$jp?9bDkjBb0ICLFX`i?Z z76>CgSQsRT!(~dN6%hLqqz0FK6zh}N3^T@bMh61SKk$BnexJJ$3{XNLFF+wqFoY)v z;FApgd8k5+qdendECXgh5R8%`CIXWgVm6Z;i!!+2i4LDSi*sfRH&F2q)^Eau$;IIiNmN$d7N>=AUMxA zLck}%lwqiGBtnX4B*1`A3d7|I+KfEk#bgG=M!|Zc7@5hYV+;yT;IqJE)ioy9XDRSm;IZoZzsY6s z@xX)0!M~s+aGcrF7cd7Lg-l}S1_uy^HfJ@R0dEtPA<=3AVbWKIYaAiJz#a^mX#~PR zvxg?;*3)M8z%5I`&>BI2Z#dm;IR~ygPY$cm>D(O$63(62@FLpees_lHP1gfYvSZ}dH+F_I zAC~65O0>PTw&>-3pD#l!d$PTg(v;mNy^dyVoVUI6a+z_DBy_S Hiq!uC7u6r~ literal 0 HcmV?d00001 diff --git a/core/assets/erase_big.png b/core/assets/erase_big.png new file mode 100644 index 0000000000000000000000000000000000000000..4282cf69d03f1497f7018130c96fab3c6150b806 GIT binary patch literal 9543 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 GMf?wCQ-2oS1(QbcKrG?gy(A{~_~ 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 F{{T1RsapU5 literal 0 HcmV?d00001 diff --git a/core/assets/menu.png b/core/assets/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..55027b2da4d71fde67311cbbce583125b7a1215f GIT binary patch literal 5194 zcmeHKX;f238;;bpyeOojZ5td7pXTXWp5~>qYk(vexm26kV$yDDC_&+uF~u5F||_eNv%tUYJKJ!(g5EAm%d zq_b~!eG_OuoG~`$5$5^u?6ff5SwLgS%{fcUoa>u2EML^DihXLAa>cGiU^gsS5;vA} z$j0c=imRiYGfLJjn78fM9ohQa?7f@SYrUS9sL6##vNu;}pH;10V)Dm9|Ca$eM@|Hf z-184O(G_c1b#0lk;QpDK`0R|U?;cQ035ZX0=UtPai^9wZwtl*TmPfckbN$f6s77n$l=o*YwCRJ z+m$wX9eWoX+25YYo?)v}mj0r*9our|_M%P68L5XKHHHqV6m_(H;iC`A6y0xn33d-* zb#!svb`z%8BF`PfYBehrb2M+Zt;=;;)3Ge3j~zFCf0F8n%wxNXdkCof<;dMOMyB+D z)a8}gy6`89_k_@!_8t%ZW7kk@$?2m9@_Bm2oc!1e+sxO+{Azu(IbLc`+N5`cx{q)w-=ZYv9QNXWL-%7gKX5;hPZU3l+ni2HV z4`;aCVChPRF=Wx^Y!doM>?Z|%{!Zv12!Okj8D+oAyJ zN&~Nghu@oJZJHH_f{n-rY@I1F&%nacVV!gJywuw4am%~wtye5;KKpz-ebhP&?__*! zWu;vAlJCjqnd8Bi*{c@pZo=+D-0`o-v^}+WT}AD5CyN?&ul(rp+lI5c?JY`;bKWHr zPHlFtjA<|*CZWPoBL-=Pr-y6Qt4}STEN)$u)~K~b(B=gLv;v(+OHr?_UcK4S@$zC% z+s;w@tJIq4w&bpcoujsCk87e63zdC@>vzhE1PyneTBxB}?|v*^W0~)oUHF1uU%FVg zCrZfoFKdYx9{uUhXJTyAd|5LxX^?^lu{KMPJ*ti@S9hYRw?f|=!ktdZ`niM<8tEBr-z?~>0aS11a%ZR5>X zb~W^$-^iW8x6L>cv&rI24Kkv3Vfc)-tq&}T3)X}-%sle5dSkbK+2FG|&1&D@l@)E{ z8_XXRr4`}6vzh}K-@b6xq0F#h8BwVF?(i!YaFtu_ENEO2XKqP6w<*`ZaAANnf54`e z9rS?k@$ni}3de%W~?^cS9>HSLs9{%zGYtBl@p(=|FFi7Cayxuva-t14Qa zAK0IG;fZ1D^FTs*nyMW8(I|lcHPu2Yqs48xEU=0{DOcgi66tF`o_T>FpQz(Ly5umnRTw ztw6CqL&|uZPhx%Mo2Ew_&QwQ0^N+lrp+Cf~1%oI*KZ?7ECD*v;?M{Vj#;34FEFPPp ztzsSU7&04!LvU~mGy;z&00<_53?N7t5|P6qlW-gYdkU4eP%2{xS%8KLBuDZ<9wv*7 zCE}QP1lxg(Mc|1TJc3Cg;1NWyWHE_kHj6`=LJ=V0fmzAmPxVSe#RjR^0Fl5TV>k#l zkwHe_F(d+l#3q6@EDM9f;5bAF0!~ZCW>Gvu5&;98PM(0l1yEukSKFZxoZ=GbO@(8T z=ua(ye1?nz8c^Y@ctW}IQ%5jQ00hYx8Z|K>JIMiuCX*dV4g?3{C(s%|A_X&1!-+v7 zaTC0nX`z6@fWk5~c?uF}Ex=ePZW4eY6G?(aB0d$aaS5ie)TTGgabi-ucv29tL6h;5 zIUfYXO?*u(0X|RL1%qkRmcn373?gO30~0d>+D&w^q8UOi0Iu)Jg!<6V`-jOQ;+Z%S zflWlZi`G=_pk!%>p~qcp4fL&T1#|Ix%z3z)JEfOZpY z;PwLUR;Z8L)ud(`8UMxKzsmJht}jyHi@;yA>#JN}q`((}zh>9} zO)i5^7al+e{tHro*O>wPxq9GLNQdd`=?;0neOF5@_@yuQ3Y9`2hO;%#G{^xJ6l~O$ zdHd0H2X*vJX3fsah>ixEsNU`_!Asj%Z`WV!ZF7c(M=}29ieavOo+*9Pq3=;qr)*vC yPcu*Xk5Rgj^~|^H-xokn6>RHU)ZhYdxu*wFLNK1~aXG~x5yYG3?|#BHD*3+|vHElX literal 0 HcmV?d00001 diff --git a/core/assets/success_bld.png b/core/assets/success_bld.png new file mode 100644 index 0000000000000000000000000000000000000000..1119e3864dd8264472a23842009f897346cb45c7 GIT binary patch literal 6451 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 T-Grw2$O&K?`s$UccESGx^b#>= literal 0 HcmV?d00001 diff --git a/core/embed/bootloader/.changelog.d/1049.added b/core/embed/bootloader/.changelog.d/1049.added new file mode 100644 index 000000000..873319b6a --- /dev/null +++ b/core/embed/bootloader/.changelog.d/1049.added @@ -0,0 +1 @@ +Bootloader redesign diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 69dc9346d..ae5a9d6c8 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -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 - } -} diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h index 73285e419..5159d7a7a 100644 --- a/core/embed/bootloader/bootui.h +++ b/core/embed/bootloader/bootui.h @@ -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 diff --git a/core/embed/bootloader/icon_cancel.h b/core/embed/bootloader/icon_cancel.h deleted file mode 100644 index 50ac0a813..000000000 --- a/core/embed/bootloader/icon_cancel.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_confirm.h b/core/embed/bootloader/icon_confirm.h deleted file mode 100644 index 4c6ac8309..000000000 --- a/core/embed/bootloader/icon_confirm.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_done.h b/core/embed/bootloader/icon_done.h deleted file mode 100644 index a7cdb382d..000000000 --- a/core/embed/bootloader/icon_done.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_fail.h b/core/embed/bootloader/icon_fail.h deleted file mode 100644 index e11530a1d..000000000 --- a/core/embed/bootloader/icon_fail.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_info.h b/core/embed/bootloader/icon_info.h deleted file mode 100644 index d1e22b2a1..000000000 --- a/core/embed/bootloader/icon_info.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_install.h b/core/embed/bootloader/icon_install.h deleted file mode 100644 index 6b4eb2b9e..000000000 --- a/core/embed/bootloader/icon_install.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_logo.h b/core/embed/bootloader/icon_logo.h deleted file mode 100644 index fc2e93b5e..000000000 --- a/core/embed/bootloader/icon_logo.h +++ /dev/null @@ -1,11 +0,0 @@ -// clang-format off -static const uint8_t toi_icon_logo[] = { - // magic - 'T', 'O', 'I', 'G', - // width (16-bit), height (16-bit) - 0xf0, 0x00, 0xf0, 0x00, - // compressed data length (32-bit) - 0x85, 0x04, 0x00, 0x00, - // compressed data - 0xed, 0x92, 0x4f, 0x68, 0x5c, 0x45, 0x1c, 0xc7, 0x7f, 0xbb, 0x2f, 0xdb, 0xdd, 0xf4, 0x9f, 0x0b, 0xb6, 0xd4, 0x3f, 0x11, 0x16, 0x2f, 0x0d, 0x05, 0x49, 0xda, 0x85, 0x22, 0xa9, 0x74, 0x17, 0x5c, 0x3c, 0xf4, 0xb2, 0x11, 0x0f, 0xe2, 0xa1, 0x6c, 0x15, 0x8c, 0xc7, 0x06, 0x45, 0x0a, 0x82, 0x58, 0x73, 0x8a, 0x5e, 0x12, 0xd4, 0x83, 0x37, 0xe3, 0x45, 0x70, 0xb1, 0xb4, 0x17, 0x2f, 0x22, 0x26, 0xa2, 0xde, 0x84, 0x67, 0x0a, 0x22, 0x8a, 0x18, 0x9b, 0x22, 0x7a, 0x10, 0x5e, 0x6c, 0xbb, 0x8d, 0xf9, 0xf3, 0x76, 0x9c, 0xf9, 0xcd, 0xbc, 0xf7, 0xf6, 0x6d, 0x92, 0x65, 0xdd, 0xe7, 0xce, 0x56, 0xf8, 0x7e, 0x0e, 0x3b, 0xbf, 0x99, 0xf9, 0xed, 0x7c, 0xde, 0xcc, 0xef, 0x47, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x2c, 0xf9, 0x81, 0x58, 0x5f, 0xf9, 0x49, 0x88, 0x2b, 0xa7, 0xad, 0x6b, 0xbf, 0x69, 0x0a, 0x89, 0x77, 0xc6, 0xb2, 0x76, 0x41, 0x18, 0x72, 0x56, 0xb5, 0x05, 0x3f, 0xf0, 0x2e, 0xa6, 0x6c, 0x7a, 0x6f, 0x8a, 0x90, 0xc7, 0x6c, 0x76, 0xb2, 0x1f, 0x79, 0xdd, 0xd4, 0x00, 0xaa, 0xab, 0xc8, 0xda, 0xf3, 0x36, 0xf8, 0xa2, 0x6f, 0xbd, 0xcf, 0xd7, 0x1e, 0xb3, 0xe7, 0x6d, 0x4a, 0xdd, 0x8a, 0x43, 0x54, 0x56, 0x81, 0x6b, 0x4d, 0x5b, 0x56, 0xd7, 0x3c, 0xa0, 0xa2, 0x2f, 0x55, 0x94, 0xb6, 0xe5, 0x9d, 0x57, 0xd7, 0xe5, 0xa8, 0xd0, 0xb4, 0x59, 0xe0, 0x6b, 0x52, 0x56, 0x8a, 0x2a, 0xbd, 0xdf, 0x96, 0x77, 0x59, 0xca, 0x0e, 0xe9, 0x70, 0x4d, 0x86, 0x07, 0x6d, 0x79, 0xaf, 0x4b, 0xd9, 0xb0, 0x0e, 0x97, 0x64, 0x38, 0x62, 0xcb, 0xbb, 0x2a, 0x65, 0xb9, 0xe8, 0xc9, 0xe1, 0xed, 0x63, 0x7d, 0xad, 0x7b, 0x4f, 0x56, 0x2a, 0xbf, 0x49, 0xd9, 0x44, 0x85, 0xf9, 0x4a, 0x86, 0xe7, 0x2b, 0x15, 0xa7, 0xff, 0xde, 0x86, 0xd8, 0x85, 0xd2, 0x80, 0xbc, 0x67, 0xe1, 0x85, 0xf7, 0xff, 0xeb, 0xbd, 0x7a, 0xa9, 0x85, 0x2d, 0x7b, 0xde, 0x6a, 0xeb, 0xc2, 0x3a, 0xbc, 0xf0, 0xc2, 0x0b, 0x2f, 0xbc, 0xf0, 0xc2, 0x0b, 0xef, 0x7f, 0xef, 0xad, 0x0d, 0xc8, 0xeb, 0x1e, 0x8d, 0xe6, 0x2f, 0xf8, 0xf6, 0xbc, 0x62, 0xe5, 0x81, 0x60, 0xfa, 0x8e, 0xd2, 0x5a, 0xf3, 0x0a, 0x91, 0xd3, 0xb3, 0x0f, 0xf5, 0xcc, 0x9e, 0x57, 0x8c, 0xaa, 0xc9, 0xbb, 0xc2, 0xba, 0xd7, 0x3b, 0x47, 0xf4, 0xa9, 0x89, 0x45, 0xa9, 0xff, 0xde, 0xeb, 0x81, 0x4b, 0x3c, 0xf1, 0x7d, 0x18, 0x3e, 0xdc, 0x7f, 0x2f, 0x7d, 0x2d, 0xda, 0xf1, 0xce, 0x90, 0x0d, 0xde, 0x6b, 0xd7, 0x3e, 0x4e, 0x76, 0x98, 0x89, 0x7b, 0x8f, 0x91, 0x2d, 0xe6, 0x5b, 0x6f, 0x9b, 0x25, 0x7b, 0xbc, 0xe9, 0x07, 0x5a, 0xf7, 0x18, 0xd9, 0xe4, 0x69, 0x23, 0x5e, 0x3c, 0x42, 0x76, 0x29, 0xb3, 0xd8, 0x75, 0xc8, 0x36, 0x93, 0x5b, 0x42, 0x7c, 0x31, 0x44, 0xf6, 0x19, 0xdf, 0x98, 0x73, 0x68, 0x10, 0xe4, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7b, 0x88, 0x0b, 0x97, 0x22, 0x8e, 0xd3, 0xcb, 0x26, 0x3a, 0xca, 0x7b, 0xd1, 0xce, 0xb3, 0xad, 0x79, 0x2f, 0xf1, 0x66, 0xf9, 0x47, 0x21, 0xae, 0x64, 0xf4, 0x21, 0xaf, 0x9a, 0x0d, 0xa7, 0x7b, 0xef, 0x77, 0x22, 0xa2, 0x4a, 0x77, 0x82, 0xf0, 0xf5, 0x94, 0xdc, 0x8b, 0x76, 0x5c, 0xba, 0x11, 0x4d, 0x3c, 0xf5, 0xbf, 0x19, 0x1d, 0x4f, 0xf0, 0x21, 0xdb, 0xc1, 0xd6, 0x44, 0xd7, 0xde, 0xa5, 0xdd, 0xbd, 0xe2, 0x7c, 0x67, 0xef, 0xe5, 0x60, 0x72, 0x42, 0x1d, 0xb2, 0x15, 0xee, 0x3d, 0x94, 0xd4, 0x2b, 0xb2, 0x9d, 0xbc, 0x79, 0x3f, 0x9c, 0x64, 0x62, 0x5e, 0x2f, 0x95, 0xd4, 0x5b, 0xed, 0xe4, 0xbd, 0x15, 0xcd, 0x6a, 0x31, 0xaf, 0x38, 0x98, 0xd4, 0xeb, 0xa6, 0xf6, 0xf6, 0x46, 0xd7, 0x95, 0x0c, 0xc5, 0xbc, 0xb5, 0x64, 0x7d, 0xc5, 0xe7, 0xed, 0xe1, 0x75, 0x69, 0xa1, 0x25, 0x4d, 0x1c, 0x8e, 0x79, 0xdd, 0x2e, 0xbd, 0xd3, 0xb3, 0xb3, 0xb3, 0x7f, 0xc8, 0xfc, 0x39, 0x39, 0x8e, 0xb2, 0xf7, 0xb9, 0xa9, 0xa9, 0x1f, 0xd4, 0x09, 0xc3, 0xca, 0xeb, 0xbd, 0x38, 0xa5, 0x78, 0x8a, 0xf3, 0x74, 0xa6, 0xb8, 0x4f, 0x7f, 0xde, 0x9c, 0x53, 0xf8, 0x5d, 0x8d, 0x8b, 0xda, 0x2b, 0x13, 0x37, 0xd4, 0x34, 0xd5, 0x75, 0x4b, 0xd3, 0x35, 0x3e, 0x4c, 0xa1, 0x0e, 0x54, 0x8d, 0xb2, 0xce, 0x95, 0x92, 0x3f, 0x2b, 0xf1, 0xcc, 0x86, 0x7e, 0x88, 0xa6, 0xfc, 0xfd, 0x80, 0x4c, 0x9e, 0x70, 0xd8, 0x4b, 0xa6, 0xdb, 0x32, 0xbd, 0x7a, 0xf7, 0xc9, 0x71, 0x99, 0xdf, 0x6f, 0x87, 0x77, 0x5c, 0x09, 0xe7, 0x68, 0x52, 0xd9, 0x72, 0x6a, 0x61, 0x52, 0x3f, 0x8c, 0xf1, 0xd2, 0x5d, 0x39, 0x66, 0x93, 0x78, 0xf5, 0xca, 0x0e, 0xaf, 0x5a, 0x16, 0x87, 0x78, 0x30, 0x1b, 0x7f, 0xcb, 0x70, 0x24, 0xf4, 0x36, 0x82, 0xef, 0x49, 0x7c, 0x5f, 0xef, 0x54, 0x51, 0x71, 0xbf, 0x4e, 0xe4, 0x67, 0x4d, 0xf3, 0x6e, 0x49, 0xaf, 0xfc, 0x2a, 0xc3, 0xb1, 0xd0, 0xbb, 0x9e, 0xe4, 0xbe, 0xb2, 0x40, 0xf9, 0xcd, 0xa0, 0xbe, 0x86, 0x8b, 0x14, 0xbe, 0x6a, 0x95, 0xe8, 0xa6, 0xee, 0x62, 0xc5, 0x65, 0x19, 0xd6, 0x02, 0x6f, 0xb9, 0xc9, 0xd5, 0xef, 0xd1, 0xfb, 0x49, 0xbd, 0xfe, 0xa7, 0x2e, 0x60, 0xbb, 0x77, 0xd5, 0xd4, 0xf5, 0x0e, 0x17, 0x35, 0xfc, 0x94, 0x37, 0xd8, 0x5b, 0xaf, 0x7f, 0xe6, 0x27, 0xea, 0xe7, 0x00, 0x67, 0x87, 0x57, 0x1d, 0xef, 0xa6, 0x74, 0x19, 0xcd, 0x73, 0x96, 0xb9, 0xb3, 0xb7, 0xa2, 0xcc, 0x45, 0x4a, 0xe8, 0x95, 0x07, 0xb4, 0x79, 0xa7, 0x85, 0xa9, 0x6b, 0xc3, 0x74, 0x81, 0xea, 0xf0, 0x76, 0x6f, 0x35, 0xa9, 0x77, 0x6c, 0x87, 0x97, 0x37, 0x33, 0xd4, 0xf9, 0xbe, 0xc3, 0x49, 0xbd, 0xe9, 0x76, 0x6f, 0x5e, 0xb0, 0xc3, 0x64, 0xed, 0x6f, 0xaf, 0xef, 0xbf, 0x7f, 0xe6, 0xdd, 0xbc, 0xde, 0x09, 0xd2, 0xde, 0x8f, 0xeb, 0x8a, 0x73, 0x26, 0x4b, 0x3c, 0xa2, 0xb2, 0x5a, 0xfa, 0x79, 0x9e, 0x9f, 0x36, 0xf4, 0xae, 0x64, 0x13, 0x7a, 0xf9, 0xbb, 0xd5, 0x41, 0x51, 0xd6, 0xba, 0x6e, 0x36, 0xc9, 0xb2, 0x0c, 0x6a, 0x7a, 0x71, 0x8d, 0x2b, 0x12, 0x7a, 0x4b, 0xd4, 0xbb, 0xf7, 0xf8, 0x8c, 0xba, 0x6f, 0xba, 0xdd, 0x5b, 0x10, 0xfc, 0xa4, 0x41, 0xbe, 0xa7, 0x57, 0x37, 0x65, 0x38, 0xc2, 0xde, 0x47, 0x57, 0x83, 0x32, 0xf4, 0xe8, 0xcd, 0x50, 0xd3, 0xf4, 0x47, 0xcc, 0x7b, 0x43, 0x84, 0x55, 0x9d, 0x0c, 0xc3, 0x0b, 0x2a, 0xca, 0xb1, 0x97, 0x5b, 0xcc, 0x73, 0x7a, 0xf7, 0xee, 0xa3, 0xdb, 0xe6, 0xc5, 0x62, 0x5e, 0x75, 0xb6, 0x6b, 0x62, 0x5f, 0x55, 0x42, 0x2a, 0xf2, 0x1b, 0xba, 0x03, 0xd9, 0xcb, 0xbf, 0x07, 0x92, 0x78, 0x17, 0x8c, 0x50, 0x1d, 0xca, 0x6d, 0x55, 0x7f, 0x5b, 0xdf, 0xcc, 0xfb, 0x56, 0xf1, 0x39, 0x7f, 0x98, 0x70, 0x4f, 0x3f, 0xb3, 0x69, 0x5a, 0x5c, 0x7b, 0x6f, 0x47, 0x65, 0xef, 0xcd, 0x3b, 0x2e, 0x78, 0x20, 0x11, 0xe2, 0xea, 0x67, 0x36, 0xbd, 0xce, 0x5d, 0x1c, 0x72, 0x38, 0xf0, 0xaa, 0xcf, 0x15, 0x49, 0xbc, 0xdc, 0x2d, 0x63, 0x1d, 0xbc, 0xf9, 0xed, 0x96, 0x59, 0x3a, 0xf0, 0xf2, 0xe7, 0x0e, 0x27, 0xf1, 0xde, 0xd2, 0xcf, 0xb7, 0xa7, 0xb7, 0x75, 0x56, 0xa5, 0xc0, 0xcb, 0x9f, 0x7b, 0x36, 0x89, 0x97, 0xdf, 0xd1, 0xe9, 0xe0, 0xa5, 0xf0, 0xc2, 0xdc, 0xc1, 0xc6, 0xbb, 0x26, 0x87, 0xab, 0x49, 0xbc, 0xd4, 0xe4, 0xd6, 0xec, 0xe0, 0x9d, 0xf6, 0x4d, 0xfc, 0x20, 0x45, 0x5e, 0xee, 0xbd, 0xa1, 0xee, 0xbd, 0x4b, 0xa1, 0xb7, 0x61, 0xbc, 0x77, 0xe5, 0x78, 0x31, 0xe6, 0xfd, 0x2b, 0xee, 0xa5, 0xe7, 0x59, 0xec, 0x8d, 0xf2, 0xdf, 0xb6, 0xb5, 0x37, 0xef, 0x87, 0x07, 0x75, 0x45, 0xa1, 0x58, 0x2c, 0xea, 0xcf, 0x3c, 0x59, 0x2c, 0x3e, 0x99, 0x92, 0xe3, 0xb8, 0x5c, 0x29, 0x52, 0x31, 0x42, 0xaf, 0x04, 0x13, 0x45, 0xfe, 0xa3, 0x9f, 0x7f, 0x79, 0x2d, 0xad, 0x0f, 0x38, 0x65, 0x16, 0xd5, 0x78, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x99, 0x7f, 0x00, -}; diff --git a/core/embed/bootloader/icon_safeplace.h b/core/embed/bootloader/icon_safeplace.h deleted file mode 100644 index 1089f3781..000000000 --- a/core/embed/bootloader/icon_safeplace.h +++ /dev/null @@ -1,11 +0,0 @@ -// clang-format off -static const uint8_t toi_icon_safeplace[] = { - // magic - 'T', 'O', 'I', 'G', - // width (16-bit), height (16-bit) - 0xc8, 0x00, 0x3c, 0x00, - // compressed data length (32-bit) - 0xc3, 0x04, 0x00, 0x00, - // compressed data - 0xed, 0x92, 0x4f, 0x68, 0x1b, 0x47, 0x14, 0xc6, 0x47, 0xb2, 0x64, 0xcb, 0x52, 0xf4, 0xe7, 0x50, 0x68, 0x7b, 0xb2, 0xd9, 0x5c, 0x5a, 0x0a, 0xb1, 0x29, 0xee, 0xc1, 0x50, 0xf0, 0x52, 0x02, 0x2d, 0x24, 0x35, 0x02, 0x1d, 0x4b, 0x88, 0xa1, 0xd7, 0x12, 0x09, 0x72, 0x2d, 0x58, 0xa5, 0xc7, 0xc6, 0xd8, 0x49, 0xce, 0x4d, 0x74, 0xce, 0xc5, 0xbe, 0xb8, 0x97, 0x42, 0xe4, 0xd2, 0x86, 0xe6, 0x54, 0x39, 0x07, 0x1f, 0x0a, 0x4d, 0xa5, 0x43, 0x48, 0x8b, 0x93, 0x46, 0x8a, 0xfc, 0xdf, 0xd6, 0xee, 0x97, 0xf7, 0x66, 0x67, 0xb4, 0x23, 0xaf, 0xac, 0xb8, 0x71, 0xe5, 0x4b, 0xfd, 0x40, 0x9a, 0xdd, 0xf7, 0xbe, 0x79, 0xbf, 0x9d, 0x79, 0x9f, 0x10, 0x7e, 0x34, 0x90, 0x16, 0xaf, 0x8b, 0x65, 0xf7, 0x5a, 0xb7, 0x34, 0xd0, 0x7b, 0x5b, 0x03, 0x5e, 0xa4, 0x8f, 0xc1, 0x28, 0x01, 0xf3, 0xfd, 0x66, 0x6c, 0x9f, 0x02, 0xa3, 0x85, 0x4b, 0xe7, 0xdf, 0x84, 0x31, 0x6a, 0x59, 0x4d, 0x8c, 0x59, 0x56, 0xf8, 0x18, 0x0c, 0x17, 0x21, 0xf1, 0x26, 0x8c, 0xf6, 0xb4, 0x8f, 0xc1, 0x38, 0xaa, 0xd7, 0xbf, 0x61, 0x3c, 0xc0, 0x57, 0xf4, 0x30, 0xf3, 0xac, 0xfe, 0x85, 0xaa, 0xdc, 0x72, 0xea, 0x5f, 0xd3, 0xa7, 0x67, 0x7e, 0x75, 0x2b, 0xef, 0x70, 0x2b, 0xb0, 0xf2, 0x07, 0xf7, 0x7e, 0x54, 0xf5, 0x2e, 0xdf, 0x74, 0xef, 0x84, 0x3c, 0x86, 0xd2, 0x08, 0x7b, 0x1d, 0x73, 0x7c, 0x5a, 0x5f, 0xd5, 0xc1, 0x58, 0xa4, 0x1e, 0x09, 0x91, 0x71, 0xe4, 0xc2, 0x51, 0xe0, 0xae, 0xd3, 0x42, 0x3c, 0xe1, 0x75, 0x50, 0x31, 0x1e, 0xd1, 0x7f, 0x59, 0x31, 0xea, 0x2e, 0x70, 0xc1, 0x63, 0x28, 0x8d, 0xd8, 0xf3, 0xb6, 0x18, 0xaa, 0x0e, 0x06, 0xc7, 0x55, 0xb1, 0xca, 0xcb, 0x5d, 0x59, 0xd8, 0x92, 0xa9, 0xf0, 0xb8, 0x5c, 0xa6, 0x14, 0x83, 0x3f, 0x01, 0xc3, 0xea, 0x8e, 0x28, 0xaa, 0x92, 0xa1, 0x35, 0x45, 0x6f, 0x8b, 0xa9, 0xea, 0x60, 0x54, 0x3f, 0x5a, 0x23, 0xf6, 0x1e, 0xae, 0x7c, 0xe9, 0x92, 0x4a, 0xfa, 0xe8, 0x72, 0xce, 0x41, 0x7c, 0x09, 0xe5, 0x89, 0x35, 0x2c, 0x0a, 0x0b, 0xb0, 0xc2, 0x0b, 0x24, 0x7b, 0x8a, 0x31, 0xc5, 0xb8, 0x7c, 0x1d, 0x88, 0x32, 0x43, 0x6b, 0x9a, 0xc8, 0xe7, 0x5c, 0x0c, 0x9b, 0xaa, 0x0e, 0xc6, 0x08, 0x7d, 0x4d, 0x9d, 0x36, 0x84, 0xc4, 0x26, 0x62, 0xda, 0x47, 0x4d, 0xa4, 0x4a, 0x3f, 0x26, 0xc4, 0x28, 0x55, 0xe4, 0x9d, 0xb0, 0xcc, 0xc6, 0xbc, 0x62, 0x84, 0xc4, 0x2e, 0xe2, 0x9c, 0xd7, 0x9a, 0x5d, 0x0c, 0xd1, 0xee, 0x94, 0xa9, 0x3a, 0xec, 0x2b, 0xc0, 0x46, 0xd9, 0xb2, 0x36, 0x70, 0xce, 0x3b, 0x47, 0x4c, 0x49, 0x32, 0x16, 0xf7, 0xe7, 0xdf, 0x26, 0x3e, 0xb0, 0x3e, 0x44, 0xa5, 0xed, 0xa7, 0x0d, 0x24, 0x95, 0xaf, 0xa4, 0xc6, 0xa1, 0x1b, 0x28, 0xdd, 0x8b, 0x9b, 0xaa, 0x20, 0x63, 0x46, 0x5e, 0x28, 0x52, 0x5c, 0xd8, 0x04, 0xbe, 0x8b, 0x78, 0x5e, 0x81, 0x66, 0x6c, 0xcb, 0x72, 0xbd, 0xcd, 0xe0, 0x6d, 0xbc, 0x2a, 0x8d, 0xb2, 0xb1, 0xa9, 0x0a, 0x32, 0x0a, 0x1e, 0x83, 0x33, 0x62, 0xbc, 0x45, 0x4f, 0xef, 0x7b, 0x5e, 0xd1, 0x8c, 0x1d, 0xa8, 0x97, 0x43, 0x0c, 0xa5, 0x51, 0x15, 0x53, 0xd5, 0x9b, 0x21, 0xc6, 0x1f, 0xd2, 0xe3, 0x40, 0x01, 0xd5, 0xb7, 0xc4, 0x6b, 0x18, 0x5a, 0x73, 0x2c, 0x46, 0x96, 0xcc, 0x61, 0x84, 0xdd, 0x42, 0x62, 0x95, 0x0d, 0xa2, 0x19, 0x5b, 0x34, 0x63, 0x1d, 0xc6, 0x3c, 0xb4, 0x86, 0xe7, 0x61, 0x5f, 0x8c, 0x98, 0xaa, 0x20, 0x23, 0x43, 0x1f, 0x2e, 0xc4, 0xa0, 0x2c, 0xfc, 0xf1, 0x58, 0x88, 0x97, 0x6c, 0x92, 0x34, 0x79, 0x46, 0x31, 0x5e, 0x62, 0x4a, 0x88, 0x19, 0x61, 0xf8, 0x6a, 0x98, 0xf3, 0x5a, 0xc3, 0xbe, 0x22, 0x2b, 0x9a, 0xaa, 0x20, 0x83, 0x2e, 0x76, 0x76, 0xe2, 0x41, 0x59, 0x16, 0x0e, 0xc8, 0x57, 0x5b, 0x48, 0xd6, 0x50, 0x9e, 0xf8, 0x4b, 0x33, 0x16, 0x80, 0x4f, 0x73, 0xad, 0x84, 0x62, 0x7c, 0x7e, 0x1d, 0x88, 0x70, 0x5e, 0x6b, 0x9a, 0xc8, 0xe7, 0x1c, 0xc4, 0x4d, 0x55, 0x17, 0xc6, 0x23, 0x79, 0x93, 0x51, 0x2e, 0x6c, 0x00, 0xff, 0x00, 0x83, 0x45, 0x18, 0x33, 0xcf, 0x38, 0xfc, 0x7c, 0x55, 0x31, 0x28, 0x2a, 0x32, 0xaf, 0x35, 0xde, 0x1a, 0x36, 0x55, 0x5d, 0x18, 0x19, 0x36, 0xd3, 0xac, 0x37, 0x0c, 0x97, 0x1e, 0xe7, 0xf9, 0x3c, 0xa8, 0xbb, 0x74, 0x2f, 0x72, 0x88, 0x3f, 0xf1, 0xee, 0x21, 0xc5, 0xa0, 0x56, 0x17, 0x3c, 0xb6, 0xd6, 0xec, 0x53, 0x75, 0xba, 0x43, 0xd5, 0x85, 0x21, 0xb2, 0xcf, 0xea, 0x73, 0x61, 0xaf, 0xf2, 0xcd, 0x73, 0x7c, 0x4f, 0xd3, 0x99, 0x69, 0xd5, 0xdf, 0xdd, 0xa3, 0xa3, 0x79, 0x46, 0x59, 0x76, 0xff, 0x7c, 0x4f, 0xcf, 0xe3, 0xa6, 0x7b, 0x27, 0xe4, 0xed, 0xd2, 0x1a, 0x7b, 0x1d, 0x37, 0x42, 0x1d, 0xaa, 0x93, 0x85, 0x69, 0xce, 0x7e, 0xc5, 0x19, 0xe3, 0xff, 0xc7, 0x38, 0x8b, 0xb3, 0xe8, 0x19, 0xcb, 0xee, 0xb5, 0x93, 0xb6, 0xc8, 0xbe, 0x88, 0xf6, 0xac, 0x97, 0x80, 0xf9, 0x93, 0x32, 0x6a, 0x48, 0xf5, 0xac, 0x6f, 0x9f, 0x02, 0xa3, 0x85, 0x4b, 0xe7, 0xfb, 0x7d, 0x57, 0x2e, 0x42, 0x7d, 0x1f, 0x39, 0x70, 0x0a, 0x08, 0x20, 0x2d, 0xc4, 0x2d, 0xa7, 0x3a, 0x29, 0xdf, 0x8a, 0x6e, 0x4c, 0x0e, 0x29, 0xc9, 0x2f, 0x03, 0x2a, 0xdf, 0x60, 0x09, 0x7d, 0x0c, 0xad, 0x7f, 0x7b, 0xc3, 0xb3, 0xd7, 0x31, 0xc7, 0xe7, 0xf7, 0xeb, 0x28, 0xcf, 0x3c, 0xaf, 0xd2, 0xde, 0xd1, 0xdf, 0xf1, 0xdb, 0xdb, 0xdd, 0x18, 0x05, 0x5e, 0x62, 0xfc, 0x76, 0xc0, 0x0b, 0x6d, 0x1a, 0x13, 0x36, 0xea, 0x3a, 0xef, 0x33, 0xca, 0xca, 0x20, 0x7b, 0x94, 0x9f, 0x16, 0x66, 0x1d, 0xd5, 0x03, 0xa0, 0x22, 0xc4, 0x0e, 0x65, 0xea, 0x91, 0x2e, 0x0c, 0x2e, 0x60, 0xd6, 0x7b, 0x63, 0xc6, 0x12, 0xf2, 0xa2, 0x48, 0xed, 0x54, 0xde, 0x67, 0x28, 0x13, 0x16, 0x39, 0x8f, 0xb0, 0x59, 0x97, 0x19, 0x0c, 0x66, 0xe5, 0x32, 0xd6, 0xc1, 0xb0, 0x00, 0x2b, 0x9c, 0x01, 0x26, 0x6f, 0x83, 0x86, 0xaf, 0x19, 0x59, 0x94, 0xc5, 0x2a, 0xa6, 0x74, 0x3e, 0xc0, 0x68, 0x22, 0x9f, 0x73, 0x31, 0x6c, 0xd6, 0x81, 0x2b, 0x39, 0x07, 0x89, 0x15, 0x54, 0x26, 0xd6, 0xb0, 0x18, 0x9c, 0x79, 0x91, 0x93, 0xfb, 0xd4, 0x1d, 0xf8, 0xb8, 0x9d, 0x6d, 0x22, 0xa9, 0xf3, 0x3e, 0xa3, 0x32, 0x20, 0xcb, 0xbb, 0x18, 0x12, 0x9b, 0x48, 0x99, 0x75, 0xfe, 0xc4, 0x0d, 0xa4, 0x6a, 0x18, 0x11, 0xa3, 0x74, 0xcb, 0x01, 0xc6, 0x0a, 0xa6, 0x04, 0x09, 0xce, 0x49, 0xa1, 0x8c, 0x7d, 0x44, 0xb7, 0x11, 0xd3, 0x79, 0x9f, 0x31, 0xe2, 0x95, 0x1d, 0xba, 0xa7, 0xd2, 0xbd, 0xb8, 0x59, 0xe7, 0x3e, 0xb4, 0xae, 0xa8, 0x81, 0x05, 0x18, 0x35, 0xee, 0xd1, 0x40, 0xca, 0x37, 0xf2, 0x16, 0x86, 0x0f, 0x10, 0xd6, 0x79, 0xc3, 0x57, 0x1d, 0x86, 0x37, 0xeb, 0x8a, 0x61, 0x03, 0xe5, 0xcf, 0xba, 0x31, 0x1a, 0x9e, 0x36, 0xed, 0x33, 0x68, 0x23, 0xaa, 0xed, 0xfc, 0x51, 0x0c, 0xb3, 0xae, 0xfb, 0xfc, 0x4c, 0x33, 0xbb, 0x1b, 0x3e, 0x92, 0x61, 0x9c, 0xa3, 0x84, 0x59, 0xed, 0xa7, 0x1e, 0xe7, 0x68, 0x04, 0xcf, 0x21, 0xc4, 0xb7, 0x2d, 0xf0, 0x15, 0xf6, 0x98, 0x87, 0xca, 0xda, 0xd2, 0x80, 0xc1, 0x79, 0xa4, 0xfd, 0x79, 0xd8, 0x17, 0x23, 0x5d, 0xe6, 0xc1, 0xd5, 0x5f, 0xc8, 0x95, 0xbd, 0x7c, 0xa5, 0xd3, 0x2e, 0x08, 0xe9, 0xfb, 0x86, 0xcd, 0x62, 0x30, 0xd8, 0x57, 0xcd, 0xc3, 0xbe, 0xf2, 0x66, 0xfe, 0x38, 0x29, 0x32, 0xdd, 0x7c, 0xa5, 0x7d, 0x6e, 0x30, 0xf6, 0x40, 0x6d, 0x74, 0x9e, 0x4d, 0xff, 0xd4, 0x64, 0x34, 0x91, 0xcf, 0x39, 0x88, 0xeb, 0xba, 0xc1, 0x58, 0x22, 0x5f, 0x65, 0x69, 0x94, 0xd9, 0x17, 0x91, 0x4e, 0x86, 0xd8, 0xa1, 0xab, 0xa1, 0xfb, 0x37, 0x18, 0x9b, 0xd2, 0xc6, 0x2a, 0x5f, 0x80, 0x0c, 0x9f, 0x51, 0x94, 0xef, 0x61, 0x5d, 0x37, 0x18, 0xe3, 0x40, 0xd5, 0x45, 0x9e, 0x2c, 0x97, 0x3c, 0xc4, 0x90, 0x7b, 0x62, 0x26, 0xa3, 0x86, 0x8a, 0x91, 0x6f, 0xc9, 0x9d, 0x3e, 0x83, 0x2e, 0x08, 0x98, 0x6e, 0xd7, 0xcd, 0x79, 0x3c, 0xf1, 0x52, 0x41, 0x86, 0xb8, 0xed, 0x54, 0x27, 0x85, 0xc9, 0x58, 0xa0, 0x6f, 0xf1, 0xf3, 0x85, 0x56, 0x35, 0xd6, 0x32, 0x19, 0xf6, 0x3a, 0x6e, 0x84, 0xda, 0x75, 0x93, 0x91, 0x79, 0xe8, 0x72, 0xaa, 0xe3, 0xae, 0x8e, 0x88, 0x22, 0x8d, 0xb9, 0xdf, 0x51, 0x43, 0xa2, 0xdf, 0x88, 0x4f, 0x0e, 0x10, 0xed, 0x37, 0x83, 0x66, 0x2c, 0xfa, 0xcf, 0x98, 0xea, 0x3b, 0xc3, 0xb9, 0x3f, 0xf0, 0x5f, 0xb4, 0x79, 0x05, -}; diff --git a/core/embed/bootloader/icon_welcome.h b/core/embed/bootloader/icon_welcome.h deleted file mode 100644 index 097daf0ff..000000000 --- a/core/embed/bootloader/icon_welcome.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/icon_wipe.h b/core/embed/bootloader/icon_wipe.h deleted file mode 100644 index 574e794b3..000000000 --- a/core/embed/bootloader/icon_wipe.h +++ /dev/null @@ -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, -}; diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 8570431e2..cdcca3254 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -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)"); } diff --git a/core/embed/bootloader/memory.ld b/core/embed/bootloader/memory.ld index 2c9eb7440..5010b5814 100644 --- a/core/embed/bootloader/memory.ld +++ b/core/embed/bootloader/memory.ld @@ -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); diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index eab9c771b..2674198b8 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -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(¤t_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"); diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index 0937f6062..dd4f3df5b 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -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 diff --git a/core/embed/bootloader_ci/.changelog.d/1049.added b/core/embed/bootloader_ci/.changelog.d/1049.added new file mode 100644 index 000000000..873319b6a --- /dev/null +++ b/core/embed/bootloader_ci/.changelog.d/1049.added @@ -0,0 +1 @@ +Bootloader redesign diff --git a/core/embed/bootloader_ci/messages.c b/core/embed/bootloader_ci/messages.c index c2e0c92e6..78c0bf383 100644 --- a/core/embed/bootloader_ci/messages.c +++ b/core/embed/bootloader_ci/messages.c @@ -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 diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index 760a9d599..6f157a691 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -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, diff --git a/core/embed/rust/rust_ui.h b/core/embed/rust/rust_ui.h index 2bda41a2a..1cd3b6bba 100644 --- a/core/embed/rust/rust_ui.h +++ b/core/embed/rust/rust_ui.h @@ -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); diff --git a/core/embed/rust/src/error.rs b/core/embed/rust/src/error.rs index 579ea8f6a..d3e26e91b 100644 --- a/core/embed/rust/src/error.rs +++ b/core/embed/rust/src/error.rs @@ -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), diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index b403a2fd7..0edae7d19 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -26,6 +26,7 @@ mod trace; #[cfg(feature = "ui")] #[macro_use] pub mod ui; +pub mod strutil; #[cfg(feature = "debug")] #[panic_handler] diff --git a/core/embed/rust/src/micropython/buffer.rs b/core/embed/rust/src/micropython/buffer.rs index 498f0082a..1c40a1799 100644 --- a/core/embed/rust/src/micropython/buffer.rs +++ b/core/embed/rust/src/micropython/buffer.rs @@ -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 { if !obj.is_bytes() { return Err(Error::TypeError); diff --git a/core/embed/rust/src/strutil.rs b/core/embed/rust/src/strutil.rs new file mode 100644 index 000000000..d1454e878 --- /dev/null +++ b/core/embed/rust/src/strutil.rs @@ -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; + } +} diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index 17e9c5128..a90278ef2 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -175,3 +175,9 @@ pub fn sync() { ffi::display_sync(); } } + +pub fn refresh() { + unsafe { + ffi::display_refresh(); + } +} diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index 73897bdc0..75bb54f8d 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -36,6 +36,13 @@ pub trait ParagraphStrType: AsRef { 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 { /// 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 Paragraph { @@ -268,6 +277,8 @@ impl Paragraph { align: Alignment::Start, break_after: false, no_break: false, + padding_top: PARAGRAPH_TOP_SPACE, + padding_bottom: PARAGRAPH_BOTTOM_SPACE, } } @@ -286,6 +297,16 @@ impl Paragraph { 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 Paragraph { 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 ParagraphSource for Vec, N> { + type StrType = T; + + fn at(&self, index: usize, offset: usize) -> Paragraph { + let para = &self[index]; + para.map(|content| content.skip_prefix(offset)) + } + + fn size(&self) -> usize { + self.len() + } +} + +impl ParagraphSource for [Paragraph; N] { + type StrType = T; + + fn at(&self, index: usize, offset: usize) -> Paragraph { + let para = &self[index]; + para.map(|content| content.skip_prefix(offset)) + } + + fn size(&self) -> usize { + self.len() + } +} + +impl ParagraphSource for Paragraph { + type StrType = T; + + fn at(&self, index: usize, offset: usize) -> Paragraph { + assert_eq!(index, 0); + self.map(|content| content.skip_prefix(offset)) + } + + fn size(&self) -> usize { + 1 + } +} diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index c7512d8e5..fdeb48118 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -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]; diff --git a/core/embed/rust/src/ui/geometry.rs b/core/embed/rust/src/ui/geometry.rs index 339706921..077ee9aa8 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -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, diff --git a/core/embed/rust/src/ui/layout/util.rs b/core/embed/rust/src/ui/layout/util.rs index e97ea0cd4..71c68582e 100644 --- a/core/embed/rust/src/ui/layout/util.rs +++ b/core/embed/rust/src/ui/layout/util.rs @@ -197,45 +197,6 @@ impl ParagraphSource for PropsList { } } -impl ParagraphSource for Vec, N> { - type StrType = T; - - fn at(&self, index: usize, offset: usize) -> Paragraph { - let para = &self[index]; - para.map(|content| content.skip_prefix(offset)) - } - - fn size(&self) -> usize { - self.len() - } -} - -impl ParagraphSource for [Paragraph; N] { - type StrType = T; - - fn at(&self, index: usize, offset: usize) -> Paragraph { - let para = &self[index]; - para.map(|content| content.skip_prefix(offset)) - } - - fn size(&self) -> usize { - self.len() - } -} - -impl ParagraphSource for Paragraph { - type StrType = T; - - fn at(&self, index: usize, offset: usize) -> Paragraph { - 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) diff --git a/core/embed/rust/src/ui/mod.rs b/core/embed/rust/src/ui/mod.rs index e7aec850f..c44b24dd5 100644 --- a/core/embed/rust/src/ui/mod.rs +++ b/core/embed/rust/src/ui/mod.rs @@ -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; diff --git a/core/embed/rust/src/ui/model_tt/bootloader/confirm.rs b/core/embed/rust/src/ui/model_tt/bootloader/confirm.rs new file mode 100644 index 000000000..af38bf7e3 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/confirm.rs @@ -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, + title: Option>>, + message: Child>>, + left: Child>, + right: Child>, + info_button: Option>, + close_button: Option>, + info_title: Option>>, + info_text: Option>>, + show_info: bool, + + confirm_left: bool, +} + +impl<'a> Confirm<'a> { + pub fn new( + bg_color: Color, + icon: Option, + left: Button<&'static str>, + right: Button<&'static str>, + confirm_left: bool, + confirm: (Option<&'static str>, Paragraphs>), + info: Option<(&'static str, Paragraphs>)>, + ) -> 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 { + 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); + } +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/connect.rs b/core/embed/rust/src/ui/model_tt/bootloader/connect.rs new file mode 100644 index 000000000..379f7cad2 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/connect.rs @@ -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 { + 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, + ); + } +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/intro.rs b/core/embed/rust/src/ui/model_tt/bootloader/intro.rs new file mode 100644 index 000000000..fabbc93e2 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/intro.rs @@ -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>>, + menu: Child>, + host: Child>, + text: Child>>, +} + +impl<'a> Intro<'a> { + pub fn new(bld_version: &'static str, content: Paragraphs>) -> 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 { + 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); + } +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/menu.rs b/core/embed/rust/src/ui/model_tt/bootloader/menu.rs new file mode 100644 index 000000000..aa2e30510 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/menu.rs @@ -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>>, + close: Child>, + reboot: Child>, + reset: Child>, +} + +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 { + 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); + } +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs new file mode 100644 index 000000000..79f4a8875 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs @@ -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 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(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(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 { + 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(); +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/theme.rs b/core/embed/rust/src/ui/model_tt/bootloader/theme.rs new file mode 100644 index 000000000..e5fc4cf3d --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/bootloader/theme.rs @@ -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, +); diff --git a/core/embed/rust/src/ui/model_tt/component/button.rs b/core/embed/rust/src/ui/model_tt/component/button.rs index 55a4d4914..ba4ed7743 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -21,6 +21,7 @@ pub enum ButtonMsg { pub struct Button { area: Rect, + touch_expand: Option, content: ButtonContent, styles: ButtonStyleSheet, state: State, @@ -37,6 +38,7 @@ impl Button { Self { content, area: Rect::zero(), + touch_expand: None, styles: theme::button_default(), state: State::Initial, long_press: None, @@ -69,6 +71,11 @@ impl Button { 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 { + 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) { diff --git a/core/embed/rust/src/ui/model_tt/component/mod.rs b/core/embed/rust/src/ui/model_tt/component/mod.rs index bb3a68e13..4d4a82f76 100644 --- a/core/embed/rust/src/ui/model_tt/component/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/mod.rs @@ -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; diff --git a/core/embed/rust/src/ui/model_tt/component/result.rs b/core/embed/rust/src/ui/model_tt/component/result.rs new file mode 100644 index 000000000..7c16eb1b0 --- /dev/null +++ b/core/embed/rust/src/ui/model_tt/component/result.rs @@ -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 { + bg: Pad, + small_pad: Pad, + fg_color: Color, + bg_color: Color, + icon: Icon, + message_top: Child>>, + message_bottom: Child>>, +} + +impl ResultScreen { + pub fn new( + fg_color: Color, + bg_color: Color, + icon: Icon, + message_top: Paragraphs>, + message_bottom: Paragraphs>, + complete_draw: bool, + ) -> Self { + let mut instance = Self { + bg: Pad::with_background(bg_color), + small_pad: Pad::with_background(bg_color), + fg_color, + bg_color, + icon, + message_top: Child::new(message_top), + message_bottom: Child::new(message_bottom), + }; + + if complete_draw { + instance.bg.clear(); + } else { + instance.small_pad.clear(); + } + instance + } +} + +impl Component for ResultScreen { + type Msg = Never; + + fn place(&mut self, bounds: Rect) -> Rect { + self.bg + .place(Rect::new(Point::new(0, 0), Point::new(WIDTH, HEIGHT))); + + self.message_top + .place(Rect::new(Point::new(15, 59), Point::new(WIDTH - 15, 149))); + + let bottom_area = Rect::new(Point::new(15, 151), Point::new(WIDTH - 15, HEIGHT)); + + self.small_pad.place(bottom_area); + self.message_bottom.place(bottom_area); + + bounds + } + + fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { + 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(); + } +} diff --git a/core/embed/rust/src/ui/model_tt/constant.rs b/core/embed/rust/src/ui/model_tt/constant.rs index ceed7fe46..9648dd16e 100644 --- a/core/embed/rust/src/ui/model_tt/constant.rs +++ b/core/embed/rust/src/ui/model_tt/constant.rs @@ -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) } diff --git a/core/embed/rust/src/ui/model_tt/mod.rs b/core/embed/rust/src/ui/model_tt/mod.rs index 8d508e39c..8308f1b73 100644 --- a/core/embed/rust/src/ui/model_tt/mod.rs +++ b/core/embed/rust/src/ui/model_tt/mod.rs @@ -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; diff --git a/core/embed/rust/src/ui/model_tt/res/close.toif b/core/embed/rust/src/ui/model_tt/res/close.toif new file mode 100644 index 0000000000000000000000000000000000000000..a53d508b3dcb563ccbd05817fc0df9fa00ed99d4 GIT binary patch literal 94 zcmV-k0HOa>Pf14*01yCD0002H!NdRr26ex6LGx zHW2^!{Dl9!3=CiX|JP&qUC+Sq8>IBhc?O1mIe*z14*X{b_|GNCz_8#yYr=mHpw@=} zY;cAG*wh36nHc`$0~z0dy1u<O|M#*oFaQ7+ C>`toy literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/erase_big.toif b/core/embed/rust/src/ui/model_tt/res/erase_big.toif new file mode 100644 index 0000000000000000000000000000000000000000..5d2508a18af18a19668e334733bdc2ef7ad1d84c GIT binary patch literal 220 zcmV<203-iYPf14@02~0&0001EU;qIFO$Io)aGw#*{`Ox6&i->_KaA;cU*RtYl)a&z z;c-2PF^h@evo1rydnN{kg#Wn=zkmvUaWgPnI{)AEKTzzUKZt+r|9qg*g8d8%|Ctni z>4WtBV@UuSoWKTR|KL0j53}SG-zPp8`+@ye9vFK;{tr$VdqVvWm^&C6`oANIf8&O+ zH^hIGgRwvAZ^(!HjVA%-Q-Qy148NfUHQZ-lcvKHz|Ih>ZMH|H2^qv_=Uj84+tg!9B WEl5P+*MA_G4>3aF*uVGHASM9emS__I literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/info_small.toif b/core/embed/rust/src/ui/model_tt/res/info_small.toif new file mode 100644 index 0000000000000000000000000000000000000000..bad082376ce8de5a9670cfa19eedb280e95bbe10 GIT binary patch literal 191 zcmV;w06_m#Pf14@02lzX0001EV896kmj6AU1!X_|4+ObjX2bs)CWCAL*+J~@bs+M2 zKafax&&*)J#9;7`m4V?xJp;oR9-y=w1H(6728N5A3=A89V!zoSrWNdGVED((z_6Wz zfkELu1H*ry*l&IYFayN?1ZDqYVqo|HWE%k0{AP!+6M%ZZa|7A>Kzs5T7#;%6{QwdJ t+7keDLxKg+4Iq1dBHRXab1t*O_Wzt<+pmHA5(o9zw!iPI8F7aJ0{~EHRsjG2 literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/menu.toif b/core/embed/rust/src/ui/model_tt/res/menu.toif new file mode 100644 index 0000000000000000000000000000000000000000..b690421488fbb26470d4e71d0c18d6202e563bb9 GIT binary patch literal 47 zcmWIX_jDIw5MWSdU|_htPnv62{r~o+zdS0<(d7#h-kmNFFz7D{t~(f8@QFv{3IhWG DgS!#+ literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/reboot.toif b/core/embed/rust/src/ui/model_tt/res/reboot.toif new file mode 100644 index 0000000000000000000000000000000000000000..d45654cbcdb5612e2080624469025397b4882ac9 GIT binary patch literal 126 zcmV-^0D=EhPf14-01^Ol0000JU{E-D_pAg1!yV06|NsAg&%yBj?0*oL&j2LWWuE;1 zpY#9!^Gpm3&;IKHpAmj6YB_$??;I=W&?*2+p^l0Pm?BOum>Xz}Ri{;|~E{d_5tcOzdw;bYB8g%8Tns{7P+Bxckr!E4Efp=g1hbQeNtpH Fffou5Wn=&V literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/success_bld.toif b/core/embed/rust/src/ui/model_tt/res/success_bld.toif new file mode 100644 index 0000000000000000000000000000000000000000..dc80a59e580619ada734a39e263ee2ce9520a38f GIT binary patch literal 111 zcmV-#0FeJwPf14@01*IV0001EV1NOKyKpAMk^69_!5<$u`|x=dxbSa;@aFTZ2;o=; zhH^%T@O_~0I|;Zj!^=L9#NzvGK(Y}eVel&#Br5=tfCw`%OnfH}6$VMvErAMyB>v{Y RtPyw#7iM5sR0pw-0RSueEpPw; literal 0 HcmV?d00001 diff --git a/core/embed/rust/src/ui/model_tt/res/trezor_empty.toif b/core/embed/rust/src/ui/model_tt/res/trezor_empty.toif new file mode 100644 index 0000000000000000000000000000000000000000..9747b207be2816b210f7166acbc072b5ece6e236 GIT binary patch literal 647 zcmV;20(kvYPf15q0C@m=0ssK*k~@3aFcgMmNIQ#5>FPAltae&p1wRd5fwXunG1zm9 z?ZZESpB-D0jgBM}wk-Y@9*$p~>qm$p_jh(W#?L+Sj%Wx%&(>AYPGR%2OUS*qhu~UT zCyR$774@>XeI=uu$4!G)9l66m2&({gfI57Q+{+CH|mHsQ|a_;Uz9-x zTTaEGE&040IiQXH{mOo}Xn6-d%fHWPbRJBQA6NX{7M-3~plQp{{1O#PSW9+<#-tH@ zIP-PVQC26SfgIyyXNnsR>>PGOelXc#%EW~`NC_e%Cg5ZoAps}h57U_knTckRBJQx$ znd#VNg}BW+ins~C6?!@y$&G5rk0#w;wd!GVd#=?$A^jgUN>p-C*Wb1w8?qrAvLPGN zOinE`NE3xz!J=gZcX1z+0Ap5rrgP7IZZ*tw4z+^DO9T;E))|9E}m`rt*K8 zBrZZd{1B1EmdbgsK^0C%%Z%nA+66rUp2FTiF)dvyAaNlpH(RY5MivcWfp3;in{Prx zCm~EQ=t11hTZ17A=LvO?^-{EkKdd!S^g=ZhxASsonreOFW~Ax#S>;b-rGjrn?F56x z6ySFapk%uXYVfD6F1zxlg1TlQku0gk{1 ztUy?R=!=gSqJkP)=`FQ-A4tB?{Jj9kxg20762dZSHqF-PZ72B``--`IqE&=IyV7bM zAd5%%^3<+oO2`T{_UuWFP2g=bNY2Q}42vGxH1l`d^*+nKa=Bwe &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(); +} diff --git a/core/embed/rust/src/ui/model_tt/theme.rs b/core/embed/rust/src/ui/model_tt/theme.rs index d262d5f16..bb18afa82 100644 --- a/core/embed/rust/src/ui/model_tt/theme.rs +++ b/core/embed/rust/src/ui/model_tt/theme.rs @@ -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); diff --git a/core/embed/rust/src/ui/screens.rs b/core/embed/rust/src/ui/screens.rs new file mode 100644 index 000000000..0460d0319 --- /dev/null +++ b/core/embed/rust/src/ui/screens.rs @@ -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); +} diff --git a/core/embed/rust/src/ui/util.rs b/core/embed/rust/src/ui/util.rs index c31135c1b..acea051a0 100644 --- a/core/embed/rust/src/ui/util.rs +++ b/core/embed/rust/src/ui/util.rs @@ -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; diff --git a/core/embed/trezorhal/common.c b/core/embed/trezorhal/common.c index d8864b033..3de831926 100644 --- a/core/embed/trezorhal/common.c +++ b/core/embed/trezorhal/common.c @@ -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."); +} diff --git a/core/embed/trezorhal/common.h b/core/embed/trezorhal/common.h index 770e7ec97..52aab3b0f 100644 --- a/core/embed/trezorhal/common.h +++ b/core/embed/trezorhal/common.h @@ -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) \ diff --git a/core/embed/trezorhal/image.c b/core/embed/trezorhal/image.c index 44d536d46..6c6c043ed 100644 --- a/core/embed/trezorhal/image.c +++ b/core/embed/trezorhal/image.c @@ -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)) diff --git a/core/embed/trezorhal/image.h b/core/embed/trezorhal/image.h index 21c1aa64b..ff29c4516 100644 --- a/core/embed/trezorhal/image.h +++ b/core/embed/trezorhal/image.h @@ -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 diff --git a/core/embed/trezorhal/touch/ft6x36.c b/core/embed/trezorhal/touch/ft6x36.c index 96b8690d2..f1b383c7b 100644 --- a/core/embed/trezorhal/touch/ft6x36.c +++ b/core/embed/trezorhal/touch/ft6x36.c @@ -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) { diff --git a/core/embed/unix/common.c b/core/embed/unix/common.c index 57d9e4944..6f4c7ee35 100644 --- a/core/embed/unix/common.c +++ b/core/embed/unix/common.c @@ -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."); +} diff --git a/core/embed/unix/common.h b/core/embed/unix/common.h index 9641f3b29..89d7da71d 100644 --- a/core/embed/unix/common.h +++ b/core/embed/unix/common.h @@ -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) \ diff --git a/legacy/common.c b/legacy/common.c index 77fe4b85a..f87bff509 100644 --- a/legacy/common.c +++ b/legacy/common.c @@ -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); +} diff --git a/legacy/common.h b/legacy/common.h index 44ae32d9d..399818ba3 100644 --- a/legacy/common.h +++ b/legacy/common.h @@ -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) \ diff --git a/storage/storage.c b/storage/storage.c index 80e5040eb..b8bac967b 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -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; } diff --git a/storage/tests/c/common.c b/storage/tests/c/common.c index 252b1bbfe..82234756b 100644 --- a/storage/tests/c/common.c +++ b/storage/tests/c/common.c @@ -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) {} diff --git a/storage/tests/c/common.h b/storage/tests/c/common.h index b24d9ebfe..845dbf0c0 100644 --- a/storage/tests/c/common.h +++ b/storage/tests/c/common.h @@ -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) \