From a8ae9496cbf0d89e63e1c5995af517a60ed1c5f6 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Wed, 7 Jun 2023 13:17:40 +0200 Subject: [PATCH] STM --- common/protob/messages-common.proto | 1 + core/Makefile | 3 + core/SConscript.bootloader | 8 +- core/SConscript.firmware | 3 +- core/SConscript.unix | 1 + core/embed/bootloader/bootui.c | 5 + core/embed/bootloader/bootui.h | 2 + core/embed/bootloader/main.c | 390 ++++++-- core/embed/bootloader/messages.c | 393 ++++---- 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-poll.h | 1 - core/embed/lib/protob_helpers.c | 87 ++ core/embed/lib/protob_helpers.h | 87 ++ core/embed/rust/build.rs | 3 + core/embed/rust/rust_ui.h | 7 +- core/embed/rust/src/trezorhal/ble.rs | 9 + core/embed/rust/src/trezorhal/mod.rs | 2 + .../rust/src/ui/model_tt/bootloader/mod.rs | 86 +- .../src/ui/model_tt/component/keyboard/pin.rs | 5 +- .../rust/src/ui/model_tt/theme/bootloader.rs | 2 + core/embed/rust/trezorhal.h | 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/trezor_t3w1_d1.h | 1 + .../trezorhal/boards/trezor_t3w1_d1_NRF.h | 24 + core/embed/trezorhal/stm32f4/ble_hal.c | 438 +++++++++ core/embed/trezorhal/stm32f4/supervise.c | 8 + core/embed/trezorhal/stm32f4/usb.c | 8 + core/embed/trezorhal/unix/ble.h | 19 + core/embed/trezorhal/usb.h | 1 + core/mocks/generated/trezorutils.pyi | 1 + core/site_scons/boards/stm32f4_common.py | 1 + core/site_scons/tools.py | 16 +- core/src/apps/debug/__init__.py | 2 +- core/src/trezor/enums/FailureType.py | 1 + core/src/trezor/enums/__init__.py | 1 + core/src/trezor/utils.py | 2 +- 43 files changed, 3077 insertions(+), 312 deletions(-) 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_hal.c create mode 100644 core/embed/trezorhal/unix/ble.h 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/core/Makefile b/core/Makefile index 38e03463f6..321b72dbc0 100644 --- a/core/Makefile +++ b/core/Makefile @@ -403,6 +403,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 39e5318991..64c2273001 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -21,7 +21,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"] if DISABLE_OPTIGA and PYOPT == '0': FEATURES_WANTED.remove("optiga") @@ -441,6 +441,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 4984e3db8d..9612fa28e7 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -526,6 +526,7 @@ env.Replace( 'embed/unix', 'embed/trezorhal', 'embed/trezorhal/unix', + 'embed/trezorhal/unix/ble', 'embed/extmod/modtrezorui', 'vendor/micropython', 'vendor/micropython/ports/unix', diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 5794502e70..fbe03afb0f 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -69,10 +69,15 @@ static void format_ver(const char *format, uint32_t version, char *buffer, // boot UI static uint16_t boot_background; +static uint8_t iface; static bool initial_setup = true; void ui_set_initial_setup(bool initial) { initial_setup = initial; } +void ui_set_interface(uint8_t iface_num) { iface = iface_num; } + +void ui_screen_connect(void) { screen_connect(initial_setup, iface); } + void ui_screen_boot(const vendor_header *const vhdr, const image_header *const hdr) { const int show_string = ((vhdr->vtrust & VTRUST_STRING) == 0); diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h index 50df21f8e3..d021bf190d 100644 --- a/core/embed/bootloader/bootui.h +++ b/core/embed/bootloader/bootui.h @@ -41,6 +41,7 @@ void ui_screen_boot_click(void); void ui_click(void); void ui_screen_welcome(void); +void ui_screen_connect(void); uint32_t ui_screen_intro(const vendor_header* const vhdr, const image_header* const hdr, bool fw_ok); @@ -67,6 +68,7 @@ void ui_screen_install_restricted(void); void ui_fadein(void); void ui_fadeout(void); void ui_set_initial_setup(bool initial); +void ui_set_interface(uint8_t iface_num); void ui_screen_boot_empty(bool fading); diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index f9fed5cc0f..0df3a12086 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -51,12 +51,20 @@ #ifdef USE_RGB_LED #include "rgb_led.h" #endif +#ifdef USE_BLE +#include "ble/messages.h" +#include "ble/state.h" +#include "ble_hal.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" @@ -75,7 +83,8 @@ 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); @@ -121,12 +130,111 @@ 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(); +#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 (;;) { @@ -138,107 +246,168 @@ 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) { + ble_comm_start(); + if (!ble_initialize() || !ble_initialized()) { + error_shutdown("INTERNAL ERROR", "BLE NOT AVAILABLE"); + } + + auto_start_advertising(); + + uint8_t buf[BLE_PACKET_SIZE]; + + uint8_t active_iface = 0; + int r = 0; + + for (;;) { + for (;;) { + if ((button_read() & (BTN_EVT_DOWN | BTN_POWER)) == + (BTN_EVT_DOWN | BTN_POWER)) { + start_advertising(false); + } + + 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; + } + ui_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; + } + ui_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; + } + ui_screen_connect(); + 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, + bool show_connect_screen) { + // 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_us(1000); + if (usb_configured_now() == sectrue) { + usb = true; + break; + } + } + + if (usb) { + return bootloader_usb_loop(vhdr, hdr); + } else { + usb_stop(); + usb_deinit(); + ui_set_interface(1); + if (show_connect_screen) { + ui_screen_connect(); + } + return bootloader_ble_loop(vhdr, hdr); + } +#else + return bootloader_usb_loop(vhdr, hdr); +#endif +} + static secbool check_vendor_header_lock(const vendor_header *const vhdr) { uint8_t lock[FLASH_OTP_BLOCK_SIZE]; ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock, @@ -457,6 +626,9 @@ int bootloader_main(void) { #ifdef USE_RGB_LED rgb_led_init(); #endif +#ifdef USE_BLE + ble_comm_init(); +#endif unit_variant_init(); @@ -554,7 +726,8 @@ int bootloader_main(void) { ui_screen_welcome(); // and start the usb loop - switch (bootloader_usb_loop(NULL, NULL)) { + ui_set_interface(0); + switch (bootloader_comm_select(NULL, NULL, false)) { case CONTINUE_TO_FIRMWARE: continue_to_firmware = sectrue; continue_to_firmware_backup = sectrue; @@ -610,8 +783,9 @@ int bootloader_main(void) { } break; case SCREEN_WAIT_FOR_HOST: - screen_connect(auto_upgrade == sectrue); - switch (bootloader_usb_loop(&vhdr, hdr)) { + screen_connect(auto_upgrade == sectrue, 0); + ui_set_interface(1); + switch (bootloader_comm_select(&vhdr, hdr, true)) { case CONTINUE_TO_FIRMWARE: continue_to_firmware = sectrue; continue_to_firmware_backup = sectrue; @@ -646,6 +820,10 @@ int bootloader_main(void) { } } +#ifdef USE_BLE + ble_stop_all_comm(); +#endif + ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup), NULL); if (sectrue == firmware_present) { diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 96c2f3b48b..ccd9583ba3 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -25,6 +25,12 @@ #include "messages.pb.h" #include "boot_internal.h" +#include TREZOR_BOARD + +#ifdef USE_BLE +#include "ble/ble.h" +#include "ble_hal.h" +#endif #include "common.h" #include "flash.h" #include "image.h" @@ -36,6 +42,7 @@ #include "bootui.h" #include "messages.h" +#include "protob_helpers.h" #include "rust_ui.h" #include "memzero.h" @@ -49,30 +56,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 @@ -92,9 +78,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); @@ -106,7 +103,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 @@ -114,94 +111,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++) { @@ -221,16 +177,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 @@ -240,10 +234,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; @@ -253,43 +256,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, @@ -316,19 +300,70 @@ static void send_msg_features(uint8_t iface_num, MSG_SEND_ASSIGN_VALUE(unit_color, unit_variant_get_color()); MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_variant_get_btconly()); } - #if USE_OPTIGA 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); } @@ -336,17 +371,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; @@ -362,7 +397,7 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, erase_offset = 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) && @@ -375,13 +410,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); } } @@ -501,13 +536,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; } @@ -522,7 +557,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; } @@ -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 vendor header signature"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; } @@ -543,7 +578,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; } @@ -551,7 +586,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; } @@ -560,7 +595,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; } @@ -611,7 +646,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 mismatch"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_FIRMWARE_MISMATCH; } @@ -621,7 +656,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, "Not a firmware upgrade"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE; } @@ -629,7 +664,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, "Not a full-trust image"); - MSG_SEND(Failure); + MSG_SEND_BLD(Failure); return UPLOAD_ERR_NOT_FULLTRUST_IMAGE; } @@ -681,7 +716,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; @@ -697,7 +732,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; } @@ -709,14 +744,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; } @@ -784,15 +819,27 @@ 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; } secbool bootloader_WipeDevice(void) { +#ifdef USE_BLE + if (!ble_firmware_running()) { + return secfalse; + } + stop_advertising(); + send_erase_bonds(); + + if (!wait_for_answer()) { + return secfalse; + } +#endif + return flash_area_erase(&WIPE_AREA, ui_screen_wipe_progress); } @@ -802,11 +849,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; } } @@ -831,7 +878,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 1b69508634..078aee8b16 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 @@ -78,4 +80,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-poll.h b/core/embed/extmod/modtrezorio/modtrezorio-poll.h index 9bf42b03d6..346ba72c24 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-poll.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-poll.h @@ -155,7 +155,6 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, return mp_const_true; } } -#endif #ifdef USE_BLE else if (iface == BLE_EVENTS_IFACE) { ble_event_poll(); 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/build.rs b/core/embed/rust/build.rs index ecbcad264f..ba03d1d191 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -369,6 +369,9 @@ fn generate_trezorhal_bindings() { .no_copy("buffer_jpeg_work_t") .no_copy("buffer_blurring_t") .no_copy("buffer_blurring_totals_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 5f2ff97e1c..1238bb245f 100644 --- a/core/embed/rust/rust_ui.h +++ b/core/embed/rust/rust_ui.h @@ -15,7 +15,7 @@ uint32_t screen_intro(const char* bld_version_str, const char* vendor_str, uint8_t vendor_str_len, const char* version_str, bool fw_ok); uint32_t screen_menu(secbool firmware_present); -void screen_connect(bool initial_setup); +void screen_connect(bool initial_setup, uint8_t iface); void screen_fatal_error_rust(const char* title, const char* msg, const char* footer); void screen_wipe_success(void); @@ -28,6 +28,11 @@ void screen_boot_empty(bool fading); void screen_boot_full(void); 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 f57cbf2660..5e5492e3d6 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs @@ -11,7 +11,7 @@ use crate::{ bootloader::{connect::Connect, welcome::Welcome}, component::{ bl_confirm::{Confirm, ConfirmTitle}, - Button, ResultScreen, WelcomeScreen, + Button, PinKeyboard, ResultScreen, WelcomeScreen, }, constant, theme::{ @@ -28,6 +28,7 @@ use crate::{ util::{from_c_array, from_c_str}, }, }; +use core::slice; use heapless::String; use num_traits::ToPrimitive; @@ -36,7 +37,13 @@ pub mod intro; pub mod menu; pub mod welcome; -use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK}; +use crate::{ + trezorhal::secbool::secbool, + ui::{ + geometry::Alignment, + model_tt::theme::{bootloader::text_title, BLACK}, + }, +}; use intro::Intro; use menu::Menu; @@ -71,13 +78,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(); display::refresh(); @@ -187,7 +197,7 @@ extern "C" fn screen_install_confirm( frame = frame.with_alert(alert); } - run(&mut frame) + run(&mut frame, false) } #[no_mangle] @@ -206,12 +216,12 @@ extern "C" fn screen_wipe_confirm() -> u32 { let mut frame = Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg).with_alert(alert); - 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), false) } #[no_mangle] @@ -238,7 +248,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( @@ -297,9 +307,14 @@ extern "C" fn screen_wipe_progress(progress: u16, initialize: bool) { } #[no_mangle] -extern "C" fn screen_connect(initial_setup: bool) { +extern "C" fn screen_connect(initial_setup: bool, iface: u8) { let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG }; - let mut frame = Connect::new("Waiting for host...", bg); + + let mut frame = if iface == 0 { + Connect::new("Waiting for host...(USB)", bg) + } else { + Connect::new("Waiting for host...(BLE)", bg) + }; show(&mut frame, true); } @@ -421,3 +436,56 @@ 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, + text_title(BLD_BG), + ) + .vertically_centered(); + + let mut frame = Confirm::new( + BLD_BG, + left, + right, + ConfirmTitle::Text(title), + Label::new(code, Alignment::Center, TEXT_NORMAL), + ); + + 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, text_title(BLD_BG)).vertically_centered(); + + let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg); + + run(&mut frame, true) +} 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/src/ui/model_tt/theme/bootloader.rs b/core/embed/rust/src/ui/model_tt/theme/bootloader.rs index cfc47a793d..871351faa8 100644 --- a/core/embed/rust/src/ui/model_tt/theme/bootloader.rs +++ b/core/embed/rust/src/ui/model_tt/theme/bootloader.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 fn text_title(bg: Color) -> TextStyle { TextStyle::new( Font::BOLD, 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/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/trezor_t3w1_d1.h b/core/embed/trezorhal/boards/trezor_t3w1_d1.h index 7c5fbfc36e..2abefa6b9c 100644 --- a/core/embed/trezorhal/boards/trezor_t3w1_d1.h +++ b/core/embed/trezorhal/boards/trezor_t3w1_d1.h @@ -12,6 +12,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_hal.c b/core/embed/trezorhal/stm32f4/ble_hal.c new file mode 100644 index 0000000000..e7338d45b5 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/ble_hal.c @@ -0,0 +1,438 @@ +/* + * 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_hal.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; + +static bool g_comm_running = false; + +void ble_comm_init(void) { + GPIO_InitTypeDef GPIO_InitStructure; + + // synchronization signals + 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); + + 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); + + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStructure.Pull = GPIO_PULLDOWN; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_3_PIN; + HAL_GPIO_Init(GPIO_3_PORT, &GPIO_InitStructure); + + __HAL_RCC_USART1_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); + + tail = 0; +} + +void ble_comm_start(void) { + memset(spi_queue, 0, sizeof(spi_queue)); + head = 0, tail = 0; + overrun = false; + svc_enableIRQ(DMA2_Stream0_IRQn); + HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE); + spi_queue[0].used = true; + g_comm_running = true; + ble_signal_running(); +} + +void ble_comm_stop(void) { + ble_signal_off(); + g_comm_running = false; + svc_disableIRQ(DMA2_Stream0_IRQn); + HAL_SPI_Abort(&spi); + memset(spi_queue, 0, sizeof(spi_queue)); +} + +bool ble_comm_running(void) { return g_comm_running; } + +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; + uint8_t cmd = INTERNAL_CMD_ACK; + ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT); + 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; +} + +void ble_signal_running(void) { + HAL_GPIO_WritePin(GPIO_3_PORT, GPIO_3_PIN, GPIO_PIN_SET); +} + +void ble_signal_off(void) { + HAL_GPIO_WritePin(GPIO_3_PORT, GPIO_3_PIN, GPIO_PIN_RESET); +} diff --git a/core/embed/trezorhal/stm32f4/supervise.c b/core/embed/trezorhal/stm32f4/supervise.c index 0a52028e6d..23a3151c68 100644 --- a/core/embed/trezorhal/stm32f4/supervise.c +++ b/core/embed/trezorhal/stm32f4/supervise.c @@ -5,6 +5,10 @@ #include "../mpu.h" #include "common.h" #include "supervise.h" +#include TREZOR_BOARD +#ifdef USE_BLE +#include "ble/ble.h" +#endif #ifdef ARM_USER_MODE @@ -72,6 +76,10 @@ void SVC_C_Handler(uint32_t *stack) { ; break; case SVC_REBOOT_TO_BOOTLOADER: +#ifdef USE_BLE + ble_stop_all_comm(); + // TODO: make sure that no answer is pending from NRF +#endif ensure_compatible_settings(); __asm__ volatile("msr control, %0" ::"r"(0x0)); diff --git a/core/embed/trezorhal/stm32f4/usb.c b/core/embed/trezorhal/stm32f4/usb.c index 0feaac1c1d..6588652e42 100644 --- a/core/embed/trezorhal/stm32f4/usb.c +++ b/core/embed/trezorhal/stm32f4/usb.c @@ -187,6 +187,14 @@ secbool usb_configured(void) { return secfalse; } +secbool usb_configured_now(void) { + const USBD_HandleTypeDef *pdev = &usb_dev_handle; + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + return sectrue; + } + return secfalse; +} + /* * Utility functions for USB interfaces */ 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/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index bc93b63dde..b5f561d481 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -115,6 +115,7 @@ VERSION_PATCH: int USE_BLE: bool USE_SD_CARD: bool """Whether the hardware supports SD card.""" +USE_BACKLIGHT: bool USE_TOUCH: bool USE_BUTTON: bool USE_BACKLIGHT: bool diff --git a/core/site_scons/boards/stm32f4_common.py b/core/site_scons/boards/stm32f4_common.py index b47e45c8f3..f9526e36a0 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/tools.py b/core/site_scons/tools.py index eb98303fbb..e61ac7ca94 100644 --- a/core/site_scons/tools.py +++ b/core/site_scons/tools.py @@ -4,8 +4,6 @@ import subprocess import zlib from pathlib import Path -import subprocess - from boards import ( discovery, trezor_1, @@ -105,14 +103,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/apps/debug/__init__.py b/core/src/apps/debug/__init__.py index 3b9174f680..3e0fd508c6 100644 --- a/core/src/apps/debug/__init__.py +++ b/core/src/apps/debug/__init__.py @@ -9,7 +9,7 @@ if __debug__: import trezorui2 from storage import debug as storage from storage.debug import debug_events - from trezor import log, loop, utils, wire + from trezor import log, loop, wire from trezor.enums import MessageType from trezor.messages import DebugLinkLayout, Success from trezor.ui import display 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/__init__.py b/core/src/trezor/enums/__init__.py index 0a8e6c42d3..38decef487 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -286,6 +286,7 @@ if TYPE_CHECKING: PinMismatch = 12 WipeCodeMismatch = 13 InvalidSession = 14 + DeviceIsBusy = 15 FirmwareError = 99 class ButtonRequestType(IntEnum): diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index 7f7ab193ab..be6846a4d5 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -10,8 +10,8 @@ from trezorutils import ( # noqa: F401 UI_LAYOUT, USE_BACKLIGHT, USE_BLE, - USE_OPTIGA, USE_BUTTON, + USE_OPTIGA, USE_SD_CARD, USE_TOUCH, VERSION_MAJOR,