Chapter 4 Basics: TrustZone-M

4.1 Introduction

This chapter introduces you to the basic concepts and terms of TrustZone on Armv8-M. TrustZone, or Security Extensions, are an optional feature on some Armv8-M cores.The security extensions are also provided on A-profile cores, but their actual implementation differs, as we will see during this chapter.

In the first few chapters introduce you to important concepts and terms for TrustZone on Cortex-M processors, which are applicable to all Cortex-M processors with security extension:

  • Chapter 4.3: A comparison between TrustZone on Cortex-A and Cortex-M.
  • Chapter 4.4: Introduction to secure and non-secure worlds.
  • Chapter 4.5: IDAU and SAU
  • Chapter 4.6: Transitions between worlds: non-secure callable region, SG

4.2 Execution Modes and Privilege Levels (with TrustZone)

More on Arm Privilege Levels:

  • Chapter 4.2: Execution Modes and Privilege Levels (with TrustZone)
  • Chapter 3.4: Arm Architecture: Execution Modes and Privilege Levels

Security Extensions introduce an additional partitioning of the system - the security level - which is orthogonal to the execution mode already available (see Chapter 3.4): secure world (S) (or TrustZone) and non-secure (NS) world. In both of these worlds thread and handler mode are available. Additionally you can assign Peripherals, Flash and RAM to one world. Once TrustZone is enabled on a processor, it always starts in S world.

Privileged and Unprivileged Execution in secure and non-secure wold

Figure 4.1: Privileged and Unprivileged Execution in secure and non-secure wold

4.3 TrustZone in Cortex-M vs -A

TrustZone-A (TrustZone available in Cortex-A cores) share one characteristic with Trustzone-M: Both have the division into secure and non-secure world, where non-secure world can only access non-secure memories. Although the implementations differ, for example TrustZone-M is memory map based, as we will see soon.

Trustzone-A also has a so-called Security Monitor, which is the sole entry point into secure world. Trustzone-M processors can have many entry points, which are placed in a dedicated region, called non-secure callable region.

4.4 Two worlds: Secure and non-secure

Terms explained in this chapter:

  • Security Attribute
  • secure security attribute
  • non-secure security attribute
  • non-secure callable security attribute

Unlike in TrustZone-A, TrustZone-M is memory map based: Memory areas and other critical resources are marked as secure and then can only be accessed when the core is executing in secure state. The program address, the address of the instruction currently executed, determines the security state of the processor. Memory regions are marked by a security attribute, determining their security attribution. When TrustZone-M is enabled, the processors starts always in secure world and all memory addresses (Flash, SRAM, Peripherals) are secure.

There are three security attributes a memory region can have. One for each security level (secure and non-secure) and an additional (non-secure callable), which we are going to discuss soon:

  • non-secure (NS)
  • non-secure callable (NSC)
  • secure (S)

When the processor executes instruction in a NS region, the processor is in security level non-secure.

S world can’t be called directly from NS world. To call functions in S world a small entry point, which resides in a non-secure callable region, must be called. In this region the transition to secure world takes place: For example some registers are banked between security states. After the core has transitioned into secure state, it branches off to the actual implementation of the function being called.

4.4.1 Banked Registers

More on Arm Registers:

  • Chapter 6.2: AAPCS: Subroutine Call
  • Chapter 4.4.1: Banked Registers
  • Chapter 3.7: Arm Architecture: General-Purpose registers, special-purpose registers

Between security states the following registers are banked. Banking a register means that there are distinct instances of these registers available. These instances are switched automatically by the core during transitioning from NS to S and back. The following registers are banked in Armv8-M:

Banked general-purpose registers:

  • R13: SP (Stack Pointer)

Banked special-purpose registers:

  • PRIMASK, FAULTMASK, BASEPRI
  • Some bits in CONTROL

The System Control Space SCS is also banked.

4.5 IDAU and SAU: Security attribution

Terms explained in this chapter:

  • SAU
  • IDAU
  • Attribution Unit

More on Arm Memory System:

  • Chapter 10.2.4: STM32L5: Memory Map
  • Chapter 3.5: Arm Architecture: Memory System

More on Security Attribution Unit:

  • Chapter 4.5: IDAU and SAU: Security attribution
  • Chapter 5.3: CMSIS: ROM segment and Boot Process
  • Chapter 7.2: SAU Initialization
  • Chapter 10.2.4: STM32L5: Memory Map

More on Implementation Defined Attribution Unit:

  • Chapter 4.5: IDAU and SAU: Security attribution
  • Chapter 10.2.4: STM32L5: Memory Map

Security attribution of memory addresses is done in so-called Attribution Units:

  • Security Attribution Unit (SAU), which is always available in Armv8-M cores. On reset SAU is disabled.
  • Implementation Defined Attribution Unit (IDAU), which is external to the core and the presence depends on the vendors implementation.

The default state of memory is secure when SAU is disabled. This is the state in which the core starts at reset, meaning the core can’t switch to non-secure world. If both IDAU and SAU are present in a system, the attribution from SAU is used unless IDAU provides a higher attribution for an address.

SAU and IDAU indicate the attribution of an address by two bits: Non-secure and non-secure callable. If IDAU is present it could be used to define a default, unchangeable memory layout and let the developer then make the final attribution in SAU.

Programming SAU happens only in secure world.

Table 4.1 shows the logic of determining the security attribution for a given address. To determine the security attribution of an address the core presents the address to the SAU and IDAU, which in turn signal to the core the security attributed of the address.

Table: Table 4.1: (from [5])
IDAU security attribution SAU security attribution Final security attribution
non-secure secure secure
NSC NSC
non-secure non-secure
NSC secure secure
NSC NSC
non-secure NSC
secure secure secure
non-secure secure
non-secure callable secure

In chapter 3.5 the M-profile system memory map was introduced. System designers are free on how they further split the system memory map regions into NS and S. An example how NS / S division could be done is by using bit[28] of the address to subdivide each of the regions further into a secure and a non-secure aliases. The code region for example could be then:

  • NS from 0x0000.0000 to 0x0FFF.FFFF
  • S from 0x1000.0000 to 0x1FFF.FFFF

Using that approach the system memory map from chapter 3.5 would result in the following security attribution:

Example memory map with security attribution

Figure 4.2: Example memory map with security attribution

The memory-map based security attribution differs from A-Profile TrustZone, where a virtual address space is available in secure world.

4.6 Transitions

The following chapter gives a high-level overview on how transitions from non-secure to secure world and from secure to non-secure world work.

4.6.1 Secure function call

Terms explained in this chapter:

  • secure function call
  • SG instruction
  • entry function
  • secure gateway veneer

The following chapter gives a high-level overview on secure function calls. We are going to examine the details of secure function calls, how the compiler and linker handle them and how the transition happens in great detail through the book. If you want to dive deeper into the topic at hand, use the “More on..” boxes at the end of the chapter!

A secure function call is a call from non-secure world to secure world. A entry function is a function in secure world, which is marked as being callable from non-secure world. A function can declared as entry function by using the function attribute

__attribute__((cmse_nonsecure_entry))

on the functions its declaration.


In chapter 4.4 we already introduced the NSC region, which is important when calling a S function from NS world. Another important part of transitions from NS to S is the SG instruction, which marks a branch target for NS code calling S code. The SG instruction sets the security level to S and banks registers (see 4.4.1). It also sets bit[0] of LR register to 0, which indicates that the return will cause a transition back from S to NS (see chapter 4.6.2). The SG instruction must be placed into NSC attributed memory.

For a successful secure function call, some requirements must be met:

  • The first instruction of the transition must be a SG instruction.
  • The processor must be in NS state, when SG is executed.
  • SG instructions are only allowed in the NSC region.

When a developer adds the csme_nonsecur_entry to a function, the compiler automatically generates a secure gateway veneers (SG veneers), for that function. This SG veneer contains the SG instruction following a branch to the function in secure world. The veneers are meant to be placed into the NSC region by the linker.

More on Secure function calls:

  • Chapter 5.4: CMSIS: Non-Secure Callable segment
  • Chapter 6.2: AAPCS: Subroutine Call
  • Chapter 4.4.1: Banked Registers
  • Chapter 7.3: Details: Secure function call
  • Chapter 4.6.1: Overview: Secure function call

A typical SG veneer would look like:

secure_function:
    SG
    B.W __acle_se_secure_function

The SG veneer is called from NS world, so the core is in non-secure state. Using the SG instruction the core transitions to S world and then branches off to the actual implementation of the secure function (__acle_se_secure_function). The SG veneers are called the same way as other functions by non-secure code, using BL:

nonsecure_code:
    BL secure_function

The secure world function returns using BXNS LR. The SG instruction had set bit[0] of LR to 0, indicating a transition back to non-secure world:

__acle_se_secure_function:
    doing secret things in secure world
    ...
    BXNS LR

A graphical summary of the secure function call flow:

secure function call

Figure 4.3: secure function call

The compiler places the secure gateway veneers in a NSC region with a specific name and optionally an additional veneer in NS region, to reach the SG veneer (long-branch veneer, see 2.10).

More on Veneers:

  • Chapter 2.10: Veneers
  • Chapter 4.6.1: Overview: Secure function call

More on Secure function calls:

  • Chapter 5.4: CMSIS: Non-Secure Callable segment
  • Chapter 6.2: AAPCS: Subroutine Call
  • Chapter 4.4.1: Banked Registers
  • Chapter 7.3: Details: Secure function call
  • Chapter 4.6.1: Overview: Secure function call

To return from secure world the compiler uses the instruction BXNS Rm - Branch and Exchange NS - which causes a branch to an address specified by a register Rm. If bit[0] (LSB) of Rm is 0 a switch from S to NS is done. The SG instruction has set LR[0]=0, so a the transition to NS is triggered by BXNS LR.

4.6.2 Non-secure function call

Terms explained in this chapter:

  • non-secure function call

The following chapter gives a high-level overview on non-secure function calls. We are going to examine the details of non-secure function calls, how the compiler and linker handle them and how the transition happens in great detail through the book. If you want to dive deeper into the topic at hand, use the “More on..” boxes at the end of the chapter!

A non-secure function call is a function call from S to NS world. To declare a function in NS as S callable, the function attribute: __attribute__((cmse_nonsecure_call)) has to be used.

A call to a NS function would look like:


typedef void (*ns_funcptr) (void) __attribute__((cmse_nonsecure_call));

void __attribute__((cmse_nonsecure_entry)) myEntryToSecureWorld(ns_funcptr callback){
    callback();
}

myEntryToSecureWorld() is a function in secure world. A callback is passed as parameter callback, which is from type ns_funcptr. ns_funcptr is a type definition, which defines a function pointer returning void, having no parameters (void). The type adds the necessary attribute cmse_nonsecure_call to the function pointer. This prompts the compiler to include code for a transition to NS, which saves all registers on secure stack - including the return address - and cleaning the registers afterwards. The branch to NS is done by calling BLXNS Rm. Again, to transition to NS, bit[0] of the target register has to be cleared. BLXNS then sets LR to a special value FNC_RETURN (0xFEFFFFFF).

When callback finishes executing in NS world it will use BX LR to return to myEntryToSecureWorld(). When BX detects FNC_RETURN in LR, a transition to S is done by popping all saved registers including the saved return address, from secure stack.

non-secure function calls with instructions showed

Figure 4.4: non-secure function calls with instructions showed

More on Nonsecure function calls:

  • Chapter 6.2: AAPCS: Subroutine Call
  • Chapter 4.4.1: Banked Registers
  • Chapter 4.6.2: Overview: Non-Secure function call
  • Chapter 7.4: Details: Non-secure function call

4.7 System Design: Security Gates and System Security Controllers

Terms explained in this chapter:

  • transaction
  • bus master
  • bus slave
  • security gate
  • TrustZone-aware
  • system security controller
  • Security Gate

Until now the focus was on the security attribution done by IDAU and SAU. But they are only two components of a secure a system. On the STM32L5, which is a Cortex-M33 with TrustZone, the SAU and IDAU for example are only relevant for the processing unit (the Cortex-M33): IDAU and SAU decide whether a secure / non-secure transaction from the core is allowed or denied based on the security attribution of the target address.

In a complex system like the STM32L5 all components of the system are connected on a bus. Additional to the core, there might be other components which could issue transactions to peripherals, flash and SRAM. Securing those is especially important if they are programmable. In the STM32L5 there are two DMA controllers, which are masters on the bus, meaning they can issue transactions and they are programmable. So you might have secured your transactions issued by the core through SAU/IDAU, but having a programmable master on the bus, which could issue transactions completely unaffected by SAU/IDAU is obviously a security gap.

The solution is to have the slaves on the bus also secured by adding an additional security layer in front on them: Either slaves are TrustZone-aware by themselves or they secured by a security gate in front of them. There are different types of security gates (AHB/APD firewalls):

  • block-based gate
  • watermark-based gate
  • select-based gate
  • security wrapper

These security gates are controlled by a system security controller [6]. Figure [4.5 shows these components used in an example system.

System Security example scheme

Figure 4.5: System Security example scheme

Masters and transactions are terms used in the AHB bus defined by Arm. In a future update of the book.

4.7.1 System Security Controller

The system security controller is a component accessible through the bus by secure transactions and holds registers to configure the security aspects of the system and might also have functions to collect and consolidate violations of security (e.g. an illegal transaction) from the gates to issue a secure interrupt to the PE.

4.7.2 Security wrapper

Security wrappers are put in front of legacy or non-security aware masters to wrap their transactions into secure / non-secure transactions.

4.7.3 Block-based Gate

Using a block-based gate flash or RAM can be divided into multiple, alternating blocks of secure and non-secure regions. A block-based gate filters transactions based on these regions.

4.7.4 Watermark-based Gate

A watermark-based gate in contrast to the block-based gate splits flash or RAM into two regions divided by a watermark address, one region being secure and the other non-secure.

4.7.5 Select-based Gate

An address region in the memory map might contain multiple slaves. One slave might be assigned to secure or non-secure world, so there is a need to have a select-based gate which filters transactions based on the slaves address and the assigned world.

4.7.6 Lite-IDAU

The Lite-IDAU inform TrustZone-aware masters about the security attribution of an address. As mentioned in chapter 4.5 the full IDAU signals the security attribution of an address, including NSC, NS and S to the PE. The NSC regions are not relevant for other masters, so a Lite IDAU does not signal these back to these masters.


More on Flash Protections:

  • Chapter 4.7: System Design: Security Gates and System Security Controllers
  • Chapter 10.2.5.4: STM32L5: Flash
  • Chapter 10.1.5: STM32F103: Read out and write protections

More on SRAM Protections:

  • Chapter 4.7: System Design: Security Gates and System Security Controllers
  • Chapter 10.2.5.3: STM32L5: SRAM

4.7.7 System Security in STM32L5

The system showed in figure 4.6 is a STM32L5 system on a chip. It basically uses all components described above.

System Security in STM32L5

Figure 4.6: System Security in STM32L5

  • The TZSC and the TZIC represent the system security controller. The TZIC collects security violations and may issue a security interrupt to the NVIC.
  • The TZSC (TrustZone Security Controller) holds the registers to configure the security gates
  • Other peripherals like flash are TrustZone-aware

This is a sneak peek on the STM32L5 chapter, which will be included in the next release of this book.

References

[5]
ST Microelectronics, “Reference Manual.” 2020, [Online]. Available: https://www.st.com/en/microcontrollers-microprocessors/stm32l5-series.html.
[6]
Arm Ltd, “System Design with ARMv8-M.” 2016, [Online]. Available: https://developer.arm.com/docs/100767/0100/system-design-for-armv8m.