Exception return issues with Cortex-M3 on STM32F103C8T6

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 ensured that the return stack for exception return is set up properly.
  • I tried to load EXC_RETURN value into PC by different ways like POP, LDR, none work.
  • I made sure that PSP value set for subsequent user code is valid.
  • The SVC is the only active interrupt when it's triggered, so no need to set NONBASETHRDENA bit in Configuration Control Register
  • I made sure exception number in xPSR weren't modified.

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

Parents
  • 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

Reply
  • 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

Children
No data