Skip to content

Repository files navigation

STM32L552 TrustZone Rust Example

This workspace contains two no_std Cortex-M projects for an STM32L552-class Cortex-M33 target:

  • secure/: secure-world image
  • nonsecure/: non-secure-world image
  • xtask/: host-side helper used to build both images in the right order

The secure image:

  • prints a semihosting banner from the secure world
  • configures a simple SAU layout
  • installs the non-secure VTOR
  • initializes the non-secure MSP
  • branches to the non-secure reset handler
  • exports one secure callable function through an NSC veneer

The non-secure image:

  • prints a semihosting banner from the non-secure world
  • calls the secure veneer-backed function

Memory layout assumptions

This example now follows the same TrustZone partitioning as the reference C project:

  • Secure flash alias: 0x0C00_0000 .. 0x0C03_DFFF
  • NSC veneers forced to: 0x0C03_E000 .. 0x0C03_FFFF
  • Non-secure flash: 0x0804_0000 .. 0x0807_FFFF
  • Secure SRAM linker region: 0x3000_0000 .. 0x3001_7FFF
  • Non-secure SRAM linker region: 0x2001_8000 .. 0x2002_FFFF
  • SAU non-secure SRAM window: 0x2001_8000 .. 0x2003_FFFF

The secure project also mirrors the C project's GTZC policy:

  • LPUART1 and VREFBUF are secure, non-privileged
  • lower 96 KiB of SRAM1 remain secure
  • upper 96 KiB of SRAM1 are non-secure
  • all of SRAM2 is non-secure

Adjust the linker scripts under secure/ and nonsecure/ if your TrustZone option bytes or board layout differ.

Building each image in isolation

Build the secure image:

cd secure
cargo build

That produces the secure import library / veneer object at:

target/thumbv8m.main-none-eabihf/debug/veneer.o

Build the non-secure image against the secure veneer object:

cd nonsecure
cargo build

If you want a custom veneer path, set:

SECURE_VENEER=/path/to/veneer.o cargo build

One-command build

Use the workspace helper:

cargo run -p xtask -- build

Or use the convenience shell script from the workspace root:

./build.sh

There is also a Cargo alias at the workspace root:

cargo tz-build

Release mode:

cargo run -p xtask -- build --release

Or:

cargo tz-build-release

Or:

./build.sh --release

The helper builds secure/ first, then passes the generated veneer.o into the nonsecure/ build. It also emits a merged HEX image at target/combined-debug.hex or target/combined-release.hex.

Cleaning Build Artifacts

The workspace uses one shared ./target directory. A plain cargo clean at the workspace root cleans the default host-side xtask artifacts; use the workspace helper to clean the firmware target directory as well.

To clean all build artifacts together, use:

cargo tz-clean

Or:

./clean.sh

This removes:

  • ./target

Interactive Debug

TrustZone-M+OpenOCD+GDB

For an interactive OpenOCD + GDB session, use:

./debug.sh server

in one terminal, then:

./debug.sh gdb

in another.

The helper:

  • starts openocd with a GDB server on :3333
  • loads the secure ELF into gdb-multiarch
  • adds non-secure symbols from the non-secure ELF at 0x08040000
  • issues monitor reset halt automatically

If the images are already built and flashed:

./debug.sh --no-build --no-flash server
./debug.sh gdb

Flashing

The workspace includes a convenience script for the NUCLEO-L552ZE board:

./flash.sh

This script:

  • builds the secure and non-secure images unless --no-build is passed
  • flashes both the secure and non-secure ELFs with openocd in a single session
  • resets the target at the end

Release flashing:

./flash.sh --release

To launch the project and forward semihosting output to your terminal:

./semihost.sh

Release mode:

./semihost.sh --release

If the images are already built and flashed:

./semihost.sh --no-build --no-flash

semihost.sh uses ./flash.sh for programming, then uses openocd only for attach, reset, and semihost output.

If you need to choose a specific debug probe or SWD speed:

./flash.sh --probe <serial-or-probe-selector> --speed 4000

If the device is hard to attach to, try:

./flash.sh --connect-under-reset

The current tooling choice is based on observed behavior on this board:

  • openocd reliably programmed and verified both the secure and non-secure ELFs

Important TrustZone Prerequisite

Flashing the two ELFs is not sufficient by itself on STM32L5. The board's TrustZone-related option bytes and flash partitioning must match the layout used by this example:

  • secure image flash physical window: 0x0800_0000 .. 0x0803_DFFF
  • secure image flash alias window: 0x0C00_0000 .. 0x0C03_DFFF
  • NSC veneers at: 0x0803_E000 .. 0x0803_FFFF physical, 0x0C03_E000 .. 0x0C03_FFFF alias
  • non-secure flash window: 0x0804_0000 .. 0x0807_FFFF
  • secure SRAM linker region: 0x3000_0000 .. 0x3001_7FFF
  • non-secure SRAM linker region: 0x2001_8000 .. 0x2002_FFFF

In practice, that means:

  • TrustZone must be enabled on the STM32L552
  • the secure watermark / memory attribution option bytes must be configured consistently with the linker scripts
  • NSBOOTADD0 should point to the non-secure image base at 0x0804_0000

This repository automates image flashing with openocd, semihost bring-up with openocd, and includes a separate helper for STM32L5 option-byte provisioning with STM32CubeProgrammer CLI:

./provision.sh

To inspect the current option bytes only:

./provision.sh --display-only

The detailed board-preparation notes are in docs/board_setup_nucleo_l552ze.md.

Notes

  • secure/ uses nightly because the secure gateway export relies on cmse_nonsecure_entry.
  • Both images use build-std so you do not need a preinstalled thumbv8m.main-none-eabihf standard library target.
  • This workspace was written to be semihosting-friendly for debug bring-up. On hardware you will need a debugger/server that supports semihosting.
  • Current nightly emits a warning for -C target-feature=+trustzone; the build still succeeds and is expected for this example.

About

Demonstration project illustrating how to create a TrustZone-M based project in Rust

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages