我正在编写系统调用,正如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,并恢复用户线程。
1条答案
按热度按时间yrefmtwq1#
看来你误解了异常进入和返回在Cortex-M上的工作原理。当您从线程模式发出
SVC
指令时,CPU会像处理任何其他异常一样转换到处理程序模式。处理程序模式始终是特权模式,并且始终使用主堆栈(MSP)。根据
CONTROL
寄存器中的nPRIV
位(位0),线程模式可以是特权模式或非特权模式,也可以通过从线程模式设置CONTROL
寄存器中的SPSEL
位(位1)来配置为使用进程堆栈(PSP)。在进入处理程序模式时,
r0-r3
、r12
、lr
、pc
和xPSR
被推送到活动堆栈(PSP或MSP,取决于使用的是哪个),并将异常返回值加载到lr
中。堆栈切换到MSP。在处理程序结束时,BX lr
指令(或等效指令)会将此值用作分支目标,这会自动恢复先前的模式和堆栈,并弹出r0-r3
、r12
、lr
、pc
和xPSR
。pc
的弹出将从中断发生的位置恢复执行。这种机制的重要之处在于它与ARM ABI 100%兼容。换句话说,可以编写一个普通函数并将其用作异常处理程序,只需将函数的地址放在中断向量表中的适当位置即可。这是因为函数末尾的返回是由
BX lr
或等效指令执行的,这与触发从处理程序模式返回的指令完全相同。因此,要编写一个使用回调的SVC处理程序,有必要:
pc
以找到SVC指令本身的地址,并从SVC指令中提取8位常数回调可以是一个非常普通的函数。当它返回时,它将触发从处理程序模式返回,因为适当的异常返回代码仍然在
lr
中。M3指南第10章中介绍了一个可以完成所有这些任务的处理程序。
如果需要回调函数接收参数,这就有点复杂了,但如果你愿意,我可以扩展我的答案。一般来说,处理程序回调以处理程序模式执行(这几乎是SVC的重点)。如果出于某种原因,您需要在没有特权的情况下执行回调,那就更复杂了;不过,在M3指南的第23章中有一个例子。您在注解中提到不想“管理嵌套中断”,但实际上嵌套中断只是管理它们自己。