一, 什么是異常
異常(Exception)通俗點來講,就是系統(tǒng)在正常運行的時候出現(xiàn)的非正常事件,這個非正常事件會導致系統(tǒng)狀態(tài)更改或者其他錯誤,為了確保系統(tǒng)功能能正常運行,需要一些帶有特權(quán)的軟件代碼(exception handler)來采取一些補救措施或者更新系統(tǒng)狀態(tài),這個過程被稱為異常處理,系統(tǒng)會停止當前程序,并進入handler,在handler處理完成之后,系統(tǒng)會從handler返回,再繼續(xù)正常運行。
所以可以說,異常就是可以導致當前正常程序被掛起的任何事件。
如下圖所示,程序正常執(zhí)行過程中,發(fā)生了異常,程序掛起,并跳轉(zhuǎn)到異常處理函數(shù),在處理完異常后,從異常返回,程序恢復正常運行:
有些處理器架構(gòu)可能會講上述過程描述為一個中斷,但是在ARM AArch64中,中斷是一種由外部產(chǎn)生的,特殊類型的異常。
異常的用途非常廣泛,包括以下:
- 模擬虛擬設備
- 虛擬內(nèi)存管理
- 處理軟件錯誤
- 處理硬件錯誤
- 調(diào)試
- 異常等級跳轉(zhuǎn)或者安全狀態(tài)切換
- 處理中斷(定時器或者外設間的交互)
- 執(zhí)行狀態(tài)切換(AArch64和AArch32),也就是interprocessing
當異常發(fā)生時,處理器將會停止當前程序的運行,跳轉(zhuǎn)到一段稱為異常處理函數(shù)的代碼來處理該異常,處理結(jié)束后將從exception handler返回到之前程序停止的地方繼續(xù)執(zhí)行。每一種異常類型都有屬于自己的異常處理函數(shù)。此外,ARM架構(gòu)將異常分為同步異常(synchronous exceptions)和異步異常(asynchronous exceptions)。
二,同步異常(synchronous exceptions)
同步異常用一句話概括就是:由處理器執(zhí)行指令所產(chǎn)生的異常。同步異常和當前處理器所執(zhí)行的指令流是同步的,因為正是處理器執(zhí)行了某條指令才導致異常發(fā)生。比如,MMU定義了某個內(nèi)存區(qū)間為只讀屬性,如果CPU對該區(qū)間某地址執(zhí)行了寫的指令(如STR),此時一個同步異常將會發(fā)生。
同步異常有如下特點:
- 同步異常是由于直接或試圖執(zhí)行指令而產(chǎn)生的。
- 處理異常后返回的地址(返回地址)與導致異常的指令之間的關系,是由arm體系結(jié)構(gòu)定義的。
- 同步異常也稱為 精準(precise)異常,因為我們可以知道具體是哪條指令導致的異常,可以精準地知道執(zhí)行了哪條指令之后系統(tǒng)狀態(tài)才開始發(fā)生變化,并且進入異常處理函數(shù)。
同步異常也分了很多種,并且也可能存在 執(zhí)行一條指令,導致多個同步異常的產(chǎn)生的情況,為了處理多個同步異常同時產(chǎn)生的情況,ARM架構(gòu)為每種同步異常規(guī)定了固定的優(yōu)先級來處理。
2.1 無效的指令和陷阱異常(Invalid instructions and trap exceptions)
無效的指令有很多種情況,比如:
- Cortex-A73支持的某些指令或者寄存器,A53不支持,對于A53來講就是無效的指令,如果執(zhí)行這樣的指令或者訪問不支持的寄存器,處理器將進入 未定義的異常(UNDEFINED Exception)。
- 有些指令或者寄存器只針對特定的異常等級有效,如果在其他的異常等級下執(zhí)行這樣的指令或者訪問這些寄存器,也將進入未定義的異常(UNDEFINED Exception)。
此外,ARM架構(gòu)還允許操作系統(tǒng)或者hypervisor為更低異常等級的某些操作(比如讀取一個特定的寄存器)設置陷阱。當處理器在這些更低異常等級執(zhí)行這些操作的時候,將會觸發(fā)一個異常。
例如,EL1上的操作系統(tǒng)內(nèi)核可能會在EL0上禁用使用浮點指令,以節(jié)省在應用程序之間進行上下文切換時的時間(浮點寄存器通常有128-bit的寬度,是普通通用寄存器的兩倍,上下文切換時,需要將所有GPR的值壓入?;蛘邚臈V谢謴偷紾PR)。這被稱為惰性上下文切換;例如,如果在上下文切換之前沒有使用SIMD/浮點(FP)單元(NEON),則出棧入棧的寄存器數(shù)量可以減少。所以,一般情況下,在應用程序跑的EL0異常等級下,禁用浮點指令,并且使用一個陷阱異常來處理邊緣情況(少數(shù)情況下,某些應用程序需要開啟浮點運算)。
在這種情況下,OS內(nèi)核可以通過禁用SIMD/FP單元來監(jiān)視SIMD/FP操作的狀態(tài)。當執(zhí)行FP或SIMD指令時,陷阱異常被帶到EL1的OS內(nèi)核。然后,內(nèi)核可以啟用SIMD/FP單元,以執(zhí)行之前失敗的指令,并設置一個標志,說明已經(jīng)使能了SIMD/FP單元。這確保了在下一個上下文交換中,將大型SIMD/FP寄存器文件包含在上下文切換的寄存器上下文中。如果在下一個上下文切換中沒有發(fā)現(xiàn)這個使能標志,則不需要包含SIMD/FP寄存器。
這種設置陷阱異常的能力對于虛擬化尤為重要。關于AArch64 的虛擬化操作,可以參考:AArch64 virtualization guide。
2.2 內(nèi)存訪問產(chǎn)生的異常
在訪問內(nèi)存時也會產(chǎn)生同步異常。可能的原因有:
- MMU使能后,MMU進行地址轉(zhuǎn)換時進行檢查產(chǎn)生的。
- 由內(nèi)存系統(tǒng)(比如DDR)返回的錯誤。
比如,將MMU使能后,使用Load或者store指令訪問內(nèi)存的操作將會受到MMU的檢查。比如對一些地址空間的內(nèi)存屬性設置了只讀,這時候CPU對該區(qū)域進行寫操作,還比如對一些地址空間設置了只有較高的異常等級才能去訪問(即該區(qū)域具有特權(quán)),此時若CPU處于非特權(quán)狀態(tài)去訪問該區(qū)域,MMU對這些操作都會進行檢查,并將這些操作攔截,然后觸發(fā)一個MMU 錯誤的異常。由于MMU產(chǎn)生的錯誤是同步的,所以該異常的產(chǎn)生將會在內(nèi)存訪問之前。
此外,內(nèi)存訪問也同樣會產(chǎn)生異步異常,比如SEerror。這個將會在下文中描述。
在AArch64中,同步的Data abort(數(shù)據(jù)訪問中止)將會產(chǎn)生一個同步異常,如果是異步的abort,將會產(chǎn)生一個SError中斷異常。
2.3 產(chǎn)生異常的指令
ARM架構(gòu)提供了一些顯式的指令,用于產(chǎn)生一個異常。這些指令用于實現(xiàn)系統(tǒng)調(diào)用接口,以允許低特權(quán)的軟件向高特權(quán)的軟件請求服務。這些方法有時被稱為系統(tǒng)調(diào)用,通常用于基于軟件的API。
ARM架構(gòu)包含三個異常產(chǎn)生的指令:SVC,HVC以及SMC。這三個指令僅僅是用于產(chǎn)生一個異常,并以此讓處理器在各個異常等級中切換:
-
Supervisor Call (SVC),SVC指令用于EL0切換到EL1,比如一個工作在EL0的用戶程序,可以使用SVC指令,請求一個工作在EL1操作系統(tǒng)的服務。
-
Hypervisor Call (HVC),如果虛擬化在當前架構(gòu)中被實現(xiàn),比如在虛擬機程序中,工作在EL1的操作系統(tǒng)可以通過HVC指令,向工作在EL2的hypervisor請求服務,可以進行不同的OS間的切換。
-
Secure Monitor Call (SMC),如果安全擴展功能在當前架構(gòu)中被實現(xiàn),則程序如果想要在安全世界(secure world)和非安全世界(normal world,non-secure world)切換,需要通過使用SMC指令來實現(xiàn)。
-
此外,異常等級切換(低等級向高等級切換)需要遵守如下圖規(guī)則:
如果當前處理器處于EL0,則它不能直接使用HVC或者SMC,切換到更高等級。只能使用HVC指令先進入EL1,才可以切換到其他更高的異常等級。
由于異常無法向更低等級切換,換句話說,如果是由高等級切到低等級,而是使用ERET指令??偨Y(jié)如下: -
系統(tǒng)調(diào)用(System Call),切換(SVC,HVC,SMC)到更高等級。
-
返回(ERET,exception return)到更低等級。
所以,如果處于EL2的處理器執(zhí)行SVC指令是不會切到EL1的。
2.4 調(diào)試異常 Debug exceptions
調(diào)試異常也是同步異常,該異常會被路由到當前調(diào)試器所處的異常等級。然后,調(diào)試器代碼執(zhí)行得很像異常處理程序代碼一樣。調(diào)試異常有很多種,包括如下:
- Breakpoint Instruction exceptions
- Breakpoint exceptions
- Watchpoint exceptions
- Vector Catch exceptions
- Software Step exceptions
關于調(diào)試異常的具體介紹,可以查看AArch64 self-hosted debug guide。
三, 異步異常 Asynchronous exceptions
上文我們提到過,同步異常是由于處理器執(zhí)行指令觸發(fā)的異常,而有些異常是產(chǎn)生于處理器外部,因此無法和CPU當前執(zhí)行的指令流同步,這種和CPU當前執(zhí)行的指令沒有直接關系的異常,我們稱為異步異常(Asynchronous exceptions),并且這種異常一般是由CPU外部的系統(tǒng)事件產(chǎn)生的,這可能是軟件需要響應的系統(tǒng)事件,如計時器的活動或屏幕的觸摸,但是我們并不知道它們什么時候會發(fā)生。
根據(jù)定義,如果一個異常不是同步的,則該異常是異步異常。中斷(interrupts)實際上就是異步異常的一種。
在發(fā)生異步異常時,程序流將被中斷,并傳遞給handler以專門處理此外部請求。因為不可能準確地保證何時會發(fā)生異步異常,所以AArch64架構(gòu)只要求它在有限的時間內(nèi)發(fā)生。
3.1 物理中斷 Physical interrupts
物理中斷是指為了響應來自處理器外部的信號而產(chǎn)生的中斷,通常是由外圍設備產(chǎn)生的。處理器并不需要主動地不斷輪詢這種外部信號,系統(tǒng)會通過產(chǎn)生中斷的方式來通知處理器,處理器收到中斷后,會進入相應的中斷處理函數(shù)以響應該信號。
比如,系統(tǒng)中可能使用UART(Universal Asynchronous Receiver/Transmitter)連接CPU,并與外界通信。當UART收到數(shù)據(jù)后,系統(tǒng)中需要有個機制來告訴CPU,UART收到新數(shù)據(jù)了,CPU這邊需要接收并處理這些數(shù)據(jù)。其中一種機制就是中斷:UART可以產(chǎn)生一個中斷來通知CPU有新數(shù)據(jù)需要處理。
在復雜的SOC系統(tǒng)中,可能有非常多的帶有優(yōu)先級的中斷源,并且包含中斷嵌套的能力:在處理較低優(yōu)先級的中斷時,更高優(yōu)先級的中斷可以打斷低優(yōu)先級中斷的處理,在處理完高優(yōu)先級中斷后,再接著處理低優(yōu)先級中斷。
處理器在處理這種嵌套中斷的響應速度可能是系統(tǒng)設計中的一個關鍵問題,這被稱為中斷延遲(interrupt latency)。
3.2 系統(tǒng)錯誤異常 SError
當內(nèi)存系統(tǒng)響應了一些不正常的事件,并反饋給處理器,將會產(chǎn)生一種 SError的異步異常。在正常操作流程中,我們不希望有這種異常發(fā)生,但是處理器有必要知道它的一些和內(nèi)存系統(tǒng)的交互是否觸發(fā)了這種異常。這種異常的上報是異步的,導致這種異常發(fā)生的指令(比如讀寫內(nèi)存系統(tǒng)的指令,load/store)可能已經(jīng)執(zhí)行完了。
一種典型的SError異常就是來自外部的異步中止,比如有:
- 內(nèi)存總線上的錯誤,處理器發(fā)出一個訪問內(nèi)存的請求(比如讀寫),首先會經(jīng)過MMU的檢查(假設MMU被使能),MMU檢查通過后,會傳到內(nèi)存總線上,在內(nèi)存總線上遇到的錯誤。
- 在一些RAM上進行 奇偶檢驗或者錯誤糾正(Parity or Error Correction Code (ECC)時發(fā)生的錯誤,比如一些內(nèi)置的cache。
- 將臟數(shù)據(jù)由cache line中寫回到外部內(nèi)存系統(tǒng)中發(fā)生的錯誤。
SErrors被視為一個單獨的異步異常類型,因此用戶代碼通常對這些情況有單獨的處理程序。并且SError異常的產(chǎn)生是由具體的架構(gòu)實現(xiàn)來定義的。
3.3 IRQ和FIQ 中斷
Arm體系結(jié)構(gòu)有兩種異步異常類型:IRQ和FIQ,旨在用于支持外圍設備中斷的處理。處理器會引出兩個信號線:IRQ和FIQ,這些是用于傳入外部事件,如設置的計時器(timer)超時了,這并不代表系統(tǒng)錯誤。IRQ和FIQ是外部信號與處理器的指令流之間的,雖然是異步的,但是預期的事件。IRQ和FIQ有獨立的路由控制,通常用于實現(xiàn)安全和非安全中斷。關于這兩種中斷類型的具體使用方式,由具體的架構(gòu)實現(xiàn)來定義。
在老版本的Arm體系結(jié)構(gòu)中,F(xiàn)IQ被用作更高優(yōu)先級的快速中斷。這與AArch64不同,在AArch64中,F(xiàn)IQ與IRQ具有相同的優(yōu)先級。
在幾乎所有的情況下,中斷控制器(Generic Interrupt Controler)都與系統(tǒng)中的AArch64處理器成對使用,GIC用于收集、優(yōu)先級排序和處理所有要發(fā)送到處理器的中斷。所有的ARM架構(gòu)實現(xiàn)都使用(GIC)架構(gòu)來管理IRQs和FIQs。GIC執(zhí)行中斷管理、優(yōu)先級和路由任務,為每個物理中斷類型都提供一個中斷號。詳情可以參考 Arm Generic Interrupt Controller v3 and v4 guide。
3.4 虛擬中斷 Virtual interrupts
如果系統(tǒng)中實現(xiàn)了虛擬化,則對中斷處理有了更復雜的需求,一些中斷可能由hypervisor處理,有些中斷可以在VM(虛擬機程序)中處理。被虛擬機程序看到的中斷就是虛擬中斷。虛擬中斷可以由軟件產(chǎn)生,也可以由連接到中斷控制器的外設產(chǎn)生。因此想要支持虛擬中斷,必須要額外的機制,在AArch64架構(gòu)中支持以下虛擬中斷:文章來源:http://www.zghlxwxcb.cn/news/detail-810926.html
- vSError, Virtual System Error
- vIRQ, Virtual IRQ
- vFIQ, Virtual FIQ
虛擬中斷按照其中斷類型被控制,這些虛擬中斷的功能與物理對應的中斷相同,但是它們只能向EL1發(fā)出信號。虛擬中斷可以從EL2處的虛擬機管理程序或使用中斷控制器生成。hypervisor必須在Hypervisor Configuration Register(HCR_EL2)中設置相應的控制bit。例如,要啟用vIRQ信令,系統(tǒng)管理程序必須設置HCR_EL2的IMO位,此設置將物理IRQ異常路由到EL2,并允許將虛擬中斷信號發(fā)送到EL1。
在寄存器 HCR_EL2中,有三個bit來控制虛擬中斷的產(chǎn)生: - VSE,設置這個bit將注冊一個 vSError.
- VI ,設置這個bit將注冊一個vIRQ.
- VF, 設置這個bit將注冊一個 vFIQ.
設置其中一個bit相當于一個中斷控制器斷言了其中一個虛擬中斷信號到一個虛擬CPU。該方法的一個影響是:需要hypervisor來模擬虛擬機程序中的中斷控制器的操作。當頻繁地這樣操作時,這可能會導致系統(tǒng)大量的開銷,因此建議直接使用中斷控制器模擬一個虛擬中斷。
GICv2及更高版本提供物理CPU接口和虛擬CPU接口,并且同時支持物理中斷和虛擬中斷。關于GIC的詳細使用可以參考: Arm
Generic Interrupt Controller v3 and v4.。關于虛擬中斷,參考 AArch64 virtualization guide。
四,異常屏蔽(Masking)
對于異步異常:可以暫時屏蔽物理和虛擬的異步異常。這意味著異步異??梢员3衷趻炱馉顟B(tài),直到它們被停止屏蔽,并被處理器接收異常。這對于處理嵌套的異常特別有用。
對于同步異常:ARM架構(gòu)無法屏蔽同步異常。這是因為同步異常是由指令的執(zhí)行直接引起的,因此,如果它們被擱置或忽略,將會阻塞CPU的執(zhí)行。
在2021年的擴展中,Armv8.8-A和Armv9.3-A增加了不可屏蔽的中斷(Non-maskable interrupt,NMI)支持。當NMI被支持和啟用時,可以通過該功能將具有超優(yōu)先級(Superpriority)的中斷發(fā)送給給處理器。文章來源地址http://www.zghlxwxcb.cn/news/detail-810926.html
到了這里,關于ARMv8-AArch64 的異常處理模型詳解之異常類型 Exception types的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!