mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-26 09:52:34 +00:00
feat(core): update NRF application from firmware
[no changelog]
This commit is contained in:
parent
deac4dc099
commit
01465aac7c
@ -831,6 +831,7 @@ protobuf_blobs = env.Command(
|
|||||||
)
|
)
|
||||||
env.Depends(protobuf_blobs, qstr_generated)
|
env.Depends(protobuf_blobs, qstr_generated)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rust library
|
# Rust library
|
||||||
#
|
#
|
||||||
@ -891,7 +892,14 @@ tools.embed_raw_binary(
|
|||||||
f'build/kernel/kernel.bin',
|
f'build/kernel/kernel.bin',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if 'nrf' in FEATURES_AVAILABLE:
|
||||||
|
tools.embed_raw_binary(
|
||||||
|
obj_program,
|
||||||
|
env,
|
||||||
|
'nrf_app',
|
||||||
|
'build/firmware/nrf_app.o',
|
||||||
|
f'embed/models/{TREZOR_MODEL}/trezor-ble.bin',
|
||||||
|
)
|
||||||
|
|
||||||
env.Depends(obj_program, qstr_generated)
|
env.Depends(obj_program, qstr_generated)
|
||||||
|
|
||||||
|
@ -263,6 +263,10 @@ env = Environment(
|
|||||||
|
|
||||||
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
|
||||||
|
|
||||||
|
if 'nrf' in FEATURES_AVAILABLE:
|
||||||
|
FEATURES_AVAILABLE.append('smp')
|
||||||
|
CPPDEFINES_HAL.append('USE_SMP')
|
||||||
|
|
||||||
FEATURE_FLAGS["AES_GCM"] = FEATURE_FLAGS["AES_GCM"] or "tropic" in FEATURES_AVAILABLE
|
FEATURE_FLAGS["AES_GCM"] = FEATURE_FLAGS["AES_GCM"] or "tropic" in FEATURES_AVAILABLE
|
||||||
|
|
||||||
if not 'secmon_layout' in FEATURES_AVAILABLE:
|
if not 'secmon_layout' in FEATURES_AVAILABLE:
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include <sha2.h>
|
#include <sha2.h>
|
||||||
|
|
||||||
|
|
||||||
// maximum data size allowed to be sent
|
// maximum data size allowed to be sent
|
||||||
#define NRF_MAX_TX_DATA_SIZE (244)
|
#define NRF_MAX_TX_DATA_SIZE (244)
|
||||||
|
|
||||||
@ -58,66 +57,170 @@ typedef struct {
|
|||||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||||
} nrf_info_t;
|
} nrf_info_t;
|
||||||
|
|
||||||
|
/** Callback type invoked when data is received on a registered service */
|
||||||
typedef void (*nrf_rx_callback_t)(const uint8_t *data, uint32_t len);
|
typedef void (*nrf_rx_callback_t)(const uint8_t *data, uint32_t len);
|
||||||
|
|
||||||
|
/** Callback type invoked when a message transmission completes */
|
||||||
typedef void (*nrf_tx_callback_t)(nrf_status_t status, void *context);
|
typedef void (*nrf_tx_callback_t)(nrf_status_t status, void *context);
|
||||||
|
|
||||||
// Initialize the NRF driver
|
/**
|
||||||
|
* @brief Initialize the NRF driver.
|
||||||
|
*/
|
||||||
void nrf_init(void);
|
void nrf_init(void);
|
||||||
|
|
||||||
// Deinitialize the NRF driver
|
/**
|
||||||
|
* @brief Deinitialize the NRF driver.
|
||||||
|
*/
|
||||||
void nrf_deinit(void);
|
void nrf_deinit(void);
|
||||||
|
|
||||||
// Suspend NRF driver
|
/**
|
||||||
|
* @brief Suspend the NRF driver.
|
||||||
|
*/
|
||||||
void nrf_suspend(void);
|
void nrf_suspend(void);
|
||||||
|
|
||||||
// Check that NRF is running
|
/**
|
||||||
|
* @brief Check if the NRF communication is currently running.
|
||||||
|
*
|
||||||
|
* @return true if running, false otherwise
|
||||||
|
*/
|
||||||
bool nrf_is_running(void);
|
bool nrf_is_running(void);
|
||||||
|
|
||||||
// Register listener for a service
|
/**
|
||||||
// The listener will be called when a message is received for the service
|
* @brief Register a listener for a specific NRF service.
|
||||||
// The listener will be called from an interrupt context
|
*
|
||||||
// Returns false if a listener for the service is already registered
|
* The listener callback will be invoked from an interrupt context when a
|
||||||
|
* message is received for the specified service.
|
||||||
|
*
|
||||||
|
* @param service Service identifier to register for
|
||||||
|
* @param callback Function to call when data arrives
|
||||||
|
* @return false if a listener for the service is already registered, true
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
bool nrf_register_listener(nrf_service_id_t service,
|
bool nrf_register_listener(nrf_service_id_t service,
|
||||||
nrf_rx_callback_t callback);
|
nrf_rx_callback_t callback);
|
||||||
|
|
||||||
// Unregister listener for a service
|
/**
|
||||||
|
* @brief Unregister the listener for a specific NRF service.
|
||||||
|
*
|
||||||
|
* @param service Service identifier to unregister
|
||||||
|
*/
|
||||||
void nrf_unregister_listener(nrf_service_id_t service);
|
void nrf_unregister_listener(nrf_service_id_t service);
|
||||||
|
|
||||||
// Send a message to a service
|
/**
|
||||||
// The message will be queued and sent as soon as possible
|
* @brief Send a message to a specific NRF service.
|
||||||
// If the queue is full, the message will be dropped
|
*
|
||||||
// returns ID of the message if it was successfully queued, otherwise -1
|
* The message will be queued and sent as soon as possible. If the queue is
|
||||||
|
* full, the message will be dropped.
|
||||||
|
*
|
||||||
|
* @param service Service identifier to send to
|
||||||
|
* @param data Pointer to the data buffer to send
|
||||||
|
* @param len Length of the data buffer
|
||||||
|
* @param callback Function to call upon transmission completion
|
||||||
|
* @param context Context pointer passed to the callback
|
||||||
|
* @return ID of the message if successfully queued; -1 otherwise
|
||||||
|
*/
|
||||||
int32_t nrf_send_msg(nrf_service_id_t service, const uint8_t *data,
|
int32_t nrf_send_msg(nrf_service_id_t service, const uint8_t *data,
|
||||||
uint32_t len, nrf_tx_callback_t callback, void *context);
|
uint32_t len, nrf_tx_callback_t callback, void *context);
|
||||||
|
|
||||||
// Abort a message by ID
|
/**
|
||||||
// If the message is already sent or the id is not found, it does nothing and
|
* @brief Abort a queued message by its ID.
|
||||||
// returns false If the message is queued, it will be removed from the queue If
|
*
|
||||||
// the message is being sent, it will be sent. The callback will not be called.
|
* If the message is already sent or the ID is not found, this function does
|
||||||
|
* nothing and returns false. If the message is queued, it will be removed from
|
||||||
|
* the queue. If the message is in the process of being sent, it will complete,
|
||||||
|
* but its callback will not be invoked.
|
||||||
|
*
|
||||||
|
* @param id Identifier of the message to abort
|
||||||
|
* @return false if the message was already sent or not found, true if aborted
|
||||||
|
*/
|
||||||
bool nrf_abort_msg(int32_t id);
|
bool nrf_abort_msg(int32_t id);
|
||||||
|
|
||||||
// Reads version and other info from NRF application.
|
/**
|
||||||
// Blocking function.
|
* @brief Read version and other information from the NRF application.
|
||||||
|
*
|
||||||
|
* Blocking function that fills the provided nrf_info_t structure.
|
||||||
|
*
|
||||||
|
* @param info Pointer to an nrf_info_t structure to populate
|
||||||
|
* @return true on success; false on communication error
|
||||||
|
*/
|
||||||
bool nrf_get_info(nrf_info_t *info);
|
bool nrf_get_info(nrf_info_t *info);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
/**
|
||||||
// TEST only functions
|
* @brief Place the NRF device into system-off (deep sleep) mode.
|
||||||
|
*
|
||||||
// Test SPI communication with NRF
|
* @return true if the command was acknowledged; false otherwise
|
||||||
bool nrf_test_spi_comm(void);
|
*/
|
||||||
|
|
||||||
// Test UART communication with NRF
|
|
||||||
bool nrf_test_uart_comm(void);
|
|
||||||
|
|
||||||
// Test reset pin
|
|
||||||
bool nrf_test_reset(void);
|
|
||||||
|
|
||||||
// Test GPIO stay in bootloader
|
|
||||||
bool nrf_test_gpio_stay_in_bld(void);
|
|
||||||
|
|
||||||
// Test GPIO reserved
|
|
||||||
bool nrf_test_gpio_reserved(void);
|
|
||||||
|
|
||||||
bool nrf_system_off(void);
|
bool nrf_system_off(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reboot the NRF device immediately.
|
||||||
|
*/
|
||||||
void nrf_reboot(void);
|
void nrf_reboot(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send raw UART data to the NRF device (for debugging purposes).
|
||||||
|
*
|
||||||
|
* @param data Pointer to the data buffer
|
||||||
|
* @param len Length of the data buffer
|
||||||
|
*/
|
||||||
|
void nrf_send_uart_data(const uint8_t *data, uint32_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if an nRF device firmware update is required by comparing SHA256
|
||||||
|
* hashes.
|
||||||
|
*
|
||||||
|
* @param image_ptr Pointer to the firmware image in memory
|
||||||
|
* @param image_len Length of the firmware image in bytes
|
||||||
|
* @return true if an update is required (e.g., corrupted image detected or hash
|
||||||
|
* mismatch), false if the device already has the same firmware version
|
||||||
|
*/
|
||||||
|
bool nrf_update_required(const uint8_t *image_ptr, size_t image_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform a firmware update on the nRF device via DFU (Device Firmware
|
||||||
|
* Update).
|
||||||
|
*
|
||||||
|
* @param image_ptr Pointer to the firmware image in memory
|
||||||
|
* @param image_len Length of the firmware image in bytes
|
||||||
|
* @return true always (indicates that the update process was initiated)
|
||||||
|
*/
|
||||||
|
bool nrf_update(const uint8_t *image_ptr, size_t image_len);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TEST-only functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test SPI communication with the NRF device.
|
||||||
|
*
|
||||||
|
* @return true if SPI communication succeeds; false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_test_spi_comm(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test UART communication with the NRF device.
|
||||||
|
*
|
||||||
|
* @return true if UART communication succeeds; false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_test_uart_comm(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test the NRF reset pin functionality.
|
||||||
|
*
|
||||||
|
* @return true if reset behavior is correct; false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_test_reset(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test the GPIO pin that forces the device to stay in bootloader.
|
||||||
|
*
|
||||||
|
* @return true if the GPIO behaves correctly; false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_test_gpio_stay_in_bld(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test a reserved GPIO pin on the NRF device.
|
||||||
|
*
|
||||||
|
* @return true if the GPIO behavior is correct; false otherwise
|
||||||
|
*/
|
||||||
|
bool nrf_test_gpio_reserved(void);
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -52,3 +52,6 @@ bool nrf_in_reserved(void);
|
|||||||
|
|
||||||
void nrf_uart_send(uint8_t data);
|
void nrf_uart_send(uint8_t data);
|
||||||
uint8_t nrf_uart_get_received(void);
|
uint8_t nrf_uart_get_received(void);
|
||||||
|
|
||||||
|
void nrf_set_dfu_mode(bool set);
|
||||||
|
bool nrf_is_dfu_mode(void);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "../crc8.h"
|
#include "../crc8.h"
|
||||||
#include "../nrf_internal.h"
|
#include "../nrf_internal.h"
|
||||||
|
#include "rust_smp.h"
|
||||||
|
|
||||||
#define MAX_SPI_DATA_SIZE (244)
|
#define MAX_SPI_DATA_SIZE (244)
|
||||||
|
|
||||||
@ -91,6 +92,9 @@ typedef struct {
|
|||||||
systimer_t *timer;
|
systimer_t *timer;
|
||||||
bool pending_spi_transaction;
|
bool pending_spi_transaction;
|
||||||
|
|
||||||
|
bool dfu_mode;
|
||||||
|
bool dfu_tx_pending;
|
||||||
|
|
||||||
} nrf_driver_t;
|
} nrf_driver_t;
|
||||||
|
|
||||||
static nrf_driver_t g_nrf_driver = {0};
|
static nrf_driver_t g_nrf_driver = {0};
|
||||||
@ -645,6 +649,14 @@ uint8_t nrf_uart_get_received(void) {
|
|||||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *urt) {
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *urt) {
|
||||||
nrf_driver_t *drv = &g_nrf_driver;
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
if (drv->initialized && urt == &drv->urt) {
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
#ifdef USE_SMP
|
||||||
|
if (nrf_is_dfu_mode()) {
|
||||||
|
smp_process_rx_byte(drv->urt_rx_byte);
|
||||||
|
HAL_UART_Receive_IT(&drv->urt, &drv->urt_rx_byte, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
drv->urt_rx_complete = true;
|
drv->urt_rx_complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -652,6 +664,7 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *urt) {
|
|||||||
void HAL_UART_ErrorCallback(UART_HandleTypeDef *urt) {
|
void HAL_UART_ErrorCallback(UART_HandleTypeDef *urt) {
|
||||||
nrf_driver_t *drv = &g_nrf_driver;
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
if (drv->initialized && urt == &drv->urt) {
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
drv->dfu_tx_pending = false;
|
||||||
HAL_UART_Receive_IT(&drv->urt, &drv->urt_rx_byte, 1);
|
HAL_UART_Receive_IT(&drv->urt, &drv->urt_rx_byte, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,6 +672,7 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *urt) {
|
|||||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *urt) {
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *urt) {
|
||||||
nrf_driver_t *drv = &g_nrf_driver;
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
if (drv->initialized && urt == &drv->urt) {
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
drv->dfu_tx_pending = false;
|
||||||
drv->urt_tx_complete = true;
|
drv->urt_tx_complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -938,19 +952,19 @@ bool nrf_system_off(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nrf_set_dfu_mode(void) {
|
#ifdef USE_SMP
|
||||||
|
void nrf_set_dfu_mode(bool set) {
|
||||||
nrf_driver_t *drv = &g_nrf_driver;
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
|
||||||
if (!drv->initialized) {
|
if (!drv->initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
drv->dfu_mode = set;
|
||||||
// if (nrf_reboot_to_bootloader()) {
|
|
||||||
// drv->mode_current = BLE_MODE_DFU;
|
if (set) {
|
||||||
// } else {
|
HAL_UART_Receive_IT(&drv->urt, &drv->urt_rx_byte, 1);
|
||||||
// drv->status_valid = false;
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nrf_is_dfu_mode(void) {
|
bool nrf_is_dfu_mode(void) {
|
||||||
@ -960,8 +974,27 @@ bool nrf_is_dfu_mode(void) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return drv->dfu_mode;
|
||||||
// TODO
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void nrf_send_uart_data(const uint8_t *data, uint32_t len) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized) {
|
||||||
|
while (drv->dfu_tx_pending) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->dfu_tx_pending = true;
|
||||||
|
|
||||||
|
HAL_UART_Transmit_IT(&drv->urt, data, len);
|
||||||
|
|
||||||
|
while (drv->dfu_tx_pending) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
147
core/embed/io/nrf/stm32u5/nrf_update.c
Normal file
147
core/embed/io/nrf/stm32u5/nrf_update.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Trezor project, https://trezor.io/
|
||||||
|
*
|
||||||
|
* Copyright (c) SatoshiLabs
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
#ifdef USE_SMP
|
||||||
|
|
||||||
|
#include <trezor_bsp.h>
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <io/nrf.h>
|
||||||
|
|
||||||
|
#include "../nrf_internal.h"
|
||||||
|
#include "rust_smp.h"
|
||||||
|
#include "sec/hash_processor.h"
|
||||||
|
#include "sys/systick.h"
|
||||||
|
|
||||||
|
#define IMAGE_HASH_LEN 32
|
||||||
|
#define IMAGE_TLV_SHA256 0x10
|
||||||
|
|
||||||
|
struct image_version {
|
||||||
|
uint8_t iv_major;
|
||||||
|
uint8_t iv_minor;
|
||||||
|
uint16_t iv_revision;
|
||||||
|
uint32_t iv_build_num;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct image_header {
|
||||||
|
uint32_t ih_magic;
|
||||||
|
uint32_t ih_load_addr;
|
||||||
|
uint16_t ih_hdr_size; /* Size of image header (bytes). */
|
||||||
|
uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */
|
||||||
|
uint32_t ih_img_size; /* Does not include header. */
|
||||||
|
uint32_t ih_flags; /* IMAGE_F_[...]. */
|
||||||
|
struct image_version ih_ver;
|
||||||
|
uint32_t _pad1;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the SHA-256 image hash from the TLV trailer of the given flash slot.
|
||||||
|
*
|
||||||
|
* @param binary_ptr pointer to the binary image
|
||||||
|
* @param out_hash Buffer of at least IMAGE_HASH_LEN bytes to receive the hash
|
||||||
|
* @return 0 on success, or a negative errno on failure
|
||||||
|
*/
|
||||||
|
static int read_image_sha256(const uint8_t *binary_ptr, size_t binary_size,
|
||||||
|
uint8_t out_hash[IMAGE_HASH_LEN]) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Read header to get image_size and hdr_size */
|
||||||
|
struct image_header *hdr = (struct image_header *)binary_ptr;
|
||||||
|
|
||||||
|
uint32_t img_size = hdr->ih_img_size;
|
||||||
|
uint32_t hdr_size = hdr->ih_hdr_size;
|
||||||
|
uint32_t tvl1_size = hdr->ih_protect_tlv_size;
|
||||||
|
|
||||||
|
/* Compute start of TLV trailer */
|
||||||
|
off_t off = 0 + hdr_size + img_size + tvl1_size + 4;
|
||||||
|
|
||||||
|
/* Scan TLVs until we find the SHA-256 entry */
|
||||||
|
while (true) {
|
||||||
|
uint16_t tlv_hdr[2];
|
||||||
|
|
||||||
|
if (off + sizeof(tlv_hdr) > binary_size) {
|
||||||
|
rc = -1; // Not enough data for TLV header
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(tlv_hdr, binary_ptr + off, sizeof(tlv_hdr));
|
||||||
|
|
||||||
|
uint16_t type = tlv_hdr[0];
|
||||||
|
uint16_t len = tlv_hdr[1];
|
||||||
|
|
||||||
|
if (off + sizeof(tlv_hdr) + len > binary_size) {
|
||||||
|
rc = -1; // Not enough data for TLV value
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == IMAGE_TLV_SHA256) {
|
||||||
|
if (len != IMAGE_HASH_LEN) {
|
||||||
|
rc = -1;
|
||||||
|
} else {
|
||||||
|
memcpy(out_hash, binary_ptr + off + sizeof(tlv_hdr), IMAGE_HASH_LEN);
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
off += sizeof(tlv_hdr) + len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_update_required(const uint8_t *image_ptr, size_t image_len) {
|
||||||
|
nrf_info_t info = {0};
|
||||||
|
|
||||||
|
uint16_t try_cntr = 0;
|
||||||
|
while (!nrf_get_info(&info)) {
|
||||||
|
nrf_reboot();
|
||||||
|
systick_delay_ms(500);
|
||||||
|
try_cntr++;
|
||||||
|
if (try_cntr > 3) {
|
||||||
|
// Assuming corrupted image, but we could also check comm with MCUboot
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t expected_hash[SHA256_DIGEST_LENGTH] = {0};
|
||||||
|
|
||||||
|
read_image_sha256(image_ptr, image_len, expected_hash);
|
||||||
|
|
||||||
|
return memcmp(info.hash, expected_hash, SHA256_DIGEST_LENGTH) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_update(const uint8_t *image_ptr, size_t image_len) {
|
||||||
|
nrf_reboot_to_bootloader();
|
||||||
|
nrf_set_dfu_mode(true);
|
||||||
|
|
||||||
|
uint8_t sha256[SHA256_DIGEST_LENGTH] = {0};
|
||||||
|
|
||||||
|
hash_processor_sha256_calc(image_ptr, image_len, sha256);
|
||||||
|
|
||||||
|
smp_upload_app_image(image_ptr, image_len, sha256, SHA256_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
smp_reset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
BIN
core/embed/models/T3W1/trezor-ble.bin
Normal file
BIN
core/embed/models/T3W1/trezor-ble.bin
Normal file
Binary file not shown.
@ -44,6 +44,15 @@
|
|||||||
#include "zkp_context.h"
|
#include "zkp_context.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
#include <io/nrf.h>
|
||||||
|
|
||||||
|
extern const void nrf_app_start;
|
||||||
|
extern const void nrf_app_end;
|
||||||
|
extern const void nrf_app_size;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
int main_func(uint32_t cmd, void *arg) {
|
int main_func(uint32_t cmd, void *arg) {
|
||||||
if (cmd == 1) {
|
if (cmd == 1) {
|
||||||
systask_postmortem_t *info = (systask_postmortem_t *)arg;
|
systask_postmortem_t *info = (systask_postmortem_t *)arg;
|
||||||
@ -51,7 +60,17 @@ int main_func(uint32_t cmd, void *arg) {
|
|||||||
system_exit(0);
|
system_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
screen_boot_stage_2(DISPLAY_JUMP_BEHAVIOR == DISPLAY_RESET_CONTENT);
|
bool fading = DISPLAY_JUMP_BEHAVIOR == DISPLAY_RESET_CONTENT;
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
if (nrf_update_required(&nrf_app_start, (size_t)&nrf_app_size)) {
|
||||||
|
screen_update();
|
||||||
|
nrf_update(&nrf_app_start, (size_t)&nrf_app_size);
|
||||||
|
fading = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
screen_boot_stage_2(fading);
|
||||||
|
|
||||||
#ifdef USE_SECP256K1_ZKP
|
#ifdef USE_SECP256K1_ZKP
|
||||||
ensure(sectrue * (zkp_context_init() == 0), NULL);
|
ensure(sectrue * (zkp_context_init() == 0), NULL);
|
||||||
|
7
core/embed/rust/Cargo.lock
generated
7
core/embed/rust/Cargo.lock
generated
@ -165,6 +165,12 @@ version = "2.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minicbor"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50699691433ccd88fbe1f11e9155691d71fc363595109f35a92b77be2e0158f6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -346,6 +352,7 @@ dependencies = [
|
|||||||
"easer",
|
"easer",
|
||||||
"glob",
|
"glob",
|
||||||
"heapless",
|
"heapless",
|
||||||
|
"minicbor",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pareen",
|
"pareen",
|
||||||
|
@ -46,6 +46,8 @@ backlight = []
|
|||||||
usb = []
|
usb = []
|
||||||
optiga = []
|
optiga = []
|
||||||
ble = []
|
ble = []
|
||||||
|
nrf = []
|
||||||
|
smp = []
|
||||||
tropic = []
|
tropic = []
|
||||||
translations = ["crypto"]
|
translations = ["crypto"]
|
||||||
secmon_layout = []
|
secmon_layout = []
|
||||||
@ -58,8 +60,10 @@ test = [
|
|||||||
"debug",
|
"debug",
|
||||||
"glob",
|
"glob",
|
||||||
"micropython",
|
"micropython",
|
||||||
|
"nrf",
|
||||||
"optiga",
|
"optiga",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
|
"smp",
|
||||||
"touch",
|
"touch",
|
||||||
"translations",
|
"translations",
|
||||||
"ui",
|
"ui",
|
||||||
@ -141,6 +145,10 @@ version = "0.3.0"
|
|||||||
default-features = false
|
default-features = false
|
||||||
features = ["libm"]
|
features = ["libm"]
|
||||||
|
|
||||||
|
[dependencies.minicbor]
|
||||||
|
version = "1.0.0"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
|
||||||
# Build dependencies
|
# Build dependencies
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
|
|||||||
"-I../io/button/inc",
|
"-I../io/button/inc",
|
||||||
"-I../io/display/inc",
|
"-I../io/display/inc",
|
||||||
"-I../io/haptic/inc",
|
"-I../io/haptic/inc",
|
||||||
|
"-I../io/nrf/inc",
|
||||||
"-I../io/touch/inc",
|
"-I../io/touch/inc",
|
||||||
"-I../io/rgb_led/inc",
|
"-I../io/rgb_led/inc",
|
||||||
"-I../io/usb/inc",
|
"-I../io/usb/inc",
|
||||||
@ -46,6 +47,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
|
|||||||
"-I../sys/time/inc",
|
"-I../sys/time/inc",
|
||||||
"-I../sys/task/inc",
|
"-I../sys/task/inc",
|
||||||
"-I../sys/power_manager/inc",
|
"-I../sys/power_manager/inc",
|
||||||
|
"-I../sys/irq/inc",
|
||||||
"-I../util/flash/inc",
|
"-I../util/flash/inc",
|
||||||
"-I../util/translations/inc",
|
"-I../util/translations/inc",
|
||||||
"-I../models",
|
"-I../models",
|
||||||
@ -56,6 +58,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
|
|||||||
"-DUSE_RGB_LED",
|
"-DUSE_RGB_LED",
|
||||||
"-DUSE_BLE",
|
"-DUSE_BLE",
|
||||||
"-DUSE_POWER_MANAGER",
|
"-DUSE_POWER_MANAGER",
|
||||||
|
"-DUSE_NRF",
|
||||||
"-DUSE_HW_JPEG_DECODER",
|
"-DUSE_HW_JPEG_DECODER",
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -449,6 +452,11 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_type("pm_event_t")
|
.allowlist_type("pm_event_t")
|
||||||
.allowlist_function("pm_get_events")
|
.allowlist_function("pm_get_events")
|
||||||
.allowlist_function("pm_get_state")
|
.allowlist_function("pm_get_state")
|
||||||
|
// irq
|
||||||
|
.allowlist_function("irq_lock_fn")
|
||||||
|
.allowlist_function("irq_unlock_fn")
|
||||||
|
// nrf
|
||||||
|
.allowlist_function("nrf_send_uart_data")
|
||||||
// c_layout
|
// c_layout
|
||||||
.allowlist_type("c_layout_t");
|
.allowlist_type("c_layout_t");
|
||||||
|
|
||||||
|
12
core/embed/rust/rust_smp.h
Normal file
12
core/embed/rust/rust_smp.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
bool smp_echo(const char* text, uint8_t text_len);
|
||||||
|
|
||||||
|
void smp_reset(void);
|
||||||
|
|
||||||
|
void smp_process_rx_byte(uint8_t byte);
|
||||||
|
|
||||||
|
bool smp_upload_app_image(const uint8_t* data, size_t len,
|
||||||
|
const uint8_t* image_hash, size_t image_hash_len);
|
@ -5,6 +5,8 @@ void display_rsod_rust(const char* title, const char* message,
|
|||||||
|
|
||||||
void screen_boot_stage_2(bool fade_in);
|
void screen_boot_stage_2(bool fade_in);
|
||||||
|
|
||||||
|
void screen_update(void);
|
||||||
|
|
||||||
void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen);
|
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,
|
void display_icon(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen,
|
||||||
uint16_t fg_color, uint16_t bg_color);
|
uint16_t fg_color, uint16_t bg_color);
|
||||||
|
@ -44,6 +44,8 @@ mod trezorhal;
|
|||||||
#[cfg(feature = "ui")]
|
#[cfg(feature = "ui")]
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|
||||||
|
#[cfg(feature = "smp")]
|
||||||
|
pub mod smp;
|
||||||
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
33
core/embed/rust/src/smp/api.rs
Normal file
33
core/embed/rust/src/smp/api.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use super::{echo, process_rx_byte, reset, upload};
|
||||||
|
|
||||||
|
use crate::util::from_c_array;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn smp_echo(text: *const cty::c_char, text_len: u8) -> bool {
|
||||||
|
let text = unwrap!(unsafe { from_c_array(text, text_len as usize) });
|
||||||
|
|
||||||
|
echo::send(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn smp_reset() {
|
||||||
|
reset::send();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn smp_upload_app_image(
|
||||||
|
data: *const cty::uint8_t,
|
||||||
|
len: cty::size_t,
|
||||||
|
image_hash: *const cty::uint8_t,
|
||||||
|
image_hash_len: cty::size_t,
|
||||||
|
) -> bool {
|
||||||
|
let data_slice = unsafe { core::slice::from_raw_parts(data, len) };
|
||||||
|
let hash_slice = unsafe { core::slice::from_raw_parts(image_hash, image_hash_len) };
|
||||||
|
|
||||||
|
upload::upload_image(data_slice, hash_slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn smp_process_rx_byte(byte: u8) {
|
||||||
|
process_rx_byte(byte)
|
||||||
|
}
|
166
core/embed/rust/src/smp/base64.rs
Normal file
166
core/embed/rust/src/smp/base64.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/// Provides Base64 encoding and decoding in a `no_std` environment.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - `OutputBufferTooSmall` if the provided output buffer is too short.
|
||||||
|
/// - `InvalidLength` if the input length is not a multiple of 4 for decoding.
|
||||||
|
/// - `InvalidCharacter` if an invalid Base64 character is encountered during
|
||||||
|
/// decoding.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Base64Error {
|
||||||
|
OutputBufferTooSmall,
|
||||||
|
InvalidLength,
|
||||||
|
InvalidCharacter,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Base64 encoding table
|
||||||
|
static B64_TABLE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
/// Table for calculating padding bytes (0, 2, 1) based on input length mod 3
|
||||||
|
static MOD_TABLE: [usize; 3] = [0, 2, 1];
|
||||||
|
|
||||||
|
/// Encodes `input` bytes into Base64, writing into `output`.
|
||||||
|
/// Returns the number of bytes written on success.
|
||||||
|
pub fn base64_encode(input: &[u8], output: &mut [u8]) -> Result<usize, Base64Error> {
|
||||||
|
let len = input.len();
|
||||||
|
let output_len = 4 * len.div_ceil(3);
|
||||||
|
if output.len() < output_len {
|
||||||
|
return Err(Base64Error::OutputBufferTooSmall);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let mut j = 0;
|
||||||
|
while i < len {
|
||||||
|
let a = input[i];
|
||||||
|
let b = if i + 1 < len { input[i + 1] } else { 0 };
|
||||||
|
let c = if i + 2 < len { input[i + 2] } else { 0 };
|
||||||
|
i += 3;
|
||||||
|
|
||||||
|
let triple = ((a as u32) << 16) | ((b as u32) << 8) | (c as u32);
|
||||||
|
output[j] = B64_TABLE[((triple >> 18) & 0x3F) as usize];
|
||||||
|
output[j + 1] = B64_TABLE[((triple >> 12) & 0x3F) as usize];
|
||||||
|
output[j + 2] = B64_TABLE[((triple >> 6) & 0x3F) as usize];
|
||||||
|
output[j + 3] = B64_TABLE[(triple & 0x3F) as usize];
|
||||||
|
j += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply padding
|
||||||
|
for pad in 0..MOD_TABLE[len % 3] {
|
||||||
|
output[output_len - 1 - pad] = b'=';
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps a Base64 character to its 6-bit value.
|
||||||
|
fn base64_char_value(c: u8) -> Option<u8> {
|
||||||
|
match c {
|
||||||
|
b'A'..=b'Z' => Some(c - b'A'),
|
||||||
|
b'a'..=b'z' => Some(c - b'a' + 26),
|
||||||
|
b'0'..=b'9' => Some(c - b'0' + 52),
|
||||||
|
b'+' => Some(62),
|
||||||
|
b'/' => Some(63),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes Base64 `input` bytes into raw bytes, writing into `output`.
|
||||||
|
/// Returns the number of bytes written on success.
|
||||||
|
pub fn base64_decode(input: &[u8], output: &mut [u8]) -> Result<usize, Base64Error> {
|
||||||
|
let len = input.len();
|
||||||
|
if len % 4 != 0 {
|
||||||
|
return Err(Base64Error::InvalidLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count padding '=' bytes
|
||||||
|
let mut padding = 0;
|
||||||
|
if len >= 2 {
|
||||||
|
if input[len - 1] == b'=' {
|
||||||
|
padding += 1;
|
||||||
|
}
|
||||||
|
if input[len - 2] == b'=' {
|
||||||
|
padding += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoded_len = (len / 4) * 3 - padding;
|
||||||
|
if output.len() < decoded_len {
|
||||||
|
return Err(Base64Error::OutputBufferTooSmall);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let mut j = 0;
|
||||||
|
while i < len {
|
||||||
|
let v1 = base64_char_value(input[i]).ok_or(Base64Error::InvalidCharacter)? as u32;
|
||||||
|
let v2 = base64_char_value(input[i + 1]).ok_or(Base64Error::InvalidCharacter)? as u32;
|
||||||
|
let v3 = if input[i + 2] == b'=' {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
base64_char_value(input[i + 2]).ok_or(Base64Error::InvalidCharacter)? as u32
|
||||||
|
};
|
||||||
|
let v4 = if input[i + 3] == b'=' {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
base64_char_value(input[i + 3]).ok_or(Base64Error::InvalidCharacter)? as u32
|
||||||
|
};
|
||||||
|
i += 4;
|
||||||
|
|
||||||
|
let triple = (v1 << 18) | (v2 << 12) | (v3 << 6) | v4;
|
||||||
|
output[j] = ((triple >> 16) & 0xFF) as u8;
|
||||||
|
j += 1;
|
||||||
|
if input[i - 2] != b'=' {
|
||||||
|
output[j] = ((triple >> 8) & 0xFF) as u8;
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
if input[i - 1] != b'=' {
|
||||||
|
output[j] = (triple & 0xFF) as u8;
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(decoded_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing uses std; keeps `no_std` for library code.
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_decode() {
|
||||||
|
let tests: [(&[u8], &[u8]); 7] = [
|
||||||
|
(b"", b""),
|
||||||
|
(b"f", b"Zg=="),
|
||||||
|
(b"fo", b"Zm8="),
|
||||||
|
(b"foo", b"Zm9v"),
|
||||||
|
(b"foob", b"Zm9vYg=="),
|
||||||
|
(b"fooba", b"Zm9vYmE="),
|
||||||
|
(b"foobar", b"Zm9vYmFy"),
|
||||||
|
];
|
||||||
|
for &(input, expected) in &tests {
|
||||||
|
let mut enc_buf = [0u8; 16];
|
||||||
|
let len = base64_encode(input, &mut enc_buf).unwrap();
|
||||||
|
assert_eq!(&enc_buf[..len], expected);
|
||||||
|
|
||||||
|
let mut dec_buf = [0u8; 16];
|
||||||
|
let dec_len = base64_decode(&enc_buf[..len], &mut dec_buf).unwrap();
|
||||||
|
assert_eq!(&dec_buf[..dec_len], input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_decode() {
|
||||||
|
let mut buf = [0u8; 16];
|
||||||
|
assert!(matches!(
|
||||||
|
base64_decode(b"abc", &mut buf),
|
||||||
|
Err(Base64Error::InvalidLength)
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
base64_decode(b"!!!!", &mut buf),
|
||||||
|
Err(Base64Error::InvalidCharacter)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
14
core/embed/rust/src/smp/crc16.rs
Normal file
14
core/embed/rust/src/smp/crc16.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/// Compute CRC-16-ITU-T over `data`, starting from `seed`.
|
||||||
|
pub fn crc16_itu_t(mut seed: u16, data: &[u8]) -> u16 {
|
||||||
|
for &byte in data {
|
||||||
|
// swap high/low byte:
|
||||||
|
seed = seed.rotate_left(8);
|
||||||
|
// mix in next input byte
|
||||||
|
seed ^= byte as u16;
|
||||||
|
// apply the ITU-T polynomial bitwise mix
|
||||||
|
seed ^= (seed & 0x00FF) >> 4;
|
||||||
|
seed ^= seed << 12;
|
||||||
|
seed ^= (seed & 0x00FF) << 5;
|
||||||
|
}
|
||||||
|
seed
|
||||||
|
}
|
76
core/embed/rust/src/smp/echo.rs
Normal file
76
core/embed/rust/src/smp/echo.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use super::{
|
||||||
|
receiver_acquire, send_request, wait_for_response, MsgType, SmpBuffer, SmpHeader,
|
||||||
|
SMP_CMD_ID_ECHO, SMP_GROUP_OS, SMP_HEADER_SIZE, SMP_OP_READ,
|
||||||
|
};
|
||||||
|
use crate::time::Duration;
|
||||||
|
use minicbor::{data::Type, decode, Decoder, Encoder};
|
||||||
|
|
||||||
|
pub fn send(text: &str) -> bool {
|
||||||
|
let mut cbor_data = [0u8; 64];
|
||||||
|
let mut data = [0u8; 64];
|
||||||
|
let mut buffer = [0u8; 64];
|
||||||
|
|
||||||
|
let mut writer = SmpBuffer::new(&mut cbor_data);
|
||||||
|
|
||||||
|
let mut enc = Encoder::new(&mut writer);
|
||||||
|
unwrap!(enc.map(1));
|
||||||
|
unwrap!(enc.str("d"));
|
||||||
|
unwrap!(enc.str(text));
|
||||||
|
|
||||||
|
unwrap!(receiver_acquire());
|
||||||
|
|
||||||
|
let data_len = writer.bytes_written();
|
||||||
|
|
||||||
|
let header = SmpHeader::new(SMP_OP_READ, data_len, SMP_GROUP_OS, 0, SMP_CMD_ID_ECHO).to_bytes();
|
||||||
|
|
||||||
|
data[..SMP_HEADER_SIZE].copy_from_slice(&header);
|
||||||
|
data[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_len].copy_from_slice(&cbor_data[..data_len]);
|
||||||
|
|
||||||
|
send_request(&mut data[..SMP_HEADER_SIZE + data_len], &mut buffer);
|
||||||
|
|
||||||
|
let mut resp_buffer = [0u8; 64];
|
||||||
|
if wait_for_response(MsgType::Echo, &mut resp_buffer, Duration::from_millis(100)).is_ok() {
|
||||||
|
let echo_msg = process_msg(&resp_buffer);
|
||||||
|
return if let Ok(msg) = echo_msg {
|
||||||
|
msg == text
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_msg(buf: &[u8]) -> Result<&str, decode::Error> {
|
||||||
|
let mut dec = Decoder::new(buf);
|
||||||
|
|
||||||
|
match dec.map()? {
|
||||||
|
Some(n) => {
|
||||||
|
// definite-length: iterate exactly n times
|
||||||
|
for _ in 0..n {
|
||||||
|
let key = dec.str()?;
|
||||||
|
let val = dec.str()?;
|
||||||
|
if key == "r" {
|
||||||
|
return Ok(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// indefinite-length: keep reading until we hit the "break"
|
||||||
|
loop {
|
||||||
|
// peek at the next major type
|
||||||
|
if let Type::Break = dec.datatype()? {
|
||||||
|
dec.skip()?; // consume the break
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let key = dec.str()?;
|
||||||
|
let val = dec.str()?;
|
||||||
|
if key == "r" {
|
||||||
|
return Ok(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(decode::Error::message("key \"r\" not found"))
|
||||||
|
}
|
472
core/embed/rust/src/smp/mod.rs
Normal file
472
core/embed/rust/src/smp/mod.rs
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
mod api;
|
||||||
|
mod base64;
|
||||||
|
mod crc16;
|
||||||
|
mod echo;
|
||||||
|
mod reset;
|
||||||
|
mod upload;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
time::{Duration, Instant},
|
||||||
|
trezorhal::{
|
||||||
|
irq::{irq_lock, irq_unlock},
|
||||||
|
nrf::send_data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use base64::{base64_decode, base64_encode};
|
||||||
|
use core::{cell::UnsafeCell, convert::Infallible};
|
||||||
|
use crc16::crc16_itu_t;
|
||||||
|
use minicbor::encode::write::Write;
|
||||||
|
|
||||||
|
pub const SMP_HEADER_SIZE: usize = 8;
|
||||||
|
|
||||||
|
pub const SMP_GROUP_OS: u16 = 0;
|
||||||
|
pub const SMP_GROUP_IMAGE: u16 = 1;
|
||||||
|
|
||||||
|
pub const SMP_CMD_ID_ECHO: u8 = 0;
|
||||||
|
pub const SMP_CMD_ID_RESET: u8 = 5;
|
||||||
|
pub const SMP_CMD_ID_IMAGE_UPLOAD: u8 = 1;
|
||||||
|
|
||||||
|
pub const SMP_OP_READ: u8 = 0;
|
||||||
|
pub const SMP_OP_READ_RSP: u8 = 1;
|
||||||
|
pub const SMP_OP_WRITE: u8 = 2;
|
||||||
|
pub const SMP_OP_WRITE_RSP: u8 = 3;
|
||||||
|
|
||||||
|
const MSG_HEADER_SIZE: usize = 2;
|
||||||
|
const MSG_FOOTER_SIZE: usize = 2;
|
||||||
|
|
||||||
|
const FRAME_HEADER_SIZE: usize = 2;
|
||||||
|
const FRAME_FOOTER_SIZE: usize = 1; // newline
|
||||||
|
|
||||||
|
// Frame sizing
|
||||||
|
const BOOT_SERIAL_FRAME_MTU_BIN: usize = 93;
|
||||||
|
const BOOT_SERIAL_FRAME_MTU: usize = 124;
|
||||||
|
const BOOT_SERIAL_MAX_MSG_SIZE: usize = 512;
|
||||||
|
|
||||||
|
// Frame start bytes
|
||||||
|
const START_INIT_FRAME_BYTE_0: u8 = 6;
|
||||||
|
const START_INIT_FRAME_BYTE_1: u8 = 9;
|
||||||
|
const START_CONT_FRAME_BYTE_0: u8 = 4;
|
||||||
|
const START_CONT_FRAME_BYTE_1: u8 = 20;
|
||||||
|
|
||||||
|
/// ReceiverStorage wraps an UnsafeCell<Option<SmpReceiver>> in a static.
|
||||||
|
/// SAFETY: We need manual synchronization (irq_lock/irq_unlock) whenever
|
||||||
|
/// accessing this static. UnsafeCell allows interior mutability, but we must
|
||||||
|
/// ensure only one context writes at a time.
|
||||||
|
struct ReceiverStorage(UnsafeCell<Option<SmpReceiver>>);
|
||||||
|
static SMP_RECEIVER: ReceiverStorage = ReceiverStorage(UnsafeCell::new(None));
|
||||||
|
|
||||||
|
/// We assert that it is safe to share `ReceiverStorage` across
|
||||||
|
/// threads/interrupt contexts because we manually lock interrupts
|
||||||
|
/// (irq_lock/irq_unlock) around all accesses. SAFETY: If any code touches
|
||||||
|
/// SMP_RECEIVER without locking IRQ, data races could occur.
|
||||||
|
unsafe impl Sync for ReceiverStorage {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SmpError {
|
||||||
|
Timeout,
|
||||||
|
WrongMessage,
|
||||||
|
Busy,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SmpHeader {
|
||||||
|
op: u8,
|
||||||
|
_reserved: u8,
|
||||||
|
len: usize,
|
||||||
|
group: u16,
|
||||||
|
seq: u8,
|
||||||
|
cmd_id: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SmpHeader {
|
||||||
|
pub fn new(op: u8, len: usize, group: u16, seq: u8, cmd_id: u8) -> Self {
|
||||||
|
SmpHeader {
|
||||||
|
op,
|
||||||
|
_reserved: 0,
|
||||||
|
len,
|
||||||
|
group,
|
||||||
|
seq,
|
||||||
|
cmd_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(b: &[u8]) -> Self {
|
||||||
|
// we assume b.len() >= HEADER_SIZE
|
||||||
|
|
||||||
|
let len: u16 = u16::from_be_bytes([b[2], b[3]]);
|
||||||
|
let group: u16 = u16::from_be_bytes([b[4], b[5]]); // [hi, lo]
|
||||||
|
|
||||||
|
SmpHeader {
|
||||||
|
op: b[0],
|
||||||
|
_reserved: b[1],
|
||||||
|
len: len as usize,
|
||||||
|
group,
|
||||||
|
seq: b[6],
|
||||||
|
cmd_id: b[7],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> [u8; SMP_HEADER_SIZE] {
|
||||||
|
let len_be = (self.len as u16).to_be_bytes(); // [hi, lo]
|
||||||
|
let group_be = self.group.to_be_bytes(); // [hi, lo]
|
||||||
|
[
|
||||||
|
self.op,
|
||||||
|
self._reserved,
|
||||||
|
len_be[0],
|
||||||
|
len_be[1],
|
||||||
|
group_be[0],
|
||||||
|
group_be[1],
|
||||||
|
self.seq,
|
||||||
|
self.cmd_id,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_request(data: &[u8], out: &mut [u8]) {
|
||||||
|
let len = data.len();
|
||||||
|
|
||||||
|
if out.len() < len + MSG_HEADER_SIZE + MSG_FOOTER_SIZE {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// length including CRC (2 bytes) and length field itself
|
||||||
|
let length_field = (len + MSG_HEADER_SIZE) as u16;
|
||||||
|
out[0] = (length_field >> 8) as u8;
|
||||||
|
out[1] = (length_field & 0xFF) as u8;
|
||||||
|
|
||||||
|
// copy the payload
|
||||||
|
out[MSG_HEADER_SIZE..MSG_HEADER_SIZE + len].copy_from_slice(data);
|
||||||
|
|
||||||
|
// compute CRC
|
||||||
|
let crc = crc16_itu_t(0, data);
|
||||||
|
|
||||||
|
// append CRC hi/lo
|
||||||
|
out[len + MSG_HEADER_SIZE] = (crc >> 8) as u8;
|
||||||
|
out[len + MSG_HEADER_SIZE + 1] = (crc & 0xFF) as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_request(data: &mut [u8], buffer: &mut [u8]) {
|
||||||
|
encode_request(data, buffer);
|
||||||
|
|
||||||
|
let total = data.len() + MSG_HEADER_SIZE + MSG_FOOTER_SIZE;
|
||||||
|
|
||||||
|
let data = &buffer[..total];
|
||||||
|
|
||||||
|
// One buffer big enough for header + max‐encoded data + newline
|
||||||
|
// header = 2 bytes
|
||||||
|
// base64 of BOOT_SERIAL_FRAME_MTU_BIN fits in BOOT_SERIAL_FRAME_MTU
|
||||||
|
// newline = 1 byte
|
||||||
|
let mut buf = [0u8; FRAME_HEADER_SIZE + BOOT_SERIAL_FRAME_MTU + FRAME_FOOTER_SIZE];
|
||||||
|
|
||||||
|
let mut init_frame = true;
|
||||||
|
for chunk in data.chunks(BOOT_SERIAL_FRAME_MTU_BIN) {
|
||||||
|
// 1) write the two‐byte header
|
||||||
|
let (b0, b1) = if init_frame {
|
||||||
|
(START_INIT_FRAME_BYTE_0, START_INIT_FRAME_BYTE_1)
|
||||||
|
} else {
|
||||||
|
(START_CONT_FRAME_BYTE_0, START_CONT_FRAME_BYTE_1)
|
||||||
|
};
|
||||||
|
buf[0] = b0;
|
||||||
|
buf[1] = b1;
|
||||||
|
|
||||||
|
let enc_len = unwrap!(base64_encode(chunk, &mut buf[FRAME_HEADER_SIZE..]));
|
||||||
|
|
||||||
|
// 3) append newline
|
||||||
|
let total_len = FRAME_HEADER_SIZE + enc_len;
|
||||||
|
buf[total_len] = b'\n';
|
||||||
|
|
||||||
|
// 4) send it out
|
||||||
|
send_data(&buf[..total_len + FRAME_FOOTER_SIZE]);
|
||||||
|
|
||||||
|
init_frame = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple writer that copies into a `&mut [u8]` and counts bytes written.
|
||||||
|
pub struct SmpBuffer<'a> {
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
written: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SmpBuffer<'a> {
|
||||||
|
/// Wrap your buffer:
|
||||||
|
pub fn new(buf: &'a mut [u8]) -> Self {
|
||||||
|
SmpBuffer { buf, written: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How many bytes have been written so far?
|
||||||
|
pub fn bytes_written(&self) -> usize {
|
||||||
|
self.written
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the filled portion of the buffer.
|
||||||
|
pub fn filled(&self) -> &[u8] {
|
||||||
|
&self.buf[..self.written]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Write for SmpBuffer<'a> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
let end = self.written + data.len();
|
||||||
|
// In production you might guard against overflow:
|
||||||
|
// if end > self.buf.len() { return Err(...); }
|
||||||
|
self.buf[self.written..end].copy_from_slice(data);
|
||||||
|
self.written = end;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
pub enum MsgType {
|
||||||
|
Echo,
|
||||||
|
ImageUploadResponse,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct SmpReceiver {
|
||||||
|
rx_frame: [u8; BOOT_SERIAL_FRAME_MTU + FRAME_HEADER_SIZE + FRAME_FOOTER_SIZE],
|
||||||
|
rx_frame_len: usize,
|
||||||
|
rx_frame_dec: [u8; BOOT_SERIAL_FRAME_MTU + FRAME_HEADER_SIZE + FRAME_FOOTER_SIZE],
|
||||||
|
rx_msg: [u8; BOOT_SERIAL_MAX_MSG_SIZE],
|
||||||
|
rx_msg_len: usize,
|
||||||
|
msg_type: Option<MsgType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SmpReceiver {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
rx_frame: [0; BOOT_SERIAL_FRAME_MTU + FRAME_HEADER_SIZE + FRAME_FOOTER_SIZE],
|
||||||
|
rx_frame_len: 0,
|
||||||
|
rx_frame_dec: [0; BOOT_SERIAL_FRAME_MTU + FRAME_HEADER_SIZE + FRAME_FOOTER_SIZE],
|
||||||
|
rx_msg: [0; BOOT_SERIAL_MAX_MSG_SIZE],
|
||||||
|
rx_msg_len: 0,
|
||||||
|
msg_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call this for each incoming byte
|
||||||
|
pub fn process_byte(&mut self, byte: u8) {
|
||||||
|
if self.msg_type.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if byte == b'\n' {
|
||||||
|
// end of a frame
|
||||||
|
if self.rx_frame_len > 0 {
|
||||||
|
let frame = &self.rx_frame[..self.rx_frame_len];
|
||||||
|
|
||||||
|
// init or continuation?
|
||||||
|
if frame[0] == START_INIT_FRAME_BYTE_0 && frame[1] == START_INIT_FRAME_BYTE_1 {
|
||||||
|
self.rx_msg_len = 0;
|
||||||
|
self.process_frame();
|
||||||
|
} else if frame[0] == START_CONT_FRAME_BYTE_0
|
||||||
|
&& frame[1] == START_CONT_FRAME_BYTE_1
|
||||||
|
&& self.rx_msg_len != 0
|
||||||
|
{
|
||||||
|
self.process_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reset for next frame
|
||||||
|
self.rx_frame_len = 0;
|
||||||
|
} else {
|
||||||
|
// accumulate into smp_rx_frame[]
|
||||||
|
if self.rx_frame_len < self.rx_frame.len() {
|
||||||
|
self.rx_frame[self.rx_frame_len] = byte;
|
||||||
|
self.rx_frame_len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle one decoded frame chunk
|
||||||
|
fn process_frame(&mut self) {
|
||||||
|
// Base64‐decode into rx_frame_dec[]
|
||||||
|
let decode_res =
|
||||||
|
base64_decode(&self.rx_frame[2..self.rx_frame_len], &mut self.rx_frame_dec);
|
||||||
|
|
||||||
|
if let Ok(len) = decode_res {
|
||||||
|
if len > 0 {
|
||||||
|
// copy into rx_msg at current offset
|
||||||
|
let remaining = self.rx_msg.len().saturating_sub(self.rx_msg_len);
|
||||||
|
let copy_len = len.min(remaining);
|
||||||
|
|
||||||
|
self.rx_msg[self.rx_msg_len..self.rx_msg_len + copy_len]
|
||||||
|
.copy_from_slice(&self.rx_frame_dec[..copy_len]);
|
||||||
|
|
||||||
|
let received_len = self.rx_msg_len + len;
|
||||||
|
|
||||||
|
// the first two bytes of rx_msg are the length field
|
||||||
|
let msg_len = ((self.rx_msg[0] as u16) << 8) | (self.rx_msg[1] as u16);
|
||||||
|
|
||||||
|
// too long? (received_len - 2) > msg_len
|
||||||
|
if received_len.saturating_sub(2) > msg_len as usize {
|
||||||
|
self.rx_msg_len = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance offset by the *full* decoded length
|
||||||
|
self.rx_msg_len = received_len;
|
||||||
|
|
||||||
|
// complete?
|
||||||
|
if received_len.saturating_sub(2) == msg_len as usize {
|
||||||
|
// TODO: CRC check here
|
||||||
|
|
||||||
|
self.process_msg(msg_len as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_msg(self: &mut SmpReceiver, msg_len: usize) {
|
||||||
|
// hand off [2..2+msg_len] as header+payload
|
||||||
|
let start = MSG_HEADER_SIZE;
|
||||||
|
let end = start + msg_len;
|
||||||
|
|
||||||
|
let msg = &self.rx_msg[start..end];
|
||||||
|
|
||||||
|
// too short?
|
||||||
|
if msg.len() < SMP_HEADER_SIZE {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hdr = SmpHeader::from_bytes(&msg[..SMP_HEADER_SIZE]);
|
||||||
|
let group = hdr.group;
|
||||||
|
let cmd_id = hdr.cmd_id;
|
||||||
|
|
||||||
|
match (group, cmd_id) {
|
||||||
|
(SMP_GROUP_OS, SMP_CMD_ID_ECHO) => {
|
||||||
|
self.msg_type = Some(MsgType::Echo);
|
||||||
|
}
|
||||||
|
(SMP_GROUP_IMAGE, SMP_CMD_ID_IMAGE_UPLOAD) => {
|
||||||
|
self.msg_type = Some(MsgType::ImageUploadResponse);
|
||||||
|
}
|
||||||
|
_ => self.msg_type = Some(MsgType::Unknown),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called from interrupt context.
|
||||||
|
pub fn process_rx_byte(byte: u8) {
|
||||||
|
// SAFETY: Called from interrupt context so no concurrency
|
||||||
|
unsafe {
|
||||||
|
let opt_ref: &mut Option<SmpReceiver> = &mut *SMP_RECEIVER.0.get();
|
||||||
|
if let Some(receiver) = opt_ref.as_mut() {
|
||||||
|
receiver.process_byte(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver_release() {
|
||||||
|
let key = irq_lock();
|
||||||
|
|
||||||
|
// SAFETY: Protected by IRQ lock. Resets Option<SmpReceiver> → None
|
||||||
|
unsafe {
|
||||||
|
let opt_ref: &mut Option<SmpReceiver> = &mut *SMP_RECEIVER.0.get();
|
||||||
|
*opt_ref = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver_acquire() -> Result<(), SmpError> {
|
||||||
|
let key = irq_lock();
|
||||||
|
|
||||||
|
// SAFETY: Protected by IRQ lock
|
||||||
|
let already_acquired = unsafe {
|
||||||
|
let opt_ref: &Option<SmpReceiver> = &*SMP_RECEIVER.0.get();
|
||||||
|
opt_ref.is_some()
|
||||||
|
};
|
||||||
|
|
||||||
|
if already_acquired {
|
||||||
|
irq_unlock(key);
|
||||||
|
return Err(SmpError::Busy);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_rcv = SmpReceiver::new();
|
||||||
|
// SAFETY: Protected by IRQ lock
|
||||||
|
unsafe {
|
||||||
|
let opt_mut: &mut Option<SmpReceiver> = &mut *SMP_RECEIVER.0.get();
|
||||||
|
*opt_mut = Some(new_rcv);
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read message type without removing it.
|
||||||
|
/// SAFETY: Unsafe because we access static mutable without compile-time borrow
|
||||||
|
/// checks. Must always be called with IRQ lock held.
|
||||||
|
unsafe fn receiver_read_msg_type() -> Option<MsgType> {
|
||||||
|
// SAFETY: Caller must hold lock to avoid races.
|
||||||
|
let msg_type = unsafe {
|
||||||
|
let opt_ref: &Option<SmpReceiver> = &*SMP_RECEIVER.0.get();
|
||||||
|
unwrap!(opt_ref.as_ref().map(|r| r.msg_type))
|
||||||
|
};
|
||||||
|
|
||||||
|
msg_type
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy received message payload (excluding header/footer) into `buf`.
|
||||||
|
/// Returns the payload length on success.
|
||||||
|
/// SAFETY: Caller must hold IRQ lock, and `buf` must be large enough.
|
||||||
|
/// Also, `unwrap!` will panic if `opt_ref` is None (i.e., no receiver
|
||||||
|
/// acquired).
|
||||||
|
unsafe fn received_read_msg(buf: &mut [u8]) -> Result<usize, SmpError> {
|
||||||
|
// SAFETY: Caller held lock, so safe to read.
|
||||||
|
let receiver_ref = unsafe {
|
||||||
|
let opt_ref: &Option<SmpReceiver> = &*SMP_RECEIVER.0.get();
|
||||||
|
unwrap!(opt_ref.as_ref(), "Receiver is not initialized")
|
||||||
|
};
|
||||||
|
|
||||||
|
if receiver_ref.rx_msg_len == 0 {
|
||||||
|
return Err(SmpError::WrongMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_start = MSG_HEADER_SIZE + SMP_HEADER_SIZE;
|
||||||
|
let data_end = receiver_ref.rx_msg_len - MSG_FOOTER_SIZE;
|
||||||
|
let data = &receiver_ref.rx_msg[data_start..data_end];
|
||||||
|
let data_len = data.len();
|
||||||
|
|
||||||
|
if data_len > buf.len() {
|
||||||
|
fatal_error!("Buffer too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[..data_len].copy_from_slice(data);
|
||||||
|
|
||||||
|
Ok(data_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_for_response(
|
||||||
|
expected_msg_type: MsgType,
|
||||||
|
buf: &mut [u8],
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<usize, SmpError> {
|
||||||
|
let start = Instant::now();
|
||||||
|
loop {
|
||||||
|
let key = irq_lock();
|
||||||
|
// SAFETY: IRQ locked
|
||||||
|
let msg_type = unsafe { receiver_read_msg_type() };
|
||||||
|
irq_unlock(key);
|
||||||
|
if let Some(msg_type) = msg_type {
|
||||||
|
if msg_type != expected_msg_type {
|
||||||
|
return Err(SmpError::WrongMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = irq_lock();
|
||||||
|
// SAFETY: IRQ locked, safe to read and clear receiver
|
||||||
|
let len = unsafe { unwrap!(received_read_msg(buf)) };
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
receiver_release();
|
||||||
|
|
||||||
|
return Ok(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if Instant::now().checked_duration_since(start) > Some(timeout) {
|
||||||
|
// timeout reached
|
||||||
|
receiver_release();
|
||||||
|
return Err(SmpError::Timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
core/embed/rust/src/smp/reset.rs
Normal file
27
core/embed/rust/src/smp/reset.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use minicbor::Encoder;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
send_request, SmpBuffer, SmpHeader, SMP_CMD_ID_RESET, SMP_GROUP_OS, SMP_HEADER_SIZE,
|
||||||
|
SMP_OP_READ,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn send() {
|
||||||
|
let mut cbor_data = [0u8; 64];
|
||||||
|
let mut data = [0u8; 64];
|
||||||
|
let mut buffer = [0u8; 64];
|
||||||
|
|
||||||
|
let mut writer = SmpBuffer::new(&mut cbor_data);
|
||||||
|
|
||||||
|
let mut enc = Encoder::new(&mut writer);
|
||||||
|
unwrap!(enc.map(0));
|
||||||
|
|
||||||
|
let data_len = writer.bytes_written();
|
||||||
|
|
||||||
|
let header =
|
||||||
|
SmpHeader::new(SMP_OP_READ, data_len, SMP_GROUP_OS, 0, SMP_CMD_ID_RESET).to_bytes();
|
||||||
|
|
||||||
|
data[..SMP_HEADER_SIZE].copy_from_slice(&header);
|
||||||
|
data[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_len].copy_from_slice(&cbor_data[..data_len]);
|
||||||
|
|
||||||
|
send_request(&mut data[..SMP_HEADER_SIZE + data_len], &mut buffer);
|
||||||
|
}
|
101
core/embed/rust/src/smp/upload.rs
Normal file
101
core/embed/rust/src/smp/upload.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use super::{
|
||||||
|
receiver_acquire, send_request, wait_for_response, MsgType, SmpBuffer, SmpHeader,
|
||||||
|
SMP_CMD_ID_IMAGE_UPLOAD, SMP_GROUP_IMAGE, SMP_HEADER_SIZE, SMP_OP_WRITE,
|
||||||
|
};
|
||||||
|
use crate::time::Duration;
|
||||||
|
use minicbor::Encoder;
|
||||||
|
|
||||||
|
const CHUNK_SIZE: usize = 256;
|
||||||
|
const MAX_PACKET_SIZE: usize = 512;
|
||||||
|
|
||||||
|
pub fn upload_image(image_data: &[u8], image_hash: &[u8]) -> bool {
|
||||||
|
let mut cbor_data = [0u8; MAX_PACKET_SIZE];
|
||||||
|
let mut data = [0u8; MAX_PACKET_SIZE];
|
||||||
|
let mut buffer = [0u8; MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
let mut writer = SmpBuffer::new(&mut cbor_data);
|
||||||
|
|
||||||
|
let mut enc = Encoder::new(&mut writer);
|
||||||
|
|
||||||
|
unwrap!(enc.map(5));
|
||||||
|
unwrap!(enc.str("image"));
|
||||||
|
unwrap!(enc.u8(0));
|
||||||
|
unwrap!(enc.str("len"));
|
||||||
|
unwrap!(enc.u64(image_data.len() as _));
|
||||||
|
unwrap!(enc.str("off"));
|
||||||
|
unwrap!(enc.u8(0));
|
||||||
|
unwrap!(enc.str("hash"));
|
||||||
|
unwrap!(enc.bytes(image_hash));
|
||||||
|
unwrap!(enc.str("data"));
|
||||||
|
unwrap!(enc.bytes(&image_data[..CHUNK_SIZE]));
|
||||||
|
|
||||||
|
let data_len = writer.bytes_written();
|
||||||
|
unwrap!(receiver_acquire());
|
||||||
|
|
||||||
|
let header = SmpHeader::new(
|
||||||
|
SMP_OP_WRITE,
|
||||||
|
data_len,
|
||||||
|
SMP_GROUP_IMAGE,
|
||||||
|
0,
|
||||||
|
SMP_CMD_ID_IMAGE_UPLOAD,
|
||||||
|
)
|
||||||
|
.to_bytes();
|
||||||
|
|
||||||
|
data[..SMP_HEADER_SIZE].copy_from_slice(&header);
|
||||||
|
data[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_len].copy_from_slice(&cbor_data[..data_len]);
|
||||||
|
|
||||||
|
send_request(&mut data[..SMP_HEADER_SIZE + data_len], &mut buffer);
|
||||||
|
|
||||||
|
let mut resp_buffer = [0u8; 64];
|
||||||
|
if wait_for_response(
|
||||||
|
MsgType::ImageUploadResponse,
|
||||||
|
&mut resp_buffer,
|
||||||
|
Duration::from_millis(100),
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = CHUNK_SIZE;
|
||||||
|
|
||||||
|
for chunk in image_data.chunks(CHUNK_SIZE).skip(1) {
|
||||||
|
let mut cbor_data = [0u8; MAX_PACKET_SIZE];
|
||||||
|
let mut data = [0u8; MAX_PACKET_SIZE];
|
||||||
|
let mut buffer = [0u8; MAX_PACKET_SIZE];
|
||||||
|
let mut writer = SmpBuffer::new(&mut cbor_data);
|
||||||
|
let mut enc = Encoder::new(&mut writer);
|
||||||
|
|
||||||
|
unwrap!(enc.map(2));
|
||||||
|
unwrap!(enc.str("off"));
|
||||||
|
unwrap!(enc.u32(offset as _));
|
||||||
|
unwrap!(enc.str("data"));
|
||||||
|
unwrap!(enc.bytes(chunk));
|
||||||
|
|
||||||
|
let data_len = writer.bytes_written();
|
||||||
|
|
||||||
|
unwrap!(receiver_acquire());
|
||||||
|
|
||||||
|
let header = SmpHeader::new(SMP_OP_WRITE, data_len, SMP_GROUP_IMAGE, 0, 1).to_bytes();
|
||||||
|
|
||||||
|
data[..SMP_HEADER_SIZE].copy_from_slice(&header);
|
||||||
|
data[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_len].copy_from_slice(&cbor_data[..data_len]);
|
||||||
|
|
||||||
|
send_request(&mut data[..SMP_HEADER_SIZE + data_len], &mut buffer);
|
||||||
|
|
||||||
|
let mut resp_buffer = [0u8; 64];
|
||||||
|
if wait_for_response(
|
||||||
|
MsgType::ImageUploadResponse,
|
||||||
|
&mut resp_buffer,
|
||||||
|
Duration::from_millis(100),
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
13
core/embed/rust/src/trezorhal/irq.rs
Normal file
13
core/embed/rust/src/trezorhal/irq.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use super::ffi;
|
||||||
|
|
||||||
|
pub use ffi::irq_key_t as IrqKey;
|
||||||
|
|
||||||
|
pub fn irq_lock() -> IrqKey {
|
||||||
|
unsafe { ffi::irq_lock_fn() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn irq_unlock(key: IrqKey) {
|
||||||
|
unsafe {
|
||||||
|
ffi::irq_unlock_fn(key);
|
||||||
|
}
|
||||||
|
}
|
7
core/embed/rust/src/trezorhal/nrf.rs
Normal file
7
core/embed/rust/src/trezorhal/nrf.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use super::ffi;
|
||||||
|
|
||||||
|
pub fn send_data(data: &[u8]) {
|
||||||
|
unsafe {
|
||||||
|
ffi::nrf_send_uart_data(data.as_ptr(), data.len() as _);
|
||||||
|
}
|
||||||
|
}
|
@ -30,3 +30,8 @@ extern "C" fn display_rsod_rust(
|
|||||||
extern "C" fn screen_boot_stage_2(fade_in: bool) {
|
extern "C" fn screen_boot_stage_2(fade_in: bool) {
|
||||||
ModelUI::screen_boot_stage_2(fade_in);
|
ModelUI::screen_boot_stage_2(fade_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn screen_update() {
|
||||||
|
ModelUI::screen_update();
|
||||||
|
}
|
||||||
|
@ -80,6 +80,15 @@ impl CommonUI for UIBolt {
|
|||||||
show(&mut frame, fade_in);
|
show(&mut frame, fade_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screen_update() {
|
||||||
|
let mut frame = ErrorScreen::new(
|
||||||
|
"Update".into(),
|
||||||
|
"Finishing firmware update".into(),
|
||||||
|
"Do not turn of your trezor".into(),
|
||||||
|
);
|
||||||
|
show(&mut frame, true);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
fn render_debug_overlay<'s>(_target: &mut impl shape::Renderer<'s>, _info: DebugOverlay) {
|
fn render_debug_overlay<'s>(_target: &mut impl shape::Renderer<'s>, _info: DebugOverlay) {
|
||||||
// Not implemented
|
// Not implemented
|
||||||
|
@ -35,6 +35,10 @@ impl CommonUI for UICaesar {
|
|||||||
screens::screen_boot_stage_2(fade_in);
|
screens::screen_boot_stage_2(fade_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screen_update() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
fn render_debug_overlay<'s>(_target: &mut impl shape::Renderer<'s>, _info: DebugOverlay) {
|
fn render_debug_overlay<'s>(_target: &mut impl shape::Renderer<'s>, _info: DebugOverlay) {
|
||||||
// Not implemented
|
// Not implemented
|
||||||
|
@ -81,6 +81,10 @@ impl CommonUI for UIDelizia {
|
|||||||
screens::screen_boot_stage_2(fade_in);
|
screens::screen_boot_stage_2(fade_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screen_update() {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
fn render_debug_overlay<'s>(target: &mut impl shape::Renderer<'s>, info: DebugOverlay) {
|
fn render_debug_overlay<'s>(target: &mut impl shape::Renderer<'s>, info: DebugOverlay) {
|
||||||
let mut text = ShortString::new();
|
let mut text = ShortString::new();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::{geometry::Rect, CommonUI};
|
use super::{geometry::Rect, CommonUI};
|
||||||
|
use crate::ui::layout::simplified::show;
|
||||||
use theme::backlight;
|
use theme::backlight;
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
@ -34,6 +35,8 @@ pub mod ui_firmware;
|
|||||||
#[cfg(feature = "prodtest")]
|
#[cfg(feature = "prodtest")]
|
||||||
mod prodtest;
|
mod prodtest;
|
||||||
|
|
||||||
|
use component::ErrorScreen;
|
||||||
|
|
||||||
pub struct UIEckhart;
|
pub struct UIEckhart;
|
||||||
|
|
||||||
impl CommonUI for UIEckhart {
|
impl CommonUI for UIEckhart {
|
||||||
@ -87,6 +90,15 @@ impl CommonUI for UIEckhart {
|
|||||||
screens::screen_boot_stage_2(fade_in);
|
screens::screen_boot_stage_2(fade_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screen_update() {
|
||||||
|
let mut frame = ErrorScreen::new(
|
||||||
|
"Update".into(),
|
||||||
|
"Finishing firmware update".into(),
|
||||||
|
"Do not turn of your trezor".into(),
|
||||||
|
);
|
||||||
|
show(&mut frame, true);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
fn render_debug_overlay<'s>(target: &mut impl shape::Renderer<'s>, info: DebugOverlay) {
|
fn render_debug_overlay<'s>(target: &mut impl shape::Renderer<'s>, info: DebugOverlay) {
|
||||||
let mut text = ShortString::new();
|
let mut text = ShortString::new();
|
||||||
|
@ -44,6 +44,8 @@ pub trait CommonUI {
|
|||||||
|
|
||||||
fn screen_boot_stage_2(fade_in: bool);
|
fn screen_boot_stage_2(fade_in: bool);
|
||||||
|
|
||||||
|
fn screen_update();
|
||||||
|
|
||||||
/// Renders a partially transparent overlay over the screen content
|
/// Renders a partially transparent overlay over the screen content
|
||||||
/// using data from the `DebugOverlay` struct.
|
/// using data from the `DebugOverlay` struct.
|
||||||
#[cfg(feature = "ui_debug_overlay")]
|
#[cfg(feature = "ui_debug_overlay")]
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <io/usb.h>
|
#include <io/usb.h>
|
||||||
#include <rtl/secbool.h>
|
#include <rtl/secbool.h>
|
||||||
#include <sec/entropy.h>
|
#include <sec/entropy.h>
|
||||||
|
#include <sys/irq.h>
|
||||||
#include <sys/sysevent.h>
|
#include <sys/sysevent.h>
|
||||||
#include <sys/systick.h>
|
#include <sys/systick.h>
|
||||||
#include <util/flash.h>
|
#include <util/flash.h>
|
||||||
@ -22,6 +23,10 @@
|
|||||||
#include <io/ble.h>
|
#include <io/ble.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
#include <io/nrf.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
#include <io/button.h>
|
#include <io/button.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,10 +17,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TREZORHAL_IRQ_H
|
#pragma once
|
||||||
#define TREZORHAL_IRQ_H
|
|
||||||
|
|
||||||
#include <trezor_bsp.h>
|
|
||||||
#include <trezor_types.h>
|
#include <trezor_types.h>
|
||||||
|
|
||||||
#ifdef SYSTEM_VIEW
|
#ifdef SYSTEM_VIEW
|
||||||
@ -37,6 +35,10 @@
|
|||||||
|
|
||||||
typedef uint32_t irq_key_t;
|
typedef uint32_t irq_key_t;
|
||||||
|
|
||||||
|
#ifndef TREZOR_EMULATOR
|
||||||
|
|
||||||
|
#include <trezor_bsp.h>
|
||||||
|
|
||||||
// Checks if interrupts are enabled
|
// Checks if interrupts are enabled
|
||||||
#define IS_IRQ_ENABLED(key) (((key) & 1) == 0)
|
#define IS_IRQ_ENABLED(key) (((key) & 1) == 0)
|
||||||
|
|
||||||
@ -131,4 +133,8 @@ static inline void irq_unlock_ns(irq_key_t key) {
|
|||||||
// Lowest priority in the system used by SVC and PENDSV exception handlers
|
// Lowest priority in the system used by SVC and PENDSV exception handlers
|
||||||
#define IRQ_PRI_LOWEST NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0)
|
#define IRQ_PRI_LOWEST NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 15, 0)
|
||||||
|
|
||||||
#endif // TREZORHAL_IRQ_H
|
#endif
|
||||||
|
|
||||||
|
// functions for rust exposure, same behavior as the macros above
|
||||||
|
irq_key_t irq_lock_fn(void);
|
||||||
|
void irq_unlock_fn(irq_key_t key);
|
||||||
|
26
core/embed/sys/irq/stm32/irq.c
Normal file
26
core/embed/sys/irq/stm32/irq.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Trezor project, https://trezor.io/
|
||||||
|
*
|
||||||
|
* Copyright (c) SatoshiLabs
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <sys/irq.h>
|
||||||
|
|
||||||
|
irq_key_t irq_lock_fn(void) { return irq_lock(); }
|
||||||
|
void irq_unlock_fn(irq_key_t key) { irq_unlock(key); }
|
||||||
|
|
||||||
|
#endif
|
@ -46,6 +46,9 @@ SECTIONS {
|
|||||||
*(.text*);
|
*(.text*);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
*(.rodata*);
|
*(.rodata*);
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(.nrf_app));
|
||||||
|
*(.nrf_app*);
|
||||||
. = ALIGN(512);
|
. = ALIGN(512);
|
||||||
} >FLASH AT>FLASH
|
} >FLASH AT>FLASH
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ typedef enum {
|
|||||||
SYSCALL_BLE_CAN_READ,
|
SYSCALL_BLE_CAN_READ,
|
||||||
SYSCALL_BLE_READ,
|
SYSCALL_BLE_READ,
|
||||||
|
|
||||||
|
SYSCALL_NRF_UPDATE_REQUIRED,
|
||||||
|
SYSCALL_NRF_UPDATE,
|
||||||
|
|
||||||
SYSCALL_POWER_MANAGER_SUSPEND,
|
SYSCALL_POWER_MANAGER_SUSPEND,
|
||||||
SYSCALL_POWER_MANAGER_HIBERNATE,
|
SYSCALL_POWER_MANAGER_HIBERNATE,
|
||||||
SYSCALL_POWER_MANAGER_GET_STATE,
|
SYSCALL_POWER_MANAGER_GET_STATE,
|
||||||
|
@ -45,6 +45,10 @@
|
|||||||
#include <io/ble.h>
|
#include <io/ble.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
#include <io/nrf.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
#include <io/button.h>
|
#include <io/button.h>
|
||||||
#endif
|
#endif
|
||||||
@ -741,6 +745,22 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
|
|||||||
} break;
|
} break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
|
||||||
|
case SYSCALL_NRF_UPDATE_REQUIRED: {
|
||||||
|
const uint8_t *data = (const uint8_t *)args[0];
|
||||||
|
size_t len = args[1];
|
||||||
|
args[0] = nrf_update_required__verified(data, len);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_NRF_UPDATE: {
|
||||||
|
const uint8_t *data = (const uint8_t *)args[0];
|
||||||
|
size_t len = args[1];
|
||||||
|
args[0] = nrf_update__verified(data, len);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_POWER_MANAGER
|
#ifdef USE_POWER_MANAGER
|
||||||
case SYSCALL_POWER_MANAGER_SUSPEND: {
|
case SYSCALL_POWER_MANAGER_SUSPEND: {
|
||||||
args[0] = pm_suspend();
|
args[0] = pm_suspend();
|
||||||
|
@ -681,6 +681,24 @@ uint32_t ble_read(uint8_t *data, uint16_t len) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// nrf.h
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
bool nrf_update_required(const uint8_t *data, size_t len) {
|
||||||
|
return (bool)syscall_invoke2((uint32_t)data, (uint32_t)len,
|
||||||
|
SYSCALL_NRF_UPDATE_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_update(const uint8_t *data, size_t len) {
|
||||||
|
return (bool)syscall_invoke2((uint32_t)data, (uint32_t)len,
|
||||||
|
SYSCALL_NRF_UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// power_manager.h
|
// power_manager.h
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
@ -817,6 +817,36 @@ access_violation:
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef USE_NRF
|
||||||
|
|
||||||
|
bool nrf_update_required__verified(const uint8_t *data, size_t len) {
|
||||||
|
if (!probe_read_access(data, len)) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nrf_update_required(data, len);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_update__verified(const uint8_t *data, size_t len) {
|
||||||
|
if (!probe_read_access(data, len)) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nrf_update(data, len);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef USE_POWER_MANAGER
|
#ifdef USE_POWER_MANAGER
|
||||||
|
|
||||||
pm_status_t pm_get_state__verified(pm_state_t *status) {
|
pm_status_t pm_get_state__verified(pm_state_t *status) {
|
||||||
|
@ -207,6 +207,16 @@ secbool ble_read__verified(uint8_t *data, size_t len);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
#ifdef USE_NRF
|
||||||
|
|
||||||
|
#include <io/nrf.h>
|
||||||
|
|
||||||
|
bool nrf_update_required__verified(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
bool nrf_update__verified(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
#endif
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef USE_POWER_MANAGER
|
#ifdef USE_POWER_MANAGER
|
||||||
|
@ -109,8 +109,11 @@ def configure(
|
|||||||
defines += [("USE_BLE", "1")]
|
defines += [("USE_BLE", "1")]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
||||||
|
sources += ["embed/io/nrf/stm32u5/nrf_update.c"]
|
||||||
sources += ["embed/io/nrf/crc8.c"]
|
sources += ["embed/io/nrf/crc8.c"]
|
||||||
paths += ["embed/io/nrf/inc"]
|
paths += ["embed/io/nrf/inc"]
|
||||||
|
features_available.append("nrf")
|
||||||
|
defines += [("USE_NRF", "1")]
|
||||||
sources += [
|
sources += [
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
||||||
|
@ -109,8 +109,11 @@ def configure(
|
|||||||
defines += [("USE_BLE", "1")]
|
defines += [("USE_BLE", "1")]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
||||||
|
sources += ["embed/io/nrf/stm32u5/nrf_update.c"]
|
||||||
sources += ["embed/io/nrf/crc8.c"]
|
sources += ["embed/io/nrf/crc8.c"]
|
||||||
paths += ["embed/io/nrf/inc"]
|
paths += ["embed/io/nrf/inc"]
|
||||||
|
features_available.append("nrf")
|
||||||
|
defines += [("USE_NRF", "1")]
|
||||||
sources += [
|
sources += [
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
||||||
|
@ -109,8 +109,11 @@ def configure(
|
|||||||
defines += [("USE_BLE", "1")]
|
defines += [("USE_BLE", "1")]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf.c"]
|
||||||
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
sources += ["embed/io/nrf/stm32u5/nrf_test.c"]
|
||||||
|
sources += ["embed/io/nrf/stm32u5/nrf_update.c"]
|
||||||
sources += ["embed/io/nrf/crc8.c"]
|
sources += ["embed/io/nrf/crc8.c"]
|
||||||
paths += ["embed/io/nrf/inc"]
|
paths += ["embed/io/nrf/inc"]
|
||||||
|
features_available.append("nrf")
|
||||||
|
defines += [("USE_NRF", "1")]
|
||||||
sources += [
|
sources += [
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart.c",
|
||||||
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
|
||||||
|
@ -71,6 +71,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
|||||||
"embed/sec/secret/stm32f4/secret.c",
|
"embed/sec/secret/stm32f4/secret.c",
|
||||||
"embed/sec/time_estimate/stm32/time_estimate.c",
|
"embed/sec/time_estimate/stm32/time_estimate.c",
|
||||||
"embed/sys/dbg/stm32/dbg_printf.c",
|
"embed/sys/dbg/stm32/dbg_printf.c",
|
||||||
|
"embed/sys/irq/stm32/irq.c",
|
||||||
"embed/sys/linker/linker_utils.c",
|
"embed/sys/linker/linker_utils.c",
|
||||||
"embed/sys/mpu/stm32f4/mpu.c",
|
"embed/sys/mpu/stm32f4/mpu.c",
|
||||||
"embed/sys/pvd/stm32/pvd.c",
|
"embed/sys/pvd/stm32/pvd.c",
|
||||||
|
@ -91,6 +91,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
|
|||||||
"embed/sec/secure_aes/stm32u5/secure_aes_unpriv.c",
|
"embed/sec/secure_aes/stm32u5/secure_aes_unpriv.c",
|
||||||
"embed/sec/time_estimate/stm32/time_estimate.c",
|
"embed/sec/time_estimate/stm32/time_estimate.c",
|
||||||
"embed/sys/dbg/stm32/dbg_printf.c",
|
"embed/sys/dbg/stm32/dbg_printf.c",
|
||||||
|
"embed/sys/irq/stm32/irq.c",
|
||||||
"embed/sys/linker/linker_utils.c",
|
"embed/sys/linker/linker_utils.c",
|
||||||
"embed/sys/mpu/stm32u5/mpu.c",
|
"embed/sys/mpu/stm32u5/mpu.c",
|
||||||
"embed/sys/pvd/stm32/pvd.c",
|
"embed/sys/pvd/stm32/pvd.c",
|
||||||
|
@ -18,6 +18,7 @@ def unix_common_files(env, defines, sources, paths):
|
|||||||
"embed/sec/rng/inc",
|
"embed/sec/rng/inc",
|
||||||
"embed/sec/monoctr/inc",
|
"embed/sec/monoctr/inc",
|
||||||
"embed/sec/secret/inc",
|
"embed/sec/secret/inc",
|
||||||
|
"embed/sys/irq/inc",
|
||||||
"embed/sys/mpu/inc",
|
"embed/sys/mpu/inc",
|
||||||
"embed/sys/startup/inc",
|
"embed/sys/startup/inc",
|
||||||
"embed/sys/task/inc",
|
"embed/sys/task/inc",
|
||||||
|
@ -118,12 +118,27 @@ def embed_compressed_binary(obj_program, env, section, target_, file, build, sym
|
|||||||
|
|
||||||
|
|
||||||
def embed_raw_binary(obj_program, env, section, target_, file):
|
def embed_raw_binary(obj_program, env, section, target_, file):
|
||||||
|
|
||||||
|
def redefine_sym(suffix):
|
||||||
|
src = (
|
||||||
|
"_binary_"
|
||||||
|
+ file.replace("/", "_").replace(".", "_").replace("-", "_")
|
||||||
|
+ "_"
|
||||||
|
+ suffix
|
||||||
|
)
|
||||||
|
dest = f"{section}_{suffix}"
|
||||||
|
return f" --redefine-sym {src}={dest}"
|
||||||
|
|
||||||
obj_program.extend(
|
obj_program.extend(
|
||||||
env.Command(
|
env.Command(
|
||||||
target=target_,
|
target=target_,
|
||||||
source=file,
|
source=file,
|
||||||
action="$OBJCOPY -I binary -O elf32-littlearm -B arm"
|
action="$OBJCOPY -I binary -O elf32-littlearm -B arm"
|
||||||
f" --rename-section .data=.{section}" + " $SOURCE $TARGET",
|
f" --rename-section .data=.{section}"
|
||||||
|
+ redefine_sym("start")
|
||||||
|
+ redefine_sym("end")
|
||||||
|
+ redefine_sym("size")
|
||||||
|
+ " $SOURCE $TARGET",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user