ARM基础学习-SWI异常中断处理

在ARM处理器中,我们常常使用SWI指令来产生一个软中断。软中断指令SWI指令中包含了一个24位的立即数,这个立即数指示了用户请求的特定的SWI功能,即这个立即数表示的是SWI指令所想要触发中断的中断号。

所以,当SWI指令触发了一次异常后进入异常处理的程序时,异常程序必须要从SWI指令中提取出来中断号,即提出出来SWI指令中低24位的值,从而得到用户请求的特定的SWI功能。

SWI异常处理程序

通常情况下,SWI异常中断处理函数分为两级,第一级的SWI处理函数用于从SWI指令中提取24位的立即数即中断号,通第一级函数通过汇编语言、内嵌汇编来完成。第二级SWI异常中断处理程序实现各个SWI的具体功能,第二级程序可以是汇编程序,也可以是C程序。
  
第一级SWI异常处理程序通过LR寄存器内容得到SWI指令地址,LR寄存器中保存的是该SWI指令的下一条指令的地址,并从存储器中得到SWI指令编码,从而提取出来24位中断号。下面的例子显示了提取中断向量号的标准过程。

.SWI_Handler:
    STMFD sp!,{r0-r12,lr} ;保存寄存器
    LDR r0,[lr,#-4]   ;计算SWI指令地址
    BIC r0,r0,#0xff000000 ;提取指令编码的后24位
    ;
    ; 提取出的中断号放r0寄存器,函数返回
    ;
    LDMFD sp!, {r0-r12,pc}^ ;恢复寄存器

第二级中断处理函数(根据提取的中断向量号,跳转到具体处理函数)却可以使用C语言来完成。因为第一级的中断处理函数已经将中断号提取到寄存器r0中,所以根据AAPCS函数调用规则,可以直接使用BL指令跳转到C语言函数,而且中断向量号作为第一个参数被传递到C函数。


void C_SWI_handler (unsigned number)
{
    switch (number)
     {
      case 0 : /* SWI number 0 code */
      break;
      case 1 : /* SWI number 1 code */
      break;
      ...
      default : /* Unknown SWI - report error */
     }
}

另外,如果需要传递的参数多于1个,那么可以使用堆栈,将堆栈指针作为函数的参数传递给C类型的二级中断处理程序,就可以实现在两级中断之间传递多个参数。

例如:

MOV r1, sp   ;将传递的第二个参数(堆栈指针)放到r1中
BL C_SWI_Handler ;调用C函数

相应的C函数的入口变为:

void C_SWI_handler(unsigned number, unsigned *reg)

在2级中断处理程序,可以通过下面的操作数读取参数,这些参数是在SWI异常中断产生时各寄存器的值,这些寄存器保存在SWI异常中断的数据栈中:

value_in_reg_0 =reg [0];
value_in_reg_1 =reg [1];
value_in_reg_2 =reg [2];
value_in_reg_3= reg [3];

SWI异常中断调用

(1) 在特权模式下调用SWI

比如在中断模式下调用SWI,系统此时已经属于特权模式,这是在调用SWI会破坏SPSR_svc和寄存器LR_svc,所以在调用SWI前保存SPSR_svc和寄存器LR_svc在数据栈中:

STMFD sp!,{r0-r3,r12,lr}

MOV r1,sp;
MRS  r0,spsr;
STMFD sp!,[r0];
...
SWI
...

LDMFD sp!,{r0};
MSR spsr_cf,r0;
LDMFD sp!, {r0-r3,r12,pc}^ ;

(2)应用程序中调用SWI

最简单的是用汇编:

MOV r0,#100;
SWI 0x0;

C语言调用SWI比较复杂,因为这时需要将一个C程序的子程序调用映射到一个SWI异常中断程序,这些被映射的C语言子程序使用编译器伪操作_SWI来声明,如果该子程序需要的参数和返回结果只使用R0~R3寄存器,则该SWI可以编译成inline,不需要使用子程序调用过程,否则编译器需要用数据结构来返回参数,这时需要用伪操作_value_in_reg声明该C语言程序;

需要安装异常中断处理程序到向量表中相对位置;

详细参考ARM体系结构与编程P-272;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章