TrustZone Basics#

Introduction#

TrustZone for ARMv8-M adds extra states to the Cortex-M processor operations to ensure there is a Secure and Non-secure state. These security states are orthogonal to the existing Thread and Handler modes, thereby having both a Thread and Handler mode in both Secure and Non-secure states. The Thread mode can also be either Privileged or Unprivileged.

Operation States and Modes of TrustZone ImplementationOperation States and Modes of TrustZone Implementation

Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

TrustZone for ARMv8-M is an optional architecture extension. By default, the system starts up in a Secure state if the processor implements the TrustZone security extension. The division of Secure and Non-secure worlds is memory-map based (security state depends on the address of the fetched instruction), and the transitions happen automatically. It is also possible to leave the Non-secure state unused and execute the whole application in the Secure state.

Memory Security Attributes#

TrustZone classifies memory into four security attributes as described in the following table.

Security Attribute

Processor State

Description

Non-secure (NS)

Non-secure

Non-secure and Secure software can access these memory regions.

Secure (S)

Secure

Secure software can access these memory regions. Non-secure software cannot gain access to the Secure memory.

Non-secure Callable (NSC)

Secure

Secure memory with an NSC attribute provides entry points for Secure APIs that can be called from a Non-secure space. It is a region of memory that contains the Secure Gateway (SG) veneers that allow Non-secure code to call secure functions that exist in Secure code. Non-secure software cannot read/write to an NSC memory but can branch into it if the branch target is an SG instruction.

Exempted

Secure/Non-secure

Non-secure and Secure software can access these memory regions (exempted from security checking). Exempted regions are typically used by debugging components that do not pose any security risk (e.g., system ROM table) when accessed by the Non-secure software.

Note: The Non-secure Callable is also known as Secure Non-secure Callable (Secure NSC) to declare that this region resides in Secure memory.

Banked Register#

The concept of a banked register in ARMv8-M between Secure and Non-secure states means that there are two copies of the register, and the core automatically uses the copy that belongs to the current security state. When a register is banked, the _S and _NS suffixes are used in the ARMv8-M architecture to identify whether the resource is for the Secure state or Non-secure state.

General-Purpose Registers#

The Cortex-M processors have 16 general-purpose registers (R0 - R15) for data processing (R0 - R12) and control. The following figure shows the general-purpose register view of the ARMv8-M system with TrustZone. Refer to the ARM Cortex-M33 Devices Generic User Guide for details about these registers.

General-Purpose Register View with TrustZoneGeneral-Purpose Register View with TrustZone

Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

The Secure or Non-secure state can access the data processing registers R0 - R12 and special usage registers R13 - R15. The register R13 (banked SP) is the stack pointer alias, and the actual stack pointer (MSP_NS, PSP_NS, MSP_S, PSP_S) accessed depends on the state (Secure or Non-secure) and mode (Handler or Thread) as described in the following figure.

In addition, stack limit registers (special registers) enable hardware to detect stack overflow conditions. Two pairs of stack limit registers (MSPLIM_NS and PSPLIM_NS, MSPLIM_S and PSPLIM_S) are implemented, one per security state, to protect the Main Stack Pointer (MSP) and Process Stack Pointer (PSP).

Banked Registers in the General-Purpose RegistersBanked Registers in the General-Purpose Registers

Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

In Thread mode, execution can be privileged or unprivileged. The stack pointer used can be the MSP or PSP, depending on the SPSEL bit in the CONTROL register. When in Handler mode, the processor is Privileged. The stack pointer is always MSP.

It is possible to directly access the stack pointers (MSP and PSP) and stack limit registers (MSPLIM and PSPLIM), providing that the processor is in a privileged state. If the processor is in a Secure privileged state, the software can also access the Non-secure stack pointers (MSP_NS and PSP_NS) through Core Register Access Functions in CMSIS-Core.

Special-Purpose Registers#

Except for the general-purpose registers, there are several special-purpose registers for conditional flags, interrupt masking, control, and stack pointer limit. The following figure shows the special-purpose registers view of the ARMv8-M system with TrustZone. Refer to the ARM Cortex-M33 Devices Generic User Guide for details about these registers.

Special-purpose Registers View with TrustZoneSpecial-purpose Registers View with TrustZone

Image:https://documentation-service.arm.com/.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

The Combined Program Status Register (xPSR) consists of the Application Program Status Register (APSR), Interrupt Program Status Register (IPSR), and Execution Program Status Register (EPSR).

Some of the special-purpose registers are banked between Secure and Non-secure states. Special-purpose registers are not memory-mapped and can be accessed using Core Register Access Functions in CMSIS-Core (except for EPSR in xPSR).

Secure privileged software can also access the Non-secure interrupt masking registers (PRIMASK_NS, FAULTMASK_NS, and BASEPRI_NS), CONTROL register (CONTROL_NS), and stack limit registers (MSPLIM_NS and PSPLIM_NS) through Core Register Access Functions in CMSIS-Core.

System Private Peripheral Bus (PPB)#

The banking of registers is usually used to separate the Secure and Non-secure information of the system components inside the processor. The following figure shows the System Private Peripheral Bus (PPB) registers view of the ARMv8-M system with TrustZone. Refer to the ARM Cortex-M33 Devices Generic User Guide for details about the System PPB registers.

System Private Peripheral Bus (PPB) Registers View with TrustZoneSystem Private Peripheral Bus (PPB) Registers View with TrustZone

System components for debugging and trace operations (0xE0000000 to 0xE0002FFF):

  • Instrumentation Trace Macrocell (ITM)

  • Data Watch point and Trace unit (DWT)

  • Flash Patch and Breakpoint unit (FPB)

System Control Space (SCS):

  • The registers in SCS address spaces are memory-mapped and can be accessed using pointers in software

  • Secure SCS (0xE000E000 to 0xE000EFFF) - Secure software using this address space to access the banked Secure SCS registers (e.g., SCB->CPUID)

  • Non-secure SCS (0xE000E000 to 0xE000EFFF) - Non-secure software using this address space to access the banked Non-secure SCS registers (e.g., SCB->CPUID)

  • Non-secure alias SCS (0xE002E000 to 0xE002EFFF) - Secure software using this address space to access the Non-secure SCS registers (e.g., SCB_NS->CPUID)

The following table describes some core peripherals in the SCS and corresponding data structures defined in the CMSIS-Core header file to access the registers of core peripherals in two SCS address spaces.

Core Peripheral

Data Structure for Secure and NS SCS

Data Structure for NS Alias SCS

Implementation Control Block

SCnSCB (0xE000E004)

SCnSCB_NS (0xE002E004)

SysTick Timer

SysTick (0xE000E010)

SysTick_NS (0xE002E010)

Nested Vectored Interrupt Controller

NVIC (0xE000E100)

NVIC_NS (0xE002E100)

System Control Block

SCB (0xE000ECFC)

SCB_NS (0xE002ECFC)

Memory Protection Unit

MPU (0xE000ED90)

MPU_NS (0xE002ED90)

Security Attribution Unit

SAU (0xE000EDD0)

-

Debug Control Block

CoreDebug (0xE000EDF0)

CoreDebug_NS (0xE002EDF0)

Software Interrupt Generation

STIR (0xE000EF00)

STIR_NS (0xE002EF00)

Floating-Point Extension

FPU (0xE000EF34)

FPU_NS (0xE002EF34)

Notes:

  • The SCB is a group of system control registers for the various usages below.

    • System Control Register (SCR) to configure processor low power mode

    • Fault Status Register (xFSR) to provide fault status information

    • Vector Table Offset Register (VTOR) for vector table relocation

  • The SAU register is accessible from the Secure state only.

  • The STIR register is not physically banked.

  • Core peripherals such as SysTick, SCB, and MPU are duplicated. One instance is Secure and the other one is Non-secure.

  • Secure software can use the corresponding functions for ARMv8-M in CMSIS-Core to configure the Non-secure NVIC and SysTick through the Non-secure alias SCS.

Debug or vendor specific components (0xE0040000 to 0xE00FFFFF):

Secure Attribution Unit (SAU), Implementation Defined Attribution Unit (IDAU), and Memory Protection Unit (MPU)#

Two units determine the security attribute of an address:

  1. The internal programmable Secure Attribution Unit (SAU).

  2. The external Implementation Defined Attribution Unit (IDAU), through the IDAU interface, returns the security attribute and region number of an address.

mpumpu

Three possible configurations to define the security attribute of an address:

  1. Internal SAU only

  2. External IDAU only

  3. A combination of the internal SAU and external IDAU

Notes:

The Memory Protection Unit (MPU) is a programmable unit that allows privileged software to define memory access permission. If the TrustZone is enabled, there can be up to two MPUs, one for Secure and one for Non-secure.

SoftwareNon-secure MPU RegistersSecure MPU RegistersMemManage Fault

Non-secure privileged

0xE000ED90 - 0xE000EDC4

-

Non-secure MPU violation

Secure privileged

0xE002ED90 - 0xE002EDC4

0xE000ED90 - 0xE000EDC4

Secure MPU violation

Exceptions and Interrupts#

Type of Exceptions#

The following table describes the types of exceptions in the TrustZone implemented system.

SectionGuidanceTypeDefault State

1 (-)

Reset

Secure only

Secure

2 (-14)

NMI

Configurable

Secure

3 (-13)

HardFault

Configurable

Secure

4 (-12)

MemManage Fault

Banked

Banked

5 (-11)

BusFault

Configurable

Secure

6 (-10)

UsageFault

Banked

Banked

7 (-9)

SecureFault

Secure only

Secure

11 (-5)

SVCall

Banked

Banked

12 (-4)

DebugMonitor

Configurable

Secure

14 (-2)

PendSV

Banked

Banked

15 (-1)

SysTick

Banked

Banked

16 - 495 (0 - 479)

IRQ0 - IRQ479

Configurable

Secure

Notes:

  • "Secure only" means the system exceptions can only trigger in the Secure state.

  • "Configurable" means the system exceptions and interrupts can be configured to target either the Secure state or the Non-secure state.

  • Banked means the system exceptions can have Secure and Non-secure versions. Both can be triggered and executed independently and have different priority level settings.

Exception Priorities#

It may cause a security issue if the Non-secure software uses high priority levels to mask the Secure interrupts. To avoid this issue, TrustZone introduces a programmable bit in the AIRCR register called PRIS (Prioritize Secure exception) for Secure software to prioritize, if needed, Secure exceptions and interrupts.

The AIRCR.PRIS is set to 0 out of reset, which means Secure and Non-secure exceptions/interrupts share the same configurable programmable priority level space (columns 2 and 3 in the following table). When the AIRCR.PRIS is set to 1, all Non-secure configurable exceptions/interrupts are placed in the lower half of the priority level space so that Secure exceptions/interrupts can potentially have higher priorities (columns 2 and 4 in the following table).

Priority ValueSecure PriorityNon-secure Priority (PRIS = 0)Non-secure Priority (PRIS = 1)

0

0

0 (0x00)

128 (0x80)

1

32

32 (0x20)

144 (0x90)

2

64

64 (0x40)

160 (0xA0)

3

96

96 (0x60)

176 (0xB0)

4

128

128 (0x80)

192 (0xC0)

5

160

160 (0xA0)

208 (0xD0)

6

192

192 (0xC0)

224 (0xE0)

7

224

224 (0xE0)

240 (0xF0)

Note: This table uses three bits (Bit [7:5]) of the group priority level (AIRCR.PRIGROUP) to limit the maximum number of preemption levels to 8. A lower priority value indicates a higher priority.

Vector Tables#

The following figure shows two vector tables for Secure and Non-secure exceptions and interrupts. The vector table offset is defined by a Vector Table Offset Register (VTOR at 0xE000ED08), which can only be programmed in the privileged state.

vector-tablevector-table

Image: Vector Table.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

Notes:

  • The VTOR_S defines the address of the Secure vector table in Secure memory, and the Secure Main Stack Pointer (MSP_S) is the default stack for the Secure exception handler.

  • The VTOR_NS defines the address of the Non-secure vector table in Non-secure memory, and the Non-secure Main Stack Pointer (MSP_NS) is the default stack for the Non-secure exception handler.

  • Secure privileged software can access the VTOR_NS using the Non-secure SCB alias (0xE002ED08).

  • The System Control Space contains registers for the SysTick timer, NVIC, and SCB.

  • The interrupt masking registers (PRIMASK, FAULTMASK, and BASEPRI) are banked between security states. The priority level space is shared between the Secure and the Non-secure world, setting an interrupt mask register on one side can block some, or all, of the exceptions on the other side.

  • Interrupts (IRQ0 - IRQ479) are defined as Secure by default. Each interrupt can be configured as Secure or Non-secure and is determined by the Interrupt Target Non-secure (NVIC_ITNS) register, which is only programmable in the Secure software.

State Transitions in Exceptions and Interrupts#

The following figure shows transitions between the processor states in ARMv8-M TrustZone.State TransitionsState Transitions

Image (left): Switching-between-Secure-and-Non-secure-states.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

  1. Secure Thread → Secure Handler or Non-secure Thread to Non-secure Handler

    • No security state transition.

    • The exception sequence is almost identical to the exception stacking mechanism of current Cortex-M processors.

    • The Interrupt Service Routine (ISR) is executed in the current security state (either Secure or Non-secure).

  2. Non-secure Thread → Secure Handler or Non-secure Handler → Secure Handler

    • The transition from Non-secure to Secure state.

    • The exception sequence is almost identical to the exception stacking mechanism of current Cortex-M processors.

    • The ISR is executed in a Secure state.

  3. Secure Thread → Non-secure Handler or Secure Handler → Non-secure Handler

    • The transition from Secure to Non-secure state.

    • To avoid an information leak when transitioning from the Secure to Non-secure state. The processor automatically pushes all general-purpose registers into the Secure stack and erases the contents of all general-purpose registers before executing the Non-secure ISR. The processor pops the contents of all general-purpose registers from the Secure stack when returning from the Non-secure ISR (right side in Figure 2.6 State Transitions on page 12). It incurs a slightly longer interrupt latency.

    • The ISR is executed in a Non-secure state.

  4. Secure Privileged Thread ↔ Non-secure Privileged Thread or Secure Unprivileged Thread ↔ Non-secure Unprivileged Thread

    • The transition from Secure to Non-secure state or Non-secure to Secure state.

    • The Function calls and returns can be used when the privileged level remains the same.

Note: Subject to interrupt priority, there are no restrictions regarding whether a Non-secure or Secure interrupt can occur when the processor runs Non-secure or Secure code.

Switching Between Secure and Non-secure States#

The TrustZone allows direct calling between Secure and Non-secure software. The following figure shows how to use an API function call to trigger security state transitions. The state transitions can also happen because of exceptions and interrupts.

swich stateswich state

Image: Switching-between-Secure-and-Non-secure-states.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

Switching from Non-secure to Secure State#

When the Non-secure program calls a Secure software, the first instruction must be a Secure Gateway (SG) instruction residing in Non-secure Callable memory. The Secure Gateway entry points (veneers) decouple the address of the SG instructions in the Non-secure Callable memory region from the rest of the Secure code. It can eliminate the risk of having inadvertent entry points when the Secure software contains a pattern that matches the opcode of the SG instruction.

switch-imageswitch-image

Image (right): Whitepaper - ARMv8-M Architecture Technical Overview. Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

The bit 0 of the Link Register (LR) is cleared to zero by SG instruction to indicate that returning from this function transits from Secure to Non-secure. The processor is still in the Non-secure state when the SG instruction is executed. The BXNS LR instruction is used when returning since a normal BX LR instruction interprets it as an unsupported execution mode change. A SecureFault exception is triggered if the processor returns to a Secure address. It prevents a hacker from calling a Secure API with a fake return address pointing to a Secure program location. If bit 0 of LR is 1, the BXNS LR instruction behaves like a normal BX LR. Therefore, Secure code can call a Secure API in the NSC region even it is not a usual practice.

ProgramCall InstructionSG InstructionReturn Instruction

Non-secure call Non-secure

BL or BLX

-

BX LR (Return to Non-secure state)

Non-secure call Secure

BL or BLX

Clear bit 0 of LR

BXNS LR (Return to Non-secure state)

Secure call Secure

BL or BLX

Set bit 0 of LR

BXNS LR (Return to Secure state)

To help software developers create Secure APIs in C/C++, the Cortex-M Security Extension (CMSE) defines a C function attribute called cmse_nonsecure_entry.

  • GCC — __attribute__((cmse_nonsecure_entry))

  • IAR — __cmse_nonsecure_entry

Test Target (TT) Instruction#

The software can use an ARMv8-M instruction called Test Target (TT) and the region number generated by the SAU or the IDAU to determine if a contiguous range of memory shares common security attributes and privilege levels.

The TT instruction returns the SAU/IDAU region number, security attributes (S/NS), and MPU region number after passing the start and end addresses of the memory range to the TT instruction. The software can determine whether the memory range has required security attributes and resides in the same region number.

tt-instructiontt-instruction

Image: Test-target-instruction.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

This mechanism allows security checking at the beginning of the API service (instead of during the operation) to determine if the memory referenced by a pointer from Non-secure software points to the Non-secure address. It prevents Non-secure software from using those APIs in Secure software to access or modify Secure data.

To make these operations easier in a C/C++ programming environment, the Cortex-M Security Extension (CMSE) has defined a range of intrinsic functions for dealing with pointer checks with the TT instructions.

Switching from Secure to Non-secure State#

When the Secure program calls a Non-secure software, the Secure program must use a BLXNS <reg> instruction to invoke the process. If bit 0 of the <reg> is 0, the processor must switch to the Non-secure state when branching to the target address. During the state transition, the return address and some processor state information are pushed onto the Secure stack, while the return address on the Link Register (LR) is set to a special value called FNC_RETURN (0xFEFFFFFF).

The Non-secure function completes by performing a branch (BX LR) to the FNC_RETURN address (bit 0 is 1 to indicate the function was called from the Secure state). It automatically triggers the unstacking of the actual return address from the Secure stack and returns to the calling function. The FNC_RETURN hides the return address of the Secure program from the Non-secure software to avoid the leakage of any secret information. It also prevents Non-secure software from modifying the Secure return address stored in the Secure stack.

switch_nsswitch_ns

Image: Switching-between-Secure-and-Non-secure-states.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

To help software developers declare Non-secure function pointers in C/C++, the Cortex-M Security Extension (CMSE) defines a C function attribute called cmse_nonsecure_call.

  • GCC: __attribute__((cmse_nonsecure_call))

  • IAR: __cmse_nonsecure_call

Software Flow#

The following figure describes a software flow example in a TrustZone implemented system.

Execution Flow of a TrustZone Implemented SystemExecution Flow of a TrustZone Implemented System

Image: Software Development in ARMv8-M Architecture.Copyright © 1995-2022 Arm Limited (or its affiliates). All rights reserved.

  1. The system starts executing code in the Secure state after a power-on or reset (Secure boot).

    • The Secure stack pointer (MSP_S) is set from the address of the Secure vector table (VTOR_S).

    • The Secure Reset Handler pointed by the VTOR_S is called.

    • Perform various initialization tasks such as C startup code.

    • Place peripherals and associated interrupts in either Secure or Non-secure applications.

    • Program SAU/IDAU to partition the entire memory into Secure, Non-secure Callable, and Non-secure regions.

    • Program the address of the Non-secure vector table (VTOR_NS).

    • Initialize the two first entries of the table for the Non-secure stack pointer (MSP_NS) and Reset Handler to emulate a Non-secure reset.

  2. The Secure firmware branches to the entry point (Reset Handler pointed by the VTOR_NS) of the Non-secure application.

    • The Non-secure software has its Reset Handler.

    • Perform various initialization tasks such as C startup code and hardware initialization (e.g., Non-secure peripherals).

    • It does not conflict with initialization from the Secure code as the stack and heap spaces of Secure and Non-secure code are separated.

  3. During the execution of Non-secure applications, the application could call Secure APIs through the Secure Gateway (SG) veneer in the Non-secure Callable region.

  4. In some cases, Secure APIs might need to call Non-secure call-back functions (e.g., a hardware driver).