tychovrahe/T3W1/devkit1_with_ble
tychovrahe 1 year ago
parent d13fe73164
commit d85bbf65aa

@ -0,0 +1,101 @@
syntax = "proto2";
package hw.trezor.messages.ble;
// Sugar for easier handling in Java
option java_package = "com.satoshilabs.trezor.lib.protobuf";
option java_outer_classname = "TrezorMessageBLE";
option (include_in_bitcoin_only) = true;
import "messages.proto";
/**
* Request: initializes upload of a new ble firmware im
* @start
* @next UploadBLEFirmwareNextChunk
* @next Failure
*/
message UploadBLEFirmwareInit {
required bytes init_data = 1;
required uint32 binsize = 2;
}
/**
* Response: Requests next chunk of a new ble firmware im
* @next UploadBLEFirmwareChunk
*/
message UploadBLEFirmwareNextChunk {
required uint32 offset = 1;
}
/**
* Request: sends next chunk of a new ble firmware im
* @next UploadBLEFirmwareNextChunk
* @next Success
* @next Failure
*/
message UploadBLEFirmwareChunk {
required bytes data = 1;
}
/**
* Request: erases all BLE bonds
* @start
* @next Success
* @next Failure
*/
message EraseBonds {
}
/**
* Request: disconnect
* @start
* @next Success
* @next Failure
*/
message Disconnect {
}
/**
* Request: initiates new pairing request
* @start
* @next AuthKey
* @next Failure
*/
message PairingRequest {
}
/**
* Request: confirms pairing request with auth key entered on the device
* @end
*/
message AuthKey {
required bytes key = 1;
}
/**
* Request: initiates repairing request
* @start
* @next Success
* @next Failure
*/
message RepairRequest {
}
/**
* Request: initiates new pairing request with numeric comparison
* @start
* @next Success
* @next Failure
*/
message ComparisonRequest {
required bytes key = 1;
}

@ -39,6 +39,7 @@ message Failure {
Failure_PinMismatch = 12;
Failure_WipeCodeMismatch = 13;
Failure_InvalidSession = 14;
Failure_DeviceIsBusy = 15;
Failure_FirmwareError = 99;
}
}

@ -138,6 +138,17 @@ enum MessageType {
MessageType_FirmwareRequest = 8 [(bitcoin_only) = true, (wire_out) = true, (wire_bootloader) = true];
MessageType_SelfTest = 32 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true];
// BLE
MessageType_UploadBLEFirmwareInit = 8000 [(bitcoin_only) = true, (wire_in) = true];
MessageType_UploadBLEFirmwareNextChunk = 8001 [(bitcoin_only) = true, (wire_out) = true];
MessageType_UploadBLEFirmwareChunk = 8002 [(bitcoin_only) = true, (wire_in) = true];
MessageType_PairingRequest = 8003 [(bitcoin_only) = true, (wire_in) = true];
MessageType_AuthKey = 8004 [(bitcoin_only) = true, (wire_out) = true];
MessageType_RepairRequest = 8005 [(bitcoin_only) = true, (wire_in) = true];
MessageType_EraseBonds = 8006 [(bitcoin_only) = true, (wire_in) = true];
MessageType_Disconnect = 8007 [(bitcoin_only) = true, (wire_in) = true];
MessageType_ComparisonRequest = 8008 [(bitcoin_only) = true, (wire_in) = true];
// Bitcoin
MessageType_GetPublicKey = 11 [(bitcoin_only) = true, (wire_in) = true];
MessageType_PublicKey = 12 [(bitcoin_only) = true, (wire_out) = true];

@ -395,6 +395,9 @@ upload: ## upload firmware using trezorctl
upload_prodtest: ## upload prodtest using trezorctl
trezorctl firmware_update -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

@ -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',

@ -19,7 +19,7 @@ FEATURE_FLAGS = {
"SYSTEM_VIEW": False,
}
FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga"]
FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb", "optiga", "ble"]
CCFLAGS_MOD = ''
CPPPATH_MOD = []
@ -432,6 +432,7 @@ env.Replace(
'embed/firmware',
'embed/lib',
'embed/models',
'embed/firmware/ble',
'embed/trezorhal',
'embed/extmod/modtrezorui',
'vendor/micropython',

@ -368,6 +368,7 @@ SOURCE_MICROPYTHON = [
]
SOURCE_UNIX = [
'embed/trezorhal/unix/ble.c',
'embed/trezorhal/unix/common.c',
'embed/trezorhal/unix/display-unix.c',
'embed/trezorhal/unix/flash.c',
@ -378,6 +379,10 @@ SOURCE_UNIX = [
'embed/unix/main.c',
'embed/unix/main_main.c',
'embed/unix/profile.c',
'embed/lib/ble/state.c',
'embed/lib/ble/fwu.c',
'embed/lib/ble/messages.c',
'embed/lib/ble/dfu.c',
'vendor/micropython/shared/runtime/gchelper_generic.c',
'vendor/micropython/ports/unix/alloc.c',
'vendor/micropython/ports/unix/gccollect.c',
@ -503,6 +508,7 @@ env.Replace(
'embed/unix',
'embed/trezorhal',
'embed/trezorhal/unix',
'embed/trezorhal/unix/ble',
'embed/extmod/modtrezorui',
'vendor/micropython',
'vendor/micropython/ports/unix',
@ -785,6 +791,7 @@ def cargo_build():
features.append('touch')
features.append('button')
features.append('sd_card')
features.append('ble')
if TREZOR_MODEL in ('R', '1'):
features.append('button')

@ -47,12 +47,19 @@
#ifdef USE_RGB_LED
#include "rgb_led.h"
#endif
#ifdef USE_BLE
#include "ble.h"
#include "ble/state.h"
#endif
#include "model.h"
#include "usb.h"
#include "version.h"
#include "bootui.h"
#include "messages.h"
#include "messages.pb.h"
#include "protob_helpers.h"
#include "rust_ui.h"
#include "unit_variant.h"
@ -78,13 +85,12 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
#endif
};
#define USB_IFACE_NUM 0
typedef enum {
CONTINUE = 0,
RETURN = 1,
SHUTDOWN = 2,
} usb_result_t;
NO_RESULT = -1,
} comm_result_t;
static void usb_init_all(secbool usb21_landing) {
usb_dev_info_t dev_info = {
@ -126,12 +132,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;
}
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;
} 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;
}
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;
}
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 (;;) {
@ -143,105 +248,152 @@ 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;
comm_result_t res = process_common_messages(USB_IFACE_NUM, buf, vhdr, hdr);
if (res != NO_RESULT) {
return res;
}
switch (msg_id) {
case MessageType_MessageType_Initialize:
process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr);
}
}
#ifdef USE_BLE
static comm_result_t bootloader_ble_loop(const vendor_header *const vhdr,
const image_header *const hdr) {
start_advertising(true);
uint8_t buf[BLE_PACKET_SIZE];
uint8_t active_iface = 0;
int r = 0;
for (;;) {
for (;;) {
r = ble_ext_comm_receive(buf, sizeof(buf));
if (r == BLE_PACKET_SIZE) {
active_iface = BLE_EXT_IFACE_NUM;
break;
case MessageType_MessageType_Ping:
process_msg_Ping(USB_IFACE_NUM, msg_size, buf);
}
r = ble_int_comm_receive(buf, sizeof(buf));
if (r != 0) {
active_iface = BLE_INT_IFACE_NUM;
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;
}
}
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;
}
}
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;
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;
}
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();
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;
}
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;
}
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;
}
// screen_connect();
// todo - screen connect or timeout?
break;
default:
process_msg_unknown(active_iface, msg_size, buf);
break;
}
if (next) {
break;
}
}
}
if (active_iface == BLE_EXT_IFACE_NUM) {
for (;;) {
if (r == 0) {
r = ble_ext_comm_receive(buf, sizeof(buf));
if (r != BLE_PACKET_SIZE) {
continue;
}
usb_stop();
usb_deinit();
return SHUTDOWN;
} else if (r == UPLOAD_ERR_USER_ABORT) {
hal_delay(100);
usb_stop();
usb_deinit();
return RETURN;
} else if (r == 0) { // last chunk received
ui_screen_install_progress_upload(1000);
ui_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;
}
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;
r = 0;
comm_result_t res =
process_common_messages(active_iface, buf, vhdr, hdr);
if (res != NO_RESULT) {
return res;
}
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;
static comm_result_t bootloader_comm_select(const vendor_header *const vhdr,
const image_header *const hdr) {
// if both are NULL, we don't have a firmware installed
// let's show a webusb landing page in this case
usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
#ifdef USE_BLE
bool usb = false;
for (int i = 0; i < 2000; i++) {
hal_delay(1);
if (usb_configured_now() == sectrue) {
usb = true;
break;
}
}
if (usb) {
return bootloader_usb_loop(vhdr, hdr);
} else {
usb_stop();
usb_deinit();
return bootloader_ble_loop(vhdr, hdr);
}
#else
return bootloader_usb_loop(vhdr, hdr);
#endif
}
secbool check_vendor_header_keys(const vendor_header *const vhdr) {
@ -377,6 +529,9 @@ int bootloader_main(void) {
#ifdef USE_RGB_LED
rgb_led_init();
#endif
#ifdef USE_BLE
ble_comm_init();
#endif
unit_variant_init();
@ -443,7 +598,7 @@ int bootloader_main(void) {
NULL);
// and start the usb loop
if (bootloader_usb_loop(NULL, NULL) != CONTINUE) {
if (bootloader_comm_select(NULL, NULL) != CONTINUE) {
return 1;
}
}
@ -502,7 +657,7 @@ int bootloader_main(void) {
break;
case SCREEN_WAIT_FOR_HOST:
screen_connect();
switch (bootloader_usb_loop(&vhdr, hdr)) {
switch (bootloader_comm_select(&vhdr, hdr)) {
case CONTINUE:
continue_to_firmware = true;
break;

@ -24,6 +24,11 @@
#include <pb_encode.h>
#include "messages.pb.h"
#include TREZOR_BOARD
#ifdef USE_BLE
#include "ble.h"
#endif
#include "common.h"
#include "flash.h"
#include "image.h"
@ -35,6 +40,7 @@
#include "bootui.h"
#include "messages.h"
#include "protob_helpers.h"
#include "rust_ui.h"
#include "memzero.h"
@ -44,30 +50,9 @@
#include "emulator.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
@ -87,9 +72,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);
@ -101,7 +97,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
@ -109,94 +105,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;
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,
},
};
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;
#define BLE_GAP_PASSKEY_LEN 6
/* 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;
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)
typedef struct {
uint8_t iface_num;
uint8_t packet_index;
uint8_t packet_pos;
uint8_t *buf;
} usb_read_state;
static bool _read_authkey(pb_istream_t *stream, const pb_field_t *field,
void **arg) {
uint8_t *key_buffer = (uint8_t *)(*arg);
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++) {
@ -216,16 +171,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
@ -235,10 +228,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;
@ -248,43 +250,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 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)
static void _read_flush(read_state *state) { (void)state; }
#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,
@ -311,14 +294,66 @@ 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());
}
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);
}
@ -326,17 +361,17 @@ void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
const vendor_header *const vhdr,
const image_header *const hdr) {
MSG_RECV_INIT(GetFeatures);
MSG_RECV(GetFeatures);
MSG_RECV_BLD(GetFeatures, iface_num);
send_msg_features(iface_num, vhdr, hdr);
}
void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
MSG_RECV_INIT(Ping);
MSG_RECV(Ping);
MSG_RECV_BLD(Ping, iface_num);
MSG_SEND_INIT(Success);
MSG_SEND_ASSIGN_STRING(message, msg_recv.message);
MSG_SEND(Success);
MSG_SEND_BLD(Success);
}
static uint32_t firmware_remaining, firmware_block, chunk_requested;
@ -348,7 +383,7 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size,
chunk_requested = 0;
MSG_RECV_INIT(FirmwareErase);
MSG_RECV(FirmwareErase);
MSG_RECV_BLD(FirmwareErase, iface_num);
firmware_remaining = msg_recv.has_length ? msg_recv.length : 0;
if ((firmware_remaining > 0) &&
@ -361,13 +396,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);
}
}
@ -479,13 +514,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;
}
@ -500,7 +535,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;
}
@ -508,7 +543,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;
}
@ -521,7 +556,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;
}
@ -529,7 +564,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;
}
@ -538,7 +573,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
MSG_SEND_INIT(Failure);
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature");
MSG_SEND(Failure);
MSG_SEND_BLD(Failure);
return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG;
}
@ -619,7 +654,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;
@ -635,7 +670,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;
}
@ -647,14 +682,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;
}
@ -684,10 +719,10 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
MSG_SEND_INIT(FirmwareRequest);
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
MSG_SEND(FirmwareRequest);
MSG_SEND_BLD(FirmwareRequest);
} else {
MSG_SEND_INIT(Success);
MSG_SEND(Success);
MSG_SEND_BLD(Success);
}
return (int)firmware_remaining;
}
@ -702,11 +737,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;
}
}
@ -731,7 +766,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

@ -25,8 +25,10 @@
#include "secbool.h"
#include TREZOR_BOARD
#define USB_IFACE_NUM 0
#define BLE_INT_IFACE_NUM 16
#define BLE_EXT_IFACE_NUM 17
#define USB_TIMEOUT 500
#define USB_PACKET_SIZE 64
#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2
@ -75,4 +77,11 @@ void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size,
secbool bootloader_WipeDevice(void);
uint32_t process_msg_ComparisonRequest(uint8_t iface_num, uint32_t msg_size,
uint8_t *buf);
uint32_t process_msg_Pairing(uint8_t iface_num, uint32_t msg_size,
uint8_t *buf);
uint32_t process_msg_Repair(uint8_t iface_num, uint32_t msg_size, uint8_t *buf);
#endif

@ -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)

@ -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;
@ -135,8 +155,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
@ -164,6 +184,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}
@ -176,8 +200,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
@ -290,6 +320,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;
@ -302,6 +352,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
@ -316,9 +370,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
@ -327,7 +387,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

@ -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;
}
/**
@ -154,3 +158,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;
}

@ -0,0 +1,151 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ble/dfu.h"
#include "ble/messages.h"
/// package: trezorio.ble
/// def update_init(data: bytes, binsize: int) -> int:
/// """
/// Initializes the BLE firmware update
/// """
STATIC mp_obj_t mod_trezorio_BLE_update_init(mp_obj_t data, mp_obj_t binsize) {
mp_buffer_info_t buffer = {0};
mp_int_t binsize_int = mp_obj_get_int(binsize);
mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ);
ble_set_dfu_mode(true);
dfu_result_t result = dfu_update_init(buffer.buf, buffer.len, binsize_int);
if (result == DFU_NEXT_CHUNK) {
return mp_obj_new_int(0);
} else if (result == DFU_SUCCESS) {
ble_set_dfu_mode(false);
return mp_obj_new_int(1);
} else {
ble_set_dfu_mode(false);
mp_raise_msg(&mp_type_RuntimeError, "Upload failed.");
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_update_init_obj,
mod_trezorio_BLE_update_init);
/// def update_chunk(chunk: bytes) -> int:
/// """
/// Writes next chunk of BLE firmware update
/// """
STATIC mp_obj_t mod_trezorio_BLE_update_chunk(mp_obj_t data) {
mp_buffer_info_t buffer = {0};
mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ);
dfu_result_t result = dfu_update_chunk(buffer.buf, buffer.len);
if (result == DFU_NEXT_CHUNK) {
return mp_obj_new_int(0);
} else if (result == DFU_SUCCESS) {
ble_set_dfu_mode(false);
return mp_obj_new_int(1);
} else {
ble_set_dfu_mode(false);
mp_raise_msg(&mp_type_RuntimeError, "Upload failed.");
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_update_chunk_obj,
mod_trezorio_BLE_update_chunk);
/// def write_int(self, msg: bytes) -> int:
/// """
/// Sends internal message to NRF.
/// """
STATIC mp_obj_t mod_trezorio_BLE_write_int(mp_obj_t self, mp_obj_t msg) {
mp_buffer_info_t buf = {0};
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
ble_int_comm_send(buf.buf, buf.len, INTERNAL_MESSAGE);
return MP_OBJ_NEW_SMALL_INT(buf.len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_int_obj,
mod_trezorio_BLE_write_int);
/// def write_ext(self, msg: bytes) -> int:
/// """
/// Sends message over BLE
/// """
STATIC mp_obj_t mod_trezorio_BLE_write_ext(mp_obj_t self, mp_obj_t msg) {
mp_buffer_info_t buf = {0};
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
ble_int_comm_send(buf.buf, buf.len, EXTERNAL_MESSAGE);
return MP_OBJ_NEW_SMALL_INT(buf.len);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_ext_obj,
mod_trezorio_BLE_write_ext);
/// def erase_bonds() -> None:
/// """
/// Erases all BLE bonds
/// """
STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) {
bool result = send_erase_bonds();
if (result) {
return mp_const_none;
} else {
mp_raise_msg(&mp_type_RuntimeError, "Erase bonds failed.");
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_erase_bonds_obj,
mod_trezorio_BLE_erase_bonds);
/// def disconnect() -> None:
/// """
/// Disconnect BLE
/// """
STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) {
bool result = send_disconnect();
if (result) {
return mp_const_none;
} else {
mp_raise_msg(&mp_type_RuntimeError, "Disconnect failed.");
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_disconnect_obj,
mod_trezorio_BLE_disconnect);
STATIC const mp_rom_map_elem_t mod_trezorio_BLE_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ble)},
{MP_ROM_QSTR(MP_QSTR_update_init),
MP_ROM_PTR(&mod_trezorio_BLE_update_init_obj)},
{MP_ROM_QSTR(MP_QSTR_update_chunk),
MP_ROM_PTR(&mod_trezorio_BLE_update_chunk_obj)},
{MP_ROM_QSTR(MP_QSTR_write_int),
MP_ROM_PTR(&mod_trezorio_BLE_write_int_obj)},
{MP_ROM_QSTR(MP_QSTR_write_ext),
MP_ROM_PTR(&mod_trezorio_BLE_write_ext_obj)},
{MP_ROM_QSTR(MP_QSTR_erase_bonds),
MP_ROM_PTR(&mod_trezorio_BLE_erase_bonds_obj)},
{MP_ROM_QSTR(MP_QSTR_disconnect),
MP_ROM_PTR(&mod_trezorio_BLE_disconnect_obj)},
};
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_BLE_globals,
mod_trezorio_BLE_globals_table);
STATIC const mp_obj_module_t mod_trezorio_BLE_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&mod_trezorio_BLE_globals};

@ -19,15 +19,22 @@
#include <string.h>
#include "ble.h"
#include "ble/int_comm_defs.h"
#include "ble/state.h"
#include "button.h"
#include "common.h"
#include "display.h"
#include "embed/extmod/trezorobj.h"
#define BLE_EVENTS_IFACE (252)
#define USB_DATA_IFACE (253)
#define INPUT_IFACE (255)
#define TOUCH_INPUT_FLAG (0x400000)
#define BUTTON_INPUT_FLAG (0x800000)
#define USB_RW_IFACE_MAX (15) // 0-15 reserved for USB
#define BLE_IFACE_INT (16)
#define BLE_IFACE_EXT (17)
#define POLL_READ (0x0000)
#define POLL_WRITE (0x0100)
@ -146,35 +153,81 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
ret->items[1] = usb_connected ? mp_const_true : mp_const_false;
return mp_const_true;
}
} else if (mode == POLL_READ) {
if (sectrue == usb_hid_can_read(iface)) {
uint8_t buf[64] = {0};
int len = usb_hid_read(iface, buf, sizeof(buf));
if (len > 0) {
}
#ifdef USE_BLE
else if (iface == BLE_EVENTS_IFACE) {
ble_event_poll();
uint8_t connected = ble_connected();
if (connected != ble_connected_previously) {
ble_connected_previously = connected;
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = connected ? mp_const_true : mp_const_false;
return mp_const_true;
}
}
#endif
else if (iface <= USB_RW_IFACE_MAX) {
if (mode == POLL_READ) {
if (sectrue == usb_hid_can_read(iface)) {
uint8_t buf[64] = {0};
int len = usb_hid_read(iface, buf, sizeof(buf));
if (len > 0) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_obj_new_bytes(buf, len);
return mp_const_true;
}
} else if (sectrue == usb_webusb_can_read(iface)) {
uint8_t buf[64] = {0};
int len = usb_webusb_read(iface, buf, sizeof(buf));
if (len > 0) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_obj_new_bytes(buf, len);
return mp_const_true;
}
}
} else if (mode == POLL_WRITE) {
if (sectrue == usb_hid_can_write(iface)) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_obj_new_bytes(buf, len);
ret->items[1] = mp_const_none;
return mp_const_true;
} else if (sectrue == usb_webusb_can_write(iface)) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_const_none;
return mp_const_true;
}
} else if (sectrue == usb_webusb_can_read(iface)) {
}
}
#ifdef USE_BLE
else if (iface == BLE_IFACE_INT) {
if (mode == POLL_READ) {
uint8_t buf[64] = {0};
int len = usb_webusb_read(iface, buf, sizeof(buf));
int len = ble_int_comm_receive(buf, sizeof(buf));
if (len > 0) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_obj_new_bytes(buf, len);
return mp_const_true;
}
}
} else if (mode == POLL_WRITE) {
if (sectrue == usb_hid_can_write(iface)) {
} else if (mode == POLL_WRITE) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_const_none;
return mp_const_true;
} else if (sectrue == usb_webusb_can_write(iface)) {
}
} else if (iface == BLE_IFACE_EXT) {
if (mode == POLL_READ) {
uint8_t buf[BLE_PACKET_SIZE] = {0};
int len = ble_ext_comm_receive(buf, sizeof(buf));
if (len > 0) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_obj_new_bytes(buf, len);
return mp_const_true;
}
} else if (mode == POLL_WRITE) {
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
ret->items[1] = mp_const_none;
return mp_const_true;
}
}
#endif
}
if (mp_hal_ticks_ms() >= deadline) {

@ -34,6 +34,8 @@
// Whether USB data pins were connected on last check (USB configured)
bool usb_connected_previously = true;
uint8_t ble_connected_previously = false;
bool ble_last_internal = false;
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
if (value < minimum || value > maximum) { \
@ -48,6 +50,9 @@ bool usb_connected_previously = true;
#include "modtrezorio-webusb.h"
#include "modtrezorio-usb.h"
// clang-format on
#ifdef USE_BLE
#include "modtrezorio-ble.h"
#endif
#ifdef USE_SBU
#include "modtrezorio-sbu.h"
#endif
@ -57,7 +62,7 @@ bool usb_connected_previously = true;
#endif
/// package: trezorio.__init__
/// from . import fatfs, sdcard
/// from . import fatfs, sdcard, ble
/// POLL_READ: int # wait until interface is readable and return read data
/// POLL_WRITE: int # wait until interface is writable
@ -74,8 +79,9 @@ bool usb_connected_previously = true;
/// BUTTON_RIGHT: int # button number of right button
/// USB_CHECK: int # interface id for check of USB data connection
/// BLE_CHECK: int # interface id for check of BLE data connection
/// WireInterface = Union[HID, WebUSB]
/// WireInterface = Union[HID, WebUSB, BleInterface]
STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio)},
@ -87,6 +93,9 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
#ifdef USE_SD_CARD
{MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)},
{MP_ROM_QSTR(MP_QSTR_sdcard), MP_ROM_PTR(&mod_trezorio_sdcard_module)},
#endif
#ifdef USE_BLE
{MP_ROM_QSTR(MP_QSTR_ble), MP_ROM_PTR(&mod_trezorio_BLE_module)},
#endif
{MP_ROM_QSTR(MP_QSTR_INPUT), MP_ROM_INT(INPUT_IFACE)},
#ifdef USE_TOUCH
@ -118,6 +127,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_ROM_INT(POLL_WRITE)},
{MP_ROM_QSTR(MP_QSTR_USB_CHECK), MP_ROM_INT(USB_DATA_IFACE)},
{MP_ROM_QSTR(MP_QSTR_BLE_CHECK), MP_ROM_INT(BLE_EVENTS_IFACE)},
};
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals,

@ -261,6 +261,7 @@ STATIC mp_obj_str_t mod_trezorutils_revision_obj = {
/// VERSION_MAJOR: int
/// VERSION_MINOR: int
/// VERSION_PATCH: int
/// USE_BLE: bool
/// USE_SD_CARD: bool
/// USE_BACKLIGHT: bool
/// USE_TOUCH: bool
@ -297,6 +298,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
#else
{MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_false},
#endif
#ifdef USE_BLE
{MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_true},
#else
{MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_false},
#endif
#ifdef USE_TOUCH
{MP_ROM_QSTR(MP_QSTR_USE_TOUCH), mp_const_true},
#else

@ -73,6 +73,11 @@
#ifdef USE_OPTIGA
#include "optiga_transport.h"
#endif
#ifdef USE_BLE
#include "ble.h"
#include "ble/dfu.h"
#include "ble/state.h"
#endif
#include "unit_variant.h"
#ifdef SYSTEM_VIEW
@ -164,6 +169,11 @@ int main(void) {
optiga_init();
#endif
#ifdef USE_BLE
dfu_init();
ble_comm_init();
#endif
#if !defined TREZOR_MODEL_1
drop_privileges();
#endif
@ -260,6 +270,10 @@ void SVC_C_Handler(uint32_t *stack) {
;
break;
case SVC_REBOOT_TO_BOOTLOADER:
#ifdef USE_BLE
stop_advertising();
// TODO: make sure that no answer is pending from NRF
#endif
ensure_compatible_settings();
mpu_config_bootloader();
__asm__ volatile("msr control, %0" ::"r"(0x0));

@ -0,0 +1,135 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include TREZOR_BOARD
#include "ble.h"
#include "common.h"
#include "dfu.h"
#include "fwu.h"
static TFwu sFwu;
static uint32_t tick_start = 0;
void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len);
static uint8_t readData(uint8_t *data, int maxLen);
void dfu_init(void) {}
dfu_result_t dfu_update_process(void) {
while (1) {
// Can send 4 chars...
// (On a microcontroller, you'd use the TX Empty interrupt or test a
// register.)
fwuCanSendData(&sFwu, 4);
// Data available? Get up to 4 bytes...
// (On a microcontroller, you'd use the RX Available interrupt or test a
// register.)
uint8_t rxBuf[4];
uint8_t rxLen = readData(rxBuf, 4);
if (rxLen > 0) {
fwuDidReceiveData(&sFwu, rxBuf, rxLen);
}
// Give the firmware update module a timeslot to continue the process.
EFwuProcessStatus status = fwuYield(&sFwu, 0);
if (status == FWU_STATUS_COMPLETION) {
ble_reset();
return DFU_SUCCESS;
}
if (status == FWU_STATUS_FAILURE) {
return DFU_FAIL;
}
if (hal_ticks_ms() - tick_start > 2000) {
return DFU_FAIL;
}
if (fwuIsReadyForChunk(&sFwu)) {
return DFU_NEXT_CHUNK;
}
}
}
dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len) {
sFwu.commandObject = data;
sFwu.commandObjectLen = len;
sFwu.dataObject = NULL;
sFwu.dataObjectLen = binary_len;
sFwu.txFunction = txFunction;
sFwu.responseTimeoutMillisec = 2000;
if (!ble_reset_to_bootloader()) {
return DFU_FAIL;
}
tick_start = hal_ticks_ms();
// Prepare the firmware update process.
fwuInit(&sFwu);
// Start the firmware update process.
fwuExec(&sFwu);
return dfu_update_process();
}
dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len) {
tick_start = hal_ticks_ms();
fwuSendChunk(&sFwu, data, len);
return dfu_update_process();
}
dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len,
uint8_t *binfile, uint32_t binfile_len) {
uint32_t chunk_offset = 0;
uint32_t rem_data = binfile_len;
dfu_result_t res = dfu_update_init(datfile, datfile_len, binfile_len);
while (res == DFU_NEXT_CHUNK) {
// Send the next chunk of the data object.
uint32_t chunk_size = 4096;
if (rem_data < 4096) {
chunk_size = rem_data;
rem_data = 0;
} else {
rem_data -= 4096;
}
res = dfu_update_chunk(&binfile[chunk_offset], chunk_size);
chunk_offset += chunk_size;
}
return res;
}
void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len) {
ble_comm_send(buf, len);
}
static uint8_t readData(uint8_t *data, int maxLen) {
return ble_comm_receive(data, maxLen);
}

@ -0,0 +1,17 @@
#ifndef __DFU_H__
#define __DFU_H__
typedef enum {
DFU_NEXT_CHUNK,
DFU_SUCCESS,
DFU_FAIL,
} dfu_result_t;
void dfu_init(void);
dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len);
dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len);
dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len,
uint8_t *binfile, uint32_t binfile_len);
#endif

@ -0,0 +1,664 @@
//
// fwu.c
// nrf52-dfu
//
// C library for the Nordic firmware update protocol.
//
// Created by Andreas Schweizer on 30.11.2018.
// Copyright © 2018-2019 Classy Code GmbH
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "fwu.h"
#include <stdbool.h>
// TODO too big, split in separate files!
typedef enum {
FWU_PS_IDLE = 0,
FWU_PS_PING = 10,
FWU_PS_RCPT_NOTIF = 20,
FWU_PS_MTU = 30,
FWU_PS_OBJ1_SELECT = 40,
FWU_PS_OBJ1_CREATE = 50,
FWU_PS_OBJ1_WRITE = 60,
FWU_PS_OBJ1_CRC_GET = 70,
FWU_PS_OBJ1_EXECUTE = 80,
FWU_PS_OBJ2_SELECT = 90,
FWU_PS_OBJ2_WAIT_FOR_CHUNK = 91,
FWU_PS_OBJ2_CREATE = 100,
FWU_PS_OBJ2_WRITE = 110,
FWU_PS_OBJ2_CRC_GET = 120,
FWU_PS_OBJ2_EXECUTE = 130,
FWU_PS_FAIL = 254,
FWU_PS_DONE = 255,
} EFwuProcessState;
// Process requests, triggering process state transitions.
typedef enum {
FWU_PR_NONE = 0,
FWU_PR_START = 1,
FWU_PR_RECEIVED_RESPONSE,
FWU_PR_REQUEST_FAILED,
FWU_PR_REQUEST_SENT,
} EFwuProcessRequest;
typedef enum {
FWU_CS_IDLE = 0,
FWU_CS_SEND = 1, // sending data from the private request buffer
FWU_CS_RECEIVE = 2, // receiving data into the private response buffer
FWU_CS_FAIL = 3,
FWU_CS_DONE = 4,
} EFwuCommandState;
// Command requests, triggering command state transitions.
typedef enum {
FWU_CR_NONE = 0,
FWU_CR_SEND = 1,
FWU_CR_SENDONLY = 2,
FWU_CR_EOM_RECEIVED = 3,
FWU_CR_RX_OVERFLOW = 4,
FWU_CR_INVALID_ESCAPE_SEQ,
} EFwuCommandRequest;
#define FWU_EOM 0xC0
#define FWU_RESPONSE_START 0x60
#define FWU_RESPONSE_SUCCESS 0x01
// PING 09 01 C0 -> 60 09 01 01 C0
static uint8_t sPingRequest[] = {0x09, 0x01};
static uint8_t sPingRequestLen = 2;
// SET RECEIPT 02 00 00 C0 -> 60 02 01 C0
static uint8_t sSetReceiptRequest[] = {0x02, 0x00, 0x00};
static uint8_t sSetReceiptRequestLen = 3;
// Get the preferred MTU size on the request.
// GET MTU 07 -> 60 07 01 83 00 C0
static uint8_t sGetMtuRequest[] = {0x07};
static uint8_t sGetMtuRequestLen = 1;
// Triggers the last transferred object of the specified type to be selected
// and queries information (max size, cur offset, cur CRC) about the object.
// If there's no object of the specified type, the object type is still
// selected, CRC and offset are 0 in this case.
// SELECT OBJECT 06 01 C0 -> 60 06 01 00 01 00 00 00 00 00 00 00 00 00 00 C0
static uint8_t sSelectObjectRequest[] = {0x06, 0x01};
static uint8_t sSelectObjectRequestLen = 2;
// Creating a command or data object; the target reserves the space, resets the
// progress since the last Execute command and selects the new object.)
// CREATE OBJECT 01 01 87 00 00 00 C0 -> 60 01 01 C0
static uint8_t sCreateObjectRequest[] = {0x01, 0x01, 0x87, 0x00, 0x00, 0x00};
static uint8_t sCreateObjectRequestLen = 6;
// CRC GET 03 C0 -> 60 03 01 87 00 00 00 38 f4 97 72 C0
static uint8_t sGetCrcRequest[] = {0x03};
static uint8_t sGetCrcRequestLen = 1;
// Execute an object after it has been fully transmitted.
// EXECUTE OBJECT 04 C0 -> 60 04 01 C0
static uint8_t sExecuteObjectRequest[] = {0x04};
static uint8_t sExecuteObjectRequestLen = 1;
static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec);
static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec);
static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu);
// Don't send more than FWU_REQUEST_BUF_SIZE bytes.
// Don't include the EOM.
static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len);
static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode);
// static void fwuDebugPrintStatus(TFwu *fwu, char *msg);
static void updateCrc(TFwu *fwu, uint8_t b);
static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason);
static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes);
static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes);
static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes);
// First function to call to set up the internal state in the FWU structure.
void fwuInit(TFwu *fwu) {
fwu->privateProcessState = FWU_PS_IDLE;
fwu->privateProcessRequest = FWU_PR_NONE;
fwu->privateCommandState = FWU_CS_IDLE;
fwu->processStatus = FWU_STATUS_UNDEFINED;
fwu->responseStatus = FWU_RSP_OK;
}
// Execute the firmware update.
void fwuExec(TFwu *fwu) {
// Start with sending a PING command to the target to see if it's there...
fwu->privateProcessRequest = FWU_PR_START;
}
// Call regularly to allow asynchronous processing to continue.
EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec) {
// Nothing to do if processing has failed or successfully completed...
if (fwu->processStatus == FWU_STATUS_FAILURE ||
fwu->privateProcessState == FWU_PS_FAIL) {
return FWU_STATUS_FAILURE;
} else if (fwu->processStatus == FWU_STATUS_COMPLETION ||
fwu->privateProcessState == FWU_PS_DONE) {
return FWU_STATUS_COMPLETION;
}
// Processing is ongoing, yield to FSMs.
fwuYieldCommandFsm(fwu, elapsedMillisec);
fwuYieldProcessFsm(fwu, elapsedMillisec);
return fwu->processStatus;
}
// Call after data from the target has been received.
void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len) {
while (len > 0) {
if (fwu->privateResponseLen == FWU_RESPONSE_BUF_SIZE) {
fwu->privateCommandRequest = FWU_CR_RX_OVERFLOW;
return;
}
uint8_t c = *bytes++;
if (c == FWU_EOM) {
fwu->privateCommandRequest = FWU_CR_EOM_RECEIVED;
}
if (c == 0xDB) {
fwu->privateResponseEscapeCharacter = 1;
} else {
if (fwu->privateResponseEscapeCharacter) {
fwu->privateResponseEscapeCharacter = 0;
if (c == 0xDC) {
c = 0xC0;
} else if (c == 0xDD) {
c = 0xDB;
} else {
fwu->privateCommandRequest = FWU_CR_INVALID_ESCAPE_SEQ;
return;
}
}
fwu->privateResponseBuf[fwu->privateResponseLen++] = c;
}
len--;
}
}
// Inform the FWU module that it may send maxLen bytes of data to the target.
void fwuCanSendData(TFwu *fwu, uint8_t maxLen) {
fwu->privateSendBufSpace = maxLen;
}
void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len) {
if (fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK &&
fwu->privateDataObjectSize == len) {
fwu->dataObject = buf;
fwu->privateProcessState = FWU_PS_OBJ2_CREATE;
}
}
bool fwuIsReadyForChunk(TFwu *fwu) {
return fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK;
}
static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec) {
uint8_t tmpPrivateProcessRequest = fwu->privateProcessRequest;
fwu->privateProcessRequest = FWU_PR_NONE;
// No processing in final states
if (fwu->privateProcessState == FWU_PS_DONE ||
fwu->privateProcessState == FWU_PS_FAIL) {
return;
}
// Failure handling
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_FAILED) {
fwu->privateProcessState = FWU_PS_FAIL;
fwu->processStatus = FWU_STATUS_FAILURE;
return;
}
// Executing the firmware update process.
switch (fwu->privateProcessState) {
case FWU_PS_IDLE:
if (tmpPrivateProcessRequest == FWU_PR_START) {
// Send a PING and switch to the PING state to wait for the response.
fwuPrepareSendBuffer(fwu, sPingRequest, sPingRequestLen);
fwu->privateProcessState = FWU_PS_PING;
}
break;
// PING: Check if the nRF52 DFU code is listening
case FWU_PS_PING:
// Wait for the PING response, then verify it.
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
// ID match?
if (fwu->privateRequestBuf[1] == fwu->privateResponseBuf[3]) {
// Send a SET_RECEIPT and switch to the corresponding state to wait
// for the response.
fwuPrepareSendBuffer(fwu, sSetReceiptRequest, sSetReceiptRequestLen);
fwu->privateProcessState = FWU_PS_RCPT_NOTIF;
} else {
fwuSignalFailure(fwu, FWU_RSP_PING_ID_MISMATCH);
}
}
break;
// RCPT_NOTIF: Define Receipt settings
case FWU_PS_RCPT_NOTIF:
// Wait for the SET_RECEIPT response.
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
// Send a SET_RECEIPT and switch to the corresponding state to wait for
// the response.
fwuPrepareSendBuffer(fwu, sGetMtuRequest, sGetMtuRequestLen);
fwu->privateProcessState = FWU_PS_MTU;
}
break;
// FWU_PS_MTU: Get maximum transmission unit size
case FWU_PS_MTU:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
fwu->privateMtuSize =
fwuLittleEndianToHost16(&fwu->privateResponseBuf[3]);
// Send a SET_RECEIPT and switch to the corresponding state to wait for
// the response.
sSelectObjectRequest[1] = 0x01; // select object 1 (command object)
fwuPrepareSendBuffer(fwu, sSelectObjectRequest,
sSelectObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ1_SELECT;
}
break;
// FWU_PS_OBJ1_SELECT: Select the INIT command object
case FWU_PS_OBJ1_SELECT:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
uint32_t maxSize = fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
if (maxSize < fwu->commandObjectLen) {
fwuSignalFailure(fwu, FWU_RSP_INIT_COMMAND_TOO_LARGE);
} else {
sCreateObjectRequest[1] = 0x01; // create type 1 object (COMMAND)
fwuHostToLittleEndian32(fwu->commandObjectLen,
&sCreateObjectRequest[2]);
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
sCreateObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ1_CREATE;
}
}
break;
// FWU_PS_OBJ1_CREATE: Create the INIT command object
case FWU_PS_OBJ1_CREATE:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
fwu->privateProcessState = FWU_PS_OBJ1_WRITE;
fwu->privateObjectBuf = fwu->commandObject;
fwu->privateObjectLen = fwu->commandObjectLen;
fwu->privateObjectIx = 0;
fwu->privateObjectCrc = 0xffffffff;
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
}
break;
// FWU_PS_OBJ1_WRITE: Write the INIT command object
case FWU_PS_OBJ1_WRITE:
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) {
// more to send?
if (fwu->privateObjectIx == fwu->privateObjectLen) {
// no - request the CRC of the written data...
fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen);
fwu->privateProcessState = FWU_PS_OBJ1_CRC_GET;
} else {
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
}
}
break;
// FWU_PS_OBJ1_CRC_GET: Checksum verification
case FWU_PS_OBJ1_CRC_GET:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
// uint32_t actualLen =
// fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
uint32_t actualCks =
fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]);
if (actualCks == ~fwu->privateObjectCrc) {
// Checksum is OK; execute the command!
fwuPrepareSendBuffer(fwu, sExecuteObjectRequest,
sExecuteObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ1_EXECUTE;
} else {
fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR);
}
}
break;
case FWU_PS_OBJ1_EXECUTE:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
sSelectObjectRequest[1] = 0x02; // select object 2 (DATA object)
fwu->privateDataObjectOffset = 0; // from the beginning
fwuPrepareSendBuffer(fwu, sSelectObjectRequest,
sSelectObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ2_SELECT;
}
break;
// FWU_PS_OBJ2_SELECT: Select the DATA object
case FWU_PS_OBJ2_SELECT:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
fwu->privateDataObjectMaxSize =
fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
fwu->privateObjectCrc =
0xffffffff; // do it here because it's global for the entire blob
// We'll create and execute multiple data objects, so it's ok if the
// actual size is greater than max size.
fwu->privateDataObjectSize =
(fwu->dataObjectLen -
fwu->privateDataObjectOffset); // nof bytes remaining
if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) {
fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize;
}
sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND)
fwuHostToLittleEndian32(fwu->privateDataObjectSize,
&sCreateObjectRequest[2]);
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
sCreateObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK;
}
break;
case FWU_PS_OBJ2_WAIT_FOR_CHUNK:
break;
// FWU_PS_OBJ2_CREATE: Create the DATA object
case FWU_PS_OBJ2_CREATE:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
fwu->privateProcessState = FWU_PS_OBJ2_WRITE;
fwu->privateObjectBuf = fwu->dataObject;
fwu->privateObjectLen = fwu->privateDataObjectSize;
fwu->privateObjectIx = 0;
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
}
break;
// FWU_PS_OBJ2_WRITE: Write the DATA object
case FWU_PS_OBJ2_WRITE:
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) {
// more to send?
if (fwu->privateObjectIx == fwu->privateObjectLen) {
// no - request the CRC of the written data...
fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen);
fwu->privateProcessState = FWU_PS_OBJ2_CRC_GET;
} else {
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
}
}
break;
// FWU_PS_OBJ2_CRC_GET: Checksum verification
case FWU_PS_OBJ2_CRC_GET:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
// uint32_t actualLen =
// fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
uint32_t actualCks =
fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]);
if (actualCks == ~fwu->privateObjectCrc) {
// Checksum is OK; execute the command!
fwuPrepareSendBuffer(fwu, sExecuteObjectRequest,
sExecuteObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ2_EXECUTE;
} else {
fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR);
}
}
break;
case FWU_PS_OBJ2_EXECUTE:
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
fwu->privateDataObjectOffset += fwu->privateDataObjectSize;
if (fwu->privateDataObjectOffset == fwu->dataObjectLen) {
fwu->privateProcessState = FWU_PS_DONE;
fwu->processStatus = FWU_STATUS_COMPLETION;
} else {
// We'll create and execute multiple data objects, so it's ok if the
// actual size is greater than max size.
fwu->privateDataObjectSize =
(fwu->dataObjectLen -
fwu->privateDataObjectOffset); // nof bytes remaining
if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) {
fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize;
}
sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND)
fwuHostToLittleEndian32(fwu->privateDataObjectSize,
&sCreateObjectRequest[2]);
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
sCreateObjectRequestLen);
fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK;
}
}
break;
default:
fwu->privateProcessState = FWU_PS_FAIL;
break;
}
}
static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec) {
uint8_t toSend;
// Automatically return from final states to IDLE.
if (fwu->privateCommandState == FWU_CS_DONE ||
fwu->privateCommandState == FWU_CS_FAIL) {
fwu->privateCommandState = FWU_CS_IDLE;
}
// Timeout?
if (fwu->privateCommandState != FWU_CS_IDLE) {
if (fwu->privateCommandTimeoutRemainingMillisec < elapsedMillisec) {
fwu->privateCommandTimeoutRemainingMillisec = 0;
} else {
fwu->privateCommandTimeoutRemainingMillisec -= elapsedMillisec;
}
if (fwu->privateCommandTimeoutRemainingMillisec == 0) {
fwuSignalFailure(fwu, FWU_RSP_TIMEOUT);
return;
}
}
// Catch errors
if (fwu->privateCommandRequest == FWU_CR_RX_OVERFLOW) {
fwuSignalFailure(fwu, FWU_RSP_RX_OVERFLOW);
return;
}
if (fwu->privateCommandRequest == FWU_CR_INVALID_ESCAPE_SEQ) {
fwuSignalFailure(fwu, FWU_RSP_RX_INVALID_ESCAPE_SEQ);
return;
}
switch (fwu->privateCommandState) {
case FWU_CS_IDLE:
// Ready and waiting for a transmission request.
if (fwu->privateCommandRequest == FWU_CR_SEND ||
fwu->privateCommandRequest == FWU_CR_SENDONLY) {
fwu->privateCommandSendOnly =
fwu->privateCommandRequest == FWU_CR_SENDONLY ? 1 : 0;
fwu->privateCommandRequest = FWU_CR_NONE;
fwu->privateCommandState = FWU_CS_SEND;
fwu->privateCommandTimeoutRemainingMillisec =
fwu->responseTimeoutMillisec;
}
break;
case FWU_CS_SEND:
// Continue sending data until the entire request has been sent.
toSend = fwu->privateRequestLen - fwu->privateRequestIx;
if (toSend == 0) {
if (fwu->privateCommandSendOnly) {
// This was a fire-and-forget request; we don't expect a response.
fwu->privateProcessRequest = FWU_PR_REQUEST_SENT;
fwu->privateCommandState = FWU_CS_DONE;
} else {
// The request has been sent; wait for response.
fwu->privateCommandState = FWU_CS_RECEIVE;
}
} else if (fwu->privateSendBufSpace > 0) {
uint8_t n = fwu->privateSendBufSpace;
if (n > toSend) {
n = toSend;
}
fwu->txFunction(fwu, &fwu->privateRequestBuf[fwu->privateRequestIx], n);
fwu->privateRequestIx += n;
}
break;
case FWU_CS_RECEIVE:
// Continue receiving data until the end-of-message marker has been
// received.
if (fwu->privateCommandRequest == FWU_CR_EOM_RECEIVED) {
fwu->privateCommandRequest = FWU_CR_NONE;
EFwuResponseStatus responseStatus = fwuTestReceivedPacketValid(fwu);
if (responseStatus == FWU_RSP_OK) {
// Inform the process state machine that command reception has
// completed.
fwu->privateProcessRequest = FWU_PR_RECEIVED_RESPONSE;
fwu->privateCommandState = FWU_CS_DONE;
} else {
fwu->responseStatus = responseStatus;
fwu->privateCommandState = FWU_CS_FAIL;
}
}
break;
default:
fwu->privateCommandState = FWU_CS_FAIL;
break;
}
}
static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu) {
// 60 <cmd> <ok> C0
if (fwu->privateResponseLen < 4) {
return FWU_RSP_TOO_SHORT;
}
if (fwu->privateResponseBuf[0] != FWU_RESPONSE_START) {
return FWU_RSP_START_MARKER_MISSING;
}
if (fwu->privateResponseBuf[1] != fwu->privateRequestBuf[0]) {
return FWU_RSP_REQUEST_REFERENCE_INVALID;
}
if (fwu->privateResponseBuf[2] != FWU_RESPONSE_SUCCESS) {
return FWU_RSP_ERROR_RESPONSE;
}
if (fwu->privateResponseBuf[fwu->privateResponseLen - 1] != FWU_EOM) {
return FWU_RSP_END_MARKER_MISSING;
}
return FWU_RSP_OK;
}
static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode) {
uint16_t bytesTodo = fwu->privateObjectLen - fwu->privateObjectIx;
uint16_t bufSpace = FWU_REQUEST_BUF_SIZE - 2;
uint16_t i;
uint8_t *p = &fwu->privateRequestBuf[0];
*p++ = requestCode;
fwu->privateRequestLen = 2; // including requestCode and FWU_EOM
fwu->privateRequestIx = 0;
if (bytesTodo > 32) {
bytesTodo = 32;
}
for (i = 0; i < bytesTodo && bufSpace >= 2; i++) {
uint8_t b = fwu->privateObjectBuf[fwu->privateObjectIx];
// SLIP escape characters: C0->DBDC, DB->DBDD
if (b == 0xC0 || b == 0xDB) {
*p++ = 0xDB;
*p++ = (b == 0xC0) ? 0xDC : 0xDD;
fwu->privateRequestLen += 2;
bufSpace -= 2;
} else {
*p++ = b;
fwu->privateRequestLen++;
bufSpace--;
}
updateCrc(fwu, b);
fwu->privateObjectIx++;
}
*p = FWU_EOM;
fwu->privateCommandRequest = FWU_CR_SENDONLY;
}
static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len) {
// TODO assert privateCommandState == FWU_CS_IDLE | _DONE | _FAIL
// TODO assert len <= FWU_REQUEST_BUF_SIZE
uint8_t i;
uint8_t *p = &fwu->privateRequestBuf[0];
fwu->privateRequestIx = 0;
fwu->privateRequestLen = len + 1;
fwu->privateResponseLen = 0;
// Copy the data into our internal buffer.
for (i = 0; i < len; i++) {
*p++ = *data++;
}
// Add the end-of-message marker.
*p = FWU_EOM;
// Ready to send!
fwu->privateCommandRequest = FWU_CR_SEND;
}
static void updateCrc(TFwu *fwu, uint8_t b) {
uint8_t i;
uint32_t crc = fwu->privateObjectCrc;
crc ^= b;
for (i = 0; i < 8; i++) {
uint32_t m = (crc & 1) ? 0xffffffff : 0;
crc = (crc >> 1) ^ (0xedb88320u & m);
}
fwu->privateObjectCrc = crc;
}
static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason) {
fwu->responseStatus = reason;
fwu->privateCommandState = FWU_CS_FAIL;
// Signal failure to process state machine
fwu->privateProcessRequest = FWU_PR_REQUEST_FAILED;
}
static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes) {
return bytes[0] | ((uint16_t)bytes[1] << 8);
}
static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes) {
return bytes[0] | ((uint16_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) |
((uint32_t)bytes[3] << 24);
}
static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes) {
uint8_t i;
for (i = 0; i < 4; i++) {
*bytes++ = v & 0xff;
v = v >> 8;
}
}

@ -0,0 +1,128 @@
//
// fwu.h
// nrf52-dfu
//
// C library for the Nordic firmware update protocol.
//
// Created by Andreas Schweizer on 30.11.2018.
// Copyright © 2018-2019 Classy Code GmbH
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#ifndef __FWU_H__
#define __FWU_H__ 1
#include <stdbool.h>
#include <stdint.h>
struct SFwu;
#define FWU_REQUEST_BUF_SIZE 67
#define FWU_RESPONSE_BUF_SIZE 16
typedef enum {
FWU_STATUS_UNDEFINED = 0,
FWU_STATUS_FAILURE = 1,
FWU_STATUS_COMPLETION = 2,
} EFwuProcessStatus;
typedef enum {
FWU_RSP_OK = 0,
FWU_RSP_TOO_SHORT = 1,
FWU_RSP_START_MARKER_MISSING = 2,
FWU_RSP_END_MARKER_MISSING = 3,
FWU_RSP_REQUEST_REFERENCE_INVALID = 4,
FWU_RSP_ERROR_RESPONSE = 5,
FWU_RSP_TIMEOUT = 6,
FWU_RSP_PING_ID_MISMATCH = 7,
FWU_RSP_RX_OVERFLOW = 8,
FWU_RSP_INIT_COMMAND_TOO_LARGE = 9,
FWU_RSP_CHECKSUM_ERROR = 10,
FWU_RSP_DATA_OBJECT_TOO_LARGE = 11,
FWU_RSP_RX_INVALID_ESCAPE_SEQ = 12,
} EFwuResponseStatus;
typedef void (*FTxFunction)(struct SFwu *fwu, uint8_t *buf, uint8_t len);
typedef struct SFwu {
// --- public - define these before calling fwuInit ---
// .dat
uint8_t *commandObject;
uint32_t commandObjectLen;
// .bin
uint8_t *dataObject;
uint32_t dataObjectLen;
// Sending bytes to the target
FTxFunction txFunction;
// Timeout when waiting for a response from the target
uint32_t responseTimeoutMillisec;
// --- public - result codes
// Overall process status code
EFwuProcessStatus processStatus;
// Response status code
EFwuResponseStatus responseStatus;
// --- private, don't modify ---
uint32_t privateDataObjectOffset;
uint32_t privateDataObjectSize;
uint32_t privateDataObjectMaxSize;
uint8_t privateProcessState;
uint8_t privateCommandState;
uint8_t privateCommandSendOnly;
uint32_t privateCommandTimeoutRemainingMillisec;
uint8_t privateRequestBuf[FWU_REQUEST_BUF_SIZE + 1];
uint8_t privateRequestLen;
uint8_t privateRequestIx;
uint8_t privateResponseBuf[FWU_RESPONSE_BUF_SIZE];
uint8_t privateResponseEscapeCharacter;
uint8_t privateResponseLen;
uint32_t privateResponseTimeElapsedMillisec;
uint8_t privateSendBufSpace;
uint8_t privateProcessRequest;
uint8_t privateCommandRequest;
uint16_t privateMtuSize;
// sending a large object buffer
uint8_t *privateObjectBuf;
uint32_t privateObjectLen;
uint32_t privateObjectIx;
uint32_t privateObjectCrc;
} TFwu;
// First function to call to set up the internal state in the FWU structure.
void fwuInit(TFwu *fwu);
// Execute the firmware update.
void fwuExec(TFwu *fwu);
// Call regularly to allow asynchronous processing to continue.
EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec);
// Call after data from the target has been received.
void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len);
// Inform the FWU module that it may send maxLen bytes of data to the target.
void fwuCanSendData(TFwu *fwu, uint8_t maxLen);
// Call to send a chunk of the data object to the target.
void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len);
// Call to check if a chunk of the data object can be sent to the target.
bool fwuIsReadyForChunk(TFwu *fwu);
#endif // __FWU_H__

@ -0,0 +1,31 @@
#ifndef __INT_COMM_DEFS__
#define __INT_COMM_DEFS__
#define BLE_PACKET_SIZE (244)
#define USB_DATA_SIZE (64)
#define COMM_HEADER_SIZE (3)
#define COMM_FOOTER_SIZE (1)
#define OVERHEAD_SIZE (COMM_HEADER_SIZE + COMM_FOOTER_SIZE)
#define UART_PACKET_SIZE (USB_DATA_SIZE + OVERHEAD_SIZE)
#define EOM (0x55)
#define INTERNAL_EVENT (0xA2)
#define EXTERNAL_MESSAGE (0xA1)
#define INTERNAL_MESSAGE (0xA0)
typedef enum {
INTERNAL_EVENT_STATUS = 0x01,
INTERNAL_EVENT_SUCCESS = 0x02,
INTERNAL_EVENT_FAILURE = 0x03,
} InternalEvent_t;
typedef enum {
INTERNAL_CMD_SEND_STATE = 0x00,
INTERNAL_CMD_ADVERTISING_ON = 0x01,
INTERNAL_CMD_ADVERTISING_OFF = 0x02,
INTERNAL_CMD_ERASE_BONDS = 0x03,
INTERNAL_CMD_DISCONNECT = 0x04,
} InternalCmd_t;
#endif

@ -0,0 +1,80 @@
#include <stdint.h>
#include "ble.h"
#include "common.h"
#include "int_comm_defs.h"
#include "messages.h"
void send_state_request(void) {
uint8_t cmd = INTERNAL_CMD_SEND_STATE;
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
}
void send_advertising_on(bool whitelist) {
uint8_t data[2];
data[0] = INTERNAL_CMD_ADVERTISING_ON;
data[1] = whitelist ? 1 : 0;
ble_int_comm_send(data, sizeof(data), INTERNAL_EVENT);
}
void send_advertising_off(void) {
uint8_t cmd = INTERNAL_CMD_ADVERTISING_OFF;
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
}
bool send_erase_bonds(void) {
if (!ble_firmware_running()) {
return false;
}
uint8_t cmd = INTERNAL_CMD_ERASE_BONDS;
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
uint8_t buf[64] = {0};
uint32_t ticks_start = hal_ticks_ms();
int len = 0;
while (len == 0) {
len = ble_int_event_receive(buf, sizeof(buf));
if (hal_ticks_ms() - ticks_start > 1000) {
// timeout
return false;
}
}
if (buf[0] == INTERNAL_EVENT_SUCCESS) {
return true;
}
return false;
}
bool send_disconnect(void) {
if (!ble_firmware_running()) {
return false;
}
uint8_t cmd = INTERNAL_CMD_DISCONNECT;
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
uint8_t buf[64] = {0};
uint32_t ticks_start = hal_ticks_ms();
int len = 0;
while (len == 0) {
len = ble_int_event_receive(buf, sizeof(buf));
if (hal_ticks_ms() - ticks_start > 1000) {
// timeout
return false;
}
}
if (buf[0] == INTERNAL_EVENT_SUCCESS) {
return true;
}
return false;
}

@ -0,0 +1,16 @@
#ifndef __BLE_MESSAGES__
#define __BLE_MESSAGES__
#include <stdbool.h>
void send_state_request(void);
void send_advertising_on(bool whitelist);
void send_advertising_off(void);
bool send_erase_bonds(void);
bool send_disconnect(void);
#endif

@ -0,0 +1,72 @@
#include "ble/state.h"
#include "ble.h"
#include "messages.h"
static bool ble_state_connected = false;
static bool ble_state_initialized = false;
static bool ble_advertising_wanted = false;
static bool ble_advertising_wl_wanted = false;
static bool ble_advertising = false;
static bool ble_advertising_wl = false;
static bool ble_dfu_mode = false;
static uint8_t ble_peer_count = 0;
bool ble_connected(void) {
return ble_state_connected && ble_firmware_running();
}
void set_connected(bool connected) {}
void set_status(bool connected, bool advertising, bool whitelist,
uint8_t count) {
if (ble_state_connected != connected) {
ble_advertising_wanted = count > 0;
ble_advertising_wl_wanted = true;
}
ble_state_connected = connected;
ble_peer_count = count;
if (count > 0 && !ble_initialized()) {
ble_advertising_wanted = true;
ble_advertising_wl_wanted = true;
}
if (ble_advertising_wanted != advertising ||
(ble_advertising_wl_wanted != whitelist)) {
if (ble_advertising_wanted) {
send_advertising_on(ble_advertising_wl_wanted);
}
if (!ble_advertising_wanted && ble_advertising) {
send_advertising_off();
}
}
ble_advertising = advertising;
ble_advertising_wl = whitelist;
}
void set_initialized(bool initialized) { ble_state_initialized = initialized; }
bool ble_initialized(void) {
return ble_state_initialized && ble_firmware_running();
}
void start_advertising(bool whitelist) {
ble_advertising_wl_wanted = whitelist;
ble_advertising_wanted = true;
if (!ble_advertising || ble_advertising_wl != whitelist) {
send_advertising_on(whitelist);
}
}
void stop_advertising(void) {
ble_advertising_wanted = false;
if (ble_advertising) {
send_advertising_off();
}
}
void ble_set_dfu_mode(bool dfu) { ble_dfu_mode = dfu; }
bool is_ble_dfu_mode(void) { return ble_dfu_mode; }

@ -0,0 +1,25 @@
#ifndef __BLE_STATE__
#define __BLE_STATE__
#include <stdbool.h>
#include <stdint.h>
bool ble_initialized(void);
void set_initialized(bool initialized);
bool ble_connected(void);
void set_status(bool connected, bool advertising, bool whitelist,
uint8_t count);
void start_advertising(bool whitelist);
void stop_advertising(void);
void ble_set_dfu_mode(bool dfu);
bool is_ble_dfu_mode(void);
#endif

@ -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;
}

@ -0,0 +1,87 @@
#include <pb_decode.h>
#include <pb_encode.h>
#include <stdbool.h>
#include <stdint.h>
#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);

@ -23,6 +23,7 @@ clippy = []
jpeg = []
disp_i8080_8bit_dw = [] # write pixels directly to peripheral
disp_i8080_16bit_dw = [] # write pixels directly to peripheral
ble = []
debug = ["ui_debug"]
sbu = []
sd_card = []

@ -354,6 +354,9 @@ fn generate_trezorhal_bindings() {
.no_copy("buffer_jpeg_t")
.no_copy("buffer_jpeg_work_t")
.no_copy("buffer_blurring_t")
// ble
.allowlist_function("ble_connected")
.allowlist_function("start_advertising")
//usb
.allowlist_function("usb_configured")
// touch

@ -27,6 +27,11 @@ void screen_welcome(void);
void screen_boot_empty(bool fading);
uint32_t screen_unlock_bootloader_confirm(void);
void screen_unlock_bootloader_success(void);
uint32_t screen_pairing_confirm(uint8_t* buffer);
uint32_t screen_comparison_confirm(uint8_t* code, uint8_t code_len);
uint32_t screen_repair_confirm(void);
void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen);
void display_icon(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen,
uint16_t fg_color, uint16_t bg_color);

@ -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) }
}

@ -1,4 +1,6 @@
pub mod bip39;
#[cfg(feature = "ble")]
pub mod ble;
#[macro_use]
#[allow(unused_macros)]
pub mod fatal_error;

@ -14,17 +14,19 @@ use crate::{
theme::{
button_bld, button_confirm, button_wipe_cancel, button_wipe_confirm, BLD_BG,
BLD_FG, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD32, FIRE32, FIRE40,
TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40,
WELCOME_COLOR, X24,
},
welcome::Welcome,
},
component::{Button, ResultScreen, WelcomeScreen},
component::{Button, PinKeyboard, ResultScreen, WelcomeScreen},
constant,
theme::{BACKLIGHT_DIM, BACKLIGHT_NORMAL, FG, WHITE},
},
util::{from_c_array, from_c_str},
},
};
use core::slice;
use heapless::String;
use num_traits::ToPrimitive;
@ -39,6 +41,7 @@ use crate::ui::model_tt::theme::BLACK;
use confirm::Confirm;
use intro::Intro;
use menu::Menu;
use crate::ui::geometry::Alignment;
use self::theme::{RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE};
@ -73,13 +76,16 @@ fn fadeout() {
display::fade_backlight_duration(BACKLIGHT_DIM, 150);
}
fn run<F>(frame: &mut F) -> u32
fn run<F>(frame: &mut F, clear: bool) -> u32
where
F: Component,
F::Msg: ReturnToC,
{
frame.place(constant::screen());
fadeout();
if clear {
display::rect_fill(screen(), BLACK);
}
display::sync();
frame.paint();
fadein();
@ -190,7 +196,7 @@ extern "C" fn screen_install_confirm(
Some(("FW FINGERPRINT", fingerprint_str)),
);
run(&mut frame)
run(&mut frame, false)
}
#[no_mangle]
@ -216,12 +222,12 @@ extern "C" fn screen_wipe_confirm() -> u32 {
None,
);
run(&mut frame)
run(&mut frame, false)
}
#[no_mangle]
extern "C" fn screen_menu() -> u32 {
run(&mut Menu::new())
run(&mut Menu::new(), false)
}
#[no_mangle]
@ -247,7 +253,7 @@ extern "C" fn screen_intro(
let mut frame = Intro::new(title_str.as_str(), version_str.as_str());
run(&mut frame)
run(&mut frame, false)
}
fn screen_progress(
@ -422,3 +428,65 @@ extern "C" fn bld_continue_label(bg_color: cty::uint16_t) {
Color::from_u16(bg_color),
);
}
#[no_mangle]
extern "C" fn screen_pairing_confirm(buffer: *const cty::uint8_t) -> u32 {
let pin_slice = unsafe { slice::from_raw_parts_mut(buffer as *mut u8, 6) };
let mut pin = PinKeyboard::new("Enter passkey", "", None, true);
let res = run(&mut pin, true);
if res == 2 {
let pin = pin.pin().as_bytes();
if pin.len() == 6 {
pin_slice.copy_from_slice(&pin[0..6]);
}
}
res
}
#[no_mangle]
extern "C" fn screen_comparison_confirm(code: *const cty::uint8_t, code_len: u8) -> u32 {
let code = unwrap!(unsafe { from_c_array(code, code_len as usize) });
let right = Button::with_text("YES").styled(button_confirm());
let left = Button::with_text("CANCEL").styled(button_bld());
let title = Label::new("DO THE NUMBERS MATCH?", Alignment::Start, theme::TEXT_BOLD)
.vertically_centered();
let mut frame = Confirm::new(
BLD_BG,
left,
right,
ConfirmTitle::Text(title),
Label::new(code, Alignment::Center, TEXT_NORMAL),
None,
None,
);
run(&mut frame, true)
}
#[no_mangle]
extern "C" fn screen_repair_confirm() -> u32 {
let msg = Label::new("Allow repair?", Alignment::Center, TEXT_NORMAL);
let right = Button::with_text("ALLOW").styled(button_confirm());
let left = Button::with_text("DENY").styled(button_bld());
let title = Label::new("REPAIR", Alignment::Start, theme::TEXT_BOLD)
.vertically_centered();
let mut frame = Confirm::new(
BLD_BG,
left,
right,
ConfirmTitle::Text(title),
msg,
None,
None,
);
run(&mut frame, true)
}

@ -229,6 +229,8 @@ pub fn button_bld() -> ButtonStyleSheet {
}
}
pub const TEXT_NORMAL_BLACK: TextStyle = TextStyle::new(Font::NORMAL, WHITE, BLACK, WHITE, WHITE);
pub const TEXT_TITLE: TextStyle = TextStyle::new(
Font::BOLD,
BLD_TITLE_COLOR,

@ -19,9 +19,10 @@ use crate::{
},
};
#[derive(Copy, Clone, ToPrimitive)]
pub enum PinKeyboardMsg {
Confirmed,
Cancelled,
Cancelled = 1,
Confirmed = 2,
}
const MAX_LENGTH: usize = 50;

@ -1,4 +1,5 @@
#include TREZOR_BOARD
#include "ble/state.h"
#include "buffers.h"
#include "button.h"
#include "common.h"

@ -123,7 +123,6 @@ static uint32_t nrf_sw_backend_hash_sha256_finalize(void * const p_context,
}
const nrf_crypto_hash_info_t g_nrf_crypto_hash_sha256_info =
{
.init_fn = nrf_sw_backend_hash_sha256_init,

@ -27,6 +27,7 @@ SRC_FILES += \
$(SDK_ROOT)/components/libraries/crc16/crc16.c \
$(SDK_ROOT)/components/libraries/timer/drv_rtc.c \
$(SDK_ROOT)/components/libraries/fds/fds.c \
$(SDK_ROOT)/components/libraries/fds/fds.c \
$(SDK_ROOT)/components/libraries/hardfault/hardfault_implementation.c \
$(SDK_ROOT)/components/libraries/util/nrf_assert.c \
$(SDK_ROOT)/components/libraries/atomic_fifo/nrf_atfifo.c \

@ -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 *************************************************/

@ -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 *************************************************/

@ -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 *************************************************/

@ -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 *************************************************/

@ -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 ****************************/

@ -14,6 +14,7 @@
#define USE_SD_CARD 1
#define USE_SBU 1
#define USE_RGB_COLORS 1
#define USE_BLE 1
#endif
#ifdef TREZOR_MODEL_1

@ -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

@ -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

@ -0,0 +1,428 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include STM32_HAL_H
#include TREZOR_BOARD
#include "ble.h"
#include <string.h>
#include "ble/int_comm_defs.h"
#include "ble/messages.h"
#include "ble/state.h"
#include "buffers.h"
#include "irq.h"
#define SPI_QUEUE_SIZE 16
static UART_HandleTypeDef urt;
static SPI_HandleTypeDef spi = {0};
static DMA_HandleTypeDef spi_dma = {0};
typedef struct {
uint8_t buffer[BLE_PACKET_SIZE];
bool used;
bool ready;
} spi_buffer_t;
BUFFER_SECTION spi_buffer_t spi_queue[SPI_QUEUE_SIZE];
static int head = 0, tail = 0;
static bool overrun = false;
volatile uint16_t overrun_count = 0;
volatile uint16_t msg_cntr = 0;
volatile uint16_t first_overrun_at = 0;
static uint8_t int_comm_buffer[USB_DATA_SIZE];
static uint16_t int_comm_msg_len = 0;
static uint8_t int_event_buffer[USB_DATA_SIZE];
static uint16_t int_event_msg_len = 0;
void ble_comm_init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// synchronization signals
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_PIN_1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_PIN_12;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_1_PIN;
HAL_GPIO_Init(GPIO_1_PORT, &GPIO_InitStructure);
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Alternate = GPIO_AF7_USART1;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
urt.Init.Mode = UART_MODE_TX_RX;
urt.Init.BaudRate = 1000000;
urt.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
urt.Init.OverSampling = UART_OVERSAMPLING_16;
urt.Init.Parity = UART_PARITY_NONE, urt.Init.StopBits = UART_STOPBITS_1;
urt.Init.WordLength = UART_WORDLENGTH_8B;
urt.Instance = USART1;
HAL_UART_Init(&urt);
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_5;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
// GPIO_InitStructure.Pin = GPIO_PIN_9;
// HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
spi_dma.Init.Channel = DMA_CHANNEL_3;
spi_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
spi_dma.Init.PeriphInc = DMA_PINC_DISABLE;
spi_dma.Init.MemInc = DMA_MINC_ENABLE;
spi_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
spi_dma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
spi_dma.Init.Mode = DMA_NORMAL;
spi_dma.Init.Priority = DMA_PRIORITY_LOW;
spi_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
spi_dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
spi_dma.Init.MemBurst = DMA_MBURST_SINGLE;
spi_dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
spi_dma.Instance = DMA2_Stream0;
HAL_DMA_Init(&spi_dma);
spi.Instance = SPI1;
spi.Init.Mode = SPI_MODE_SLAVE;
spi.Init.Direction = SPI_DIRECTION_2LINES; // rx only?
spi.Init.DataSize = SPI_DATASIZE_8BIT;
spi.Init.CLKPolarity = SPI_POLARITY_LOW;
spi.Init.CLKPhase = SPI_PHASE_1EDGE;
spi.Init.NSS = SPI_NSS_HARD_INPUT;
spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
spi.Init.TIMode = SPI_TIMODE_DISABLE;
spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spi.Init.CRCPolynomial = 0;
spi.hdmarx = &spi_dma;
spi_dma.Parent = &spi;
HAL_SPI_Init(&spi);
set_initialized(false);
HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE);
spi_queue[0].used = true;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Alternate = 0;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GPIO_2_PIN;
HAL_GPIO_Init(GPIO_2_PORT, &GPIO_InitStructure);
tail = 0;
}
void ble_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 process_poll(uint8_t *data, uint32_t len) {
uint8_t cmd = data[0];
switch (cmd) {
// case INTERNAL_EVENT_INITIALIZED: {
// set_connected(false);
// set_initialized(true);
// break;
// }
case INTERNAL_EVENT_STATUS: {
set_status(data[1], data[2], data[3], data[4]);
set_initialized(true);
break;
}
default:
break;
}
}
void flush_line(void) {
while (urt.Instance->SR & USART_SR_RXNE) {
(void)urt.Instance->DR;
}
}
void ble_uart_receive(void) {
if (urt.Instance->SR & USART_SR_RXNE) {
uint8_t init_byte = 0;
HAL_UART_Receive(&urt, &init_byte, 1, 1);
if (init_byte == INTERNAL_EVENT || init_byte == INTERNAL_MESSAGE) {
uint8_t len_hi = 0;
uint8_t len_lo = 0;
HAL_UART_Receive(&urt, &len_hi, 1, 1);
HAL_UART_Receive(&urt, &len_lo, 1, 1);
uint16_t act_len = (len_hi << 8) | len_lo;
if (act_len > UART_PACKET_SIZE) {
flush_line();
return;
}
uint8_t *data = NULL;
uint16_t *len = NULL;
if (init_byte == INTERNAL_EVENT) {
data = int_event_buffer;
len = &int_event_msg_len;
} else if (init_byte == INTERNAL_MESSAGE) {
data = int_comm_buffer;
len = &int_comm_msg_len;
} else {
memset(data, 0, USB_DATA_SIZE);
*len = 0;
flush_line();
return;
}
HAL_StatusTypeDef result =
HAL_UART_Receive(&urt, data, act_len - OVERHEAD_SIZE, 5);
if (result != HAL_OK) {
memset(data, 0, USB_DATA_SIZE);
*len = 0;
flush_line();
return;
}
uint8_t eom = 0;
HAL_UART_Receive(&urt, &eom, 1, 1);
if (eom == EOM) {
*len = act_len - OVERHEAD_SIZE;
} else {
memset(data, 0, USB_DATA_SIZE);
*len = 0;
flush_line();
}
} else {
flush_line();
}
}
//
}
void ble_event_poll() {
ble_uart_receive();
if (int_event_msg_len > 0) {
process_poll(int_event_buffer, int_event_msg_len);
memset(int_event_buffer, 0, USB_DATA_SIZE);
int_event_msg_len = 0;
}
if (!ble_initialized() && !is_ble_dfu_mode() && ble_firmware_running()) {
send_state_request();
}
}
bool ble_firmware_running(void) {
return HAL_GPIO_ReadPin(GPIO_2_PORT, GPIO_2_PIN) != 0;
}
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) {
ble_uart_receive();
if (int_event_msg_len > 0) {
memcpy(data, int_event_buffer,
int_event_msg_len > len ? len : int_event_msg_len);
memset(int_event_buffer, 0, USB_DATA_SIZE);
uint32_t res = int_event_msg_len;
int_event_msg_len = 0;
return res;
}
return 0;
}
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) {
ble_uart_receive();
if (int_comm_msg_len > 0) {
memcpy(data, int_comm_buffer,
int_comm_msg_len > len ? len : int_comm_msg_len);
memset(int_comm_buffer, 0, USB_DATA_SIZE);
uint32_t res = int_comm_msg_len;
int_comm_msg_len = 0;
return res;
}
return 0;
}
bool start_spi_dma(void) {
int tmp_tail = (tail + 1) % SPI_QUEUE_SIZE;
if (spi_queue[tmp_tail].used || spi_queue[tmp_tail].ready) {
overrun = true;
overrun_count++;
if (first_overrun_at == 0) {
first_overrun_at = msg_cntr;
}
return false;
}
spi_queue[tmp_tail].used = true;
HAL_SPI_Receive_DMA(&spi, spi_queue[tmp_tail].buffer, BLE_PACKET_SIZE);
tail = tmp_tail;
return true;
}
void DMA2_Stream0_IRQHandler(void) {
IRQ_ENTER(DMA2_Stream0_IRQn);
HAL_DMA_IRQHandler(&spi_dma);
IRQ_EXIT(DMA2_Stream0_IRQn);
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
spi_queue[tail].ready = true;
msg_cntr++;
start_spi_dma();
}
#include "supervise.h"
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);
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;
}

@ -0,0 +1,27 @@
#ifndef __BLE_COMM_H__
#define __BLE_COMM_H__
#include <stdbool.h>
#include <stdint.h>
#include "ble/int_comm_defs.h"
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);
#endif

@ -187,6 +187,15 @@ secbool usb_configured(void) {
return secfalse;
}
secbool usb_configured_now(void) {
const USBD_HandleTypeDef *pdev = &usb_dev_handle;
if (pdev->dev_state == USBD_STATE_SUSPENDED &&
pdev->dev_old_state == USBD_STATE_CONFIGURED) {
return sectrue;
}
return secfalse;
}
/*
* Utility functions for USB interfaces
*/

@ -0,0 +1,29 @@
#include "ble.h"
#include <stdint.h>
static bool firmware_running = true;
void ble_comm_init(void) {}
void ble_comm_send(uint8_t *data, uint32_t len) {}
uint32_t ble_comm_receive(uint8_t *data, uint32_t len) { return 0; }
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type) {}
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) { return 0; }
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) { return 0; }
uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len) { return 0; }
void ble_event_poll(void) {}
bool ble_firmware_running(void) { return firmware_running; }
bool ble_reset_to_bootloader(void) {
firmware_running = false;
return true;
}
bool ble_reset(void) {
firmware_running = true;
return true;
}

@ -0,0 +1,19 @@
#include <stdbool.h>
#include <stdint.h>
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);

@ -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

@ -190,7 +190,7 @@ class WebUSB:
"""
Sends message using USB WebUSB (device) or UDP (emulator).
"""
from . import fatfs, sdcard
from . import fatfs, sdcard, ble
POLL_READ: int # wait until interface is readable and return read data
POLL_WRITE: int # wait until interface is writable
INPUT: int # interface id of unified input events
@ -202,4 +202,5 @@ BUTTON_RELEASED: int # button up event
BUTTON_LEFT: int # button number of left button
BUTTON_RIGHT: int # button number of right button
USB_CHECK: int # interface id for check of USB data connection
WireInterface = Union[HID, WebUSB]
BLE_CHECK: int # interface id for check of BLE data connection
WireInterface = Union[HID, WebUSB, BleInterface]

@ -0,0 +1,43 @@
from typing import *
# extmod/modtrezorio/modtrezorio-ble.h
def update_init(data: bytes, binsize: int) -> int:
"""
Initializes the BLE firmware update
"""
# extmod/modtrezorio/modtrezorio-ble.h
def update_chunk(chunk: bytes) -> int:
"""
Writes next chunk of BLE firmware update
"""
# extmod/modtrezorio/modtrezorio-ble.h
def write_int(self, msg: bytes) -> int:
"""
Sends internal message to NRF.
"""
# extmod/modtrezorio/modtrezorio-ble.h
def write_ext(self, msg: bytes) -> int:
"""
Sends message over BLE
"""
# extmod/modtrezorio/modtrezorio-ble.h
def erase_bonds() -> None:
"""
Erases all BLE bonds
"""
# extmod/modtrezorio/modtrezorio-ble.h
def disconnect() -> None:
"""
Disconnect BLE
"""

@ -83,10 +83,11 @@ SCM_REVISION: bytes
VERSION_MAJOR: int
VERSION_MINOR: int
VERSION_PATCH: int
USE_BLE: bool
USE_SD_CARD: bool
USE_BACKLIGHT: bool
USE_TOUCH: bool
USE_BUTTON: bool
USE_BACKLIGHT: bool
MODEL: str
INTERNAL_MODEL: str
EMULATOR: bool

@ -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",
]

@ -56,7 +56,16 @@ def configure(
sources += ["embed/extmod/modtrezorio/ffunicode.c"]
features_available.append("sd_card")
if "sd_card" in features_wanted:
if "ble" in features_wanted:
sources += ["embed/trezorhal/stm32f4/ble.c"]
sources += ["embed/lib/ble/dfu.c"]
sources += ["embed/lib/ble/fwu.c"]
sources += ["embed/lib/ble/state.c"]
sources += ["embed/lib/ble/messages.c"]
features_available.append("ble")
if "ble" in features_wanted or "sd_card" in features_wanted:
sources += [
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c"
]

@ -3,8 +3,6 @@ from __future__ import annotations
import subprocess
from pathlib import Path
import subprocess
from boards import (
discovery,
trezor_1,
@ -59,7 +57,7 @@ def configure_board(
elif model_r_version == 10:
return trezor_r_v10.configure(env, features_wanted, defines, sources, paths)
raise Exception("Unknown model_r_version")
elif model in ('T3W1',):
elif model in ("T3W1",):
return trezor_t3w1_d1.configure(env, features_wanted, defines, sources, paths)
elif model in ("DISC1",):
return discovery.configure(env, features_wanted, defines, sources, paths)
@ -104,14 +102,14 @@ def get_version_int(file):
patch = 0
file = PROJECT_ROOT / file
with open(file, 'r') as f:
with open(file, "r") as f:
for line in f:
if line.startswith('#define VERSION_MAJOR '):
major = int(line.split('VERSION_MAJOR')[1].strip())
if line.startswith('#define VERSION_MINOR '):
minor = int(line.split('VERSION_MINOR')[1].strip())
if line.startswith('#define VERSION_PATCH '):
patch = int(line.split('VERSION_PATCH')[1].strip())
if line.startswith("#define VERSION_MAJOR "):
major = int(line.split("VERSION_MAJOR")[1].strip())
if line.startswith("#define VERSION_MINOR "):
minor = int(line.split("VERSION_MINOR")[1].strip())
if line.startswith("#define VERSION_PATCH "):
patch = int(line.split("VERSION_PATCH")[1].strip())
if major > 99 or minor > 99 or patch > 99:
raise Exception("Version number too large")
return major * 10000 + minor * 100 + patch

@ -33,10 +33,14 @@ from trezor import utils
all_modules
import all_modules
bluetooth
import bluetooth
boot
import boot
main
import main
mutex
import mutex
session
import session
typing
@ -317,6 +321,18 @@ apps.management.backup_device
import apps.management.backup_device
apps.management.backup_types
import apps.management.backup_types
apps.management.ble.comparison_request
import apps.management.ble.comparison_request
apps.management.ble.disconnect
import apps.management.ble.disconnect
apps.management.ble.erase_bonds
import apps.management.ble.erase_bonds
apps.management.ble.pairing_request
import apps.management.ble.pairing_request
apps.management.ble.repair_request
import apps.management.ble.repair_request
apps.management.ble.upload_ble_firmware_init
import apps.management.ble.upload_ble_firmware_init
apps.management.change_pin
import apps.management.change_pin
apps.management.change_wipe_code

@ -324,7 +324,7 @@ def set_homescreen() -> None:
def lock_device(interrupt_workflow: bool = True) -> None:
if config.has_pin():
config.lock()
wire.find_handler = get_pinlocked_handler
wire.common_find_handler.register_find_handler(get_pinlocked_handler)
set_homescreen()
if interrupt_workflow:
workflow.close_others()
@ -335,7 +335,7 @@ def lock_device_if_unlocked() -> None:
lock_device(interrupt_workflow=workflow.autolock_interrupts_workflow)
async def unlock_device() -> None:
async def unlock_device(skip_button_request: bool = False) -> None:
"""Ensure the device is in unlocked state.
If the storage is locked, attempt to unlock it. Reset the homescreen and the wire
@ -345,10 +345,13 @@ async def unlock_device() -> None:
if not config.is_unlocked():
# verify_user_pin will raise if the PIN was invalid
await verify_user_pin()
await verify_user_pin(skip_button_request=skip_button_request)
set_homescreen()
wire.find_handler = workflow_handlers.find_registered_handler
wire.common_find_handler.register_find_handler(
workflow_handlers.find_registered_handler
)
def get_pinlocked_handler(
@ -404,7 +407,10 @@ def boot() -> None:
workflow_handlers.register(msg_type, handler) # type: ignore [cannot be assigned to type]
reload_settings_from_storage()
if config.is_unlocked():
wire.find_handler = workflow_handlers.find_registered_handler
wire.common_find_handler.register_find_handler(
workflow_handlers.find_registered_handler
)
else:
wire.find_handler = get_pinlocked_handler
wire.common_find_handler.register_find_handler(get_pinlocked_handler)

@ -84,6 +84,7 @@ async def verify_user_pin(
allow_cancel: bool = True,
retry: bool = True,
cache_time_ms: int = 0,
skip_button_request: bool = False,
) -> None:
# _get_last_unlock_time
last_unlock = int.from_bytes(
@ -101,7 +102,12 @@ async def verify_user_pin(
if config.has_pin():
from trezor.ui.layouts import request_pin_on_device
pin = await request_pin_on_device(prompt, config.get_pin_rem(), allow_cancel)
pin = await request_pin_on_device(
prompt,
config.get_pin_rem(),
allow_cancel,
skip_button_request=skip_button_request,
)
config.ensure_not_wipe_code(pin)
else:
pin = ""
@ -115,7 +121,11 @@ async def verify_user_pin(
while retry:
pin = await request_pin_on_device( # type: ignore ["request_pin_on_device" is possibly unbound]
"Enter PIN", config.get_pin_rem(), allow_cancel, wrong_pin=True
"Enter PIN",
config.get_pin_rem(),
allow_cancel,
wrong_pin=True,
skip_button_request=skip_button_request,
)
if config.unlock(pin, salt):
_set_last_unlock_time()

@ -0,0 +1,20 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
Success,
ComparisonRequest,
)
async def comparison_request(msg: ComparisonRequest) -> Success:
from trezor.messages import (
Success,
)
from trezor.ui.layouts import confirm_action
await confirm_action(
None, "DO THE NUMBERS MATCH?", description=msg.key.decode("utf-8")
)
return Success()

@ -0,0 +1,21 @@
from trezorio import ble
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
Success,
Disconnect,
)
async def disconnect(_msg: Disconnect) -> Success:
from trezor.messages import (
Success,
)
from trezor.ui.layouts import confirm_action
await confirm_action(None, "DISCONNECT")
ble.disconnect()
return Success()

@ -0,0 +1,21 @@
from trezorio import ble
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
Success,
EraseBonds,
)
async def erase_bonds(_msg: EraseBonds) -> Success:
from trezor.messages import (
Success,
)
from trezor.ui.layouts import confirm_action
await confirm_action(None, "ERASE BONDS")
ble.erase_bonds()
return Success()

@ -0,0 +1,21 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
AuthKey,
PairingRequest,
)
async def pairing_request(_msg: PairingRequest) -> AuthKey:
from trezor.messages import (
AuthKey,
)
from trezor.ui.layouts import request_pin_on_device
pin = await request_pin_on_device("PAIRING", None, True, False, True)
if len(pin) != 6:
pin = "000000"
return AuthKey(key=pin.encode())

@ -0,0 +1,18 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
Success,
RepairRequest,
)
async def repair_request(_msg: RepairRequest) -> Success:
from trezor.messages import (
Success,
)
from trezor.ui.layouts import confirm_action
await confirm_action(None, "RE-PAIR DEVICE")
return Success()

@ -0,0 +1,79 @@
from trezorio import ble
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from trezor.messages import (
UploadBLEFirmwareInit,
UploadBLEFirmwareChunk,
Success,
)
async def upload_ble_firmware_chunk(msg: UploadBLEFirmwareChunk) -> int:
result = ble.update_chunk(msg.data)
return result
async def upload_ble_firmware_init(msg: UploadBLEFirmwareInit) -> Success:
from trezor.enums import ButtonRequestType
from trezor.messages import (
UploadBLEFirmwareNextChunk,
UploadBLEFirmwareChunk,
Success,
)
from trezor.ui.layouts import confirm_action
await confirm_action(
"confirm_upload_ble_firmware",
"Upload BLE firmware",
"",
"Update BLE FW?\n",
reverse=True,
verb="Confirm",
br_code=ButtonRequestType.Other,
)
from trezor.ui.layouts import progress
from trezor.wire.context import get_context
from trezor.enums import MessageType
ctx = get_context()
progress_layout = progress.progress("Uploading...")
upload_progress = 0
p = int(1000 * upload_progress / msg.binsize)
progress_layout.report(p)
res = ble.update_init(msg.init_data, msg.binsize)
await ctx.write(UploadBLEFirmwareNextChunk(offset=0))
if res == 0:
while True:
received_msg = await ctx.read(
(MessageType.UploadBLEFirmwareChunk,),
UploadBLEFirmwareChunk,
)
result = await upload_ble_firmware_chunk(received_msg)
upload_progress += len(received_msg.data)
p = int(1000 * upload_progress / msg.binsize)
progress_layout.report(p)
if result == 0:
result_msg = UploadBLEFirmwareNextChunk(offset=0)
await ctx.write(result_msg)
del (result_msg, received_msg)
else:
del received_msg
break
progress_layout.report(1000)
return Success(message="BLE firmware update successful")

@ -1,7 +1,7 @@
def boot() -> None:
def boot(mutex) -> None:
import usb
from trezor import loop
from .fido2 import handle_reports
loop.schedule(handle_reports(usb.iface_webauthn))
loop.schedule(handle_reports(usb.iface_webauthn, mutex))

@ -539,7 +539,7 @@ def send_cmd_sync(cmd: Cmd, iface: HID) -> None:
seq += 1
async def handle_reports(iface: HID) -> None:
async def handle_reports(iface: HID, mutex) -> None:
dialog_mgr = DialogManager(iface)
while True:
@ -547,10 +547,14 @@ async def handle_reports(iface: HID) -> None:
req = await _read_cmd(iface)
if req is None:
continue
if not dialog_mgr.allow_cid(req.cid):
if mutex is not None and mutex.get_busy(iface.iface_num()):
resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY)
else:
resp = _dispatch_cmd(req, dialog_mgr)
mutex.set_busy(iface.iface_num())
if not dialog_mgr.allow_cid(req.cid):
resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY)
else:
resp = _dispatch_cmd(req, dialog_mgr)
if resp is not None:
await send_cmd(resp, iface)
except Exception as e:

@ -14,7 +14,7 @@ def register(wire_type: int, handler: Handler[Msg]) -> None:
workflow_handlers[wire_type] = handler
def _find_message_handler_module(msg_type: int) -> str:
def _find_message_handler_module(msg_type: int, iface: WireInterface) -> str:
"""Statically find the appropriate workflow handler.
For now, new messages must be registered by hand in the if-elif manner below.
@ -59,6 +59,14 @@ def _find_message_handler_module(msg_type: int) -> str:
if utils.USE_SD_CARD and msg_type == MessageType.SdProtect:
return "apps.management.sd_protect"
if utils.USE_BLE:
if msg_type == MessageType.UploadBLEFirmwareInit:
return "apps.management.ble.upload_ble_firmware_init"
if msg_type == MessageType.EraseBonds:
return "apps.management.ble.erase_bonds"
if msg_type == MessageType.Disconnect:
return "apps.management.ble.disconnect"
# bitcoin
if msg_type == MessageType.AuthorizeCoinJoin:
return "apps.bitcoin.authorize_coinjoin"
@ -196,7 +204,7 @@ def find_registered_handler(iface: WireInterface, msg_type: int) -> Handler | No
return workflow_handlers[msg_type]
try:
modname = _find_message_handler_module(msg_type)
modname = _find_message_handler_module(msg_type, iface)
handler_name = modname[modname.rfind(".") + 1 :]
module = __import__(modname, None, None, (handler_name,), 0)
return getattr(module, handler_name)

@ -0,0 +1,72 @@
from trezorio import ble
from typing import TYPE_CHECKING
from trezor import config
from apps.base import unlock_device
if TYPE_CHECKING:
from trezor import protobuf, wire
class BleInterfaceInternal:
def iface_num(self) -> int:
return 16
def write(self, msg: bytes) -> int:
return ble.write_int(self, msg)
class BleInterfaceExternal:
def iface_num(self) -> int:
return 17
def write(self, msg: bytes) -> int:
return ble.write_ext(self, msg)
def find_ble_int_handler(iface, msg_type) -> wire.Handler | None:
from trezor.enums import MessageType
modname = None
if msg_type == MessageType.PairingRequest:
modname = "apps.management.ble.pairing_request"
if msg_type == MessageType.RepairRequest:
modname = "apps.management.ble.repair_request"
if msg_type == MessageType.ComparisonRequest:
modname = "apps.management.ble.comparison_request"
if modname is not None:
try:
handler_name = modname[modname.rfind(".") + 1 :]
module = __import__(modname, None, None, (handler_name,), 0)
return getattr(module, handler_name)
except ValueError:
return None
return None
def int_find_handler(
iface: wire.WireInterface, msg_type: int
) -> wire.Handler[wire.Msg] | None:
orig_handler = find_ble_int_handler(iface, msg_type)
if config.is_unlocked():
return orig_handler
else:
if orig_handler is None:
return None
async def wrapper(msg: wire.Msg) -> protobuf.MessageType:
await unlock_device(True)
return await orig_handler(msg)
return wrapper
# interface used for trezor wire protocol
iface_ble_int = BleInterfaceInternal()
iface_ble_ext = BleInterfaceExternal()

@ -0,0 +1,23 @@
class Mutex:
def __init__(self):
self.ifaces = []
self.busy = None
def add(self, iface_num: int):
if iface_num not in self.ifaces:
self.ifaces.append(iface_num)
def set_busy(self, iface_num: int):
if iface_num in self.ifaces:
self.busy = iface_num
def get_busy(self, iface_num: int) -> int:
return (
iface_num in self.ifaces
and self.busy is not None
and self.busy != iface_num
)
def release(self, iface_num: int):
if iface_num == self.busy:
self.busy = None

@ -1,29 +1,67 @@
# isort: skip_file
from micropython import const
from mutex import Mutex
from trezor import log, loop, utils, wire, workflow
import apps.base
import usb
_PROTOBUF_BUFFER_SIZE = const(8192)
WIRE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE)
apps.base.boot()
mutex = Mutex()
if not utils.BITCOIN_ONLY and usb.ENABLE_IFACE_WEBAUTHN:
import apps.webauthn
apps.webauthn.boot()
apps.webauthn.boot(mutex)
if __debug__:
import apps.debug
apps.debug.boot()
# run main event loop and specify which screen is the default
apps.base.set_homescreen()
workflow.start_default()
mutex.add(usb.iface_wire.iface_num())
mutex.add(usb.iface_debug.iface_num())
# initialize the wire codec
wire.setup(usb.iface_wire)
wire.setup(usb.iface_wire, WIRE_BUFFER, wire.common_find_handler, mutex=mutex)
if __debug__:
wire.setup(usb.iface_debug, is_debug_session=True)
PROTOBUF_BUFFER_SIZE_DEBUG = 1024
WIRE_BUFFER_DEBUG = bytearray(PROTOBUF_BUFFER_SIZE_DEBUG)
wire.setup(
usb.iface_debug,
WIRE_BUFFER_DEBUG,
wire.common_find_handler,
is_debug_session=True,
)
if utils.USE_BLE:
import bluetooth
BLE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE)
ble_find_handler = wire.MessageHandler()
ble_find_handler.register_find_handler(bluetooth.int_find_handler)
mutex.add(bluetooth.iface_ble_int.iface_num())
mutex.add(bluetooth.iface_ble_ext.iface_num())
wire.setup(bluetooth.iface_ble_int, BLE_BUFFER, ble_find_handler, mutex=mutex)
wire.setup(
bluetooth.iface_ble_ext, BLE_BUFFER, wire.common_find_handler, mutex=mutex
)
loop.run()

@ -16,4 +16,5 @@ NotInitialized = 11
PinMismatch = 12
WipeCodeMismatch = 13
InvalidSession = 14
DeviceIsBusy = 15
FirmwareError = 99

@ -52,6 +52,15 @@ FirmwareErase = 6
FirmwareUpload = 7
FirmwareRequest = 8
SelfTest = 32
UploadBLEFirmwareInit = 8000
UploadBLEFirmwareNextChunk = 8001
UploadBLEFirmwareChunk = 8002
PairingRequest = 8003
AuthKey = 8004
RepairRequest = 8005
EraseBonds = 8006
Disconnect = 8007
ComparisonRequest = 8008
GetPublicKey = 11
PublicKey = 12
SignTx = 15

@ -74,6 +74,15 @@ if TYPE_CHECKING:
FirmwareUpload = 7
FirmwareRequest = 8
SelfTest = 32
UploadBLEFirmwareInit = 8000
UploadBLEFirmwareNextChunk = 8001
UploadBLEFirmwareChunk = 8002
PairingRequest = 8003
AuthKey = 8004
RepairRequest = 8005
EraseBonds = 8006
Disconnect = 8007
ComparisonRequest = 8008
GetPublicKey = 11
PublicKey = 12
SignTx = 15
@ -268,6 +277,7 @@ if TYPE_CHECKING:
PinMismatch = 12
WipeCodeMismatch = 13
InvalidSession = 14
DeviceIsBusy = 15
FirmwareError = 99
class ButtonRequestType(IntEnum):

@ -1178,6 +1178,102 @@ if TYPE_CHECKING:
def is_type_of(cls, msg: Any) -> TypeGuard["TxAckPrevExtraDataWrapper"]:
return isinstance(msg, cls)
class UploadBLEFirmwareInit(protobuf.MessageType):
init_data: "bytes"
binsize: "int"
def __init__(
self,
*,
init_data: "bytes",
binsize: "int",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareInit"]:
return isinstance(msg, cls)
class UploadBLEFirmwareNextChunk(protobuf.MessageType):
offset: "int"
def __init__(
self,
*,
offset: "int",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareNextChunk"]:
return isinstance(msg, cls)
class UploadBLEFirmwareChunk(protobuf.MessageType):
data: "bytes"
def __init__(
self,
*,
data: "bytes",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareChunk"]:
return isinstance(msg, cls)
class EraseBonds(protobuf.MessageType):
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["EraseBonds"]:
return isinstance(msg, cls)
class Disconnect(protobuf.MessageType):
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["Disconnect"]:
return isinstance(msg, cls)
class PairingRequest(protobuf.MessageType):
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["PairingRequest"]:
return isinstance(msg, cls)
class AuthKey(protobuf.MessageType):
key: "bytes"
def __init__(
self,
*,
key: "bytes",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["AuthKey"]:
return isinstance(msg, cls)
class RepairRequest(protobuf.MessageType):
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["RepairRequest"]:
return isinstance(msg, cls)
class ComparisonRequest(protobuf.MessageType):
key: "bytes"
def __init__(
self,
*,
key: "bytes",
) -> None:
pass
@classmethod
def is_type_of(cls, msg: Any) -> TypeGuard["ComparisonRequest"]:
return isinstance(msg, cls)
class CardanoBlockchainPointerType(protobuf.MessageType):
block_index: "int"
tx_index: "int"

@ -30,12 +30,13 @@ async def button_request(
async def interact(
layout: LayoutType,
br_type: str,
br_type: str | None,
br_code: ButtonRequestType = ButtonRequestType.Other,
) -> Any:
pages = None
if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"]
# We know for certain how many pages the layout will have
pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"]
await button_request(br_type, br_code, pages)
if br_type is not None:
pages = None
if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"]
# We know for certain how many pages the layout will have
pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"]
await button_request(br_type, br_code, pages)
return await context.wait(layout)

@ -243,7 +243,7 @@ async def raise_if_not_confirmed(a: Awaitable[T], exc: Any = ActionCancelled) ->
async def confirm_action(
br_type: str,
br_type: str | None,
title: str,
action: str | None = None,
description: str | None = None,
@ -1165,9 +1165,13 @@ async def request_pin_on_device(
attempts_remaining: int | None,
allow_cancel: bool,
wrong_pin: bool = False,
skip_button_request: bool = False,
) -> str:
from trezor.wire import PinCancelled
if not skip_button_request:
await button_request("pin_device", code=ButtonRequestType.PinEntry)
if attempts_remaining is None:
subprompt = ""
elif attempts_remaining == 1:

@ -8,6 +8,7 @@ from trezorutils import ( # noqa: F401
MODEL,
SCM_REVISION,
USE_BACKLIGHT,
USE_BLE,
USE_BUTTON,
USE_SD_CARD,
USE_TOUCH,

@ -49,7 +49,6 @@ from trezor.wire.errors import ActionCancelled, DataError, Error
# other packages.
from trezor.wire.errors import * # isort:skip # noqa: F401,F403
if TYPE_CHECKING:
from trezorio import WireInterface
from typing import Any, Callable, Container, Coroutine, TypeVar
@ -65,9 +64,35 @@ if TYPE_CHECKING:
EXPERIMENTAL_ENABLED = False
def setup(iface: WireInterface, is_debug_session: bool = False) -> None:
class MessageHandler:
def __init__(self):
self._find_handler = None
def find_handler(self, iface: WireInterface, msg_type: int) -> Handler | None:
if self._find_handler is not None:
return self._find_handler(iface, msg_type)
return None
def register_find_handler(self, handler):
self._find_handler = handler
common_find_handler = MessageHandler()
def setup(
iface: WireInterface,
buffer: bytearray,
handler: MessageHandler,
is_debug_session: bool = False,
mutex=None,
) -> None:
"""Initialize the wire stack on passed USB interface."""
loop.schedule(handle_session(iface, codec_v1.SESSION_ID, is_debug_session))
loop.schedule(
handle_session(
iface, codec_v1.SESSION_ID, buffer, handler, is_debug_session, mutex
)
)
def wrap_protobuf_load(
@ -100,7 +125,10 @@ if __debug__:
async def _handle_single_message(
ctx: context.Context, msg: codec_v1.Message, use_workflow: bool
ctx: context.Context,
msg: codec_v1.Message,
find_handler: MessageHandler,
use_workflow: bool,
) -> codec_v1.Message | None:
"""Handle a message that was loaded from USB by the caller.
@ -132,7 +160,9 @@ async def _handle_single_message(
res_msg: protobuf.MessageType | None = None
# We need to find a handler for this message type. Should not raise.
handler = find_handler(ctx.iface, msg.type) # pylint: disable=assignment-from-none
handler = find_handler.find_handler(
ctx.iface, msg.type
) # pylint: disable=assignment-from-none
if handler is None:
# If no handler is found, we can skip decoding and directly
@ -205,13 +235,13 @@ async def _handle_single_message(
async def handle_session(
iface: WireInterface, session_id: int, is_debug_session: bool = False
iface: WireInterface,
session_id: int,
ctx_buffer: bytearray,
message_handler: MessageHandler,
is_debug_session: bool = False,
mutex=None,
) -> None:
if __debug__ and is_debug_session:
ctx_buffer = WIRE_BUFFER_DEBUG
else:
ctx_buffer = WIRE_BUFFER
ctx = context.Context(iface, session_id, ctx_buffer)
next_msg: codec_v1.Message | None = None
@ -230,6 +260,18 @@ async def handle_session(
# wait for a new one coming from the wire.
try:
msg = await ctx.read_from_wire()
if mutex is not None:
if mutex.get_busy(iface.iface_num()):
await ctx.write(
Failure(
code=FailureType.DeviceIsBusy,
message="Device is busy",
)
)
continue
else:
mutex.set_busy(iface.iface_num())
except codec_v1.CodecError as exc:
if __debug__:
log.exception(__name__, exc)
@ -243,8 +285,9 @@ async def handle_session(
try:
next_msg = await _handle_single_message(
ctx, msg, use_workflow=not is_debug_session
ctx, msg, message_handler, not is_debug_session
)
except Exception as exc:
# Log and ignore. The session handler can only exit explicitly in the
# following finally block.
@ -271,12 +314,6 @@ async def handle_session(
log.exception(__name__, exc)
def _find_handler_placeholder(iface: WireInterface, msg_type: int) -> Handler | None:
"""Placeholder handler lookup before a proper one is registered."""
return None
find_handler = _find_handler_placeholder
AVOID_RESTARTING_FOR: Container[int] = ()

Loading…
Cancel
Save