[llvm-dev] [RFC][ARM][CMSE] ARMv8-M Security Extensions - upstreaming clang/llvm support

Javed Absar via llvm-dev llvm-dev at lists.llvm.org
Wed Mar 27 03:58:46 PDT 2019

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:
     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:
  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.


[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.
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>

More information about the llvm-dev mailing list