I am porting FreeRTOS with TrustZone on LPC5500, I put FreeRTOS in secure memory, and created several user tasks in non-secure memory, as shown below:
But so far, I have not successfully switched from a secure task to a non-secure task.
How to switch from a secure task to a non-secure task?
Assumed that
- Non-secure task use PSP_NS (pointing to return stack for the Non-secure task)
- At the beginning of the Non-secure task, FPCA was 0 (no floating point context at start)
You can return from Secure handler to Non-secure thread with a EXC_RETURN code of 0xFFFFFFBD.
However, with the guidance from PSA & Trusted Firmware team, the plan for FreeRTOS port is to have the OS running in Non-secure world, and OS helper APIs in Secure world to help context switching.
regards,
Joseph
Hi Joseph
Thank you for your answer, but after I change the value of r14 (EXC_RETURN) from 0XFFFFFFFD to 0XFFFFFFBD in the PendSV interrupt, when the "bx r14" is executed, the program enters the hardfault state, it seems 0XFFFFFFBD is an illegal EXC_RETURN value?
Best Regards
Yang Zhang
There could be different causes for the HardFault:
- is the PSP_NS set correctly to Non-secure memory?
- what is in the stack frame? Was the stacked xPSR correct? (T bit set and IPSR=0)
- Is the PC value really pointing to Non-secure memory?
- what is showing in the fault status registers?
1) The psp_ns set to Non-secure memory.
2) The stack frame is standard stack frame , the value of the xPSR seems to be incorrect(T bit set , IPSR=3)
3) In PendSV interrupt, the PC is pointing Secure memory, new PC value is stored in the non-secure task stack and the new PC will point Non-secure memory.
4) The value of the fault status registers are shown as following:
CFSR:0X00000000
HFSR:0X40000000
DFSR:0X00000001
AFSR:0X00000000
SFSR:0X00000000
Can you help me analyze the cause of the HardFault?
Thanks.
Regarding stack frame, sorry for not clear. I meant the stack frame you created in Non-secure stack for the exception return to take place. What is in that stack frame?
In Non-secure task stack:
The stack frame as following:
Thanks
What is the value in the xPSR you put in for that stack frame?
The value of xPSR in stack frame is 0x01000000.
Thanks. That's look correct.
There is nothing flagged up the in FSR values you shown:
DFSR:0X00000001 - Halted bit is set
HFSR:0X40000000 - there is an escalated exception
But none of the other FSR is set which is a bit strange.
In your Non-secure code, the diagram showing that you are using printf. Have the C startup for the Non-secure work been executed? What happen if you replace that Non-secure task with just simple LED toggling?
The C startup for Non-secure work has been executed and after replacing Non-secure task with simple LED toggling the HardFault still exists.
The definition of the PendSV interrupt in my project is shown below. Based on the original PendSV interrupt function, I made some modifications, please help me check if it is feasible?
Thanks a lot.
Hi,
Sorry it took a bit long to reply. I created an example:
- Non-secure world first call a Secure API to pass the pointer of the NS thread to Secure world.
- Non-secure world call a Secure API that execute the test sequence:
1) Setup Non-secure PSP
2) Setup Non-secure CONTROL
3) Create Non-secure stack frame
4) Trigger PendSV that do the EXC_RETURN
Please note I didn't enable the FPU in this example, and Secure world used MSP for thread.
If Secure world use PSP for thread the exception return value is 0xFFFFFFBD.
================== Secure world =================
void __attribute__((cmse_nonsecure_entry)) set_thread_addr(uint32_t addr) { ptr = addr; printf ("[Secure world received pointer 0x%x]\n", ptr); return; }void __attribute__((cmse_nonsecure_entry)) tz_test1(void) { uint32_t v_MSP_NS; uint32_t v_PSP_NS; v_MSP_NS = __TZ_get_MSP_NS(); // Get Non-secure stack space v_PSP_NS = (v_MSP_NS - 0x100) & 0xFFFFFFF8U; // allocate 256 bytes for NS task stack, double word aligned __TZ_set_PSP_NS(v_PSP_NS); // Set PSP_NS __TZ_set_CONTROL_NS(__TZ_get_CONTROL_NS()|0x2); // Set CONTROL_NS to use PSP // Create stack frame for exception return HW32_REG((v_PSP_NS + 0x1C)) = 0x01000000U; // xPSR HW32_REG((v_PSP_NS + 0x18)) = ptr; // PC HW32_REG((v_PSP_NS + 0x14)) = 0xFFFFFFFFU; // LR HW32_REG((v_PSP_NS + 0x10)) = 0; // r12 HW32_REG((v_PSP_NS + 0x0C)) = 0; // r3 HW32_REG((v_PSP_NS + 0x08)) = 0; // r2 HW32_REG((v_PSP_NS + 0x04)) = 0; // r1 HW32_REG((v_PSP_NS + 0x00)) = 0; // r0 printf ("Stack frame created, triggering PendSV...\n"); // Trigger Secure PendSV SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; __DSB(); __ISB(); // PendSV should be triggered now return ; }void __attribute__((naked)) PendSV_Handler(void){ __asm volatile ( "LDR R0,= 0xFFFFFFB9\n\t" "BX R0\n\t" // Exception return to NS thread ); }
================== Non-secure world =================
int main(void){ unsigned int p; UART_Config(); printf("Non-secure Hello world\n"); printf ("Run test\n"); p = (unsigned) NS_thread; // Get starting address of thread printf ("NS_thread address is 0x%x\n", p); set_thread_addr(p); // Pass address of NS_thread printf ("Return to NS world #1\n"); tz_test1(); printf ("Return to NS world #2\n"); while(1); } void NS_thread(void){ puts("[NS thread]\n"); while(1); return;}
Thanks for your answer, I will try it.
The example you provided is very good, I can return to a non-secure task from the PendSV interrupt now. But when I call a secure function (entry function) in the non-secure task, the program will jump to the secure memory. At this time, the PSP stack pointer is used. The use of the PSP will destroy the scene of the secure task. After several task switches have been performed, the program will appear hardfault. I think this is due to the use of both PSP and PSP_NS. Can you give me some advice?
Thank you very much.
Non-secure tasks also need Secure stack allocation if you want to allow them to call Secure APIs. So when the Non-secure task is created, a Secure stack space (possibly based on the maximum stack usage of a Secure API).
When doing context switch, you need need to switch both PSP_NS and PSP_S (and their stack limit registers).
Do you know any way to set the PSP in a non-secure task?