What changed, and why it matters
This commit hardens the security boundaries inside Trezor's STM32U5 microcontroller. It moves USB power initialization into the trusted secure monitor, restricts access to sensitive system registers (power, clock, cache, watchdog, RAM config), and locks down the TrustZone configuration so it cannot be changed later. These are defensive improvements that reduce the attack surface if untrusted code runs on the device, but the commit itself does not fix a known exploitable bug.
Treat as a security-hardening commit rather than an urgent vulnerability fix. Review that PLL3 non-secure access for the display driver does not introduce a side-channel or fault-injection path, and verify that the secure monitor's usb_power_init() is called before any non-secure USB code attempts to use the peripheral.
Security signals we found
TrustZone peripheral isolation tightened (PWR/RCC/SYSCFG/cache/watchdog made secure/privileged)
USB power initialization moved to secure monitor to match new PWR/RCC restrictions
GPDMA1 secure channel mask corrected from 0xEFFF to 0xFFFF with explicit channel 12 secure
SAU, AIRCR, and GTZC peripheral attribute registers locked after initialization
SRAM caching in DCACHE2 disabled (GPU not used)
Removal of incomplete/placeholder security comments (TODO!@#) for RCC clocks
Evidence from the diff
The patch strengthens STM32U5 TrustZone configuration in three areas: (1) USB power/PHY initialization is relocated from the non-secure USB driver to the secure monitor because PWR and RCC peripherals are now secure-only; (2) additional peripherals (RAMCFG, WWDG, ICACHE/DCACHE registers, SYSCFG, most RCC, PWR) are marked secure and privileged, with PLL3 left non-secure for the display driver; (3) the GPDMA1 secure channel mask is corrected to make only channel 12 secure, and the SAU, AIRCR, and GTZC peripheral attribute registers are locked after configuration. The change removes TODO comments and incomplete clock-security settings.
Changed components
core/embed/sys/trustzone/stm32u5/trustzone.ccore/embed/projects/secmon/main.ccore/embed/io/usb/stm32/usbd_conf.cInspect captured patch +82 / −8
diff --git a/core/embed/io/usb/stm32/usbd_conf.c b/core/embed/io/usb/stm32/usbd_conf.c
index defbf70e2..978f8c026 100644
--- a/core/embed/io/usb/stm32/usbd_conf.c
+++ b/core/embed/io/usb/stm32/usbd_conf.c
@@ -232,6 +232,11 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
__HAL_RCC_USBPHYC_CLK_ENABLE();
+
+#ifdef SECURE_MODE
+ // If not in secure mode, this initialization is done
+ // in secure monitor
+
/* Enable VDDUSB */
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableVddUSB();
@@ -239,6 +244,7 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
/*configure VOSR register of USB*/
HAL_PWREx_EnableUSBHSTranceiverSupply();
__HAL_RCC_PWR_CLK_DISABLE();
+#endif
/*Configuring the SYSCFG registers OTG_HS PHY*/
diff --git a/core/embed/projects/secmon/main.c b/core/embed/projects/secmon/main.c
index 59bf00b08..b74df15d0 100644
--- a/core/embed/projects/secmon/main.c
+++ b/core/embed/projects/secmon/main.c
@@ -52,6 +52,23 @@
#include <sec/hash_processor.h>
#endif
+// Configure and enable power for USB peripheral
+// (need to be called in secure mode since PWR and RCC peripheras are
+// not accessible from non-secure mode)
+
+#if defined(USE_USB_HS) && !defined(USE_USB_HS_IN_FS)
+void usb_power_init(void) {
+ __HAL_RCC_PWR_CLK_ENABLE();
+ // Enable VDDUSB
+ HAL_PWREx_EnableVddUSB();
+ // Power-on USB PHY
+ HAL_PWREx_EnableUSBHSTranceiverSupply();
+ __HAL_RCC_PWR_CLK_DISABLE();
+}
+#else
+#error Not implemented
+#endif
+
static void drivers_init(void) {
flash_init();
@@ -91,6 +108,8 @@ static void drivers_init(void) {
#ifdef USE_HASH_PROCESSOR
hash_processor_init();
#endif
+
+ usb_power_init();
}
// Secure monitor panic handler
diff --git a/core/embed/sys/trustzone/stm32u5/trustzone.c b/core/embed/sys/trustzone/stm32u5/trustzone.c
index d109472c4..62c1bd39e 100644
--- a/core/embed/sys/trustzone/stm32u5/trustzone.c
+++ b/core/embed/sys/trustzone/stm32u5/trustzone.c
@@ -529,6 +529,22 @@ void tz_init(void) {
HAL_GTZC_TZSC_ConfigPeriphAttributes(
GTZC_PERIPH_HASH, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+ // Set RAMCFG as secure & privileged
+ HAL_GTZC_TZSC_ConfigPeriphAttributes(
+ GTZC_PERIPH_RAMCFG, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+
+ // Set WWDG as secure & privileged
+ HAL_GTZC_TZSC_ConfigPeriphAttributes(
+ GTZC_PERIPH_WWDG, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+
+ // Set CACHE registers as secure & privileged
+ HAL_GTZC_TZSC_ConfigPeriphAttributes(
+ GTZC_PERIPH_ICACHE_REG, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+ HAL_GTZC_TZSC_ConfigPeriphAttributes(
+ GTZC_PERIPH_DCACHE1_REG, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+ HAL_GTZC_TZSC_ConfigPeriphAttributes(
+ GTZC_PERIPH_DCACHE2_REG, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
+
// Set all interrupts as non-secure
for (int i = 0; i < 512; i++) {
NVIC_SetTargetState(i);
@@ -537,11 +553,43 @@ void tz_init(void) {
// Set GTZC interrupt as secure
NVIC_ClearTargetState(GTZC_IRQn);
+ // System Configuration Controller accessible only from secure mode
+ SYSCFG->SECCFGR |= SYSCFG_SECCFGR_FPUSEC | SYSCFG_SECCFGR_CLASSBSEC |
+ SYSCFG_SECCFGR_SYSCFGSEC;
+
+ // Disable chaching of SRAM in DCACHE2 (used only by GPU which we do not use)
+ SYSCFG->CFGR1 &= ~SYSCFG_CFGR1_SRAMCACHED;
+
+ // All RCC peripherals secure by default
+ const uint32_t RCC_SECCFGR_ALL_BITS =
+ RCC_SECCFGR_HSISEC | RCC_SECCFGR_HSESEC | RCC_SECCFGR_MSISEC |
+ RCC_SECCFGR_LSISEC | RCC_SECCFGR_LSESEC | RCC_SECCFGR_SYSCLKSEC |
+ RCC_SECCFGR_PRESCSEC | RCC_SECCFGR_PLL1SEC | RCC_SECCFGR_PLL2SEC |
+ RCC_SECCFGR_PLL3SEC | RCC_SECCFGR_ICLKSEC | RCC_SECCFGR_HSI48SEC |
+ RCC_SECCFGR_RMVFSEC;
+
+ // RCC should be accessible only from secure/privileged mode
+ // (only exceptions is PLL3 used for display deriver, which is non-secure)
+ RCC->SECCFGR |= RCC_SECCFGR_ALL_BITS; // All secure
+ RCC->SECCFGR &= ~RCC_SECCFGR_PLL3SEC; // PLL3 non-secure
+ RCC->PRIVCFGR |= RCC_PRIVCFGR_SPRIV | RCC_PRIVCFGR_NSPRIV;
+
+ const uint32_t PWR_SECCFGR_ALL_BITS =
+ PWR_SECCFGR_WUP1SEC | PWR_SECCFGR_WUP2SEC | PWR_SECCFGR_WUP3SEC |
+ PWR_SECCFGR_WUP4SEC | PWR_SECCFGR_WUP5SEC | PWR_SECCFGR_WUP6SEC |
+ PWR_SECCFGR_WUP7SEC | PWR_SECCFGR_WUP8SEC | PWR_SECCFGR_LPMSEC |
+ PWR_SECCFGR_VDMSEC | PWR_SECCFGR_VBSEC | PWR_SECCFGR_APCSEC;
+
+ // PWR should be accessible only from secure/privileged mode
+ PWR->SECCFGR |= PWR_SECCFGR_ALL_BITS; // All secure
+ PWR->PRIVCFGR |= PWR_PRIVCFGR_NSPRIV | PWR_PRIVCFGR_SPRIV;
+
// Make GPDMA1 non-secure & privilege mode
- // Channel 12 is secure, all others are non-secure
+ // Channel 12 (used for hash processor) is secure, all others are non-secure
__HAL_RCC_GPDMA1_CLK_ENABLE();
- GPDMA1->SECCFGR &= ~0xEFFF;
+ GPDMA1->SECCFGR &= ~0xFFFF;
+ GPDMA1->SECCFGR |= (1 << 12);
GPDMA1->PRIVCFGR |= 0xFFFF;
// Enable all GPIOS and make them non-secure & privileged
@@ -604,15 +652,16 @@ void tz_init(void) {
TROPIC01_SPI_GTZC_PERIPH, GTZC_TZSC_PERIPH_SEC | GTZC_TZSC_PERIPH_PRIV);
#endif
- // Set all clocks except (TODO!@#) non-secure & privileged
+ tz_enable_illegal_access_interrupt();
- RCC->SECCFGR |= RCC_SECCFGR_LSESEC | RCC_SECCFGR_LSISEC; // !@# improve
- // todo: PLL3 - display - non-secure
- // Access to RCC only from privileged mode
- RCC->PRIVCFGR |= RCC_PRIVCFGR_SPRIV | RCC_PRIVCFGR_NSPRIV;
+ // Lock SAU configuration & AIRCR register against further modifications
+ SYSCFG->CSLCKR |= SYSCFG_CSLCKR_LOCKSAU | SYSCFG_CSLCKR_LOCKSVTAIRCR;
- tz_enable_illegal_access_interrupt();
+ // Lock GTZC peripheral attributes against further modifications
+ GTZC_TZSC1->CR |= GTZC_TZSC_CR_LCK_Msk;
+ GTZC_TZSC2->CR |= GTZC_TZSC_CR_LCK_Msk;
}
+
#endif // SECMON
#endif // KERNEL_MODE
Why this scored 35/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.