What changed, and why it matters
This commit adds support for a new microcontroller chip (the STM32U5A5) to the Trezor firmware build system. It is a hardware-enablement change: it wires up the right compiler flags, linker scripts, memory layouts, and trustzone settings so the existing code can compile and run on the new chip. There is no user-facing behavior change, no bug fix, and no security patch in this diff.
No security action required. Treat as normal hardware-support commit. If this MCU is intended for a shipping product, validate the new linker memory map and TrustZone configuration against the STM32U5A5 reference manual during hardware bring-up, but that is outside the scope of this code review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces the mcu_stm32u5a feature across multiple Cargo.toml files, adds STM32U5A5xx preprocessor definitions in build.rs scripts, extends conditional compilation blocks in option_bytes.c and trustzone.c to include STM32U5A5xx, defines the startup RAM region for STM32U5A5xx in linker_utils.h, adds new linker scripts under core/embed/sys/linker/stm32u5a/, and updates xbuild/xtask tooling to recognize the new MCU. The linker scripts mirror the existing stm32u5g/stm32u58 layouts with memory sizes adjusted for the U5A5 part (e.g., 2496K SRAM1). No functional code logic is modified.
Changed components
core/embed build feature flagscore/embed/sys/linker/stm32u5a linker scriptscore/embed/sys/bsp/build.rscore/embed/sys/flash/build.rscore/embed/sec/option_bytes/stm32u5/option_bytes.ccore/embed/sys/trustzone/stm32u5/trustzone.ccore/embed/sys/linker/inc/sys/linker_utils.hcore/embed/xbuild/src/trezor.rscore/embed/xtask/src/config.rsInspect captured patch +612 / −4
diff --git a/core/embed/io/Cargo.toml b/core/embed/io/Cargo.toml
index 1a308ee3..3eb53f04 100644
--- a/core/embed/io/Cargo.toml
+++ b/core/embed/io/Cargo.toml
@@ -24,6 +24,7 @@ mcu_stm32f427 = ["sec/mcu_stm32f427", "mcu_stm32f4"]
mcu_stm32f429 = ["sec/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32u58 = ["sec/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["sec/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["sec/mcu_stm32u5a", "mcu_stm32u5"]
# --------------------------------------------------------------------------
# Privilege mode features
diff --git a/core/embed/models/Cargo.toml b/core/embed/models/Cargo.toml
index 9634e76f..c15b5195 100644
--- a/core/embed/models/Cargo.toml
+++ b/core/embed/models/Cargo.toml
@@ -33,6 +33,7 @@ mcu_stm32f427 = ["mcu_stm32f4"]
mcu_stm32f429 = ["mcu_stm32f4"]
mcu_stm32u58 = ["mcu_stm32u5"]
mcu_stm32u5g = ["mcu_stm32u5"]
+mcu_stm32u5a = ["mcu_stm32u5"]
# --------------------------------------------------------------------------
# Privilege mode features
diff --git a/core/embed/projects/boardloader/Cargo.toml b/core/embed/projects/boardloader/Cargo.toml
index 6fd800d7..e5733a0d 100644
--- a/core/embed/projects/boardloader/Cargo.toml
+++ b/core/embed/projects/boardloader/Cargo.toml
@@ -52,6 +52,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = []
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
backlight = ["io/backlight"]
diff --git a/core/embed/projects/bootloader/Cargo.toml b/core/embed/projects/bootloader/Cargo.toml
index 27dfb56c..50e40088 100644
--- a/core/embed/projects/bootloader/Cargo.toml
+++ b/core/embed/projects/bootloader/Cargo.toml
@@ -62,6 +62,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = []
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
backlight = ["io/backlight", "trezor_lib/backlight"]
diff --git a/core/embed/projects/bootloader_ci/Cargo.toml b/core/embed/projects/bootloader_ci/Cargo.toml
index c3419104..3271edbd 100644
--- a/core/embed/projects/bootloader_ci/Cargo.toml
+++ b/core/embed/projects/bootloader_ci/Cargo.toml
@@ -53,6 +53,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = ["io/mcu_stm32f4"]
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
backlight = ["io/backlight"]
diff --git a/core/embed/projects/firmware/Cargo.toml b/core/embed/projects/firmware/Cargo.toml
index 7e900584..98aa201b 100644
--- a/core/embed/projects/firmware/Cargo.toml
+++ b/core/embed/projects/firmware/Cargo.toml
@@ -75,6 +75,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "upymod/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = ["mcu_stm32"]
mcu_stm32u58 = ["io/mcu_stm32u58", "upymod/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "upymod/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "upymod/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone", "mcu_stm32"]
mcu_stm32 = []
diff --git a/core/embed/projects/kernel/Cargo.toml b/core/embed/projects/kernel/Cargo.toml
index a00d6c24..5f197d9d 100644
--- a/core/embed/projects/kernel/Cargo.toml
+++ b/core/embed/projects/kernel/Cargo.toml
@@ -59,6 +59,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = []
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
secure_mode = ["io/secure_mode"]
diff --git a/core/embed/projects/prodtest/Cargo.toml b/core/embed/projects/prodtest/Cargo.toml
index b8b38529..d3e532e0 100644
--- a/core/embed/projects/prodtest/Cargo.toml
+++ b/core/embed/projects/prodtest/Cargo.toml
@@ -62,6 +62,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = []
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
backlight = ["io/backlight", "trezor_lib/backlight"]
diff --git a/core/embed/projects/secmon/Cargo.toml b/core/embed/projects/secmon/Cargo.toml
index a1f686ee..447e806e 100644
--- a/core/embed/projects/secmon/Cargo.toml
+++ b/core/embed/projects/secmon/Cargo.toml
@@ -54,6 +54,7 @@ default = [
mcu_stm32u58 = ["sec/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["sec/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["sec/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone"]
backup_ram = ["sec/backup_ram"]
diff --git a/core/embed/projects/unix/Cargo.toml b/core/embed/projects/unix/Cargo.toml
index 02c96934..0119d161 100644
--- a/core/embed/projects/unix/Cargo.toml
+++ b/core/embed/projects/unix/Cargo.toml
@@ -76,6 +76,7 @@ mcu_stm32f429 = ["io/mcu_stm32f429", "upymod/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32f4 = ["mcu_stm32"]
mcu_stm32u58 = ["io/mcu_stm32u58", "upymod/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "upymod/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "upymod/mcu_stm32u5a", "mcu_stm32u5"]
mcu_stm32u5 = ["sec/trustzone", "mcu_stm32"]
mcu_stm32 = []
diff --git a/core/embed/sec/Cargo.toml b/core/embed/sec/Cargo.toml
index 1d16ed1b..3106447c 100644
--- a/core/embed/sec/Cargo.toml
+++ b/core/embed/sec/Cargo.toml
@@ -23,6 +23,7 @@ mcu_stm32f427 = ["sys/mcu_stm32f427", "mcu_stm32f4"]
mcu_stm32f429 = ["sys/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32u58 = ["sys/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["sys/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["sys/mcu_stm32u5a", "mcu_stm32u5"]
# --------------------------------------------------------------------------
# Privilege mode features
diff --git a/core/embed/sec/option_bytes/stm32u5/option_bytes.c b/core/embed/sec/option_bytes/stm32u5/option_bytes.c
index c652a4bc..4215ffe9 100644
--- a/core/embed/sec/option_bytes/stm32u5/option_bytes.c
+++ b/core/embed/sec/option_bytes/stm32u5/option_bytes.c
@@ -47,7 +47,7 @@
#error "VDD_3V3 or VDD_1V8 must be defined"
#endif
-#if defined STM32U5A9xx | defined STM32U5G9xx
+#if defined STM32U5A9xx | defined STM32U5G9xx | defined STM32U5A5xx
#define WRP_DEFAULT_VALUE 0xFF00FFFF
#define SEC_WM1R1_DEFAULT_VALUE 0xFF00FF00
#define SEC_WM1R2_DEFAULT_VALUE 0x7F007F00
diff --git a/core/embed/sys/Cargo.toml b/core/embed/sys/Cargo.toml
index 673746ce..088edfbb 100644
--- a/core/embed/sys/Cargo.toml
+++ b/core/embed/sys/Cargo.toml
@@ -22,6 +22,7 @@ mcu_stm32f427 = ["models/mcu_stm32f427", "mcu_stm32f4"]
mcu_stm32f429 = ["models/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32u58 = ["models/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["models/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["models/mcu_stm32u5a", "mcu_stm32u5"]
# --------------------------------------------------------------------------
# Privilege mode features
diff --git a/core/embed/sys/bsp/build.rs b/core/embed/sys/bsp/build.rs
index 81bdea20..254b6f5c 100644
--- a/core/embed/sys/bsp/build.rs
+++ b/core/embed/sys/bsp/build.rs
@@ -19,6 +19,8 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
fn add_stm32u5_bsp(lib: &mut CLibrary) -> Result<()> {
if cfg!(feature = "mcu_stm32u5g") {
lib.add_define("STM32U5G9xx", None);
+ } else if cfg!(feature = "mcu_stm32u5a") {
+ lib.add_define("STM32U5A5xx", None);
} else if cfg!(feature = "mcu_stm32u58") {
lib.add_define("STM32U585xx", None);
} else {
diff --git a/core/embed/sys/flash/build.rs b/core/embed/sys/flash/build.rs
index 4d026078..90dfbd26 100644
--- a/core/embed/sys/flash/build.rs
+++ b/core/embed/sys/flash/build.rs
@@ -35,6 +35,8 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
if cfg!(feature = "mcu_stm32u5g") {
lib.add_define("STM32U5G9xx", None);
+ } else if cfg!(feature = "mcu_stm32u5a") {
+ lib.add_define("STM32U5A5xx", None);
} else if cfg!(feature = "mcu_stm32u58") {
lib.add_define("STM32U585xx", None);
} else {
diff --git a/core/embed/sys/linker/inc/sys/linker_utils.h b/core/embed/sys/linker/inc/sys/linker_utils.h
index 528be071..66eec4f7 100644
--- a/core/embed/sys/linker/inc/sys/linker_utils.h
+++ b/core/embed/sys/linker/inc/sys/linker_utils.h
@@ -94,6 +94,17 @@ typedef struct {
}, \
}; \
})
+#elif defined(STM32U5A5xx)
+#define MEMREGION_ALL_STARTUP_RAM \
+ ({ \
+ (memregion_t){ \
+ .block = \
+ { \
+ MEMBLOCK(SRAM1_BASE, SIZE_2496K), \
+ MEMBLOCK(SRAM4_BASE, SIZE_16K), \
+ }, \
+ }; \
+ })
#else
#error "Unknown STM32 family"
#endif
diff --git a/core/embed/sys/linker/stm32u5a/boardloader.ld b/core/embed/sys/linker/stm32u5a/boardloader.ld
new file mode 100644
index 00000000..e4e8850b
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/boardloader.ld
@@ -0,0 +1,86 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = BOARDLOADER_START, LENGTH = BOARDLOADER_MAXSIZE
+ BOARDCAPS (rw) : ORIGIN = BOARDCAPS_START, LENGTH = BOARDCAPS_MAXSIZE
+ MAIN_RAM (rw) : ORIGIN = MAIN_RAM_START, LENGTH = MAIN_RAM_SIZE
+ AUX1_RAM (rw) : ORIGIN = AUX1_RAM_START, LENGTH = AUX1_RAM_SIZE
+ BOOT_ARGS (rw) : ORIGIN = BOOTARGS_START, LENGTH = BOOTARGS_SIZE
+ FB1_RAM (rw) : ORIGIN = FB1_RAM_START, LENGTH = FB1_RAM_SIZE
+ FB2_RAM (rw) : ORIGIN = FB2_RAM_START, LENGTH = FB2_RAM_SIZE
+}
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_bootargs_ram_start = BOOTARGS_START;
+_bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE;
+
+
+SECTIONS {
+ .vector_table : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.vector_table));
+ } >FLASH AT>FLASH
+
+ .text : ALIGN(4) {
+ *(.text*);
+ . = ALIGN(4); /* make the section size a multiple of the word size */
+ } >FLASH AT>FLASH
+
+ .rodata : ALIGN(4) {
+ *(.rodata*);
+ . = ALIGN(4); /* make the section size a multiple of the word size */
+ } >FLASH AT>FLASH
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(8);
+ } >MAIN_RAM AT>FLASH
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ .bss : ALIGN(4) {
+ *(.bss*);
+ . = ALIGN(4);
+ } >MAIN_RAM
+
+ .buf : ALIGN(4) {
+ *(.buf*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ .stack : ALIGN(8) {
+ . += 16K; /* Overflow causes UsageFault */
+ } >MAIN_RAM
+
+ .fb1 : ALIGN(4) {
+ *(.fb1*);
+ . = ALIGN(4);
+ } >FB1_RAM
+
+ .fb2 : ALIGN(4) {
+ *(.fb2*);
+ . = ALIGN(4);
+ } >FB2_RAM
+
+ .boot_args : ALIGN(8) {
+ *(.boot_command*);
+ . = ALIGN(8);
+ *(.boot_args*);
+ . = ALIGN(8);
+ } >BOOT_ARGS
+
+ .capabilities : ALIGN(4) {
+ KEEP(*(.capabilities_section*));
+ . = ALIGN(4);
+ } >BOARDCAPS
+}
diff --git a/core/embed/sys/linker/stm32u5a/bootloader.ld b/core/embed/sys/linker/stm32u5a/bootloader.ld
new file mode 100644
index 00000000..5ea8a9e5
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/bootloader.ld
@@ -0,0 +1,91 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = BOOTLOADER_START, LENGTH = BOOTLOADER_MAXSIZE
+ MAIN_RAM (rw) : ORIGIN = MAIN_RAM_START, LENGTH = MAIN_RAM_SIZE
+ AUX1_RAM (rw) : ORIGIN = AUX1_RAM_START, LENGTH = AUX1_RAM_SIZE
+ BOOT_ARGS (rw) : ORIGIN = BOOTARGS_START, LENGTH = BOOTARGS_SIZE
+ FB1_RAM (rw) : ORIGIN = FB1_RAM_START, LENGTH = FB1_RAM_SIZE
+ FB2_RAM (rw) : ORIGIN = FB2_RAM_START, LENGTH = FB2_RAM_SIZE
+}
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_bootargs_ram_start = BOOTARGS_START;
+_bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE;
+
+_codelen = _bootloader_code_end - ADDR(.flash);
+
+SECTIONS {
+ .header : ALIGN(4) {
+ KEEP(*(.header));
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.vector_table));
+ . = ALIGN(4);
+ *(.text*);
+ . = ALIGN(4);
+ *(.rodata*);
+ . = ALIGN(4);
+ } >FLASH AT>FLASH
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(4);
+ } >MAIN_RAM AT>FLASH
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ .bss : ALIGN(4) {
+ *(.bss*);
+ . = ALIGN(4);
+ } >MAIN_RAM
+
+ .buf : ALIGN(4) {
+ *(.buf*);
+ . = ALIGN(4);
+ *(.no_dma_buffers*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ .stack : ALIGN(8) {
+ . += 16K; /* Overflow causes UsageFault */
+ } >MAIN_RAM
+
+ .fb1 : ALIGN(4) {
+ *(.fb1*);
+ . = ALIGN(4);
+ } >FB1_RAM
+
+ .fb2 : ALIGN(4) {
+ *(.fb2*);
+ . = ALIGN(4);
+ } >FB2_RAM
+
+ .boot_args : ALIGN(8) {
+ *(.boot_command*);
+ . = ALIGN(8);
+ *(.boot_args*);
+ . = ALIGN(8);
+ } >BOOT_ARGS
+
+ .flash : ALIGN(4) {
+ /* Pad the rest of bootloader area with zeros */
+ BYTE(0x00)
+ FILL(0x00)
+ /* Use alignment required by the boardloader */
+ . = ALIGN(512);
+ _bootloader_code_end = .;
+ } >FLASH
+}
diff --git a/core/embed/sys/linker/stm32u5a/firmware.ld b/core/embed/sys/linker/stm32u5a/firmware.ld
new file mode 100644
index 00000000..74fffeb6
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/firmware.ld
@@ -0,0 +1,92 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = FIRMWARE_START, LENGTH = FIRMWARE_MAXSIZE
+ AUX1_RAM (rw) : ORIGIN = AUX1_RAM_START, LENGTH = AUX1_RAM_SIZE
+}
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+_stack_section_size = SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_tls_section_start = ADDR(.tls);
+_tls_section_size = SIZEOF(.tls);
+
+_codelen = SIZEOF(.padding) + SIZEOF(.flash) + SIZEOF(.data);
+
+_heap_start = ADDR(.heap);
+_heap_end = ADDR(.heap) + SIZEOF(.heap);
+
+SECTIONS {
+ .vendorheader : ALIGN(4) {
+ KEEP(*(.vendorheader))
+ } >FLASH AT>FLASH
+
+ .header : ALIGN(4) {
+ KEEP(*(.header));
+ } >FLASH AT>FLASH
+
+ .padding : ALIGN(4) {
+ . = ALIGN(4);
+ . = ALIGN(CODE_ALIGNMENT);
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.kernel));
+ . = ALIGN(COREAPP_ALIGNMENT);
+ KEEP(*(.coreapp_header));
+ . = ALIGN(4);
+ *(.text*);
+ . = ALIGN(4);
+ *(.rodata*);
+ . = ALIGN(4);
+ KEEP(*(.nrf_app));
+ *(.nrf_app*);
+ . = ALIGN(4);
+ KEEP(*(.bootloader));
+ *(.bootloader*);
+ . = ALIGN(512);
+ } >FLASH AT>FLASH
+
+ .stack : ALIGN(8) {
+ . += 32K; /* Overflow causes UsageFault */
+ } >AUX1_RAM
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(512);
+ } >AUX1_RAM AT>FLASH
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ .tls : ALIGN(32) {
+ *(.tls*); /* 32-byte alignment required by MPU */
+ . = ALIGN(32);
+ } > AUX1_RAM
+
+ .bss : ALIGN(4) {
+ *(.no_dma_buffers*);
+ *(.bss*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ .buf : ALIGN(4) {
+ *(.buf*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ .heap : ALIGN(4) {
+ . = 37K; /* this acts as a build time assertion that at least this much memory is available for heap use */
+ . = ABSOLUTE(ORIGIN(AUX1_RAM) + LENGTH(AUX1_RAM)); /* this explicitly sets the end of the heap */
+ } >AUX1_RAM
+
+}
diff --git a/core/embed/sys/linker/stm32u5a/kernel.ld b/core/embed/sys/linker/stm32u5a/kernel.ld
new file mode 100644
index 00000000..07b7a608
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/kernel.ld
@@ -0,0 +1,94 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = FIRMWARE_START, LENGTH = FIRMWARE_MAXSIZE
+ MAIN_RAM (rw) : ORIGIN = MAIN_RAM_START, LENGTH = MAIN_RAM_SIZE
+ BOOT_ARGS (rw) : ORIGIN = BOOTARGS_START, LENGTH = BOOTARGS_SIZE
+ FB1_RAM (rw) : ORIGIN = FB1_RAM_START, LENGTH = FB1_RAM_SIZE
+ FB2_RAM (rw) : ORIGIN = FB2_RAM_START, LENGTH = FB2_RAM_SIZE
+}
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_bootargs_ram_start = BOOTARGS_START;
+_bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE;
+
+_codelen = _kernel_flash_end - ORIGIN(FLASH);
+
+SECTIONS {
+ .vendorheader : ALIGN(4) {
+ KEEP(*(.vendorheader))
+ } >FLASH AT>FLASH
+
+ .header : ALIGN(4) {
+ . += 1K;
+ . = ALIGN(CODE_ALIGNMENT);
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.secmon));
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ _kernel_flash_start = .;
+ KEEP(*(.vector_table));
+ . = ALIGN(4);
+ *(.text*);
+ . = ALIGN(4);
+ *(.rodata*);
+ . = ALIGN(512);
+ } >FLASH AT>FLASH
+
+ .stack : ALIGN(8) {
+ . += 12K; /* Overflow causes UsageFault */
+ } >MAIN_RAM
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(512);
+ } >MAIN_RAM AT>FLASH
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ .bss : ALIGN(4) {
+ *(.no_dma_buffers*);
+ *(.bss*);
+ . = ALIGN(4);
+ } >MAIN_RAM
+
+ .fb1 : ALIGN(4) {
+ *(.fb1*);
+ . = ALIGN(4);
+ } >FB1_RAM
+
+ .fb2 : ALIGN(4) {
+ *(.fb2*);
+ . = ALIGN(4);
+ } >FB2_RAM
+
+ .buf : ALIGN(4) {
+ *(.buf*);
+ . = ALIGN(4);
+ } >MAIN_RAM
+
+ .boot_args : ALIGN(8) {
+ *(.boot_command*);
+ . = ALIGN(8);
+ *(.boot_args*);
+ . = ALIGN(8);
+ } >BOOT_ARGS
+
+ .flash : ALIGN(4) {
+ _kernel_flash_end = .;
+ } >FLASH
+}
diff --git a/core/embed/sys/linker/stm32u5a/prodtest.ld b/core/embed/sys/linker/stm32u5a/prodtest.ld
new file mode 100644
index 00000000..3c50cf1c
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/prodtest.ld
@@ -0,0 +1,101 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = FIRMWARE_START, LENGTH = FIRMWARE_MAXSIZE
+ MAIN_RAM (rw) : ORIGIN = MAIN_RAM_START, LENGTH = MAIN_RAM_SIZE
+ AUX1_RAM (rw) : ORIGIN = AUX1_RAM_START, LENGTH = AUX1_RAM_SIZE
+ BOOT_ARGS (rw) : ORIGIN = BOOTARGS_START, LENGTH = BOOTARGS_SIZE
+ FB1_RAM (rw) : ORIGIN = FB1_RAM_START, LENGTH = FB1_RAM_SIZE
+ FB2_RAM (rw) : ORIGIN = FB2_RAM_START, LENGTH = FB2_RAM_SIZE
+}
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_bootargs_ram_start = BOOTARGS_START;
+_bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE;
+
+_codelen = _image_flash_end - ADDR(.flash);
+
+SECTIONS {
+ .vendorheader : ALIGN(4) {
+ KEEP(*(.vendorheader))
+ } >FLASH AT>FLASH
+
+ .header : ALIGN(4) {
+ KEEP(*(.header));
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.vector_table));
+ . = ALIGN(4);
+ *(.text*);
+ . = ALIGN(4);
+ *(.rodata*);
+ . = ALIGN(4);
+
+ _prodtest_cli_cmd_section_start = .;
+ KEEP(*(.prodtest_cli_cmd))
+ _prodtest_cli_cmd_section_end = .;
+ . = ALIGN(4);
+
+ _unit_test_section_start = .;
+ KEEP(*(.unit_test))
+ _unit_test_section_end = .;
+
+ . = ALIGN(512);
+ } >FLASH AT>FLASH
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(512);
+ } >AUX1_RAM AT>FLASH
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ /* End of code/data in FLASH */
+ _image_flash_end = LOADADDR(.data) + SIZEOF(.data);
+
+ .bss : ALIGN(4) {
+ *(.bss*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ /* D003 has no AUX2_RAM, so .buf shares AUX1_RAM with .data/.bss. */
+ .buf : ALIGN(4) {
+ *(.buf*);
+ . = ALIGN(4);
+ *(.no_dma_buffers*);
+ . = ALIGN(4);
+ } >AUX1_RAM
+
+ .stack : ALIGN(8) {
+ . += 16K; /* Overflow causes UsageFault */
+ } >MAIN_RAM
+
+ .fb1 : ALIGN(4) {
+ *(.fb1*);
+ . = ALIGN(4);
+ } >FB1_RAM
+
+ .fb2 : ALIGN(4) {
+ *(.fb2*);
+ . = ALIGN(4);
+ } >FB2_RAM
+
+ .boot_args : ALIGN(8) {
+ *(.boot_command*);
+ . = ALIGN(8);
+ *(.boot_args*);
+ . = ALIGN(8);
+ } >BOOT_ARGS
+}
diff --git a/core/embed/sys/linker/stm32u5a/secmon.ld b/core/embed/sys/linker/stm32u5a/secmon.ld
new file mode 100644
index 00000000..cc9f54d3
--- /dev/null
+++ b/core/embed/sys/linker/stm32u5a/secmon.ld
@@ -0,0 +1,112 @@
+ENTRY(reset_handler)
+
+MEMORY {
+ FLASH (rx) : ORIGIN = FIRMWARE_START_S, LENGTH = FIRMWARE_MAXSIZE
+ RAM (rw) : ORIGIN = SECMON_RAM_START, LENGTH = SECMON_RAM_SIZE
+ BOOT_ARGS (rw) : ORIGIN = BOOTARGS_START, LENGTH = BOOTARGS_SIZE
+ FB1_RAM (rw) : ORIGIN = FB1_RAM_START, LENGTH = FB1_RAM_SIZE
+ FB2_RAM (rw) : ORIGIN = FB2_RAM_START, LENGTH = FB2_RAM_SIZE
+}
+
+_sgstubs_section_start = ADDR(.gnu.sgstubs);
+_sgstubs_section_end = ADDR(.gnu.sgstubs) + SIZEOF(.gnu.sgstubs);
+
+_stack_section_start = ADDR(.stack);
+_stack_section_end = ADDR(.stack) + SIZEOF(.stack);
+
+_data_section_loadaddr = LOADADDR(.data);
+_data_section_start = ADDR(.data);
+_data_section_end = ADDR(.data) + SIZEOF(.data);
+
+_bss_section_start = ADDR(.bss);
+_bss_section_end = ADDR(.bss) + SIZEOF(.bss);
+
+_bootargs_ram_start = BOOTARGS_START;
+_bootargs_ram_end = BOOTARGS_START + BOOTARGS_SIZE;
+
+_codelen = _secmon_flash_end - _secmon_code_start;
+_secmon_size = _secmon_flash_end - ORIGIN(FLASH);
+
+SECTIONS {
+ .vendorheader : ALIGN(4) {
+ KEEP(*(.vendorheader))
+ } >FLASH
+
+ .header : ALIGN(4) {
+ . += 1K;
+ . = ALIGN(CODE_ALIGNMENT);
+ } >FLASH
+
+ .secmon_header : ALIGN(4) {
+ KEEP(*(.secmon_header));
+ } >FLASH
+
+ .padding : ALIGN(4) {
+ _secmon_code_start = .;
+ . = ALIGN(CODE_ALIGNMENT);
+ } >FLASH AT>FLASH
+
+ .flash : ALIGN(CODE_ALIGNMENT) {
+ KEEP(*(.vector_table));
+ . = ALIGN(4);
+ } >FLASH
+
+ .gnu.sgstubs : ALIGN (32) {
+ . = ALIGN(32);
+ KEEP(*(.gnu.sgstubs*));
+ . = ALIGN(32);
+ } >FLASH
+
+ .flash : {
+ *(.text*);
+ . = ALIGN(4);
+ *(.rodata*);
+ . = ALIGN(4);
+ } >FLASH
+
+ /* .data and .bss are intentionally placed before the .stack
+ section to ensure they are located in SRAM2, which is
+ cleared on security events */
+
+ .data : ALIGN(4) {
+ *(.data*);
+ . = ALIGN(4);
+ } >RAM AT>FLASH
+
+ .bss : ALIGN(4) {
+ *(.bss*);
+ . = ALIGN(4);
+ } >RAM
+
+ .stack : ALIGN(8) {
+ . = 80K; /* Overflow causes UsageFault */
+ } >RAM
+
+ .fb1 : ALIGN(4) {
+ *(.fb1*);
+ . = ALIGN(4);
+ } >FB1_RAM
+
+ .fb2 : ALIGN(4) {
+ *(.fb2*);
+ . = ALIGN(4);
+ } >FB2_RAM
+
+ .boot_args : ALIGN(8) {
+ *(.boot_command*);
+ . = ALIGN(8);
+ *(.boot_args*);
+ . = ALIGN(8);
+ } >BOOT_ARGS
+
+ /DISCARD/ : {
+ *(.ARM.exidx*);
+ }
+
+ .flash : {
+ BYTE(0x00)
+ FILL(0x00)
+ . = ALIGN(8K);
+ _secmon_flash_end = .;
+ } >FLASH
+}
diff --git a/core/embed/sys/trustzone/stm32u5/trustzone.c b/core/embed/sys/trustzone/stm32u5/trustzone.c
index ef8ca44d..8964d3ba 100644
--- a/core/embed/sys/trustzone/stm32u5/trustzone.c
+++ b/core/embed/sys/trustzone/stm32u5/trustzone.c
@@ -79,7 +79,7 @@ sram_region_t g_sram_regions[] = {
{SRAM1_BASE, SRAM1_BASE + SRAM1_SIZE, GTZC_MPCBB1},
{SRAM2_BASE, SRAM2_BASE + SRAM2_SIZE, GTZC_MPCBB2},
{SRAM3_BASE, SRAM3_BASE + SRAM3_SIZE, GTZC_MPCBB3},
-#if defined STM32U5A9xx | defined STM32U5G9xx
+#if defined STM32U5A9xx | defined STM32U5G9xx | defined STM32U5A5xx
{SRAM5_BASE, SRAM5_BASE + SRAM5_SIZE, GTZC_MPCBB5},
#endif
#if defined STM32U5G9xx
@@ -179,6 +179,8 @@ typedef struct {
#define XFLASH_BANK_SIZE 0x200000
#elif defined STM32U5G9xx
#define XFLASH_BANK_SIZE 0x200000
+#elif defined STM32U5A5xx
+#define XFLASH_BANK_SIZE 0x200000
#elif defined STM32U585xx
#define XFLASH_BANK_SIZE 0x100000
#else
diff --git a/core/embed/upymod/Cargo.toml b/core/embed/upymod/Cargo.toml
index b97992e1..e4640162 100644
--- a/core/embed/upymod/Cargo.toml
+++ b/core/embed/upymod/Cargo.toml
@@ -24,6 +24,7 @@ mcu_stm32f427 = ["io/mcu_stm32f427", "mcu_stm32f4"]
mcu_stm32f429 = ["io/mcu_stm32f429", "mcu_stm32f4"]
mcu_stm32u58 = ["io/mcu_stm32u58", "mcu_stm32u5"]
mcu_stm32u5g = ["io/mcu_stm32u5g", "mcu_stm32u5"]
+mcu_stm32u5a = ["io/mcu_stm32u5a", "mcu_stm32u5"]
# --------------------------------------------------------------------------
# Selectable components
diff --git a/core/embed/xbuild/src/trezor.rs b/core/embed/xbuild/src/trezor.rs
index 7327dafb..8c9e251e 100644
--- a/core/embed/xbuild/src/trezor.rs
+++ b/core/embed/xbuild/src/trezor.rs
@@ -225,6 +225,8 @@ impl CLibrary {
// final binary according to the selected binary type.
let target_ld = if has_feature("mcu_stm32u5g") {
format!("sys/linker/stm32u5g/{binary_type}.ld")
+ } else if has_feature("mcu_stm32u5a") {
+ format!("sys/linker/stm32u5a/{binary_type}.ld")
} else if has_feature("mcu_stm32u58") {
format!("sys/linker/stm32u58/{binary_type}.ld")
} else if has_feature("mcu_stm32f4") {
diff --git a/core/embed/xtask/src/config.rs b/core/embed/xtask/src/config.rs
index cd46ffb8..a9def6c6 100644
--- a/core/embed/xtask/src/config.rs
+++ b/core/embed/xtask/src/config.rs
@@ -48,7 +48,7 @@ impl ModelConfig {
pub fn target_triple(&self) -> Result<&'static str> {
match self.mcu.as_str() {
"stm32f427" | "stm32f429" => Ok("thumbv7em-none-eabihf"),
- "stm32u58" | "stm32u5g" => Ok("thumbv8m.main-none-eabihf"),
+ "stm32u58" | "stm32u5g" | "stm32u5a" => Ok("thumbv8m.main-none-eabihf"),
mcu => Err(anyhow!("Unknown MCU: {mcu}")),
}
}
@@ -56,7 +56,7 @@ impl ModelConfig {
pub fn openocd_target(&self) -> Result<&'static str> {
match self.mcu.as_str() {
"stm32f427" | "stm32f429" => Ok("target/stm32f4x.cfg"),
- "stm32u58" | "stm32u5g" => Ok("target/stm32u5x.cfg"),
+ "stm32u58" | "stm32u5g" | "stm32u5a" => Ok("target/stm32u5x.cfg"),
mcu => Err(anyhow!("Unknown MCU: {mcu}")),
}
}
Why this scored 15/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.