How to get a handle on TrustZone for ARMv8-M software development
ARM TrustZone technology is a system-wide approach to security for system-on-chip (SoC) designs. It makes it possible to design in security, from the smallest microcontrollers, with TrustZone for Cortex-M processors, to high performance applications processors, with TrustZone technology for Cortex-A processors. It is hardware-based security built into the heart of CPUs and systems and used by semiconductor chip designers who want to provide security to devices, such as root of trust. TrustZone technology is available on any ARM Cortex-A based system, and now with the Cortex-M23 and Cortex-M33 processors, it is also available on the latest Cortex-M based systems (see Thomas Ensergueix’s blog on Cortex-M23 and Cortex-M33 – Security foundation for billions of devices).
[Figure 1 | TrustZone security enables a controlled separation of shared resources]
At the heart of the TrustZone approach is the concept of secure and non-secure worlds that are hardware-separated from each other. Within the processor, software either resides in the secure world or the non-secure world; a switch between these two worlds is accomplished via software in Cortex-A processors (referred to as the secure monitor) and by hardware (core logic) in Cortex-M processors. This concept of secure (trusted) and non-secure (non-trusted) worlds extends beyond the CPU. It also covers memories, on-chip bus systems, interrupts, peripheral interfaces and software within an SoC.
Software modules within a system secured by TrustZone for ARMv8-M
For secure solutions, security experts recommend reducing the trusted code to a minimum to reduce the attack surface and simplify the security audit. That is, only put code in trusted space after it has gone through exhaustive inspection and tests against attacks. This trusted code needs to be tested that it works per specification and it needs to be tested to make sure it does not work as not expected. If we apply this recommendation, then we imagine a code distribution, as shown in Figure 1 above.
The secure boot, device resource manager and the security API would reside in the privileged trusted space. This is the box that has access control to all resources and has the highest priority level. Trusted libraries reside in the unprivileged, but trusted, space. These libraries are under control of the resource manager and have further mutual protection using the memory protection unit. On the untrusted side, we see the same code distribution as any Cortex-M today. The OS in the privileged box and the application in the user space. This is one of the powerful benefits of TrustZone for ARMv8-M, the user side remains the same while the system gains security. Efficient and user friendly security – this is the promise of TrustZone for ARMv8-M.
The bulk of the work is in the camp of the developer of the code on the trusted side. A security audit of the code is recommended which is why a smaller code is recommended.
By the way, for developers, new opportunities will open up. For example, there will now spring up a new market for ‘health monitors’ for IoT nodes, were a small piece of code in the trusted space would watch the behavior of the system to detect anomalies and hack attempts to take corrective action. If we consider that the IoT node will remain in the field for multiple years without human intervention, then this is probably the most important piece of code that is required to ensure a long operational life.
For those interested in what makes TrustZone tick, I present to you the top three main new concepts:
Concept 1: Security defined by address
The processor uses a newly-introduced security attribution unit to check the security attribute of the address. A system-level interface may override that attribution based on the overall SoC design. After the state is selected, the address also passes through the appropriate memory protection unit, if present in the system.
Concept 2: Additional states
The ARMv7-M and ARMv6-M architecture defined two execution modes: handler mode and thread mode. Handler mode is privileged and may access all resources of the SoC, while thread mode could be either privileged or unprivileged. With the TrustZone security extension, the processor modes are mirrored to form Secure state and Non-secure state, each has a handler mode and a thread mode. The security states and processor modes are orthogonal, resulting in four combinations of states and modes. When running software in secure memories, the processor is automatically set to secure state. Likewise, when the processor is executing software in non-secure memories, the processor is automatically set to non-secure state. This design removes the need for any secure monitor software to manage the state switch which reduces the memory foot print and power consumption as detailed above.
Concept 3: Cross domain calls
ARMv8-M was designed for the Cortex-M profile with deterministic real time operations. Any function in any state may call any other function in the other state directly, as long as certain rules are respected based on the predefined security state entry points. Also, as expected, each state has a distinct set of stacks and stack pointers to those stacks to protect assets on the Secure side. The function call overhead is dramatically reduced since there is no need for an API layer to manage the calls. Given predefined entry points, the call proceeds directly to the called function.
Now that you are fully familiar with the workings of TrustZone for ARMv8-M we dive into the mechanics of writing software to take advantage of it using just three helpful tips.
Tip 1: Utilize new ARM C Language Extension features
TrustZone for ARMv8-M introduced a few new instructions to support the security state switching. Instead of creating assembly wrappers to generate those instructions, software developers should utilize new compiler features defined in ARM C Language Extensions (ACLE) to allow software tools to understand the security usage of the functions and generate the best code required. The ACLE features are implemented by multiple compiler vendors, and hence, the code is portable.
For example, when creating a secure API that is callable from the non-secure state, a new function attribute called “cmse_nonsecure_entry” should be used in declaring the function. At the end of the function, call in the secure state – the registers inside the processor may still contain secret information. With the correct function attribute, the compiler can automatically insert code to clear registers in R0-R3, R12 and Application Program Status Register (APSR) that might contain secret information, except if the registers are used to return the result to non-secure software. Register R4 to R11 are handled differently, as their contents should be unchanged at function boundaries. If their values are changed in the middle of the function the values should be restored to original values before returns to Non-secure caller.
Tip 2: Validate non-trusted pointers
There will be cases where the non-secure code may provide incorrect pointers by design to try to gain access to secure memory. To counter that possibility, a new instruction has been introduced in ARMv8-M, the Test Target (TT) instruction. The TT instructions return the security attributes of an address, so Secure software can determine if a pointer is pointing to a secure or non-secure address.
To make the checking of pointers more efficient, there is an associated region number for each memory region defined by the security configuration. This region number can be utilized by software to determine if a contiguous range of memory has similar security attributes.
The TT instruction returns the security attributes and region number (as well as MPU region number) from an address value. By using a TT instruction on the start and end addresses of the memory range, and identifying that both reside in the same region number, software can quickly determine that the memory range (e.g. data array or data structure) is located entirely in non-secure space.
[Figure 2 | Checking pointers for valid region boundaries]
Using this mechanism, secure code servicing APIs in to the secure side can determine if the memory referenced by a pointer from non-secure software has the appropriate security attribute for the API. This prevents non-secure software from using APIs in secure software to read out or corrupt secure information.
Tip 3: Design for asynchronous non-secure memory modifications
Non-secure interrupt service routines could change non-secure data that is being processed by secure software. Thus, input data that has already been validated by the secure API can be changed by a non-secure ISR after the validation step. One way to avoid that situation is to make a local copy of that input data in secure memory and use the secure copy for processing (including validation of the input data) and avoid the read to non-secure memory. In the cases where such copying is not desired (e.g. when dealing with large amount of data in a specific memory region), then the alternative is to program the security attribution unit to make that memory region Secure.
TrustZone is designed to be an efficient user friendly technology for all developers to create secure embedded solutions. It is up to developers of software for the secure side to make sure that whole system is secure and that no secure data leaks to the non-secure side. To accomplish this, three key TrustZone concepts were outlined along with three key actions that secure software developers can do to help create secure systems. The ACLE techniques to protect data in registers of called functions. The TT instruction for validating pointers and finally, that developers need to keep in mind that the non-secure side may change data by interrupting the secure side.