ARMv8 異常處理簡介

內核穩定性問題複雜多樣,最多見的莫過於「kernel panic」,意爲「內核恐慌,不知所措」。這種狀況下系統天然沒法正常運轉,只能自我結束生命,留下死亡信息。諸如:linux


「Unable to handle kernel XXX at virtual address XXX」編程


「undefined instruction XXX」數組


「Bad mode in Error handler detected on CPUX, code 0xbe000011 -- SError」安全

......微信


這些死亡信息是系統在什麼狀態下產生?如何產生?以及如何處理?本文主要從這三個方面介紹ARMv8架構下CPU的異常處理流程。架構



1、ARMv8異常簡介app



1.異常級別異步


不一樣於Armv7架構採用CPU模式切換的方式進行異常處理,Armv8架構定義了一組全新的異常級別進行異常處理,即EL0至EL3,有以下特性:函數


  • 若是ELn爲異常級別,則n的值增長表示軟件執行特權增長。oop


  • EL0處的執行稱爲無特權執行,不能處理異常。


  • EL2提供對虛擬化的支持。


  • EL3提供了在兩個安全狀態(安全狀態和非安全狀態)之間切換的支持。


一個實現能夠不包括全部的異常級別,但都必須包括EL0和EL1。EL2和EL3是可選的。


以下是典型的異常級別使用模型:




2. 同步異常和異步異常


若是知足如下全部條件,則將異常描述爲同步的:


  • 因爲直接執行某個指令而產生異常。


  • 異常處理程序的返回地址能夠代表致使該異常的指令。


  • 異常是精確的。


若是知足如下任一條件,則將異常描述爲異步的:


  • 不是由於直接執行某條指令而產生異常。


  • 異常處理程序的返回地址不能夠代表致使該異常的指令。


  • 異常是不精確的。



3. 主要寄存器


(1)通用寄存器R0-R30


在基本指令集處理指令時,將使用通用寄存器組。它包括31個通用寄存器R0-R30。這些寄存器能夠做爲31個64位寄存器X0-X30或31個32位寄存器W0-W30進行訪問。


(2)堆棧指針寄存器SP


在AArch64狀態下,除了通用寄存器外,還爲如下每一個異常級別實現了專用的堆棧指針寄存器,


堆棧指針寄存器爲:


  • SP_EL0和SP_EL1。


  • 若是實現包括EL2,則爲SP_EL2。


  • 若是實現包括EL3,則爲SP_EL3。


堆棧指針寄存器選擇:


在EL0上執行時,處理器使用EL0堆棧指針SP_EL0。在其餘任何異常級別執行時,能夠將處理器配置爲使用SP_EL0或配置爲對應該異常級別的堆棧指針SP_ELx。默認狀況下,採用目標異常級別的堆棧指針SP_ELx。例如,EL1的異常選擇SP_EL1,軟件能夠在目標異常級別執行的時候經過更新PSTATE.SP來指向SP_EL0的堆棧指針。


能夠經過異常級別的堆棧指針後綴代表所選的堆棧指針:


t代表使用SP_EL0堆棧指針。


h代表使用SP_ELx堆棧指針。


t和h後綴基於線程(thread)和處理程序(handler)的首字母。



(3)保存的程序狀態寄存器SPSR


保存的程序狀態寄存器SPSR(Saved Program Status Registers)用於在發生異常時保存處理器狀態。在AArch64狀態下,每一個異常級別都有一個SPSR:


  • SPSR_EL1,發生在EL1的異常。


  • 若是實現了EL2,則爲SPSR_EL2,發生在EL2的異常。


  • 若是實現了EL3,則爲SPSR_EL3,發生在EL3的異常。


注:EL0不能處理異常。


當處理器發生異常時,會將處理器狀態從SPSTATE中的PSTATE(Process state)保存到對應異常級別的SPSR。例如,若是異常發生在EL1,則將處理器狀態保存在SPSR_EL1中。


保存處理器狀態意味着異常處理程序能夠:


  • 從異常返回時,將處理器狀態恢復到SPSR中存儲的異常級別的狀態。例如,異常處理程序從EL1返回時,處理器狀態恢復到存儲在SPSR_EL1中的狀態。


  • 檢查發生異常時PSTATE的值,肯定引發異常指令的當前執行狀態和異常級別,例如,當前執行狀態是AArch64仍是AArch32等。


(4)異常連接寄存器(ELR)


異常連接寄存器ELR(Exception Link Registers)包含異常返回地址。當處理器發生異常時,返回地址將保存在異常級別對應的ELR中。例如,當處理器將異常處理交給EL1處理時,會將異常返回地址保存在ELR_EL1中。在異常返回時,PC恢復到存儲在ELR中的地址。例如,從EL1返回時,PC將恢復到ELR_EL1中存儲的地址。

AArch64狀態爲每一個異常級別都提供了ELR寄存器:


  • ELR_EL1,用於EL1的異常。


  • 若是實現了EL2,ELR_EL2用於EL2的異常。


  • 若是實現了EL3,ELR_EL3用於EL3的異常。


(5)ESR(Exception Syndrome Register)


異常綜合表徵寄存器ESR_ELn包含的異常信息用以異常處理程序肯定異常緣由。僅針對同步異常和SError進行更新。由於IRQ或FIQ中斷處理程序從通用中斷控制器(GIC)寄存器的信息獲取狀態。


  • ESR_ELn的BIT[31:26]指示處理程序執行對應的異常,好比:


    EC == 0b100010,PC alignment fault exception. 


    EC == 0b100101,Data Abort taken without a change in Exception level.


    EC == 0b101111,SError interrupt.


  • 位[25]表示被捕獲指令的長度(0爲16位指令,1爲32位指令)


  • 位[24:0]構成ISS域(Instruction Specific Syndrome),根據EC域指定的不一樣異常類型,ISS有不用的解釋。有:


    ISS encoding for an exception from an Instruction Abort


    ISS encoding for an exception from a Data Abort


    ISS encoding for an SError interrupt


    ISS encoding for an exception from a WFI or WFE instruction.

    等等。


以 Data Abort爲例,ISS解讀以下:



BIT[5:0] DFSC(Data Fault Status Code)解釋了data abort發生的狀態信息:


*其餘bit位解釋能夠參考ARM v8手冊<DDI0487F_a_armv8_arm>第10.2.6章節



4.異常入口


每一個異常都有特定的異常級別。異常所對應的異常級別是由軟件編程決定,或者由異常自身性質決定的。在任何狀況下,異常執行時都不會移至較低的異常級別。異常入口的基本執行內容是:


  • 處理器狀態保存到目標異常級別的SPSR_ELx中。


  • 返回地址保存到目標異常級別的ELR_ELx中。


  • 若是異常是同步異常或SError中斷,異常的表徵信息將保存在目標異常級別的ESR_ELx中。


  • 若是是指令止異常(Instruction Abort exception),數據停止異常(Data Abort exception,),PC對齊錯誤異常(PC alignment fault exception),故障的虛擬地址將保存在FAR_ELx中。


  • 堆棧指針保存到目標異常級別的專用堆棧指針寄存器SP_ELx。


  • 執行移至目標異常級別,並從異常向量定義的地址開始執行。



2、異常處理流程



1.異常向量表


當發生異常時,處理器必須執行與之對應的處理程序。處理程序在內存中的存儲位置稱爲異常向量。在ARM體系結構中,異常向量存儲在一個表中,該表稱爲異常向量表。每一個異常級別都有其本身的向量表,即EL3,EL2和EL1都有一個,該表包含要執行的指令。


每一個表佔128個字節,能夠保存32條指令(arm64的指令長度也是4字節),以linux kernel-4.19/arch/arm64/kernel/entry.S爲例,異常向量表的入口以下圖,一共有4組16個表:


用另一張表能夠更好理解這個異常向量表的入口:



好比當前代碼運行在內核空間,發生了data abort,異常向量表的入口地址就是0x200。



2.kernel_ventry


異常發生後,處理器從對應的異常向量表入口地址開始執行,第一條指令是kernel_ventry。kernel_ventry是一個宏定義,先檢查棧空間是否有溢出,而後跳轉到指定的異常處理標籤。


如下以EL1發生data abort異常爲例介紹異常處理流程。


EL1發生data abort異常後進入對應的異常向量表入口,先檢查棧是否有溢出,而後跳轉至:el1_sync(data abort屬於同步異常)。



3.elx_sync


(1)保存現場


el1_sync第一條指令執行kernel_entry 1。kernel_entry也是一個宏定義,首先將CPU寄存器保存到棧空間,由於這些寄存器接下來會被覆蓋使用。爲了保證kernel_exit時能恢復準確的現場,這裏有必要對第一現場先作保存。


其次設置棧幀大小S_FRAM_SIZE,S_FRAM_SIZE根據pt_regs結構體大小而設定。


pt_regs結構體:


另外就是讀取elr_el1和spsr_el1等寄存器值。總之,kernel_entry主要將CPU寄存器按照pt_regs結構體的定義將異常第一現場保存到棧上。


(2)判斷異常類型


kernel_entry保存完第一現場以後,接下來讀取esr_el1寄存器的值,並判斷異常的具體類型。如2.3.5章節所描述的ESR寄存器定義,ESR包含的異常信息主要用於異常處理程序肯定異常緣由,其中ESR_ELn的BIT[31:26] EC域指示處理程序執行的對應異常類型。


發生DataAbort時,EC = 0b100101,即ESR_ELx_EC_DABT_CUR=0x25,el1_syn將跳轉至el1_da。


ESR_ELx_EC_DABT_CUR定義在/kernel/msm-4.19/arch/arm64/include/asm/esr.h。


除此以外,還有其餘的同步異常類型,好比:


(3)跳轉至異常類型標籤


經過esr_el1寄存器值肯定同步異常的具體類型後,跳轉至對應的異常處理標籤el1_da。el1_da第一條指令,mrs x3,far_el1,將far_el1保存到x3。在2.4異常入口章節介紹過若是發生數據停止異常(DataAbort exception),故障的虛擬地址將保存在FAR_ELx中。這裏就是首先將data abort異常發生的虛擬地址第一時間取出,保持在x3中。


el1_da 跳轉到異常處理程序do_mem_abort以前,爲其設置好了三個入參:


  • x0:產生DataAbort的故障虛擬地址。


  • x1:esr_el1,異常綜合表徵寄存器值,在el1_sync第二條指令已經保存。


  • x2:stack frame 地址,即pt_regs結構體的首地址,在kernel_entry已對其填充。


x0~x2分別對應do_mem_abort函數的三個參數:addr,esr,*regs。


(4)跳轉至異常處理程序


do_mem_abort 函數位於/kernel/msm-4.19/arch/arm64/mm/fault.c


do_mem_abort首先根據esr寄存器獲取data abort fault_info。在2.3.5章節介紹了ESR寄存器BIT[24:0]的ISS域,它記錄了data abort的具體類型。這裏將用到ISS域,也就是fault_info中的name變量。咱們一般看到的「do_page_fault」、「do_translation_fault」等data abort下細分的調用棧就是由這裏的ISS域區分而來。


fault_info結構體:


Fault_info[]數組:


fault_info 數組中對應的處理函數對當前的異常進一步處理,若是發現當前的data abort確實是屬於非法,沒法處理的,好比paging request 非法地址,就會拋出異常信息,並走到panic流程。


最後,調用arm64_notify_die,若是是用戶空間發生data abort,輸出異常信息和發送signal給當前進程。若是是異常發生在內核空間,走die流程。


die函數最終可能會調用到panic。但die函數也不是必定會走到panic,它先是走oops流程告警系統如今的異常,若是異常發生在中斷上下文,走panic。或者若是設定了CONFIG_PANIC_ON_OOPS_VALUE=y,不管是否在中斷上下文均走panic。


若是這次異常並無走到panic流程,那麼系統仍是要繼續運行,拋出oops警告後系統如何恢復異常發生前的環境?回到el1_da處理指令,do_mem_abort執行完若是不須要panic,跳轉到kernel_exit進行異常退出處理。



4.kernel_exit


kernel_exit恢復現場。主要恢復kernel_entry保存在棧上的處理器相關寄存器等。至此發生在el1級別的data baort異常處理流程分析結束。



參考資料



[1]《DDI0487F_a_armv8_arm.pdf》


[2]《DEN0024A_v8_architecture_PG.pdf》


本文分享自微信公衆號 - 人人都是極客(rrgeek)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索