异常服务函数和中断服务函数都是运行在内核态的,在普通模式下,用户如果想干一些特权功能,通过修改寄存器直接进入内核态是不可能的,只能通过异常服务函数来做(一般是通过软中断异常),只要程序员们提前约定好协议,把想做的事情放到异常服务函数中就行了。
对于用户态的各种各样的特权操作代码(也即,各种系统调用system_call的代码),都放到异常服务函数中,在服务函数中做一个约定好的类似switch case的分支操作,用户就能选择执行这些system_call了,可能有些system_call还需要参数,如何选择system_call,又如何给system_call传参呢?有两种方法:
方法1:通过SWI xxx指令的操作数xxx来选择system_call,通过Rn或栈来传递实参。常见的SWI指令,高8位是操作码SWI,低24位是操作数xxx;
方法2:约定某个Rn例如R0来选择system_call,通过其他Rn或栈来传递实参,这样SWI后面的操作数就不起作用了;
下面是方法1的例子:
;--------------这段代码一般位于启动文件中,是厂家做好的-------------------
; 异常和中断向量表开始
; 0x00: 复位Reset异常
b Reset
; 0x04: NMI_Handler异常
b NMI_Handler
; 0x08: 软件中断异常,跳往软件中断处理函数HandleSWI
b HandleSWI ;跳转到SWI异常服务函数中去
… …
;设置堆、栈的起始地址、大小等,弱定义(WEAK)各种中断/异常服务函数等···略···我们常见的STM32的启动文件例如startup_stm32f10x_hd.s中可以阅读详细代码
;-------------------------------------------------------------------------------------------------
;异常服务函数是弱定义的,因此我们可以重自定义自己的异常服务函数
HandleSWI
STMFD SP!, {R0-R12, LR} ; 保存现场
LDR R4, [LR, #-4] ; LR - 4 为指令" swi xxx" 的地址,xxx为低24位,我们根据xxx的值来选择不同的系统调用
BIC R4, R4, #0xFF000000 ; 取得ARM指令swi xxx中xxx的值,放到R4中
;下面这段switch代码应该用汇编来写,不过用C语言能更清楚的显示出原理。
;c语言转为汇编,可参考搜索以下关键字自行学习:汇编调用C语言函数。难点可能在于汇编代码要符合编译器的规范,
;例如:第几个实参应放入第几个通用寄存器、从第几个实参开始,要入栈、返回值的传递方法等
;当然,如果你自定义的系统调用不是用C而是直接用汇编子程序来写,那么不必考虑C语言的编译规范了,直接用B、Bx、call等指令跳转就是了
switch(R4)
{
case 0: system_call_read(R0);break;
case 1: system_call_write(R0,R1);break;
case 2: system_call_peek();break;
case 3: system_call_3(R0);break;
case 4: system_call_4(R1);break;
case 5: system_call_5();break;
default:
}
LDMIA SP!, {R0-R12, PC}^ ;恢复现场 中断返回, ^表示将spsr的值复制到cpsr
;-------------------------------------------------------------------------------
void system_call_read(int fn)
{
//按照编译器的编译规范,R0的内容就是第一个形参的内容,读fn就可以读到R0的内容
//如果在发生系统调用前,汇编代码传的第一个实参参不是用的R0,那么该函数就不能用纯C语言来写了,只能用汇编或者在本函数内嵌入汇编,来取得实参
}
//···其余系统调用的代码··略··
1
|
|