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, &reg, &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, &reg, &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);
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to