ARM v8.1 TrustZone (Cortex-M33),
startup.s and main.c to switch Processor State from Secure
to Non-Secure
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:
- Define 2 startup.s one fore secure world one for non-secure
world
- Write 2 main.c containing main() function, one for secure
world, one for non-secure world.
- 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).
- 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.
- Compile the 'Non-Secure' startup.s and main.c files, get the
binary executable file.
- Write a very small .asm file defining '.section
section_ns_bin' as shown below.
- Compile the 'Secure' startup.s and main.c files.
- Link the object files
- Produce a single binary.
- 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