ARMv8-A Exception Handling

本節來詳細描述下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

Tags: