armhub.dev
Architecture · 15 pages
Architecturesecurity

TrustZone

How TrustZone partitions ARM systems into secure and non-secure worlds — on Cortex-M (per-access SAU/IDAU) and on Cortex-A (per-CPU NS bit).

2 min read · 496 wordsupdated · May 12, 2026

TrustZone is ARM's hardware mechanism for partitioning a system into two worlds — Secure and Non-secure — that can't see each other's memory or registers. The two profiles (M and A) implement it very differently; this page covers both.

Cortex-M TrustZone (TrustZone-M)

On ARMv8-M, the security state is checked on every memory access, by the SAU (Secure Attribution Unit) inside the core and the IDAU (Implementation-Defined Attribution Unit) on the vendor's silicon.

              NS code ──load──▶  ┌─────────┐
                                 │  SAU   │
                                 │ + IDAU │ ──▶ allow / fault
              S code ──load──▶  │ check  │
                                 └─────────┘

Each address has one of three attributes:

  • Secure (S) — accessible only when the core is in Secure state.
  • Non-secure (NS) — accessible from either state.
  • Non-secure Callable (NSC) — only NSC pages can hold the SG instructions that Non-secure code calls into Secure code through.

The SG instruction

The only legal entry into Secure code is via a secure gateway veneer:

// In a Secure module, exposed to Non-secure callers:
__attribute__((cmse_nonsecure_entry))
int secure_add(int a, int b) {
    return a + b;
}

The compiler emits a tiny veneer like:

secure_add:
    SG                  ; secure gateway — only valid in NSC region
    PUSH  {lr}
    BL    _secure_add_impl
    POP   {lr}
    BXNS  lr            ; return to Non-secure

Any other instruction in NSC space faults. Any branch from NS into S that doesn't start at an SG faults. This makes the attack surface a finite, auditable list of entry points.

Calling NS from S

The reverse — Secure code calling out to a Non-secure callback — uses BLXNS, and the compiler decorates the function pointer type:

typedef void (*ns_callback_t)(void) __attribute__((cmse_nonsecure_call));
 
void register_callback(ns_callback_t cb) {
    cb();   // compiler emits BLXNS — registers cleared before transition
}

Before the BLXNS, the compiler clears all caller-saved registers so that S code never leaks state into NS — this is the cmse_nonsecure_call ABI.

Configuring the SAU

A typical SAU setup, done early in Reset_Handler before C runtime init:

// 4 regions, each up to 32 bytes aligned
SAU->RNR  = 0;
SAU->RBAR = 0x0010_0000;   // base
SAU->RLAR = 0x001F_FFE0 | SAU_RLAR_ENABLE_Msk;  // limit + enable
// NSC bit (bit 1 of RLAR) marks a region as Non-secure Callable
 
SAU->CTRL = SAU_CTRL_ENABLE_Msk;
__DSB(); __ISB();

Build flags

The toolchain needs to know which "world" your code targets:

# Building Secure firmware (knows about NS too)
arm-none-eabi-gcc -mcpu=cortex-m33 -mcmse  secure.c -o secure.o
 
# Building Non-secure firmware (only sees public veneers)
arm-none-eabi-gcc -mcpu=cortex-m33 nonsecure.c -o nonsecure.o

Secure builds emit a veneer object (--out-implib=secure_veneers.o) that the Non-secure side links against, so the linker knows the addresses of the SG veneers.

Cortex-A TrustZone (TrustZone-A)

A-profile TrustZone is older and works differently. There's a single NS bit in the Secure Configuration Register (SCR_EL3) that says whether the whole CPU is currently Non-secure. The bit is propagated on the AXI bus as AxPROT[1], and the system memory controller decides per-region what's accessible.

The mode hierarchy gets a fourth level:

EL0  user                  ┐ Non-secure world (Linux/Android)
EL1  kernel                │
EL2  hypervisor            │
─────────────────────────  ─ Secure Monitor (EL3)
EL3  secure monitor         ─ trusted firmware (TF-A)
                            │
EL1  trusted OS (OP-TEE)    │ Secure world
EL0  trusted applets        ┘

Switching between worlds is done with SMC (Secure Monitor Call). Linux issues SMC to ask the Secure world for a service (e.g. "please decrypt this block") and OP-TEE in EL1-S services it.

Where you meet it

  • Trusted Firmware-A (TF-A) — the reference EL3 implementation on ARMv8-A.
  • OP-TEE — the open-source Trusted Execution Environment running in EL1-S.
  • DRM, key storage, fingerprint sensors — secure-world clients in shipping Android phones.

Comparing the two

AspectTrustZone-M (Cortex-M)TrustZone-A (Cortex-A)
Check granularityper memory access (SAU/IDAU)per CPU mode (NS bit on bus)
Transition costa few cycles (SG / BXNS)hundreds (SMC + monitor)
Stateadded to CPU; M+S = 4 modesadded EL3 + SCR_EL3.NS
Typical usesecure boot, key isolation, certified bootloaderstrusted OS, DRM, root of trust

See also