Base是git://git.qemu.org/qemu.git v2.6.0

入口是qemu_init_vcpu,在tcg_enabled下进入qemu_tcg_init_vcpu函数,在qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE)中看到执行函数是qemu_tcg_cpu_thread_fn,下面的函数负责控制在machine完全初始化完成前进行等待

然后进入循环开始执行tcg_exec_all函数。

在cpu_can_run(cpu)之后执行tcg_cpu_exec,去除use_icount后,就只有cpu_exec,

需要注意看一下CPUArchState,即CPUX86State结构,

更多内容自己拉出来看吧,qemu在KVM下,env内的数据只是作为vcpu sched退到qemu userspace之后保存的数据,当再次运行时则将内容加载到vmcs或真正的物理CPU上,而tcg对待env,每一个env都是tcg vcpu虚拟的寄存器,直接读取或写入。

然后执行cc->cpu_exec_enter(cpu)即x86_cpu_exec_enter,基本就是env->eflags的初始化,在下面的内容才是真正执行的地方:

本质就是循环处理不同的tb,sigsetjmp的用法自己google吧,需要注意这一点。代码的真正执行是根据env->eip执行的,eip的初始化在x86_cpu_reset函数中,前面有SegmentCache的初始化,直接处理,

一开始开发选择的模拟器是bochs,然后换成了qemu,bochs处理SegmentCache是只需要拿到selector,然后从内存中读取对应的shadow值,而qemu则不对memory 中的shadow寄存器做处理,直接从物理寄存器读取内容。

回过来看env->eip,这个值就是读取代码的链条,修改它的有:

先记住,回头看sigsetjmp。

后面一开始的部分都是中断和异常,不仅仅是guest产生的,还是qemu自身模拟的。

这些不关注,走到下一个for循环中

上面的代码才是核心内容,tb_find_fast负责找到下一个需要执行的tb,cpu_tb_exec负责执行这个translation block。

在tb_find_fast中,tb本身是有二级缓存的,hash索引是使用代码执行的pc指针,

读取缓存就需要当前tb对应的pc值,

因为本身tb_jmp_cache是有hash冲突

继续看tb_find_slow,又是一级缓存,tb_find_physical,缓存放在tcg_ctx全局变量中,以pc对应的hva作为索引。

真正的代码翻译在tb_gen_code函数中,它完成了tb的代码翻译过程,新tb加入了缓存中,cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb。

gen_intermediate_code翻译中间代码,在初始化dc结构时很明显是从env读取寄存器内容的

直接看到gen_tb_start(tb)下的for循环,


关于tcg调试的部分

DEBUG_DISAS标志下有几个print内容,

可以根据上面值的内容在代码不同位置打桩处理。


qemu tcg translation block机制来自于OenHan

链接为:http://oenhan.com/qemu-tcg-translation-block

发表评论