您的位置:首页 > 其它

QEMU ARM异常处理流程

2014-09-25 17:24 225 查看
precise exception必须考虑暂存器和内存,其中我理解的暂存器为CPUState,在二进制翻译中,我们关注的是guest的precise exception。因此必须确保当guest代码发生异常时,guest的暂存器(CPUState)和内存必须满足precise exception的要求,这样guest的exception handler才能正确处理该异常。

QEMU在每一个可能发生异常的指令或是helper function之前,会将CPUState的大部分内容更新,少数未更新的内容会在guest真正发生异常时重新再计算,对于arm来说,pc和condition code属于后者。在二进制翻译中,出于效率上的考虑,通常会在一个basic block的结尾才更新guest pc,而非每翻译一个guest指令就更新guest pc。

下面以guest发生缺页时产生的异常为例,观察QEMU是如何维护正确的guest暂存器和内存的。

配置qemu下的SConstruct文件,打开qemu调试宏,根据x86的translate.c文件中的gen_pc_load函数中的调试宏,在arm中添加修改这个宏,使其在debug模式下可以输出引发异常的host pc,和与其相对应的guest pc。

因为真正发生异常的是host binary,因此必须通过它找到guest pc,查出是哪一条guest指令发生异常,并做相应的处理。

因此使用gdb加载qemu-syste-arm,在gdb下启动v5的os,打开日志文件开关,输出in_asm,op,out_asm,和自己添加的spc和esp,之后continue,在内核引导的过程中,会出现异常,bt发现没有stack信息,说明这是翻译过来的host binary代码产生的,也就是guest cpu产生的异常。

打开日志文件,搜索spc,发现第一个引起异常的host pc为0x405156bc,对应的guest pc为0xc0140a98,这时查看第一次翻译0xc0140a98所得到的host binary

可以看出,spc 0x405156bc = 0x405156bd – 1,也也就是callq 0x5fe7c0下一条指令所在的地址减1,因此是调用 0x5fe470这个函数时发生了异常;尝试在gdb下打印print __st{b,w,l,q}_{cmmu,
mmu}和__ld{b,w,l,q}_{cmmu,mmu},发现恰巧是__stw_mmu出现异常



根据搜索找到qemu/softmmu_tempate.h中,



不命中执行else分支,在else分支中,会根据GETPC的返回值区分是一般C函数还是code cache中调用的,如果不为零,则为code cache中所调用;

只后调用tlb_fill函数,这个函数在target-arm/op-helper.c中定义,



在tlb_fill函数中,首先进入cpu_arm_handle_mmu_fault函数根据返回值判断是否要处理异常



如果返回值为1,且taddr不为0,则执行异常处理,见下图:



首先,将retaddr转换为ulong,这个值代表发生异常的host binary所在的地址;之后根据发生异常的host binary所在的地址(retaddr),调用tb_find_pc函数查找是哪一个tb中的host binary发生异常,如果找到了,则确定这是一个guest的异常,qemu通过调用cpu_restore_state()函数,restore guest CPUState以便guest exception handler处理guest的缺页异常



如本文档开头所描述,通常情况下qemu在翻译quest binary时是不会记录guest pc的,这时为了定位guest pc,qemu在翻译guest binary会记录额外的信息,包含guest pc。(这是通过传递searched_pc来判断是否记录额外信息),可参看上图中的注释;

比较重要的函数是target-arm/translate.c中的gen_intermediate_code_internal这个函数,比较长,就不贴图了,里面提高如何处理condexec bit,保存完必要的信息后,重新翻译,覆盖原来的翻译结果(翻译的结果应该是一样的,只是为了保存必要信息)

之后重要的是



其中

j为某个数已经在

这个函数中被标记了,这里的遍历是为了找到这个j,详情可看函数tcg_gen_code_search_pc。

之后调用



其中#if中的内容不用在意,是我为了调试加上去的,重点是下面那两个语句,给我的感觉是设置pc(这个还没看手册)和条件位。

回到



这个地方,执行raise_exception,参数是异常信息,在cpu_restore_state中设置的,

Raise_exception最终是调用了longjmp函数,这是程序会长跳转到cpu-exec.c中的cpu_exec(cpu_arm_exec)这个主要的函数中,调用do_interrput函数处理异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: