gcc Cortex M3 -在C函数中调用SVC,并返回线程模式

n7taea2i  于 2023-06-30  发布在  其他
关注(0)|答案(1)|浏览(193)

我正在编写系统调用,正如Joseph Yiu(M3指南)所推荐的那样,通过从堆栈中获取参数。程序集SVC处理程序如下所示:

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

那么,svchandler_main是一个C函数,它恢复即时数(系统调用的参数),创建内核堆栈并分支到0xFFFFFF 9(MSP特权)。系统调用本身如下所示:

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

也就是说,在处理程序模式下运行的回调函数:

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

执行最后一个SV调用,以便汇编中的SVC_Handler将识别出它来自处理程序模式(MSP特权),并将退出内核-内核上的协作调度类型。问题是上下文是用指向SysCall_MyCall内部的PSP保存的,它会返回到那里,并且永远不会退出。如果我使用内联函数,我将失去方便的svchandler_main。有什么想法吗?我没有在这里写svchandler_main,因为它是ARM应用笔记上的经典代码。谢谢
编辑,以澄清:我没有分支到回调函数INSIDE SVC Handler。它创建回调堆栈,将LR更改为0xFFFFFFF 9并执行BX LR,退出中断并转到指示的MSP。要退出内核,调用另一个SVC,并恢复用户线程。

yrefmtwq

yrefmtwq1#

看来你误解了异常进入和返回在Cortex-M上的工作原理。当您从线程模式发出SVC指令时,CPU会像处理任何其他异常一样转换到处理程序模式。
处理程序模式始终是特权模式,并且始终使用主堆栈(MSP)。根据CONTROL寄存器中的nPRIV位(位0),线程模式可以是特权模式或非特权模式,也可以通过从线程模式设置CONTROL寄存器中的SPSEL位(位1)来配置为使用进程堆栈(PSP)。
在进入处理程序模式时,r0-r3r12lrpcxPSR被推送到活动堆栈(PSP或MSP,取决于使用的是哪个),并将异常返回值加载到lr中。堆栈切换到MSP。在处理程序结束时,BX lr指令(或等效指令)会将此值用作分支目标,这会自动恢复先前的模式和堆栈,并弹出r0-r3r12lrpcxPSRpc的弹出将从中断发生的位置恢复执行。
这种机制的重要之处在于它与ARM ABI 100%兼容。换句话说,可以编写一个普通函数并将其用作异常处理程序,只需将函数的地址放在中断向量表中的适当位置即可。这是因为函数末尾的返回是由BX lr或等效指令执行的,这与触发从处理程序模式返回的指令完全相同。
因此,要编写一个使用回调的SVC处理程序,有必要:

  • 找出发出SVC指令时正在使用的堆栈
  • 挖出堆栈的pc以找到SVC指令本身的地址,并从SVC指令中提取8位常数
  • 使用常量来确定要调用哪个回调
  • 分支到(not call)适当的回调

回调可以是一个非常普通的函数。当它返回时,它将触发从处理程序模式返回,因为适当的异常返回代码仍然在lr中。
M3指南第10章中介绍了一个可以完成所有这些任务的处理程序。
如果需要回调函数接收参数,这就有点复杂了,但如果你愿意,我可以扩展我的答案。一般来说,处理程序回调以处理程序模式执行(这几乎是SVC的重点)。如果出于某种原因,您需要在没有特权的情况下执行回调,那就更复杂了;不过,在M3指南的第23章中有一个例子。您在注解中提到不想“管理嵌套中断”,但实际上嵌套中断只是管理它们自己。

相关问题