1. 程式人生 > >ARM處理器異常處理

ARM處理器異常處理

1.1 處理器異常處理

所謂異常就是正常的使用者程式被暫時中止,處理器就進入異常模式,例如響應一個來自外設的中斷,或者當前程式非法訪問記憶體地址都會進入相應異常模式。

1.1.1  異常分類

(1)復位異常

當CPU剛上電時或按下reset重啟鍵之後進入該異常,該異常在管理模式下處理。

(2)一般/快速中斷請求

CPU和外部裝置是分別獨立的硬體執行單元,CPU對全部裝置進行管理和資源排程處理,CPU要想知道外部裝置的執行狀態,要麼CPU定時的去檢視外部裝置特定暫存器,要麼讓外部裝置在出現需要CPU干涉處理時“打斷”CPU,讓它來處理外部裝置的請求,毫無疑問第二種方式更合理,可以讓CPU“專心”去工作,這裡的“打斷”操作就叫做中斷請求,根據請求的緊急情況,中斷請求分一般中斷和快速中斷,快速中斷具有最高中斷優先順序和最小的中斷延遲,通常用於處理高速資料傳輸及通道的中資料恢復處理,如DMA等,絕大部分外設使用一般中斷請求。

(3)預取指令中止異常

該異常發生在CPU流水線取指階段,如果目標指令地址是非法地址進入該異常,該異常在中止異常模式下處理。

(4)未定義指令異常

該異常發生在流水線技術裡的譯碼階段,如果當前指令不能被識別為有效指令,產生未定義指令異常,該異常在未定義異常模式下處理。

(5)軟體中斷指令(swi)異常

該異常是應用程式自己呼叫時產生的,用於使用者程式申請訪問硬體資源時,例如:printf()列印函式,要將使用者資料列印到顯示器上,使用者程式要想實現列印必須申請使用顯示器,而使用者程式又沒有外設硬體的使用權,只能通過使用軟體中斷指令切換到核心態,通過作業系統核心程式碼來訪問外設硬體,核心態是工作在特權模式下,作業系統在特權模式下完成將使用者資料列印到顯示器上。這樣做的目的無非是為了保護作業系統的安全和硬體資源的合理使用,該異常在管理模式下處理。

(6)資料中止訪問異常

該異常發生在要訪問資料地址不存在或者為非法地址時,該異常在中止異常模式下處理。

1.1.2 異常發生的硬體操作

在異常發生後,ARM核心會自動做以下工作:

a.儲存執行狀態:將CPSR複製到發生的異常模式下SPSR中;

b.模式切換:將CPSR模式位強制設定為與異常型別相對應的值,同時處理器進入到ARM執行模式,禁止所有IRQ中斷,當進入FIQ快速中斷模式時禁止FIQ中斷;

c.儲存返回地址:將下一條指令的地址(被打斷程式)儲存在LR(異常模式下LR_excep)中。

d.跳入異常向量表:強制設定PC的值為相應異常向量地址,跳轉到異常處理程式中。

(1)儲存執行狀態

當前程式的執行狀態是儲存在CPSR裡面的,異常發生時,要儲存當前的CPSR裡的執行狀態到異常模式裡的SPSR裡,將來異常返回時,恢復回CPSR,恢復執行狀態。

(2)模式切換

硬體自動根據當前的異常型別,將異常碼寫入CPSR裡的M[4:0]模式位,這樣CPU就進入了對應異常模式下。不管是在ARM狀態下還是在THUMB狀態下發生異常,都會自動切換到ARM狀態下進行異常的處理,這是由硬體自動完成的,將CPSR[5] 設定為 0。同時,CPU會關閉中斷IRQ(設定CPSR 暫存器I位),防止中斷進入,如果當前是快速中斷FIQ異常,關閉快速中斷(設定CPSR暫存器F位)。

(3)儲存返回地址

當前程式被異常打斷,切換到異常處理程式裡,異常處理完之後,返回當前被打斷模式繼續執行,因此必須要儲存當前執行指令的下一條指令的地址到LR_excep(異常模式下LR,並不存在LR_excep暫存器,為方便讀者理解加上_excep,以下道理相同),由於異常模式不同以及ARM核心採用流水線技術,異常處理程式裡要根據異常模式計算返回地址。

(4)跳入異常向量表

該操作是CPU硬體自動完成的,當異常發生時,CPU強制將PC的值修改為一個固定記憶體地址,這個固定地址叫做異常向量(詳見3.2.4章節)。

1.1.3 異常返回地址

由1.1.3節可知,一條指令的執行分為:取指,譯碼,執行三個主要階段, CPU由於使用流水線技術,造成當前執行指令的地址應該是PC – 8(32位機一條指令四個位元組),那麼執行指令的下條指令應該是PC – 4。在異常發生時,CPU自動會將將PC – 4 的值儲存到LR裡,但是該值是否正確還要看異常型別才能決定。

各模式的返回地址說明如下:

(a)一般/快速中斷請求:

快速中斷請求和一般中斷請求返回處理是一樣的。通常處理器執行完當前指令後,查詢FIQ/IRQ中斷引腳,並檢視是否允許FIQ/IRQ中斷,如果某個中斷引腳有效,並且系統允許該中斷產生,處理器將產生FIQ/IRQ異常中斷,當FIQ/IRQ異常中斷產生時,程式計數器pc的值已經更新,它指向當前指令後面第3條指令(對於ARM指令,它指向當前指令地址加12位元組的位置;對於Thumb指令,它指向當前指令地址加6位元組的位置),當FIQ/IRQ異常中斷產生時,處理器將值(pc-4)儲存到FIQ/IRQ異常模式下的暫存器lr_irq/lr_irq中,它指向當前指令之後的第2條指令,因此正確返回地址可以通過下面指令算出:

SUBS    PC,LR_irq,#4        ; 一般中斷

SUBS    PC,LR_fiq,#4                   ; 快速中斷

注:LR_irq/LR_fiq分別為一般中斷和快速中斷異常模式下LR,並不存在LR_xxx暫存器,為方便讀者理解加上_xxx

(b)預取指中止異常:

在指令預取時,如果目標地址是非法的,該指令被標記成有問題的指令,這時,流水線上該指令之前的指令繼續執行,當執行到該被標記成有問題的指令時,處理器產生指令預取中止異常中斷。發生指令預取異常中斷時,程式要返回到該有問題的指令處,重新讀取並執行該指令,因此指令預取中止異常中斷應該返回到產生該指令預取中止異常中斷的指令處,而不是當前指令的下一條指令。 

指令預取中止異常中斷由當前執行的指令在ALU裡執行時產生,當指令預取中止異常中斷髮生時,程式計數器pc的值還未更新,它指向當前指令後面第2條指令(對於ARM指令,它指向當前指令地址加8位元組的位置;對於Thumb指令,它指向當前指令地址加4位元組的位置)。此時處理器將值(pc-4)儲存到lr_abt中,它指向當前指令的下一條指令,所以返回操作可以通過下面指令實現:

SUBS  PC,LR_abt,#4

注:LR_abt為中止模式下LR,並不存在LR_abt暫存器,為方便讀者理解加上_abt

(c)未定義指令異常:

未定義指令異常中斷由當前執行的指令在ALU裡執行時產生,當未定義指令異常中斷產生時,程式計數器pc的值還未更新,它指向當前指令後面第2條指令(對於ARM指令,它指向當前指令地址加8位元組的位置;對於Thumb指令,它指向當前指令地址加4位元組的位置),當未定義指令異常中斷髮生時,處理器將值(pc-4)儲存到lr_und中,此時(pc-4)指向當前指令的下一條指令,所以從未定義指令異常中斷返回可以通過如下指令來實現:

MOV  PC,  LR_und

注:LR_und為未定義模式下LR,並不存在LR_und暫存器,為方便讀者理解加上_und

(d)軟中斷指令(SWI)異常:

SWI異常中斷和未定義異常中斷指令一樣,也是由當前執行的指令在ALU裡執行時產生,當SWI指令執行時,pc的值還未更新,它指向當前指令後面第2條指令(對於ARM指令,它指向當前指令地址加8位元組的位置;對於Thumb指令,它指向當前指令地址加4位元組的位置),當未定義指令異常中斷髮生時,處理器將值(pc-4)儲存到lr_svc中,此時(pc-4)指向當前指令的下一條指令,所以從SWI異常中斷處理返回的實現方法與從未定義指令異常中斷處理返回一樣:

MOV  PC,  LR_svc

注:LR_svc為管理模式下LR,並不存在LR_svc暫存器,為方便讀者理解加上_svc

(e)資料中止異常:

發生資料訪問異常中斷時,程式要返回到該有問題的指令處,重新訪問該資料,因此資料訪問異常中斷應該返回到產生該資料訪問中止異常中斷的指令處,而不是當前指令的下一條指令。

資料訪問異常中斷由當前執行的指令在ALU裡執行時產生,當資料訪問異常中斷髮生時,程式計數器pc的值已經更新,它指向當前指令後面第3條指令(對於ARM指令,它指向當前指令地址加12位元組的位置;對於Thumb指令,它指向當前指令地址加6位元組的位置)。此時處理器將值(pc-4)儲存到lr_abt中,它指向當前指令後面第2條指令,所以返回操作可以通過下面指令實現:

SUBS  PC,  LR_abt,  #8

注:LR_abt為中止模式下LR,並不存在LR_abt暫存器,為方便讀者理解加上_abt

上述每一種異常發生時,其返回地址都要根據具體異常型別進行重新修復返回地址,再次強調下,被打斷程式的返回地址儲存在對應異常模式下的LR_excep裡。

1.1.4 異常向量表

異常向量表是一段特定記憶體地址空間,每種ARM異常對應一個字長空間(4Bytes),正好是一條32位指令長度,當異常發生時,CPU強制將PC的值設定為當前異常對應的固定記憶體地址。如表3-4所示是S3C2440的異常向量表。

表3-4 異常向量表

注:

異常向量也可以出現在高地址0xFFFF0000處,當今作業系統為了控制記憶體訪問許可權,通常會開啟虛擬記憶體,開啟了虛擬記憶體之後,記憶體的開始空間通常為核心程序空間,和頁表空間,異常向量表不能再安裝在0地址處了

ARM的例外優先順序從高到低依次為Reset→Data abort→FIQ→IRQ→Prefetch abort→Undefined instruction/SWI。

跳入異常向量表操作是異常發生時,硬體自動完成的,剩下的異常處理任務完全交給了程式設計師。由上表可知,異常向量是一個固定的記憶體地址,我們可以通過向該地址處寫一條跳轉指令,讓它跳向我們自己定義的異常處理程式的入口,就可以完成異常處理了。

正是由於異常向量表的存在,才讓硬體異常處理和程式設計師自定義處理程式有機聯絡起來。異常向量表裡0x00000000地址處是reset復位異常,之所以它為0地址,是因為CPU在上電時自動從0地址處載入指令,由此可見將復位異常安裝在此地址處也是前後接合起來設計的,不得不感嘆CPU設計師的偉大,其後面分別是其餘7種異常向量,每種異常向量都佔有四個位元組,正好是一條指令的大小,最後一個異常是快速中斷異常,將其安裝在此也有它的意義,在0x0000001C地址處可以直接存放快速中斷的處理程式,不用設定跳轉指令,這樣可以節省一個時鐘週期,加快快速中斷處理時間。

我們可以通過簡單的使用下面的指令來安裝異常向量表:

b reset                                      ;跳入reset處理程式

b HandleUndef              ;跳入未定義處理程式

b HandSWI                    ;跳入軟中斷處理程式

b HandPrefetchAbt        ;跳入預取指令處理程式

b HandDataAbt              ;跳入資料訪問中止處理程式

b HandNoUsed              ;跳入未使用程式

b HandleIRQ                 ;跳入中斷處理程式

b HandleFIQ                  ;跳入快速中斷處理程式

通常安裝完異常向量表,跳到我們自己定義的處理程式入口,這時我們還沒有儲存被打斷程式的現場,因此在異常處理程式的入口裡先要儲存打斷程式現場。

儲存執行現場:

異常處理程式最開始,要儲存被打斷程式的執行現場,程式的執行現場無非就是儲存當前操作暫存器裡的資料,可以通過下面的棧操作指令實現儲存現場:

STMFD  SP_excep!,  {R0 – R12,  LR_excep}

注:LR_abt,SP_excep分別為對應異常模式下LR和SP,為方便讀者理解加上_abt

需要注意的是,在跳轉到異常處理程式入口時,已經切換到對應異常模式下了,因此這裡的SP是異常模式下的SP_excep了,所以被打斷程式現場(暫存器資料)是儲存在異常模式下的棧裡,上述指令將R0~R12全部都儲存到了異常模式棧,最後將修改完的被打斷程式返回地址入棧儲存,之所以儲存該返回地址就是將來可以通過類似:MOV  PC,  LR的指令,返回使用者程式繼續執行。

異常發生後,要針對異常型別進行處理,因此,每種異常都有自己的異常處理程式,異常處理過程通過下節的系統中斷處理來進行分析。

1.1.5 異常處理的返回

異常處理完成之後,返回被打斷程式繼續執行,具體操作如下:

a.恢復被打斷程式執行時暫存器資料

b. 恢復程式執行時狀態CPSR

c.通過進入異常時儲存的返回地址,返回到被打斷程式繼續執行

異常發生後,進入異常處理程式時,將使用者程式暫存器R0~R12裡的資料儲存在了異常模式下棧裡面,異常處理完返回時,要將棧裡儲存的的資料再恢復回原先R0~R12裡,毫無疑問在異常處理過程中必須要保證異常處理入口和出口時棧指標SP_excep要一樣,否則恢復到R0~R12裡的資料不正確,返回被打斷程式時執行現場不一致,出現問題,雖然將執行現場恢復了,但是此時還是在異常模式下,CPSR裡的狀態是異常模式下狀態,因此要恢復SPSR_excep裡的儲存狀態到CPSR裡,SPSR_excep是被打斷程式執行時的狀態,在恢復SPSR_excep到CPSR的同時,CPU的模式和狀態從異常模式切換回了被打斷程式執行時的模式和狀態。此刻程式現場恢復了,狀態也恢復了,但PC裡的值仍然指向異常模式下的地址空間,我們要讓CPU繼續執行被打斷程式,因此要再手動改變PC的值為進入異常時的返回地址,該地址在異常處理入口時已經計算好,直接將PC = LR_excep即可。

上述操作可以一步一步實現,但是通常我們可以通過一條指令實現上述全部操作:

LDMFD  SP_excp!,  {r0-r12,  pc}^

注:SP_excep為對應異常模式下SP,^符號表示恢復SPSR_excep到CPSR

以上操作可以用下圖來描述:

圖3-2中斷處理示意圖