6.2.1 異常中斷處理程序的方法種類及其介紹
在 SWI 指令中包括一個 24 位的立即數,該立即數指示了用戶請求的特定的 SWI 功能。在
SWI
異常中斷處理程序要讀取該 24 位的立即數,這涉及到 SWI異常模式下對寄存器 LR的讀
取,并且要從存儲器讀取該 SWI 指令。這樣需要使用匯編程序來實現。通常 SWI異常中斷處
理程序分為兩級:第 1 級 SWI
異常中斷處理程序為匯編,用于確定 SWI 指令中的 24 位的立
即數;第 2級 SWI 異常中斷處理程序具體實現 SWI 的各個功能,它可以是匯編程序,也可以
時 C 程序,下面我們分別介紹這兩級。
● SWI 異常中斷調用
① 在特權模式下調用 SWI
執(zhí)行 SWI 指令后,系統將會把 CPSR 寄存器的內容保存到寄存器 SPSR_SVC 中,
將返回地址保存到寄存器 LR_svc 中。這樣如果執(zhí)行 SWI 指令時,系統已經處于
特權模式下,這時寄存器 SPSR_svc 和寄存器 LR_svc 中的內容就會被破壞。因
此如果在特權模式下調用 SWI 功能,比如在一個 SWI 異常中斷處理程序中執(zhí)行
SWI指令,就必須將原始的寄存器SPSR_svc和寄存器LR_svc值保存在數據棧中。
程序6.1說明在SWI中斷處理程序中如何保存寄存器SPSR_svc和寄存器LR_svc
值。
程序 6.1 在SWI 中斷處理程序中保存寄存器 SPSR_svc 和寄存器 LR_svc 值
;保存寄存器,包括寄存器 lr_svc
STMFD sp!(r0-r3,r12,lr)
;保存 SPSR_svc
MOV r1,sp
MRS r0,spsr
STMFD sp!,(r0)
;讀取SWI指令
LDR r0,(lr,#4)
;計算其中的 24 位立即數,并將其放入寄存器 R0 中
BIC r0,r0.#0xff000000
;調用C_SWI_Handler 完成相應的 SWI 功能
BL C_SWI_Handler
;恢復SPSR_svc 的值
LDMFD sp!,(r0)
MSB spsr_cf,r0
;恢復其他寄存器,包括寄存器 LR_svc
LDMFD sp!,(r0-r3,r12,pc)^
② 從應用程序中調用 SWI
這里分兩種情況考慮從應用程序中調用特定的 SWI 功能:一種考慮使用匯編指
令調用特定的 SWI 功能;一種考慮從 C 語言程序中調用特定的 SWI 功能。
使用匯編指令調用特定的 SWI 功能比較簡單,將需要的參數按照 ATPCS 的要求
放在相應的寄存器中,然后在指令 SWI 中指定相應 24 位立即數即可。下面的例
子中,SWI 中斷處理程序需要的參數放在寄存器 R0 中,這里該參數為 100,然
后調用功能號為 0x0 的SWI 功能調用。
MOV R0,#100
SWI 0x0
從 C 語言程序中調用特定的 SWI 功能比較復雜,因為這時需要將一個 C 程序的
子程序調用映射到一個 SWI 異常中斷處理程序。這些被映射的 C 語言子程序使
用編譯器偽操作__SWI 來聲明。如果該子程序需要的參數和返回的結果只使用寄
存器 R0~R3,則該 SWI 可以被編譯成 inline 的,不需要使用子程序調用過程。
否則必須告訴編譯器通過結構數據類型來返回參數,這時需要使用編譯器偽操
作_value_in_reg 聲明該C 語言子程序。
下面通過一個完整的例子來說明如何從 C 程序中調用特定的 SWI 功能,該例子
是 ARM 公司的 ADS1.2 中所帶的。該例子提供的 4 個SWI 功能調用,功能號分別
為 0x0,0x1,0x2,0x3。其中 SWI 0x0及 SWI 0x1 使用兩個整形的輸入參數,并返
回一個結果值,SWI 0x2使用 4 個輸入參數,并返回一個結果值;SWI 0x3 使用
4 個輸入參數,并返回 4個結果值。
整個 SWI 異常中斷處理程序分為兩級結構。第 1 級的 SWI 異常中斷處理程序是
匯編程序 SWI_HANDLER,它讀取 SWI 指令中的 24 位立即數,然后調用第 2 個級
SWI 異常中斷處理程序 C_SWI_HANDLER來實現具體的 SWI 功能。第 2 級SWI異常
中谷底你處理程序 C_SWI_HANDLER 為 C 語言程序。其中實現了功能號分別為
0x0,0x1,0x2,0x3的 SWI功能調用。
主程序中的子程序 multiply_two()對應著 SWI 0x0;add_two()對應著
0x1;add_multiply_two()對應著 0x2;many_operations()對應著 SWI 0x3。
Many_operations()返回 4 個結果,使用編譯器偽操作_valuc_in_reg 聲明。4
個子程序都使用編譯器偽操作__SWI來聲明。主程序使用 lnstall_Handler()來
安裝該 SWI異常中斷處理程序,lnstall_Handler()在前面已經有詳細的介紹。
整個代碼如程序 6.2 所示。
程序 6.2 從C 程序中調用特定的 SWI功能
__swi(0) int multiply_two (int,int);
__swi(1) int add_two (int,int);
__swi(2) int add_multiply_two (int ,int ,int ,int );
Struct four_results
{
int a;
int b;
int c;
int d;
};
__swi(3) __value_in_regs struct four_results
Many operations (int ,int,int,int);
#include
#include “swi.h”
Unsigned *swi_vec=(unsigned *)0x08;
Unsigned install_Handler(unsigned routine, unsigned *vector)
{
Unsigned vec,old_vec;
Vec=(routine –(unsigned)vector-8)>>2;
If (vec & 0xff000000)
{
print(“Handler greter than 32Mbytes from vector”);
vec=0xea000000 |vec;
Old_vec=*vector;
*vector=vec;
Return(old_vec);
}
Int result1,result2;
Struct fcur_results result3;
Install_Handler ((unsigned) SWI_Handler,swi_vec);
Printf(“result1=mutiply_two (2,4)=%dn”,
Result1=multiply_two(2,4));
Printf(“result2=mutiply_two (3,6)=%dn”,
Result2=multiply_two(3,6));
Printf(“add_two(result1,result2 ) =%dn, add_two(result1,result2));
Printf(‘add_multiply_two(2,4,3,6)=&dn”,add_multiply_two(2,4,3,6));
Res_3=many_operations(12,4,3,1);
Printf(“res_3.a=&dn”,res_3.a);
Printf(“res_3.b=&dn”,res_3.b);
Printf(“res_3.c=&dn”,res_3.c);
Printf(“res_3.d=&dn”,res_3.d);
Return 0;
}
;第1 級SWI 異常中斷處理程序 SWI_Handler
;SWI_Handler 在前面已有詳細介紹
AREA SWI_Area, CODE,PEADONLY
EXPORT SWI_Handler
IMPORT C_SWI_Handler
T_bit EQU 0x2c
SWI_Handler
STMED sp!(r0-r3,r12,lr)
MOV r1,sp
MRS r0,spsr
STMFD sp!,(r0)
TST r0,#T_bit
LDRNEH r0(lr,#-2)
BTCNE r0,r0,#0xff00
LDREQ r0,(lr,#-4)
BICEQ r0,r0,#0xff000000
BL C_SWI_Handler
LDMFD sp!,(r0)
MSR spsr cf,r0
LDMFD sp!,(r0-r3,r12,pc)^
END
Void C_SWI_Handler (int swi_num,int *regs)
{
Switch (swi_num)
{
//對應于SWI 0x0
case 0:
regs[0]=regs[0]*regs[1];
break;
//對應于SWI 0x1
case 1:
regs[0]=regs[0]+ regs[1];
break;
//對應于SWI 0x2
case 2:
regs[0]=(regs[0]*regs[1]) +(regs[2]*regs[3]);
break;
//對應于SWI 0x3
case 3:
{int w,x,y,z;
w=regs[0];
x=regs[1];
y=regs[2];
z=regs[3];
regs[0]=w+x+y+z;
regs[1]=w-x-y-z;
regs[2]=w*x*y*z;
regs[3]=(w+x)*(y-z);
}
Break;
}
}
③ 從應用程序中動態(tài)調用 SWI
在有些情況下,直到運行時才能夠確定需要調用的 SWI 功能號。這時,有兩種
方法處理這種情況。
第 1 種方法是在運行時得到 SWI 功能號,然后構造出相應的 SWI 指令的編碼,
把這個指令的編碼保存在一個存儲器單元中,執(zhí)行該指令即可。
第 2 種方法使用一個通用的 SWI 異常中斷處理程序,將運行時需要調用的 SWI
功能號作為參數傳遞給該通用的 SWI 異常中斷處理程序,通用的 SWI 異常中斷
處理程序根據參數值調用相應的 SWI 處理程序完成需要的操作。
在匯編程序中很容易實現第 2 中方法。在執(zhí)行 SWI 指令之前先將需要調用的 SWI
功能號放在一個寄存器。在通用的 SWI 異常中斷處理程序讀取該寄存器值,決
定需要執(zhí)行的操作,但有些 SWI 處理程序需要 SWI 指令的 24位立即數,因而上
述兩種方法常常組合使用。
在操作系統中通常使用一個 SWI 功能號和一個寄存器來提供很多的 SWI 功能調
用。這樣可以將其他的 SWI 功能號留給用戶使用。在 DOS 系統中,DOS 提供功能
調用是 INT 21H ,這時通過指定寄存器 AX 的值,可以實現很多不同的功能調用。
ARM 體系中semihost 的實現也是一個例子。ARM 程序使用 SWI 0x123456 來實現
semilhost 功能調用;Thunb 程序使用SWI 0xAB 來實現 semihost 功能調用。在
下面的例子中,將子程序 WRITEC (unsigned op,char *c)映射到 semihost
功能調用,具體 semihost SWI 的子功能號通過參數 op 傳遞。
程序 6.3 從應用程序中動態(tài)調用 SWI 功能
#ifdef_thumb
#else
#define semiSWI 0x123456
#endif
__swi (semiSWI) void semibosting (unsigned op,char *c);
Void write_a_character (int ch)
{
char tempth=ch;
writec (&tempch);
}
2.FIQ 和IRQ 異常中斷處理程序
ARM 提供的FIQ和 IRQ 異常中斷用于外部設備向 CPU 請求中斷服務。這兩個異常中斷的引腳
都是低電平有效的。當前程序狀態(tài)寄存器 CPSR 的 1 控制位可以屏蔽這兩個異常中斷請求;
當程序狀態(tài)寄存器 CPSR由 1 控制位位 0 時,CPU正常響應 FIQ 和IRQ 異常中斷請求。
FIQ 異常中斷為快速異常中斷,它比 IRQ 異常中斷優(yōu)先級高,這主要表現在下面的兩個方
面:
● 當 FIQ 和IRQ 異常中斷同時產生時,CPU 先處理 FIQ 異常中斷。
● 在 FIQ 異常中斷處理程序中 IRQ 異常中斷被禁止。
由于 FIQ 異常中斷通常用于系統對于響應時間要求比較苛刻的任務,ARM 體系在設計上有
一些特別的安排,以盡量減少 FIQ 異常中斷響應時間。FIQ 異常中斷的中斷向量為 0x1c,位
于中斷向量表的最后。這樣 FIQ 異常中斷處理程序可以直接放在地址 0x1c 開始的存儲單元,
這種安排省掉了中斷向量表的跳轉指令,從而也就節(jié)省了中斷響應時間。當系統中存在
cache 時,可以把 FIQ 異常中斷向量以及處理程序一起鎖在 cache 中,從而大大地提高了
FIQ 異常中斷響應時間。除此之外,與其他的異常模式相比,FIQ 異常中斷還有額外的 5 個
物理寄存器,這樣在進入 FIQ 處理程序時可以保存這 5 個寄存器,從而也提高了 FIQ 異常中
斷的執(zhí)行速度。
在有些 IRQ/FIQ 異常中斷處理程序中,允許新的 IRQ/FIQ 異常中斷,這時將需要一些特
別的操作保證“老的”異常中斷的寄存器不會“新的”異常中斷破壞,這種 IRQ/FIQ 異常中
斷處理程序稱為可重入的異常中斷處理程序。否則稱為不可重入的異常中斷處理程序。
① 不可重入的 IRQ/FIQ 異常中斷處理程序
對于 C 語言不可重入的 IRQ/FIQ 異常中斷處理程序可以使用關鍵詞_irq來說明。關鍵詞
_irq 可以實現下面的操作:
● 保存 APCS 規(guī)定的被破壞的寄存器。
● 保存其他中斷處理程序中用到的寄存器。
● 同時將(LD-4)賦予程序計數器 pc 實現中斷處理程序的返回,并且恢復 CPSR 寄
存器的內容。
當 IRQ/FIQ異常中斷處理程序調用了子程序時,關鍵詞_irq可以使 IRQ/FIQ 異常中斷
處理程序返回時從其數據棧中讀取 LR_irq 值,并通過 SUBS PC,LR,#4 實現返回。程序
6.4 說明的關鍵詞_irq 的作用,其中列出了 C 語言程序及其對應的匯編程序,兩個 C
語言程序中,第 1 個使用關鍵詞_irq 聲明,第2個沒有使用關鍵詞_irq聲明。
程序 6.4 關鍵詞_irq 的作用
;第1 個程序使用關鍵詞_irq 聲明
_irq void IRQHandler (void)
{
Volatile unsigned int *base=(unsigned int *)0x8000000;
If (*base 1)
{
//調用相應的 C 語言處理程序
C_int_Handler ();
}
//清除中斷標志
*(base=1)=0;
}
;第1 個C 語言程序對應的匯編程序
IRQHandler PROC
STMFD sp!,(r0-r4,r12,lr)
MOV r4,#0x8000000
LDR r0,(r4,#0)
SUB sp,sp,#4
CMP r0,#1
BLEQ Q_int_handler
MOV r0,#0
STR r0,(r4,#4)
ADD sp,sp,#4
LDMFD sp!,(r0-r4,r12,lr)
SUBS pc,lr,#4
ENDP
BXPORT IRQHandler
//第2 個程序沒有使用關鍵詞_irq 聲明
_irq void IRQHandler (void)
{
Volatile unsigned int *base=(unsigned int *) 0x80000;
If(*base 1)
{
//調用相應的 C 語言處理程序
C_int_handler();
}
//清除中斷標志
*(base+1)=0;
}
;第1 個C 語言程序對應的匯編程序
IRQHandler PROC
STMFD sp!(r4,lr)
MOV r4,#0x8000000
LDR r0,(r4,#0)
CMP r0,#1
SLEQ C_int_handler
MOV r0,#0
STR r0,(r4,#4)
LDMFD sp!(r4,pc)
ENDP
② 可重入的 IRQ/FIQ 異常中斷處理程序
如果在可重入的 IRQ/FIQ 異常中斷處理程序中調用了子程序,子程序的返回地址被保存
到寄存器的 LR_irq 中,這時如果發(fā)生了 IRQ/FIQ異常中斷,這個 LR_irq寄存器的值將
被破壞,那么被調用的子程序將不能正確的返回。因此,對于可重入的 IRQ/FIQ異常中
斷處理程序一些需要特別的操作。下面列出了在可重入的 IRQ/FIQ 異常中斷處理程序中
需要的操作。這時,第1 級中斷處理程序不能使用 C 語言,因為其中一些操作不能通過
C 語言實現:
● 將返回地址保存到 IRQ的數據棧中。
● 保存工作寄存器和 SPSR_irq。
● 清除中斷標志位。
● 將處理器切換到系統模式,重新使能中斷(IRQ/FIQ)。
● 保存用戶模式的 LR 寄存器和被調用者的不保存的寄存器。
● 調用 C 語言的 IRQ/FIQ異常中斷處理程序。
● 當 C 語言的 IRQ/FIQ 異常中斷程序返回后,恢復用戶模式的寄存器,并禁止中斷
(IRQ/FIQ)。
● 切換到 IRQ模式,禁止中斷。
● 恢復工作組寄存器和寄存器 LR_irq。
● 從 IRQ 異常中斷處理程序中返回。
下面程序 6.5 演示了這些操作的過程。
程序6.5 可重入的 IRQ/FIQ 異常中斷處理程序
AREA INTERRUPT ,CODE,PEADONJY
;引入C語言的 IRQ中斷處理程序 C_irq_handler
IMPORT C_irq_handler
IRQ
;保存返回 IRQ 處理程序地址
SUB lr,lr,#4
STMFD sp! ,(lr)
保存 SPSR_irq,及其他工作寄存器
MRS r14,SPSR
STMFD sp!,lr12,r14
;
;在這里添加指令,清除中斷標志位
;添加指令重新使能中斷
;
;切換到系統模式,并使能中斷
MSR CPSR_C,#0x1f
;保存用戶模式的 LR_usr 及被調用者不保存寄存器
STMFD sp!,(r0-r3,lr)
;跳轉到C 語言的中斷處理程序
BL C_irq_handler
;恢復用戶模式的寄存器
LDMFD sp!,(r0-r3,lr)
;切換到IRQ 模式,禁止 IRQ 中斷,FIQ 中斷允許
MSR CPSP_c,#0x92
;恢復工作寄存器和 SPSR_irq
LDMFD sp!,(pc)^
END
6.2.2 復位中的異常中斷處理程序
復位異常中斷處理程序在系統加電或復位時執(zhí)行,它將進行一些初始化的工作,具體內
容與復位系統相關,然后程序控制權交給應用程序,因而復位異常中斷處理程序不需要返回。
下面時通常在復位異常中斷處理程序進行的一些處理:
● 設置異常中斷向量表。
● 初始化數據棧和寄存器。
● 初始化存儲系統,如系統中的 MMU等。
● 初始化一些關鍵的 I/O設備。
● 使用中斷。
● 將處理器切換到會話模式。
● 初始化 C 語言環(huán)境變量,條狀到應用程序執(zhí)行。
6.2.3 C 語言程序中的異常中斷處理程序
在程序運行過程中,也可以在 C 語言程序中安裝異常中斷處理程序。這時需要把相應的跳
轉指令或者數據讀取指令的編碼寫到中斷向量表的響應位置。下面分別討論這兩種情況下安
裝異常中斷處理程序的方法。
1.中斷向量表中使用跳轉指令的情況
當中斷向量表中使用跳轉指令時,在 C 程序中安裝異常中斷處理程序的操作如下:
(1) 讀取中斷處理程序的地址。
(2 ) 從上一步得到的地址中減去該異常中斷對應的中斷向量的地址。
(3) 從上一步得到的地址中減去 8,以允許指令的預取。
(4 ) 將上一步得到的地址右移 2 位,得到以字(32位)為單位的偏移量。
(5) 確保上一步得到的地址高 8 位為0,因為跳轉指令只允許 24位的偏移量。
(6) 將上一步得到的地址與數據 0xea000000 作邏輯或,從而得到將要寫到中斷向
量表的跳轉指令的編碼。
以上具體操作通過程序 6.6 實現下面的 C 程序。其中參數 routine 是中斷處理程序
的地址,vector 為中斷向量的地址。
程序 6.6 使用跳轉指令的中斷向量表
Unsigned install_handler (unsigned routine ,unsigned *vector)
{ unsigned vec, oldvec;
vec=((routine-(unsigned)vector-0x8)>>2);
If (vec & 0xff000000)
{
Printf(“Installation of handler failed” )
exit (2);
}
vec=0xea000000|vec;
oldvec=^vector;
*vector=vec;
return (oldvec);
}
2. 中斷向量表中使用數據讀取指令的情況
當中斷向量表中使用數據讀取指令時,在 C 程序中安裝異常中斷處理程序的操作序列如下
所示:
(1) 讀取中斷處理程序的地址。
(2) 從上一步得到的地址中減去該異常中斷對應的中斷向量的地址。
(3) 從上一步得到的地址中減去 8,允許指令預取。
(4) 將上一步得到的地址與數據 0xe59f000 作邏輯或,從而得到將要寫到中斷向量
表中的數據讀取指令的編碼。
(5) 將中斷處理程序的地址放到相應的存儲單元。
程序 6.7中的 C 程序實現了上面的操作序列。其中參數 location 是一個存儲單元,其中
保存了中斷處理程序的地址;vector 為中斷向量的地址。
程序6.7 數據讀取指令的中斷向量表
Unsigned Install_Handler (unsigned location ,unsigned *vector)
{ unsigned vec ,oldvec;
Vec=((unsigned)location-(unsigned)vector-0x8) | (0xe59ff0000 oldvec -
*vector;
*vector=vec;
Return (oldvec);
}
下面的語句調用上面的代碼,在 C 程序中安裝中斷處理程序。
Unsigned *irqvec=(unsigned *)0x18;
Install_Handler ((unsigned)IRQHandler,irqvec);
6.3 其它種類的異常中斷
1.數據訪問中止異常中斷處理程序
如果系統不包含 MMU,數據訪問中止異常中斷處理程序只是簡單地報告錯誤,然后退出。如
果系統中包含 MMU,數據訪問中止異常中斷處理程序要處理該數據訪問中止。當發(fā)生數據訪
問中止異常中斷的指令時。LR_abt 寄存器已經被更新,它指向引起數據訪問中止異常中斷
的指令后面第 2 條指令。此時要返回到引起數據訪問中止異常中斷的指令。即(LR_abt)處。
下面3 種情況可能引起數據訪問中止異常中斷。
① LDR/STR 指令
對于 ARM 數據訪問中止異常中斷發(fā)生時,LR_abt 寄存器已經被更新,它指向引起數據訪問
中止異常中斷的指令后面第 2 條指令,此時要返回到引起數據訪問中止異常中斷的指令。
對于 ARM9、ARM10、strongARM 處理器,數據訪問中止異常中斷發(fā)生后,處理器將程序計數
器設置稱引起數據訪問中止異常中斷的指令的地址,不需要用戶來完成這種程序計數器的設
置操作。
② SWAP 指令
SWAP 指令執(zhí)行時,未更新及存取 LR_abt.
③ LDM/STM 指令
對于 ARM6及 ARM7 處理器,如果寫回機制使能的話,基址寄存器將被更新。對于 ARM9、ARM10
及 strongARM 處理器,如果寫回機制使能的話,數據訪問中止異常中斷發(fā)生時,處理器將恢
復基址寄存器的值。
3.指令預取中止異常中斷處理程序
如果系統不包含 MMC,指令預取中止異常中斷處理程序只是簡單地報告錯誤,然后退出。
如果系統中包含 MMU,則發(fā)生錯誤的指令觸發(fā)虛擬地址失效,在該失效處理程序中重新
讀取該指令。指令預取中止異常中斷是有錯的指令執(zhí)行時被觸發(fā)是,這時 LR_abt 寄存
器還沒有被更新,它指向該指令的下面一條指令。因為該有問題的指令要被重新讀取,
因而應該返回到該有問題的指令,即返回到(LR_abt-4)處。
4.未定義指令異常中斷
當 CPU 不認識當前指令時,它將該指令發(fā)送到協處理器。如果所以的協處理器都不認識
該指令,這時將產生未定義指令異常中斷。在未定義指令異常中斷進行響應的處理。可
以看出這種機制可以用來通過軟件仿真系統中一些部件的功能。比如,如果系統中不包
含浮點運算部件,CPU 遇到浮點運算指令時。將發(fā)生未定義指令異常中斷,在該未定義
指令異常中斷的處理程序中可以通過其他指令序列仿真該浮點運算指令。
這種仿真的處理過程類似有 SWI異常中斷的功能調用。在 SWI 異常中斷的功能調用中
通過讀取 SWI 指令中的 24 位的立即數。判斷具體請求的 SWI 功能。這種仿真機制的操
作過程如下:
① 將仿真程序設置成未定義指令異常中斷的中斷處理程序(鏈接到未定義指令異常中
斷的中斷處理程序鏈中),并保存原來的中斷處理程序。這是通過修改中斷向量表
中未定義指令異常中斷對應的中斷向量來實現的。
② 讀取該未定義的位[27:24],判斷該未定義指令是否是一個協處理器指令。當位
[27:24]為 0b1110 或 0b110x 時,該未定義指令時一個協處理器指令。接著讀取該
未定義的指令的位[11:8],如果位[11:8]指定通過仿真程序實現該未定義指令,則
相應的調用仿真程序實現該指令的功能,后來返回到用戶程序。
③ 如果不仿真該未定義指令,程序跳轉到原來的未定義指令異常中斷的中斷處理程序
執(zhí)行。
Thumb 指令集中不包含協處理器指令,因而不需要這種仿真機制。
評論