From e36b8fda3b7f88b691af0f1940c3d091c37f3bf6 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Wed, 7 Jun 2023 13:17:40 +0200 Subject: [PATCH] STM --- common/protob/messages-ble.proto | 101 ++ common/protob/messages-common.proto | 1 + common/protob/messages.proto | 11 + core/Makefile | 3 + core/SConscript.bootloader | 8 +- core/SConscript.firmware | 3 +- core/SConscript.unix | 7 + core/embed/ble_firmware/int_comm.c | 35 +- core/embed/bootloader/main.c | 371 +++++-- core/embed/bootloader/messages.c | 373 +++---- core/embed/bootloader/messages.h | 11 +- core/embed/bootloader/protob/messages.pb.c | 12 + core/embed/bootloader/protob/messages.pb.h | 68 +- core/embed/bootloader/protob/messages.proto | 44 + .../extmod/modtrezorio/modtrezorio-ble.h | 151 +++ .../extmod/modtrezorio/modtrezorio-poll.h | 85 +- core/embed/extmod/modtrezorio/modtrezorio.c | 14 +- .../extmod/modtrezorutils/modtrezorutils.c | 6 + core/embed/firmware/main.c | 20 + core/embed/lib/ble/dfu.c | 135 +++ core/embed/lib/ble/dfu.h | 17 + core/embed/lib/ble/fwu.c | 664 +++++++++++++ core/embed/lib/ble/fwu.h | 128 +++ core/embed/lib/ble/int_comm_defs.h | 49 + core/embed/lib/ble/messages.c | 124 +++ core/embed/lib/ble/messages.h | 20 + core/embed/lib/ble/state.c | 73 ++ core/embed/lib/ble/state.h | 26 + core/embed/lib/protob_helpers.c | 87 ++ core/embed/lib/protob_helpers.h | 87 ++ core/embed/rust/Cargo.toml | 1 + core/embed/rust/build.rs | 3 + core/embed/rust/rust_ui.h | 5 + core/embed/rust/src/trezorhal/ble.rs | 9 + core/embed/rust/src/trezorhal/mod.rs | 2 + .../rust/src/ui/model_tt/bootloader/mod.rs | 82 +- .../rust/src/ui/model_tt/bootloader/theme.rs | 2 + .../src/ui/model_tt/component/keyboard/pin.rs | 5 +- core/embed/rust/trezorhal.h | 1 + .../backend/nrf_sw/nrf_sw_backend_hash.c | 1 - .../pca10056/s140/armgcc/Makefile | 1 + .../segger/SEGGER_MMD/HardFaultHandler.S | 208 ++++ core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c | 155 +++ core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h | 65 ++ .../segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S | 927 ++++++++++++++++++ .../SEGGER_MMD/SEGGER_HardFaultHandler.c | 280 ++++++ core/embed/trezorhal/boards/board-unix.h | 1 + core/embed/trezorhal/boards/trezor_t3w1_d1.h | 1 + .../trezorhal/boards/trezor_t3w1_d1_NRF.h | 24 + core/embed/trezorhal/stm32f4/ble.c | 421 ++++++++ core/embed/trezorhal/stm32f4/ble.h | 30 + core/embed/trezorhal/stm32f4/usb.c | 9 + core/embed/trezorhal/unix/ble.c | 29 + core/embed/trezorhal/unix/ble.h | 19 + core/embed/trezorhal/usb.h | 1 + core/mocks/generated/trezorio/__init__.pyi | 5 +- core/mocks/generated/trezorio/ble.pyi | 43 + core/mocks/generated/trezorutils.pyi | 1 + core/site_scons/boards/stm32f4_common.py | 1 + core/site_scons/boards/trezor_t3w1_d1.py | 10 +- core/site_scons/tools.py | 18 +- core/src/all_modules.py | 16 + core/src/apps/base.py | 18 +- core/src/apps/common/request_pin.py | 14 +- .../apps/management/ble/comparison_request.py | 15 + core/src/apps/management/ble/disconnect.py | 16 + core/src/apps/management/ble/erase_bonds.py | 16 + .../apps/management/ble/pairing_request.py | 16 + .../src/apps/management/ble/repair_request.py | 13 + .../ble/upload_ble_firmware_init.py | 75 ++ core/src/apps/webauthn/__init__.py | 4 +- core/src/apps/webauthn/fido2.py | 10 +- core/src/apps/workflow_handlers.py | 12 +- core/src/bluetooth.py | 72 ++ core/src/mutex.py | 23 + core/src/session.py | 44 +- core/src/trezor/enums/FailureType.py | 1 + core/src/trezor/enums/MessageType.py | 9 + core/src/trezor/enums/__init__.py | 10 + core/src/trezor/messages.py | 96 ++ core/src/trezor/ui/layouts/common.py | 13 +- core/src/trezor/ui/layouts/tt/__init__.py | 6 +- core/src/trezor/utils.py | 1 + core/src/trezor/wire/__init__.py | 73 +- 84 files changed, 5288 insertions(+), 379 deletions(-) create mode 100644 common/protob/messages-ble.proto create mode 100644 core/embed/extmod/modtrezorio/modtrezorio-ble.h create mode 100644 core/embed/lib/ble/dfu.c create mode 100644 core/embed/lib/ble/dfu.h create mode 100644 core/embed/lib/ble/fwu.c create mode 100644 core/embed/lib/ble/fwu.h create mode 100644 core/embed/lib/ble/int_comm_defs.h create mode 100644 core/embed/lib/ble/messages.c create mode 100644 core/embed/lib/ble/messages.h create mode 100644 core/embed/lib/ble/state.c create mode 100644 core/embed/lib/ble/state.h create mode 100644 core/embed/lib/protob_helpers.c create mode 100644 core/embed/lib/protob_helpers.h create mode 100644 core/embed/rust/src/trezorhal/ble.rs create mode 100644 core/embed/segger/SEGGER_MMD/HardFaultHandler.S create mode 100644 core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c create mode 100644 core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h create mode 100644 core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S create mode 100644 core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c create mode 100644 core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h create mode 100644 core/embed/trezorhal/stm32f4/ble.c create mode 100644 core/embed/trezorhal/stm32f4/ble.h create mode 100644 core/embed/trezorhal/unix/ble.c create mode 100644 core/embed/trezorhal/unix/ble.h create mode 100644 core/mocks/generated/trezorio/ble.pyi create mode 100644 core/src/apps/management/ble/comparison_request.py create mode 100644 core/src/apps/management/ble/disconnect.py create mode 100644 core/src/apps/management/ble/erase_bonds.py create mode 100644 core/src/apps/management/ble/pairing_request.py create mode 100644 core/src/apps/management/ble/repair_request.py create mode 100644 core/src/apps/management/ble/upload_ble_firmware_init.py create mode 100644 core/src/bluetooth.py create mode 100644 core/src/mutex.py diff --git a/common/protob/messages-ble.proto b/common/protob/messages-ble.proto new file mode 100644 index 0000000000..e7ea875d85 --- /dev/null +++ b/common/protob/messages-ble.proto @@ -0,0 +1,101 @@ +syntax = "proto2"; +package hw.trezor.messages.ble; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageBLE"; + +option (include_in_bitcoin_only) = true; + +import "messages.proto"; + + +/** + * Request: initializes upload of a new ble firmware im + * @start + * @next UploadBLEFirmwareNextChunk + * @next Failure + */ +message UploadBLEFirmwareInit { + required bytes init_data = 1; + required uint32 binsize = 2; +} + + +/** + * Response: Requests next chunk of a new ble firmware im + * @next UploadBLEFirmwareChunk + */ +message UploadBLEFirmwareNextChunk { + required uint32 offset = 1; +} + +/** + * Request: sends next chunk of a new ble firmware im + * @next UploadBLEFirmwareNextChunk + * @next Success + * @next Failure + */ +message UploadBLEFirmwareChunk { + required bytes data = 1; +} + + +/** + * Request: erases all BLE bonds + * @start + * @next Success + * @next Failure + */ +message EraseBonds { +} + + +/** + * Request: disconnect + * @start + * @next Success + * @next Failure + */ +message Disconnect { +} + + + +/** + * Request: initiates new pairing request + * @start + * @next AuthKey + * @next Failure + */ +message PairingRequest { +} + +/** + * Request: confirms pairing request with auth key entered on the device + * @end + */ +message AuthKey { + required bytes key = 1; +} + + +/** + * Request: initiates repairing request + * @start + * @next Success + * @next Failure + */ +message RepairRequest { +} + + +/** + * Request: initiates new pairing request with numeric comparison + * @start + * @next Success + * @next Failure + */ +message ComparisonRequest { + required bytes key = 1; +} diff --git a/common/protob/messages-common.proto b/common/protob/messages-common.proto index e7a0ac7153..3de07ca537 100644 --- a/common/protob/messages-common.proto +++ b/common/protob/messages-common.proto @@ -39,6 +39,7 @@ message Failure { Failure_PinMismatch = 12; Failure_WipeCodeMismatch = 13; Failure_InvalidSession = 14; + Failure_DeviceIsBusy = 15; Failure_FirmwareError = 99; } } diff --git a/common/protob/messages.proto b/common/protob/messages.proto index ae9637e060..7f210217ed 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -140,6 +140,17 @@ enum MessageType { MessageType_FirmwareRequest = 8 [(bitcoin_only) = true, (wire_out) = true, (wire_bootloader) = true]; MessageType_SelfTest = 32 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true]; + // BLE + MessageType_UploadBLEFirmwareInit = 8000 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_UploadBLEFirmwareNextChunk = 8001 [(bitcoin_only) = true, (wire_out) = true]; + MessageType_UploadBLEFirmwareChunk = 8002 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_PairingRequest = 8003 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_AuthKey = 8004 [(bitcoin_only) = true, (wire_out) = true]; + MessageType_RepairRequest = 8005 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_EraseBonds = 8006 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_Disconnect = 8007 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_ComparisonRequest = 8008 [(bitcoin_only) = true, (wire_in) = true]; + // Bitcoin MessageType_GetPublicKey = 11 [(bitcoin_only) = true, (wire_in) = true]; MessageType_PublicKey = 12 [(bitcoin_only) = true, (wire_out) = true]; diff --git a/core/Makefile b/core/Makefile index 7ce585df08..66894e9313 100644 --- a/core/Makefile +++ b/core/Makefile @@ -395,6 +395,9 @@ upload: ## upload firmware using trezorctl upload_prodtest: ## upload prodtest using trezorctl trezorctl firmware_update -s -f $(PRODTEST_BUILD_DIR)/prodtest.bin +upload_ble: ## upload ble firmware using trezorctl + trezorctl ble update $(BLE_FIRMWARE_BUILD_DIR)/ble_firmware.zip + coverage: ## generate coverage report ./tools/coverage-report diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 17d0b24a76..20acd2d4dd 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -20,7 +20,7 @@ if TREZOR_MODEL in ('1', ): ) Return() -FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga"] +FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "ble"] CCFLAGS_MOD = '' CPPPATH_MOD = [] @@ -38,9 +38,9 @@ if TREZOR_MODEL in ('R', ): FONT_BIG=None elif TREZOR_MODEL in ('T', 'T3W1', 'DISC1'): FONT_NORMAL='Font_TTHoves_Regular_21' - FONT_DEMIBOLD=None + FONT_DEMIBOLD='Font_TTHoves_Regular_21' FONT_BOLD='Font_TTHoves_Bold_17' - FONT_MONO=None + FONT_MONO='Font_TTHoves_Regular_21' FONT_BIG=None # modtrezorcrypto @@ -87,6 +87,7 @@ SOURCE_MOD += [ 'embed/lib/fonts/fonts.c', 'embed/lib/fonts/font_bitmap.c', 'embed/lib/image.c', + 'embed/lib/protob_helpers.c', 'embed/extmod/modtrezorcrypto/rand.c', 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', @@ -150,7 +151,6 @@ env.Replace( CPPPATH=[ 'embed/rust', 'embed/bootloader', - 'embed/bootloader/nanopb', 'embed/bootloader/protob', 'embed/lib', 'embed/models', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 86b092a423..c94660dc1e 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -19,7 +19,7 @@ FEATURE_FLAGS = { "SYSTEM_VIEW": False, } -FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga"] +FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb", "optiga", "ble"] CCFLAGS_MOD = '' CPPPATH_MOD = [] @@ -436,6 +436,7 @@ env.Replace( 'embed/firmware', 'embed/lib', 'embed/models', + 'embed/firmware/ble', 'embed/trezorhal', 'embed/extmod/modtrezorui', 'vendor/micropython', diff --git a/core/SConscript.unix b/core/SConscript.unix index 8730b9eaa4..4c82ec333b 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -372,6 +372,7 @@ SOURCE_MICROPYTHON = [ ] SOURCE_UNIX = [ + 'embed/trezorhal/unix/ble.c', 'embed/trezorhal/unix/common.c', 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/flash.c', @@ -382,6 +383,10 @@ SOURCE_UNIX = [ 'embed/unix/main.c', 'embed/unix/main_main.c', 'embed/unix/profile.c', + 'embed/lib/ble/state.c', + 'embed/lib/ble/fwu.c', + 'embed/lib/ble/messages.c', + 'embed/lib/ble/dfu.c', 'vendor/micropython/shared/runtime/gchelper_generic.c', 'vendor/micropython/ports/unix/alloc.c', 'vendor/micropython/ports/unix/gccollect.c', @@ -517,6 +522,7 @@ env.Replace( 'embed/unix', 'embed/trezorhal', 'embed/trezorhal/unix', + 'embed/trezorhal/unix/ble', 'embed/extmod/modtrezorui', 'vendor/micropython', 'vendor/micropython/ports/unix', @@ -803,6 +809,7 @@ def cargo_build(): features.append('touch') features.append('button') features.append('sd_card') + features.append('ble') if TREZOR_MODEL in ('R', '1'): features.append('button') diff --git a/core/embed/ble_firmware/int_comm.c b/core/embed/ble_firmware/int_comm.c index 5aa619b039..a577e3732d 100644 --- a/core/embed/ble_firmware/int_comm.c +++ b/core/embed/ble_firmware/int_comm.c @@ -7,6 +7,7 @@ #include "ble_nus.h" #include "connection.h" #include "messages.pb.h" +#include "nrf_dfu_types.h" #include "nrf_drv_spi.h" #include "nrf_log.h" #include "pm.h" @@ -18,7 +19,7 @@ static uint8_t m_uart_rx_data[BLE_NUS_MAX_DATA_LEN]; static uint8_t m_spi_tx_data[BLE_PACKET_SIZE]; -static bool m_uart_rx_data_ready_internal = false; +static volatile bool m_uart_rx_data_ready_internal = false; BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */ @@ -28,6 +29,11 @@ static const nrf_drv_spi_t spi = static volatile bool spi_xfer_done = true; /**< Flag used to indicate that SPI instance completed the transfer. */ +#define CODE_PAGE_SIZE (MBR_PAGE_SIZE_IN_WORDS * sizeof(uint32_t)) +#define BOOTLOADER_SETTINGS_PAGE_SIZE (CODE_PAGE_SIZE) +uint8_t m_dfu_settings_buffer[BOOTLOADER_SETTINGS_PAGE_SIZE] + __attribute__((section(".bootloader_settings_page"))) __attribute__((used)); + /** * @brief SPI user event handler. * @param event @@ -353,15 +359,24 @@ void nus_data_handler(ble_nus_evt_t *p_evt) { /**@snippet [Handling the data received over BLE] */ void send_status_event(void) { - uint8_t tx_data[] = { - INTERNAL_EVENT_STATUS, - (get_connection_handle() != BLE_CONN_HANDLE_INVALID) ? 1 - : 0, // connected - is_advertising() ? 1 : 0, // advertising - is_advertising_wl() ? 1 : 0, // advertising whitelist - pm_peer_count(), // peer count - }; - send_packet(INTERNAL_EVENT, tx_data, sizeof(tx_data)); + ble_version_t version = {0}; + nrf_dfu_settings_t *settins = (nrf_dfu_settings_t *)m_dfu_settings_buffer; + + sd_ble_version_get(&version); + + event_status_msg_t msg = {0}; + msg.msg_id = INTERNAL_EVENT_STATUS; + msg.connected = (get_connection_handle() != BLE_CONN_HANDLE_INVALID) ? 1 : 0; + msg.advertising = is_advertising() ? 1 : 0; + msg.advertising_whitelist = is_advertising_wl() ? 1 : 0; + msg.peer_count = pm_peer_count(); + msg.sd_version_number = version.version_number; + msg.sd_company_id = version.company_id; + msg.sd_subversion_number = version.subversion_number; + msg.app_version = settins->app_version; + msg.bld_version = settins->bootloader_version; + + send_packet(INTERNAL_EVENT, (uint8_t *)&msg, sizeof(msg)); } void send_success_event(void) { diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index fd6a6e5d45..dadfaad359 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -50,12 +50,19 @@ #ifdef USE_RGB_LED #include "rgb_led.h" #endif +#ifdef USE_BLE +#include "ble.h" +#include "ble/state.h" +#endif + #include "model.h" #include "usb.h" #include "version.h" #include "bootui.h" #include "messages.h" +#include "messages.pb.h" +#include "protob_helpers.h" #include "rust_ui.h" #include "unit_variant.h" @@ -81,13 +88,12 @@ static const uint8_t * const BOOTLOADER_KEYS[] = { #endif }; -#define USB_IFACE_NUM 0 - typedef enum { SHUTDOWN = 0, CONTINUE_TO_FIRMWARE = 0xAABBCCDD, RETURN_TO_MENU = 0x55667788, -} usb_result_t; + NO_RESULT = -1, +} comm_result_t; volatile secbool dont_optimize_out_true = sectrue; void failed_jump_to_firmware(void); @@ -133,12 +139,112 @@ static void usb_init_all(secbool usb21_landing) { usb_start(); } -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); +void stop_comm(void) { + hal_delay(100); + usb_stop(); + usb_deinit(); +#ifdef USE_BLE + stop_advertising(); + ble_stop(); +#endif +} +static comm_result_t process_common_messages(uint8_t iface, uint8_t *buf, + const vendor_header *const vhdr, + const image_header *const hdr) { + uint16_t msg_id; + uint32_t msg_size; + uint32_t response; + int32_t upload_response; + if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) { + // invalid header -> discard + return NO_RESULT; + } + switch (msg_id) { + case MessageType_MessageType_Initialize: + process_msg_Initialize(iface, msg_size, buf, vhdr, hdr); + break; + case MessageType_MessageType_Ping: + process_msg_Ping(iface, msg_size, buf); + break; + case MessageType_MessageType_WipeDevice: + response = ui_screen_wipe_confirm(); + if (INPUT_CANCEL == response) { + send_user_abort(iface, "Wipe cancelled"); + stop_comm(); + return RETURN_TO_MENU; + } + ui_screen_wipe(); + upload_response = process_msg_WipeDevice(iface, msg_size, buf); + if (upload_response < 0) { // error + screen_wipe_fail(); + stop_comm(); + return SHUTDOWN; + } else { // success + screen_wipe_success(); + stop_comm(); + return SHUTDOWN; + } + break; + case MessageType_MessageType_FirmwareErase: + process_msg_FirmwareErase(iface, msg_size, buf); + break; + case MessageType_MessageType_FirmwareUpload: + upload_response = process_msg_FirmwareUpload(iface, msg_size, buf); + if (upload_response < 0 && + upload_response != + UPLOAD_ERR_USER_ABORT) { // error, but not user abort + if (upload_response == UPLOAD_ERR_BOOTLOADER_LOCKED) { + ui_screen_install_restricted(); + } else { + ui_screen_fail(); + } + stop_comm(); + return SHUTDOWN; + } else if (upload_response == UPLOAD_ERR_USER_ABORT) { + stop_comm(); + return RETURN_TO_MENU; + } else if (upload_response == 0) { // last chunk received + ui_screen_install_progress_upload(1000); + ui_screen_done(4, sectrue); + ui_screen_done(3, secfalse); + hal_delay(1000); + ui_screen_done(2, secfalse); + hal_delay(1000); + ui_screen_done(1, secfalse); + hal_delay(1000); + stop_comm(); + ui_screen_boot_empty(true); + return CONTINUE_TO_FIRMWARE; + } + break; + case MessageType_MessageType_GetFeatures: + process_msg_GetFeatures(iface, msg_size, buf, vhdr, hdr); + break; +#ifdef USE_OPTIGA + case MessageType_MessageType_UnlockBootloader: + response = ui_screen_unlock_bootloader_confirm(); + if (INPUT_CANCEL == response) { + send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled"); + stop_comm(); + return RETURN_TO_MENU; + } + process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf); + screen_unlock_bootloader_success(); + stop_comm(); + return SHUTDOWN; + break; +#endif + default: + process_msg_unknown(iface, msg_size, buf); + break; + } + + return NO_RESULT; +} + +static comm_result_t bootloader_usb_loop(const vendor_header *const vhdr, + const image_header *const hdr) { uint8_t buf[USB_PACKET_SIZE]; for (;;) { @@ -150,107 +256,155 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, if (r != USB_PACKET_SIZE) { continue; } - 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; - } - switch (msg_id) { - case MessageType_MessageType_Initialize: - process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; - case MessageType_MessageType_Ping: - process_msg_Ping(USB_IFACE_NUM, msg_size, buf); - break; - case MessageType_MessageType_WipeDevice: - response = ui_screen_wipe_confirm(); - if (INPUT_CANCEL == response) { - send_user_abort(USB_IFACE_NUM, "Wipe cancelled"); - hal_delay(100); - usb_stop(); - usb_deinit(); - return RETURN_TO_MENU; - } - ui_screen_wipe(); - r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf); - if (r < 0) { // error - screen_wipe_fail(); - hal_delay(100); - usb_stop(); - usb_deinit(); - return SHUTDOWN; - } else { // success - screen_wipe_success(); - hal_delay(100); - usb_stop(); - usb_deinit(); - return SHUTDOWN; - } - break; - case MessageType_MessageType_FirmwareErase: - process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf); - break; - case MessageType_MessageType_FirmwareUpload: - r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); - if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort - if (r == UPLOAD_ERR_BOOTLOADER_LOCKED) { - ui_screen_install_restricted(); - } else { - ui_screen_fail(); - } - usb_stop(); - usb_deinit(); - return SHUTDOWN; - } else if (r == UPLOAD_ERR_USER_ABORT) { - hal_delay(100); - usb_stop(); - usb_deinit(); - return RETURN_TO_MENU; - } else if (r == 0) { // last chunk received - ui_screen_install_progress_upload(1000); - ui_screen_done(4, sectrue); - ui_screen_done(3, secfalse); - hal_delay(1000); - ui_screen_done(2, secfalse); - hal_delay(1000); - ui_screen_done(1, secfalse); - hal_delay(1000); - usb_stop(); - usb_deinit(); - ui_screen_boot_empty(true); - return CONTINUE_TO_FIRMWARE; - } - break; - case MessageType_MessageType_GetFeatures: - process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; -#ifdef USE_OPTIGA - case MessageType_MessageType_UnlockBootloader: - response = ui_screen_unlock_bootloader_confirm(); - if (INPUT_CANCEL == response) { - send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled"); - hal_delay(100); - usb_stop(); - usb_deinit(); - return RETURN_TO_MENU; - } - process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf); - screen_unlock_bootloader_success(); - hal_delay(100); - usb_stop(); - usb_deinit(); - return SHUTDOWN; - break; -#endif - default: - process_msg_unknown(USB_IFACE_NUM, msg_size, buf); - break; + + comm_result_t res = process_common_messages(USB_IFACE_NUM, buf, vhdr, hdr); + + if (res != NO_RESULT) { + return res; } } } +#ifdef USE_BLE +static comm_result_t bootloader_ble_loop(const vendor_header *const vhdr, + const image_header *const hdr) { + start_advertising(true); + + uint8_t buf[BLE_PACKET_SIZE]; + + uint8_t active_iface = 0; + int r = 0; + + for (;;) { + for (;;) { + r = ble_ext_comm_receive(buf, sizeof(buf)); + + if (r == BLE_PACKET_SIZE) { + active_iface = BLE_EXT_IFACE_NUM; + break; + } + + r = ble_int_comm_receive(buf, sizeof(buf)); + if (r != 0) { + active_iface = BLE_INT_IFACE_NUM; + break; + } + } + + if (active_iface == BLE_INT_IFACE_NUM) { + for (;;) { + bool next = false; + if (r == 0) { + r = ble_int_comm_receive(buf, sizeof(buf)); + + if (r == 0) { + continue; + } + } + + r = 0; + + 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; + } + switch (msg_id) { + case MessageType_MessageType_ComparisonRequest: // pairing request + response = + process_msg_ComparisonRequest(active_iface, msg_size, buf); + if (response != INPUT_CONFIRM) { + stop_comm(); + return RETURN_TO_MENU; + } + screen_connect(); + next = true; + + break; + case MessageType_MessageType_PairingRequest: // pairing request + response = process_msg_Pairing(active_iface, msg_size, buf); + if (response != INPUT_CONFIRM) { + stop_comm(); + return RETURN_TO_MENU; + } + screen_connect(); + next = true; + + break; + case MessageType_MessageType_RepairRequest: // repairing request + response = process_msg_Repair(active_iface, msg_size, buf); + if (response != INPUT_CONFIRM) { + stop_comm(); + return RETURN_TO_MENU; + } + // screen_connect(); + // todo - screen connect or timeout? + break; + default: + process_msg_unknown(active_iface, msg_size, buf); + break; + } + if (next) { + break; + } + } + } + + if (active_iface == BLE_EXT_IFACE_NUM) { + for (;;) { + if (r == 0) { + r = ble_ext_comm_receive(buf, sizeof(buf)); + + if (r != BLE_PACKET_SIZE) { + continue; + } + } + r = 0; + + comm_result_t res = + process_common_messages(active_iface, buf, vhdr, hdr); + + if (res != NO_RESULT) { + return res; + } + } + } + } +} +#endif + +static comm_result_t bootloader_comm_select(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); + +#ifdef USE_BLE + bool usb = false; + + for (int i = 0; i < 2000; i++) { + hal_delay(1); + if (usb_configured_now() == sectrue) { + usb = true; + break; + } + } + + if (usb) { + return bootloader_usb_loop(vhdr, hdr); + } else { + usb_stop(); + usb_deinit(); + ble_start(); + return bootloader_ble_loop(vhdr, hdr); + } +#else + return bootloader_usb_loop(vhdr, hdr); +#endif +} + secbool check_vendor_header_keys(const vendor_header *const vhdr) { return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N, BOOTLOADER_KEYS); @@ -475,6 +629,9 @@ int bootloader_main(void) { #ifdef USE_RGB_LED rgb_led_init(); #endif +#ifdef USE_BLE + ble_comm_init(); +#endif unit_variant_init(); @@ -557,7 +714,7 @@ int bootloader_main(void) { ui_screen_welcome(); // and start the usb loop - switch (bootloader_usb_loop(NULL, NULL)) { + switch (bootloader_comm_select(NULL, NULL)) { case CONTINUE_TO_FIRMWARE: continue_to_firmware = sectrue; continue_to_firmware_backup = sectrue; @@ -614,7 +771,7 @@ int bootloader_main(void) { break; case SCREEN_WAIT_FOR_HOST: screen_connect(); - switch (bootloader_usb_loop(&vhdr, hdr)) { + switch (bootloader_comm_select(&vhdr, hdr)) { case CONTINUE_TO_FIRMWARE: continue_to_firmware = sectrue; continue_to_firmware_backup = sectrue; diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 9226b1b809..e08bff0aa6 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -24,6 +24,11 @@ #include #include "messages.pb.h" +#include TREZOR_BOARD + +#ifdef USE_BLE +#include "ble.h" +#endif #include "common.h" #include "flash.h" #include "image.h" @@ -35,6 +40,7 @@ #include "bootui.h" #include "messages.h" +#include "protob_helpers.h" #include "rust_ui.h" #include "memzero.h" @@ -48,30 +54,9 @@ #include "secret.h" #endif -#define MSG_HEADER1_LEN 9 -#define MSG_HEADER2_LEN 1 - -secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, - uint32_t *msg_size) { - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { - return secfalse; - } - *msg_id = (buf[3] << 8) + buf[4]; - *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; - return sectrue; -} - -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t buf[USB_PACKET_SIZE]; -} usb_write_state; - /* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count) { - usb_write_state *state = (usb_write_state *)(stream->state); +static bool _write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { + write_state *state = (write_state *)(stream->state); size_t written = 0; // while we have data left @@ -91,9 +76,20 @@ static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf, USB_PACKET_SIZE - state->packet_pos); written += USB_PACKET_SIZE - state->packet_pos; // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); + + if (state->iface_num == USB_IFACE_NUM) { + int r = usb_webusb_write_blocking(state->iface_num, state->buf, + USB_PACKET_SIZE, USB_TIMEOUT); + ensure(sectrue * (r == USB_PACKET_SIZE), NULL); + } +#ifdef USE_BLE + else if (state->iface_num == BLE_INT_IFACE_NUM) { + ble_int_comm_send(state->buf, USB_PACKET_SIZE, INTERNAL_MESSAGE); + } else if (state->iface_num == BLE_EXT_IFACE_NUM) { + ble_int_comm_send(state->buf, USB_PACKET_SIZE, EXTERNAL_MESSAGE); + } +#endif + // prepare new packet state->packet_index++; memzero(state->buf, USB_PACKET_SIZE); @@ -105,7 +101,7 @@ static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf, return true; } -static void _usb_write_flush(usb_write_state *state) { +static void _write_flush(write_state *state) { // if packet is not filled up completely if (state->packet_pos < USB_PACKET_SIZE) { // pad it with zeroes @@ -113,94 +109,53 @@ static void _usb_write_flush(usb_write_state *state) { USB_PACKET_SIZE - state->packet_pos); } // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); + if (state->iface_num == USB_IFACE_NUM) { + int r = usb_webusb_write_blocking(state->iface_num, state->buf, + USB_PACKET_SIZE, USB_TIMEOUT); + ensure(sectrue * (r == USB_PACKET_SIZE), NULL); + } +#ifdef USE_BLE + else if (state->iface_num == BLE_INT_IFACE_NUM) { + ble_int_comm_send(state->buf, USB_PACKET_SIZE, INTERNAL_MESSAGE); + } else if (state->iface_num == BLE_EXT_IFACE_NUM) { + ble_int_comm_send(state->buf, USB_PACKET_SIZE, EXTERNAL_MESSAGE); + } +#endif } -static secbool _send_msg(uint8_t iface_num, uint16_t msg_id, - const pb_msgdesc_t *fields, const void *msg) { - // determine message size by serializing it into a dummy stream - pb_ostream_t sizestream = {.callback = NULL, - .state = NULL, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - if (false == pb_encode(&sizestream, fields, msg)) { - return secfalse; - } - const uint32_t msg_size = sizestream.bytes_written; +#define BLE_GAP_PASSKEY_LEN 6 - usb_write_state state = { - .iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = - { - '?', - '#', - '#', - (msg_id >> 8) & 0xFF, - msg_id & 0xFF, - (msg_size >> 24) & 0xFF, - (msg_size >> 16) & 0xFF, - (msg_size >> 8) & 0xFF, - msg_size & 0xFF, - }, - }; +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool _write_authkey(pb_ostream_t *stream, const pb_field_iter_t *field, + void *const *arg) { + uint8_t *key = (uint8_t *)(*arg); + if (!pb_encode_tag_for_field(stream, field)) return false; - pb_ostream_t stream = {.callback = &_usb_write, - .state = &state, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - - if (false == pb_encode(&stream, fields, msg)) { - return secfalse; - } - - _usb_write_flush(&state); - - return sectrue; + return pb_encode_string(stream, (uint8_t *)key, BLE_GAP_PASSKEY_LEN); } -#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default -#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ - { msg_send.FIELD = VALUE; } -#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - msg_send.FIELD = VALUE; \ - } -#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ - } -#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ - } -#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ - memcpy(msg_send.FIELD.bytes, VALUE, \ - MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ - msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ - } -#define MSG_SEND(TYPE) \ - _send_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, &msg_send) +static bool _read_authkey(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + uint8_t *key_buffer = (uint8_t *)(*arg); -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t *buf; -} usb_read_state; + if (stream->bytes_left > BLE_GAP_PASSKEY_LEN) { + return false; + } + + memset(key_buffer, 0, BLE_GAP_PASSKEY_LEN); + + while (stream->bytes_left) { + // read data + if (!pb_read(stream, (pb_byte_t *)(key_buffer), + (stream->bytes_left > BLE_GAP_PASSKEY_LEN) + ? BLE_GAP_PASSKEY_LEN + : stream->bytes_left)) { + return false; + } + } + + return true; +} static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) { for (int retry = 0;; retry++) { @@ -220,16 +175,54 @@ static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) { } } +#ifdef USE_BLE +static void _ble_read_retry(uint8_t iface_num, uint8_t *buf) { + for (int retry = 0;; retry++) { + int r = ble_ext_comm_receive(buf, BLE_PACKET_SIZE); + if (r != BLE_PACKET_SIZE) { // reading failed + if (r == 0 && retry < 500) { + // only timeout => let's try again + HAL_Delay(10); + continue; + } else { + // error + error_shutdown("BLE ERROR", + "Error reading from BLE. Try different BLE cable."); + } + } + return; // success + } +} + +static void _ble_read_retry_int(uint8_t iface_num, uint8_t *buf) { + for (int retry = 0;; retry++) { + int r = ble_int_comm_receive(buf, USB_PACKET_SIZE); + if (r == 0) { // reading failed + if (retry < 500) { + // only timeout => let's try again + HAL_Delay(10); + continue; + } else { + // error + error_shutdown("BLE ERROR", + "Error reading from BLE. Try different BLE cable."); + } + } + return; // success + } +} +#endif + /* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { - usb_read_state *state = (usb_read_state *)(stream->state); +static bool _read(pb_istream_t *stream, uint8_t *buf, size_t count) { + read_state *state = (read_state *)(stream->state); size_t read = 0; // while we have data left while (read < count) { size_t remaining = count - read; // if all remaining data fit into our packet - if (state->packet_pos + remaining <= USB_PACKET_SIZE) { + if (state->packet_pos + remaining <= state->packet_size) { // append data from buf to state->buf memcpy(buf + read, state->buf + state->packet_pos, remaining); // advance position @@ -239,10 +232,19 @@ static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { } else { // append data that fits memcpy(buf + read, state->buf + state->packet_pos, - USB_PACKET_SIZE - state->packet_pos); - read += USB_PACKET_SIZE - state->packet_pos; + state->packet_size - state->packet_pos); + read += state->packet_size - state->packet_pos; // read next packet (with retry) - _usb_webusb_read_retry(state->iface_num, state->buf); +#ifdef USE_BLE + if (state->iface_num == BLE_EXT_IFACE_NUM) { + _ble_read_retry(state->iface_num, state->buf); + } else if (state->iface_num == BLE_INT_IFACE_NUM) { + _ble_read_retry_int(state->iface_num, state->buf); + } else +#endif + { + _usb_webusb_read_retry(state->iface_num, state->buf); + } // prepare next packet state->packet_index++; state->packet_pos = MSG_HEADER2_LEN; @@ -252,43 +254,24 @@ static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { return true; } -static void _usb_read_flush(usb_read_state *state) { (void)state; } +static void _read_flush(read_state *state) { (void)state; } -static secbool _recv_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const pb_msgdesc_t *fields, void *msg) { - usb_read_state state = {.iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = buf}; - - pb_istream_t stream = {.callback = &_usb_read, - .state = &state, - .bytes_left = msg_size, - .errmsg = NULL}; - - if (false == pb_decode_noinit(&stream, fields, msg)) { - return secfalse; - } - - _usb_read_flush(&state); - - return sectrue; -} - -#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default -#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ - { \ - msg_recv.FIELD.funcs.decode = &CALLBACK; \ - msg_recv.FIELD.arg = (void *)ARGUMENT; \ - } -#define MSG_RECV(TYPE) \ - _recv_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv) +#define MSG_SEND_BLD(msg) (MSG_SEND(msg, _write, _write_flush)) +#ifdef USE_BLE +#define MSG_RECV_BLD(msg, iface_num) \ + (MSG_RECV( \ + msg, _read, _read_flush, \ + ((iface_num) == BLE_EXT_IFACE_NUM ? BLE_PACKET_SIZE : USB_PACKET_SIZE))) +#else +#define MSG_RECV_BLD(msg, iface_num) \ + (MSG_RECV(msg, _read, _read_flush, USB_PACKET_SIZE)) +#endif void send_user_abort(uint8_t iface_num, const char *msg) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); MSG_SEND_ASSIGN_STRING(message, msg); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); } static void send_msg_features(uint8_t iface_num, @@ -320,14 +303,66 @@ static void send_msg_features(uint8_t iface_num, MSG_SEND_ASSIGN_VALUE(bootloader_locked, (secret_bootloader_locked() == sectrue)); #endif - MSG_SEND(Features); + MSG_SEND_BLD(Features); +} + +uint32_t process_msg_ComparisonRequest(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf) { + uint8_t buffer[BLE_GAP_PASSKEY_LEN]; + MSG_RECV_INIT(ComparisonRequest); + MSG_RECV_CALLBACK(key, _read_authkey, buffer); + MSG_RECV_BLD(ComparisonRequest, iface_num); + + uint32_t result = screen_comparison_confirm(buffer, BLE_GAP_PASSKEY_LEN); + + if (result == INPUT_CONFIRM) { + MSG_SEND_INIT(Success); + MSG_SEND_BLD(Success); + } else { + send_user_abort(iface_num, "Pairing cancelled"); + } + + return result; +} + +uint32_t process_msg_Pairing(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf) { + uint8_t buffer[BLE_GAP_PASSKEY_LEN]; + MSG_RECV_INIT(PairingRequest); + MSG_RECV_BLD(PairingRequest, iface_num); + + uint32_t result = screen_pairing_confirm(buffer); + + if (result == INPUT_CONFIRM) { + MSG_SEND_INIT(AuthKey); + MSG_SEND_CALLBACK(key, _write_authkey, buffer); + MSG_SEND_BLD(AuthKey); + } else { + send_user_abort(iface_num, "Pairing cancelled"); + } + + return result; +} + +uint32_t process_msg_Repair(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf) { + MSG_RECV_INIT(RepairRequest); + MSG_RECV_BLD(RepairRequest, iface_num); + uint32_t result = screen_repair_confirm(); + if (result == INPUT_CONFIRM) { + MSG_SEND_INIT(Success); + MSG_SEND_BLD(Success); + } else { + send_user_abort(iface_num, "Pairing cancelled"); + } + return result; } void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, const vendor_header *const vhdr, const image_header *const hdr) { MSG_RECV_INIT(Initialize); - MSG_RECV(Initialize); + MSG_RECV_BLD(Initialize, iface_num); send_msg_features(iface_num, vhdr, hdr); } @@ -335,17 +370,17 @@ void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, const vendor_header *const vhdr, const image_header *const hdr) { MSG_RECV_INIT(GetFeatures); - MSG_RECV(GetFeatures); + MSG_RECV_BLD(GetFeatures, iface_num); send_msg_features(iface_num, vhdr, hdr); } void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_RECV_INIT(Ping); - MSG_RECV(Ping); + MSG_RECV_BLD(Ping, iface_num); MSG_SEND_INIT(Success); MSG_SEND_ASSIGN_STRING(message, msg_recv.message); - MSG_SEND(Success); + MSG_SEND_BLD(Success); } static uint32_t firmware_remaining, firmware_block, chunk_requested; @@ -357,7 +392,7 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, chunk_requested = 0; MSG_RECV_INIT(FirmwareErase); - MSG_RECV(FirmwareErase); + MSG_RECV_BLD(FirmwareErase, iface_num); firmware_remaining = msg_recv.has_length ? msg_recv.length : 0; if ((firmware_remaining > 0) && @@ -370,13 +405,13 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(FirmwareRequest); MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, 0); MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); + MSG_SEND_BLD(FirmwareRequest); } else { // invalid firmware size MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Wrong firmware size"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); } } @@ -488,13 +523,13 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_RECV_INIT(FirmwareUpload); MSG_RECV_CALLBACK(payload, _read_payload, read_offset); - const secbool r = MSG_RECV(FirmwareUpload); + const secbool r = MSG_RECV_BLD(FirmwareUpload, iface_num); if (sectrue != r || chunk_size != (chunk_requested + read_offset)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_CHUNK_SIZE; } @@ -509,7 +544,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_VENDOR_HEADER; } @@ -517,7 +552,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; } @@ -530,7 +565,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_IMAGE_HEADER; } @@ -538,7 +573,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_IMAGE_MODEL; } @@ -547,7 +582,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; } @@ -628,7 +663,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, chunk_requested = chunk_limit - read_offset; MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, read_offset); MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); + MSG_SEND_BLD(FirmwareRequest); firmware_remaining -= read_offset; return (int)firmware_remaining; @@ -644,7 +679,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_FIRMWARE_TOO_BIG; } @@ -656,14 +691,14 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(FirmwareRequest); MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); + MSG_SEND_BLD(FirmwareRequest); return (int)firmware_remaining; } MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_CHUNK_HASH; } @@ -693,10 +728,10 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(FirmwareRequest); MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); + MSG_SEND_BLD(FirmwareRequest); } else { MSG_SEND_INIT(Success); - MSG_SEND(Success); + MSG_SEND_BLD(Success); } return (int)firmware_remaining; } @@ -711,11 +746,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return WIPE_ERR_CANNOT_ERASE; } else { MSG_SEND_INIT(Success); - MSG_SEND(Success); + MSG_SEND_BLD(Success); return WIPE_OK; } } @@ -740,7 +775,7 @@ void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage); MSG_SEND_ASSIGN_STRING(message, "Unexpected message"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); } #ifdef USE_OPTIGA diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index 45a128560b..f9f2c537c5 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -25,8 +25,10 @@ #include "secbool.h" #include TREZOR_BOARD +#define USB_IFACE_NUM 0 +#define BLE_INT_IFACE_NUM 16 +#define BLE_EXT_IFACE_NUM 17 #define USB_TIMEOUT 500 -#define USB_PACKET_SIZE 64 #define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 @@ -75,4 +77,11 @@ void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, secbool bootloader_WipeDevice(void); +uint32_t process_msg_ComparisonRequest(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf); +uint32_t process_msg_Pairing(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf); + +uint32_t process_msg_Repair(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); + #endif diff --git a/core/embed/bootloader/protob/messages.pb.c b/core/embed/bootloader/protob/messages.pb.c index 087828cf70..6a7a8bba60 100644 --- a/core/embed/bootloader/protob/messages.pb.c +++ b/core/embed/bootloader/protob/messages.pb.c @@ -42,6 +42,18 @@ PB_BIND(FirmwareUpload, FirmwareUpload, AUTO) PB_BIND(UnlockBootloader, UnlockBootloader, AUTO) +PB_BIND(PairingRequest, PairingRequest, AUTO) + + +PB_BIND(AuthKey, AuthKey, AUTO) + + +PB_BIND(RepairRequest, RepairRequest, AUTO) + + +PB_BIND(ComparisonRequest, ComparisonRequest, AUTO) + + diff --git a/core/embed/bootloader/protob/messages.pb.h b/core/embed/bootloader/protob/messages.pb.h index 563cebcafc..4666652b7a 100644 --- a/core/embed/bootloader/protob/messages.pb.h +++ b/core/embed/bootloader/protob/messages.pb.h @@ -23,7 +23,11 @@ typedef enum _MessageType { MessageType_MessageType_ButtonRequest = 26, MessageType_MessageType_ButtonAck = 27, MessageType_MessageType_GetFeatures = 55, - MessageType_MessageType_UnlockBootloader = 96 + MessageType_MessageType_UnlockBootloader = 96, + MessageType_MessageType_PairingRequest = 8003, + MessageType_MessageType_AuthKey = 8004, + MessageType_MessageType_RepairRequest = 8005, + MessageType_MessageType_ComparisonRequest = 8008 } MessageType; typedef enum _FailureType { @@ -38,10 +42,18 @@ typedef enum _ButtonRequestType { } ButtonRequestType; /* Struct definitions */ +typedef struct _AuthKey { + pb_callback_t key; +} AuthKey; + typedef struct _ButtonAck { char dummy_field; } ButtonAck; +typedef struct _ComparisonRequest { + pb_callback_t key; +} ComparisonRequest; + typedef struct _GetFeatures { char dummy_field; } GetFeatures; @@ -50,6 +62,14 @@ typedef struct _Initialize { char dummy_field; } Initialize; +typedef struct _PairingRequest { + char dummy_field; +} PairingRequest; + +typedef struct _RepairRequest { + char dummy_field; +} RepairRequest; + typedef struct _UnlockBootloader { char dummy_field; } UnlockBootloader; @@ -137,8 +157,8 @@ typedef struct _Success { /* Helper constants for enums */ #define _MessageType_MIN MessageType_MessageType_Initialize -#define _MessageType_MAX MessageType_MessageType_UnlockBootloader -#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_UnlockBootloader+1)) +#define _MessageType_MAX MessageType_MessageType_ComparisonRequest +#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_ComparisonRequest+1)) #define _FailureType_MIN FailureType_Failure_UnexpectedMessage #define _FailureType_MAX FailureType_Failure_ProcessError @@ -166,6 +186,10 @@ extern "C" { #define FirmwareRequest_init_default {0, 0} #define FirmwareUpload_init_default {{{NULL}, NULL}, false, {0, {0}}} #define UnlockBootloader_init_default {0} +#define PairingRequest_init_default {0} +#define AuthKey_init_default {{{NULL}, NULL}} +#define RepairRequest_init_default {0} +#define ComparisonRequest_init_default {{{NULL}, NULL}} #define Initialize_init_zero {0} #define GetFeatures_init_zero {0} #define Features_init_zero {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0, false, 0} @@ -178,8 +202,14 @@ extern "C" { #define FirmwareRequest_init_zero {0, 0} #define FirmwareUpload_init_zero {{{NULL}, NULL}, false, {0, {0}}} #define UnlockBootloader_init_zero {0} +#define PairingRequest_init_zero {0} +#define AuthKey_init_zero {{{NULL}, NULL}} +#define RepairRequest_init_zero {0} +#define ComparisonRequest_init_zero {{{NULL}, NULL}} /* Field tags (for use in manual encoding/decoding) */ +#define AuthKey_key_tag 1 +#define ComparisonRequest_key_tag 1 #define ButtonRequest_code_tag 1 #define Failure_code_tag 1 #define Failure_message_tag 2 @@ -294,6 +324,26 @@ X(a, STATIC, OPTIONAL, BYTES, hash, 2) #define UnlockBootloader_CALLBACK NULL #define UnlockBootloader_DEFAULT NULL +#define PairingRequest_FIELDLIST(X, a) \ + +#define PairingRequest_CALLBACK NULL +#define PairingRequest_DEFAULT NULL + +#define AuthKey_FIELDLIST(X, a) \ +X(a, CALLBACK, REQUIRED, BYTES, key, 1) +#define AuthKey_CALLBACK pb_default_field_callback +#define AuthKey_DEFAULT NULL + +#define RepairRequest_FIELDLIST(X, a) \ + +#define RepairRequest_CALLBACK NULL +#define RepairRequest_DEFAULT NULL + +#define ComparisonRequest_FIELDLIST(X, a) \ +X(a, CALLBACK, REQUIRED, BYTES, key, 1) +#define ComparisonRequest_CALLBACK pb_default_field_callback +#define ComparisonRequest_DEFAULT NULL + extern const pb_msgdesc_t Initialize_msg; extern const pb_msgdesc_t GetFeatures_msg; extern const pb_msgdesc_t Features_msg; @@ -306,6 +356,10 @@ extern const pb_msgdesc_t FirmwareErase_msg; extern const pb_msgdesc_t FirmwareRequest_msg; extern const pb_msgdesc_t FirmwareUpload_msg; extern const pb_msgdesc_t UnlockBootloader_msg; +extern const pb_msgdesc_t PairingRequest_msg; +extern const pb_msgdesc_t AuthKey_msg; +extern const pb_msgdesc_t RepairRequest_msg; +extern const pb_msgdesc_t ComparisonRequest_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define Initialize_fields &Initialize_msg @@ -320,9 +374,15 @@ extern const pb_msgdesc_t UnlockBootloader_msg; #define FirmwareRequest_fields &FirmwareRequest_msg #define FirmwareUpload_fields &FirmwareUpload_msg #define UnlockBootloader_fields &UnlockBootloader_msg +#define PairingRequest_fields &PairingRequest_msg +#define AuthKey_fields &AuthKey_msg +#define RepairRequest_fields &RepairRequest_msg +#define ComparisonRequest_fields &ComparisonRequest_msg /* Maximum encoded size of messages (where known) */ /* FirmwareUpload_size depends on runtime parameters */ +/* AuthKey_size depends on runtime parameters */ +/* ComparisonRequest_size depends on runtime parameters */ #define ButtonAck_size 0 #define ButtonRequest_size 2 #define Failure_size 260 @@ -331,7 +391,9 @@ extern const pb_msgdesc_t UnlockBootloader_msg; #define FirmwareRequest_size 12 #define GetFeatures_size 0 #define Initialize_size 0 +#define PairingRequest_size 0 #define Ping_size 258 +#define RepairRequest_size 0 #define Success_size 258 #define UnlockBootloader_size 0 diff --git a/core/embed/bootloader/protob/messages.proto b/core/embed/bootloader/protob/messages.proto index bfe08db486..1d76f1336b 100644 --- a/core/embed/bootloader/protob/messages.proto +++ b/core/embed/bootloader/protob/messages.proto @@ -18,6 +18,10 @@ enum MessageType { MessageType_ButtonAck = 27; MessageType_GetFeatures = 55; MessageType_UnlockBootloader = 96; + MessageType_PairingRequest = 8003; + MessageType_AuthKey = 8004; + MessageType_RepairRequest = 8005; + MessageType_ComparisonRequest = 8008; } /** @@ -155,3 +159,43 @@ message FirmwareUpload { */ message UnlockBootloader { } + + +/** + * Request: initiates new pairing request + * @start + * @next AuthKey + * @next Failure + */ +message PairingRequest { +} + +/** + * Request: confirms pairing request with auth key entered on the device + * @end + */ +message AuthKey { + required bytes key = 1; +} + + +/** + * Request: initiates repairing request + * @start + * @next Success + * @next Failure + */ +message RepairRequest { +} + + + +/** + * Request: initiates new pairing request with numeric comparison + * @start + * @next Success + * @next Failure + */ +message ComparisonRequest { + required bytes key = 1; +} diff --git a/core/embed/extmod/modtrezorio/modtrezorio-ble.h b/core/embed/extmod/modtrezorio/modtrezorio-ble.h new file mode 100644 index 0000000000..487fafc817 --- /dev/null +++ b/core/embed/extmod/modtrezorio/modtrezorio-ble.h @@ -0,0 +1,151 @@ +/* + * 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 . + */ + +#include "ble/dfu.h" +#include "ble/messages.h" + +/// package: trezorio.ble + +/// def update_init(data: bytes, binsize: int) -> int: +/// """ +/// Initializes the BLE firmware update +/// """ +STATIC mp_obj_t mod_trezorio_BLE_update_init(mp_obj_t data, mp_obj_t binsize) { + mp_buffer_info_t buffer = {0}; + mp_int_t binsize_int = mp_obj_get_int(binsize); + + mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ); + + ble_set_dfu_mode(true); + + dfu_result_t result = dfu_update_init(buffer.buf, buffer.len, binsize_int); + if (result == DFU_NEXT_CHUNK) { + return mp_obj_new_int(0); + } else if (result == DFU_SUCCESS) { + ble_set_dfu_mode(false); + return mp_obj_new_int(1); + } else { + ble_set_dfu_mode(false); + mp_raise_msg(&mp_type_RuntimeError, "Upload failed."); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_update_init_obj, + mod_trezorio_BLE_update_init); + +/// def update_chunk(chunk: bytes) -> int: +/// """ +/// Writes next chunk of BLE firmware update +/// """ +STATIC mp_obj_t mod_trezorio_BLE_update_chunk(mp_obj_t data) { + mp_buffer_info_t buffer = {0}; + + mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ); + + dfu_result_t result = dfu_update_chunk(buffer.buf, buffer.len); + + if (result == DFU_NEXT_CHUNK) { + return mp_obj_new_int(0); + } else if (result == DFU_SUCCESS) { + ble_set_dfu_mode(false); + return mp_obj_new_int(1); + } else { + ble_set_dfu_mode(false); + mp_raise_msg(&mp_type_RuntimeError, "Upload failed."); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_update_chunk_obj, + mod_trezorio_BLE_update_chunk); + +/// def write_int(self, msg: bytes) -> int: +/// """ +/// Sends internal message to NRF. +/// """ +STATIC mp_obj_t mod_trezorio_BLE_write_int(mp_obj_t self, mp_obj_t msg) { + mp_buffer_info_t buf = {0}; + mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ); + ble_int_comm_send(buf.buf, buf.len, INTERNAL_MESSAGE); + return MP_OBJ_NEW_SMALL_INT(buf.len); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_int_obj, + mod_trezorio_BLE_write_int); + +/// def write_ext(self, msg: bytes) -> int: +/// """ +/// Sends message over BLE +/// """ +STATIC mp_obj_t mod_trezorio_BLE_write_ext(mp_obj_t self, mp_obj_t msg) { + mp_buffer_info_t buf = {0}; + mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ); + ble_int_comm_send(buf.buf, buf.len, EXTERNAL_MESSAGE); + return MP_OBJ_NEW_SMALL_INT(buf.len); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_ext_obj, + mod_trezorio_BLE_write_ext); + +/// def erase_bonds() -> None: +/// """ +/// Erases all BLE bonds +/// """ +STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) { + bool result = send_erase_bonds(); + if (result) { + return mp_const_none; + } else { + mp_raise_msg(&mp_type_RuntimeError, "Erase bonds failed."); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_erase_bonds_obj, + mod_trezorio_BLE_erase_bonds); + +/// def disconnect() -> None: +/// """ +/// Disconnect BLE +/// """ +STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) { + bool result = send_disconnect(); + if (result) { + return mp_const_none; + } else { + mp_raise_msg(&mp_type_RuntimeError, "Disconnect failed."); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_disconnect_obj, + mod_trezorio_BLE_disconnect); + +STATIC const mp_rom_map_elem_t mod_trezorio_BLE_globals_table[] = { + {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ble)}, + {MP_ROM_QSTR(MP_QSTR_update_init), + MP_ROM_PTR(&mod_trezorio_BLE_update_init_obj)}, + {MP_ROM_QSTR(MP_QSTR_update_chunk), + MP_ROM_PTR(&mod_trezorio_BLE_update_chunk_obj)}, + {MP_ROM_QSTR(MP_QSTR_write_int), + MP_ROM_PTR(&mod_trezorio_BLE_write_int_obj)}, + {MP_ROM_QSTR(MP_QSTR_write_ext), + MP_ROM_PTR(&mod_trezorio_BLE_write_ext_obj)}, + {MP_ROM_QSTR(MP_QSTR_erase_bonds), + MP_ROM_PTR(&mod_trezorio_BLE_erase_bonds_obj)}, + {MP_ROM_QSTR(MP_QSTR_disconnect), + MP_ROM_PTR(&mod_trezorio_BLE_disconnect_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(mod_trezorio_BLE_globals, + mod_trezorio_BLE_globals_table); + +STATIC const mp_obj_module_t mod_trezorio_BLE_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&mod_trezorio_BLE_globals}; diff --git a/core/embed/extmod/modtrezorio/modtrezorio-poll.h b/core/embed/extmod/modtrezorio/modtrezorio-poll.h index 3d6347554d..c39a0055f1 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-poll.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-poll.h @@ -19,15 +19,22 @@ #include +#include "ble.h" +#include "ble/int_comm_defs.h" +#include "ble/state.h" #include "button.h" #include "common.h" #include "display.h" #include "embed/extmod/trezorobj.h" +#define BLE_EVENTS_IFACE (252) #define USB_DATA_IFACE (253) #define INPUT_IFACE (255) #define TOUCH_INPUT_FLAG (0x400000) #define BUTTON_INPUT_FLAG (0x800000) +#define USB_RW_IFACE_MAX (15) // 0-15 reserved for USB +#define BLE_IFACE_INT (16) +#define BLE_IFACE_EXT (17) #define POLL_READ (0x0000) #define POLL_WRITE (0x0100) @@ -146,35 +153,81 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, ret->items[1] = usb_connected ? mp_const_true : mp_const_false; return mp_const_true; } - } else if (mode == POLL_READ) { - if (sectrue == usb_hid_can_read(iface)) { - uint8_t buf[64] = {0}; - int len = usb_hid_read(iface, buf, sizeof(buf)); - if (len > 0) { - ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); - ret->items[1] = mp_obj_new_bytes(buf, len); - return mp_const_true; + } +#ifdef USE_BLE + else if (iface == BLE_EVENTS_IFACE) { + ble_event_poll(); + uint8_t connected = ble_connected(); + if (connected != ble_connected_previously) { + ble_connected_previously = connected; + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = connected ? mp_const_true : mp_const_false; + return mp_const_true; + } + } +#endif + else if (iface <= USB_RW_IFACE_MAX) { + if (mode == POLL_READ) { + if (sectrue == usb_hid_can_read(iface)) { + uint8_t buf[64] = {0}; + int len = usb_hid_read(iface, buf, sizeof(buf)); + if (len > 0) { + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = mp_obj_new_bytes(buf, len); + return mp_const_true; + } + } else if (sectrue == usb_webusb_can_read(iface)) { + uint8_t buf[64] = {0}; + int len = usb_webusb_read(iface, buf, sizeof(buf)); + if (len > 0) { + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = mp_obj_new_bytes(buf, len); + return mp_const_true; + } } - } else if (sectrue == usb_webusb_can_read(iface)) { - uint8_t buf[64] = {0}; - int len = usb_webusb_read(iface, buf, sizeof(buf)); - if (len > 0) { + } else if (mode == POLL_WRITE) { + if (sectrue == usb_hid_can_write(iface)) { ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); - ret->items[1] = mp_obj_new_bytes(buf, len); + ret->items[1] = mp_const_none; + return mp_const_true; + } else if (sectrue == usb_webusb_can_write(iface)) { + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = mp_const_none; return mp_const_true; } } - } else if (mode == POLL_WRITE) { - if (sectrue == usb_hid_can_write(iface)) { + } +#ifdef USE_BLE + else if (iface == BLE_IFACE_INT) { + if (mode == POLL_READ) { + uint8_t buf[64] = {0}; + int len = ble_int_comm_receive(buf, sizeof(buf)); + if (len > 0) { + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = mp_obj_new_bytes(buf, len); + return mp_const_true; + } + } else if (mode == POLL_WRITE) { ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); ret->items[1] = mp_const_none; return mp_const_true; - } else if (sectrue == usb_webusb_can_write(iface)) { + } + } else if (iface == BLE_IFACE_EXT) { + if (mode == POLL_READ) { + uint8_t buf[BLE_PACKET_SIZE] = {0}; + int len = ble_ext_comm_receive(buf, sizeof(buf)); + if (len > 0) { + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = mp_obj_new_bytes(buf, len); + return mp_const_true; + } + } else if (mode == POLL_WRITE) { ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); ret->items[1] = mp_const_none; return mp_const_true; } } +#endif } if (mp_hal_ticks_ms() >= deadline) { diff --git a/core/embed/extmod/modtrezorio/modtrezorio.c b/core/embed/extmod/modtrezorio/modtrezorio.c index ccc61a4b20..6a44b3cdf8 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio.c +++ b/core/embed/extmod/modtrezorio/modtrezorio.c @@ -34,6 +34,8 @@ // Whether USB data pins were connected on last check (USB configured) bool usb_connected_previously = true; +uint8_t ble_connected_previously = false; +bool ble_last_internal = false; #define CHECK_PARAM_RANGE(value, minimum, maximum) \ if (value < minimum || value > maximum) { \ @@ -48,6 +50,9 @@ bool usb_connected_previously = true; #include "modtrezorio-webusb.h" #include "modtrezorio-usb.h" // clang-format on +#ifdef USE_BLE +#include "modtrezorio-ble.h" +#endif #ifdef USE_SBU #include "modtrezorio-sbu.h" #endif @@ -57,7 +62,7 @@ bool usb_connected_previously = true; #endif /// package: trezorio.__init__ -/// from . import fatfs, sdcard +/// from . import fatfs, sdcard, ble /// POLL_READ: int # wait until interface is readable and return read data /// POLL_WRITE: int # wait until interface is writable @@ -74,8 +79,9 @@ bool usb_connected_previously = true; /// BUTTON_RIGHT: int # button number of right button /// USB_CHECK: int # interface id for check of USB data connection +/// BLE_CHECK: int # interface id for check of BLE data connection -/// WireInterface = Union[HID, WebUSB] +/// WireInterface = Union[HID, WebUSB, BleInterface] STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio)}, @@ -87,6 +93,9 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { #ifdef USE_SD_CARD {MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)}, {MP_ROM_QSTR(MP_QSTR_sdcard), MP_ROM_PTR(&mod_trezorio_sdcard_module)}, +#endif +#ifdef USE_BLE + {MP_ROM_QSTR(MP_QSTR_ble), MP_ROM_PTR(&mod_trezorio_BLE_module)}, #endif {MP_ROM_QSTR(MP_QSTR_INPUT), MP_ROM_INT(INPUT_IFACE)}, #ifdef USE_TOUCH @@ -118,6 +127,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_ROM_INT(POLL_WRITE)}, {MP_ROM_QSTR(MP_QSTR_USB_CHECK), MP_ROM_INT(USB_DATA_IFACE)}, + {MP_ROM_QSTR(MP_QSTR_BLE_CHECK), MP_ROM_INT(BLE_EVENTS_IFACE)}, }; STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals, diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 7bc4b52bc2..af816e46ed 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -295,6 +295,7 @@ STATIC mp_obj_str_t mod_trezorutils_full_name_obj = { /// """Minor version.""" /// VERSION_PATCH: int /// """Patch version.""" +/// USE_BLE: bool /// USE_SD_CARD: bool /// """Whether the hardware supports SD card.""" /// USE_BACKLIGHT: bool @@ -346,6 +347,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { #else {MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_false}, #endif +#ifdef USE_BLE + {MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_true}, +#else + {MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_false}, +#endif #ifdef USE_TOUCH {MP_ROM_QSTR(MP_QSTR_USE_TOUCH), mp_const_true}, #else diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index 304699f88c..98fb171521 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -76,6 +76,11 @@ #include "optiga_transport.h" #include "secret.h" #endif +#ifdef USE_BLE +#include "ble.h" +#include "ble/dfu.h" +#include "ble/state.h" +#endif #include "unit_variant.h" #ifdef SYSTEM_VIEW @@ -85,7 +90,9 @@ #include "rng.h" #include "supervise.h" #ifdef USE_SECP256K1_ZKP +#include "ble/messages.h" #include "zkp_context.h" + #endif // from util.s @@ -178,6 +185,14 @@ int main(void) { memzero(secret, sizeof(secret)); #endif +#ifdef USE_BLE + dfu_init(); + ble_comm_init(); + send_state_request(); + wait_for_answer(); + ble_start(); +#endif + #if !defined TREZOR_MODEL_1 drop_privileges(); #endif @@ -284,6 +299,11 @@ void SVC_C_Handler(uint32_t *stack) { clear_firmware_header = false; // break is omitted here because we want to continue to reboot below case SVC_REBOOT_TO_BOOTLOADER: +#ifdef USE_BLE + stop_advertising(); + ble_stop(); + // TODO: make sure that no answer is pending from NRF +#endif // if not going from copy image header & reboot, clean preventively this // part of CCMRAM if (clear_firmware_header) { diff --git a/core/embed/lib/ble/dfu.c b/core/embed/lib/ble/dfu.c new file mode 100644 index 0000000000..6719088b83 --- /dev/null +++ b/core/embed/lib/ble/dfu.c @@ -0,0 +1,135 @@ +/* + * 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 . + */ + +#include "common.h" +#include TREZOR_BOARD +#include "ble.h" +#include "common.h" +#include "dfu.h" +#include "fwu.h" + +static TFwu sFwu; + +static uint32_t tick_start = 0; + +void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len); +static uint8_t readData(uint8_t *data, int maxLen); + +void dfu_init(void) {} + +dfu_result_t dfu_update_process(void) { + while (1) { + // Can send 4 chars... + // (On a microcontroller, you'd use the TX Empty interrupt or test a + // register.) + + fwuCanSendData(&sFwu, 4); + + // Data available? Get up to 4 bytes... + // (On a microcontroller, you'd use the RX Available interrupt or test a + // register.) + uint8_t rxBuf[4]; + uint8_t rxLen = readData(rxBuf, 4); + if (rxLen > 0) { + fwuDidReceiveData(&sFwu, rxBuf, rxLen); + } + + // Give the firmware update module a timeslot to continue the process. + EFwuProcessStatus status = fwuYield(&sFwu, 0); + + if (status == FWU_STATUS_COMPLETION) { + ble_reset(); + return DFU_SUCCESS; + } + + if (status == FWU_STATUS_FAILURE) { + return DFU_FAIL; + } + + if (hal_ticks_ms() - tick_start > 2000) { + return DFU_FAIL; + } + + if (fwuIsReadyForChunk(&sFwu)) { + return DFU_NEXT_CHUNK; + } + } +} + +dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len) { + sFwu.commandObject = data; + sFwu.commandObjectLen = len; + sFwu.dataObject = NULL; + sFwu.dataObjectLen = binary_len; + sFwu.txFunction = txFunction; + sFwu.responseTimeoutMillisec = 2000; + + if (!ble_reset_to_bootloader()) { + return DFU_FAIL; + } + + tick_start = hal_ticks_ms(); + + // Prepare the firmware update process. + fwuInit(&sFwu); + + // Start the firmware update process. + fwuExec(&sFwu); + + return dfu_update_process(); +} + +dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len) { + tick_start = hal_ticks_ms(); + + fwuSendChunk(&sFwu, data, len); + + return dfu_update_process(); +} + +dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len, + uint8_t *binfile, uint32_t binfile_len) { + uint32_t chunk_offset = 0; + uint32_t rem_data = binfile_len; + + dfu_result_t res = dfu_update_init(datfile, datfile_len, binfile_len); + + while (res == DFU_NEXT_CHUNK) { + // Send the next chunk of the data object. + uint32_t chunk_size = 4096; + if (rem_data < 4096) { + chunk_size = rem_data; + rem_data = 0; + } else { + rem_data -= 4096; + } + res = dfu_update_chunk(&binfile[chunk_offset], chunk_size); + chunk_offset += chunk_size; + } + + return res; +} + +void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len) { + ble_comm_send(buf, len); +} + +static uint8_t readData(uint8_t *data, int maxLen) { + return ble_comm_receive(data, maxLen); +} diff --git a/core/embed/lib/ble/dfu.h b/core/embed/lib/ble/dfu.h new file mode 100644 index 0000000000..fcd6eeb36b --- /dev/null +++ b/core/embed/lib/ble/dfu.h @@ -0,0 +1,17 @@ + +#ifndef __DFU_H__ +#define __DFU_H__ + +typedef enum { + DFU_NEXT_CHUNK, + DFU_SUCCESS, + DFU_FAIL, +} dfu_result_t; + +void dfu_init(void); +dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len); +dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len); +dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len, + uint8_t *binfile, uint32_t binfile_len); + +#endif diff --git a/core/embed/lib/ble/fwu.c b/core/embed/lib/ble/fwu.c new file mode 100644 index 0000000000..4937aed4e9 --- /dev/null +++ b/core/embed/lib/ble/fwu.c @@ -0,0 +1,664 @@ +// +// fwu.c +// nrf52-dfu +// +// C library for the Nordic firmware update protocol. +// +// Created by Andreas Schweizer on 30.11.2018. +// Copyright © 2018-2019 Classy Code GmbH +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#include "fwu.h" +#include + +// TODO too big, split in separate files! + +typedef enum { + FWU_PS_IDLE = 0, + FWU_PS_PING = 10, + FWU_PS_RCPT_NOTIF = 20, + FWU_PS_MTU = 30, + FWU_PS_OBJ1_SELECT = 40, + FWU_PS_OBJ1_CREATE = 50, + FWU_PS_OBJ1_WRITE = 60, + FWU_PS_OBJ1_CRC_GET = 70, + FWU_PS_OBJ1_EXECUTE = 80, + FWU_PS_OBJ2_SELECT = 90, + FWU_PS_OBJ2_WAIT_FOR_CHUNK = 91, + FWU_PS_OBJ2_CREATE = 100, + FWU_PS_OBJ2_WRITE = 110, + FWU_PS_OBJ2_CRC_GET = 120, + FWU_PS_OBJ2_EXECUTE = 130, + FWU_PS_FAIL = 254, + FWU_PS_DONE = 255, +} EFwuProcessState; + +// Process requests, triggering process state transitions. +typedef enum { + FWU_PR_NONE = 0, + FWU_PR_START = 1, + FWU_PR_RECEIVED_RESPONSE, + FWU_PR_REQUEST_FAILED, + FWU_PR_REQUEST_SENT, +} EFwuProcessRequest; + +typedef enum { + FWU_CS_IDLE = 0, + FWU_CS_SEND = 1, // sending data from the private request buffer + FWU_CS_RECEIVE = 2, // receiving data into the private response buffer + FWU_CS_FAIL = 3, + FWU_CS_DONE = 4, +} EFwuCommandState; + +// Command requests, triggering command state transitions. +typedef enum { + FWU_CR_NONE = 0, + FWU_CR_SEND = 1, + FWU_CR_SENDONLY = 2, + FWU_CR_EOM_RECEIVED = 3, + FWU_CR_RX_OVERFLOW = 4, + FWU_CR_INVALID_ESCAPE_SEQ, +} EFwuCommandRequest; + +#define FWU_EOM 0xC0 +#define FWU_RESPONSE_START 0x60 +#define FWU_RESPONSE_SUCCESS 0x01 + +// PING 09 01 C0 -> 60 09 01 01 C0 +static uint8_t sPingRequest[] = {0x09, 0x01}; +static uint8_t sPingRequestLen = 2; + +// SET RECEIPT 02 00 00 C0 -> 60 02 01 C0 +static uint8_t sSetReceiptRequest[] = {0x02, 0x00, 0x00}; +static uint8_t sSetReceiptRequestLen = 3; + +// Get the preferred MTU size on the request. +// GET MTU 07 -> 60 07 01 83 00 C0 +static uint8_t sGetMtuRequest[] = {0x07}; +static uint8_t sGetMtuRequestLen = 1; + +// Triggers the last transferred object of the specified type to be selected +// and queries information (max size, cur offset, cur CRC) about the object. +// If there's no object of the specified type, the object type is still +// selected, CRC and offset are 0 in this case. +// SELECT OBJECT 06 01 C0 -> 60 06 01 00 01 00 00 00 00 00 00 00 00 00 00 C0 +static uint8_t sSelectObjectRequest[] = {0x06, 0x01}; +static uint8_t sSelectObjectRequestLen = 2; + +// Creating a command or data object; the target reserves the space, resets the +// progress since the last Execute command and selects the new object.) +// CREATE OBJECT 01 01 87 00 00 00 C0 -> 60 01 01 C0 +static uint8_t sCreateObjectRequest[] = {0x01, 0x01, 0x87, 0x00, 0x00, 0x00}; +static uint8_t sCreateObjectRequestLen = 6; + +// CRC GET 03 C0 -> 60 03 01 87 00 00 00 38 f4 97 72 C0 +static uint8_t sGetCrcRequest[] = {0x03}; +static uint8_t sGetCrcRequestLen = 1; + +// Execute an object after it has been fully transmitted. +// EXECUTE OBJECT 04 C0 -> 60 04 01 C0 +static uint8_t sExecuteObjectRequest[] = {0x04}; +static uint8_t sExecuteObjectRequestLen = 1; + +static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec); +static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec); + +static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu); + +// Don't send more than FWU_REQUEST_BUF_SIZE bytes. +// Don't include the EOM. +static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len); + +static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode); + +// static void fwuDebugPrintStatus(TFwu *fwu, char *msg); + +static void updateCrc(TFwu *fwu, uint8_t b); +static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason); +static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes); +static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes); +static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes); + +// First function to call to set up the internal state in the FWU structure. +void fwuInit(TFwu *fwu) { + fwu->privateProcessState = FWU_PS_IDLE; + fwu->privateProcessRequest = FWU_PR_NONE; + fwu->privateCommandState = FWU_CS_IDLE; + + fwu->processStatus = FWU_STATUS_UNDEFINED; + fwu->responseStatus = FWU_RSP_OK; +} + +// Execute the firmware update. +void fwuExec(TFwu *fwu) { + // Start with sending a PING command to the target to see if it's there... + fwu->privateProcessRequest = FWU_PR_START; +} + +// Call regularly to allow asynchronous processing to continue. +EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec) { + // Nothing to do if processing has failed or successfully completed... + if (fwu->processStatus == FWU_STATUS_FAILURE || + fwu->privateProcessState == FWU_PS_FAIL) { + return FWU_STATUS_FAILURE; + } else if (fwu->processStatus == FWU_STATUS_COMPLETION || + fwu->privateProcessState == FWU_PS_DONE) { + return FWU_STATUS_COMPLETION; + } + + // Processing is ongoing, yield to FSMs. + fwuYieldCommandFsm(fwu, elapsedMillisec); + fwuYieldProcessFsm(fwu, elapsedMillisec); + + return fwu->processStatus; +} + +// Call after data from the target has been received. +void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len) { + while (len > 0) { + if (fwu->privateResponseLen == FWU_RESPONSE_BUF_SIZE) { + fwu->privateCommandRequest = FWU_CR_RX_OVERFLOW; + return; + } + uint8_t c = *bytes++; + if (c == FWU_EOM) { + fwu->privateCommandRequest = FWU_CR_EOM_RECEIVED; + } + + if (c == 0xDB) { + fwu->privateResponseEscapeCharacter = 1; + } else { + if (fwu->privateResponseEscapeCharacter) { + fwu->privateResponseEscapeCharacter = 0; + if (c == 0xDC) { + c = 0xC0; + } else if (c == 0xDD) { + c = 0xDB; + } else { + fwu->privateCommandRequest = FWU_CR_INVALID_ESCAPE_SEQ; + return; + } + } + fwu->privateResponseBuf[fwu->privateResponseLen++] = c; + } + len--; + } +} + +// Inform the FWU module that it may send maxLen bytes of data to the target. +void fwuCanSendData(TFwu *fwu, uint8_t maxLen) { + fwu->privateSendBufSpace = maxLen; +} + +void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len) { + if (fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK && + fwu->privateDataObjectSize == len) { + fwu->dataObject = buf; + fwu->privateProcessState = FWU_PS_OBJ2_CREATE; + } +} + +bool fwuIsReadyForChunk(TFwu *fwu) { + return fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK; +} + +static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec) { + uint8_t tmpPrivateProcessRequest = fwu->privateProcessRequest; + fwu->privateProcessRequest = FWU_PR_NONE; + + // No processing in final states + if (fwu->privateProcessState == FWU_PS_DONE || + fwu->privateProcessState == FWU_PS_FAIL) { + return; + } + + // Failure handling + if (tmpPrivateProcessRequest == FWU_PR_REQUEST_FAILED) { + fwu->privateProcessState = FWU_PS_FAIL; + fwu->processStatus = FWU_STATUS_FAILURE; + return; + } + + // Executing the firmware update process. + switch (fwu->privateProcessState) { + case FWU_PS_IDLE: + if (tmpPrivateProcessRequest == FWU_PR_START) { + // Send a PING and switch to the PING state to wait for the response. + fwuPrepareSendBuffer(fwu, sPingRequest, sPingRequestLen); + fwu->privateProcessState = FWU_PS_PING; + } + break; + + // PING: Check if the nRF52 DFU code is listening + case FWU_PS_PING: + // Wait for the PING response, then verify it. + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + // ID match? + if (fwu->privateRequestBuf[1] == fwu->privateResponseBuf[3]) { + // Send a SET_RECEIPT and switch to the corresponding state to wait + // for the response. + fwuPrepareSendBuffer(fwu, sSetReceiptRequest, sSetReceiptRequestLen); + fwu->privateProcessState = FWU_PS_RCPT_NOTIF; + } else { + fwuSignalFailure(fwu, FWU_RSP_PING_ID_MISMATCH); + } + } + break; + + // RCPT_NOTIF: Define Receipt settings + case FWU_PS_RCPT_NOTIF: + // Wait for the SET_RECEIPT response. + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + // Send a SET_RECEIPT and switch to the corresponding state to wait for + // the response. + fwuPrepareSendBuffer(fwu, sGetMtuRequest, sGetMtuRequestLen); + fwu->privateProcessState = FWU_PS_MTU; + } + break; + + // FWU_PS_MTU: Get maximum transmission unit size + case FWU_PS_MTU: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + fwu->privateMtuSize = + fwuLittleEndianToHost16(&fwu->privateResponseBuf[3]); + // Send a SET_RECEIPT and switch to the corresponding state to wait for + // the response. + sSelectObjectRequest[1] = 0x01; // select object 1 (command object) + fwuPrepareSendBuffer(fwu, sSelectObjectRequest, + sSelectObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ1_SELECT; + } + break; + + // FWU_PS_OBJ1_SELECT: Select the INIT command object + case FWU_PS_OBJ1_SELECT: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + uint32_t maxSize = fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]); + if (maxSize < fwu->commandObjectLen) { + fwuSignalFailure(fwu, FWU_RSP_INIT_COMMAND_TOO_LARGE); + } else { + sCreateObjectRequest[1] = 0x01; // create type 1 object (COMMAND) + fwuHostToLittleEndian32(fwu->commandObjectLen, + &sCreateObjectRequest[2]); + fwuPrepareSendBuffer(fwu, sCreateObjectRequest, + sCreateObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ1_CREATE; + } + } + break; + + // FWU_PS_OBJ1_CREATE: Create the INIT command object + case FWU_PS_OBJ1_CREATE: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + fwu->privateProcessState = FWU_PS_OBJ1_WRITE; + fwu->privateObjectBuf = fwu->commandObject; + fwu->privateObjectLen = fwu->commandObjectLen; + fwu->privateObjectIx = 0; + fwu->privateObjectCrc = 0xffffffff; + fwuPrepareLargeObjectSendBuffer(fwu, 0x08); + } + break; + + // FWU_PS_OBJ1_WRITE: Write the INIT command object + case FWU_PS_OBJ1_WRITE: + if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) { + // more to send? + if (fwu->privateObjectIx == fwu->privateObjectLen) { + // no - request the CRC of the written data... + fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen); + fwu->privateProcessState = FWU_PS_OBJ1_CRC_GET; + } else { + fwuPrepareLargeObjectSendBuffer(fwu, 0x08); + } + } + break; + + // FWU_PS_OBJ1_CRC_GET: Checksum verification + case FWU_PS_OBJ1_CRC_GET: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + // uint32_t actualLen = + // fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]); + uint32_t actualCks = + fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]); + if (actualCks == ~fwu->privateObjectCrc) { + // Checksum is OK; execute the command! + fwuPrepareSendBuffer(fwu, sExecuteObjectRequest, + sExecuteObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ1_EXECUTE; + } else { + fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR); + } + } + break; + + case FWU_PS_OBJ1_EXECUTE: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + sSelectObjectRequest[1] = 0x02; // select object 2 (DATA object) + fwu->privateDataObjectOffset = 0; // from the beginning + fwuPrepareSendBuffer(fwu, sSelectObjectRequest, + sSelectObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ2_SELECT; + } + break; + + // FWU_PS_OBJ2_SELECT: Select the DATA object + case FWU_PS_OBJ2_SELECT: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + fwu->privateDataObjectMaxSize = + fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]); + fwu->privateObjectCrc = + 0xffffffff; // do it here because it's global for the entire blob + // We'll create and execute multiple data objects, so it's ok if the + // actual size is greater than max size. + fwu->privateDataObjectSize = + (fwu->dataObjectLen - + fwu->privateDataObjectOffset); // nof bytes remaining + if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) { + fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize; + } + sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND) + fwuHostToLittleEndian32(fwu->privateDataObjectSize, + &sCreateObjectRequest[2]); + fwuPrepareSendBuffer(fwu, sCreateObjectRequest, + sCreateObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK; + } + break; + case FWU_PS_OBJ2_WAIT_FOR_CHUNK: + break; + // FWU_PS_OBJ2_CREATE: Create the DATA object + case FWU_PS_OBJ2_CREATE: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + fwu->privateProcessState = FWU_PS_OBJ2_WRITE; + fwu->privateObjectBuf = fwu->dataObject; + fwu->privateObjectLen = fwu->privateDataObjectSize; + fwu->privateObjectIx = 0; + fwuPrepareLargeObjectSendBuffer(fwu, 0x08); + } + break; + + // FWU_PS_OBJ2_WRITE: Write the DATA object + case FWU_PS_OBJ2_WRITE: + if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) { + // more to send? + if (fwu->privateObjectIx == fwu->privateObjectLen) { + // no - request the CRC of the written data... + fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen); + fwu->privateProcessState = FWU_PS_OBJ2_CRC_GET; + } else { + fwuPrepareLargeObjectSendBuffer(fwu, 0x08); + } + } + break; + + // FWU_PS_OBJ2_CRC_GET: Checksum verification + case FWU_PS_OBJ2_CRC_GET: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + // uint32_t actualLen = + // fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]); + uint32_t actualCks = + fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]); + if (actualCks == ~fwu->privateObjectCrc) { + // Checksum is OK; execute the command! + fwuPrepareSendBuffer(fwu, sExecuteObjectRequest, + sExecuteObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ2_EXECUTE; + + } else { + fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR); + } + } + break; + + case FWU_PS_OBJ2_EXECUTE: + if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) { + fwu->privateDataObjectOffset += fwu->privateDataObjectSize; + if (fwu->privateDataObjectOffset == fwu->dataObjectLen) { + fwu->privateProcessState = FWU_PS_DONE; + fwu->processStatus = FWU_STATUS_COMPLETION; + + } else { + // We'll create and execute multiple data objects, so it's ok if the + // actual size is greater than max size. + fwu->privateDataObjectSize = + (fwu->dataObjectLen - + fwu->privateDataObjectOffset); // nof bytes remaining + if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) { + fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize; + } + sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND) + fwuHostToLittleEndian32(fwu->privateDataObjectSize, + &sCreateObjectRequest[2]); + fwuPrepareSendBuffer(fwu, sCreateObjectRequest, + sCreateObjectRequestLen); + fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK; + } + } + break; + + default: + fwu->privateProcessState = FWU_PS_FAIL; + break; + } +} + +static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec) { + uint8_t toSend; + + // Automatically return from final states to IDLE. + if (fwu->privateCommandState == FWU_CS_DONE || + fwu->privateCommandState == FWU_CS_FAIL) { + fwu->privateCommandState = FWU_CS_IDLE; + } + + // Timeout? + if (fwu->privateCommandState != FWU_CS_IDLE) { + if (fwu->privateCommandTimeoutRemainingMillisec < elapsedMillisec) { + fwu->privateCommandTimeoutRemainingMillisec = 0; + } else { + fwu->privateCommandTimeoutRemainingMillisec -= elapsedMillisec; + } + if (fwu->privateCommandTimeoutRemainingMillisec == 0) { + fwuSignalFailure(fwu, FWU_RSP_TIMEOUT); + return; + } + } + + // Catch errors + if (fwu->privateCommandRequest == FWU_CR_RX_OVERFLOW) { + fwuSignalFailure(fwu, FWU_RSP_RX_OVERFLOW); + return; + } + if (fwu->privateCommandRequest == FWU_CR_INVALID_ESCAPE_SEQ) { + fwuSignalFailure(fwu, FWU_RSP_RX_INVALID_ESCAPE_SEQ); + return; + } + + switch (fwu->privateCommandState) { + case FWU_CS_IDLE: + // Ready and waiting for a transmission request. + if (fwu->privateCommandRequest == FWU_CR_SEND || + fwu->privateCommandRequest == FWU_CR_SENDONLY) { + fwu->privateCommandSendOnly = + fwu->privateCommandRequest == FWU_CR_SENDONLY ? 1 : 0; + fwu->privateCommandRequest = FWU_CR_NONE; + fwu->privateCommandState = FWU_CS_SEND; + fwu->privateCommandTimeoutRemainingMillisec = + fwu->responseTimeoutMillisec; + } + break; + case FWU_CS_SEND: + // Continue sending data until the entire request has been sent. + toSend = fwu->privateRequestLen - fwu->privateRequestIx; + if (toSend == 0) { + if (fwu->privateCommandSendOnly) { + // This was a fire-and-forget request; we don't expect a response. + fwu->privateProcessRequest = FWU_PR_REQUEST_SENT; + fwu->privateCommandState = FWU_CS_DONE; + } else { + // The request has been sent; wait for response. + fwu->privateCommandState = FWU_CS_RECEIVE; + } + } else if (fwu->privateSendBufSpace > 0) { + uint8_t n = fwu->privateSendBufSpace; + if (n > toSend) { + n = toSend; + } + fwu->txFunction(fwu, &fwu->privateRequestBuf[fwu->privateRequestIx], n); + fwu->privateRequestIx += n; + } + break; + case FWU_CS_RECEIVE: + // Continue receiving data until the end-of-message marker has been + // received. + if (fwu->privateCommandRequest == FWU_CR_EOM_RECEIVED) { + fwu->privateCommandRequest = FWU_CR_NONE; + EFwuResponseStatus responseStatus = fwuTestReceivedPacketValid(fwu); + if (responseStatus == FWU_RSP_OK) { + // Inform the process state machine that command reception has + // completed. + fwu->privateProcessRequest = FWU_PR_RECEIVED_RESPONSE; + fwu->privateCommandState = FWU_CS_DONE; + } else { + fwu->responseStatus = responseStatus; + fwu->privateCommandState = FWU_CS_FAIL; + } + } + break; + default: + fwu->privateCommandState = FWU_CS_FAIL; + break; + } +} + +static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu) { + // 60 C0 + if (fwu->privateResponseLen < 4) { + return FWU_RSP_TOO_SHORT; + } + if (fwu->privateResponseBuf[0] != FWU_RESPONSE_START) { + return FWU_RSP_START_MARKER_MISSING; + } + if (fwu->privateResponseBuf[1] != fwu->privateRequestBuf[0]) { + return FWU_RSP_REQUEST_REFERENCE_INVALID; + } + if (fwu->privateResponseBuf[2] != FWU_RESPONSE_SUCCESS) { + return FWU_RSP_ERROR_RESPONSE; + } + if (fwu->privateResponseBuf[fwu->privateResponseLen - 1] != FWU_EOM) { + return FWU_RSP_END_MARKER_MISSING; + } + return FWU_RSP_OK; +} + +static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode) { + uint16_t bytesTodo = fwu->privateObjectLen - fwu->privateObjectIx; + uint16_t bufSpace = FWU_REQUEST_BUF_SIZE - 2; + + uint16_t i; + uint8_t *p = &fwu->privateRequestBuf[0]; + + *p++ = requestCode; + fwu->privateRequestLen = 2; // including requestCode and FWU_EOM + fwu->privateRequestIx = 0; + + if (bytesTodo > 32) { + bytesTodo = 32; + } + + for (i = 0; i < bytesTodo && bufSpace >= 2; i++) { + uint8_t b = fwu->privateObjectBuf[fwu->privateObjectIx]; + // SLIP escape characters: C0->DBDC, DB->DBDD + if (b == 0xC0 || b == 0xDB) { + *p++ = 0xDB; + *p++ = (b == 0xC0) ? 0xDC : 0xDD; + fwu->privateRequestLen += 2; + bufSpace -= 2; + } else { + *p++ = b; + fwu->privateRequestLen++; + bufSpace--; + } + updateCrc(fwu, b); + fwu->privateObjectIx++; + } + + *p = FWU_EOM; + fwu->privateCommandRequest = FWU_CR_SENDONLY; +} + +static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len) { + // TODO assert privateCommandState == FWU_CS_IDLE | _DONE | _FAIL + // TODO assert len <= FWU_REQUEST_BUF_SIZE + + uint8_t i; + uint8_t *p = &fwu->privateRequestBuf[0]; + + fwu->privateRequestIx = 0; + fwu->privateRequestLen = len + 1; + fwu->privateResponseLen = 0; + + // Copy the data into our internal buffer. + for (i = 0; i < len; i++) { + *p++ = *data++; + } + + // Add the end-of-message marker. + *p = FWU_EOM; + + // Ready to send! + fwu->privateCommandRequest = FWU_CR_SEND; +} + +static void updateCrc(TFwu *fwu, uint8_t b) { + uint8_t i; + uint32_t crc = fwu->privateObjectCrc; + crc ^= b; + for (i = 0; i < 8; i++) { + uint32_t m = (crc & 1) ? 0xffffffff : 0; + crc = (crc >> 1) ^ (0xedb88320u & m); + } + fwu->privateObjectCrc = crc; +} + +static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason) { + fwu->responseStatus = reason; + fwu->privateCommandState = FWU_CS_FAIL; + // Signal failure to process state machine + fwu->privateProcessRequest = FWU_PR_REQUEST_FAILED; +} + +static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes) { + return bytes[0] | ((uint16_t)bytes[1] << 8); +} + +static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes) { + return bytes[0] | ((uint16_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes) { + uint8_t i; + for (i = 0; i < 4; i++) { + *bytes++ = v & 0xff; + v = v >> 8; + } +} diff --git a/core/embed/lib/ble/fwu.h b/core/embed/lib/ble/fwu.h new file mode 100644 index 0000000000..a380b40c1e --- /dev/null +++ b/core/embed/lib/ble/fwu.h @@ -0,0 +1,128 @@ +// +// fwu.h +// nrf52-dfu +// +// C library for the Nordic firmware update protocol. +// +// Created by Andreas Schweizer on 30.11.2018. +// Copyright © 2018-2019 Classy Code GmbH +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef __FWU_H__ +#define __FWU_H__ 1 + +#include +#include + +struct SFwu; + +#define FWU_REQUEST_BUF_SIZE 67 +#define FWU_RESPONSE_BUF_SIZE 16 + +typedef enum { + FWU_STATUS_UNDEFINED = 0, + FWU_STATUS_FAILURE = 1, + FWU_STATUS_COMPLETION = 2, +} EFwuProcessStatus; + +typedef enum { + FWU_RSP_OK = 0, + FWU_RSP_TOO_SHORT = 1, + FWU_RSP_START_MARKER_MISSING = 2, + FWU_RSP_END_MARKER_MISSING = 3, + FWU_RSP_REQUEST_REFERENCE_INVALID = 4, + FWU_RSP_ERROR_RESPONSE = 5, + FWU_RSP_TIMEOUT = 6, + FWU_RSP_PING_ID_MISMATCH = 7, + FWU_RSP_RX_OVERFLOW = 8, + FWU_RSP_INIT_COMMAND_TOO_LARGE = 9, + FWU_RSP_CHECKSUM_ERROR = 10, + FWU_RSP_DATA_OBJECT_TOO_LARGE = 11, + FWU_RSP_RX_INVALID_ESCAPE_SEQ = 12, +} EFwuResponseStatus; + +typedef void (*FTxFunction)(struct SFwu *fwu, uint8_t *buf, uint8_t len); + +typedef struct SFwu { + // --- public - define these before calling fwuInit --- + // .dat + uint8_t *commandObject; + uint32_t commandObjectLen; + // .bin + uint8_t *dataObject; + uint32_t dataObjectLen; + // Sending bytes to the target + FTxFunction txFunction; + // Timeout when waiting for a response from the target + uint32_t responseTimeoutMillisec; + // --- public - result codes + // Overall process status code + EFwuProcessStatus processStatus; + // Response status code + EFwuResponseStatus responseStatus; + // --- private, don't modify --- + uint32_t privateDataObjectOffset; + uint32_t privateDataObjectSize; + uint32_t privateDataObjectMaxSize; + uint8_t privateProcessState; + uint8_t privateCommandState; + uint8_t privateCommandSendOnly; + uint32_t privateCommandTimeoutRemainingMillisec; + uint8_t privateRequestBuf[FWU_REQUEST_BUF_SIZE + 1]; + uint8_t privateRequestLen; + uint8_t privateRequestIx; + uint8_t privateResponseBuf[FWU_RESPONSE_BUF_SIZE]; + uint8_t privateResponseEscapeCharacter; + uint8_t privateResponseLen; + uint32_t privateResponseTimeElapsedMillisec; + uint8_t privateSendBufSpace; + uint8_t privateProcessRequest; + uint8_t privateCommandRequest; + uint16_t privateMtuSize; + // sending a large object buffer + uint8_t *privateObjectBuf; + uint32_t privateObjectLen; + uint32_t privateObjectIx; + uint32_t privateObjectCrc; +} TFwu; + +// First function to call to set up the internal state in the FWU structure. +void fwuInit(TFwu *fwu); + +// Execute the firmware update. +void fwuExec(TFwu *fwu); + +// Call regularly to allow asynchronous processing to continue. +EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec); + +// Call after data from the target has been received. +void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len); + +// Inform the FWU module that it may send maxLen bytes of data to the target. +void fwuCanSendData(TFwu *fwu, uint8_t maxLen); + +// Call to send a chunk of the data object to the target. +void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len); + +// Call to check if a chunk of the data object can be sent to the target. +bool fwuIsReadyForChunk(TFwu *fwu); + +#endif // __FWU_H__ diff --git a/core/embed/lib/ble/int_comm_defs.h b/core/embed/lib/ble/int_comm_defs.h new file mode 100644 index 0000000000..d48f7c3212 --- /dev/null +++ b/core/embed/lib/ble/int_comm_defs.h @@ -0,0 +1,49 @@ + +#ifndef __INT_COMM_DEFS__ +#define __INT_COMM_DEFS__ + +#define BLE_PACKET_SIZE (244) +#define USB_DATA_SIZE (64) + +#define COMM_HEADER_SIZE (3) +#define COMM_FOOTER_SIZE (1) +#define OVERHEAD_SIZE (COMM_HEADER_SIZE + COMM_FOOTER_SIZE) +#define UART_PACKET_SIZE (USB_DATA_SIZE + OVERHEAD_SIZE) + +#define EOM (0x55) +#define INTERNAL_EVENT (0xA2) +#define EXTERNAL_MESSAGE (0xA1) +#define INTERNAL_MESSAGE (0xA0) + +typedef struct { + uint8_t msg_id; + uint8_t connected; + uint8_t advertising; + uint8_t advertising_whitelist; + + uint8_t peer_count; + uint8_t reserved[2]; + uint8_t sd_version_number; + + uint16_t sd_company_id; + uint16_t sd_subversion_number; + + uint32_t app_version; + uint32_t bld_version; + +} event_status_msg_t; + +typedef enum { + INTERNAL_EVENT_STATUS = 0x01, + INTERNAL_EVENT_SUCCESS = 0x02, + INTERNAL_EVENT_FAILURE = 0x03, +} InternalEvent_t; + +typedef enum { + INTERNAL_CMD_SEND_STATE = 0x00, + INTERNAL_CMD_ADVERTISING_ON = 0x01, + INTERNAL_CMD_ADVERTISING_OFF = 0x02, + INTERNAL_CMD_ERASE_BONDS = 0x03, + INTERNAL_CMD_DISCONNECT = 0x04, +} InternalCmd_t; +#endif diff --git a/core/embed/lib/ble/messages.c b/core/embed/lib/ble/messages.c new file mode 100644 index 0000000000..b084892aac --- /dev/null +++ b/core/embed/lib/ble/messages.c @@ -0,0 +1,124 @@ + +#include +#include + +#include "ble.h" +#include "common.h" +#include "int_comm_defs.h" +#include "messages.h" +#include "state.h" + +void process_poll(uint8_t *data, uint32_t len) { + uint8_t cmd = data[0]; + + switch (cmd) { + // case INTERNAL_EVENT_INITIALIZED: { + // set_connected(false); + // set_initialized(true); + // break; + // } + case INTERNAL_EVENT_STATUS: { + event_status_msg_t *msg = (event_status_msg_t *)data; + set_status(msg); + set_initialized(true); + break; + } + default: + break; + } +} + +bool wait_for_answer(void) { + uint8_t buf[64] = {0}; + + uint32_t ticks_start = hal_ticks_ms(); + int len = 0; + + while (len == 0) { + if (hal_ticks_ms() - ticks_start > 1000) { + // timeout + return false; + } + + len = ble_int_event_receive(buf, sizeof(buf)); + + if (len > 0) { + process_poll(buf, len); + } + } + + return true; +} + +void send_state_request(void) { + uint8_t cmd = INTERNAL_CMD_SEND_STATE; + ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT); +} + +void send_advertising_on(bool whitelist) { + uint8_t data[2]; + data[0] = INTERNAL_CMD_ADVERTISING_ON; + data[1] = whitelist ? 1 : 0; + ble_int_comm_send(data, sizeof(data), INTERNAL_EVENT); +} + +void send_advertising_off(void) { + uint8_t cmd = INTERNAL_CMD_ADVERTISING_OFF; + ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT); +} + +bool send_erase_bonds(void) { + if (!ble_firmware_running()) { + return false; + } + uint8_t cmd = INTERNAL_CMD_ERASE_BONDS; + ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT); + + uint8_t buf[64] = {0}; + + uint32_t ticks_start = hal_ticks_ms(); + int len = 0; + + while (len == 0) { + len = ble_int_event_receive(buf, sizeof(buf)); + + if (hal_ticks_ms() - ticks_start > 1000) { + // timeout + return false; + } + } + + if (buf[0] == INTERNAL_EVENT_SUCCESS) { + return true; + } + + return false; +} + +bool send_disconnect(void) { + if (!ble_firmware_running()) { + return false; + } + uint8_t cmd = INTERNAL_CMD_DISCONNECT; + ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT); + + uint8_t buf[64] = {0}; + + uint32_t ticks_start = hal_ticks_ms(); + int len = 0; + + while (len == 0) { + len = ble_int_event_receive(buf, sizeof(buf)); + + if (hal_ticks_ms() - ticks_start > 1000) { + // timeout + return false; + } + } + + if (buf[0] == INTERNAL_EVENT_SUCCESS) { + return true; + } + + return false; +} diff --git a/core/embed/lib/ble/messages.h b/core/embed/lib/ble/messages.h new file mode 100644 index 0000000000..d7e7e2a52e --- /dev/null +++ b/core/embed/lib/ble/messages.h @@ -0,0 +1,20 @@ +#ifndef __BLE_MESSAGES__ +#define __BLE_MESSAGES__ + +#include + +bool wait_for_answer(void); + +void process_poll(uint8_t *data, uint32_t len); + +void send_state_request(void); + +void send_advertising_on(bool whitelist); + +void send_advertising_off(void); + +bool send_erase_bonds(void); + +bool send_disconnect(void); + +#endif diff --git a/core/embed/lib/ble/state.c b/core/embed/lib/ble/state.c new file mode 100644 index 0000000000..10c0aac756 --- /dev/null +++ b/core/embed/lib/ble/state.c @@ -0,0 +1,73 @@ + + +#include "ble/state.h" +#include "ble.h" +#include "messages.h" + +static bool ble_state_connected = false; +static bool ble_state_initialized = false; +static bool ble_advertising_wanted = false; +static bool ble_advertising_wl_wanted = false; +static bool ble_advertising = false; +static bool ble_advertising_wl = false; +static bool ble_dfu_mode = false; +static uint8_t ble_peer_count = 0; + +bool ble_connected(void) { + return ble_state_connected && ble_firmware_running(); +} + +void set_status(event_status_msg_t *msg) { + if (ble_state_connected != msg->connected) { + ble_advertising_wanted = msg->peer_count > 0; + ble_advertising_wl_wanted = true; + } + ble_state_connected = msg->connected; + + ble_peer_count = msg->peer_count; + if (msg->peer_count > 0 && !ble_initialized()) { + ble_advertising_wanted = true; + ble_advertising_wl_wanted = true; + } + + if (ble_advertising_wanted != msg->advertising || + (ble_advertising_wl_wanted != msg->advertising_whitelist)) { + if (ble_advertising_wanted && !ble_state_connected) { + send_advertising_on(ble_advertising_wl_wanted); + } + if (!ble_advertising_wanted && ble_advertising) { + send_advertising_off(); + } + } + ble_advertising = msg->advertising; + ble_advertising_wl = msg->advertising_whitelist; +} + +void set_initialized(bool initialized) { ble_state_initialized = initialized; } + +bool ble_initialized(void) { + return ble_state_initialized && ble_firmware_running(); +} + +void start_advertising(bool whitelist) { + ble_advertising_wl_wanted = whitelist; + ble_advertising_wanted = true; + if (!ble_advertising || ble_advertising_wl != whitelist) { + send_advertising_on(whitelist); + } else { + send_state_request(); + } +} + +void stop_advertising(void) { + ble_advertising_wanted = false; + if (ble_advertising) { + send_advertising_off(); + } else { + send_state_request(); + } +} + +void ble_set_dfu_mode(bool dfu) { ble_dfu_mode = dfu; } + +bool is_ble_dfu_mode(void) { return ble_dfu_mode; } diff --git a/core/embed/lib/ble/state.h b/core/embed/lib/ble/state.h new file mode 100644 index 0000000000..c908284e74 --- /dev/null +++ b/core/embed/lib/ble/state.h @@ -0,0 +1,26 @@ + +#ifndef __BLE_STATE__ +#define __BLE_STATE__ + +#include +#include + +#include "int_comm_defs.h" + +bool ble_initialized(void); + +void set_initialized(bool initialized); + +bool ble_connected(void); + +void set_status(event_status_msg_t *msg); + +void start_advertising(bool whitelist); + +void stop_advertising(void); + +void ble_set_dfu_mode(bool dfu); + +bool is_ble_dfu_mode(void); + +#endif diff --git a/core/embed/lib/protob_helpers.c b/core/embed/lib/protob_helpers.c new file mode 100644 index 0000000000..c4734bbf92 --- /dev/null +++ b/core/embed/lib/protob_helpers.c @@ -0,0 +1,87 @@ + + +#include "protob_helpers.h" + +secbool send_protob_msg(uint8_t iface_num, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg, + bool (*write)(pb_ostream_t *stream, + const pb_byte_t *buf, size_t count), + void (*write_flush)(write_state *state)) { + // determine message size by serializing it into a dummy stream + pb_ostream_t sizestream = {.callback = NULL, + .state = NULL, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + if (false == pb_encode(&sizestream, fields, msg)) { + return secfalse; + } + const uint32_t msg_size = sizestream.bytes_written; + + write_state state = { + .iface_num = iface_num, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .buf = + { + '?', + '#', + '#', + (msg_id >> 8) & 0xFF, + msg_id & 0xFF, + (msg_size >> 24) & 0xFF, + (msg_size >> 16) & 0xFF, + (msg_size >> 8) & 0xFF, + msg_size & 0xFF, + }, + }; + + pb_ostream_t stream = {.callback = write, + .state = &state, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + + if (false == pb_encode(&stream, fields, msg)) { + return secfalse; + } + + write_flush(&state); + return secfalse; +} + +secbool recv_protob_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg, + bool (*read)(pb_istream_t *stream, pb_byte_t *buf, + size_t count), + void (*read_flush)(read_state *state), + uint16_t packet_size) { + read_state state = {.iface_num = iface_num, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .packet_size = packet_size, + .buf = buf}; + + pb_istream_t stream = {.callback = read, + .state = &state, + .bytes_left = msg_size, + .errmsg = NULL}; + + if (false == pb_decode_noinit(&stream, fields, msg)) { + return secfalse; + } + + read_flush(&state); + + return sectrue; +} + +secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, + uint32_t *msg_size) { + if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { + return secfalse; + } + *msg_id = (buf[3] << 8) + buf[4]; + *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + return sectrue; +} diff --git a/core/embed/lib/protob_helpers.h b/core/embed/lib/protob_helpers.h new file mode 100644 index 0000000000..b960212f31 --- /dev/null +++ b/core/embed/lib/protob_helpers.h @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include "secbool.h" + +#define USB_PACKET_SIZE 64 +#define MSG_HEADER1_LEN 9 +#define MSG_HEADER2_LEN 1 + +#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default +#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ + { msg_send.FIELD = VALUE; } +#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + msg_send.FIELD = VALUE; \ + } +#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ + } +#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ + } +#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ + memcpy(msg_send.FIELD.bytes, VALUE, \ + MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ + msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ + } +#define MSG_SEND_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ + { \ + msg_send.FIELD.funcs.encode = &CALLBACK; \ + msg_send.FIELD.arg = (void *)ARGUMENT; \ + } +#define MSG_SEND(TYPE, WRITE, WRITE_FLUSH) \ + send_protob_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, \ + &msg_send, WRITE, WRITE_FLUSH) + +#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default +#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ + { \ + msg_recv.FIELD.funcs.decode = &CALLBACK; \ + msg_recv.FIELD.arg = (void *)ARGUMENT; \ + } +#define MSG_RECV(TYPE, READ, READ_FLUSH, PACKET_SIZE) \ + recv_protob_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv, READ, \ + READ_FLUSH, PACKET_SIZE) + +typedef struct { + uint8_t iface_num; + uint8_t packet_index; + uint8_t packet_pos; + uint8_t buf[USB_PACKET_SIZE]; +} write_state; + +typedef struct { + uint8_t iface_num; + uint8_t packet_index; + uint8_t packet_pos; + uint16_t packet_size; + uint8_t *buf; +} read_state; + +secbool send_protob_msg(uint8_t iface_num, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg, + bool (*write_fnc)(pb_ostream_t *stream, + const pb_byte_t *buf, size_t count), + void (*write_flush)(write_state *state)); + +secbool recv_protob_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg, + bool (*read)(pb_istream_t *stream, pb_byte_t *buf, + size_t count), + void (*read_flush)(read_state *state), + uint16_t packet_size); + +secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, + uint32_t *msg_size); diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index ae829ef746..2cf495f894 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -23,6 +23,7 @@ clippy = [] jpeg = [] disp_i8080_8bit_dw = [] # write pixels directly to peripheral disp_i8080_16bit_dw = [] # write pixels directly to peripheral +ble = [] debug = ["ui_debug"] sbu = [] sd_card = [] diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 357211d8de..2bce4837be 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -358,6 +358,9 @@ fn generate_trezorhal_bindings() { .no_copy("buffer_jpeg_t") .no_copy("buffer_jpeg_work_t") .no_copy("buffer_blurring_t") + // ble + .allowlist_function("ble_connected") + .allowlist_function("start_advertising") //usb .allowlist_function("usb_configured") // touch diff --git a/core/embed/rust/rust_ui.h b/core/embed/rust/rust_ui.h index fb79f8de59..0718c2ae7d 100644 --- a/core/embed/rust/rust_ui.h +++ b/core/embed/rust/rust_ui.h @@ -28,6 +28,11 @@ void screen_welcome(void); void screen_boot_empty(bool fading); uint32_t screen_unlock_bootloader_confirm(void); void screen_unlock_bootloader_success(void); + +uint32_t screen_pairing_confirm(uint8_t* buffer); +uint32_t screen_comparison_confirm(uint8_t* code, uint8_t code_len); +uint32_t screen_repair_confirm(void); + void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen); void display_icon(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen, uint16_t fg_color, uint16_t bg_color); diff --git a/core/embed/rust/src/trezorhal/ble.rs b/core/embed/rust/src/trezorhal/ble.rs new file mode 100644 index 0000000000..7e9d829004 --- /dev/null +++ b/core/embed/rust/src/trezorhal/ble.rs @@ -0,0 +1,9 @@ +use super::ffi; + +pub fn ble_connected() -> bool { + unsafe { ffi::ble_connected() } +} + +pub fn start_advertising(whitelist: bool) { + unsafe { ffi::start_advertising(whitelist) } +} diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index 86fe0a76be..0bb6febdda 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -1,4 +1,6 @@ pub mod bip39; +#[cfg(feature = "ble")] +pub mod ble; #[macro_use] #[allow(unused_macros)] pub mod fatal_error; diff --git a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs index c6eff2353d..f09b3db8a9 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs @@ -14,17 +14,19 @@ use crate::{ theme::{ button_bld, button_confirm, button_wipe_cancel, button_wipe_confirm, BLD_BG, BLD_FG, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD32, FIRE32, FIRE40, - TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24, + TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, + WELCOME_COLOR, X24, }, welcome::Welcome, }, - component::{Button, ResultScreen, WelcomeScreen}, + component::{Button, PinKeyboard, ResultScreen, WelcomeScreen}, constant, theme::{BACKLIGHT_DIM, BACKLIGHT_NORMAL, FG, WHITE}, }, util::{from_c_array, from_c_str}, }, }; +use core::slice; use heapless::String; use num_traits::ToPrimitive; @@ -39,6 +41,7 @@ use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK}; use confirm::Confirm; use intro::Intro; use menu::Menu; +use crate::ui::geometry::Alignment; use self::theme::{RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE}; @@ -73,13 +76,16 @@ fn fadeout() { display::fade_backlight_duration(BACKLIGHT_DIM, 150); } -fn run(frame: &mut F) -> u32 +fn run(frame: &mut F, clear: bool) -> u32 where F: Component, F::Msg: ReturnToC, { frame.place(constant::screen()); fadeout(); + if clear { + display::rect_fill(screen(), BLACK); + } display::sync(); frame.paint(); fadein(); @@ -190,7 +196,7 @@ extern "C" fn screen_install_confirm( Some(("FW FINGERPRINT", fingerprint_str)), ); - run(&mut frame) + run(&mut frame, false) } #[no_mangle] @@ -216,12 +222,12 @@ extern "C" fn screen_wipe_confirm() -> u32 { None, ); - run(&mut frame) + run(&mut frame, false) } #[no_mangle] extern "C" fn screen_menu(firmware_present: secbool) -> u32 { - run(&mut Menu::new(firmware_present)) + run(&mut Menu::new(firmware_present), true) } #[no_mangle] @@ -248,7 +254,7 @@ extern "C" fn screen_intro( let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok); - run(&mut frame) + run(&mut frame, false) } fn screen_progress( @@ -433,3 +439,65 @@ extern "C" fn bld_continue_label(bg_color: cty::uint16_t) { Color::from_u16(bg_color), ); } + +#[no_mangle] +extern "C" fn screen_pairing_confirm(buffer: *const cty::uint8_t) -> u32 { + let pin_slice = unsafe { slice::from_raw_parts_mut(buffer as *mut u8, 6) }; + + let mut pin = PinKeyboard::new("Enter passkey", "", None, true); + let res = run(&mut pin, true); + + if res == 2 { + let pin = pin.pin().as_bytes(); + if pin.len() == 6 { + pin_slice.copy_from_slice(&pin[0..6]); + } + } + + res +} + +#[no_mangle] +extern "C" fn screen_comparison_confirm(code: *const cty::uint8_t, code_len: u8) -> u32 { + let code = unwrap!(unsafe { from_c_array(code, code_len as usize) }); + + + let right = Button::with_text("YES").styled(button_confirm()); + let left = Button::with_text("CANCEL").styled(button_bld()); + let title = Label::new("DO THE NUMBERS MATCH?", Alignment::Start, theme::TEXT_BOLD) + .vertically_centered(); + + let mut frame = Confirm::new( + BLD_BG, + left, + right, + ConfirmTitle::Text(title), + Label::new(code, Alignment::Center, TEXT_NORMAL), + None, + None, + ); + + run(&mut frame, true) +} + + +#[no_mangle] +extern "C" fn screen_repair_confirm() -> u32 { + let msg = Label::new("Allow repair?", Alignment::Center, TEXT_NORMAL); + let right = Button::with_text("ALLOW").styled(button_confirm()); + let left = Button::with_text("DENY").styled(button_bld()); + let title = Label::new("REPAIR", Alignment::Start, theme::TEXT_BOLD) + .vertically_centered(); + + let mut frame = Confirm::new( + BLD_BG, + left, + right, + ConfirmTitle::Text(title), + msg, + None, + None, + ); + + run(&mut frame, true) +} diff --git a/core/embed/rust/src/ui/model_tt/bootloader/theme.rs b/core/embed/rust/src/ui/model_tt/bootloader/theme.rs index 6c34ba3d97..dc38dfc8fb 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/theme.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/theme.rs @@ -231,6 +231,8 @@ pub fn button_bld() -> ButtonStyleSheet { } } +pub const TEXT_NORMAL_BLACK: TextStyle = TextStyle::new(Font::NORMAL, WHITE, BLACK, WHITE, WHITE); + pub const TEXT_TITLE: TextStyle = TextStyle::new( Font::BOLD, BLD_TITLE_COLOR, diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs index 9f2ccde6e0..f7d7b488f2 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs @@ -19,9 +19,10 @@ use crate::{ }, }; +#[derive(Copy, Clone, ToPrimitive)] pub enum PinKeyboardMsg { - Confirmed, - Cancelled, + Cancelled = 1, + Confirmed = 2, } const MAX_LENGTH: usize = 50; diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h index e34711d70c..ae7ac447a1 100644 --- a/core/embed/rust/trezorhal.h +++ b/core/embed/rust/trezorhal.h @@ -1,4 +1,5 @@ #include TREZOR_BOARD +#include "ble/state.h" #include "buffers.h" #include "button.h" #include "common.h" diff --git a/core/embed/sdk/nrf52/components/libraries/crypto/backend/nrf_sw/nrf_sw_backend_hash.c b/core/embed/sdk/nrf52/components/libraries/crypto/backend/nrf_sw/nrf_sw_backend_hash.c index ac9031604d..e33cf814c7 100644 --- a/core/embed/sdk/nrf52/components/libraries/crypto/backend/nrf_sw/nrf_sw_backend_hash.c +++ b/core/embed/sdk/nrf52/components/libraries/crypto/backend/nrf_sw/nrf_sw_backend_hash.c @@ -123,7 +123,6 @@ static uint32_t nrf_sw_backend_hash_sha256_finalize(void * const p_context, } - const nrf_crypto_hash_info_t g_nrf_crypto_hash_sha256_info = { .init_fn = nrf_sw_backend_hash_sha256_init, diff --git a/core/embed/sdk/nrf52/examples/ble_peripheral/ble_app_hids_keyboard/pca10056/s140/armgcc/Makefile b/core/embed/sdk/nrf52/examples/ble_peripheral/ble_app_hids_keyboard/pca10056/s140/armgcc/Makefile index bad5db197f..7092488dc6 100644 --- a/core/embed/sdk/nrf52/examples/ble_peripheral/ble_app_hids_keyboard/pca10056/s140/armgcc/Makefile +++ b/core/embed/sdk/nrf52/examples/ble_peripheral/ble_app_hids_keyboard/pca10056/s140/armgcc/Makefile @@ -27,6 +27,7 @@ SRC_FILES += \ $(SDK_ROOT)/components/libraries/crc16/crc16.c \ $(SDK_ROOT)/components/libraries/timer/drv_rtc.c \ $(SDK_ROOT)/components/libraries/fds/fds.c \ + $(SDK_ROOT)/components/libraries/fds/fds.c \ $(SDK_ROOT)/components/libraries/hardfault/hardfault_implementation.c \ $(SDK_ROOT)/components/libraries/util/nrf_assert.c \ $(SDK_ROOT)/components/libraries/atomic_fifo/nrf_atfifo.c \ diff --git a/core/embed/segger/SEGGER_MMD/HardFaultHandler.S b/core/embed/segger/SEGGER_MMD/HardFaultHandler.S new file mode 100644 index 0000000000..e318df17b0 --- /dev/null +++ b/core/embed/segger/SEGGER_MMD/HardFaultHandler.S @@ -0,0 +1,208 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2014 - 2020 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Neither the name of SEGGER Microcontroller GmbH * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. * +* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +; +;---------------------------------------------------------------------- +;File : HardFaultHandler.S +;Purpose : HardFault exception handler for IAR, Keil and GNU assembler. +; Evaluates used stack (MSP, PSP) and passes appropiate stack +; pointer to the HardFaultHandler "C"-routine. +;------------- END-OF-HEADER ------------------------------------------ +;*/ + +#ifndef __IAR_SYSTEMS_ASM__ + #ifndef __CC_ARM + #ifndef __clang__ + #ifndef __GNUC__ + #error "Unsupported assembler!" + #endif + #endif + #endif +#endif + +;/********************************************************************* +;* +;* Forward declarations of segments used +;* +;********************************************************************** +;*/ + +#if (defined(__IAR_SYSTEMS_ASM__)) + + SECTION CODE:CODE:NOROOT(2) + SECTION CSTACK:DATA:NOROOT(3) + +#elif (defined(__CC_ARM)) + + AREA OSKERNEL, CODE, READONLY, ALIGN=2 + PRESERVE8 + +#endif + +;/********************************************************************* +;* +;* Publics +;* +;********************************************************************** +;*/ + +#if (defined(__IAR_SYSTEMS_ASM__)) + + SECTION .text:CODE:NOROOT(2) + PUBLIC HardFault_Handler + +#elif (defined(__CC_ARM)) + + EXPORT HardFault_Handler + +#elif (defined(__clang__) || defined(__GNUC__)) + + .global HardFault_Handler + .type HardFault_Handler, function + +#endif + +;/********************************************************************* +;* +;* Externals, code +;* +;********************************************************************** +;*/ + +#if (defined(__IAR_SYSTEMS_ASM__)) + + EXTERN HardFaultHandler + +#elif (defined(__CC_ARM)) + + IMPORT HardFaultHandler + +#elif (defined(__clang__) || defined(__GNUC__)) + + .extern HardFaultHandler + +#endif + +;/********************************************************************* +;* +;* CODE segment +;* +;********************************************************************** +;*/ + +#if (defined(__clang__) || defined(__GNUC__)) + + .syntax unified + .thumb + .balign 4 + .text + +#else + + THUMB + +#endif + +;/********************************************************************* +;* +;* Global functions +;* +;********************************************************************** +;*/ + +;/********************************************************************* +;* +;* HardFault_Handler() +;* +;* Function description +;* Evaluates the used stack (MSP, PSP) and passes the appropiate +;* stack pointer to the HardFaultHandler "C"-routine. +;* +;* Notes +;* (1) Ensure that HardFault_Handler is part of the exception table +;*/ +#if (defined(__clang__) || defined(__GNUC__)) +HardFault_Handler: +#else +HardFault_Handler +#endif +#if (defined(__IAR_SYSTEMS_ASM__) && defined(__ARM6M__) && (__CORE__ == __ARM6M__)) || \ + (defined(__CC_ARM) && defined(__TARGET_ARCH_6S_M)) || \ + (defined(__clang__) && defined(__ARM_ARCH) && (__ARM_ARCH == 6)) || \ + (defined(__GNUC__) && (defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_8M_BASE__))) + ;// This version is for Cortex M0 + movs R0, #4 + mov R1, LR + tst R0, R1 ;// Check EXC_RETURN in Link register bit 2. + bne Uses_PSP + mrs R0, MSP ;// Stacking was using MSP. + b Pass_StackPtr +#if (defined(__clang__) || defined(__GNUC__)) +Uses_PSP: +#else +Uses_PSP +#endif + mrs R0, PSP ;// Stacking was using PSP. +#if (defined(__GNUC__) || defined(__clang__)) +Pass_StackPtr: +#else +Pass_StackPtr +#endif +#else + ;// This version is for Cortex M3, Cortex M4 and Cortex M4F + tst LR, #4 ;// Check EXC_RETURN in Link register bit 2. + ite EQ + mrseq R0, MSP ;// Stacking was using MSP. + mrsne R0, PSP ;// Stacking was using PSP. +#endif +#if (defined(__CC_ARM)) + ALIGN +#endif + ldr R1,=HardFaultHandler + bx R1 ;// Stack pointer passed through R0. + +#if (defined(__clang__) || defined(__GNUC__)) + .end +#else + END +#endif + +;/****** End Of File *************************************************/ diff --git a/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c new file mode 100644 index 0000000000..f8efe0caf6 --- /dev/null +++ b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c @@ -0,0 +1,155 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2018 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the monitor mode protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* - Neither the name of SEGGER Microcontroller GmbH * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------------------------------------------------- +File : JLINK_MONITOR.c +Purpose : Implementation of debug monitor for J-Link monitor mode debug on Cortex-M devices. +-------- END-OF-HEADER --------------------------------------------- +*/ + +#include "JLINK_MONITOR.h" + +/********************************************************************* +* +* Configuration +* +********************************************************************** +*/ + +/********************************************************************* +* +* Defines +* +********************************************************************** +*/ + +/********************************************************************* +* +* Types +* +********************************************************************** +*/ + +/********************************************************************* +* +* Static data +* +********************************************************************** +*/ + +/********************************************************************* +* +* Local functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* Global functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* JLINK_MONITOR_OnExit() +* +* Function description +* Called from DebugMon_Handler(), once per debug exit. +* May perform some target specific operations to be done on debug mode exit. +* +* Notes +* (1) Must not keep the CPU busy for more than 100 ms +*/ +void JLINK_MONITOR_OnExit(void) { + // + // Add custom code here + // +// BSP_ClrLED(0); +} + +/********************************************************************* +* +* JLINK_MONITOR_OnEnter() +* +* Function description +* Called from DebugMon_Handler(), once per debug entry. +* May perform some target specific operations to be done on debug mode entry +* +* Notes +* (1) Must not keep the CPU busy for more than 100 ms +*/ +void JLINK_MONITOR_OnEnter(void) { + // + // Add custom code here + // +// BSP_SetLED(0); +// BSP_ClrLED(1); +} + +/********************************************************************* +* +* JLINK_MONITOR_OnPoll() +* +* Function description +* Called periodically from DebugMon_Handler(), to perform some actions that need to be performed periodically during debug mode. +* +* Notes +* (1) Must not keep the CPU busy for more than 100 ms +*/ +void JLINK_MONITOR_OnPoll(void) { + // + // Add custom code here + // +// BSP_ToggleLED(0); +// _Delay(500000); +} + +/****** End Of File *************************************************/ diff --git a/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h new file mode 100644 index 0000000000..6c43f3e688 --- /dev/null +++ b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h @@ -0,0 +1,65 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2018 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the monitor mode protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* - Neither the name of SEGGER Microcontroller GmbH * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------------------------------------------------- +File : JLINK_MONITOR.h +Purpose : Header file of debug monitor for J-Link monitor mode debug on Cortex-M devices. +-------- END-OF-HEADER --------------------------------------------- +*/ + +#ifndef JLINK_MONITOR_H +#define JLINK_MONITOR_H + +void JLINK_MONITOR_OnExit (void); +void JLINK_MONITOR_OnEnter (void); +void JLINK_MONITOR_OnPoll (void); + +#endif + +/****** End Of File *************************************************/ diff --git a/core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S new file mode 100644 index 0000000000..e1851181dc --- /dev/null +++ b/core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S @@ -0,0 +1,927 @@ +/********************************************************************* +* (c) SEGGER Microcontroller GmbH * +* The Embedded Experts * +* www.segger.com * +********************************************************************** +* * +* (c) 1995 - 2020 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the monitor mode protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the following * +* disclaimer in the documentation and/or other materials provided * +* with the distribution. * +* * +* - Neither the name of SEGGER Microcontroller GmbH * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------------------------------------------------- +File : JLINK_MONITOR_ISR_SES.s +Purpose : Implementation of debug monitor for J-Link monitor mode + debug on Cortex-M devices, supporting SES compiler. +-------- END-OF-HEADER --------------------------------------------- +*/ + + .name JLINK_MONITOR_ISR + .syntax unified + + .extern JLINK_MONITOR_OnEnter + .extern JLINK_MONITOR_OnExit + .extern JLINK_MONITOR_OnPoll + + .global DebugMon_Handler + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ + +#define _MON_VERSION 100 // V x.yy + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ + +#define _APP_SP_OFF_R0 0x00 +#define _APP_SP_OFF_R1 0x04 +#define _APP_SP_OFF_R2 0x08 +#define _APP_SP_OFF_R3 0x0C +#define _APP_SP_OFF_R12 0x10 +#define _APP_SP_OFF_R14_LR 0x14 +#define _APP_SP_OFF_PC 0x18 +#define _APP_SP_OFF_XPSR 0x1C +#define _APP_SP_OFF_S0 0x20 +#define _APP_SP_OFF_S1 0x24 +#define _APP_SP_OFF_S2 0x28 +#define _APP_SP_OFF_S3 0x2C +#define _APP_SP_OFF_S4 0x30 +#define _APP_SP_OFF_S5 0x34 +#define _APP_SP_OFF_S6 0x38 +#define _APP_SP_OFF_S7 0x3C +#define _APP_SP_OFF_S8 0x40 +#define _APP_SP_OFF_S9 0x44 +#define _APP_SP_OFF_S10 0x48 +#define _APP_SP_OFF_S11 0x4C +#define _APP_SP_OFF_S12 0x50 +#define _APP_SP_OFF_S13 0x54 +#define _APP_SP_OFF_S14 0x58 +#define _APP_SP_OFF_S15 0x5C +#define _APP_SP_OFF_FPSCR 0x60 + +#define _NUM_BYTES_BASIC_STACKFRAME 32 +#define _NUM_BYTES_EXTENDED_STACKFRAME 104 // Values for stackframes are explained at location where they are used + +#define _SYSTEM_DCRDR_OFF 0x00 +#define _SYSTEM_DEMCR_OFF 0x04 + +#define _SYSTEM_DHCSR 0xE000EDF0 // Debug Halting Control and Status Register (DHCSR) +#define _SYSTEM_DCRSR 0xE000EDF4 // Debug Core Register Selector Register (DCRSR) +#define _SYSTEM_DCRDR 0xE000EDF8 // Debug Core Register Data Register (DCRDR) +#define _SYSTEM_DEMCR 0xE000EDFC // Debug Exception and Monitor Control Register (DEMCR) + +#define _SYSTEM_FPCCR 0xE000EF34 // Floating-Point Context Control Register (FPCCR) +#define _SYSTEM_FPCAR 0xE000EF38 // Floating-Point Context Address Register (FPCAR) +#define _SYSTEM_FPDSCR 0xE000EF3C // Floating-Point Default Status Control Register (FPDSCR) +#define _SYSTEM_MVFR0 0xE000EF40 // Media and FP Feature Register 0 (MVFR0) +#define _SYSTEM_MVFR1 0xE000EF44 // Media and FP Feature Register 1 (MVFR1) + +/* +* Defines for determining if the current debug config supports FPU registers +* For some compilers like IAR EWARM when disabling the FPU in the compiler settings an error is thrown when +*/ +#ifdef __FPU_PRESENT + #if __FPU_PRESENT + #define _HAS_FPU_REGS 1 + #else + #define _HAS_FPU_REGS 0 + #endif +#else + #define _HAS_FPU_REGS 0 +#endif + +/********************************************************************* +* +* Signature of monitor +* +* Function description +* Needed for targets where also a boot ROM is present that possibly specifies a vector table with a valid debug monitor exception entry +*/ + .section .text, "ax" + + // + // JLINKMONHANDLER + // + .byte 0x4A + .byte 0x4C + .byte 0x49 + .byte 0x4E + .byte 0x4B + .byte 0x4D + .byte 0x4F + .byte 0x4E + .byte 0x48 + .byte 0x41 + .byte 0x4E + .byte 0x44 + .byte 0x4C + .byte 0x45 + .byte 0x52 + .byte 0x00 // Align to 8-bytes + +/********************************************************************* +* +* DebugMon_Handler() +* +* Function description +* Debug monitor handler. CPU enters this handler in case a "halt" request is made from the debugger. +* This handler is also responsible for handling commands that are sent by the debugger. +* +* Notes +* This is actually the ISR for the debug inerrupt (exception no. 12) +*/ + .thumb_func + +DebugMon_Handler: + /* + General procedure: + DCRDR is used as communication register + DEMCR[19] is used as ready flag + For the command J-Link sends to the monitor: DCRDR[7:0] == Cmd, DCRDR[31:8] == ParamData + + 1) Monitor sets DEMCR[19] whenever it is ready to receive new commands/data + DEMCR[19] is initially set on debug monitor entry + 2) J-Link will clear DEMCR[19] it has placed command/data in DCRDR for monitor + 3) Monitor will wait for DEMCR[19] to be cleared + 4) Monitor will process command (May cause additional data transfers etc., depends on command). Monitor will set DEMCR[19] whenever it has placed data for J-Link in the DCRDR + 5) No restart-CPU command? => Back to 2), Otherwise => 6) + 6) Monitor will clear DEMCR[19] 19 to indicate that it is no longer ready + */ + PUSH {LR} + BL JLINK_MONITOR_OnEnter + POP {LR} + LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR + B.N _IndicateMonReady +_WaitProbeReadIndicateMonRdy: // while(_SYSTEM_DEMCR & (1uL << 19)); => Wait until J-Link has read item + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR + LSLS R0,R0,#+12 + BMI.N _WaitProbeReadIndicateMonRdy +_IndicateMonReady: + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands + ORR R0,R0,#0x80000 + STR R0,[R3, #+_SYSTEM_DEMCR_OFF] + /* + During command loop: + R0 = Tmp + R1 = Tmp + R2 = Tmp + R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) + R12 = Tmp + + Outside command loop R0-R3 and R12 may be overwritten by MONITOR_OnPoll() + */ +_WaitForJLinkCmd: // do { + PUSH {LR} + BL JLINK_MONITOR_OnPoll + POP {LR} + LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] + LSRS R0,R0,#+20 // DEMCR[19] -> Carry Clear? => J-Link has placed command for us + BCS _WaitForJLinkCmd + /* + Perform command + Command is placed by J-Link in DCRDR[7:0] and additional parameter data is stored in DCRDR[31:8] + J-Link clears DEMCR[19] to indicate that it placed a command/data or read data + Monitor sets DEMCR[19] to indicate that it placed data or read data / is ready for a new command + Setting DEMCR[19] indicates "monitor ready for new command / data" and also indicates: "data has been placed in DCRDR by monitor, for J-Link" + Therefore it is responsibility of the commands to respond to the commands accordingly + + Commands for debug monitor + Commands must not exceed 0xFF (255) as we only defined 8-bits for command-part. Higher 24-bits are parameter info for current command + + Protocol for different commands: + J-Link: Cmd -> DCRDR, DEMCR[19] -> 0 => Cmd placed by probe + */ + LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // ParamInfo = _SYSTEM_DCRDR + LSRS R1,R0,#+8 // ParamInfo >>= 8 + LSLS R0,R0,#+24 + LSRS R0,R0,#+24 // Cmd = ParamInfo & 0xFF + // + // switch (Cmd) + // + CMP R0,#+0 + BEQ.N _HandleGetMonVersion // case _MON_CMD_GET_MONITOR_VERSION + CMP R0,#+2 + BEQ.N _HandleReadReg // case _MON_CMD_READ_REG + BCC.N _HandleRestartCPU // case _MON_CMD_RESTART_CPU + CMP R0,#+3 + BEQ.N _HandleWriteReg_Veneer // case _MON_CMD_WRITE_REG + B.N _IndicateMonReady // default : while (1); + /* + Return + _MON_CMD_RESTART_CPU + CPU: DEMCR[19] -> 0 => Monitor no longer ready + */ +_HandleRestartCPU: + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR &= ~(1uL << 19); => Clear MON_REQ to indicate that monitor is no longer active + BIC R0,R0,#0x80000 + STR R0,[R3, #+_SYSTEM_DEMCR_OFF] + PUSH {LR} + BL JLINK_MONITOR_OnExit + POP {PC} + // + // Place data section here to not get in trouble with load-offsets + // + .section .text, "ax", %progbits + .align 2 +_AddrDCRDR: + .long 0xE000EDF8 +_AddrCPACR: + .long 0xE000ED88 + + .section .text, "ax" + .thumb_func + +;/********************************************************************* +;* +;* _HandleGetMonVersion +;* +;*/ +_HandleGetMonVersion: + /* + _MON_CMD_GET_MONITOR_VERSION + CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready + J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read + CPU: DEMCR[19] -> 1 => Mon ready + */ + MOVS R0,#+_MON_VERSION + STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // _SYSTEM_DCRDR = x + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands + ORR R0,R0,#0x80000 + STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready + B _WaitProbeReadIndicateMonRdy + +/********************************************************************* +* +* _HandleReadReg +* +*/ +_HandleWriteReg_Veneer: + B.N _HandleWriteReg +_HandleReadReg: + /* + _MON_CMD_READ_REG + CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready + J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read + CPU: DEMCR[19] -> 1 => Mon ready + + + Register indexes + 0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!) + 16: XPSR + 17: MSP + 18: PSP + 19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] + 20: FPSCR + 21-52: FPS0-FPS31 + + + Register usage when entering this "subroutine": + R0 Cmd + R1 ParamInfo + R2 --- + R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) + R12 --- + + Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension + LR Return to Return SP Frame type + --------------------------------------------------------- + 0xFFFFFFE1 Handler mode. MSP Extended + 0xFFFFFFE9 Thread mode MSP Extended + 0xFFFFFFED Thread mode PSP Extended + 0xFFFFFFF1 Handler mode. MSP Basic + 0xFFFFFFF9 Thread mode MSP Basic + 0xFFFFFFFD Thread mode PSP Basic + + So LR[2] == 1 => Return stack == PSP else MSP + + R0-R3, R12, PC, xPSR can be read from application stackpointer + Other regs can be read directly + */ + LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP + ITE CS + MRSCS R2,PSP + MRSCC R2,MSP + CMP R1,#+4 // if (RegIndex < 4) { (R0-R3) + BCS _HandleReadRegR4 + LDR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3) + B.N _HandleReadRegDone +_HandleReadRegR4: + CMP R1,#+5 // if (RegIndex < 5) { (R4) + BCS _HandleReadRegR5 + MOV R0,R4 + B.N _HandleReadRegDone +_HandleReadRegR5: + CMP R1,#+6 // if (RegIndex < 6) { (R5) + BCS _HandleReadRegR6 + MOV R0,R5 + B.N _HandleReadRegDone +_HandleReadRegR6: + CMP R1,#+7 // if (RegIndex < 7) { (R6) + BCS _HandleReadRegR7 + MOV R0,R6 + B.N _HandleReadRegDone +_HandleReadRegR7: + CMP R1,#+8 // if (RegIndex < 8) { (R7) + BCS _HandleReadRegR8 + MOV R0,R7 + B.N _HandleReadRegDone +_HandleReadRegR8: + CMP R1,#+9 // if (RegIndex < 9) { (R8) + BCS _HandleReadRegR9 + MOV R0,R8 + B.N _HandleReadRegDone +_HandleReadRegR9: + CMP R1,#+10 // if (RegIndex < 10) { (R9) + BCS _HandleReadRegR10 + MOV R0,R9 + B.N _HandleReadRegDone +_HandleReadRegR10: + CMP R1,#+11 // if (RegIndex < 11) { (R10) + BCS _HandleReadRegR11 + MOV R0,R10 + B.N _HandleReadRegDone +_HandleReadRegR11: + CMP R1,#+12 // if (RegIndex < 12) { (R11) + BCS _HandleReadRegR12 + MOV R0,R11 + B.N _HandleReadRegDone +_HandleReadRegR12: + CMP R1,#+14 // if (RegIndex < 14) { (R12) + BCS _HandleReadRegR14 + LDR R0,[R2, #+_APP_SP_OFF_R12] + B.N _HandleReadRegDone +_HandleReadRegR14: + CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR) + BCS _HandleReadRegR15 + LDR R0,[R2, #+_APP_SP_OFF_R14_LR] + B.N _HandleReadRegDone +_HandleReadRegR15: + CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC) + BCS _HandleReadRegXPSR + LDR R0,[R2, #+_APP_SP_OFF_PC] + B.N _HandleReadRegDone +_HandleReadRegXPSR: + CMP R1,#+17 // if (RegIndex < 17) { (xPSR) + BCS _HandleReadRegMSP + LDR R0,[R2, #+_APP_SP_OFF_XPSR] + B.N _HandleReadRegDone +_HandleReadRegMSP: + /* + Stackpointer is tricky because we need to get some info about the SP used in the user app, first + + Handle reading R0-R3 which can be read right from application stackpointer + + Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension + LR Return to Return SP Frame type + --------------------------------------------------------- + 0xFFFFFFE1 Handler mode. MSP Extended + 0xFFFFFFE9 Thread mode MSP Extended + 0xFFFFFFED Thread mode PSP Extended + 0xFFFFFFF1 Handler mode. MSP Basic + 0xFFFFFFF9 Thread mode MSP Basic + 0xFFFFFFFD Thread mode PSP Basic + + So LR[2] == 1 => Return stack == PSP else MSP + Per architecture definition: Inside monitor (exception) SP = MSP + + Stack pointer handling is complicated because it is different what is pushed on the stack before entering the monitor ISR... + Cortex-M: 8 regs + Cortex-M + forced-stack-alignment: 8 regs + 1 dummy-word if stack was not 8-byte aligned + Cortex-M + FPU: 8 regs + 17 FPU regs + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned + Cortex-M + FPU + lazy mode: 8 regs + 17 dummy-words + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned + */ + CMP R1,#+18 // if (RegIndex < 18) { (MSP) + BCS _HandleReadRegPSP + MRS R0,MSP + LSRS R1,LR,#+3 // LR[2] -> Carry == 0 => CPU was running on MSP => Needs correction + BCS _HandleReadRegDone_Veneer // CPU was running on PSP? => No correction necessary +_HandleSPCorrection: + LSRS R1,LR,#+5 // LR[4] -> Carry == 0 => extended stack frame has been allocated. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry + ITE CS + ADDCS R0,R0,#+_NUM_BYTES_BASIC_STACKFRAME + ADDCC R0,R0,#+_NUM_BYTES_EXTENDED_STACKFRAME + LDR R1,[R2, #+_APP_SP_OFF_XPSR] // Get xPSR from application stack (R2 has been set to app stack on beginning of _HandleReadReg) + LSRS R1,R1,#+5 // xPSR[9] -> Carry == 1 => Stack has been force-aligned before pushing regs. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry + IT CS + ADDCS R0,R0,#+4 + B _HandleReadRegDone +_HandleReadRegPSP: // RegIndex == 18 + CMP R1,#+19 // if (RegIndex < 19) { + BCS _HandleReadRegCFBP + MRS R0,PSP // PSP is not touched by monitor + LSRS R1,LR,#+3 // LR[2] -> Carry == 1 => CPU was running on PSP => Needs correction + BCC _HandleReadRegDone_Veneer // CPU was running on MSP? => No correction of PSP necessary + B _HandleSPCorrection +_HandleReadRegCFBP: + /* + CFBP is a register that can only be read via debug probe and is a merger of the following regs: + CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] + To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode + */ + CMP R1,#+20 // if (RegIndex < 20) { (CFBP) + BCS _HandleReadRegFPU + MOVS R0,#+0 + MRS R2,PRIMASK + ORRS R0,R2 // Merge PRIMASK into CFBP[7:0] + MRS R2,BASEPRI + LSLS R2,R2,#+8 // Merge BASEPRI into CFBP[15:8] + ORRS R0,R2 + MRS R2,FAULTMASK + LSLS R2,R2,#+16 // Merge FAULTMASK into CFBP[23:16] + ORRS R0,R2 + MRS R2,CONTROL + LSRS R1,LR,#3 // LR[2] -> Carry. CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior + IT CS // As J-Link sees value of CONTROL at application time, we need reconstruct original value of CONTROL + ORRCS R2,R2,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor + LSRS R1,LR,#+5 // LR[4] == NOT(CONTROL.FPCA) -> Carry + ITE CS // Merge original value of FPCA (CONTROL[2]) into read data + BICCS R2,R2,#+0x04 // Remember LR contains NOT(CONTROL) + ORRCC R2,R2,#+0x04 + LSLS R2,R2,#+24 + ORRS R0,R2 + B.N _HandleReadRegDone +_HandleReadRegFPU: +#if _HAS_FPU_REGS + CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31) + BCS _HandleReadRegDone_Veneer + /* + Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled + If not, access to floating point is not possible + CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved + CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved + */ + LDR R0,_AddrCPACR + LDR R0,[R0] + LSLS R0,R0,#+8 + LSRS R0,R0,#+28 + CMP R0,#+0xF + BEQ _HandleReadRegFPU_Allowed + CMP R0,#+0x5 + BNE _HandleReadRegDone_Veneer +_HandleReadRegFPU_Allowed: + CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR) + BCS _HandleReadRegFPS0_FPS31 + LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack + BCS _HandleReadFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame + LDR R0,=_SYSTEM_FPCCR + LDR R0,[R0] + LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack + BCS _HandleReadFPSCRLazyMode + LDR R0,[R2, #+_APP_SP_OFF_FPSCR] + B _HandleReadRegDone +_HandleReadFPSCRLazyMode: + VMRS R0,FPSCR + B _HandleReadRegDone +_HandleReadRegFPS0_FPS31: // RegIndex == 21-52 + LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack + BCS _HandleReadFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame + LDR R0,=_SYSTEM_FPCCR + LDR R0,[R0] + LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack + BCS _HandleReadFPS0_FPS31LazyMode + SUBS R1,#+21 // Convert absolute reg index into rel. one + LSLS R1,R1,#+2 // RegIndex to position on stack + ADDS R1,#+_APP_SP_OFF_S0 + LDR R0,[R2, R1] +_HandleReadRegDone_Veneer: + B _HandleReadRegDone +_HandleReadFPS0_FPS31LazyMode: + SUBS R1,#+20 // convert abs. RegIndex into rel. one + MOVS R0,#+6 + MULS R1,R0,R1 + LDR R0,=_HandleReadRegUnknown + SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1) + ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr + BX R0 + // + // Table for reading FPS0-FPS31 + // + VMOV R0,S31 // v = FPSx + B _HandleReadRegDone + VMOV R0,S30 + B _HandleReadRegDone + VMOV R0,S29 + B _HandleReadRegDone + VMOV R0,S28 + B _HandleReadRegDone + VMOV R0,S27 + B _HandleReadRegDone + VMOV R0,S26 + B _HandleReadRegDone + VMOV R0,S25 + B _HandleReadRegDone + VMOV R0,S24 + B _HandleReadRegDone + VMOV R0,S23 + B _HandleReadRegDone + VMOV R0,S22 + B _HandleReadRegDone + VMOV R0,S21 + B _HandleReadRegDone + VMOV R0,S20 + B _HandleReadRegDone + VMOV R0,S19 + B _HandleReadRegDone + VMOV R0,S18 + B _HandleReadRegDone + VMOV R0,S17 + B _HandleReadRegDone + VMOV R0,S16 + B _HandleReadRegDone + VMOV R0,S15 + B _HandleReadRegDone + VMOV R0,S14 + B _HandleReadRegDone + VMOV R0,S13 + B _HandleReadRegDone + VMOV R0,S12 + B _HandleReadRegDone + VMOV R0,S11 + B _HandleReadRegDone + VMOV R0,S10 + B _HandleReadRegDone + VMOV R0,S9 + B _HandleReadRegDone + VMOV R0,S8 + B _HandleReadRegDone + VMOV R0,S7 + B _HandleReadRegDone + VMOV R0,S6 + B _HandleReadRegDone + VMOV R0,S5 + B _HandleReadRegDone + VMOV R0,S4 + B _HandleReadRegDone + VMOV R0,S3 + B _HandleReadRegDone + VMOV R0,S2 + B _HandleReadRegDone + VMOV R0,S1 + B _HandleReadRegDone + VMOV R0,S0 + B _HandleReadRegDone +#else + B _HandleReadRegUnknown +_HandleReadRegDone_Veneer: + B _HandleReadRegDone +#endif +_HandleReadRegUnknown: + MOVS R0,#+0 // v = 0 + B.N _HandleReadRegDone +_HandleReadRegDone: + + // Send register content to J-Link and wait until J-Link has read the data + + STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // DCRDR = v; + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands + ORR R0,R0,#0x80000 + STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready + B _WaitProbeReadIndicateMonRdy + + // Data section for register addresses + +_HandleWriteReg: + /* + _MON_CMD_WRITE_REG + CPU: DEMCR[19] -> 1 => Mon ready + J-Link: Data -> DCRDR, DEMCR[19] -> 0 => Data placed by probe + CPU: DCRDR -> Read, Process command, DEMCR[19] -> 1 => Data read & mon ready + + Register indexes + 0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!) + 16: XPSR + 17: MSP + 18: PSP + 19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] + 20: FPSCR + 21-52: FPS0-FPS31 + + + Register usage when entering this "subroutine": + R0 Cmd + R1 ParamInfo + R2 --- + R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset) + R12 --- + + Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension + LR Return to Return SP Frame type + --------------------------------------------------------- + 0xFFFFFFE1 Handler mode. MSP Extended + 0xFFFFFFE9 Thread mode MSP Extended + 0xFFFFFFED Thread mode PSP Extended + 0xFFFFFFF1 Handler mode. MSP Basic + 0xFFFFFFF9 Thread mode MSP Basic + 0xFFFFFFFD Thread mode PSP Basic + + So LR[2] == 1 => Return stack == PSP else MSP + + R0-R3, R12, PC, xPSR can be written via application stackpointer + Other regs can be written directly + + + Read register data from J-Link into R0 + */ + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Monitor is ready to receive register data + ORR R0,R0,#0x80000 + STR R0,[R3, #+_SYSTEM_DEMCR_OFF] +_HandleWRegWaitUntilDataRecv: + LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] + LSLS R0,R0,#+12 + BMI.N _HandleWRegWaitUntilDataRecv // DEMCR[19] == 0 => J-Link has placed new data for us + LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // Get register data + // + // Determine application SP + // + LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP + ITE CS + MRSCS R2,PSP + MRSCC R2,MSP + CMP R1,#+4 // if (RegIndex < 4) { (R0-R3) + BCS _HandleWriteRegR4 + STR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3) + B.N _HandleWriteRegDone +_HandleWriteRegR4: + CMP R1,#+5 // if (RegIndex < 5) { (R4) + BCS _HandleWriteRegR5 + MOV R4,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR5: + CMP R1,#+6 // if (RegIndex < 6) { (R5) + BCS _HandleWriteRegR6 + MOV R5,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR6: + CMP R1,#+7 // if (RegIndex < 7) { (R6) + BCS _HandleWriteRegR7 + MOV R6,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR7: + CMP R1,#+8 // if (RegIndex < 8) { (R7) + BCS _HandleWriteRegR8 + MOV R7,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR8: + CMP R1,#+9 // if (RegIndex < 9) { (R8) + BCS _HandleWriteRegR9 + MOV R8,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR9: + CMP R1,#+10 // if (RegIndex < 10) { (R9) + BCS _HandleWriteRegR10 + MOV R9,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR10: + CMP R1,#+11 // if (RegIndex < 11) { (R10) + BCS _HandleWriteRegR11 + MOV R10,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR11: + CMP R1,#+12 // if (RegIndex < 12) { (R11) + BCS _HandleWriteRegR12 + MOV R11,R0 + B.N _HandleWriteRegDone +_HandleWriteRegR12: + CMP R1,#+14 // if (RegIndex < 14) { (R12) + BCS _HandleWriteRegR14 + STR R0,[R2, #+_APP_SP_OFF_R12] + B.N _HandleWriteRegDone +_HandleWriteRegR14: + CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR) + BCS _HandleWriteRegR15 + STR R0,[R2, #+_APP_SP_OFF_R14_LR] + B.N _HandleWriteRegDone +_HandleWriteRegR15: + CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC) + BCS _HandleWriteRegXPSR + STR R0,[R2, #+_APP_SP_OFF_PC] + B.N _HandleWriteRegDone +_HandleWriteRegXPSR: + CMP R1,#+17 // if (RegIndex < 17) { (xPSR) + BCS _HandleWriteRegMSP + STR R0,[R2, #+_APP_SP_OFF_XPSR] + B.N _HandleWriteRegDone +_HandleWriteRegMSP: + // + // For now, SP cannot be modified because it is needed to jump back from monitor mode + // + CMP R1,#+18 // if (RegIndex < 18) { (MSP) + BCS _HandleWriteRegPSP + B.N _HandleWriteRegDone +_HandleWriteRegPSP: // RegIndex == 18 + CMP R1,#+19 // if (RegIndex < 19) { + BCS _HandleWriteRegCFBP + B.N _HandleWriteRegDone +_HandleWriteRegCFBP: + /* + CFBP is a register that can only be read via debug probe and is a merger of the following regs: + CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0] + To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode + */ + CMP R1,#+20 // if (RegIndex < 20) { (CFBP) + BCS _HandleWriteRegFPU + LSLS R1,R0,#+24 + LSRS R1,R1,#+24 // Extract CFBP[7:0] => PRIMASK + MSR PRIMASK,R1 + LSLS R1,R0,#+16 + LSRS R1,R1,#+24 // Extract CFBP[15:8] => BASEPRI + MSR BASEPRI,R1 + LSLS R1,R0,#+8 // Extract CFBP[23:16] => FAULTMASK + LSRS R1,R1,#+24 + MSR FAULTMASK,R1 + LSRS R1,R0,#+24 // Extract CFBP[31:24] => CONTROL + LSRS R0,R1,#2 // Current CONTROL[1] -> Carry + ITE CS // Update saved CONTROL.SPSEL (CONTROL[1]). CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior + ORRCS LR,LR,#+4 + BICCC LR,LR,#+4 + BIC R1,R1,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor. Otherwise behavior is UNPREDICTABLE + LSRS R0,R1,#+3 // New CONTROL.FPCA (CONTROL[2]) -> Carry + ITE CS // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack + BICCS LR,LR,#+0x10 // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame + ORRCC LR,LR,#+0x10 + MRS R0,CONTROL + LSRS R0,R0,#+3 // CONTROL[2] -> Carry + ITE CS // Preserve original value of current CONTROL[2] + ORRCS R1,R1,#+0x04 + BICCC R1,R1,#+0x04 + MSR CONTROL,R1 + ISB // Necessary after writing to CONTROL, see ARM DDI0403D, B1.4.4 The special-purpose CONTROL register + B.N _HandleWriteRegDone +_HandleWriteRegFPU: +#if _HAS_FPU_REGS + CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31) + BCS _HandleWriteRegDone_Veneer + /* + Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled + If not, access to floating point is not possible + CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved + CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved + */ + MOV R12,R0 // Save register data + LDR R0,_AddrCPACR + LDR R0,[R0] + LSLS R0,R0,#+8 + LSRS R0,R0,#+28 + CMP R0,#+0xF + BEQ _HandleWriteRegFPU_Allowed + CMP R0,#+0x5 + BNE _HandleWriteRegDone_Veneer +_HandleWriteRegFPU_Allowed: + CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR) + BCS _HandleWriteRegFPS0_FPS31 + LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack + BCS _HandleWriteFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame + LDR R0,=_SYSTEM_FPCCR + LDR R0,[R0] + LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack + BCS _HandleWriteFPSCRLazyMode + STR R12,[R2, #+_APP_SP_OFF_FPSCR] + B _HandleWriteRegDone +_HandleWriteFPSCRLazyMode: + VMSR FPSCR,R12 + B _HandleWriteRegDone +_HandleWriteRegFPS0_FPS31: // RegIndex == 21-52 + LDR R0,=_SYSTEM_FPCCR + LDR R0,[R0] + LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack + BCS _HandleWriteFPS0_FPS31LazyMode + LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack + BCS _HandleWriteFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame + SUBS R1,#+21 // Convert absolute reg index into rel. one + LSLS R1,R1,#+2 // RegIndex to position on stack + ADDS R1,#+_APP_SP_OFF_S0 + STR R12,[R2, R1] +_HandleWriteRegDone_Veneer: + B _HandleWriteRegDone +_HandleWriteFPS0_FPS31LazyMode: + SUBS R1,#+20 // Convert abs. RegIndex into rel. one + MOVS R0,#+6 + MULS R1,R0,R1 + LDR R0,=_HandleReadRegUnknown + SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1) + ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr + BX R0 + // + // Table for reading FPS0-FPS31 + // + VMOV S31,R12 // v = FPSx + B _HandleWriteRegDone + VMOV S30,R12 + B _HandleWriteRegDone + VMOV S29,R12 + B _HandleWriteRegDone + VMOV S28,R12 + B _HandleWriteRegDone + VMOV S27,R12 + B _HandleWriteRegDone + VMOV S26,R12 + B _HandleWriteRegDone + VMOV S25,R12 + B _HandleWriteRegDone + VMOV S24,R12 + B _HandleWriteRegDone + VMOV S23,R12 + B _HandleWriteRegDone + VMOV S22,R12 + B _HandleWriteRegDone + VMOV S21,R12 + B _HandleWriteRegDone + VMOV S20,R12 + B _HandleWriteRegDone + VMOV S19,R12 + B _HandleWriteRegDone + VMOV S18,R12 + B _HandleWriteRegDone + VMOV S17,R12 + B _HandleWriteRegDone + VMOV S16,R12 + B _HandleWriteRegDone + VMOV S15,R12 + B _HandleWriteRegDone + VMOV S14,R12 + B _HandleWriteRegDone + VMOV S13,R12 + B _HandleWriteRegDone + VMOV S12,R12 + B _HandleWriteRegDone + VMOV S11,R12 + B _HandleWriteRegDone + VMOV S10,R12 + B _HandleWriteRegDone + VMOV S9,R12 + B _HandleWriteRegDone + VMOV S8,R12 + B _HandleWriteRegDone + VMOV S7,R12 + B _HandleWriteRegDone + VMOV S6,R12 + B _HandleWriteRegDone + VMOV S5,R12 + B _HandleWriteRegDone + VMOV S4,R12 + B _HandleWriteRegDone + VMOV S3,R12 + B _HandleWriteRegDone + VMOV S2,R12 + B _HandleWriteRegDone + VMOV S1,R12 + B _HandleWriteRegDone + VMOV S0,R12 + B _HandleWriteRegDone +#else + B _HandleWriteRegUnknown +#endif +_HandleWriteRegUnknown: + B.N _HandleWriteRegDone +_HandleWriteRegDone: + B _IndicateMonReady // Indicate that monitor has read data, processed command and is ready for a new one + .end +/****** End Of File *************************************************/ diff --git a/core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c b/core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c new file mode 100644 index 0000000000..b6dee2e5f6 --- /dev/null +++ b/core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c @@ -0,0 +1,280 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 2014 - 2020 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* conditions are met: * +* * +* - Redistributions of source code must retain the above copyright * +* notice, this list of conditions and the following disclaimer. * +* * +* - Neither the name of SEGGER Microcontroller GmbH * +* nor the names of its contributors may be used to endorse or * +* promote products derived from this software without specific * +* prior written permission. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. * +* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------------------------------------------------- +File : SEGGER_HardFaultHandler.c +Purpose : Generic SEGGER HardFault handler for Cortex-M +Literature: + [1] Analyzing HardFaults on Cortex-M CPUs (https://www.segger.com/downloads/appnotes/AN00016_AnalyzingHardFaultsOnCortexM.pdf) + +Additional information: + This HardFault handler enables user-friendly analysis of hard faults + in debug configurations. + If a release configuration requires a HardFault handler, + a specific HardFault handler should be included instead, + which for example issues a reset or lits an error LED. +-------- END-OF-HEADER --------------------------------------------- +*/ + +/********************************************************************* +* +* Defines +* +********************************************************************** +*/ +#define SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u)) // System Handler Control and State Register +#define NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u)) // Memory Management Fault Status Register +#define NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u)) // Bus Fault Status Register +#define NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) // Usage Fault Status Register +#define NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) // Hard Fault Status Register +#define NVIC_DFSR (*(volatile unsigned int*) (0xE000ED30u)) // Debug Fault Status Register +#define NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u)) // Bus Fault Manage Address Register +#define NVIC_AFSR (*(volatile unsigned int*) (0xE000ED3Cu)) // Auxiliary Fault Status Register + +#ifndef DEBUG // Should be overwritten by project settings + #define DEBUG (0) // in debug builds +#endif + +/********************************************************************* +* +* Prototypes +* +********************************************************************** +*/ +#ifdef __cplusplus + extern "C" { +#endif +void HardFaultHandler(unsigned int* pStack); +#ifdef __cplusplus + } +#endif + +/********************************************************************* +* +* Static data +* +********************************************************************** +*/ +#if DEBUG +static volatile unsigned int _Continue; // Set this variable to 1 to run further + +static struct { + struct { + volatile unsigned int r0; // Register R0 + volatile unsigned int r1; // Register R1 + volatile unsigned int r2; // Register R2 + volatile unsigned int r3; // Register R3 + volatile unsigned int r12; // Register R12 + volatile unsigned int lr; // Link register + volatile unsigned int pc; // Program counter + union { + volatile unsigned int byte; + struct { + unsigned int IPSR : 8; // Interrupt Program Status register (IPSR) + unsigned int EPSR : 19; // Execution Program Status register (EPSR) + unsigned int APSR : 5; // Application Program Status register (APSR) + } bits; + } psr; // Program status register. + } SavedRegs; + + union { + volatile unsigned int byte; + struct { + unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active + unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active + unsigned int UnusedBits1 : 1; + unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active + unsigned int UnusedBits2 : 3; + unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active + unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active + unsigned int UnusedBits3 : 1; + unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active + unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active + unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception + unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception + unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception + unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception + unsigned int MEMFAULTENA : 1; // Memory management fault handler enable + unsigned int BUSFAULTENA : 1; // Bus fault handler enable + unsigned int USGFAULTENA : 1; // Usage fault handler enable + } bits; + } syshndctrl; // System Handler Control and State Register (0xE000ED24) + + union { + volatile unsigned char byte; + struct { + unsigned char IACCVIOL : 1; // Instruction access violation + unsigned char DACCVIOL : 1; // Data access violation + unsigned char UnusedBits : 1; + unsigned char MUNSTKERR : 1; // Unstacking error + unsigned char MSTKERR : 1; // Stacking error + unsigned char UnusedBits2 : 2; + unsigned char MMARVALID : 1; // Indicates the MMAR is valid + } bits; + } mfsr; // Memory Management Fault Status Register (0xE000ED28) + + union { + volatile unsigned int byte; + struct { + unsigned int IBUSERR : 1; // Instruction access violation + unsigned int PRECISERR : 1; // Precise data access violation + unsigned int IMPREISERR : 1; // Imprecise data access violation + unsigned int UNSTKERR : 1; // Unstacking error + unsigned int STKERR : 1; // Stacking error + unsigned int UnusedBits : 2; + unsigned int BFARVALID : 1; // Indicates BFAR is valid + } bits; + } bfsr; // Bus Fault Status Register (0xE000ED29) + volatile unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38) + + union { + volatile unsigned short byte; + struct { + unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction + unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM) + unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number + unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction + unsigned short UnusedBits : 4; + unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place + unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set) + } bits; + } ufsr; // Usage Fault Status Register (0xE000ED2A) + + union { + volatile unsigned int byte; + struct { + unsigned int UnusedBits : 1; + unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch + unsigned int UnusedBits2 : 28; + unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault + unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event + } bits; + } hfsr; // Hard Fault Status Register (0xE000ED2C) + + union { + volatile unsigned int byte; + struct { + unsigned int HALTED : 1; // Halt requested in NVIC + unsigned int BKPT : 1; // BKPT instruction executed + unsigned int DWTTRAP : 1; // DWT match occurred + unsigned int VCATCH : 1; // Vector fetch occurred + unsigned int EXTERNAL : 1; // EDBGRQ signal asserted + } bits; + } dfsr; // Debug Fault Status Register (0xE000ED30) + + volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional) +} HardFaultRegs; +#endif + +/********************************************************************* +* +* Global functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* HardFaultHandler() +* +* Function description +* C part of the hard fault handler which is called by the assembler +* function HardFault_Handler +*/ +void HardFaultHandler(unsigned int* pStack) { + // + // In case we received a hard fault because of a breakpoint instruction, we return. + // This may happen when using semihosting for printf outputs and no debugger is connected, + // i.e. when running a "Debug" configuration in release mode. + // + if (NVIC_HFSR & (1u << 31)) { + NVIC_HFSR |= (1u << 31); // Reset Hard Fault status + *(pStack + 6u) += 2u; // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction. + return; // Return to interrupted application + } +#if DEBUG + // + // Read NVIC registers + // + HardFaultRegs.syshndctrl.byte = SYSHND_CTRL; // System Handler Control and State Register + HardFaultRegs.mfsr.byte = NVIC_MFSR; // Memory Fault Status Register + HardFaultRegs.bfsr.byte = NVIC_BFSR; // Bus Fault Status Register + HardFaultRegs.bfar = NVIC_BFAR; // Bus Fault Manage Address Register + HardFaultRegs.ufsr.byte = NVIC_UFSR; // Usage Fault Status Register + HardFaultRegs.hfsr.byte = NVIC_HFSR; // Hard Fault Status Register + HardFaultRegs.dfsr.byte = NVIC_DFSR; // Debug Fault Status Register + HardFaultRegs.afsr = NVIC_AFSR; // Auxiliary Fault Status Register + // + // Halt execution + // If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution. + // + _Continue = 0u; + while (_Continue == 0u) { + } + // + // Read saved registers from the stack. + // + HardFaultRegs.SavedRegs.r0 = pStack[0]; // Register R0 + HardFaultRegs.SavedRegs.r1 = pStack[1]; // Register R1 + HardFaultRegs.SavedRegs.r2 = pStack[2]; // Register R2 + HardFaultRegs.SavedRegs.r3 = pStack[3]; // Register R3 + HardFaultRegs.SavedRegs.r12 = pStack[4]; // Register R12 + HardFaultRegs.SavedRegs.lr = pStack[5]; // Link register LR + HardFaultRegs.SavedRegs.pc = pStack[6]; // Program counter PC + HardFaultRegs.SavedRegs.psr.byte = pStack[7]; // Program status word PSR + // + // Halt execution + // To step out of the HardFaultHandler, change the variable value to != 0. + // + _Continue = 0u; + while (_Continue == 0u) { + } +#else + // + // If this module is included in a release configuration, simply stay in the HardFault handler + // + (void)pStack; + do { + } while (1); +#endif +} + +/*************************** End of file ****************************/ diff --git a/core/embed/trezorhal/boards/board-unix.h b/core/embed/trezorhal/boards/board-unix.h index 21f6b52a8e..89bba0b67c 100644 --- a/core/embed/trezorhal/boards/board-unix.h +++ b/core/embed/trezorhal/boards/board-unix.h @@ -15,6 +15,7 @@ #define USE_SBU 1 #define USE_RGB_COLORS 1 #define USE_BACKLIGHT 1 +#define USE_BLE 1 #endif #ifdef TREZOR_MODEL_1 diff --git a/core/embed/trezorhal/boards/trezor_t3w1_d1.h b/core/embed/trezorhal/boards/trezor_t3w1_d1.h index 1b04df627c..3e9417700f 100644 --- a/core/embed/trezorhal/boards/trezor_t3w1_d1.h +++ b/core/embed/trezorhal/boards/trezor_t3w1_d1.h @@ -14,6 +14,7 @@ #define USE_RGB_COLORS 1 #define USE_BACKLIGHT 1 #define USE_DISP_I8080_16BIT_DW 1 +#define USE_BLE 1 #define DISPLAY_PANEL_INIT_SEQ lhs200kb_if21_init_seq #define DISPLAY_PANEL_ROTATE lhs200kb_if21_rotate diff --git a/core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h b/core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h new file mode 100644 index 0000000000..ab15a81ab6 --- /dev/null +++ b/core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h @@ -0,0 +1,24 @@ +#ifndef _TREZOR_T3W1_H +#define _TREZOR_T3W1_H + +// settings for nrf52 +#define SPI_SCK_PIN 14 +#define SPI_MISO_PIN 15 +#define SPI_MOSI_PIN 16 +#define SPI_SS_PIN 13 + +#define RX_PIN_NUMBER 26 +#define TX_PIN_NUMBER 27 +#define CTS_PIN_NUMBER 6 +#define RTS_PIN_NUMBER 8 + +#define LED_ORANGE_PIN 22 +#define LED_GREEN_PIN 24 +#define BUTTON_USER_PIN 21 + +#define STAY_BLD_PIN 40 +#define GPIO_1_PIN 28 +#define GPIO_2_PIN 29 +#define GPIO_3_PIN 30 + +#endif //_TREZOR_T3W1_H diff --git a/core/embed/trezorhal/stm32f4/ble.c b/core/embed/trezorhal/stm32f4/ble.c new file mode 100644 index 0000000000..72c053fc16 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/ble.c @@ -0,0 +1,421 @@ +/* + * 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 . + */ + +#include STM32_HAL_H +#include TREZOR_BOARD + +#include "ble.h" +#include +#include "ble/int_comm_defs.h" +#include "ble/messages.h" +#include "ble/state.h" +#include "buffers.h" +#include "irq.h" +#include "supervise.h" + +#define SPI_QUEUE_SIZE 16 + +static UART_HandleTypeDef urt; + +static SPI_HandleTypeDef spi = {0}; +static DMA_HandleTypeDef spi_dma = {0}; + +typedef struct { + uint8_t buffer[BLE_PACKET_SIZE]; + bool used; + bool ready; +} spi_buffer_t; + +BUFFER_SECTION spi_buffer_t spi_queue[SPI_QUEUE_SIZE]; +static int head = 0, tail = 0; +static bool overrun = false; +volatile uint16_t overrun_count = 0; +volatile uint16_t msg_cntr = 0; +volatile uint16_t first_overrun_at = 0; + +static uint8_t int_comm_buffer[USB_DATA_SIZE]; +static uint16_t int_comm_msg_len = 0; +static uint8_t int_event_buffer[USB_DATA_SIZE]; +static uint16_t int_event_msg_len = 0; + +void ble_comm_init(void) { + GPIO_InitTypeDef GPIO_InitStructure; + + // synchronization signals + HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_PULLUP; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_PIN_1; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_PIN_12; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); + + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_1_PIN; + HAL_GPIO_Init(GPIO_1_PORT, &GPIO_InitStructure); + + __HAL_RCC_USART1_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + + GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF7_USART1; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + + urt.Init.Mode = UART_MODE_TX_RX; + urt.Init.BaudRate = 1000000; + urt.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS; + urt.Init.OverSampling = UART_OVERSAMPLING_16; + urt.Init.Parity = UART_PARITY_NONE, urt.Init.StopBits = UART_STOPBITS_1; + urt.Init.WordLength = UART_WORDLENGTH_8B; + urt.Instance = USART1; + + HAL_UART_Init(&urt); + + __HAL_RCC_DMA2_CLK_ENABLE(); + __HAL_RCC_SPI1_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Alternate = GPIO_AF5_SPI1; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM; + GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + GPIO_InitStructure.Pin = GPIO_PIN_5; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + // GPIO_InitStructure.Pin = GPIO_PIN_9; + // HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + + spi_dma.Init.Channel = DMA_CHANNEL_3; + spi_dma.Init.Direction = DMA_PERIPH_TO_MEMORY; + spi_dma.Init.PeriphInc = DMA_PINC_DISABLE; + spi_dma.Init.MemInc = DMA_MINC_ENABLE; + spi_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + spi_dma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + spi_dma.Init.Mode = DMA_NORMAL; + spi_dma.Init.Priority = DMA_PRIORITY_LOW; + spi_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + spi_dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + spi_dma.Init.MemBurst = DMA_MBURST_SINGLE; + spi_dma.Init.PeriphBurst = DMA_PBURST_SINGLE; + spi_dma.Instance = DMA2_Stream0; + HAL_DMA_Init(&spi_dma); + + spi.Instance = SPI1; + spi.Init.Mode = SPI_MODE_SLAVE; + spi.Init.Direction = SPI_DIRECTION_2LINES; // rx only? + spi.Init.DataSize = SPI_DATASIZE_8BIT; + spi.Init.CLKPolarity = SPI_POLARITY_LOW; + spi.Init.CLKPhase = SPI_PHASE_1EDGE; + spi.Init.NSS = SPI_NSS_HARD_INPUT; + spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; + spi.Init.FirstBit = SPI_FIRSTBIT_MSB; + spi.Init.TIMode = SPI_TIMODE_DISABLE; + spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; + spi.Init.CRCPolynomial = 0; + spi.hdmarx = &spi_dma; + + spi_dma.Parent = &spi; + + HAL_SPI_Init(&spi); + + set_initialized(false); + + HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE); + spi_queue[0].used = true; + + GPIO_InitStructure.Mode = GPIO_MODE_INPUT; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + GPIO_InitStructure.Alternate = 0; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_2_PIN; + HAL_GPIO_Init(GPIO_2_PORT, &GPIO_InitStructure); + + tail = 0; +} + +void ble_start(void) { + svc_enableIRQ(DMA2_Stream0_IRQn); + memset(spi_queue, 0, sizeof(spi_queue)); + HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE); +} + +void ble_stop(void) { + svc_disableIRQ(DMA2_Stream0_IRQn); + HAL_SPI_DMAStop(&spi); +} + +void ble_comm_send(uint8_t *data, uint32_t len) { + HAL_UART_Transmit(&urt, data, len, 30); +} + +uint32_t ble_comm_receive(uint8_t *data, uint32_t len) { + if (urt.Instance->SR & USART_SR_RXNE) { + HAL_StatusTypeDef result = HAL_UART_Receive(&urt, data, len, 30); + + if (result == HAL_OK) { + return len; + } else { + if (urt.RxXferCount == len) { + return 0; + } + return len - urt.RxXferCount - 1; + } + } + return 0; +} + +void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type) { + uint16_t msg_len = len + OVERHEAD_SIZE; + uint8_t len_hi = msg_len >> 8; + uint8_t len_lo = msg_len & 0xFF; + uint8_t eom = EOM; + + HAL_UART_Transmit(&urt, &message_type, 1, 1); + HAL_UART_Transmit(&urt, &len_hi, 1, 1); + HAL_UART_Transmit(&urt, &len_lo, 1, 1); + + HAL_UART_Transmit(&urt, data, len, 10); + HAL_UART_Transmit(&urt, &eom, 1, 1); +} + +void flush_line(void) { + while (urt.Instance->SR & USART_SR_RXNE) { + (void)urt.Instance->DR; + } +} + +void ble_uart_receive(void) { + if (urt.Instance->SR & USART_SR_RXNE) { + uint8_t init_byte = 0; + + HAL_UART_Receive(&urt, &init_byte, 1, 1); + + if (init_byte == INTERNAL_EVENT || init_byte == INTERNAL_MESSAGE) { + uint8_t len_hi = 0; + uint8_t len_lo = 0; + HAL_UART_Receive(&urt, &len_hi, 1, 1); + HAL_UART_Receive(&urt, &len_lo, 1, 1); + + uint16_t act_len = (len_hi << 8) | len_lo; + + if (act_len > UART_PACKET_SIZE) { + flush_line(); + return; + } + + uint8_t *data = NULL; + uint16_t *len = NULL; + if (init_byte == INTERNAL_EVENT) { + data = int_event_buffer; + len = &int_event_msg_len; + } else if (init_byte == INTERNAL_MESSAGE) { + data = int_comm_buffer; + len = &int_comm_msg_len; + } else { + memset(data, 0, USB_DATA_SIZE); + *len = 0; + flush_line(); + return; + } + + HAL_StatusTypeDef result = + HAL_UART_Receive(&urt, data, act_len - OVERHEAD_SIZE, 5); + + if (result != HAL_OK) { + memset(data, 0, USB_DATA_SIZE); + *len = 0; + flush_line(); + return; + } + + uint8_t eom = 0; + HAL_UART_Receive(&urt, &eom, 1, 1); + + if (eom == EOM) { + *len = act_len - OVERHEAD_SIZE; + } else { + memset(data, 0, USB_DATA_SIZE); + *len = 0; + flush_line(); + } + } else { + flush_line(); + } + } + // +} + +void ble_event_poll() { + ble_uart_receive(); + + if (int_event_msg_len > 0) { + process_poll(int_event_buffer, int_event_msg_len); + memset(int_event_buffer, 0, USB_DATA_SIZE); + int_event_msg_len = 0; + } + + if (!ble_initialized() && !is_ble_dfu_mode() && ble_firmware_running()) { + send_state_request(); + } +} + +bool ble_firmware_running(void) { + return HAL_GPIO_ReadPin(GPIO_2_PORT, GPIO_2_PIN) != 0; +} + +uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) { + ble_uart_receive(); + if (int_event_msg_len > 0) { + memcpy(data, int_event_buffer, + int_event_msg_len > len ? len : int_event_msg_len); + memset(int_event_buffer, 0, USB_DATA_SIZE); + uint32_t res = int_event_msg_len; + int_event_msg_len = 0; + return res; + } + return 0; +} + +uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) { + ble_uart_receive(); + if (int_comm_msg_len > 0) { + memcpy(data, int_comm_buffer, + int_comm_msg_len > len ? len : int_comm_msg_len); + memset(int_comm_buffer, 0, USB_DATA_SIZE); + uint32_t res = int_comm_msg_len; + int_comm_msg_len = 0; + return res; + } + return 0; +} + +bool start_spi_dma(void) { + int tmp_tail = (tail + 1) % SPI_QUEUE_SIZE; + if (spi_queue[tmp_tail].used || spi_queue[tmp_tail].ready) { + overrun = true; + overrun_count++; + if (first_overrun_at == 0) { + first_overrun_at = msg_cntr; + } + return false; + } + spi_queue[tmp_tail].used = true; + HAL_SPI_Receive_DMA(&spi, spi_queue[tmp_tail].buffer, BLE_PACKET_SIZE); + + tail = tmp_tail; + return true; +} + +void DMA2_Stream0_IRQHandler(void) { + IRQ_ENTER(DMA2_Stream0_IRQn); + + HAL_DMA_IRQHandler(&spi_dma); + + IRQ_EXIT(DMA2_Stream0_IRQn); +} + +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { + spi_queue[tail].ready = true; + msg_cntr++; + start_spi_dma(); +} + +uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len) { + svc_disableIRQ(DMA2_Stream0_IRQn); + if (spi_queue[head].ready) { + uint8_t *buffer = (uint8_t *)spi_queue[head].buffer; + memcpy(data, buffer, len > BLE_PACKET_SIZE ? BLE_PACKET_SIZE : len); + + spi_queue[head].used = false; + spi_queue[head].ready = false; + head = (head + 1) % SPI_QUEUE_SIZE; + + if (overrun && start_spi_dma()) { + // overrun was before, need to restart the DMA + overrun = false; + } + + if (data[0] != '?') { + // bad packet, restart the DMA + HAL_SPI_Abort(&spi); + + memset(spi_queue, 0, sizeof(spi_queue)); + head = 0; + tail = 0; + overrun = false; + HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE); + spi_queue[0].used = true; + // todo return error? + svc_enableIRQ(DMA2_Stream0_IRQn); + + return 0; + } + + svc_enableIRQ(DMA2_Stream0_IRQn); + return len > BLE_PACKET_SIZE ? BLE_PACKET_SIZE : len; + } + + svc_enableIRQ(DMA2_Stream0_IRQn); + return 0; +} + +bool ble_reset_to_bootloader(void) { + uint32_t tick_start = 0; + + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); + + HAL_Delay(10); + HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); + + tick_start = HAL_GetTick(); + + while (HAL_GPIO_ReadPin(GPIO_1_PORT, GPIO_1_PIN) == GPIO_PIN_RESET) { + if (HAL_GetTick() - tick_start > 4000) { + return false; + } + } + + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); + + HAL_Delay(1000); + + return true; +} + +bool ble_reset(void) { + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); + HAL_Delay(50); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); + return true; +} diff --git a/core/embed/trezorhal/stm32f4/ble.h b/core/embed/trezorhal/stm32f4/ble.h new file mode 100644 index 0000000000..dce29a7b71 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/ble.h @@ -0,0 +1,30 @@ + +#ifndef __BLE_COMM_H__ +#define __BLE_COMM_H__ + +#include +#include +#include "ble/int_comm_defs.h" + +void ble_comm_init(void); + +void ble_start(void); +void ble_stop(void); + +void ble_comm_send(uint8_t *data, uint32_t len); +uint32_t ble_comm_receive(uint8_t *data, uint32_t len); + +void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type); +uint32_t ble_int_event_receive(uint8_t *data, uint32_t len); +uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len); +uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len); + +void ble_event_poll(void); + +bool ble_firmware_running(void); + +bool ble_reset_to_bootloader(void); + +bool ble_reset(void); + +#endif diff --git a/core/embed/trezorhal/stm32f4/usb.c b/core/embed/trezorhal/stm32f4/usb.c index 0feaac1c1d..14457ae404 100644 --- a/core/embed/trezorhal/stm32f4/usb.c +++ b/core/embed/trezorhal/stm32f4/usb.c @@ -187,6 +187,15 @@ secbool usb_configured(void) { return secfalse; } +secbool usb_configured_now(void) { + const USBD_HandleTypeDef *pdev = &usb_dev_handle; + if (pdev->dev_state == USBD_STATE_SUSPENDED && + pdev->dev_old_state == USBD_STATE_CONFIGURED) { + return sectrue; + } + return secfalse; +} + /* * Utility functions for USB interfaces */ diff --git a/core/embed/trezorhal/unix/ble.c b/core/embed/trezorhal/unix/ble.c new file mode 100644 index 0000000000..42f3c1dbfd --- /dev/null +++ b/core/embed/trezorhal/unix/ble.c @@ -0,0 +1,29 @@ +#include "ble.h" +#include + +static bool firmware_running = true; + +void ble_comm_init(void) {} + +void ble_comm_send(uint8_t *data, uint32_t len) {} + +uint32_t ble_comm_receive(uint8_t *data, uint32_t len) { return 0; } + +void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type) {} +uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) { return 0; } +uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) { return 0; } +uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len) { return 0; } + +void ble_event_poll(void) {} + +bool ble_firmware_running(void) { return firmware_running; } + +bool ble_reset_to_bootloader(void) { + firmware_running = false; + return true; +} + +bool ble_reset(void) { + firmware_running = true; + return true; +} diff --git a/core/embed/trezorhal/unix/ble.h b/core/embed/trezorhal/unix/ble.h new file mode 100644 index 0000000000..e79a3cf4dc --- /dev/null +++ b/core/embed/trezorhal/unix/ble.h @@ -0,0 +1,19 @@ +#include +#include + +void ble_comm_init(void); + +void ble_comm_send(uint8_t *data, uint32_t len); +uint32_t ble_comm_receive(uint8_t *data, uint32_t len); + +void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type); +uint32_t ble_int_event_receive(uint8_t *data, uint32_t len); +uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len); +uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len); + +void ble_event_poll(void); + +bool ble_firmware_running(void); + +bool ble_reset_to_bootloader(void); +bool ble_reset(void); diff --git a/core/embed/trezorhal/usb.h b/core/embed/trezorhal/usb.h index b9b27a4de8..90a197a484 100644 --- a/core/embed/trezorhal/usb.h +++ b/core/embed/trezorhal/usb.h @@ -144,5 +144,6 @@ void usb_deinit(void); void usb_start(void); void usb_stop(void); secbool usb_configured(void); +secbool usb_configured_now(void); #endif diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi index 86b8b9d287..2570c2ef3b 100644 --- a/core/mocks/generated/trezorio/__init__.pyi +++ b/core/mocks/generated/trezorio/__init__.pyi @@ -190,7 +190,7 @@ class WebUSB: """ Sends message using USB WebUSB (device) or UDP (emulator). """ -from . import fatfs, sdcard +from . import fatfs, sdcard, ble POLL_READ: int # wait until interface is readable and return read data POLL_WRITE: int # wait until interface is writable INPUT: int # interface id of unified input events @@ -202,4 +202,5 @@ BUTTON_RELEASED: int # button up event BUTTON_LEFT: int # button number of left button BUTTON_RIGHT: int # button number of right button USB_CHECK: int # interface id for check of USB data connection -WireInterface = Union[HID, WebUSB] +BLE_CHECK: int # interface id for check of BLE data connection +WireInterface = Union[HID, WebUSB, BleInterface] diff --git a/core/mocks/generated/trezorio/ble.pyi b/core/mocks/generated/trezorio/ble.pyi new file mode 100644 index 0000000000..36b30b6820 --- /dev/null +++ b/core/mocks/generated/trezorio/ble.pyi @@ -0,0 +1,43 @@ +from typing import * + + +# extmod/modtrezorio/modtrezorio-ble.h +def update_init(data: bytes, binsize: int) -> int: + """ + Initializes the BLE firmware update + """ + + +# extmod/modtrezorio/modtrezorio-ble.h +def update_chunk(chunk: bytes) -> int: + """ + Writes next chunk of BLE firmware update + """ + + +# extmod/modtrezorio/modtrezorio-ble.h +def write_int(self, msg: bytes) -> int: + """ + Sends internal message to NRF. + """ + + +# extmod/modtrezorio/modtrezorio-ble.h +def write_ext(self, msg: bytes) -> int: + """ + Sends message over BLE + """ + + +# extmod/modtrezorio/modtrezorio-ble.h +def erase_bonds() -> None: + """ + Erases all BLE bonds + """ + + +# extmod/modtrezorio/modtrezorio-ble.h +def disconnect() -> None: + """ + Disconnect BLE + """ diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index 6d5fa8fa7d..91b8aee29d 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -95,6 +95,7 @@ VERSION_MINOR: int """Minor version.""" VERSION_PATCH: int """Patch version.""" +USE_BLE: bool USE_SD_CARD: bool """Whether the hardware supports SD card.""" USE_BACKLIGHT: bool diff --git a/core/site_scons/boards/stm32f4_common.py b/core/site_scons/boards/stm32f4_common.py index 329de344cb..d2571e23f0 100644 --- a/core/site_scons/boards/stm32f4_common.py +++ b/core/site_scons/boards/stm32f4_common.py @@ -31,6 +31,7 @@ def stm32f4_common_files(env, defines, sources, paths): "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_sdram.c", "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c", "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c", + "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c", "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_fmc.c", "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_sdmmc.c", ] diff --git a/core/site_scons/boards/trezor_t3w1_d1.py b/core/site_scons/boards/trezor_t3w1_d1.py index 79eff5a920..faaae92e9e 100644 --- a/core/site_scons/boards/trezor_t3w1_d1.py +++ b/core/site_scons/boards/trezor_t3w1_d1.py @@ -56,7 +56,15 @@ def configure( sources += ["embed/extmod/modtrezorio/ffunicode.c"] features_available.append("sd_card") - if "sd_card" in features_wanted: + if "ble" in features_wanted: + sources += ["embed/trezorhal/stm32f4/ble.c"] + sources += ["embed/lib/ble/dfu.c"] + sources += ["embed/lib/ble/fwu.c"] + sources += ["embed/lib/ble/state.c"] + sources += ["embed/lib/ble/messages.c"] + features_available.append("ble") + + if "ble" in features_wanted or "sd_card" in features_wanted: sources += [ "vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c" ] diff --git a/core/site_scons/tools.py b/core/site_scons/tools.py index a228bcaed1..bb5f22fcb9 100644 --- a/core/site_scons/tools.py +++ b/core/site_scons/tools.py @@ -3,8 +3,6 @@ from __future__ import annotations import subprocess from pathlib import Path -import subprocess - from boards import ( discovery, trezor_1, @@ -59,7 +57,7 @@ def configure_board( elif model_r_version == 10: return trezor_r_v10.configure(env, features_wanted, defines, sources, paths) raise Exception("Unknown model_r_version") - elif model in ('T3W1',): + elif model in ("T3W1",): return trezor_t3w1_d1.configure(env, features_wanted, defines, sources, paths) elif model in ("DISC1",): return discovery.configure(env, features_wanted, defines, sources, paths) @@ -104,14 +102,14 @@ def get_version_int(file): patch = 0 file = PROJECT_ROOT / file - with open(file, 'r') as f: + with open(file, "r") as f: for line in f: - if line.startswith('#define VERSION_MAJOR '): - major = int(line.split('VERSION_MAJOR')[1].strip()) - if line.startswith('#define VERSION_MINOR '): - minor = int(line.split('VERSION_MINOR')[1].strip()) - if line.startswith('#define VERSION_PATCH '): - patch = int(line.split('VERSION_PATCH')[1].strip()) + if line.startswith("#define VERSION_MAJOR "): + major = int(line.split("VERSION_MAJOR")[1].strip()) + if line.startswith("#define VERSION_MINOR "): + minor = int(line.split("VERSION_MINOR")[1].strip()) + if line.startswith("#define VERSION_PATCH "): + patch = int(line.split("VERSION_PATCH")[1].strip()) if major > 99 or minor > 99 or patch > 99: raise Exception("Version number too large") return major * 10000 + minor * 100 + patch diff --git a/core/src/all_modules.py b/core/src/all_modules.py index f67f747be0..6f5f5f062d 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -33,10 +33,14 @@ from trezor import utils all_modules import all_modules +bluetooth +import bluetooth boot import boot main import main +mutex +import mutex session import session typing @@ -319,6 +323,18 @@ apps.management.backup_device import apps.management.backup_device apps.management.backup_types import apps.management.backup_types +apps.management.ble.comparison_request +import apps.management.ble.comparison_request +apps.management.ble.disconnect +import apps.management.ble.disconnect +apps.management.ble.erase_bonds +import apps.management.ble.erase_bonds +apps.management.ble.pairing_request +import apps.management.ble.pairing_request +apps.management.ble.repair_request +import apps.management.ble.repair_request +apps.management.ble.upload_ble_firmware_init +import apps.management.ble.upload_ble_firmware_init apps.management.change_pin import apps.management.change_pin apps.management.change_wipe_code diff --git a/core/src/apps/base.py b/core/src/apps/base.py index 502278e080..1d3c39c68b 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -333,7 +333,7 @@ def set_homescreen() -> None: def lock_device(interrupt_workflow: bool = True) -> None: if config.has_pin(): config.lock() - wire.find_handler = get_pinlocked_handler + wire.common_find_handler.register_find_handler(get_pinlocked_handler) set_homescreen() if interrupt_workflow: workflow.close_others() @@ -344,7 +344,7 @@ def lock_device_if_unlocked() -> None: lock_device(interrupt_workflow=workflow.autolock_interrupts_workflow) -async def unlock_device() -> None: +async def unlock_device(skip_button_request: bool = False) -> None: """Ensure the device is in unlocked state. If the storage is locked, attempt to unlock it. Reset the homescreen and the wire @@ -354,10 +354,13 @@ async def unlock_device() -> None: if not config.is_unlocked(): # verify_user_pin will raise if the PIN was invalid - await verify_user_pin() + await verify_user_pin(skip_button_request=skip_button_request) set_homescreen() - wire.find_handler = workflow_handlers.find_registered_handler + + wire.common_find_handler.register_find_handler( + workflow_handlers.find_registered_handler + ) def get_pinlocked_handler( @@ -413,7 +416,10 @@ def boot() -> None: workflow_handlers.register(msg_type, handler) # type: ignore [cannot be assigned to type] reload_settings_from_storage() + if config.is_unlocked(): - wire.find_handler = workflow_handlers.find_registered_handler + wire.common_find_handler.register_find_handler( + workflow_handlers.find_registered_handler + ) else: - wire.find_handler = get_pinlocked_handler + wire.common_find_handler.register_find_handler(get_pinlocked_handler) diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index fb9ab15194..cb6b8442e9 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -85,6 +85,7 @@ async def verify_user_pin( allow_cancel: bool = True, retry: bool = True, cache_time_ms: int = 0, + skip_button_request: bool = False, ) -> None: # _get_last_unlock_time last_unlock = int.from_bytes( @@ -102,7 +103,12 @@ async def verify_user_pin( if config.has_pin(): from trezor.ui.layouts import request_pin_on_device - pin = await request_pin_on_device(prompt, config.get_pin_rem(), allow_cancel) + pin = await request_pin_on_device( + prompt, + config.get_pin_rem(), + allow_cancel, + skip_button_request=skip_button_request, + ) config.ensure_not_wipe_code(pin) else: pin = "" @@ -116,7 +122,11 @@ async def verify_user_pin( while retry: pin = await request_pin_on_device( # type: ignore ["request_pin_on_device" is possibly unbound] - "Enter PIN", config.get_pin_rem(), allow_cancel, wrong_pin=True + "Enter PIN", + config.get_pin_rem(), + allow_cancel, + wrong_pin=True, + skip_button_request=skip_button_request, ) if config.unlock(pin, salt): _set_last_unlock_time() diff --git a/core/src/apps/management/ble/comparison_request.py b/core/src/apps/management/ble/comparison_request.py new file mode 100644 index 0000000000..d0af14d38e --- /dev/null +++ b/core/src/apps/management/ble/comparison_request.py @@ -0,0 +1,15 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import ComparisonRequest, Success + + +async def comparison_request(msg: ComparisonRequest) -> Success: + from trezor.messages import Success + from trezor.ui.layouts import confirm_action + + await confirm_action( + None, "DO THE NUMBERS MATCH?", description=msg.key.decode("utf-8") + ) + + return Success() diff --git a/core/src/apps/management/ble/disconnect.py b/core/src/apps/management/ble/disconnect.py new file mode 100644 index 0000000000..758d00ea57 --- /dev/null +++ b/core/src/apps/management/ble/disconnect.py @@ -0,0 +1,16 @@ +from trezorio import ble +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import Disconnect, Success + + +async def disconnect(_msg: Disconnect) -> Success: + from trezor.messages import Success + from trezor.ui.layouts import confirm_action + + await confirm_action(None, "DISCONNECT") + + ble.disconnect() + + return Success() diff --git a/core/src/apps/management/ble/erase_bonds.py b/core/src/apps/management/ble/erase_bonds.py new file mode 100644 index 0000000000..37e05df546 --- /dev/null +++ b/core/src/apps/management/ble/erase_bonds.py @@ -0,0 +1,16 @@ +from trezorio import ble +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import EraseBonds, Success + + +async def erase_bonds(_msg: EraseBonds) -> Success: + from trezor.messages import Success + from trezor.ui.layouts import confirm_action + + await confirm_action(None, "ERASE BONDS") + + ble.erase_bonds() + + return Success() diff --git a/core/src/apps/management/ble/pairing_request.py b/core/src/apps/management/ble/pairing_request.py new file mode 100644 index 0000000000..3c563cafd1 --- /dev/null +++ b/core/src/apps/management/ble/pairing_request.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import AuthKey, PairingRequest + + +async def pairing_request(_msg: PairingRequest) -> AuthKey: + from trezor.messages import AuthKey + from trezor.ui.layouts import request_pin_on_device + + pin = await request_pin_on_device("PAIRING", None, True, False, True) + + if len(pin) != 6: + pin = "000000" + + return AuthKey(key=pin.encode()) diff --git a/core/src/apps/management/ble/repair_request.py b/core/src/apps/management/ble/repair_request.py new file mode 100644 index 0000000000..33784c6b3e --- /dev/null +++ b/core/src/apps/management/ble/repair_request.py @@ -0,0 +1,13 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import RepairRequest, Success + + +async def repair_request(_msg: RepairRequest) -> Success: + from trezor.messages import Success + from trezor.ui.layouts import confirm_action + + await confirm_action(None, "RE-PAIR DEVICE") + + return Success() diff --git a/core/src/apps/management/ble/upload_ble_firmware_init.py b/core/src/apps/management/ble/upload_ble_firmware_init.py new file mode 100644 index 0000000000..5d9ce10973 --- /dev/null +++ b/core/src/apps/management/ble/upload_ble_firmware_init.py @@ -0,0 +1,75 @@ +from trezorio import ble +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from trezor.messages import Success, UploadBLEFirmwareChunk, UploadBLEFirmwareInit + + +async def upload_ble_firmware_chunk(msg: UploadBLEFirmwareChunk) -> int: + result = ble.update_chunk(msg.data) + + return result + + +async def upload_ble_firmware_init(msg: UploadBLEFirmwareInit) -> Success: + from trezor.enums import ButtonRequestType + from trezor.messages import ( + Success, + UploadBLEFirmwareChunk, + UploadBLEFirmwareNextChunk, + ) + from trezor.ui.layouts import confirm_action + + await confirm_action( + "confirm_upload_ble_firmware", + "Upload BLE firmware", + "", + "Update BLE FW?\n", + reverse=True, + verb="Confirm", + br_code=ButtonRequestType.Other, + ) + + from trezor.enums import MessageType + from trezor.ui.layouts import progress + from trezor.wire.context import get_context + + ctx = get_context() + + progress_layout = progress.progress("Uploading...") + + upload_progress = 0 + + p = int(1000 * upload_progress / msg.binsize) + progress_layout.report(p) + + res = ble.update_init(msg.init_data, msg.binsize) + + await ctx.write(UploadBLEFirmwareNextChunk(offset=0)) + + if res == 0: + + while True: + + received_msg = await ctx.read( + (MessageType.UploadBLEFirmwareChunk,), + UploadBLEFirmwareChunk, + ) + + result = await upload_ble_firmware_chunk(received_msg) + + upload_progress += len(received_msg.data) + p = int(1000 * upload_progress / msg.binsize) + progress_layout.report(p) + + if result == 0: + result_msg = UploadBLEFirmwareNextChunk(offset=0) + await ctx.write(result_msg) + del (result_msg, received_msg) + else: + del received_msg + break + + progress_layout.report(1000) + + return Success(message="BLE firmware update successful") diff --git a/core/src/apps/webauthn/__init__.py b/core/src/apps/webauthn/__init__.py index 6f45c1b522..44622e7b94 100644 --- a/core/src/apps/webauthn/__init__.py +++ b/core/src/apps/webauthn/__init__.py @@ -1,7 +1,7 @@ -def boot() -> None: +def boot(mutex) -> None: import usb from trezor import loop from .fido2 import handle_reports - loop.schedule(handle_reports(usb.iface_webauthn)) + loop.schedule(handle_reports(usb.iface_webauthn, mutex)) diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py index ad9cc555bf..9821b46386 100644 --- a/core/src/apps/webauthn/fido2.py +++ b/core/src/apps/webauthn/fido2.py @@ -539,7 +539,7 @@ def send_cmd_sync(cmd: Cmd, iface: HID) -> None: seq += 1 -async def handle_reports(iface: HID) -> None: +async def handle_reports(iface: HID, mutex) -> None: dialog_mgr = DialogManager(iface) while True: @@ -547,10 +547,14 @@ async def handle_reports(iface: HID) -> None: req = await _read_cmd(iface) if req is None: continue - if not dialog_mgr.allow_cid(req.cid): + if mutex is not None and mutex.get_busy(iface.iface_num()): resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY) else: - resp = _dispatch_cmd(req, dialog_mgr) + mutex.set_busy(iface.iface_num()) + if not dialog_mgr.allow_cid(req.cid): + resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY) + else: + resp = _dispatch_cmd(req, dialog_mgr) if resp is not None: await send_cmd(resp, iface) except Exception as e: diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index fd184be6e3..c565518e54 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -14,7 +14,7 @@ def register(wire_type: int, handler: Handler[Msg]) -> None: workflow_handlers[wire_type] = handler -def _find_message_handler_module(msg_type: int) -> str: +def _find_message_handler_module(msg_type: int, iface: WireInterface) -> str: """Statically find the appropriate workflow handler. For now, new messages must be registered by hand in the if-elif manner below. @@ -62,6 +62,14 @@ def _find_message_handler_module(msg_type: int) -> str: if utils.USE_OPTIGA and msg_type == MessageType.AuthenticateDevice: return "apps.management.authenticate_device" + if utils.USE_BLE: + if msg_type == MessageType.UploadBLEFirmwareInit: + return "apps.management.ble.upload_ble_firmware_init" + if msg_type == MessageType.EraseBonds: + return "apps.management.ble.erase_bonds" + if msg_type == MessageType.Disconnect: + return "apps.management.ble.disconnect" + # bitcoin if msg_type == MessageType.AuthorizeCoinJoin: return "apps.bitcoin.authorize_coinjoin" @@ -199,7 +207,7 @@ def find_registered_handler(iface: WireInterface, msg_type: int) -> Handler | No return workflow_handlers[msg_type] try: - modname = _find_message_handler_module(msg_type) + modname = _find_message_handler_module(msg_type, iface) handler_name = modname[modname.rfind(".") + 1 :] module = __import__(modname, None, None, (handler_name,), 0) return getattr(module, handler_name) diff --git a/core/src/bluetooth.py b/core/src/bluetooth.py new file mode 100644 index 0000000000..dd916c4cb6 --- /dev/null +++ b/core/src/bluetooth.py @@ -0,0 +1,72 @@ +from trezorio import ble +from typing import TYPE_CHECKING + +from trezor import config + +from apps.base import unlock_device + +if TYPE_CHECKING: + from trezor import protobuf, wire + + +class BleInterfaceInternal: + def iface_num(self) -> int: + return 16 + + def write(self, msg: bytes) -> int: + return ble.write_int(self, msg) + + +class BleInterfaceExternal: + def iface_num(self) -> int: + return 17 + + def write(self, msg: bytes) -> int: + return ble.write_ext(self, msg) + + +def find_ble_int_handler(iface, msg_type) -> wire.Handler | None: + from trezor.enums import MessageType + + modname = None + + if msg_type == MessageType.PairingRequest: + modname = "apps.management.ble.pairing_request" + if msg_type == MessageType.RepairRequest: + modname = "apps.management.ble.repair_request" + if msg_type == MessageType.ComparisonRequest: + modname = "apps.management.ble.comparison_request" + + if modname is not None: + try: + handler_name = modname[modname.rfind(".") + 1 :] + module = __import__(modname, None, None, (handler_name,), 0) + return getattr(module, handler_name) + except ValueError: + return None + + return None + + +def int_find_handler( + iface: wire.WireInterface, msg_type: int +) -> wire.Handler[wire.Msg] | None: + + orig_handler = find_ble_int_handler(iface, msg_type) + + if config.is_unlocked(): + return orig_handler + else: + if orig_handler is None: + return None + + async def wrapper(msg: wire.Msg) -> protobuf.MessageType: + await unlock_device(True) + return await orig_handler(msg) + + return wrapper + + +# interface used for trezor wire protocol +iface_ble_int = BleInterfaceInternal() +iface_ble_ext = BleInterfaceExternal() diff --git a/core/src/mutex.py b/core/src/mutex.py new file mode 100644 index 0000000000..3e6ec6723b --- /dev/null +++ b/core/src/mutex.py @@ -0,0 +1,23 @@ +class Mutex: + def __init__(self): + self.ifaces = [] + self.busy = None + + def add(self, iface_num: int): + if iface_num not in self.ifaces: + self.ifaces.append(iface_num) + + def set_busy(self, iface_num: int): + if iface_num in self.ifaces: + self.busy = iface_num + + def get_busy(self, iface_num: int) -> int: + return ( + iface_num in self.ifaces + and self.busy is not None + and self.busy != iface_num + ) + + def release(self, iface_num: int): + if iface_num == self.busy: + self.busy = None diff --git a/core/src/session.py b/core/src/session.py index 4f161c36ce..0c8a345cdc 100644 --- a/core/src/session.py +++ b/core/src/session.py @@ -1,29 +1,67 @@ # isort: skip_file +from micropython import const +from mutex import Mutex + from trezor import log, loop, utils, wire, workflow import apps.base import usb +_PROTOBUF_BUFFER_SIZE = const(8192) +WIRE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE) + + apps.base.boot() +mutex = Mutex() + if not utils.BITCOIN_ONLY and usb.ENABLE_IFACE_WEBAUTHN: import apps.webauthn - apps.webauthn.boot() + apps.webauthn.boot(mutex) if __debug__: import apps.debug apps.debug.boot() + # run main event loop and specify which screen is the default apps.base.set_homescreen() workflow.start_default() + +mutex.add(usb.iface_wire.iface_num()) + # initialize the wire codec -wire.setup(usb.iface_wire) +wire.setup(usb.iface_wire, WIRE_BUFFER, wire.common_find_handler, mutex=mutex) + if __debug__: - wire.setup(usb.iface_debug, is_debug_session=True) + PROTOBUF_BUFFER_SIZE_DEBUG = 1024 + WIRE_BUFFER_DEBUG = bytearray(PROTOBUF_BUFFER_SIZE_DEBUG) + + mutex.add(usb.iface_debug.iface_num()) + wire.setup( + usb.iface_debug, + WIRE_BUFFER_DEBUG, + wire.common_find_handler, + is_debug_session=True, + ) + +if utils.USE_BLE: + import bluetooth + + BLE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE) + ble_find_handler = wire.MessageHandler() + ble_find_handler.register_find_handler(bluetooth.int_find_handler) + + mutex.add(bluetooth.iface_ble_int.iface_num()) + mutex.add(bluetooth.iface_ble_ext.iface_num()) + wire.setup(bluetooth.iface_ble_int, BLE_BUFFER, ble_find_handler, mutex=mutex) + wire.setup( + bluetooth.iface_ble_ext, BLE_BUFFER, wire.common_find_handler, mutex=mutex + ) + loop.run() diff --git a/core/src/trezor/enums/FailureType.py b/core/src/trezor/enums/FailureType.py index fbb2001e54..625af7a461 100644 --- a/core/src/trezor/enums/FailureType.py +++ b/core/src/trezor/enums/FailureType.py @@ -16,4 +16,5 @@ NotInitialized = 11 PinMismatch = 12 WipeCodeMismatch = 13 InvalidSession = 14 +DeviceIsBusy = 15 FirmwareError = 99 diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index e010a34b12..4ef1637e69 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -54,6 +54,15 @@ FirmwareErase = 6 FirmwareUpload = 7 FirmwareRequest = 8 SelfTest = 32 +UploadBLEFirmwareInit = 8000 +UploadBLEFirmwareNextChunk = 8001 +UploadBLEFirmwareChunk = 8002 +PairingRequest = 8003 +AuthKey = 8004 +RepairRequest = 8005 +EraseBonds = 8006 +Disconnect = 8007 +ComparisonRequest = 8008 GetPublicKey = 11 PublicKey = 12 SignTx = 15 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index d2c31237c9..1c851ab217 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -76,6 +76,15 @@ if TYPE_CHECKING: FirmwareUpload = 7 FirmwareRequest = 8 SelfTest = 32 + UploadBLEFirmwareInit = 8000 + UploadBLEFirmwareNextChunk = 8001 + UploadBLEFirmwareChunk = 8002 + PairingRequest = 8003 + AuthKey = 8004 + RepairRequest = 8005 + EraseBonds = 8006 + Disconnect = 8007 + ComparisonRequest = 8008 GetPublicKey = 11 PublicKey = 12 SignTx = 15 @@ -270,6 +279,7 @@ if TYPE_CHECKING: PinMismatch = 12 WipeCodeMismatch = 13 InvalidSession = 14 + DeviceIsBusy = 15 FirmwareError = 99 class ButtonRequestType(IntEnum): diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index efe61ba805..5c8978111c 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -1188,6 +1188,102 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["TxAckPrevExtraDataWrapper"]: return isinstance(msg, cls) + class UploadBLEFirmwareInit(protobuf.MessageType): + init_data: "bytes" + binsize: "int" + + def __init__( + self, + *, + init_data: "bytes", + binsize: "int", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareInit"]: + return isinstance(msg, cls) + + class UploadBLEFirmwareNextChunk(protobuf.MessageType): + offset: "int" + + def __init__( + self, + *, + offset: "int", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareNextChunk"]: + return isinstance(msg, cls) + + class UploadBLEFirmwareChunk(protobuf.MessageType): + data: "bytes" + + def __init__( + self, + *, + data: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareChunk"]: + return isinstance(msg, cls) + + class EraseBonds(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["EraseBonds"]: + return isinstance(msg, cls) + + class Disconnect(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["Disconnect"]: + return isinstance(msg, cls) + + class PairingRequest(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["PairingRequest"]: + return isinstance(msg, cls) + + class AuthKey(protobuf.MessageType): + key: "bytes" + + def __init__( + self, + *, + key: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["AuthKey"]: + return isinstance(msg, cls) + + class RepairRequest(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["RepairRequest"]: + return isinstance(msg, cls) + + class ComparisonRequest(protobuf.MessageType): + key: "bytes" + + def __init__( + self, + *, + key: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["ComparisonRequest"]: + return isinstance(msg, cls) + class CardanoBlockchainPointerType(protobuf.MessageType): block_index: "int" tx_index: "int" diff --git a/core/src/trezor/ui/layouts/common.py b/core/src/trezor/ui/layouts/common.py index 9aea3dcce0..89412ae01a 100644 --- a/core/src/trezor/ui/layouts/common.py +++ b/core/src/trezor/ui/layouts/common.py @@ -30,12 +30,13 @@ async def button_request( async def interact( layout: LayoutType, - br_type: str, + br_type: str | None, br_code: ButtonRequestType = ButtonRequestType.Other, ) -> Any: - pages = None - if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"] - # We know for certain how many pages the layout will have - pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"] - await button_request(br_type, br_code, pages) + if br_type is not None: + pages = None + if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"] + # We know for certain how many pages the layout will have + pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"] + await button_request(br_type, br_code, pages) return await context.wait(layout) diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index 32ff977607..52cdbce0bc 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -243,7 +243,7 @@ async def raise_if_not_confirmed(a: Awaitable[T], exc: Any = ActionCancelled) -> async def confirm_action( - br_type: str, + br_type: str | None, title: str, action: str | None = None, description: str | None = None, @@ -1237,9 +1237,13 @@ async def request_pin_on_device( attempts_remaining: int | None, allow_cancel: bool, wrong_pin: bool = False, + skip_button_request: bool = False, ) -> str: from trezor.wire import PinCancelled + if not skip_button_request: + await button_request("pin_device", code=ButtonRequestType.PinEntry) + if attempts_remaining is None: subprompt = "" elif attempts_remaining == 1: diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index ebc2900405..41f1f51e6e 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -9,6 +9,7 @@ from trezorutils import ( # noqa: F401 SCM_REVISION, UI_LAYOUT, USE_BACKLIGHT, + USE_BLE, USE_OPTIGA, USE_BUTTON, USE_SD_CARD, diff --git a/core/src/trezor/wire/__init__.py b/core/src/trezor/wire/__init__.py index e18755fe52..7517dbeb9c 100644 --- a/core/src/trezor/wire/__init__.py +++ b/core/src/trezor/wire/__init__.py @@ -49,7 +49,6 @@ from trezor.wire.errors import ActionCancelled, DataError, Error # other packages. from trezor.wire.errors import * # isort:skip # noqa: F401,F403 - if TYPE_CHECKING: from trezorio import WireInterface from typing import Any, Callable, Container, Coroutine, TypeVar @@ -65,9 +64,35 @@ if TYPE_CHECKING: EXPERIMENTAL_ENABLED = False -def setup(iface: WireInterface, is_debug_session: bool = False) -> None: +class MessageHandler: + def __init__(self): + self._find_handler = None + + def find_handler(self, iface: WireInterface, msg_type: int) -> Handler | None: + if self._find_handler is not None: + return self._find_handler(iface, msg_type) + return None + + def register_find_handler(self, handler): + self._find_handler = handler + + +common_find_handler = MessageHandler() + + +def setup( + iface: WireInterface, + buffer: bytearray, + handler: MessageHandler, + is_debug_session: bool = False, + mutex=None, +) -> None: """Initialize the wire stack on passed USB interface.""" - loop.schedule(handle_session(iface, codec_v1.SESSION_ID, is_debug_session)) + loop.schedule( + handle_session( + iface, codec_v1.SESSION_ID, buffer, handler, is_debug_session, mutex + ) + ) def wrap_protobuf_load( @@ -100,7 +125,10 @@ if __debug__: async def _handle_single_message( - ctx: context.Context, msg: codec_v1.Message, use_workflow: bool + ctx: context.Context, + msg: codec_v1.Message, + find_handler: MessageHandler, + use_workflow: bool, ) -> codec_v1.Message | None: """Handle a message that was loaded from USB by the caller. @@ -132,7 +160,9 @@ async def _handle_single_message( res_msg: protobuf.MessageType | None = None # We need to find a handler for this message type. Should not raise. - handler = find_handler(ctx.iface, msg.type) # pylint: disable=assignment-from-none + handler = find_handler.find_handler( + ctx.iface, msg.type + ) # pylint: disable=assignment-from-none if handler is None: # If no handler is found, we can skip decoding and directly @@ -205,13 +235,13 @@ async def _handle_single_message( async def handle_session( - iface: WireInterface, session_id: int, is_debug_session: bool = False + iface: WireInterface, + session_id: int, + ctx_buffer: bytearray, + message_handler: MessageHandler, + is_debug_session: bool = False, + mutex=None, ) -> None: - if __debug__ and is_debug_session: - ctx_buffer = WIRE_BUFFER_DEBUG - else: - ctx_buffer = WIRE_BUFFER - ctx = context.Context(iface, session_id, ctx_buffer) next_msg: codec_v1.Message | None = None @@ -230,6 +260,18 @@ async def handle_session( # wait for a new one coming from the wire. try: msg = await ctx.read_from_wire() + if mutex is not None: + if mutex.get_busy(iface.iface_num()): + await ctx.write( + Failure( + code=FailureType.DeviceIsBusy, + message="Device is busy", + ) + ) + continue + else: + mutex.set_busy(iface.iface_num()) + except codec_v1.CodecError as exc: if __debug__: log.exception(__name__, exc) @@ -243,8 +285,9 @@ async def handle_session( try: next_msg = await _handle_single_message( - ctx, msg, use_workflow=not is_debug_session + ctx, msg, message_handler, not is_debug_session ) + except Exception as exc: # Log and ignore. The session handler can only exit explicitly in the # following finally block. @@ -271,12 +314,6 @@ async def handle_session( log.exception(__name__, exc) -def _find_handler_placeholder(iface: WireInterface, msg_type: int) -> Handler | None: - """Placeholder handler lookup before a proper one is registered.""" - return None - - -find_handler = _find_handler_placeholder AVOID_RESTARTING_FOR: Container[int] = ()