Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Arm Community blogs
Arm Community blogs
Tools, Software and IDEs blog Develop Software for the Cortex-M Security Extensions Using Arm DS and Arm GNU Toolchain
  • Blogs
  • Mentions
  • Sub-Groups
  • Tags
  • Jump...
  • Cancel
More blogs in Arm Community blogs
  • AI blog

  • Announcements

  • Architectures and Processors blog

  • Automotive blog

  • Embedded and Microcontrollers blog

  • Internet of Things (IoT) blog

  • Laptops and Desktops blog

  • Mobile, Graphics, and Gaming blog

  • Operating Systems blog

  • Servers and Cloud Computing blog

  • SoC Design and Simulation blog

  • Tools, Software and IDEs blog

Tags
  • Embedded Software
  • Arm Development Studio
  • TrustZone for Armv8-M
  • GNU Arm
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

Develop Software for the Cortex-M Security Extensions Using Arm DS and Arm GNU Toolchain

Sue Wu
Sue Wu
October 16, 2023
8 minute read time.

This post describes the steps to create a basic TrustZone example for the Armv8-M processor with Security Extension by using Arm DS and Arm GNU toolchain. It is assumed that you have a basic knowledge of the Armv8-M Security Extension and embedded-M software programming.

The Secure and Non-secure images make function calls across security boundary based on Armv8-M Security Extension. The steps to create a basic TrustZone example for the Armv8-M processor using Arm DS and Arm GNU toolchain are as follows:

  1. Configure system and memory.
  2. Create and build Secure project in Arm DS.
  3. Create and build Non-secure project in Arm DS.
  4. Run Secure and Non-secure images in Arm DS with FVP.

Prerequisites

This blog assumes that you have the following tools available on your machine:

  • Arm Development Studio (available in Arm DS). It provides support for creating and debugging Secure and Non-secure applications for Armv8-M based devices.
  • Arm GNU toolchain (available in Arm GNU Toolchain). It enables software developers to build software based on ARM embedded processors.
  • Fast Models Fixed Virtual Platforms (FVP) (installed with Arm DS). It is the platform to run the Secure and Non-secure software image.
  • CMSIS (available in GitHub repository). CMSIS pack is required for embedded-M software programming.

Configure system and memory

In the early stage of a project, you must define the overall system configuration, which includes memory, peripherals, and related interrupts in the security domain. The partitioning of the memory space into Secure and Non-secure is handled by the Security Attribution Unit (SAU) and Implementation Defined Attribution Units (IDAU).

Consider the Arm Microcontroller Prototyping System 2 (MPS2) Fixed Virtual Platform (FVP) model as an example. Part of memory configuration in the security domain is shown in the following table. The corresponding memory regions are used in the Secure and Non-secure software projects.

Memory Description
0x00200000-0x003FFFFF Non-secure SRAM
0x10000000-0x101FFFFF Secure SRAM
0x101FFC00-0x101FFFFF Secure Non-secure Callable (NSC) SRAM
0x20200000-0x203FFFFF Non-secure RAM
0x30000000-0x301FFFFF Secure SRAM

Create a Secure project in Arm DS

1. Getting started with Cortex-M using Arm DS, Arm GNU toolchain and CMSIS

Arm DS is the most comprehensive embedded C/C++ dedicated software development solution, supporting multicore debug for Cortex-A, Cortex-R, Cortex–M, and Neoverse Arm CPUs. See Getting started with Cortex-M using Arm DS and CMSIS for how to set up a Cortex-M project using Arm DS and CMSIS.

Arm DS supports to use a different compiler toolchain other than the one installed Arm DS, see the document register a compiler toolchain in Arm DS

For a Secure project, you must select Secure in Security Mode as shown in the following figure. Then, follow the Arm DS wizard to create the Secure project.

            select Secure in the Security mode to create Secure project

           

The Secure project is built with Cortex-M Security Extensions (CMSE) support. The project includes the <arm_cmse.h> header file and is compiled with the command-line build option -mcmse.

In Arm DS, you can add -mcmse by checking Secure Code from Project-> Properties->C/C++ Build-> Settings as shown in the following figure:

add -cmse support for Secure project in Arm DS

The following figure shows a Secure project structure:

       Secure project structure

2. Add Arm GNU toolchain linker script file

The linker script file is used in the linker stage, and this file:

  • Defines all the memory devices with address range and size information, based on the target hardware.
  • Describes how the sections in input files should be mapped into the output file.
  • Controls the memory layout of the output file.

In Arm GNU toolchain, the linker allocates the veneer code of Secure APIs in the .gnu.sgstubs section. You must place this section in the Secure Non-secure Callable (NSC) region in the GCC linker script .ld file. The following snippet shows you how to place veneer section in the NSC region. You should set __STACKSEAL_SIZE to 8 in the linker script to reserve the memory space for secure stack sealing.

__ROM_BASE_NSC =  0x101FFC00;    
__ROM_SIZE_NSC =  0x400; 

MEMORY
{
    FLASH_NSC (rx)  : ORIGIN = __ROM_BASE_NSC, LENGTH = __ROM_SIZE_NSC
}

.gnu.sgstubs :
{
    . = ALIGN(32);
    *(.gnu.sgstubs*)   
} > FLASH_NSC

3. Configure memory security attributes

In a CMSIS-based project, SAU configurations are done in the TZ_SAU_Setup() function. The TZ_SAU_Setup() function is called from the SystemInit() function in the reset handler. The partition_ARMv81MML.h file contains the TZ_SAU_Setup() function and related setting parameters.

The partition_ARMv81MML.h file in the Secure project defines Non-secure regions and Secure, NSC regions as follows:

//  Initialize SAU Region 0 Setup SAU Region 0 memory attributes
        #define SAU_INIT_REGION0    1
        #define SAU_INIT_START0   0x101FFC00    // Start address 
        #define SAU_INIT_END0     0x101FFFFF     // End address
        #define SAU_INIT_NSC0       1  

    //  Initialize SAU Region 1 Setup SAU Region 1 memory attributes
        #define SAU_INIT_REGION1    1
        #define SAU_INIT_START1   0x00200000   
        #define SAU_INIT_END1     0x003FFFFF  
        #define SAU_INIT_NSC1       0 

    //  Initialize SAU Region 2 Setup SAU Region 2 memory attributes
        #define SAU_INIT_REGION2    1
        #define SAU_INIT_START2   0x20200000
        #define SAU_INIT_END2     0x203FFFFF
        #define SAU_INIT_NSC2       0

   

The TZ_SAU_Setup() function uses these settings to configure SAU regions one by one. When SAU is enabled, the memory that is not covered by any enabled SAU region is Secure. The following code snippet shows you how to configure SAU regions.

#define SAU_INIT_REGION(n) \
    SAU->RNR  =  (n                                     & SAU_RNR_REGION_Msk); \
    SAU->RBAR =  (SAU_INIT_START##n                     & SAU_RBAR_BADDR_Msk); \
    SAU->RLAR =  (SAU_INIT_END##n                       & SAU_RLAR_LADDR_Msk) | \
                ((SAU_INIT_NSC##n << SAU_RLAR_NSC_Pos)  & SAU_RLAR_NSC_Msk)   | 1U

    __STATIC_INLINE void TZ_SAU_Setup (void)
    {
   
      #if defined (SAU_INIT_REGION0) && (SAU_INIT_REGION0 == 1U)
        SAU_INIT_REGION(0);
      #endif

      #if defined (SAU_INIT_REGION1) && (SAU_INIT_REGION1 == 1U)
      SAU_INIT_REGION(1);
      #endif

      #if defined (SAU_INIT_REGION2) && (SAU_INIT_REGION2 == 1U)
      SAU_INIT_REGION(2);
      #endif

      SAU->CTRL = ((SAU_INIT_CTRL_ENABLE << SAU_CTRL_ENABLE_Pos) & SAU_CTRL_ENABLE_Msk) |
                ((SAU_INIT_CTRL_ALLNS  << SAU_CTRL_ALLNS_Pos)  & SAU_CTRL_ALLNS_Msk)   ;
      ...
      
    }

4. Implement and export Secure APIs

The Interface.c file implements Secure APIs as follows:

The ns_callable_fn1 is a simple Secure API that is exported to the Non-secure world. For example:

 

int32_t __attribute__((cmse_nonsecure_entry)) ns_callable_fn1(int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4)
{
  int32_t ret = 0;
  ret  = arg1 + arg2 + arg3 + arg4;
  return ret;
}

5. Add code to launch a Non-secure image

After finishing system initialization, Secure software sets up the MSP_NS from the first item in the Non-secure vector table and branches to the Non-secure reset handler.

The following code shows you how to launch a Non-secure image:

/* TZ_START_NS: Start address of non-secure application */
#define TZ_START_NS   0x00200000

/* typedef for non-secure callback functions */
typedef void (*funcptr_void) (void) __attribute__((cmse_nonsecure_call));

/* Secure main() */

int main(void) {


  funcptr_void NonSecure_ResetHandler;
  
  printf("S:hello from Secure world\n");

  /* Set non-secure main stack (MSP_NS) */
  __TZ_set_MSP_NS(*((uint32_t *)(TZ_START_NS)));

  /* Get non-secure reset handler */
  NonSecure_ResetHandler = (funcptr_void)(*((uint32_t *)((TZ_START_NS) + 4U)));

  /* Start non-secure state software application */
  NonSecure_ResetHandler();

  return 0;
}

6. Build and generate the Secure image and import library

Add the Arm GNU toolchain linker option to generate the import library cmse_out_lib.o and Secure image. The following figure shows you the linker options:

GCC linker option to generate import library

Create a Non-secure project in Arm DS

1. Create a Non-secure project in Arm DS

You can follow the same procedure in Getting started with Cortex-M using Arm DS and CMSIS to create a Non-secure project. It is shown in the Project Explorer window as follows:

Non-secure project structure

2. Import library and call the Secure APIs

Add the interface header file Interface.h and the import library cmse_out_lib.o to the Non-secure project, as shown in the following figure:

add import library into Non-secure project

Call these Secure APIs as a normal function call. For example:

#include "Interface.h"
int main(void)
{
  
  Params test;
  int32_t result = 0;
  
  test.arg1 = 1;
  test.arg2 = 2;
  test.arg3 = 3;
  test.arg4 = 4;
  test.arg5 = 5;

  printf("NS: hello from Non-secure world \n\r");
  printf("NS: call Secure function\n\r");
  result = ns_callable_fn1(test.arg1, test.arg2, test.arg3, test.arg4);
  printf("NS: get add result from Secure side: %d + %d + %d + %d = %d\n\r",test.arg1,test.arg2,test.arg3,test.arg4,result);
  return 0;
}

Run Secure and Non-secure images in Arm DS

After the Non-secure project is built successfully, perform the following steps:

1. Set up the debug session in Arm DS to load and run Secure and Non-secure images

In Arm DS, select Run → Debug Configurations → New Launch configuration, select the appropriate target, such as MPS2_Cortex_M55 FVP, set the models parameters as below
-C NSC_CFG_0=1 -C fvp_mps2.DISABLE_GATING=1 -a SecurityExample_ns.elf -a SecurityExample_s.elf

set up debug session to launch Secure&Non-secure images

2. In the Files tab, load the symbol from the Secure and non-secure images for debug purpose, as follows:

Set up debug session to load the debug symbol from Secure&Non-secure images

3. Select Connect only for Run control in the Debugger tab as follows:

Set up debug session on Debugger connect with target

4. Run the images in Arm DS Click Debug to start. The executable image is downloaded into the target. You can run the executable by pressing the F8 key.

The text output in the Target Console view is as follows:

S: hello from Secure world 
NS: hello from Non-secure world 
NS: call Secure function
NS: get add result from Secure side: 1 + 2 + 3 + 4 = 10

Additional reading

This section lists relevant Arm publications for your reference:

  • Armv8-M Architecture Reference Manual
  • Arm Development Studio Getting Started Guide
  • Arm Development Studio User Guide
  • Fast Models Fixed Virtual Platforms (FVP) Reference Guide
  • Arm Development Studio Debugger Command Reference
Anonymous
  • Jerome Decamps - 杜尚杰
    Jerome Decamps - 杜尚杰 over 1 year ago

    Thanks for this real complete job !

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
Tools, Software and IDEs blog
  • Python on Arm: 2025 Update

    Diego Russo
    Diego Russo
    Python powers applications across Machine Learning (ML), automation, data science, DevOps, web development, and developer tooling.
    • August 21, 2025
  • Product update: Arm Development Studio 2025.0 now available

    Stephen Theobald
    Stephen Theobald
    Arm Development Studio 2025.0 now available with Arm Toolchain for Embedded Professional.
    • July 18, 2025
  • GCC 15: Continuously Improving

    Tamar Christina
    Tamar Christina
    GCC 15 brings major Arm optimizations: enhanced vectorization, FP8 support, Neoverse tuning, and 3–5% performance gains on SPEC CPU 2017.
    • June 26, 2025