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:
4.2 Execution Modes and Privilege Levels (with TrustZone)
More on Arm 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.
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:
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:
More on Security Attribution Unit:
More on Implementation Defined Attribution Unit:
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.
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:
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
((cmse_nonsecure_entry)) __attribute__
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 theNSC
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:
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:
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 Secure function calls:
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.
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.
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:
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.
- 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.