TEE: Calling Secure code from Non-Secure region

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

TEE: Calling Secure code from Non-Secure region

TEE: Calling Secure code from Non-Secure region

 

A Digital Rights Management (DRM) use case

 

In the Creating Secure and Non-Secure projects tutorial, you have attempted to access a Secure (S) FlexComm0 (USART) peripheral from a Non-Secure (NS) code and failed with the SecureFault exception. This was expected, as TrustZone® for ARM®v8-M protects Secure resources from Non-Secure access.

 

Now imagine a use case where the platform manages (protects) access to all its peripherals and firmware logic and requires all 3rd party extensions, for example plugins or widgets, to be certified (approved) for use. The result of this approval process is some form of identification, a license key for instance, the untrusted code needs to supply to the trusted code to gain peripheral access and be able to communicate via USART. This kind of behavior can be achieved with the help of Non-Secure Callable (NSC) memory.

 

NSC is a type of Secure memory exclusively permitted to hold the Secure Gateway (SG) instruction that allows transition from NS to S state. When a Non-Secure program calls a function in the Secure side, the first instruction in the API must be an SG instruction. The Branch with exchange to Non-secure state (BXNS) will return execution to the Non-Secure side after the API call. While it’s possible for NSC memory to contain entire functions, its typical usage is holding tables of SG instructions with entry points, called veneers.

 

With GCC, ARMCC, ARMCLANG, IARCC, and LLVM compilers, you must specify the __attribute__((cmse_nonsecure_entry)) function attribute to declare an entry C/C++ function callable from NS state; SG and BXNS instructions are generated automatically. GNU linker puts the resulting code into the .gnu.sgstubs section aligned at 32 bytes.

 

With the solution outlined, you can reuse projects from the “TEE projects creation” tutorial to speed things up. To extend an existing project with NSC functions, the workflow is as follows:

  • Secure state
    1. Configure project and  SAU/IDAU
    2. Implement NSC functions
    3. Generate NSC library
  • Non-Secure state
    1. Configure project (import NSC library)
    2. Implement calls for Secure code

 

 

S project update

 

Start with the Secure project conveniently named “Secure”.

  1. Reserve some flash memory space for SG_veneer_table first. Navigate to Project > Properties > C/C ++ Build.
  2. Locate the MCU settings in SDK MCUs.
  3. Click Add Flash and add SG_veneer_table at 0x1000fe00, size 0x200 bytes.
  4. Click Apply.
nxa07264_0-1627904924736.png

 

  1. To tell the MCU Linker to generate the NSC import library, navigate to Project > Properties > C/C ++ Build > Settings > MCU Linker > TrustZone.
  2. Select the Enable generation of Secure Gateway Import Library checkbox.
  3. Click Apply and Close.
nxa07264_1-1627904924964.png

 

  1. Now setup the newly added flash memory region as NSC in the TEE tool. To switch to the TEE tool, click the Open MCUXpresso Configuration button in the Project Explorer, and select Open TEE.
nxa07264_2-1627904925055.png

 

  1. The TEE tool is capable of importing project-wide memory regions from multiple projects at once. To import memory regions, click the Import button in the User Memory Regions view.
nxa07264_3-1627904925107.png

 

  1. In the Select Project dialog, select both NonSecure and Secure projects and click OK.
nxa07264_4-1627904925144.png

 

  1. Quick-fix the reported error Issue: SAU+IDAU: The following security requirements are not satisfied: NS_CALLABLE by right-clicking the cell and selecting the quick-fix from the context menu.
nxa07264_5-1627904925233.png

 

  1. To save the changes, click Update Code.
nxa07264_6-1627904925352.png

 

  1. MCUXpresso IDE v11.3.0 and earlier versions contain a bug that messes up linker script and puts SG veneers into PROGRAM_FLASH instead of SG_veneer_table when you’re modifying an existing project.
  2. To work around the problem, navigate To Project > Properties > C/C ++ Build > Settings > MCU Linker > Managed Linker Script.
  3. Deselect the Manage linker script checkbox and click Apply and Close.
nxa07264_7-1627904925595.png

 

  1. Edit the script manually by opening the Debug/Secure_Debug.ld inside the IDE, find the .gnu.sgstubs section and direct it to SG_veneer_table instead of PROGRAM_FLASH.

 

nxa07264_0-1627905852833.png

 

 

 

NS project update

 

If you attempt to build the S project now, you’ll receive several linker errors telling you the NSC library could not be built as there are no NSC functions (yet) including the name of the said library: Secure_CMSE_lib.o. You’re going to use it to configure the Non-Secure project named “Non-Secure”.

  1. Navigate to Project > Properties > C/C ++ Build > Settings > MCU Linker > TrustZone.
  2. Click the Import button next to the Secure Gateway Import Library field, and select Workspace.
nxa07264_1-1627906072670.png

 

  1. In the Input Secure Gateway Import Library dialog, select Secure_CMSE_lib.o and click OK.
nxa07264_2-1627906104266.png

 

You will see “${workspace_loc:/Secure/Debug/Secure_CMSE_lib.o}” in the Secure Gateway Import Library field of the MCU Linker Setting dialog.

 

 

NSC API

 

The source/Secure.c file already contains the sendSecureByte function implementation you need to make accessible through the NSC space. What’s missing is a few lines of code for its NSC equivalent and function prototype.

  1. Create source/nsc_api.h and source/nsc_api.c files in the Secure project workspace. The following code needs go into the newly created header file.
#ifndef NSC_API_H_
#define NSC_API_H_

#include <stdint.h>

void sendSecureByteNSC(uint8_t data);

#endif /* NSC_API_H_ */
  1. Add the following code into the source file.
#include <arm_cmse.h>
#include "fsl_debug_console.h"

#include "nsc_api.h"
#include "comm.h"

void __attribute__((cmse_nonsecure_entry)) sendSecureByteNSC(uint8_t data) {
       if (cmse_nonsecure_caller()) {
             PRINTF("sendSecureByteNSC called from NS space\r\n");
       } else {
             PRINTF("sendSecureByteNSC called from S space\r\n");
       }
       sendSecureByte(data);
}
  1. To rebuild the Secure project and generate the NSC import library click the Build ‘Debug’ for project ‘Secure’ button in the Toolbar.
nxa07264_3-1627906545822.png

 

  1. Next in line is the Non-Secure project. The NSC library is already set to be imported. Copy Secure/source/nsc_api.h to the NonSecure/source folder.
  2. Call the sendSecureByteNSC function instead of USART_WriteByte from source/NonSecure.c.
#include "nsc_api.h"

                  USART_WriteByte((USART_Type *)FLEXCOMM0, 'n');

		sendSecureByteNSC('n');
  1. Rebuild the Non-Secure project and debug both.
  2. Select the Secure project in the workspace and select Debug either from the Quickstart panel or the project context menu.

Once the code is running, each press of the WAKEUP (S2) button on the LPCXpresso55S69 board will display the “sendSecureByteNSC called from NS space” message in the IDE console and send a single character via USART instead of failing with the SecureFault exception.

 

nxa07264_0-1627907393950.png

 

Attachments
No ratings
Version history
Last update:
‎09-19-2021 11:41 PM
Updated by: