svn commit: r268701 - in head/sys/amd64: include vmm vmm/intel
Neel Natu
neel at FreeBSD.org
Tue Jul 15 17:37:19 UTC 2014
Author: neel
Date: Tue Jul 15 17:37:17 2014
New Revision: 268701
URL: http://svnweb.freebsd.org/changeset/base/268701
Log:
Add support for operand size and address size override prefixes in bhyve's
instruction emulation [1].
Fix bug in emulation of opcode 0x8A where the destination is a legacy high
byte register and the guest vcpu is in 32-bit mode. Prior to this change
instead of modifying %ah, %bh, %ch or %dh the emulation would end up
modifying %spl, %bpl, %sil or %dil instead.
Add support for moffsets by treating it as a 2, 4 or 8 byte immediate value
during instruction decoding.
Fix bug in verify_gla() where the linear address computed after decoding
the instruction was not being truncated to the effective address size [2].
Tested by: Leon Dang [1]
Reported by: Peter Grehan [2]
Sponsored by: Nahanni Systems
Modified:
head/sys/amd64/include/vmm.h
head/sys/amd64/include/vmm_instruction_emul.h
head/sys/amd64/vmm/intel/vmx.c
head/sys/amd64/vmm/vmm.c
head/sys/amd64/vmm/vmm_instruction_emul.c
Modified: head/sys/amd64/include/vmm.h
==============================================================================
--- head/sys/amd64/include/vmm.h Tue Jul 15 17:26:43 2014 (r268700)
+++ head/sys/amd64/include/vmm.h Tue Jul 15 17:37:17 2014 (r268701)
@@ -322,11 +322,11 @@ struct seg_desc {
uint32_t limit;
uint32_t access;
};
-#define SEG_DESC_TYPE(desc) ((desc)->access & 0x001f)
-#define SEG_DESC_PRESENT(desc) ((desc)->access & 0x0080)
-#define SEG_DESC_DEF32(desc) ((desc)->access & 0x4000)
-#define SEG_DESC_GRANULARITY(desc) ((desc)->access & 0x8000)
-#define SEG_DESC_UNUSABLE(desc) ((desc)->access & 0x10000)
+#define SEG_DESC_TYPE(access) ((access) & 0x001f)
+#define SEG_DESC_PRESENT(access) (((access) & 0x0080) ? 1 : 0)
+#define SEG_DESC_DEF32(access) (((access) & 0x4000) ? 1 : 0)
+#define SEG_DESC_GRANULARITY(access) (((access) & 0x8000) ? 1 : 0)
+#define SEG_DESC_UNUSABLE(access) (((access) & 0x10000) ? 1 : 0)
enum vm_cpu_mode {
CPU_MODE_REAL,
@@ -366,11 +366,14 @@ struct vie {
uint8_t num_valid; /* size of the instruction */
uint8_t num_processed;
+ uint8_t addrsize:4, opsize:4; /* address and operand sizes */
uint8_t rex_w:1, /* REX prefix */
rex_r:1,
rex_x:1,
rex_b:1,
- rex_present:1;
+ rex_present:1,
+ opsize_override:1, /* Operand size override */
+ addrsize_override:1; /* Address size override */
uint8_t mod:2, /* ModRM byte */
reg:4,
@@ -450,6 +453,7 @@ struct vm_exit {
struct {
uint64_t gpa;
uint64_t gla;
+ int cs_d; /* CS.D */
struct vm_guest_paging paging;
struct vie vie;
} inst_emul;
Modified: head/sys/amd64/include/vmm_instruction_emul.h
==============================================================================
--- head/sys/amd64/include/vmm_instruction_emul.h Tue Jul 15 17:26:43 2014 (r268700)
+++ head/sys/amd64/include/vmm_instruction_emul.h Tue Jul 15 17:37:17 2014 (r268701)
@@ -108,7 +108,7 @@ void vie_init(struct vie *vie);
*/
#define VIE_INVALID_GLA (1UL << 63) /* a non-canonical address */
int vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla,
- enum vm_cpu_mode cpu_mode, struct vie *vie);
+ enum vm_cpu_mode cpu_mode, int csd, struct vie *vie);
#endif /* _KERNEL */
#endif /* _VMM_INSTRUCTION_EMUL_H_ */
Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c Tue Jul 15 17:26:43 2014 (r268700)
+++ head/sys/amd64/vmm/intel/vmx.c Tue Jul 15 17:37:17 2014 (r268701)
@@ -1793,10 +1793,25 @@ vmx_paging_info(struct vm_guest_paging *
static void
vmexit_inst_emul(struct vm_exit *vmexit, uint64_t gpa, uint64_t gla)
{
+ struct vm_guest_paging *paging;
+ uint32_t csar;
+
+ paging = &vmexit->u.inst_emul.paging;
+
vmexit->exitcode = VM_EXITCODE_INST_EMUL;
vmexit->u.inst_emul.gpa = gpa;
vmexit->u.inst_emul.gla = gla;
- vmx_paging_info(&vmexit->u.inst_emul.paging);
+ vmx_paging_info(paging);
+ switch (paging->cpu_mode) {
+ case CPU_MODE_PROTECTED:
+ case CPU_MODE_COMPATIBILITY:
+ csar = vmcs_read(VMCS_GUEST_CS_ACCESS_RIGHTS);
+ vmexit->u.inst_emul.cs_d = SEG_DESC_DEF32(csar);
+ break;
+ default:
+ vmexit->u.inst_emul.cs_d = 0;
+ break;
+ }
}
static int
Modified: head/sys/amd64/vmm/vmm.c
==============================================================================
--- head/sys/amd64/vmm/vmm.c Tue Jul 15 17:26:43 2014 (r268700)
+++ head/sys/amd64/vmm/vmm.c Tue Jul 15 17:37:17 2014 (r268701)
@@ -1190,15 +1190,18 @@ vm_handle_inst_emul(struct vm *vm, int v
struct vm_guest_paging *paging;
mem_region_read_t mread;
mem_region_write_t mwrite;
- int error;
+ enum vm_cpu_mode cpu_mode;
+ int cs_d, error;
vcpu = &vm->vcpu[vcpuid];
vme = &vcpu->exitinfo;
gla = vme->u.inst_emul.gla;
gpa = vme->u.inst_emul.gpa;
+ cs_d = vme->u.inst_emul.cs_d;
vie = &vme->u.inst_emul.vie;
paging = &vme->u.inst_emul.paging;
+ cpu_mode = paging->cpu_mode;
vie_init(vie);
@@ -1212,7 +1215,7 @@ vm_handle_inst_emul(struct vm *vm, int v
else if (error != 0)
panic("%s: vmm_fetch_instruction error %d", __func__, error);
- if (vmm_decode_instruction(vm, vcpuid, gla, paging->cpu_mode, vie) != 0)
+ if (vmm_decode_instruction(vm, vcpuid, gla, cpu_mode, cs_d, vie) != 0)
return (EFAULT);
/* return to userland unless this is an in-kernel emulated device */
Modified: head/sys/amd64/vmm/vmm_instruction_emul.c
==============================================================================
--- head/sys/amd64/vmm/vmm_instruction_emul.c Tue Jul 15 17:26:43 2014 (r268700)
+++ head/sys/amd64/vmm/vmm_instruction_emul.c Tue Jul 15 17:37:17 2014 (r268701)
@@ -69,8 +69,9 @@ enum {
};
/* struct vie_op.op_flags */
-#define VIE_OP_F_IMM (1 << 0) /* immediate operand present */
-#define VIE_OP_F_IMM8 (1 << 1) /* 8-bit immediate operand */
+#define VIE_OP_F_IMM (1 << 0) /* 16/32-bit immediate operand */
+#define VIE_OP_F_IMM8 (1 << 1) /* 8-bit immediate operand */
+#define VIE_OP_F_MOFFSET (1 << 2) /* 16/32/64-bit immediate moffset */
static const struct vie_op two_byte_opcodes[256] = {
[0xB6] = {
@@ -181,18 +182,15 @@ vie_read_register(void *vm, int vcpuid,
return (error);
}
-static int
-vie_read_bytereg(void *vm, int vcpuid, struct vie *vie, uint8_t *rval)
+static void
+vie_calc_bytereg(struct vie *vie, enum vm_reg_name *reg, int *lhbr)
{
- uint64_t val;
- int error, rshift;
- enum vm_reg_name reg;
-
- rshift = 0;
- reg = gpr_map[vie->reg];
+ *lhbr = 0;
+ *reg = gpr_map[vie->reg];
/*
- * 64-bit mode imposes limitations on accessing legacy byte registers.
+ * 64-bit mode imposes limitations on accessing legacy high byte
+ * registers (lhbr).
*
* The legacy high-byte registers cannot be addressed if the REX
* prefix is present. In this case the values 4, 5, 6 and 7 of the
@@ -204,17 +202,56 @@ vie_read_bytereg(void *vm, int vcpuid, s
*/
if (!vie->rex_present) {
if (vie->reg & 0x4) {
- /*
- * Obtain the value of %ah by reading %rax and shifting
- * right by 8 bits (same for %bh, %ch and %dh).
- */
- rshift = 8;
- reg = gpr_map[vie->reg & 0x3];
+ *lhbr = 1;
+ *reg = gpr_map[vie->reg & 0x3];
}
}
+}
+static int
+vie_read_bytereg(void *vm, int vcpuid, struct vie *vie, uint8_t *rval)
+{
+ uint64_t val;
+ int error, lhbr;
+ enum vm_reg_name reg;
+
+ vie_calc_bytereg(vie, ®, &lhbr);
error = vm_get_register(vm, vcpuid, reg, &val);
- *rval = val >> rshift;
+
+ /*
+ * To obtain the value of a legacy high byte register shift the
+ * base register right by 8 bits (%ah = %rax >> 8).
+ */
+ if (lhbr)
+ *rval = val >> 8;
+ else
+ *rval = val;
+ return (error);
+}
+
+static int
+vie_write_bytereg(void *vm, int vcpuid, struct vie *vie, uint8_t byte)
+{
+ uint64_t origval, val, mask;
+ int error, lhbr;
+ enum vm_reg_name reg;
+
+ vie_calc_bytereg(vie, ®, &lhbr);
+ error = vm_get_register(vm, vcpuid, reg, &origval);
+ if (error == 0) {
+ val = byte;
+ mask = 0xff;
+ if (lhbr) {
+ /*
+ * Shift left by 8 to store 'byte' in a legacy high
+ * byte register.
+ */
+ val <<= 8;
+ mask <<= 8;
+ }
+ val |= origval & ~mask;
+ error = vm_set_register(vm, vcpuid, reg, val);
+ }
return (error);
}
@@ -247,17 +284,6 @@ vie_update_register(void *vm, int vcpuid
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_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
@@ -267,7 +293,7 @@ emulate_mov(void *vm, int vcpuid, uint64
uint8_t byte;
uint64_t val;
- size = 4;
+ size = vie->opsize;
error = EINVAL;
switch (vie->op.op_byte) {
@@ -277,7 +303,7 @@ emulate_mov(void *vm, int vcpuid, uint64
* 88/r: mov r/m8, r8
* REX + 88/r: mov r/m8, r8 (%ah, %ch, %dh, %bh not available)
*/
- size = 1;
+ size = 1; /* override for byte operation */
error = vie_read_bytereg(vm, vcpuid, vie, &byte);
if (error == 0)
error = memwrite(vm, vcpuid, gpa, byte, size, arg);
@@ -285,11 +311,10 @@ emulate_mov(void *vm, int vcpuid, uint64
case 0x89:
/*
* MOV from reg (ModRM:reg) to mem (ModRM:r/m)
+ * 89/r: mov r/m16, r16
* 89/r: mov r/m32, r32
* REX.W + 89/r mov r/m64, r64
*/
- if (vie->rex_w)
- size = 8;
reg = gpr_map[vie->reg];
error = vie_read_register(vm, vcpuid, reg, &val);
if (error == 0) {
@@ -298,18 +323,23 @@ emulate_mov(void *vm, int vcpuid, uint64
}
break;
case 0x8A:
+ /*
+ * MOV byte from mem (ModRM:r/m) to reg (ModRM:reg)
+ * 8A/r: mov r8, r/m8
+ * REX + 8A/r: mov r8, r/m8
+ */
+ size = 1; /* override for byte operation */
+ error = memread(vm, vcpuid, gpa, &val, size, arg);
+ if (error == 0)
+ error = vie_write_bytereg(vm, vcpuid, vie, val);
+ break;
case 0x8B:
/*
* MOV from mem (ModRM:r/m) to reg (ModRM:reg)
- * 8A/r: mov r/m8, r8
- * REX + 8A/r: mov r/m8, r8
+ * 8B/r: mov r16, r/m16
* 8B/r: mov r32, r/m32
* REX.W 8B/r: mov r64, r/m64
*/
- if (vie->op.op_byte == 0x8A)
- size = 1;
- else if (vie->rex_w)
- size = 8;
error = memread(vm, vcpuid, gpa, &val, size, arg);
if (error == 0) {
reg = gpr_map[vie->reg];
@@ -322,23 +352,17 @@ emulate_mov(void *vm, int vcpuid, uint64
* C6/0 mov r/m8, imm8
* REX + C6/0 mov r/m8, imm8
*/
- size = 1;
+ size = 1; /* override for byte operation */
error = memwrite(vm, vcpuid, gpa, vie->immediate, size, arg);
break;
case 0xC7:
/*
- * MOV from imm32 to mem (ModRM:r/m)
+ * MOV from imm16/imm32 to mem (ModRM:r/m)
+ * C7/0 mov r/m16, imm16
* C7/0 mov r/m32, imm32
* REX.W + C7/0 mov r/m64, imm32 (sign-extended to 64-bits)
*/
- val = vie->immediate; /* already sign-extended */
-
- if (vie->rex_w)
- size = 8;
-
- if (size != 8)
- val &= size2mask[size];
-
+ val = vie->immediate & size2mask[size];
error = memwrite(vm, vcpuid, gpa, val, size, arg);
break;
default:
@@ -348,17 +372,6 @@ emulate_mov(void *vm, int vcpuid, uint64
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_movx(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite,
@@ -368,7 +381,7 @@ emulate_movx(void *vm, int vcpuid, uint6
enum vm_reg_name reg;
uint64_t val;
- size = 4;
+ size = vie->opsize;
error = EINVAL;
switch (vie->op.op_byte) {
@@ -377,8 +390,9 @@ emulate_movx(void *vm, int vcpuid, uint6
* 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
+ * 0F B6/r movzx r16, r/m8
+ * 0F B6/r movzx r32, r/m8
+ * REX.W + 0F B6/r movzx r64, r/m8
*/
/* get the first operand */
@@ -389,8 +403,8 @@ emulate_movx(void *vm, int vcpuid, uint6
/* get the second operand */
reg = gpr_map[vie->reg];
- if (vie->rex_w)
- size = 8;
+ /* zero-extend byte */
+ val = (uint8_t)val;
/* write the result */
error = vie_update_register(vm, vcpuid, reg, val, size);
@@ -400,8 +414,9 @@ emulate_movx(void *vm, int vcpuid, uint6
* MOV and sign extend byte from mem (ModRM:r/m) to
* reg (ModRM:reg).
*
- * 0F BE/r movsx r/m8, r32
- * REX.W + 0F BE/r movsx r/m8, r64
+ * 0F BE/r movsx r16, r/m8
+ * 0F BE/r movsx r32, r/m8
+ * REX.W + 0F BE/r movsx r64, r/m8
*/
/* get the first operand */
@@ -412,9 +427,6 @@ emulate_movx(void *vm, int vcpuid, uint6
/* get the second operand */
reg = gpr_map[vie->reg];
- if (vie->rex_w)
- size = 8;
-
/* sign extend byte */
val = (int8_t)val;
@@ -435,7 +447,7 @@ emulate_and(void *vm, int vcpuid, uint64
enum vm_reg_name reg;
uint64_t val1, val2;
- size = 4;
+ size = vie->opsize;
error = EINVAL;
switch (vie->op.op_byte) {
@@ -444,11 +456,10 @@ emulate_and(void *vm, int vcpuid, uint64
* AND reg (ModRM:reg) and mem (ModRM:r/m) and store the
* result in reg.
*
+ * 23/r and r16, r/m16
* 23/r and r32, r/m32
* REX.W + 23/r and r64, r/m64
*/
- if (vie->rex_w)
- size = 8;
/* get the first operand */
reg = gpr_map[vie->reg];
@@ -470,8 +481,9 @@ emulate_and(void *vm, int vcpuid, uint64
* AND mem (ModRM:r/m) with immediate and store the
* result in mem.
*
- * 81/ and r/m32, imm32
- * REX.W + 81/ and r/m64, imm32 sign-extended to 64
+ * 81 /4 and r/m16, imm16
+ * 81 /4 and r/m32, imm32
+ * REX.W + 81 /4 and r/m64, imm32 sign-extended to 64
*
* Currently, only the AND operation of the 0x81 opcode
* is implemented (ModRM:reg = b100).
@@ -479,9 +491,6 @@ emulate_and(void *vm, int vcpuid, uint64
if ((vie->reg & 7) != 4)
break;
- if (vie->rex_w)
- size = 8;
-
/* get the first operand */
error = memread(vm, vcpuid, gpa, &val1, size, arg);
if (error)
@@ -507,7 +516,7 @@ emulate_or(void *vm, int vcpuid, uint64_
int error, size;
uint64_t val1;
- size = 4;
+ size = vie->opsize;
error = EINVAL;
switch (vie->op.op_byte) {
@@ -516,8 +525,9 @@ emulate_or(void *vm, int vcpuid, uint64_
* OR mem (ModRM:r/m) with immediate and store the
* result in mem.
*
- * 83/ OR r/m32, imm8 sign-extended to 32
- * REX.W + 83/ OR r/m64, imm8 sign-extended to 64
+ * 83 /1 OR r/m16, imm8 sign-extended to 16
+ * 83 /1 OR r/m32, imm8 sign-extended to 32
+ * REX.W + 83/1 OR r/m64, imm8 sign-extended to 64
*
* Currently, only the OR operation of the 0x83 opcode
* is implemented (ModRM:reg = b001).
@@ -525,9 +535,6 @@ emulate_or(void *vm, int vcpuid, uint64_
if ((vie->reg & 7) != 1)
break;
- if (vie->rex_w)
- size = 8;
-
/* get the first operand */
error = memread(vm, vcpuid, gpa, &val1, size, arg);
if (error)
@@ -651,7 +658,7 @@ vie_calculate_gla(enum vm_cpu_mode cpu_m
* then the descriptor is unusable and attempting to use
* it results in a #GP(0).
*/
- if (SEG_DESC_UNUSABLE(desc))
+ if (SEG_DESC_UNUSABLE(desc->access))
return (-1);
/*
@@ -660,13 +667,13 @@ vie_calculate_gla(enum vm_cpu_mode cpu_m
* descriptor that is not present. If this was the case then
* it would have been checked before the VM-exit.
*/
- KASSERT(SEG_DESC_PRESENT(desc), ("segment %d not present: %#x",
- seg, desc->access));
+ KASSERT(SEG_DESC_PRESENT(desc->access),
+ ("segment %d not present: %#x", seg, desc->access));
/*
* The descriptor type must indicate a code/data segment.
*/
- type = SEG_DESC_TYPE(desc);
+ type = SEG_DESC_TYPE(desc->access);
KASSERT(type >= 16 && type <= 31, ("segment %d has invalid "
"descriptor type %#x", seg, type));
@@ -695,7 +702,8 @@ vie_calculate_gla(enum vm_cpu_mode cpu_m
if ((type & 0xC) == 0x4) {
/* expand-down data segment */
low_limit = desc->limit + 1;
- high_limit = SEG_DESC_DEF32(desc) ? 0xffffffff : 0xffff;
+ high_limit = SEG_DESC_DEF32(desc->access) ?
+ 0xffffffff : 0xffff;
} else {
/* code segment or expand-up data segment */
low_limit = 0;
@@ -1022,24 +1030,65 @@ vie_advance(struct vie *vie)
}
static int
-decode_rex(struct vie *vie)
+decode_prefixes(struct vie *vie, enum vm_cpu_mode cpu_mode, int cs_d)
{
uint8_t x;
- if (vie_peek(vie, &x))
- return (-1);
+ while (1) {
+ if (vie_peek(vie, &x))
+ return (-1);
- if (x >= 0x40 && x <= 0x4F) {
- vie->rex_present = 1;
+ if (x == 0x66)
+ vie->opsize_override = 1;
+ else if (x == 0x67)
+ vie->addrsize_override = 1;
+ else
+ break;
+ vie_advance(vie);
+ }
+
+ /*
+ * From section 2.2.1, "REX Prefixes", Intel SDM Vol 2:
+ * - Only one REX prefix is allowed per instruction.
+ * - The REX prefix must immediately precede the opcode byte or the
+ * escape opcode byte.
+ * - If an instruction has a mandatory prefix (0x66, 0xF2 or 0xF3)
+ * the mandatory prefix must come before the REX prefix.
+ */
+ if (cpu_mode == CPU_MODE_64BIT && x >= 0x40 && x <= 0x4F) {
+ vie->rex_present = 1;
vie->rex_w = x & 0x8 ? 1 : 0;
vie->rex_r = x & 0x4 ? 1 : 0;
vie->rex_x = x & 0x2 ? 1 : 0;
vie->rex_b = x & 0x1 ? 1 : 0;
-
vie_advance(vie);
}
+ /*
+ * Section "Operand-Size And Address-Size Attributes", Intel SDM, Vol 1
+ */
+ if (cpu_mode == CPU_MODE_64BIT) {
+ /*
+ * Default address size is 64-bits and default operand size
+ * is 32-bits.
+ */
+ vie->addrsize = vie->addrsize_override ? 4 : 8;
+ if (vie->rex_w)
+ vie->opsize = 8;
+ else if (vie->opsize_override)
+ vie->opsize = 2;
+ else
+ vie->opsize = 4;
+ } else if (cs_d) {
+ /* Default address and operand sizes are 32-bits */
+ vie->addrsize = vie->addrsize_override ? 2 : 4;
+ vie->opsize = vie->opsize_override ? 2 : 4;
+ } else {
+ /* Default address and operand sizes are 16-bits */
+ vie->addrsize = vie->addrsize_override ? 4 : 2;
+ vie->opsize = vie->opsize_override ? 4 : 2;
+ }
return (0);
}
@@ -1086,6 +1135,9 @@ decode_modrm(struct vie *vie, enum vm_cp
{
uint8_t x;
+ if (cpu_mode == CPU_MODE_REAL)
+ return (-1);
+
if (vie_peek(vie, &x))
return (-1);
@@ -1262,22 +1314,44 @@ decode_immediate(struct vie *vie)
int i, n;
uint8_t x;
union {
- char buf[4];
+ char buf[8];
int8_t signed8;
+ int16_t signed16;
int32_t signed32;
+ int64_t signed64;
} u;
/* Figure out immediate operand size (if any) */
- if (vie->op.op_flags & VIE_OP_F_IMM)
- vie->imm_bytes = 4;
- else if (vie->op.op_flags & VIE_OP_F_IMM8)
+ if (vie->op.op_flags & VIE_OP_F_MOFFSET) {
+ /*
+ * Section 2.2.1.4, "Direct Memory-Offset MOVs", Intel SDM:
+ * The memory offset size follows the address-size of the
+ * instruction. Although this is treated as an immediate
+ * value during instruction decoding it is interpreted as
+ * a segment offset by the instruction emulation.
+ */
+ vie->imm_bytes = vie->addrsize;
+ } else if (vie->op.op_flags & VIE_OP_F_IMM) {
+ /*
+ * Section 2.2.1.5 "Immediates", Intel SDM:
+ * In 64-bit mode the typical size of immediate operands
+ * remains 32-bits. When the operand size if 64-bits, the
+ * processor sign-extends all immediates to 64-bits prior
+ * to their use.
+ */
+ if (vie->opsize == 4 || vie->opsize == 8)
+ vie->imm_bytes = 4;
+ else
+ vie->imm_bytes = 2;
+ } else if (vie->op.op_flags & VIE_OP_F_IMM8) {
vie->imm_bytes = 1;
+ }
if ((n = vie->imm_bytes) == 0)
return (0);
- if (n != 1 && n != 4)
- panic("decode_immediate: invalid imm_bytes %d", n);
+ KASSERT(n == 1 || n == 2 || n == 4 || n == 8,
+ ("%s: invalid number of immediate bytes: %d", __func__, n));
for (i = 0; i < n; i++) {
if (vie_peek(vie, &x))
@@ -1286,11 +1360,25 @@ decode_immediate(struct vie *vie)
u.buf[i] = x;
vie_advance(vie);
}
-
+
+ /* sign-extend the immediate value before use */
if (n == 1)
- vie->immediate = u.signed8; /* sign-extended */
+ vie->immediate = u.signed8;
+ else if (n == 2)
+ vie->immediate = u.signed16;
+ else if (n == 4)
+ vie->immediate = u.signed32;
else
- vie->immediate = u.signed32; /* sign-extended */
+ vie->immediate = u.signed64;
+
+
+ if (vie->op.op_flags & VIE_OP_F_MOFFSET) {
+ /*
+ * If the immediate value is going to be interpreted as a
+ * segment offset then undo the sign-extension above.
+ */
+ vie->immediate &= size2mask[n];
+ }
return (0);
}
@@ -1316,7 +1404,7 @@ static int
verify_gla(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie)
{
int error;
- uint64_t base, idx;
+ uint64_t base, idx, gla2;
/* Skip 'gla' verification */
if (gla == VIE_INVALID_GLA)
@@ -1349,11 +1437,14 @@ verify_gla(struct vm *vm, int cpuid, uin
}
}
- if (base + vie->scale * idx + vie->displacement != gla) {
+ /* XXX assuming that the base address of the segment is 0 */
+ gla2 = base + vie->scale * idx + vie->displacement;
+ gla2 &= size2mask[vie->addrsize];
+ if (gla != gla2) {
printf("verify_gla mismatch: "
"base(0x%0lx), scale(%d), index(0x%0lx), "
- "disp(0x%0lx), gla(0x%0lx)\n",
- base, vie->scale, idx, vie->displacement, gla);
+ "disp(0x%0lx), gla(0x%0lx), gla2(0x%0lx)\n",
+ base, vie->scale, idx, vie->displacement, gla, gla2);
return (-1);
}
@@ -1362,13 +1453,11 @@ verify_gla(struct vm *vm, int cpuid, uin
int
vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla,
- enum vm_cpu_mode cpu_mode, struct vie *vie)
+ enum vm_cpu_mode cpu_mode, int cs_d, struct vie *vie)
{
- if (cpu_mode == CPU_MODE_64BIT) {
- if (decode_rex(vie))
- return (-1);
- }
+ if (decode_prefixes(vie, cpu_mode, cs_d))
+ return (-1);
if (decode_opcode(vie))
return (-1);
More information about the svn-src-all
mailing list