Javed Absar via llvm-dev
2019-Mar-27 10:58 UTC
[llvm-dev] [RFC][ARM][CMSE] ARMv8-M Security Extensions - upstreaming clang/llvm support
Hi all:
I will soon be up-streaming our compiler support for Armv8-M Security
Extensions. This email is to inform the community so that interested reviewers
are aware when the patches go on Phabricator; and please do let me know if you
want to be added as reviewer.
More details about the architecture can be obtained from [1]. The requirements
on Development Tools is specified in [2]. Below I will mostly focus on the
points necessary to understand llvm and clang implementation support patches.
Armv8-M Security Extensions is in some context known as CMSE, and in the rest of
this description I will refer to it therefore simply as CMSE.
1. General Description
================CMSE defines a system-wide division of physical memory into
secure (S) regions and non-secure (NS) regions and two system-wide security
states (secure and non-secure states) that are enforced by hardware. There is a
direct relation between the memory regions and the security states:
• Code executed from a non-secure region (non-secure code) is executed in
non-secure state and can only access memory in non-secure regions.
• Code executed from a secure region (secure code) is executed in secure state
and can access memory in both secure and non-secure regions.
Attempts to access secure regions from non-secure code or a mismatch between the
code that is executed and the security state of the system results in a
SecureFault. Security state changes is performed through function calls and
returns. A function in secure code that can be called from the non-secure state
through its secure gateway is called an entry function. A function call from
secure state to the non-secure state is called a non-secure function call.
CMSE works as follow: Systems boot in secure state and then can change security
states using branches. Transitions from secure (S) to non-secure (NS) state can
be initiated by software through the use of the BXNS and BLXNS instructions that
have the Least Significant Bit (LSB) of the target address unset. The M profile
architecture does not support the A32 instruction set. This allows the LSB of an
address to denote the security state.
Transitions from non-secure (NS) to secure (S) state can be initiated by
software in two ways:
• A branch to a secure gateway.
• A branch to the reserved value FNC_RETURN.
When branching to a secure gateway from non-secure state, the SG instruction
switches state to the secure state and clears the LSB of the return address in
link register LR. On the other hand, a branch to the reserved value FNC_RETURN
causes the hardware to switch to secure state, read an address from the top of
the secure stack, and branch to that address. The reserved value FNC_RETURN is
written to LR when executing the BLXNS instruction. Security state transitions
can be caused by hardware through the handling of interrupts. Those transitions
are transparent to software.
Please note that the CMSE support is only for compiling code that runs in the
secure state. Non-secure code, which can nonetheless call secure code, does not
need any special CMSE compiler support for its compilation.
2. Example (Entry Functions)
===========================A function in secure code that can be called from the
non-secure state through its secure gateway is called an entry function. The
example below shows how entry function can be created by the CMSE enabled
compiler. The interface visible to non-secure code is defined in the header file
myinterface.h as follows:
int entry1(int x);
The non-secure code does not have to do anything special. In fact, it does not
even need to know that it is calling a secure entry function. The implementation
of this interface is given by the following secure C code:
#include <arm_cmse.h>
int func1(int x) { return x; }
int __attribute__((cmse_nonsecure_entry)) entry1(int x) { return func1(x) ; }
The secure code uses the attribute 'cmse_nonsecure_entry' which signals
to the compiler that this is an entry function. A compiler compiling an entry
function must do either of the following:
+ Generate code to read arguments from and write results to the non-secure
stack.
+ Constrain the number of parameters to the entry function, their types, and
the type of the return value, to avoid using the non-secure stack. An entry
function that would break the constraint must be diagnosed.
Our implementation currently chooses the latter option.
An entry function must use the BXNS instruction to return to its non-secure
caller. This instruction switches to non-secure state if the target address has
its LSB unset. The LSB of the return address in LR is automatically cleared by
the SG instruction when it switches the state from non-secure to secure.
The entry function does not start with an SG instruction but has two symbols
labelling its start as show below:
__acle_se_entry1:
entry1:
...
BL func1
...
BXNS lr
This indicates an entry function to the linker. When the relocatable file
corresponding to this assembly code is linked into an executable file, the
linker creates the following veneers in a section containing only entry veneers:
entry1:
SG
B.W __acle_se_entry1
Now, to prevent information leakage when an entry function returns, the
registers that contain secret information must be cleared . The code sequence
directly preceding the BXNS instruction that transitions to non-secure code
must:
+ Clear all caller-saved registers except registers that hold the result
value and the return address of the entry function.
+ Clear all registers and flags that have undefined values at the return of a
procedure, according to [AAPCS]
+ Restore all callee-saved registers as mandated by [AAPCS].
3. Example (Non-secure Call)
=====================A call to a function that switches state from secure to
non-secure is called a non-secure function call. Nonsecure function call can
only happen via function pointers.
A non-secure function type must be declared using the function attribute
__attribute__((cmse_nonsecure_call)). The function call through a pointer with a
non-secure function type as its base type switches the state of the system to
the non-secure state. To create a function call that switches to the non-secure
state, the compiler must emit code that clears the LSB of the function address
and branches using the BLXNS instruction. Below is a simple non-secure call
example:
#include <arm_cmse.h>
int __attribute__((cmse_nonsecure_call)) (*foo)(int);
int bar(int a) {
return foo(a) + 1;
}
All registers that contain secret information must be cleared to prevent
information leakage when performing a non-secure function call. Registers that
contain values that are used after the non-secure function call must be restored
after the call returns. Secure code cannot depend on the non-secure state to
restore these registers.
The code sequence directly preceding the BLXNS instruction that transitions to
non-secure code must:
+ Save all callee- and caller-saved registers by copying them to secure
memory.
+ Clear all callee- and caller-saved registers except the LR, and registers
that hold arguments of the call.
+ Clear all registers and flags that have undefined values at the entry to a
procedure according to the [AAPCS]
When the non-secure function call returns, caller- and callee-saved registers
saved before the call must be restored.
4. Checking Privileges
================To allow software to determine the security attribute of a
memory location, the test target (TT) instruction is used. It queries the
security state and access permissions of a memory location. It takes a memory
address and returns the configurations at that address. A number of support
functions such as cmse_check_address_range are provided in header file
arm_cmse.h. They rely on intrinsics which ultimately translate to variations of
TT instruction. More details of the instructions can be found at [1,2].
5. Our Implementation Overview
=======================Our implementation is divided into series of patches
that incrementally implement CMSE. Cx refers to clang patch and Lx llvm.
C1 : [ARM][CMSE] Add commandline option ‘-mcmse’ to enable CMSE and feature
macro
Defines __ARM_FEATURE_CMSE to 1 for v8-M targets and introduces -mcmse
option which for v8-M targets sets __ARM_FEATURE_CMSE to 3.
C2: [ARM][CMSE] Add CMSE intrinsic functions for TT instructions.
Provides cmse intrinsics support and introduces arm_cmse.h header file.
This patch depends on L1.
C3: [ARM][CMSE] Add CMSE attributes
Adds CMSE attributes cmse_nonsecure_call and cmse_nonsecure_entry and
their Semantic checking etc.
L1: [ARM][CMSE] Add CMSE intrinsic
Defines arm_cmse_xxx intrinsics. C1 depends on it.
L2: [ARM][CMSE] Add support for cmse attributes
Adds support for CMSE attributes cmse_nonsecure_call and
cmse_nonsecure_entry
L3: [ARM][CMSE] Codegen TT instruction and tests
L4 : [ARM][CMSE] Generate BXNS, BLXNS instruction and clear registers etc.
References:
[1] TrustZone technology for Armv8-M architecture:
https://developer.arm.com/architectures/cpu-architecture/m-profile/docs/100690/0200
[2] Armv8-M Security Extensions : Requirement on Development Tools:
https://developer.arm.com/architectures/cpu-architecture/m-profile/docs/ecm0359818/latest/armv8-m-security-extensions-requirements-on-development-tools-engineering-specification
best regards.
Javed
IMPORTANT NOTICE: The contents of this email and any attachments are
confidential and may also be privileged. If you are not the intended recipient,
please notify the sender immediately and do not disclose the contents to any
other person, use it for any purpose, or store or copy the information in any
medium. Thank you.
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20190327/66b0027b/attachment.html>