欢迎光临散文网 会员登陆 & 注册

QEMU 用户模式(User Mode)流程及源码分析 (二)

2022-02-18 19:18 作者:范文捷  | 我要投稿

GUEST端代码加载完成后,将代码入口地址写入CPUArchState的pc或ip变量变量,然后将CPUArchState对象作为参数传入cpu_loop,进行循环翻译执行。 这里以GUEST端为x86_64, HOST端为ARM64为例。

cpu_loop流程

当GUEST端为x86_64时,cpu_loop函数在linux-user/i386/cpu_loop.c文件内,对于x86_64,无论32位还是64位,都使用这个文件。其流程为

cpu_exec_start -> cpu_exec -> cpu_exec_end -> process_queued_cpu_work -> 处理异常或系统调用 -> cpu_exec_start

cpu_exec_start用于设置进入翻译执行状态时的相关参数,比如把cpu->running设置为true。cpu_exec_end是设置退出翻译执行时的相关参数。process_queued_cpu_work在多线程状态下,处理翻译执行过程中其它线程插入的任务。

cpu_exec会返回一个int值,这个值是代表处理过程中遇到的异常,例如各种中断操作、系统调用等。会通过switch操作判断该值代表的意义并进行处理。

cpu_exe则为翻译执行的主流程。

cpu_exec流程

cpu_exec的实现在accel/tcg/cpu_exec.c, cpu_handle_exception和cpu_handle_interrupt负责处理异常中断,可以看出大致代码流程如下

每次处理完中断,并不退出循环,而是将tb_exit和last_tb初始化,断点也视为中断。处理异常完成后则退出循环,并由外部处理。

tb的类型是TranslateBlock,该数据结构存储即时编译出的代码和前后的跳跃地址等参数,并存入缓存。下次执行到相同位置时,通过pc值查找出来并使用,避免了重复的翻译工作。并交由cpu_loop_exec_tb执行。

tb_gen_code负责翻译GUEST端代码至HOST端。

tb_gen_code流程

tb_gen_code的实现在accel/tcg/translate_all.c流程主要分为3个部分,gen_intermediate_code(生成中间结构码) -> tcg_gen_code (生成HOST端代码) -> tcg_tb_insert(缓存生成的TranslateBlock)

gen_intermediate_code 的实现代码的位置由GUEST端决定,当GUEST端为x86_64时,其位置在target/i386/tcg/translate.c文件内。可以看到该函数会转发给translator_loop函数执行,并将与GUEST平台相关的TranslatorOps对象作为translator_loop的首个参数。由于该TranslatorOps对象静态定义在同文件内,并包含与平台相关的一系列回调函数。

因此,可以看出生成TCG中间码的流程为

tb_gen_code(accel/tcg/translate_all.c) -> gen_intermediate_code(target/i386/tcg/translate.c) -> translator_loop(accel/tcg/translator.c)

target/i386/tcg/translate.c 主要负责生成tcg中间码过程中与GUEST平台有关的操作,并将相关操作的函数指针封装在结构体TranslatorOps内,由translator_loop进行回调。

与LLVM IR不同,TCG中间码与GUEST平台有关,而其内部的数据结构也与HOST平台有关。因此,其并不具备可移植性。其作用主要是在QEMU架构中解耦GUEST端和HOST端相关代码,降低代码复杂度。

TCG中间码解析

TCG是QEMU负责指令翻译代码生成的组件,其全称是Tiny Code Generator(微码生成器)。其输入是TCG中间码数据结构,输出是HOST端代码。TCG中间码不具备平台无关性。

以GUEST端x86_64输入为例,下列x86_64指令

翻译为TCG中间码则为(TCG中间码行注释符为 ----)

可以看出,TCG中间码与GUEST平台高度相关。比如寄存器都是GUEST平台名称。这里解释一下为什么要将寄存器替换为存储的常量立即数,因为在翻译过程中,TCG的寄存器映射的是HOST平台特定内存地址,比如当HOST为ARM64时,CPUX86State对象env存储在栈帧寄存器x29,rcx存储位置相对env的偏移是8,因此rcx会被翻译为间接寻址[x29, #8],需要读写内存。用立即数可优化不必要内存读写。TCG中间码tmp开头的临时变量映射的是HOST平台的空闲可变寄存器。

CPUArchState对象env与栈帧无关,其映射为HOST平台栈帧寄存器是因为QEMU翻译过程中将函数调用结构破坏,QEMU翻译的代码只有跳跃无调用。所以与函数调用相关的栈帧寄存器空闲出来,可用于存储CPUArchState对象。

tcg_gen_code

tcg_gen_code负责将TCG中间码翻译为HOST平台代码,其过程分析将在下章讲解。

QEMU 用户模式(User Mode)流程及源码分析 (二)的评论 (共 条)

分享到微博请遵守国家法律