feat(core/bootloader): use different wait message on BLE during fw installation
What changed, and why it matters
This commit only changes the on-screen message shown during firmware installation. If the update is happening over Bluetooth (BLE), the device now tells the user to keep the Trezor close to the host device; if it is happening over USB, it continues to tell the user not to disconnect the cable. There is no change to security logic, cryptography, or data handling.
No security action needed; this is a user-experience wording change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change threads a new boolean wireless flag through the bootloader UI code. The flag is set in the BLE and USB wire interface structs and passed into screen_install_progress. Only the Eckhart layout actually uses the flag to choose between two user-facing strings; the other layouts ignore it. No protocol, parsing, authentication, or flash-write behavior is modified.
Changed components
Trezor core bootloader UIBLE/USB wire interface metadataInspect captured patch +83 / −37
diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c
index 9e8334d7..f9063b5a 100644
--- a/core/embed/projects/bootloader/bootui.c
+++ b/core/embed/projects/bootloader/bootui.c
@@ -126,16 +126,16 @@ confirm_result_t ui_screen_install_confirm(const vendor_header *const vhdr,
is_newinstall == sectrue, version_cmp);
}
-void ui_screen_install_start() {
- screen_install_progress(0, true, initial_setup);
+void ui_screen_install_start(bool wireless) {
+ screen_install_progress(0, true, initial_setup, wireless);
}
-void ui_screen_install_progress_erase(int pos, int len) {
- screen_install_progress(250 * pos / len, false, initial_setup);
+void ui_screen_install_progress_erase(int pos, int len, bool wireless) {
+ screen_install_progress(250 * pos / len, false, initial_setup, wireless);
}
-void ui_screen_install_progress_upload(int pos) {
- screen_install_progress(pos, false, initial_setup);
+void ui_screen_install_progress_upload(int pos, bool wireless) {
+ screen_install_progress(pos, false, initial_setup, wireless);
}
// wipe UI
diff --git a/core/embed/projects/bootloader/bootui.h b/core/embed/projects/bootloader/bootui.h
index 9f33cd65..b99bb937 100644
--- a/core/embed/projects/bootloader/bootui.h
+++ b/core/embed/projects/bootloader/bootui.h
@@ -60,9 +60,9 @@ confirm_result_t ui_screen_install_confirm(const vendor_header* const vhdr,
secbool is_newvendor,
secbool is_newinstall,
int version_cmp);
-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_install_start(bool wireless);
+void ui_screen_install_progress_erase(int pos, int len, bool wireless);
+void ui_screen_install_progress_upload(int pos, bool wireless);
confirm_result_t ui_screen_wipe_confirm(void);
void ui_screen_wipe(void);
diff --git a/core/embed/projects/bootloader/wire/codec_v1.h b/core/embed/projects/bootloader/wire/codec_v1.h
index b1882165..1b9c559e 100644
--- a/core/embed/projects/bootloader/wire/codec_v1.h
+++ b/core/embed/projects/bootloader/wire/codec_v1.h
@@ -28,6 +28,8 @@
typedef struct {
// initialized flag
bool initialized;
+ // wireless flag
+ bool wireless;
// identifier of the interface used for polling communication events
uint8_t poll_iface_id;
// size of TX packet
diff --git a/core/embed/projects/bootloader/wire/wire_iface_ble.c b/core/embed/projects/bootloader/wire/wire_iface_ble.c
index 3e018bae..7b7618bc 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_ble.c
+++ b/core/embed/projects/bootloader/wire/wire_iface_ble.c
@@ -103,6 +103,7 @@ wire_iface_t* ble_iface_init(void) {
iface->write = &ble_write_;
iface->read = &ble_read_;
iface->error = &ble_error;
+ iface->wireless = true;
ble_start();
diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.c b/core/embed/projects/bootloader/wire/wire_iface_usb.c
index 50bcf064..aad70964 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_usb.c
+++ b/core/embed/projects/bootloader/wire/wire_iface_usb.c
@@ -82,6 +82,7 @@ wire_iface_t* usb_iface_init(secbool usb21_landing) {
iface->read = &usb_read;
iface->error = &usb_error;
iface->initialized = true;
+ iface->wireless = false;
return iface;
}
diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
index 68f821e0..36aafea6 100644
--- a/core/embed/projects/bootloader/workflow/wf_firmware_update.c
+++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c
@@ -84,9 +84,10 @@ typedef struct {
uint32_t erase_offset; // offset of flash memory to erase
int32_t firmware_upload_chunk_retry; // retry counter
size_t headers_offset; // offset of headers in the first block
- size_t read_offset; // offset of the next read data in the chunk buffer
- uint32_t chunk_size; // size of already received chunk data
- bool confirmed; // true if the firmware is confirmed by the user
+ size_t read_offset; // offset of the next read data in the chunk buffer
+ uint32_t chunk_size; // size of already received chunk data
+ bool confirmed; // true if the firmware is confirmed by the user
+ bool wireless_transport; // whether the transport is over BLE
#ifdef USE_SECMON_VERIFICATION
size_t secmon_code_offset; // offset of the secmon code in the first block
size_t secmon_code_size; // size of the secmon code
@@ -176,9 +177,10 @@ static void fw_data_received(size_t len, void *ctx) {
if (context->confirmed) {
ui_screen_install_progress_upload(
1000 *
- (context->firmware_block * IMAGE_CHUNK_SIZE + context->chunk_size) /
- (context->firmware_block * IMAGE_CHUNK_SIZE +
- context->firmware_remaining));
+ (context->firmware_block * IMAGE_CHUNK_SIZE + context->chunk_size) /
+ (context->firmware_block * IMAGE_CHUNK_SIZE +
+ context->firmware_remaining),
+ context->wireless_transport);
}
}
@@ -402,7 +404,7 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
return UPLOAD_ERR_USER_ABORT;
}
- ui_screen_install_start();
+ ui_screen_install_start(ctx->wireless_transport);
ctx->confirmed = true;
// if firmware is not upgrade, erase storage
@@ -606,6 +608,8 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
return WF_ERROR;
}
+ ctx.wireless_transport = iface->wire->wireless;
+
ctx.firmware_remaining = msg.has_length ? msg.length : 0;
if ((ctx.firmware_remaining > 0) &&
((ctx.firmware_remaining % sizeof(uint32_t)) == 0) &&
@@ -663,7 +667,7 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
systick_delay_ms(100);
return WF_CANCELLED;
} else if (s == UPLOAD_OK) { // last chunk received
- ui_screen_install_progress_upload(1000);
+ ui_screen_install_progress_upload(1000, ctx.wireless_transport);
ui_screen_done(4, sectrue);
ui_screen_done(3, secfalse);
systick_delay_ms(1000);
diff --git a/core/embed/rust/rust_ui_bootloader.h b/core/embed/rust/rust_ui_bootloader.h
index 44949692..c9100ee3 100644
--- a/core/embed/rust/rust_ui_bootloader.h
+++ b/core/embed/rust/rust_ui_bootloader.h
@@ -27,7 +27,7 @@ void screen_unlock_bootloader_success(void);
// progress screens
void screen_install_progress(uint16_t progress, bool initialize,
- bool initial_setup);
+ bool initial_setup, bool wireless);
void screen_wipe_progress(uint16_t progress, bool initialize);
void screen_bootloader_entry_progress(uint16_t progress, bool initialize);
diff --git a/core/embed/rust/src/ui/api/bootloader_c.rs b/core/embed/rust/src/ui/api/bootloader_c.rs
index dfd5f096..4a390f5c 100644
--- a/core/embed/rust/src/ui/api/bootloader_c.rs
+++ b/core/embed/rust/src/ui/api/bootloader_c.rs
@@ -172,8 +172,13 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) {
}
#[no_mangle]
-extern "C" fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
- ModelUI::screen_install_progress(progress, initialize, initial_setup)
+extern "C" fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ initial_setup: bool,
+ wireless: bool,
+) {
+ ModelUI::screen_install_progress(progress, initialize, initial_setup, wireless)
}
#[no_mangle]
diff --git a/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs b/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
index 618674dd..221370ac 100644
--- a/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
+++ b/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
@@ -375,7 +375,12 @@ impl BootloaderUI for UIBolt {
)
}
- fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
+ fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ initial_setup: bool,
+ _wireless: bool,
+ ) {
let bg_color = if initial_setup { WELCOME_COLOR } else { BLD_BG };
let fg_color = if initial_setup { FG } else { BLD_FG };
diff --git a/core/embed/rust/src/ui/layout_caesar/bootloader/mod.rs b/core/embed/rust/src/ui/layout_caesar/bootloader/mod.rs
index a6642f71..fe99086c 100644
--- a/core/embed/rust/src/ui/layout_caesar/bootloader/mod.rs
+++ b/core/embed/rust/src/ui/layout_caesar/bootloader/mod.rs
@@ -314,7 +314,12 @@ impl BootloaderUI for UICaesar {
);
}
- fn screen_install_progress(progress: u16, initialize: bool, _initial_setup: bool) {
+ fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ _initial_setup: bool,
+ _wireless: bool,
+ ) {
Self::screen_progress(
"Installing",
"firmware",
diff --git a/core/embed/rust/src/ui/layout_delizia/bootloader/mod.rs b/core/embed/rust/src/ui/layout_delizia/bootloader/mod.rs
index fd4396c1..b70855a8 100644
--- a/core/embed/rust/src/ui/layout_delizia/bootloader/mod.rs
+++ b/core/embed/rust/src/ui/layout_delizia/bootloader/mod.rs
@@ -395,7 +395,12 @@ impl BootloaderUI for UIDelizia {
)
}
- fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
+ fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ initial_setup: bool,
+ _wireless: bool,
+ ) {
let bg_color = if initial_setup { WELCOME_COLOR } else { BLD_BG };
let fg_color = if initial_setup { GREEN_LIGHT } else { BLD_FG };
let icon_color = BLD_FG;
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs b/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
index cfe7b271..73a2a000 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
@@ -40,7 +40,11 @@ const RESTART_MESSAGE: &str = "Restart";
const SCREEN: Rect = UIEckhart::SCREEN;
const PROGRESS_TEXT_ORIGIN: Point = SCREEN.top_left().ofs(Offset::new(theme::PADDING, 38));
-const PROGRESS_WAIT_ORIGIN: Point = SCREEN.bottom_center().ofs(Offset::new(0, -35));
+const PROGRESS_WAIT_HEIGHT: i16 = 70;
+const PROGRESS_WAIT_ORIGIN: Point = SCREEN
+ .bottom_left()
+ .ofs(Offset::new(0, -PROGRESS_WAIT_HEIGHT));
+
const SCREEN_BORDER_BLUE: ScreenBorder = ScreenBorder::new(BLUE);
const SCREEN_BORDER_RED: ScreenBorder = ScreenBorder::new(RED);
const SCREEN_BORDER_GREEN_LIGHT: ScreenBorder = ScreenBorder::new(GREEN_LIGHT);
@@ -59,6 +63,8 @@ impl UIEckhart {
display::sync();
let mut label = Label::new(text.into(), Alignment::Start, TEXT_NORMAL);
+ let mut wait_msg =
+ Label::new(wait_msg.into(), Alignment::Center, TEXT_SMALL_GREY).vertically_centered();
render_on_display(None, Some(BLD_BG), |target| {
render_loader(loader_progress, border, target);
@@ -71,10 +77,11 @@ impl UIEckhart {
));
label.render(target);
- shape::Text::new(PROGRESS_WAIT_ORIGIN, wait_msg, FONT_SATOSHI_MEDIUM_26)
- .with_align(Alignment::Center)
- .with_fg(GREY)
- .render(target);
+ wait_msg.place(Rect::from_top_left_and_size(
+ PROGRESS_WAIT_ORIGIN,
+ Offset::new(SCREEN.width(), PROGRESS_WAIT_HEIGHT),
+ ));
+ wait_msg.render(target);
});
display::refresh();
@@ -390,19 +397,25 @@ impl BootloaderUI for UIEckhart {
)
}
- fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool) {
+ fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ initial_setup: bool,
+ wireless: bool,
+ ) {
let border = if initial_setup {
&SCREEN_BORDER_GREEN_LIGHT
} else {
&SCREEN_BORDER_BLUE
};
- Self::screen_progress(
- "Installing\nfirmware...",
- WAIT_MESSAGE,
- initialize,
- progress,
- border,
- )
+
+ let msg = if wireless {
+ "Keep your Trezor close to\nthe host device"
+ } else {
+ "Do not disconnect\nyour Trezor"
+ };
+
+ Self::screen_progress("Installing\nfirmware...", msg, initialize, progress, border)
}
fn screen_wipe_success() {
diff --git a/core/embed/rust/src/ui/ui_bootloader.rs b/core/embed/rust/src/ui/ui_bootloader.rs
index 4aaead7f..f8b8f1c9 100644
--- a/core/embed/rust/src/ui/ui_bootloader.rs
+++ b/core/embed/rust/src/ui/ui_bootloader.rs
@@ -50,7 +50,12 @@ pub trait BootloaderUI {
fn screen_wipe_progress(progress: u16, initialize: bool);
- fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool);
+ fn screen_install_progress(
+ progress: u16,
+ initialize: bool,
+ initial_setup: bool,
+ wireless: bool,
+ );
fn screen_wipe_success();
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.