feat(core/ble): expose nRF get version to coreapp
What changed, and why it matters
This commit adds a new system call that lets the main Trezor application read the firmware version of the nearby nRF Bluetooth chip. It is a small feature addition that exposes read-only version information; it does not, by itself, change how data is trusted or allow code execution.
No immediate action required. Treat as normal feature code; review the coreapp consumer to ensure version data is used only for diagnostics or update gating and not as a security decision.
Security signals we found
New syscall added to kernel/userspace boundary
Read-only version query with no input parameters from caller
Returns zero on uninitialized driver, send failure, or timeout
No change to nrf_authenticate, nrf_update, or pairing trust decisions
Evidence from the diff
The change introduces nrf_get_version(), which sends a management INFO command to the nRF BLE coprocessor and returns a packed 32-bit version number (major.minor.patch.tweak). A corresponding syscall number and dispatcher entry are added so the coreapp can invoke it. The function is read-only, returns zero on failure, and does not alter authentication, update, or pairing logic.
Changed components
core/embed/io/nrf/stm32u5/nrf.ccore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/embed/sys/syscall/inc/sys/syscall_numbers.hcore/embed/io/nrf/inc/io/nrf.hInspect captured patch +51 / −0
diff --git a/core/embed/io/nrf/inc/io/nrf.h b/core/embed/io/nrf/inc/io/nrf.h
index 6e0ffd2df..679ab9280 100644
--- a/core/embed/io/nrf/inc/io/nrf.h
+++ b/core/embed/io/nrf/inc/io/nrf.h
@@ -150,6 +150,14 @@ bool nrf_abort_msg(int32_t id);
*/
bool nrf_get_info(nrf_info_t *info);
+/**
+ * Get application/firmware version of the NRF device.
+ *
+ * @return version number of the NRF device as a 32-bit integer - with major
+ * being MSB. Returns zero in case of failure.
+ */
+uint32_t nrf_get_version(void);
+
/**
* @brief Place the NRF device into system-off (deep sleep) mode.
*
@@ -192,6 +200,11 @@ bool nrf_update_required(const uint8_t *image_ptr, size_t image_len);
*/
bool nrf_update(const uint8_t *image_ptr, size_t image_len);
+/**
+ * @brief Authenticate pairing of nRF chip with Trezor
+ *
+ * @return true if nrf chip is properly paired
+ */
bool nrf_authenticate(void);
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/embed/io/nrf/stm32u5/nrf.c b/core/embed/io/nrf/stm32u5/nrf.c
index 288c074fc..60fb2132c 100644
--- a/core/embed/io/nrf/stm32u5/nrf.c
+++ b/core/embed/io/nrf/stm32u5/nrf.c
@@ -491,6 +491,35 @@ bool nrf_get_info(nrf_info_t *info) {
return false;
}
+uint32_t nrf_get_version(void) {
+ nrf_driver_t *drv = &g_nrf_driver;
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ drv->info_valid = false;
+
+ uint8_t data[1] = {MGMT_CMD_INFO};
+ if (nrf_send_msg(NRF_SERVICE_MANAGEMENT, data, 1, NULL, NULL) < 0) {
+ return 0;
+ }
+
+ uint32_t timeout = ticks_timeout(100);
+
+ while (!ticks_expired(timeout)) {
+ if (drv->info_valid) {
+ uint32_t version = 0;
+ version |= drv->info.version_major << 24;
+ version |= drv->info.version_minor << 16;
+ version |= drv->info.version_patch << 8;
+ version |= drv->info.version_tweak;
+ return version;
+ }
+ }
+
+ return 0;
+}
+
bool nrf_system_off(void) {
nrf_driver_t *drv = &g_nrf_driver;
if (!drv->initialized) {
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index b3b8c5eb0..6967d77cd 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -147,6 +147,7 @@ typedef enum {
SYSCALL_NRF_UPDATE_REQUIRED,
SYSCALL_NRF_UPDATE,
+ SYSCALL_NRF_GET_VERSION,
SYSCALL_POWER_MANAGER_SUSPEND,
SYSCALL_POWER_MANAGER_HIBERNATE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 3336d5b25..27f32efb7 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -760,6 +760,10 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
args[0] = nrf_update__verified(data, len);
} break;
+ case SYSCALL_NRF_GET_VERSION: {
+ args[0] = nrf_get_version();
+ } break;
+
#endif
#ifdef USE_POWER_MANAGER
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 846926730..f999bd0b6 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -723,6 +723,10 @@ bool nrf_update(const uint8_t *data, size_t len) {
SYSCALL_NRF_UPDATE);
}
+uint32_t nrf_get_version(void) {
+ return syscall_invoke0(SYSCALL_NRF_GET_VERSION);
+}
+
#endif
// =============================================================================
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.