You are on page 1of 11

You have 2 free member-only stories left this month.

Sign up for Medium and get an extra one

Demystifying ARM TrustZone for


Microcontrollers (and a Note on Rust Support)
Introduction:

Nihal Pasham Follow


Sep 4, 2020 · 7 min read

TrustZone is different from that of a separate physical security co-processor (like a TPM
or a secure element) with a pre-defined set of features. You can think of it as a
virtualization technology for ARM CPUs i.e. it virtualizes a physical ARM CPU core — a
TrustZone enabled ARMv8 core can exist in one of 2 states Secure OR Non-Secure.
This, in turn, allows us to partition all system HW and SW resources so that they exist in
1 of the 2 worlds.
Execution view of a TrustZone enabled ARMv8-M processor core.

TrustZone for Armv8-M has been designed for ARM microcontrollers (Cortex-M). At a
high level, this variant of TrustZone is similar to the variant in Arm Cortex-A processors
i.e.

1. In both cases, secure and non-secure code runs on the same physical processor core.

2. Execution happens in a time sliced manner (Secure <-> Normal) with non-secure
software blocked from accessing secure resources directly.

3. But there are differences between the 2 processor families

4. TrustZone in Cortex-M has been optimized for faster context switching and low
power, keeping in mind real-time processing requirements of microcontrollers. To
achieve this, Cortex-M excludes the monitor mode (of Cortex-A) and the need for
any secure monitor software, reducing world switch latency. For bridging software
between both worlds, TrustZone for Cortex-M instead relies on a few secure function
entry points. Access to/from secure function entry points is controlled via a set of
special instructions: SG, BXNS, BLXNS

TrustZone-M processor specific instructions for context switching

** TrustZone for Cortex-A processors specifies a separate processor mode called monitor
mode for running a ‘secure monitor handler’ (a piece of software running in the secure
world that mediates all access between worlds) as the sole entry point.

How does it work?


After a power on or reset, an Armv8-M system begins executing code in the `secure
state`. This usually involves secure booting of the system along with some level of
system initialization. In a TrustZone enabled system/MCU, the system needs to perform
additional initialization — a few configuration routines are used to divide the system’s
entire memory-map into non-overlapping secure, non-secure and non-secure-callable
regions. The result is a ‘security attribution map’ (for the entire system).
Configuration of `memory security attributes` is done via 2 HW blocks called `security
attribution unit` (SAU) and/or ‘implementation defined attribution unit` (IDAU).

1. Implementation Defined Attribution Unit (IDAU), which is a fixed hardware unit


external to the processor core that provides a fixed security status of the memory
map as defined by the manufacturer. (i.e. an immutable background attribution
map implemented by the vendor in hardware for their specific chip.)

2. Secure Attribution Unit (SAU), which is a programmable unit integrated in the


processor core used to define the security status of up to eight memory regions. Note
— SAU’s registers can be set to configure non-secure memory, peripheral and
interrupt access.

Key-point to remember: Security is defined by address i.e. memory security attributes are
really what define security states of the processor.

Code executing in ‘Non-Secure’ state cannot access the secure memory map. But secure code can access both
secure and non-secure memory. As some peripherals are duplicated (MPU, SCB, SysTick), HW uses an aliased secure
memory map to shift them by an offset (say 0x20000) when executing in the secure state.

After assigning ‘security attributes’ to system memory, every memory access by the
processor, whether it’s a memory read, write or execute is tested for its `memory
security attributes` (i.e. is it a secure or non-secure address). SAU and IDAU work
together to enforce memory access restrictions at runtime. Note — While the IDAU
and SAU directly enforce secure and non-secure access restrictions, they work with
secure and non-secure memory protection units (MPUs) to determine the access rights
associated with the target resource.

SAU + IDAU + MPU together enforce privilege and security levels.

The IDAU, SAU, and MPU features of these processors provide a flexible foundation for
protecting runtime execution of both system software and applications, but these
capabilities are limited to the processor itself.

1. In order to carry `the secure and privilege capabilities` over to other memory
systems and interfaces, we use logic present in system’s bus (AMBA AHB 5/APB4)
fabric i.e. the privilege attribute (HPRIV) and secure attribute (HNONSEC) are
carried across the internal Advanced High-performance Bus (AHB) matrix to reach
memory protection checkers (MPCs), peripheral protection checkers (PPCs), and
master security wrappers (MSWs) for other bus masters.

2. In other words, the core’s security state information propagates via hardware logic
present in the TrustZone-enabled AMBA AHB5 / APB4 bus fabric (an extra signal
(HNONSEC[1] = 0) on the AHB bus indicates a secure transaction and vice versa).

3. This allows extending security to memories and peripherals through bus filters also
known as TrustZone-aware peripherals which are directly connected to AHB —
MPCs, PPCs, AHB/APB bridge (used as secure gate to block or propagate
secure/non-secure transaction towards APB agents).

4. Ensuring that no secure world resources can be accessed by the non-secure world
components, enabling a strong security perimeter to be built between the 2.

Simplified View of TrustZone-M

Developer Workflow:
Every TrustZone implementation will have 2 separate projects
1. One for the secure world and

2. The other for the non-secure world

After power on or reset, code from the secure project runs first i.e. the processor core
is executing in the ‘secure state’.

While in this state, configuration routines in the secure code project (either hand
written or generated via built-in IDE tools) configure regions of memory to either be
non-secure or non-secure callable. No need to explicitly attribute a ‘secure’ tag to a
region of memory. Everything’s secure by default.

As secure and non-secure projects are (pretty much) independent of each other, we
must provide a way for code in one project to call code in the other. 2 compiler
attributes are provided to achieve this -

(cmse_nonsecure_ entry): Functions in the secure world are decorated with this
attribute to indicate that it is an entry point for non-secure calls.

(cmse_ nonsecure_call): Functions or rather function pointers in the secure world are
decorated with this attribute to indicate that the secure world wants to call into the non-
secure world.
Linking the secure and non-secure project:

Any function (in the secure world) decorated with the above compiler attributes will
be exported to an object file upon building the secure project. (Usually the object file
is named PROJECTNAME_CMSE_lib.o)

The non-secure project will use this object file and a `veneer_table.h` header file in
its build process

In summary, we’ll need the following additions (HW +SW) to fully support
TrustZone-M:
1. 2 new HW blocks or on-chip peripherals called memory attribution units — SAU
and IDAU (optional)

2. TrustZone-aware peripherals (or controllers) to extend security to memories,


peripherals and other bus masters.

3. System bus AHB5/ABP4. The AHB protocol up to AMBA4 does not support security
attribute (HNONSEC) signal.
4. 3 new instructions — SG, BLXNS, BXNS and 2 Interrupt vector tables

Additionally, there are duplicated CPU peripherals (one for each world via register
banking):

1. 2 Memory Protection Units

2. 2 System Control Blocks

3. 2 SysTick’s: Interrupts are routed to their respective sides.

A note on Interrupt Handling:


The Nested Vectored Interrupt Controller (NVIC) is also extended for security as state
transitions can also happen due to exceptions and interrupts.

1. 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 world. There are no restrictions regarding whether a Non-secure or
Secure interrupt can take place when the processor is running Non-secure or Secure
code.

2. If the arriving exception or interrupt has the same state as the current processor
state, then the exception sequence is similar to the previous M-series processors.

3. The main difference occurs when a non-secure interrupt takes place and is handled
by the processor during the execution of secure code. In this case, the processor
automatically pushes all secure information onto the secure stack and erases the
contents from the register banks — this mechanism avoids any leakage of
information.

**It is possible to deprioritize non-secure interrupts by setting the PRIS bit field of the
Application Interrupt and Reset Control Register (AIRCR) or even avoid handling them
while the secure software is running (through the PRIMASK_NS register).

Rust Support for TrustZone-M:


1. For now, TrustZone-M is fully supported by a couple of semiconductor vendors
(NXP’s LPCS55S69, Microchip’s SAML11, Nordic’s nRF9160, ST’s STM32L5) but all
of them (only) offer ‘C’ toolchains for TZ related development.
2. In order to better realize the value that TrustZone has to offer, we could combine
TrustZone’s HW-based security mechanisms and Rust’s memory safety guarantees to
create more secure runtime environments (or Trusted Execution Environments). As
an example, think of a scenario where we could host ‘Rusty drivers for secure
elements or TPMs’ in the secure world. In theory, we could write memory-safe
drivers for security-sensitive applications (assuming no unsafe code is used or formal
proofs for the unsafe part of your code exist.)

3. Rust’s open-source community is working on adding support for the 2 compiler


attributes to the Rust compiler and support for new hardware peripherals via
additions to the ‘cortex-m’ crate. You can find the first pull request for
(cmse_nonsecure_entry) here — https://github.com/rust-lang/rust/pull/75810.

Sign up for Top 10 Stories


By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories —
delivered straight into your inbox, once a week. Take a look.

Your email

Get this newsletter

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information
about our privacy practices.

Trust Zone Processors Hardware Microcontrollers Rust

About Write Help Legal

Get the Medium app

You might also like