ARM v8.1 TrustZone (Cortex-M33), startup.s and main.c to switch Processor State from Secure to Non-Secure
-Aviral Mittal avimit att yahu dat cam.
https://www.linkedin.com/in/avimit/
SITE HOME

Secure to Non-Secure Switch on ARM V8.1 Architecture processor. ARM-Cortex M33.
If you are after some theory on ARM TrustZone, Click Here.
This web page is created to provide a bare minimum startup.s and main.c files which can be compiled by
ARM Compiler 6.12 or ARM Compiler 6.13 to produce binary executable for Cortex-M33 processor
The ARM Cortex-M33 implements ARM V8.1 instruction set architecture.
This code can also be compiled by Kiel IDE while M33 or ARMv8.1 is used as a target processor.
This code when run on the Cortex-M based upon ARM v8.1 Architecture set, would make the processor switch from
Secure State to Non-Secure State and back to Secure State.

Steps:
  1. Define 2 startup.s one fore secure world one for non-secure world
  2. Write 2 main.c containing main() function, one for secure world, one for non-secure world.
  3. Define Secure and non-secure memory locations. By default all of the memory space is treated as secure. Hence for this example only 1 non-secure region is defined. This is done by programing the SAU (Security Attribution Unit).
  4. Write a 'Scatter-Load' file to instruct the linker to put the 'non_secure' object file at a location in memory region, which is defined as Non-Secure Region through SAU programming.
  5. Compile the 'Non-Secure' startup.s and main.c files, get the binary executable file.
  6. Write a very small .asm file defining '.section section_ns_bin' as shown below.
  7. Compile the 'Secure' startup.s and main.c files.
  8. Link the object files
  9. Produce a single binary.
  10. Run Simulation.

The 'Non-Secure' startup.s file:

Stack_Size EQU 0x00000a00
               AREA STACK,NOINIT,READWRITE,ALIGN=3
__stack_limit
Stack_Mem       SPACE Stack_Size
__initial_sp
; Heap
Heap_Size       EQU     0x00000C00;
                        AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_mem        SPACE Heap_Size
__heap_limit

        PRESERVE8
        THUMB


; Vector Table Mapped to Address 0 at Reset
 AREA RESET, DATA, READONLY
 EXPORT __Vectors
 EXPORT __Vectors_End
 EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
          DCD Reset_Handler ; Reset Handler
                 DCD      HardFault_Handler                   ; -13 Hard Fault Handler
                 DCD      MemManage_Handler                   ; -12 MPU Fault Handler
                 DCD      BusFault_Handler                    ; -11 Bus Fault Handler
                 DCD      UsageFault_Handler                  ; -10 Usage Fault Handler
                 DCD      SecureFault_Handler                 ;  -9 Secure Fault Handler
                 DCD      0                                   ;     Reserved
                 DCD      0                                   ;     Reserved
                 DCD      0                                   ;     Reserved
                 DCD      SVC_Handler                         ;  -5 SVCall Handler
                 DCD      DebugMon_Handler                    ;  -4 Debug Monitor Handler
                 DCD      0                                   ;     Reserved
                 DCD      PendSV_Handler                      ;  -2 PendSV Handler
                 DCD      SysTick_Handler                     ;  -1 SysTick Handler

__Vectors_End

__Vectors_Size EQU __Vectors_End - __Vectors
                AREA |.text|, CODE, READONLY
Reset_Handler   PROC
                                EXPORT Reset_Handler [Weak]
                                IMPORT __main
                                LDR R0, =__main
                                BX  R0
                                ENDP
; The default macro is not used for HardFault_Handler
; because this results in a poor debug illusion.
HardFault_Handler PROC
                 EXPORT   HardFault_Handler         [WEAK]
                 B        .
                 ENDP

; Macro to define default exception/interrupt handlers. 
; Default handler are weak symbols with an endless loop. 
; They can be overwritten by real handlers. 
                 MACRO
                 Set_Default_Handler  $Handler_Name
$Handler_Name   PROC
                 EXPORT   $Handler_Name             [WEAK]
                 B        .
                 ENDP
                 MEND
; Default exception/interrupt handler


                 Set_Default_Handler  NMI_Handler
                 Set_Default_Handler  MemManage_Handler
                 Set_Default_Handler  BusFault_Handler
                 Set_Default_Handler  UsageFault_Handler
                 Set_Default_Handler  SecureFault_Handler
                 Set_Default_Handler  SVC_Handler
                 Set_Default_Handler  DebugMon_Handler
                 Set_Default_Handler  PendSV_Handler
                 Set_Default_Handler  SysTick_Handler
                                EXPORT   __stack_limit
                                EXPORT   __initial_sp
                                EXPORT   __heap_base
                                EXPORT   __heap_limit
                                END




The Non-Secure main.c file:
#include <datatypes.h>
__asm(".global __use_no_semihosting"); //to direct the compiler not to use semi-hosting (BKPT)
__asm(".global __ARM_use_no_argv"); //direct the compiler not to use args to main
//The following function is required to support the compile flow
void _sys_exit ()
{
  while (1); //temporary place holders
}

//The following function is required to support the compile flow
void _ttywrch ()
{
  return; //temporary place holders
}

//_sys_command_string
int main ()
{
//non-secure code here
        return 0 ;
}



The Secure startup.s file:
Stack_Size EQU 0x00000a00
               AREA STACK,NOINIT,READWRITE,ALIGN=3
__stack_limit
Stack_Mem       SPACE Stack_Size
__initial_sp
; Heap
Heap_Size       EQU     0x00000C00;
                        AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_mem        SPACE Heap_Size
__heap_limit

        PRESERVE8
        THUMB


; Vector Table Mapped to Address 0 at Reset
 AREA RESET, DATA, READONLY
 EXPORT __Vectors
 EXPORT __Vectors_End
 EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
          DCD Reset_Handler ; Reset Handler
                 DCD      HardFault_Handler                   ; -13 Hard Fault Handler
                 DCD      MemManage_Handler                   ; -12 MPU Fault Handler
                 DCD      BusFault_Handler                    ; -11 Bus Fault Handler
                 DCD      UsageFault_Handler                  ; -10 Usage Fault Handler
                 DCD      SecureFault_Handler                 ;  -9 Secure Fault Handler
                 DCD      0                                   ;     Reserved
                 DCD      0                                   ;     Reserved
                 DCD      0                                   ;     Reserved
                 DCD      SVC_Handler                         ;  -5 SVCall Handler
                 DCD      DebugMon_Handler                    ;  -4 Debug Monitor Handler
                 DCD      0                                   ;     Reserved
                 DCD      PendSV_Handler                      ;  -2 PendSV Handler
                 DCD      SysTick_Handler                     ;  -1 SysTick Handler


__Vectors_End

__Vectors_Size EQU __Vectors_End - __Vectors
                AREA |.text|, CODE, READONLY
Reset_Handler   PROC
                                EXPORT Reset_Handler [Weak]
                                IMPORT __main
                                MOV      r0,#0xed88 ; loading system registers set some bits to enable fpu mve
                                MOVT     r0,#0xe000 ; providing 'full access' to CP10 and CP11
                                ldr r1, [r0]
                                orr r1, r1, #0x00f00000
                                str r1, [r0] ;storing value or of curr value and 0x00f00000 at 0xe000ed88
                                LDR R0, =__main
                                BX  R0
                                ENDP
; The default macro is not used for HardFault_Handler
; because this results in a poor debug illusion.
HardFault_Handler PROC
                 EXPORT   HardFault_Handler         [WEAK]
                 B        .
                 ENDP


; Macro to define default exception/interrupt handlers. 
; Default handler are weak symbols with an endless loop. 
; They can be overwritten by real handlers. 
                 MACRO
                 Set_Default_Handler  $Handler_Name
$Handler_Name   PROC
                 EXPORT   $Handler_Name             [WEAK]
                 B        .
                 ENDP
                 MEND



; Macro to define default exception/interrupt handlers. 
; Default handler are weak symbols with an endless loop. 
; They can be overwritten by real handlers. 
                 MACRO
                 Set_Default_Handler  $Handler_Name
$Handler_Name   PROC
                 EXPORT   $Handler_Name             [WEAK]
                 B        .
                 ENDP
                 MEND
; Default exception/interrupt handler


                 Set_Default_Handler  NMI_Handler
                 Set_Default_Handler  MemManage_Handler
                 Set_Default_Handler  BusFault_Handler
                 Set_Default_Handler  UsageFault_Handler
                 Set_Default_Handler  SecureFault_Handler
                 Set_Default_Handler  SVC_Handler
                 Set_Default_Handler  DebugMon_Handler
                 Set_Default_Handler  PendSV_Handler
                 Set_Default_Handler  SysTick_Handler
                                EXPORT   __stack_limit
                                EXPORT   __initial_sp
                                EXPORT   __heap_base
                                EXPORT   __heap_limit

                                END


The Secure datatypes.h file
typedef unsigned int uint32_t;
#define CPU_SAU_REG_BASE ((uint32_t) 0xE000EDD0)
#define __IO
#define __RO
#define __RW
#define SCR *((uint32_t *) (0xE000ED10))
#define GPIO_1 *((uint32_t *) (0x20000ff0))
#define GPIO_2 *((uint32_t *) (0x20000df0))
#define GPIO_3 *((uint32_t *) (0x20000cf0))
#define GPIO_4 *((uint32_t *) (0x20000bf0))
#define GPIO_5 *((uint32_t *) (0x20001bb0)) //lines in non-secure region
#define GPIO_6 *((uint32_t *) (0x20000bb0)) //lines in secure region
#define VTOR_NS *((uint32_t *) (0xE002ED08))

typedef struct {
        __RW uint32_t SAU_CTRL; //SAU Control Reg
        __RO uint32_t SAU_TYPE; //SAU Control Reg
        __RW uint32_t SAU_RNR;  //SAU Region Number Regiser
        __RW uint32_t SAU_RBAR; //SAU Region Base Addr Reg
        __RW uint32_t SAU_RLAR; //SAU Limit Address Register
} CPU_SAU_REGS_TypeDef;
#define SAU_REGS ((CPU_SAU_REGS_TypeDef *) CPU_SAU_REG_BASE) //SAU_REGS should be an address.


The Secure main.c file
#include <datatypes.h>
#include <arm_cmse.h>
__asm(".global __use_no_semihosting"); //to direct the compiler not to use semi-hosting (BKPT)
__asm(".global __ARM_use_no_argv"); //direct the compiler not to use args to main


//Declare a non-secure Function type.
typedef void __attribute__((cmse_nonsecure_call)) nsfunc(void);

//this function is required, just as a placeholder, just to support the compile flow
void _sys_exit ()
{
  while (1); //temporary place holders
}

//this function is required, just as a placeholder, just to support the compile flow
void _ttywrch ()
{
  return; //temporary place holders
}
void en_sau ( CPU_SAU_REGS_TypeDef * saupointer)
{
  saupointer = SAU_REGS;
  saupointer->SAU_CTRL = 0x00000001;//Enable SAU
}

void go_to_dsleep ()
{
  SCR = 0x00000004; //bit 2 of SCR for deepsleep
  __asm("WFE");
}

/**  \brief   Set Main Stack Pointer (non-secure) 
\details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. 
\param [in]    topOfMainStack  Main Stack Pointer value to set
*/
static __attribute__((always_inline)) void __TZ_set_MSP_NS(uint32_t topOfMainStack)
{
__asm volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : );
}


//Declare a pointer to Non-Secure function type
static nsfunc *fp; //static means local to the file , fp is a pointer to type nsfunc

//_sys_command_string
int main ()
{
        CPU_SAU_REGS_TypeDef * saupointer; //pointer to the structure of sau registers
        asm("nop");
        saupointer = SAU_REGS;
        saupointer->SAU_RNR = 0x00000000;
        saupointer->SAU_RBAR = 0x20010000U;
        saupointer->SAU_RLAR = 0x2001FFFDU; //d=1101 e= 1110, f = 1111 // NSC = 0, EN = 1 The region is Non-Secure
        __asm("DSB");
        saupointer->SAU_RNR = 0x00000001;
        saupointer->SAU_RBAR = 0x20020000;
        saupointer->SAU_RLAR = 0x2002FFFE; // NSC = 1, EN = 1 The region is secure and Non-Secure Callable
        en_sau(saupointer);
        __asm("DSB");
        saupointer->SAU_RNR = 0x00000000;
        __asm("DSB");

//0xE002ED08
        VTOR_NS = 0x20010000; // Put 0x2001_0000 in location 0xE002ED08 (Vector Table offset Address)
        __TZ_set_MSP_NS(0x20020a00); //Load the Non-Secure Stack Pointer with the top of the stack location
//0x2001008c -> NS Reset_Handler
       fp = (nsfunc*)(0x2001008d); //fp is a pointer pointing to a non-secure location
       fp = cmse_nsfptr_create(fp); //Clear LSB of Non-Secure Function Address
       if (cmse_is_nsfptr(fp)) //Check of the LSB is cleared   
       {
         fp();                             /* Non-secure function call */
       } else
       {
         while(1);    /* Something went wrong */
       }
       __asm("DSB");
       go_to_dsleep();
       while(1);
}




Compile the Non-Secure files: This will typically be done in a directory where you will keep your non-secure code files e.g. directory called 'non_secure_code'

/pkg/ARM/6.13/bin/armclang main.c -c --target=arm-arm-none-eabi -mcpu=cortex-m33 -o c.o -O1 -I  ../common
/pkg/ARM/6.13/bin/armasm startup.s --cpu=cortex-m33 -o a.o
/pkg/ARM/6.13/bin/armlink a.o c.o --ro_base 0x20010000 --rw_base 0x20020000 --entry Reset_Handler --first __Vectors
/pkg/ARM/6.13/bin/fromelf --text -c -s -t -z --output image.txt __image.axf
/pkg/ARM/6.13/bin/fromelf -cvf __image.axf --vhx --8x1 -o image.hex
/pkg/ARM/6.13/bin/fromelf --bin -o image.bin __image.axf


copy image.bin to ns.bin
Then copy this 'ns.bin' to the directory where your secure code is: e.g. in a directory called 'secure_code'
cp ns.bin ../secure_code.

Make a small assembly file in 'secure_code' directory, which will instruct the compiler/linker to include the 'ns.bin' file. Call it 'ns_bin.s'
Here is your 'ns_bin.s' assembly code. Its just 2 lines of code in this assembly file:
.section section_ns_bin, "a", %progbits
.incbin "ns.bin"



Here is your 'scatter.sec' : Scatter-Load file
LR_IROM1 0x00000000 0x00040000  {    ; load region size_region
  ER_IROM1 0x00000000 0x00020000  {
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
  NS_EXEC_BIN 0x20010000 0x00020000  {  ; NS code starts here. First value is the ns sp value.
    ns_bin.o(section_ns_bin)
  }
}







Compile the Secure Files and Link the Secure and Non-Secure object files: This will typically be done in a directory where you will keep your secure code .e.g a directory called 'secure_code'
/pkg/ARM/6.13/bin/armclang main.c -c --target=arm-arm-none-eabi -mcmse -mcpu=cortex-m33 -o c.o -O1 -I  ../common
/pkg/ARM/6.13/bin/armasm startup.s --cpu=cortex-m33 -o a.o
/pkg/ARM/6.13/bin/armclang --target=arm-arm-none-eabi -mcpu=cortex-m33 -c ns_bin.s -o ns_bin.o
/pkg/ARM/6.13/bin/armlink a.o c.o ns_bin.o --keep=ns_bin.o\(section_ns_bin\) --map --load_addr_map_info --list=image.lst --scatter=secure.sct --entry Reset_Handler
/pkg/ARM/6.13/bin/fromelf --text -c -s -t -z --output image.txt __image.axf
/pkg/ARM/6.13/bin/fromelf -cvf __image.axf --vhx --8x1 -o image.hex
/pkg/ARM/6.13/bin/fromelf --bincombined -o image.bin __image.axf


The above compile/link steps will produce your final binary executable file called 'image.bin'. Run your RTL simulations with this binary file.



Click Here to Make Comments or ask Questions

SITE HOME