cleveraintwise
cleveraintwise

Reputation: 37

Cortex M3 - Calling a SVC inside a C function, and returning to thread mode

I am writing System Calls, as recommended by Joseph Yiu (M3 Guide), by taking the arguments from the stack. The Assembly SVC Handler is like:

     SVC_Handler:
     MOV R0, #0
     MSR CONTROL, R0
     CMP LR, #0xFFFFFFFD
     BEQ KernelEntry
     B   KernelExit

    KernelEntry: 
    <save current user stack>
    B svchandler_main

    KernelExit:
    <code to restore the very same saved user stack saved before>
    MOV LR, #0xFFFFFFFFD
    BX      LR 

Well, so the svchandler_main is C function that recovers the immediates (arguments of the the system call) creates the kernel stack and branches to 0xFFFFFFFF9 (MSP privileged). The system call itself is made like:

#define svc(code) asm volatile ("svc %[immediate]"::[immediate] "I" (code))
void SysCall_MyCall(int32_t args)
{
  svc(CallBack_Number);
}

That said, the callback function, running in handler mode:

void SysCallBack(void* args)
{
  /* <my c routine>*/
  asm volatile("svc #0"); //to exit the kernel mode
}

The last SV Call is performed so that the SVC_Handler in assembly will identify it is coming from handler mode (MSP privileged) and will exit the kernel - kind of a cooperative scheduling on the kernel. The problem is the context is saved with PSP pointing inside SysCall_MyCall, and it returns to there, and never exits. If I use inline functions, I will lose the handy svchandler_main. Any ideas? I didnt write the svchandler_main here because it is a classic code found on ARM application notes. Thanks.

Edit, to clarify: I am not branching to the callback function INSIDE SVC Handler. It creates the callback stack, change the LR to 0xFFFFFFF9 and execute a BX LR, exiting the the interruption, and going to the indicated MSP. To exit kernel another SVC is called, and user thread resumed.

Upvotes: 0

Views: 2727

Answers (1)

cooperised
cooperised

Reputation: 2599

It seems that you misunderstand how exception entry and return works on the Cortex-M. When you issue an SVC instruction from thread mode, the CPU transitions to handler mode just as for any other exception.

Handler mode is always privileged, and always uses the main stack (MSP). Thread mode can be either privileged or unprivileged depending on the nPRIV bit (bit 0) in the CONTROL register, and may be configured to use the process stack (PSP) by setting the SPSEL bit (bit 1) in the CONTROL register from thread mode.

On entry to handler mode, r0-r3, r12, lr, pc and xPSR are pushed to the active stack (PSP or MSP, depending on which is in use) and an exception return value is loaded into lr. The stack is switched to MSP. At the end of the handler, a BX lr instruction (or equivalent) causes this value to be used as a branch target, which automatically causes a restoration of the previous mode and stack, and pops r0-r3, r12, lr, pc and xPSR. The pop of pc restores execution from the place where the interrupt occurred.

The important thing about this mechanism is that it is 100% compatible with the ARM ABI. In other words, it is possible to write an ordinary function and use it as an exception handler, just by placing the address of the function in the appropriate place in the interrupt vector table. That's because the return at the end of a function is actioned by BX lr or equivalent, which is exactly the same instruction that triggers a return from handler mode.

So to write an SVC handler that makes use of callbacks, it is necessary to:

  • Work out which stack was in use when the SVC instruction was issued
  • Dig out the stacked pc to find the address of the SVC instruction itself, and extract the 8-bit constant from within the SVC instruction
  • Use the constant to work out which callback to invoke
  • Branch to (not call) the appropriate callback

The callback can be a perfectly ordinary function. When it returns, it will trigger the return from handler mode because the appropriate exception return code will still be in lr.

A handler that does all of this is presented in the M3 Guide, chapter 10.

If it is required that the callback receives arguments, this is a bit more complex but I can expand my answer if you'd like. Generally handler callbacks execute in handler mode (that's pretty much the point of SVC). If for some reason you need the callback to be executed without privilege, that's more complex still; there is an example in Chapter 23 of the M3 guide, though. You refer in the comments to not wanting to "manage nested interrupts" but really nested interrupts just manage themselves.

Upvotes: 1

Related Questions