CMSE is an extension to the C language that can be implemented by tool vendors to provide toolchain support for Secure executable files that are written in the C language. Non-secure executable files do not require any additional toolchain support.
<arm_cmse.h> header must be included before using
CMSE support, except for using the
and 1 of feature macro
__ARM_FEATURE_CMSE are set if CMSE support for
Secure executable files is available.
Availability of CMSE implies availability of
A compiler might provide a switch to enable
support for creating CMSE Secure executable files. ARM recommends such a switch to be
Non-secure memory usage
Secure code must only use Secure memory except when communicating with the Non-secure state.
The security implications of accessing Non-secure memory through a pointer are the responsibility of the developer.
Arguments and return value
A caller from the Non-secure state is not aware it is calling an entry function. If it must use the stack to write arguments or read a result value that uses the Non-secure stack.
If a toolchain supports stack-based arguments, it must be aware of the volatile behavior of Non-secure memory and the requirements of using Non-secure memory.
In practice, a compiler might generate code that:
- Copies stack-based arguments from the Non-secure stack to the parameter on the Secure stack in the prologue of the entry function.
- Copies the stack-based return value from the Secure stack to the Non-secure stack in the epilogue.
A possible optimization would be to access the Non-secure stack directly for arguments that read at most once, but accessibility checks are still required.
The following figure shows the stack use of an entry function.
Return from an entry function
An entry function
must use the
BXNS instruction to return to its Non-secure
This instruction switches to Non-secure state if the target address has
its LSB unset. The LSB of the return address in the LR is automatically cleared by the
SG instruction when it switches the state from Non-secure to
NoteTo prevent information leakage when an entry function returns, the registers that contain secret information must be cleared.
The code sequence directly preceding the
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.
- Registers that do not contain secret information.
- Clear all registers and flags that have UNDEFINED values at the return of a procedure, according to the Procedure Call Standard for the ARM Architecture (AAPCS).
- Restore all callee-saved registers as required by the Procedure Call Standard for the ARM Architecture (AAPCS).
Floating-point registers can be cleared conditionally by checking the SFPA bit of the special-purpose CONTROL register.
A toolchain can provide the developer with the means to specify that some types of variables never hold secret information. For example, by setting the TS bit of FPCCR, The ARMv8-M Security Extension assumes that floating-point registers never hold secret information.
Because of these requirements, performing tail-calls from an entry function is difficult.
Security state of the caller
An entry function can be called from Secure or Non-secure state. Software must distinguish between these cases. To enable this, the ARMv8-M Security Extensions define the following intrinsic:
Returns non-zero if entry function is called from Non-secure state and zero otherwise.
Non-secure function call
A call to a function that switches state from Secure to Non-secure is called a Non-secure function call. A Non-secure function call must use function pointers. This is a consequence of separating Secure and Non-secure code into separate executable files.
A Non-secure function type must be declared
using the function attribute
A Non-secure function type must only be used as a base type of a pointer. This restriction disallows function definitions with this attribute and ensures that a Secure executable file only contains Secure function definitions.
Performing a call
function call through a pointer with a Non-secure function type as its base type must
switch to the Non-secure state. To create a function call that switches to the
Non-secure state, an implementation must emit code that clears the LSB of the function
address and branches using the
NoteA Non-secure function call to an entry function is possible. This call to an entry function behaves like any other Non-secure function call.
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 live caller-saved registers by copying them to Secure memory.
- Clear all callee- and caller-saved registers except:
- The LR.
- The registers that hold the arguments of the call.
- Registers that do not hold secret information.
- Clear all registers and flags that have UNDEFINED values at the entry to a procedure according to the AAPCS.
A toolchain could provide the developer with the means to specify that some types of variables never hold secret information.
When the Non-secure function call returns, caller and callee that are saved registers that are saved before the call must be restored.
An implementation need does not have to save and restore a register if its value is not live across the call. However, callee-saved registers are live across the call in almost all situations. These requirements specify behavior that is similar to a regular function call, except that:
- Callee-saved registers must be saved as if they are caller-saved registers.
- Registers that are not banked and potentially contain secret information must be cleared.
The floating-point registers can efficiently be saved and cleared using the
VLSTM instruction, and restored using
Arguments and return value
The callee of a Non-secure function call is called in Non-secure state. If stack use is required according to the AAPCS, the Non-secure state expects to find the arguments on the Non-secure stack and writes the return value to Non-secure memory.
The stack usage during a Non-secure function call is shown in the following figure:
Returns the value of p with its LSB cleared. The argument p can be any function pointer type.
Returns non-zero if p has LSB unset, zero otherwise. The argument p can be any function pointer type.
NoteThe exact type signatures of these intrinsics are implementation-defined because there is no type defined by the C programming language that can hold all function pointers. ARM recommends implementing these intrinsics as macros and recommends that the return type of
cmse_nsfptr_create()is identical to the type of its argument.
A Non-secure returning function must be
declared by using the attribute
__attribute__((cmse_nonsecure_return)) on a
A Non-secure returning function has a special epilogue, identical to that of an entry function.