我們?cè)诰帉?xiě)裸機(jī)程序(baremetal)、虛擬化管理程序(hypervisor)和操作系統(tǒng)(OS)時(shí),Debug分析程序是必不可少的。不像linux內(nèi)核,有大量的調(diào)試方法,很多裸機(jī)程序、hypervisor沒(méi)有完善的調(diào)試分析方法。
異常相關(guān)寄存器
但也不是無(wú)計(jì)可施,在硬件上,ARM架構(gòu)為程序的異常行為提供了詳細(xì)的寄存器:
ESR_ELx寄存器(x=1,2,3)
保存發(fā)生異常時(shí)的特征,比如異常分類(lèi)(ESR_ELx.EC)、異常具體原因(ESR_ELx.ISS)等。
ELR_ELx寄存器(x=1,2,3)
保存發(fā)生異常時(shí),保存要返回的地址,一般情況下就是發(fā)生異常時(shí)的指令地址。
FAR_ELx寄存器(x=1,2,3)
保存錯(cuò)誤地址。
HPFAR_EL2寄存器
保存stage-2階段的地址轉(zhuǎn)換發(fā)生的錯(cuò)誤IPA地址。
ARM從硬件架構(gòu)上設(shè)計(jì)了4層異常級(jí):EL0、EL1、EL2和EL3。不同特權(quán)等級(jí)的程序,運(yùn)行在不同的異常級(jí)上。本文從hypervisor虛擬機(jī)管理程序的角度,講解如何利用這些寄存器,對(duì)程序的異常情況進(jìn)行分析。
hypervisor本身的abort異常
我們以meta-hypervisor出現(xiàn)的具體異常為例:
esr_el2=0x97010046 elr_el2=0xfd8000005880 far_el2=0xfd8000005880
在這兒,esr_el2的值為0x97010046,對(duì)應(yīng)的位域?yàn)椋?/p>
EC | IL | ISS |
---|---|---|
100101 | 1 | 1_0000_0001_0000_0000_0100_0110 |
EC = 100101:
說(shuō)明是數(shù)據(jù)abort異常,但是沒(méi)有發(fā)生異常級(jí)改變(EL2);或者,當(dāng)支持嵌套虛擬化時(shí)與VNCR_EL2相關(guān)的訪問(wèn)產(chǎn)生的數(shù)據(jù)abort異常。
ISS編碼(數(shù)據(jù)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 |
通過(guò)上面各個(gè)位域的信息,綜合得出:把W1寄存器中一個(gè)字節(jié)的數(shù)據(jù)寫(xiě)入內(nèi)存時(shí)發(fā)生的錯(cuò)誤。
我們?cè)賮?lái)看匯編代碼中,0xfd8000005880地址處的內(nèi)容:
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]確實(shí)是往x0寄存器中的地址寫(xiě)入一個(gè)字節(jié)。這正好與我們對(duì)異常原因分析的結(jié)果相同。說(shuō)明異常正是memset函數(shù)發(fā)生的錯(cuò)誤。
ISV: 1, 說(shuō)明23-14位保存著合法指令的異常信息
SAS: 00, 說(shuō)明訪問(wèn)字節(jié)數(shù)據(jù)時(shí)產(chǎn)生的錯(cuò)誤
SSE: 0, 字節(jié)訪問(wèn)不要求符號(hào)擴(kuò)展
SRT: 00001,錯(cuò)誤指令的Wt/Xt/Rt操作數(shù)的寄存器編號(hào)
SF: 0, 指令訪問(wèn)的是32位通用寄存器
AR: 0, 指令沒(méi)有aquire/release語(yǔ)義
VNCR:0, 保留
LST: 00, 產(chǎn)生abort異常的指令未指定
FnV: 0, FAR寄存器是合法的
EA: 0, 表示不是外部abort
CM: 0, 表示錯(cuò)誤不是由cache維護(hù)指令產(chǎn)生的
S1PTW: 0, 表示不是stage-2錯(cuò)誤
WnR: 0, 表示寫(xiě)內(nèi)存
DFSC:000110,L2地址翻譯錯(cuò)誤
如果memset函數(shù)只有一處調(diào)用的話,Bug原因結(jié)合代碼就很容易分析出來(lái)了。但是,我們自己編寫(xiě)的hypervisor中有很多處調(diào)用memset函數(shù)的地方。所以,就文中的bug示例而言,目前還不能分析出原因。所以,我們需要使用qemu模擬器,通過(guò)gdb進(jìn)行單步調(diào)試,看看出問(wèn)題的代碼位置(參見(jiàn)下一篇《QEMU+GDB調(diào)試ARM程序》)。
Guest OS的abort異常
我們?cè)O(shè)計(jì)的hypervisor支持Guest OS觸發(fā)的4類(lèi)異常,具體定義如下:
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發(fā)起的HVC調(diào)用,我們?cè)O(shè)計(jì)使用HVC指令在VM之間建立通信。
ESR_EC_SMC64 = 0x17:用于處理Guest OS發(fā)起的SMC調(diào)用,我們知道ARM規(guī)定了PSCI規(guī)范,通過(guò)將電源管理等代碼在ATF代碼中實(shí)現(xiàn),這樣就實(shí)現(xiàn)了資源的安全管理。PSCI規(guī)范的底層就是通過(guò)SMC指令實(shí)現(xiàn)的。hypervisor需要將Guest OS發(fā)起的虛擬核的PSCI調(diào)用轉(zhuǎn)發(fā)給物理核。
ESR_EC_SYSRG = 0x18:模擬寄存器和外設(shè)。因?yàn)镚uest OS需要訪問(wèn)一些特殊寄存器和外設(shè),而外設(shè)有時(shí)候需要多個(gè)VM共享,hypervisor對(duì)其進(jìn)行模擬。
ESR_EC_DALEL = 0x24:用于處理Guest OS發(fā)生的abort異常。比如,Guest OS訪問(wèn)我們未指定的物理內(nèi)存。
對(duì)于ESR_EL2寄存器的分析,與前面的一段一樣,不在具體詳述。
而HPFAR_EL2寄存器保存著出錯(cuò)的IPA地址,通過(guò)該地址,我們就可以知道,Guest OS訪問(wèn)哪塊內(nèi)存出錯(cuò),就能解決某些bug了。
審核編輯:湯梓紅
-
ARM
+關(guān)注
關(guān)注
134文章
9045瀏覽量
366806 -
寄存器
+關(guān)注
關(guān)注
31文章
5317瀏覽量
120002 -
Linux
+關(guān)注
關(guān)注
87文章
11225瀏覽量
208915
原文標(biāo)題:ARM深入理解-hypervisor調(diào)試方法一(異常寄存器分析)
文章出處:【微信號(hào):嵌入式ARM和Linux,微信公眾號(hào):嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論