我们在编写裸机程序(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 = 0x97010046elr_el2 = 0xfd8000005880far_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_t abort_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了。
语音助手,赋能多形态终端
什么是手机MMS短信
魅族演唱会倒计时 魅蓝Note5将强势登场 直播地址
预计2019年无线可穿戴市场出货量1.2亿台
SUNPLUS推出8位工业级单片机学习板
hypervisor的调试分析方法
开关电源升压电路设计
单片机中断系统的特点和功能
变频器是什么?变频器的原理是怎么样的详细讲解
TI工程师再论车载平视显示
LTCC实现SIP的优势和特点
高性能电源模块为任务关键性“观察”传感器供电
一文带您了解场发射扫描电镜
教新手小白怎样去操作Android手机
移动存储加密转接器解决方案
美国国家半导体推出首款100V半桥栅极驱动器LM5113
集成电路测试仪电源电路的仿真设计研究与应用
PT4115调光芯片的功能及作用 pt4115模拟调光与pwm调光的区别
超声波水表的原理及设计
SD-WAN企业组网:实现高效、可靠和安全的网络连接