You copied the Doc URL to your clipboard.

Secure software guidelines

To prevent Secure code and data from being accessed from Non-secure state, Secure code must meet several requirements. The responsibility for meeting these security requirements is shared between hardware, toolchain, and software developer.

There are requirements to use special instructions (BXNS and BLXNS) to branch to Non-secure code and the requirement to preserve and protect Secure register values before calling Secure functions.

CMSE is an extension to the C language that can be implemented by tool vendors to provide a standard way to generate this code.

Information leakage

Information leakage from the Secure state to the Non-secure state can occur through parts of the system that are not banked between the security states. The unbanked registers that are accessible by software are:

  • General purpose registers except for the stack pointer (R0-R12, R14-R15).
  • Floating-point registers (S0-S31, D0-D15).
  • The N, Z, C, V, Q, and GE bits of the XPSR register.
  • The FPSCR register.

Secure code must clear secret information from unbanked registers before initiating a transition from Secure to Non-secure state.

Non-secure memory access

When Secure code has to access Non-secure memory using an address that is calculated by the Non-secure state, it cannot trust that the address lies in a Non-secure memory region. Furthermore, the Memory Protection Unit (MPU) is banked between the security states. Secure and Non-secure code might have different access rights to Non-secure memory.

Secure code that accesses Non-secure memory on behalf of the Non-secure state must only do so if the Non-secure state has permission to perform the same access itself.

The Secure code can use the TT instruction to check Non-secure memory permissions.

Take care when using Secure code to access Non-secure memory unless it does so on behalf of the Non-secure state. Data belonging to Secure code must reside in Secure memory.

Volatility of Non-secure memory

Non-secure memory can be changed asynchronously to the execution of Secure code. There are two possible causes:

  • Interrupts that are handled in Non-secure state can change Non-secure memory.
  • The debug interface can be used to change Non-secure memory.

There can be unexpected consequences when Secure code accesses Non-secure memory. For example:

  int array[N]
  void foo(int *p) {
      if (*p >= 0 && *p < N) {
          // Non-secure memory (*p) is changed at this point
          array[*p] = 0;
      }
  }

Secure code must treat Non-secure memory as volatile memory.

When the pointer p points to Non-secure memory, it is possible for its value to change after the memory accesses used to perform the array bounds check, but before the memory access used to index the array. Such an asynchronous change to Non-secure memory would render this array bounds check useless.

You can handle this as follows:

  int array[N]
  void foo(volatile int *p) {
      int i = *p;    
      if (i >= 0 && i < N) {
          array[i] = 0;
      }
  }

Inadvertent Secure gateway

An SG instruction can occur inadvertently. This can happen in the following cases:

  • Uninitialized memory.
  • General data in executable memory, for example jump tables.
  • A 32-bit wide instruction that contains the bit pattern 0b1110100101111111 in its first halfword that follows an SG instruction, for example two successive SG instructions.
  • A 32-bit wide instruction that contains the bit pattern 0b1110100101111111 in its last halfword that is followed by an SG instruction, for example an SG instruction that follows an LDR (immediate) instruction.

If an inadvertent SG instruction occurs in an NSC region, the result is an inadvertent Secure gateway.

Memory in an NSC region must not contain an inadvertent SG instruction.

The Secure gateway veneers limit the instructions that must be placed in NSC regions. If the NSC regions contain only these veneers, an inadvertent Secure gateway cannot occur.

Executable files

There are two different types of executable files, one for each security state. The Secure state executes Secure code from a Secure executable file. The Non-secure state executes Non-secure code from a Non-secure executable file. The Secure and Non-secure executable files are developed independently of each other.

A Non-secure executable is unaware of security states.

From the point of view of the Non-secure state, a call to a Secure gateway is a regular function call, as is the return from a Non-secure function call. You can develop Non-secure code with a toolchain that is not CMSE aware, that is, you do not require new tools when you are only building Non-secure code.

Developing a Secure executable file requires toolchain support whenever a function is called from, calls, or returns to Non-secure state and whenever memory is accessed through an address that is provided by the Non-secure state. The Secure code ABI is otherwise identical to the Non-secure code ABI.

The following figure shows the interaction between developers of Secure code, Non-secure code, and (optional) security agnostic library code:

libraries.png

The Secure gateway import library contains the addresses of the Secure gateways of the Secure code. This import library consists of or contains a relocatable file that defines symbols for all the Secure gateways. The Non-secure code links against this import library to use the functionality that is provided by the Secure code.

A relocatable file containing only copies of the (absolute) symbols of the Secure gateways in the Secure executable must be available to link Non-secure code against.

Linking against this import library is the only requirement on the toolchain that is used to develop the Non-secure code. This functionality is similar to calling ROM functions, and is expected to be available in existing toolchains.

Development tools

Development tools are expected to provide C and assembly language support for interacting between the security states. Code that is written in C++ must use the extern C linkage for any inter-state interaction.

Security state changes must be expressed through function calls and returns.

This use of the extern C linkage provides an interface that fits naturally with the C language.

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.

Calling Non-secure functions

Calling a Non-secure function from Secure code requires special code generation to be architecturally correct.

  • BLXNS must be used instead of BLX.
  • The LSB of the calling address must be zeroed.
  • Registers must be sanitized to prevent leaking of Secure data.
  • Non-secure functions are prototyped as normal in an interface header. For example:
secure_interface.h
int entry1(int x);
int entry2(int x);

Calling a Non-secure function using CMSE

  • Define a Non-secure function pointer using:

_attribute_((cmse_nonsecure_call))

  • Create a function pointer using:

cmse_nsfptr_create()

  • Validate the function pointer before calling using

cmse_is_nsfptr()

For example

Typedef void _attribute_((cmse_nonsecure_call)) nsfunc(void);
Nsfunc *FunctionPointer;
FunctionPointer = cmse_nsfptr_create((nsfunc *) (0x21000248u));
If (cmse_is_nsfptr(FunctionPointer))
FunctionPointer();
Was this page helpful? Yes No