Add support for emulating the byte move and zero extend instructions:
"mov r/m8, r32" and "mov r/m8, r64". Approved by: neel (co-mentor)
This commit is contained in:
parent
7059326b09
commit
54e03e07b3
@ -58,8 +58,10 @@ enum cpu_mode {
|
||||
enum {
|
||||
VIE_OP_TYPE_NONE = 0,
|
||||
VIE_OP_TYPE_MOV,
|
||||
VIE_OP_TYPE_MOVZX,
|
||||
VIE_OP_TYPE_AND,
|
||||
VIE_OP_TYPE_OR,
|
||||
VIE_OP_TYPE_TWO_BYTE,
|
||||
VIE_OP_TYPE_LAST
|
||||
};
|
||||
|
||||
@ -67,7 +69,18 @@ enum {
|
||||
#define VIE_OP_F_IMM (1 << 0) /* immediate operand present */
|
||||
#define VIE_OP_F_IMM8 (1 << 1) /* 8-bit immediate operand */
|
||||
|
||||
static const struct vie_op two_byte_opcodes[256] = {
|
||||
[0xB6] = {
|
||||
.op_byte = 0xB6,
|
||||
.op_type = VIE_OP_TYPE_MOVZX,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct vie_op one_byte_opcodes[256] = {
|
||||
[0x0F] = {
|
||||
.op_byte = 0x0F,
|
||||
.op_type = VIE_OP_TYPE_TWO_BYTE
|
||||
},
|
||||
[0x88] = {
|
||||
.op_byte = 0x88,
|
||||
.op_type = VIE_OP_TYPE_MOV,
|
||||
@ -313,6 +326,59 @@ emulate_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following simplifying assumptions are made during emulation:
|
||||
*
|
||||
* - guest is in 64-bit mode
|
||||
* - default address size is 64-bits
|
||||
* - default operand size is 32-bits
|
||||
*
|
||||
* - operand size override is not supported
|
||||
*
|
||||
* - address size override is not supported
|
||||
*/
|
||||
static int
|
||||
emulate_movzx(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
mem_region_read_t memread, mem_region_write_t memwrite,
|
||||
void *arg)
|
||||
{
|
||||
int error, size;
|
||||
enum vm_reg_name reg;
|
||||
uint64_t val;
|
||||
|
||||
size = 4;
|
||||
error = EINVAL;
|
||||
|
||||
switch (vie->op.op_byte) {
|
||||
case 0xB6:
|
||||
/*
|
||||
* MOV and zero extend byte from mem (ModRM:r/m) to
|
||||
* reg (ModRM:reg).
|
||||
*
|
||||
* 0F B6/r movzx r/m8, r32
|
||||
* REX.W + 0F B6/r movzx r/m8, r64
|
||||
*/
|
||||
|
||||
/* get the first operand */
|
||||
error = memread(vm, vcpuid, gpa, &val, 1, arg);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
/* get the second operand */
|
||||
reg = gpr_map[vie->reg];
|
||||
|
||||
if (vie->rex_w)
|
||||
size = 8;
|
||||
|
||||
/* write the result */
|
||||
error = vie_update_register(vm, vcpuid, reg, val, size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
emulate_and(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
|
||||
@ -447,6 +513,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
|
||||
error = emulate_mov(vm, vcpuid, gpa, vie,
|
||||
memread, memwrite, memarg);
|
||||
break;
|
||||
case VIE_OP_TYPE_MOVZX:
|
||||
error = emulate_movzx(vm, vcpuid, gpa, vie,
|
||||
memread, memwrite, memarg);
|
||||
break;
|
||||
case VIE_OP_TYPE_AND:
|
||||
error = emulate_and(vm, vcpuid, gpa, vie,
|
||||
memread, memwrite, memarg);
|
||||
@ -608,6 +678,23 @@ decode_rex(struct vie *vie)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_two_byte_opcode(struct vie *vie)
|
||||
{
|
||||
uint8_t x;
|
||||
|
||||
if (vie_peek(vie, &x))
|
||||
return (-1);
|
||||
|
||||
vie->op = two_byte_opcodes[x];
|
||||
|
||||
if (vie->op.op_type == VIE_OP_TYPE_NONE)
|
||||
return (-1);
|
||||
|
||||
vie_advance(vie);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_opcode(struct vie *vie)
|
||||
{
|
||||
@ -622,6 +709,10 @@ decode_opcode(struct vie *vie)
|
||||
return (-1);
|
||||
|
||||
vie_advance(vie);
|
||||
|
||||
if (vie->op.op_type == VIE_OP_TYPE_TWO_BYTE)
|
||||
return (decode_two_byte_opcode(vie));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user