QEMU生成tcg时函数中简写的意义

Documentation/TCG/frontend-ops

  • v,例如gen_op_mov_v_reg

    v应该是指TCG variable(TCGvi386里默认为32整形),所以gen_op_mov_v_reg是指把一个寄存器的值移动到一个TCG变量里。

  • tl,例如tcg_gen_extract_tltcg_gen_mov_tl

    大概是target length的简写。

    The vast majority of the time, this will match the native size of the emulated target, so rather than force people to type i32 or i64 all the time, the shorthand tl is made available for all helpers. e.g. to perform a 32bit register move for a 32bit target, simply use tcg_gen_mov_tl rather than tcg_gen_mov_i32.

实现translate_xxx

2020.6.9

aaa

case 0x37: /* aaa */
    if (CODE64(s))
    goto illegal_op;
    gen_update_cc_op(s);
    gen_helper_aaa(cpu_env);
    set_cc_op(s, CC_OP_EFLAGS);
    break;

cc_op在初始化是就设为了OP_CC_EFLAGS,所以可以忽略gen_update_cc_opset_cc_op(s, CC_OP_EFLAGS)

Q: tr_gen_call_to_helper1和tr_sys_gen_call_to_helper1的区别是啥?感觉没啥不同。用户态里无法调用后者

2020.6.10

bound

// target/i386/helper.h:71:
DEF_HELPER_3(boundw, void, env, tl, int)
// include/exec/helper-head.h:143:
#define DEF_HELPER_3(name, ret, t1, t2, t3) \
    DEF_HELPER_FLAGS_3(name, 0, ret, t1, t2, t3)
// include/exec/helper-gen.h:31:
#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3)                \
static inline void glue(gen_helper_, name)(dh_retvar_decl(ret)          \
    dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3))         \
{                                                                       \
  TCGTemp *args[3] = { dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3) };   \
  tcg_gen_callN(HELPER(name), dh_retvar(ret), 3, args);                 \
}
// 关于dh_arg_decl(tl, 2)如何展开为TCGv_i32 arg2的
    // include/exec/helper-head.h:135:
    #define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n)
    // include/exec/helper-head.h:36:
    #define dh_alias(t) glue(dh_alias_, t)
    // include/exec/helper-head.h:55:
    #   define dh_alias_tl i32 // 这里为什么能够嵌套一个宏展开?
    // 所以dh_arg_decl(tl)变为了TCGv_i32

    
static inline void gen_helper_boundw( TCGv_ptr arg1, TCGv_i32 arg2, TCGv_i32 arg3)
{
    TCGTemp *args[3] = {tcgv_ptr_temp(arg1), tcgv_i32_temp(arg2), tcgv_i32_temp(arg3)};
    tcg_gen_callN(helper_boundw, ((void *)0), 3, args); 
}

现在通过在编译过程给编译器加"-E"选项完全展开qemu里所有宏,可以很方便的看出这些helper函数的声明和定义。

2020.6.11

CWD/CDQ/CQO与CBW/CWDE/CDQW

intel编程手册里给出的CWD/CDQ/CQO在详细对比了x86tomips实现的版本(只实现了CDQ),对比了qemu实现的版本,对比CBW/CWDE/CDQW的语义,我觉得下表左的操作写错了,应该改为

CWD/CDQ/CQOCBW/CWDE/CDQW
IF OperandSize = 16 (* CWD instruction )
THEN
DX ← SignExtend(AX);
ELSE IF OperandSize = 32 (
CDQ instruction )
EDX ← SignExtend(EAX); FI;
ELSE IF 64-Bit Mode and OperandSize = 64 (
CQO instruction*)
RDX ← SignExtend(RAX); FI;
FI;
IF OperandSize = 16 (* Instruction = CBW )
THEN
AX ← SignExtend(AL);
ELSE IF (OperandSize = 32, Instruction = CWDE)
EAX ← SignExtend(AX); FI;
ELSE (
64-Bit Mode, OperandSize = 64, Instruction = CDQE*)
RAX ← SignExtend(EAX);
FI;

按照CWD/CDQ/CQO在手册里的文字描述,应该改为

 IF OperandSize = 16 (* CWD instruction *)
 THEN
-DX ← SignExtend(AX);
+DX:AX ← SignExtend(AX);
 ELSE IF OperandSize = 32 (* CDQ instruction *)
-EDX ← SignExtend(EAX); FI;
+EDX:EAX ← SignExtend(EAX); FI;
 ELSE IF 64-Bit Mode and OperandSize = 64 (* CQO instruction*)
-RDX ← SignExtend(RAX); FI;
+RDX:RAX ← SignExtend(RAX); FI;
 FI;

salc

为啥在intel编程手册vol2chap3~5里没有收录这调整指令?

在手册的vol3chap22.15 UNDEFINED OPCODES说是为了兼容之前老处理器,有一些指令不建议使用,SALC就是其中之一。其语义为:

D6H - When not in 64-bit mode SALC - Set AL to Cary flag. IF (CF=1), AL=FF, ELSE, AL=0 (#UD in 64-bit mode)

当我准备往al寄存器写入数据时,我产生了一个疑惑

  • store_ireg_to_ir1(value_opnd, &al_ir1_opnd, false);
  • 向env的regs里写数

有啥区别?为啥前者会用ir1?简单看了源码,用ir1就是为了方便!缺点是只能将ir2寄存器存入x86寄存器,没有立即数操作。

xlat/xlatb

IF AddressSize = 16
THEN
AL ← (DS:BX + ZeroExtend(AL));
ELSE IF (AddressSize = 32)
AL ← (DS:EBX + ZeroExtend(AL)); FI;
ELSE (AddressSize = 64)
AL ← (RBX + ZeroExtend(AL));
FI;

在intel编程手册里搜索‘colon’就能知道::@vol1chap3.7.1

  • 拼接,例如EDX:EAX
  • 段选择,例如DS:EAX

很好奇capstone会不会把隐含的操作数给准备好,比如xlatb隐含的操作数是ALDS:(E)BX+AL。实际用capstone源代码编译出来的cstool工具测试了一下发现并没有包含隐含操作数。所以这些操作数需要自己来准备。

2020.6.15

MONITOR/MWAIT

参考qemu翻译的int的过程,gen_jmp_im(s, pc_start - s->cs_base)应该是在设eip的值,为了能够精确中断。我没仔细去看qemu和xqm分别在什么的地方更新的eip。因此,

  • pc_start应该是该条指令的起始地址;
  • s->cs_base是该指令所在代码段的段基址;

MWAIT里还用到了s->pc - pc_start这个是表示指令长度,所以

  • s->pc表示下一条指令的起始地址。
2020.6.16

AMD的SVM

没啥

AMD的3DNOW!

qemu是把3DNOW!放在SSE框架里来搞的。所以需要详细厘清qemu如何如何准备的参数,如何调用的helper。

helper放在数组sse_op_table5里,3DNOW!采用mmx寄存器,所以调用3DNOW!的helper的代码框架如下,

3dnow-formatted

2020.6.17

VMOVSS

发现qemu并没有正确实现这条指令,而是把它当作MOVSS来执行。测试代码在Codes/test/x86Inst/vmovss.s里。和李欣宇交流后得知qemu有很多拓展指令没有实现。于是用shell脚本筛选出qemu不支持的指令(看target/i386/translate.c里是否支持)。

2020.6.18

MOVSLDUP/MOVSHDUP/MOVDDUP

需要从内存读128-bit数据,load_ireg_from_ir1_mem函数把128/64大小的ir1内存操作数都翻译成了mips_ld所以一次只能读64位的数据!读[127:64]应该是用is_xmm_hi=1表示。

因为没有调用helper函数,所以用户态和系统态的实现都在tr_misc.c里。用xqm i386-linux-user测试了MOVSLDUP没有问题。

2020.6.19

POPCNT

这个指令要修改eflags,和邹旭商量,我直接调用

generate_eflag_calculation(IR2_OPND dest, IR2_OPND src0, IR2_OPND src1, IR1_INST *pir1, bool is_sx);

就好,空闲的参数用zero_ir2_opnd

LDDQU

实现很简单,因为没有调用helper,所以测试了一下i386-linux-user没有问题。