x86-qemu-mips做出的修改

tb_find

在翻译TB时采用直接从x86翻译到mips(即X86toMips的方法,x86=>IR1=>IR2=>MIPS),而不经过TCG,

  • 保留QEMU的tb_alloc
  • 将QEMU的gen_intermediate_codetcg_gen_code替换为target_x86_to_mips_host
2019.12.9
# 编译x86-qemu-mips

在README里的Compile and Run基础上添加debug支持

# make clean, make distclean
./configure --enable-debug --enable-debug-info  --enable-x86tomips --disable-werror --target-list="i386-linux-user"
make

系统调用修改思路

QEMU调用helper的方法

QEMU里有一套通用的在native环境(guest)下调用QEMU(host)函数的机制——tcg_gen_callN。native环境(guest)和QEMU(host)运行环境的诸多不同,最显著的不同是32/64和ABI,由这个函数tcg_gen_callN解决。

QEMU利用tcg_gen_callN调用helper_raise_interrupt函数,来实现跳出cpu_exec,然后到cpu_loop里通过中断处理代码(linux-user/i386/cpu_loop.c: 98~241根据中断号的switch case代码)完成系统调用,helper_raise_interrupt完成的任务如下,

  • 保存中断处理代码需要的变量(跟踪helper代码,便可得下面这些变量)
    • 中断号intno
    • error_code=0
    • 是中断还是异常is_int=1,🤔有必要调研一下在QEMU里(不是X86里)的中断和异常分别是什么意思
    • 中断指令的下一条指令地址exception_next_eip
    • CPU可以执行IOcan_do_io=1
  • siglongjmp

x86-qemu-mips与QEMU的不同

native和BT环境切换

QEMU对寄存器的操作全是和CPUState数据结构在打交道,而没有映射寄存器,所以QEMU无需过多考虑native和BT环境切换保存寄存器的事。

x86-qemu-mips将x86的体系结构寄存器和QEMU的一些常用变量(DONE:整理牛根画的寄存器映射图的内容,见寄存器映射总结)映射到了mips的体系结构寄存器中,在native切到BT环境时,就要考虑保存这些映射的寄存器,以便给QEMU的运行腾出空间;在BT切到native时,需要恢复这些寄存器映射。

在native里调用BT环境里的函数

QEMU通过tcg_gen_callN实现。能够解决native和BT环境的32/64和ABI不同的问题。

x86-qemu-mips无。

x86-qemu-mips调用helper的方法

前言:目标是尽可能多的沿用QEMU处理系统调用的方法。所以学习QEMU调用helper函数来保存中断处理代码需要的变量,然后用siglongjmp跳出cpu_exec循环,去中断处理代码里执行。

x86-qemu-mips缺少在native里调用函数的能力,所以有必要实现一个类似QEMU的tcg_gen_callN的功能。

调用BT环境(MIPS)的函数func(arg1, arg2, ...)需要两步,

  1. 按照MIPS的ABI,在相应的MIPS寄存器和栈里准备好参数arg1, arg2, ...

    这一步很麻烦,即我们需要实现一个类似QEMU的tcg_gen_callN的功能,需要考虑:

    1. MIPS有o32, o64, n64共3种ABI,我们都得考虑实现;
    2. arg1, arg2, ...需要的寄存器和x86映射的寄存器可能会冲突(例如v0),所以有必要保存那些会冲突的寄存器映射;
    3. arg1, arg2, ...需要的寄存器和x86-qemu-mips的临时寄存器可能会冲突(例如a0~a3),x86-qemu-mips临时寄存器的算法听说写的还很不完善,那就需要将临时寄存器的代码搞清楚且修补存在问题的地方;
    4. 最后把arg1, arg2, ...放到MIPS ABI规定的地方,这一步十分简单;
  2. jalr func,这一步很简单,append_ir2_opnd1(mips_jalr, func)即可。

综上所述,我选择不考虑参数,即将helper_raise_interrupt需要的参数都去掉形成一个新的函数helper_raise_int。直接在native环境保存这些需要传进去的参数。由此,有了现阶段对系统调用的实现,translate_inthelper_raise_interrupt需要3个参数:

  1. CPUX86State *env,这是个全局变量,
  2. 中断号int intno,直接在translate_int里存入env即可,
  3. 中断指令的长度int next_eip_addend,总的来说是需要计算下一条指令的地址,直接在translate_int里存入env即可,

所以QEMU的helper_raise_interrupt里还没实现的加入到helper_raise_int的函数里即可:

  • 参数设置,
    • error_code=0
    • 是中断还是异常is_int=1
    • CPU可以执行IOcan_do_io=1
  • siglongjmp。
2019.12.10
# 寄存器映射总结

来源于牛根发在slack聊天信息(2019.12.6),表格使用table generator生成,

0 1 2 3
0 zero ZERO at EDX v0 EAX v1 ECX
4 a0 TMP a1 TMP a2 TMP a3 TMP
8 a4 TMP a5 TMP a6 TMP a7 TMP
12 t0 TMP t1 TMP t2 GuestBase t3 ForFutureUse
16 s0 n1 s1 SS s2 ENV s3 EBX
20 s4 ESP s5 EBP s6 ESI s7 EDI
24 t8 DBT t9 DBT k0 k1
28 gp TOP sp SP fp EFLAGS ra MDA?

图例说明

  • mips-reg:表示在上下问切换时需要保存和恢复的MIPS寄存器;这部分是QEMU的上下文,需要维护;
  • TMP:表示临时寄存器,在翻译的时候可以通过 ra_alloc_itemp() 获取;
  • x86-reg:表示对 x86 寄存器进行的寄存器映射;在上下文切换的时候从 ENV 载入、写入 ENV 中,在翻译时需要访问 x86 这些寄存器时,可以直接访问相应的 MIPS 寄存器;
  • ENV:表示 ENV 的地址;目前是 lsenv->cpu_state,即 CPUX86State
  • DBT内部使用:二进制翻译器内部使用,在不同情况下有不同的用途,使用时需要注意;
  • 浮点使用

:由于在context_switch_bt_to_native时,需要用到从 QEMU 传进来的参数(存放在a0和 a1中)因此在构建这部分代码时不能使用ra_alloc_itemp(),否则会污染掉a0和a1,

  • load_ireg_from_imm64有可能使用ra_alloc_itemp(),因此不推荐在generate_context_switch_bt_to_native中调用;
  • load_ireg_from_imm32不会使用ra_alloc_itemp(),因此可以调用。

编写上下文切换的时候可以注意一下,不过似乎只是 native_to_bt 的时候没有这些问题。

2019.12.16

因为今天张老师修好了涉及unaligned访问的相关代码,包括fpu load/store等问题,可以跑gcc编译的二进制文件了,所以测试了一下系统调用消息队列的事。

消息队列系统调用

(同QEMU的笔记)参考How do I use mqueue in a c program on a Linux based system?

:编译时记得加上-lrt,表示链接时需要rt这个库。

clientx86-qemu-mips client
server✔️
x86-qemu-mips server✔️