1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-12 14:16:06 +00:00

feat(core): add BLE to bootloader

[no changelog]
This commit is contained in:
tychovrahe 2025-03-03 15:13:56 +01:00 committed by M1nd3r
parent e997dcea32
commit 5a68da1d23
14 changed files with 402 additions and 25 deletions

View File

@ -11,7 +11,7 @@ PRODUCTION = 0 if BOOTLOADER_QA else ARGUMENTS.get('PRODUCTION', '0') == '1'
HW_REVISION = ARGUMENTS.get('HW_REVISION', None) HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
UI_DEBUG_OVERLAY = ARGUMENTS.get('UI_DEBUG_OVERLAY', '0') == '1' UI_DEBUG_OVERLAY = ARGUMENTS.get('UI_DEBUG_OVERLAY', '0') == '1'
FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d"] FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d", "ble"]
CCFLAGS_MOD = '' CCFLAGS_MOD = ''
CPPPATH_MOD = [] CPPPATH_MOD = []
@ -116,8 +116,10 @@ SOURCE_BOOTLOADER = [
'embed/projects/bootloader/workflow/wf_empty_device.c', 'embed/projects/bootloader/workflow/wf_empty_device.c',
'embed/projects/bootloader/workflow/wf_auto_update.c', 'embed/projects/bootloader/workflow/wf_auto_update.c',
'embed/projects/bootloader/workflow/wf_host_control.c', 'embed/projects/bootloader/workflow/wf_host_control.c',
'embed/projects/bootloader/workflow/wf_ble_pairing_request.c',
'embed/projects/bootloader/wire/codec_v1.c', 'embed/projects/bootloader/wire/codec_v1.c',
'embed/projects/bootloader/wire/wire_iface_usb.c', 'embed/projects/bootloader/wire/wire_iface_usb.c',
'embed/projects/bootloader/wire/wire_iface_ble.c',
'embed/projects/bootloader/protob/protob.c', 'embed/projects/bootloader/protob/protob.c',
'embed/projects/bootloader/protob/pb/messages.pb.c', 'embed/projects/bootloader/protob/pb/messages.pb.c',
'embed/projects/bootloader/version_check.c', 'embed/projects/bootloader/version_check.c',

View File

@ -59,6 +59,9 @@
#ifdef USE_TAMPER #ifdef USE_TAMPER
#include <sys/tamper.h> #include <sys/tamper.h>
#endif #endif
#ifdef USE_BLE
#include <io/ble.h>
#endif
#include "antiglitch.h" #include "antiglitch.h"
#include "bootui.h" #include "bootui.h"
@ -122,6 +125,9 @@ static void drivers_init(secbool *touch_initialized) {
#ifdef USE_RGB_LED #ifdef USE_RGB_LED
rgb_led_init(); rgb_led_init();
#endif #endif
#ifdef USE_BLE
ble_init();
#endif
} }
static void drivers_deinit(void) { static void drivers_deinit(void) {

View File

@ -18,17 +18,30 @@
*/ */
#include <trezor_bsp.h> #include <trezor_bsp.h>
#include <trezor_rtl.h>
#include "poll.h" #include "poll.h"
#include <io/usb.h> #include <io/usb.h>
#include <sys/systick.h> #include <sys/systick.h>
#ifdef USE_BLE
#include <io/ble.h>
#endif
#ifdef USE_BUTTON
#include <io/button.h>
#endif
#ifdef USE_TOUCH
#include <io/touch.h>
#endif
#ifdef TREZOR_EMULATOR #ifdef TREZOR_EMULATOR
#include "SDL.h" #include "SDL.h"
#endif #endif
uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, int16_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
poll_event_t* event, uint32_t timeout_ms) { poll_event_t* event, uint32_t timeout_ms) {
uint32_t deadline = ticks_timeout(timeout_ms); uint32_t deadline = ticks_timeout(timeout_ms);
@ -45,11 +58,41 @@ uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
if ((ifaces[i] & MODE_READ) == MODE_READ) { if ((ifaces[i] & MODE_READ) == MODE_READ) {
// check if USB can read // check if USB can read
if (sectrue == usb_webusb_can_read(iface_num)) { if (sectrue == usb_webusb_can_read(iface_num)) {
event->type = EVENT_USB_CAN_READ; event->event.usb_data_event = EVENT_USB_CAN_READ;
return iface_num; return iface_num;
} }
} }
} }
#ifdef USE_BLE
if (iface_num == IFACE_BLE) {
if ((ifaces[i] & MODE_READ) == MODE_READ) {
// check if BLE can read
if (ble_can_read()) {
event->event.ble_data_event = EVENT_BLE_CAN_READ;
return iface_num;
}
}
}
if (iface_num == IFACE_BLE_EVENT) {
ble_event_t ble_event = {0};
if (ble_get_event(&ble_event)) {
memcpy(&event->event.ble_event, &ble_event, sizeof(ble_event_t));
return iface_num;
}
}
#endif
#ifdef USE_BUTTON
if (iface_num == IFACE_BUTTON) {
uint32_t btn_event = button_get_event();
uint32_t etype = (btn_event >> 24) & 0x3U; // button down/up
uint32_t btn_number = btn_event & 0xFFFF;
if (etype != 0) {
event->event.button_event.type = etype;
event->event.button_event.button = btn_number;
return iface_num;
}
}
#endif
} }
#ifndef TREZOR_EMULATOR #ifndef TREZOR_EMULATOR
@ -57,6 +100,5 @@ uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
#endif #endif
} }
event->type = EVENT_NONE; return -1;
return 0;
} }

View File

@ -21,19 +21,55 @@
#include <trezor_types.h> #include <trezor_types.h>
#include <io/usb.h>
#ifdef USE_BLE
#include <io/ble.h>
#endif
#ifdef USE_BUTTON
#include <io/button.h>
#endif
#define IFACE_USB_MAX (15) // 0-15 reserved for USB #define IFACE_USB_MAX (15) // 0-15 reserved for USB
#define IFACE_BLE (16)
#define IFACE_BLE_EVENT (252)
#define IFACE_BUTTON (254)
#define IFACE_TOUCH (255)
#define MODE_READ 0x0000 #define MODE_READ 0x0000
#define MODE_WRITE 0x0100 #define MODE_WRITE 0x0100
typedef enum { typedef enum {
EVENT_NONE = 0, EVENT_USB_CAN_READ,
EVENT_USB_CAN_READ = 0x01, } usb_data_event_type_t;
} poll_event_type_t;
#ifdef USE_BLE
typedef enum {
EVENT_BLE_CAN_READ,
} ble_data_event_type_t;
#endif
#ifdef USE_BUTTON
typedef struct {
uint32_t type;
button_t button;
} button_event_t;
#endif
typedef struct { typedef struct {
poll_event_type_t type; union {
usb_data_event_type_t usb_data_event;
usb_event_t usb_event;
#ifdef USE_BLE
ble_data_event_type_t ble_data_event;
ble_event_t ble_event;
#endif
#ifdef USE_BUTTON
button_event_t button_event;
#endif
} event;
} poll_event_t; } poll_event_t;
uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, int16_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
poll_event_t* event, uint32_t timeout_ms); poll_event_t* event, uint32_t timeout_ms);

View File

@ -0,0 +1,116 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include "wire_iface_ble.h"
#include <io/ble.h>
#include <sys/systick.h>
static bool ble_write_(uint8_t* data, size_t size) {
if (size != BLE_TX_PACKET_SIZE) {
return false;
}
uint32_t deadline = ticks_timeout(500);
while (true) {
if (ticks_expired(deadline)) {
return false;
}
if (ble_can_write()) {
break;
}
}
return ble_write(data, size);
}
static int ble_read_(uint8_t* buffer, size_t buffer_size) {
if (buffer_size != BLE_RX_PACKET_SIZE) {
return -1;
}
uint32_t deadline = ticks_timeout(500);
while (true) {
if (ticks_expired(deadline)) {
return false;
}
if (ble_can_read()) {
break;
}
}
int r = ble_read(buffer, buffer_size);
return r;
}
static void ble_error(void) {
error_shutdown_ex("BLE ERROR",
"Error reading from BLE. Try different BLE cable.", NULL);
}
void ble_iface_init(wire_iface_t* iface) {
ble_start();
memset(iface, 0, sizeof(wire_iface_t));
iface->poll_iface_id = 16;
iface->tx_packet_size = BLE_TX_PACKET_SIZE;
iface->rx_packet_size = BLE_RX_PACKET_SIZE;
iface->write = &ble_write_;
iface->read = &ble_read_;
iface->error = &ble_error;
ble_start();
ble_state_t state = {0};
ble_get_state(&state);
if (!state.connectable && !state.pairing) {
if (state.peer_count > 0) {
ble_command_t cmd = {
.cmd_type = BLE_SWITCH_ON,
};
ble_issue_command(&cmd);
} else {
ble_iface_start_pairing();
}
}
}
void ble_iface_deinit(wire_iface_t* iface) { ble_stop(); }
void ble_iface_start_pairing(void) {
ble_command_t cmd = {
.cmd_type = BLE_PAIRING_MODE,
.data = {.adv_start =
{
.name = "Trezor Bootloader",
.static_mac = false,
}},
.data_len = 0,
};
ble_issue_command(&cmd);
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "codec_v1.h"
void ble_iface_init(wire_iface_t* iface);
void ble_iface_deinit(wire_iface_t* iface);
void ble_iface_start_pairing(void);

View File

@ -0,0 +1,49 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_BLE
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <io/ble.h>
#include "bootui.h"
#include "rust_ui_bootloader.h"
#include "workflow.h"
workflow_result_t workflow_ble_pairing_request(const uint8_t *data) {
ui_result_t result = screen_confirm_pairing(data);
if (result == UI_RESULT_CONFIRM) {
ble_command_t cmd = {
.cmd_type = BLE_ALLOW_PAIRING,
};
ble_issue_command(&cmd);
} else {
ble_command_t cmd = {
.cmd_type = BLE_REJECT_PAIRING,
};
ble_issue_command(&cmd);
}
screen_connect(false);
return WF_OK;
}
#endif

View File

@ -521,9 +521,9 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
while (true) { while (true) {
uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ}; uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ};
poll_event_t e = {0}; poll_event_t e = {0};
uint8_t i = poll_events(ifaces, 1, &e, 100); uint16_t i = poll_events(ifaces, 1, &e, 100);
if (e.type == EVENT_NONE || i != protob_get_iface_flag(iface)) { if (i < 0 || i != protob_get_iface_flag(iface)) {
continue; continue;
} }

View File

@ -30,11 +30,21 @@
#include "wire/wire_iface_usb.h" #include "wire/wire_iface_usb.h"
#include "workflow.h" #include "workflow.h"
#ifdef USE_BLE
#include <wire/wire_iface_ble.h>
#ifdef USE_BUTTON
#include <io/button.h>
#endif
#endif
workflow_result_t workflow_host_control(const vendor_header *const vhdr, workflow_result_t workflow_host_control(const vendor_header *const vhdr,
const image_header *const hdr, const image_header *const hdr,
void (*redraw_wait_screen)(void)) { void (*redraw_wait_screen)(void)) {
wire_iface_t usb_iface = {0}; wire_iface_t usb_iface = {0};
wire_iface_t ble_iface = {0};
protob_io_t protob_usb_iface = {0}; protob_io_t protob_usb_iface = {0};
protob_io_t protob_ble_iface = {0};
redraw_wait_screen(); redraw_wait_screen();
@ -43,31 +53,89 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
usb_iface_init(&usb_iface, usb_iface_init(&usb_iface,
(vhdr == NULL && hdr == NULL) ? sectrue : secfalse); (vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
ble_iface_init(&ble_iface);
protob_init(&protob_usb_iface, &usb_iface); protob_init(&protob_usb_iface, &usb_iface);
protob_init(&protob_ble_iface, &ble_iface);
workflow_result_t result = WF_ERROR_FATAL; workflow_result_t result = WF_ERROR_FATAL;
for (;;) { for (;;) {
uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ}; uint16_t ifaces[] = {
protob_get_iface_flag(&protob_usb_iface) | MODE_READ,
#ifdef USE_BLE
protob_get_iface_flag(&protob_ble_iface) | MODE_READ,
IFACE_BLE_EVENT,
#ifdef USE_BUTTON
IFACE_BUTTON,
#endif
#endif
};
poll_event_t e = {0}; poll_event_t e = {0};
uint8_t i = poll_events(ifaces, 1, &e, 100); int16_t i = poll_events(ifaces, ARRAY_LENGTH(ifaces), &e, 100);
if (i < 0) {
continue;
}
uint16_t msg_id = 0; uint16_t msg_id = 0;
protob_io_t *active_iface = NULL; protob_io_t *active_iface = NULL;
switch (e.type) { if (i < IFACE_USB_MAX) {
case EVENT_USB_CAN_READ: switch (e.event.usb_data_event) {
if (i == protob_get_iface_flag(&protob_usb_iface) && case EVENT_USB_CAN_READ:
sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) { if (i == protob_get_iface_flag(&protob_usb_iface) &&
active_iface = &protob_usb_iface; sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) {
} else { active_iface = &protob_usb_iface;
} else {
continue;
}
break;
default:
continue; continue;
} }
break; }
case EVENT_NONE: #ifdef USE_BLE
default: if (i == IFACE_BLE) {
continue; switch (e.event.ble_data_event) {
case EVENT_BLE_CAN_READ:
if (i == protob_get_iface_flag(&protob_ble_iface) &&
sectrue == protob_get_msg_header(&protob_ble_iface, &msg_id)) {
active_iface = &protob_ble_iface;
} else {
continue;
}
break;
default:
continue;
}
}
if (i == IFACE_BLE_EVENT) {
switch (e.event.ble_event.type) {
case BLE_PAIRING_REQUEST:
workflow_ble_pairing_request(e.event.ble_event.data);
continue;
default:
break;
}
}
#ifdef USE_BUTTON
if (i == IFACE_BUTTON) {
switch (e.event.button_event.type) {
case (BTN_EVT_DOWN >> 24):
ble_iface_start_pairing();
default:
continue;
}
}
#endif
#endif
if (active_iface == NULL) {
continue;
;
} }
switch (msg_id) { switch (msg_id) {

View File

@ -66,3 +66,7 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
workflow_result_t workflow_auto_update(const vendor_header *const vhdr, workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
const image_header *const hdr); const image_header *const hdr);
#ifdef USE_BLE
workflow_result_t workflow_ble_pairing_request(const uint8_t *data);
#endif

View File

@ -27,3 +27,5 @@ void bld_continue_label(uint16_t bg_color);
void screen_boot(bool warning, const char* vendor_str, size_t vendor_str_len, void screen_boot(bool warning, const char* vendor_str, size_t vendor_str_len,
uint32_t version, const void* vendor_img, uint32_t version, const void* vendor_img,
size_t vendor_img_len, int wait); size_t vendor_img_len, int wait);
uint32_t screen_confirm_pairing(const uint8_t* code);

View File

@ -144,3 +144,10 @@ extern "C" fn screen_wipe_success() {
extern "C" fn screen_wipe_fail() { extern "C" fn screen_wipe_fail() {
ModelUI::screen_wipe_fail() ModelUI::screen_wipe_fail()
} }
#[no_mangle]
extern "C" fn screen_confirm_pairing(code: *const cty::c_char) -> u32 {
let code = unwrap!(unsafe { from_c_array(code, 6) });
ModelUI::screen_confirm_pairing(code)
}

View File

@ -439,4 +439,18 @@ impl BootloaderUI for UIBolt {
display::refresh(); display::refresh();
} }
#[cfg(feature = "ble")]
fn screen_confirm_pairing(code: &str) -> u32 {
let title = Label::centered("Pair device".into(), TEXT_NORMAL);
let msg = Label::centered(code.into(), TEXT_NORMAL);
let right = Button::with_text("CONFIRM".into()).styled(button_confirm());
let left = Button::with_text("REJECT".into()).styled(button_bld());
let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg);
run(&mut frame)
}
} }

View File

@ -48,4 +48,7 @@ pub trait BootloaderUI {
vendor_img: &'static [u8], vendor_img: &'static [u8],
wait: i32, wait: i32,
); );
#[cfg(feature = "ble")]
fn screen_confirm_pairing(code: &str) -> u32;
} }