在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;