我們在編寫裸機程序(baremetal)、虛擬化管理程序(hypervisor)和操作系統(OS)時,Debug分析程序是必不可少的。不像linux內核,有大量的調試方法,很多裸機程序、hypervisor沒有完善的調試分析方法。
異常相關寄存器
但也不是無計可施,在硬件上,ARM架構為程序的異常行為提供了詳細的寄存器:
ESR_ELx寄存器(x=1,2,3)
保存發生異常時的特征,比如異常分類(ESR_ELx.EC)、異常具體原因(ESR_ELx.ISS)等。
ELR_ELx寄存器(x=1,2,3)
保存發生異常時,保存要返回的地址,一般情況下就是發生異常時的指令地址。
FAR_ELx寄存器(x=1,2,3)
保存錯誤地址。
HPFAR_EL2寄存器
保存stage-2階段的地址轉換發生的錯誤IPA地址。
ARM從硬件架構上設計了4層異常級:EL0、EL1、EL2和EL3。不同特權等級的程序,運行在不同的異常級上。本文從hypervisor虛擬機管理程序的角度,講解如何利用這些寄存器,對程序的異常情況進行分析。
hypervisor本身的abort異常
我們以meta-hypervisor出現的具體異常為例:
esr_el2=0x97010046 elr_el2=0xfd8000005880 far_el2=0xfd8000005880
在這兒,esr_el2的值為0x97010046,對應的位域為:
EC | IL | ISS |
---|---|---|
100101 | 1 | 1_0000_0001_0000_0000_0100_0110 |
EC = 100101:
說明是數據abort異常,但是沒有發生異常級改變(EL2);或者,當支持嵌套虛擬化時與VNCR_EL2相關的訪問產生的數據abort異常。
ISS編碼(數據abort異常的具體原因)
ISV | SAS | SSE | SRT | SF | AR | VNCR | LST | FnV | EA | CM | S1PTW | WnR | DFSC |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
24 | 23-22 | 21 | 20-16 | 15 | 14 | 13 | 12-11 | 10 | 9 | 8 | 7 | 6 | 5-0 |
1 | 00 | 0 | 00001 | 0 | 0 | 0 | 00 | 0 | 0 | 0 | 0 | 1 | 000110 |
通過上面各個位域的信息,綜合得出:把W1寄存器中一個字節的數據寫入內存時發生的錯誤。
我們再來看匯編代碼中,0xfd8000005880地址處的內容:
void *memset(void *dest, uint32_t c, uint32_t count) { // ......省略 *d = c; fd8000005874: b94007e0 ldr w0, [sp, #4] fd8000005878: 12001c01 and w1, w0, #0xff fd800000587c: f9400fe0 ldr x0, [sp, #24] fd8000005880: 39000001 strb w1, [x0] d++; fd8000005884: f9400fe0 ldr x0, [sp, #24] fd8000005888: 91000400 add x0, x0, #0x1 fd800000588c: f9000fe0 str x0, [sp, #24] }
代碼中,fd8000005880: 39000001 strb w1, [x0]確實是往x0寄存器中的地址寫入一個字節。這正好與我們對異常原因分析的結果相同。說明異常正是memset函數發生的錯誤。
ISV: 1, 說明23-14位保存著合法指令的異常信息
SAS: 00, 說明訪問字節數據時產生的錯誤
SSE: 0, 字節訪問不要求符號擴展
SRT: 00001,錯誤指令的Wt/Xt/Rt操作數的寄存器編號
SF: 0, 指令訪問的是32位通用寄存器
AR: 0, 指令沒有aquire/release語義
VNCR:0, 保留
LST: 00, 產生abort異常的指令未指定
FnV: 0, FAR寄存器是合法的
EA: 0, 表示不是外部abort
CM: 0, 表示錯誤不是由cache維護指令產生的
S1PTW: 0, 表示不是stage-2錯誤
WnR: 0, 表示寫內存
DFSC:000110,L2地址翻譯錯誤
如果memset函數只有一處調用的話,Bug原因結合代碼就很容易分析出來了。但是,我們自己編寫的hypervisor中有很多處調用memset函數的地方。所以,就文中的bug示例而言,目前還不能分析出原因。所以,我們需要使用qemu模擬器,通過gdb進行單步調試,看看出問題的代碼位置(參見下一篇《QEMU+GDB調試ARM程序》)。
Guest OS的abort異常
我們設計的hypervisor支持Guest OS觸發的4類異常,具體定義如下:
abort_handler_tabort_handlers[64]= { [ESR_EC_DALEL]=aborts_data_lower, [ESR_EC_SMC64]=smc64_handler, [ESR_EC_SYSRG]=sysreg_handler, [ESR_EC_HVC64]=hvc64_handler };
ESR_EC_HVC64 = 0x16:用于處理Guest OS發起的HVC調用,我們設計使用HVC指令在VM之間建立通信。
ESR_EC_SMC64 = 0x17:用于處理Guest OS發起的SMC調用,我們知道ARM規定了PSCI規范,通過將電源管理等代碼在ATF代碼中實現,這樣就實現了資源的安全管理。PSCI規范的底層就是通過SMC指令實現的。hypervisor需要將Guest OS發起的虛擬核的PSCI調用轉發給物理核。
ESR_EC_SYSRG = 0x18:模擬寄存器和外設。因為Guest OS需要訪問一些特殊寄存器和外設,而外設有時候需要多個VM共享,hypervisor對其進行模擬。
ESR_EC_DALEL = 0x24:用于處理Guest OS發生的abort異常。比如,Guest OS訪問我們未指定的物理內存。
對于ESR_EL2寄存器的分析,與前面的一段一樣,不在具體詳述。
而HPFAR_EL2寄存器保存著出錯的IPA地址,通過該地址,我們就可以知道,Guest OS訪問哪塊內存出錯,就能解決某些bug了。
審核編輯:湯梓紅
-
ARM
+關注
關注
134文章
9084瀏覽量
367386 -
寄存器
+關注
關注
31文章
5336瀏覽量
120230 -
Linux
+關注
關注
87文章
11292瀏覽量
209328
原文標題:ARM深入理解-hypervisor調試方法一(異常寄存器分析)
文章出處:【微信號:嵌入式ARM和Linux,微信公眾號:嵌入式ARM和Linux】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論