ARMv8-A Exception Handling
- 2020 年 4 月 9 日
- 筆記
本節來詳細描述下ARMv8-A下的異常處理。
異常的概述:
當設備正在愉快的執行時候,此時發生了一個異常,處理器就必須暫停當前的任務,轉而去處理髮生的異常。當異常處理完畢後,處理就會發回到被打斷的程式繼續執行。

此圖就是描述異常的處理流程。
異常的分類:
異常分為同步異常和非同步異常,而這兩種異常的區別是:
同步異常:如果異常的產生是通過執行指令,而且返回結果提供了產生異常的詳細資訊,則此類異常稱為同步異常(Synchronous Exceprions)
非同步異常:如果異常不是通過指令執行產生,而且返回結果不提供產線異常的詳細資訊,則此類異常為非同步異常(asynchronous exceptions)
非同步異常(Asynchronous Exceptions)
- 中斷
- IRQ(normal priority interrupt)
- FIQ(Fast Interrupt)
- 其中FIQ的優先順序比IRQ的優先順序高。這兩種中斷類型都是通過pin腳連接到中斷控制器(GIC),當外部觸發一個中斷的時候,中斷控制器就會仲裁和轉發此中斷到相應的CPU
- SError(System Error)
同步異常(Synchronous Exceptions)
同步異常包含的類型很多,比如第一次申請記憶體,當去寫或者讀的時候就會觸發一次page fault,此類異常就屬於同步異常

- 從MMU發出的指令abort。 當cpu嘗試去執行一塊沒有執行許可權的記憶體區域,則會發生指令abort
- 從MMU發出的數據abort。 當cpu嘗試去寫一塊只讀的記憶體區域,則會發生data abort
- SP和PC的對齊檢查
- 同步外部異常
- 未分配的異常:當第一次訪問申請記憶體時,cpu會通過mmu去尋找虛擬地址對應的物理地址,而此時因為此虛擬地址沒有對應的物理地址,則會發生未分配的異常
- debug時發生的異常
- Services Calls(SVC,SMC,HVC)
SVC,HVC,SMC指令
- SVC(Supervisor Call)
- 當用戶空間通過系統調用陷入到內核空間的時候,則最終會通過SVC指令進入到內核空間
- HVC(Hypervisor Call)
- 當在ARMv8-A架構下,normal world, EL1嘗試去訪問EL2的時候,則會陷入到虛擬化層的,其中是通過HVC指令
- SMC(Secure Moniter Call)
- 用於切換noramal world 和 secure world使用。
異常處理涉及到的暫存器
我們先來看下異常處理的整個過程

當異常發生時,硬體會自動做如下幾個步驟:
- 將PSTATE的狀態保存到SPSR_ELn暫存器中,根據當前所在的EL級別來保存到對應的SPSR暫存器中。如果當前的異常發生在EL1,則將PSTATE的狀態保存到SPSR_EL1中
- 將PC的值存儲在ELR_ELn暫存器中,也是根據當前所處的EL級別。
- 以上兩個步驟是硬體自動保存的,不需要軟體的干涉
- 假設當前是一個IRQ中斷,軟體上也會保存一些現場的。
- 當異常處理完畢後,則會將之前保存的值恢復
- SPSR_ELn的值恢復到PSTATE中,ELR_ELn恢復到PC
PSTATE,SPSR已經在上一篇文章Process Status中做過描述了,此處不做說明了。
當一個異常發生時,如果確定異常的類型
當異常發生時,我們可以從ESR_ELn暫存器中獲取對應的異常狀態。

- Bits[31:26] 用來確定異常的類型,Exception class
- Bit[25]: 用來確定異常指令的長度,0代表16位異常指令,1代表32位異常
- Bits[24:0]: 用來確定具體的異常,每種異常類型獨立定義此欄位
比如:

ARM64 異常向量表


這四組在EL1的實現為,也就是linux內核的實現為:
424/* 425 * Exception vectors. 426 */ 427 .pushsection ".entry.text", "ax" 428 429 .align 11 430ENTRY(vectors) 431 kernel_ventry 1, sync_invalid // Synchronous EL1t 432 kernel_ventry 1, irq_invalid // IRQ EL1t 433 kernel_ventry 1, fiq_invalid // FIQ EL1t 434 kernel_ventry 1, error_invalid // Error EL1t 435 436 kernel_ventry 1, sync // Synchronous EL1h 437 kernel_ventry 1, irq // IRQ EL1h 438 kernel_ventry 1, fiq_invalid // FIQ EL1h 439 kernel_ventry 1, error // Error EL1h 440 441 kernel_ventry 0, sync // Synchronous 64-bit EL0 442 kernel_ventry 0, irq // IRQ 64-bit EL0 443 kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0 444 kernel_ventry 0, error // Error 64-bit EL0 445 446#ifdef CONFIG_COMPAT 447 kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0 448 kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0 449 kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0 450 kernel_ventry 0, error_compat, 32 // Error 32-bit EL0 451#else 452 kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0 453 kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0 454 kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0 455 kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0 456#endif 457END(vectors)
分為四組,其中第一組是invaild的。
- 如果異常級別是EL1,也就是Current EL,則偏移地址為0x200
- 如果異常類型是64位應用程式,異常級別是EL0發生,則偏移地址為0x400
- 如果異常類型是32位應用程式,異常級別是EL0發生,則偏移地址為0x600

