For Applications & Industries
Free Online Training Events
Free Technical Resources
no_std TrustZone-M Application.TrustZone-M is usually explained at the architectural level: Secure and Non-secure worlds, isolated memory regions, secure gateways, hardware-enforced boundaries. Those concepts matter. But they leave open most of the questions that come up once you actually start building firmware.
What does the Secure/Non-secure split look like in code?
How do the two worlds interact? What does the boot sequence involve?
How do exceptions and faults behave once security state enters the execution model?
And how do you structure a Rust project so that all of this stays manageable?
This series documents a practical experiment: implementing a no_std Rust application on a TrustZone-M capable microcontroller. The goal was a working dual-image system: a Secure application, a Non-secure application, a controlled boundary between them, and a Rust build flow capable of producing and linking both images correctly.
The project uses a real workspace: separate Secure and Non-secure crates, custom linker configuration, CMSE entry points, and an xtask-driven build process. Working from a concrete implementation means the discussion can stay close to the code. How memory is partitioned. How the Secure world hands execution to the Non-secure side. How Secure services are exposed, how veneers are generated and linked, and how faults can be diagnosed at the security boundary.
Rust's type system and ownership model eliminate large classes of programming errors, but they do not replace hardware-enforced security isolation. The two address different problems. Rust makes firmware safer to write;
TrustZone-M keeps sensitive services and assets isolated even if less-trusted application code fails or is compromised. Combining them is worthwhile, but it introduces real complications: custom startup flow, multiple linker layouts, explicit unsafe transitions, boundary-aware APIs, and a need to understand processor behaviour across both security states. None of that is insurmountable, but it requires deliberate design decisions at every level.
This series is a guided walk through that experiment. It starts with the architectural foundations of TrustZone-M, then covers motivation and system partitioning, cross-boundary calls in both directions, exception and fault behaviour, and finally the build orchestration needed to make everything reproducible.
The posts are organised as follows:
A practical introduction to Secure and Non-secure execution, the role of hardware attribution, and what Non-secure Callable regions actually mean.
Partitioning the system: Secure image, Non-secure image, memory layout, and boot flow
How the firmware splits into two worlds, how flash and SRAM are allocated, and how execution moves from Secure reset into the Non-secure application.
Calling Secure services from Non-secure code with CMSE
Secure entry points, veneers, Non-secure Callable regions, and how Rust exposes controlled Secure services to the Non-secure side.
Calling Non-secure code from the Secure world with CMSE
The reverse direction: Secure handoff to the Non-secure reset handler, and what care is required when branching into Non-secure code.
Exceptions, interrupts, preemption, and fault handling across security states
The exception model in a TrustZone-M system: separate Secure and Non-secure handling contexts, dual vector tables, and diagnostic fault handling via semihosted output.
Organising the Rust workspace, build flow, and xtask automation
The repository structure and build process used to compile, link, and package both images reproducibly.
The discussion stays grounded in the actual project throughout, source files, linker scripts, and build flow rather than abstract diagrams. Failure modes are covered as well: incorrect attribution, transition faults, the things that go wrong before they go right. Getting a dual-image TrustZone-M system working in Rust involves more moving parts than most bare-metal projects, and understanding why each part is necessary is as useful as knowing what it does.
This article was written by Dr David Cabanis, principal member technical staff at Doulos and author of the book Embedded Software with Rust.