Hello community!I am currently working on developing a simple RTOS on STM32 for learning purposes.
When I was testing a piece of code that switches from kernel mode to user mode, I encountered a strange issue.I used SVC interrupt for context switching, and the handler name is __int_SVC.This is what the code looks like:
__int_SVC: @ __user_mode: @ save kernel context ldr r2, =kernel_save stmia r2!, {r4-r11} mov r1, pc str r1, [r2], 4 mov r1, lr str r1, [r2], 4 @ set up return frame for task mov r1, 8 loop1: ldr r2, [r0], 4 str r2, [sp], 4 add r1, -1 cmp r1, 0 bgt loop1 add sp, -4 * 8 @ restore other regs ldmia r0!, {r4-r11} ldr r1, [r0] msr psp, r1 @ enable interrupt tim2 @ ldr r0, nvic_base @ ldr r1, [r0] @ orr r1, r1, 0x10000000 @ str r1, [r0] @ go thread mode, using psp mov lr, 0xfffffffd bx lr .align 2 nvic_base: .word 0xE000E100 .section .bss .align 2 kernel_save: .rept 10 .word 0x00000000 .endr
The problem is , when I set lr to 0xFFFFFFFD, which is the EXC_RETURN value for "go to thread mode and use PSP for stack pointer", after the "bx lr", it always triggers an Usage Fault, but instead when I set it to 0xFFFFFFF9, which is for "go to thread mode and use MSP for stack pointer", it works fine and successfully switches to user code, all register context switching is good except for SP because I want it set to PSP.
The Usage Fault Status Register has bit INVPC set indicating that "Attempts to do exception with bad value in EXC_RETURN number", I don't understand why 0xFFFFFFF9 works but 0xFFFFFFFD won't.
Things I tried for debugging:
I also found a document about exception return behavior but it's for ARMv7-M:https://developer.arm.com/documentation/ddi0403/d/System-Level-Architecture/System-Level-Programmers--Model/ARMv7-M-exception-model/Exception-return-behavior?lang=en
I tried to follow the pseudo-code's logic in the article, seems if you can make 0xFFFFFFF9 return successfully you should also have 0xFFFFFFFD work as well, but that's for v7-M, I'm not sure if things work the same in Cortex-M3.
Please let me know if you have any clues or if you want more information !
About exception return in CM3: https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return?lang=en
Problem solved!
The key point is that when an exception return happens from Handler Mode to Unprivileged Thread Mode, it first switches to PSP, then it does the stack popping stuff instead of doing so on Main Stack. This design separates different stacks and makes things easier.Below is my updated ASM code doing context switching, it's way easier to write this code because of this design of separated stacks, too.
.thumb .syntax unified .global __Sys_UserMode, __Sys_KernelMode, __Int_SVC __Sys_UserMode: SVC 0 BX LR __Int_SVC: @ Save kernel regs LDR R1, =kernelSave STMIA R1!, {R4-R11} @ Set PSP to task stack bottom LDR R1, [R0, 8 * 4] MSR PSP, R1 @ Restore task regs LDMIA R0!, {R4-R11} @ Return to thread mode, using PSP MOV LR, 0xFFFFFFFD BX LR __Sys_KernelMode: @ Save task regs STMIA R0!, {R4-R11} @ Save PSP MRS R1, PSP STR R1, [R0] @ Restore kernel regs LDR R1, =kernelSave LDMIA R1!, {R4-R11} @ Return to thread mode, using MSP MOV LR, 0xFFFFFFF9 BX LR .section .bss kernelSave: .rept 8 .int 0x00000000 .endr