Deleted Added
full compact
27c27
< * $FreeBSD: projects/bhyve/sys/amd64/vmm/vmm_instruction_emul.c 241148 2012-10-03 01:18:51Z neel $
---
> * $FreeBSD: projects/bhyve/sys/amd64/vmm/vmm_instruction_emul.c 243640 2012-11-28 00:02:17Z neel $
31c31
< __FBSDID("$FreeBSD: projects/bhyve/sys/amd64/vmm/vmm_instruction_emul.c 241148 2012-10-03 01:18:51Z neel $");
---
> __FBSDID("$FreeBSD: projects/bhyve/sys/amd64/vmm/vmm_instruction_emul.c 243640 2012-11-28 00:02:17Z neel $");
32a33
> #ifdef _KERNEL
42a44,46
> #else /* !_KERNEL */
> #include <sys/types.h>
> #include <sys/errno.h>
44c48
< #include "vmm_instruction_emul.h"
---
> #include <machine/vmm.h>
46c50,51
< #define GB (1024 * 1024 * 1024)
---
> #include <vmmapi.h>
> #endif /* _KERNEL */
47a53,98
>
>
> /* struct vie_op.op_type */
> enum {
> VIE_OP_TYPE_NONE = 0,
> VIE_OP_TYPE_MOV,
> VIE_OP_TYPE_AND,
> VIE_OP_TYPE_LAST
> };
>
> /* 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 */
>
> static const struct vie_op one_byte_opcodes[256] = {
> [0x89] = {
> .op_byte = 0x89,
> .op_type = VIE_OP_TYPE_MOV,
> },
> [0x8B] = {
> .op_byte = 0x8B,
> .op_type = VIE_OP_TYPE_MOV,
> },
> [0xC7] = {
> .op_byte = 0xC7,
> .op_type = VIE_OP_TYPE_MOV,
> .op_flags = VIE_OP_F_IMM,
> },
> [0x23] = {
> .op_byte = 0x23,
> .op_type = VIE_OP_TYPE_AND,
> }
> };
>
> /* struct vie.mod */
> #define VIE_MOD_INDIRECT 0
> #define VIE_MOD_INDIRECT_DISP8 1
> #define VIE_MOD_INDIRECT_DISP32 2
> #define VIE_MOD_DIRECT 3
>
> /* struct vie.rm */
> #define VIE_RM_SIB 4
> #define VIE_RM_DISP32 5
>
> #define GB (1024 * 1024 * 1024)
>
66a118,335
> static uint64_t size2mask[] = {
> [1] = 0xff,
> [2] = 0xffff,
> [4] = 0xffffffff,
> [8] = 0xffffffffffffffff,
> };
>
> static int
> vie_valid_register(enum vm_reg_name reg)
> {
> #ifdef _KERNEL
> /*
> * XXX
> * The operand register in which we store the result of the
> * read must be a GPR that we can modify even if the vcpu
> * is "running". All the GPRs qualify except for %rsp.
> *
> * This is a limitation of the vm_set_register() API
> * and can be fixed if necessary.
> */
> if (reg == VM_REG_GUEST_RSP)
> return (0);
> #endif
> return (1);
> }
>
> static int
> vie_read_register(void *vm, int vcpuid, enum vm_reg_name reg, uint64_t *rval)
> {
> int error;
>
> if (!vie_valid_register(reg))
> return (EINVAL);
>
> error = vm_get_register(vm, vcpuid, reg, rval);
>
> return (error);
> }
>
> static int
> vie_update_register(void *vm, int vcpuid, enum vm_reg_name reg,
> uint64_t val, int size)
> {
> int error;
> uint64_t origval;
>
> if (!vie_valid_register(reg))
> return (EINVAL);
>
> switch (size) {
> case 1:
> case 2:
> error = vie_read_register(vm, vcpuid, reg, &origval);
> if (error)
> return (error);
> val &= size2mask[size];
> val |= origval & ~size2mask[size];
> break;
> case 4:
> val &= 0xffffffffUL;
> break;
> case 8:
> break;
> default:
> return (EINVAL);
> }
>
> error = vm_set_register(vm, vcpuid, reg, val);
> 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)
> {
> int error, size;
> enum vm_reg_name reg;
> uint64_t val;
>
> size = 4;
> error = EINVAL;
>
> switch (vie->op.op_byte) {
> case 0x89:
> /*
> * MOV from reg (ModRM:reg) to mem (ModRM:r/m)
> * 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) {
> val &= size2mask[size];
> error = memwrite(vm, vcpuid, gpa, val, size, arg);
> }
> break;
> case 0x8B:
> /*
> * MOV from mem (ModRM:r/m) to reg (ModRM:reg)
> * 8B/r: mov r32, r/m32
> * REX.W 8B/r: mov r64, r/m64
> */
> if (vie->rex_w)
> size = 8;
> error = memread(vm, vcpuid, gpa, &val, size, arg);
> if (error == 0) {
> reg = gpr_map[vie->reg];
> error = vie_update_register(vm, vcpuid, reg, val, size);
> }
> break;
> case 0xC7:
> /*
> * MOV from imm32 to mem (ModRM:r/m)
> * 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];
>
> error = memwrite(vm, vcpuid, gpa, val, size, arg);
> 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)
> {
> int error, size;
> enum vm_reg_name reg;
> uint64_t val1, val2;
>
> size = 4;
> error = EINVAL;
>
> switch (vie->op.op_byte) {
> case 0x23:
> /*
> * AND reg (ModRM:reg) and mem (ModRM:r/m) and store the
> * result in reg.
> *
> * 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];
> error = vie_read_register(vm, vcpuid, reg, &val1);
> if (error)
> break;
>
> /* get the second operand */
> error = memread(vm, vcpuid, gpa, &val2, size, arg);
> if (error)
> break;
>
> /* perform the operation and write the result */
> val1 &= val2;
> error = vie_update_register(vm, vcpuid, reg, val1, size);
> break;
> default:
> break;
> }
> return (error);
> }
>
> int
> vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
> mem_region_read_t memread, mem_region_write_t memwrite,
> void *memarg)
> {
> int error;
>
> if (!vie->decoded)
> return (EINVAL);
>
> switch (vie->op.op_type) {
> case VIE_OP_TYPE_MOV:
> error = emulate_mov(vm, vcpuid, gpa, vie,
> memread, memwrite, memarg);
> break;
> case VIE_OP_TYPE_AND:
> error = emulate_and(vm, vcpuid, gpa, vie,
> memread, memwrite, memarg);
> break;
> default:
> error = EINVAL;
> break;
> }
>
> return (error);
> }
>
> #ifdef _KERNEL
73,74d341
< vie->op_size = VIE_OP_SIZE_32BIT;
<
77d343
< vie->operand_register = VM_REG_LAST;
132c398
< vmm_fetch_instruction(struct vm *vm, uint64_t rip, int inst_length,
---
> vmm_fetch_instruction(struct vm *vm, int cpuid, uint64_t rip, int inst_length,
174a441
>
185,186d451
< if (vie->num_processed >= vie->num_valid)
< panic("vie_advance: %d/%d", vie->num_processed, vie->num_valid);
216,221d480
< static const uint8_t flags[256] = {
< [0x89] = VIE_F_HAS_MODRM | VIE_F_FROM_REG | VIE_F_TO_RM,
< [0x8B] = VIE_F_HAS_MODRM | VIE_F_FROM_RM | VIE_F_TO_REG,
< [0xC7] = VIE_F_HAS_MODRM | VIE_F_FROM_IMM | VIE_F_TO_RM,
< };
<
225,226c484
< vie->opcode_byte = x;
< vie->opcode_flags = flags[x];
---
> vie->op = one_byte_opcodes[x];
228,230c486
< vie_advance(vie);
<
< if (vie->opcode_flags == 0)
---
> if (vie->op.op_type == VIE_OP_TYPE_NONE)
232,233c488,490
< else
< return (0);
---
>
> vie_advance(vie);
> return (0);
244,246d500
< if ((vie->opcode_flags & VIE_F_HAS_MODRM) == 0)
< return (0);
<
253a508,515
> /*
> * A direct addressing mode makes no sense in the context of an EPT
> * fault. There has to be a memory access involved to cause the
> * EPT fault.
> */
> if (vie->mod == VIE_MOD_DIRECT)
> return (-1);
>
256,267c518,529
< /*
< * Table 2-5: Special Cases of REX Encodings
< *
< * mod=0, r/m=5 is used in the compatibility mode to
< * indicate a disp32 without a base register.
< *
< * mod!=3, r/m=4 is used in the compatibility mode to
< * indicate that the SIB byte is present.
< *
< * The 'b' bit in the REX prefix is don't care in
< * this case.
< */
---
> /*
> * Table 2-5: Special Cases of REX Encodings
> *
> * mod=0, r/m=5 is used in the compatibility mode to
> * indicate a disp32 without a base register.
> *
> * mod!=3, r/m=4 is used in the compatibility mode to
> * indicate that the SIB byte is present.
> *
> * The 'b' bit in the REX prefix is don't care in
> * this case.
> */
274c536
< /* SIB addressing not supported yet */
---
> /* SIB */
276c538
< return (-1);
---
> goto done;
280,282d541
< if (vie->opcode_flags & (VIE_F_FROM_REG | VIE_F_TO_REG))
< vie->operand_register = gpr_map[vie->reg];
<
298,302c557,558
< /* calculate the operand size */
< if (vie->rex_w)
< vie->op_size = VIE_OP_SIZE_64BIT;
<
< if (vie->opcode_flags & VIE_F_FROM_IMM)
---
> /* Figure out immediate operand size (if any) */
> if (vie->op.op_flags & VIE_OP_F_IMM)
303a560,561
> else if (vie->op.op_flags & VIE_OP_F_IMM8)
> vie->imm_bytes = 1;
304a563
> done:
310a570,633
> decode_sib(struct vie *vie)
> {
> uint8_t x;
>
> /* Proceed only if SIB byte is present */
> if (vie->mod == VIE_MOD_DIRECT || vie->rm != VIE_RM_SIB)
> return (0);
>
> if (vie_peek(vie, &x))
> return (-1);
>
> /* De-construct the SIB byte */
> vie->ss = (x >> 6) & 0x3;
> vie->index = (x >> 3) & 0x7;
> vie->base = (x >> 0) & 0x7;
>
> /* Apply the REX prefix modifiers */
> vie->index |= vie->rex_x << 3;
> vie->base |= vie->rex_b << 3;
>
> switch (vie->mod) {
> case VIE_MOD_INDIRECT_DISP8:
> vie->disp_bytes = 1;
> break;
> case VIE_MOD_INDIRECT_DISP32:
> vie->disp_bytes = 4;
> break;
> }
>
> if (vie->mod == VIE_MOD_INDIRECT &&
> (vie->base == 5 || vie->base == 13)) {
> /*
> * Special case when base register is unused if mod = 0
> * and base = %rbp or %r13.
> *
> * Documented in:
> * Table 2-3: 32-bit Addressing Forms with the SIB Byte
> * Table 2-5: Special Cases of REX Encodings
> */
> vie->disp_bytes = 4;
> } else {
> vie->base_register = gpr_map[vie->base];
> }
>
> /*
> * All encodings of 'index' are valid except for %rsp (4).
> *
> * Documented in:
> * Table 2-3: 32-bit Addressing Forms with the SIB Byte
> * Table 2-5: Special Cases of REX Encodings
> */
> if (vie->index != 4)
> vie->index_register = gpr_map[vie->index];
>
> /* 'scale' makes sense only in the context of an index register */
> if (vie->index_register < VM_REG_LAST)
> vie->scale = 1 << vie->ss;
>
> vie_advance(vie);
>
> return (0);
> }
>
> static int
350a674
> int8_t signed8;
357c681
< if (n != 4)
---
> if (n != 1 && n != 4)
368c692,695
< vie->immediate = u.signed32; /* sign-extended */
---
> if (n == 1)
> vie->immediate = u.signed8; /* sign-extended */
> else
> vie->immediate = u.signed32; /* sign-extended */
372a700,743
> #define VERIFY_GLA
> /*
> * Verify that the 'guest linear address' provided as collateral of the nested
> * page table fault matches with our instruction decoding.
> */
> #ifdef VERIFY_GLA
> static int
> verify_gla(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie)
> {
> int error;
> uint64_t base, idx;
>
> base = 0;
> if (vie->base_register != VM_REG_LAST) {
> error = vm_get_register(vm, cpuid, vie->base_register, &base);
> if (error) {
> printf("verify_gla: error %d getting base reg %d\n",
> error, vie->base_register);
> return (-1);
> }
> }
>
> idx = 0;
> if (vie->index_register != VM_REG_LAST) {
> error = vm_get_register(vm, cpuid, vie->index_register, &idx);
> if (error) {
> printf("verify_gla: error %d getting index reg %d\n",
> error, vie->index_register);
> return (-1);
> }
> }
>
> if (base + vie->scale * idx + vie->displacement != gla) {
> 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);
> return (-1);
> }
>
> return (0);
> }
> #endif /* VERIFY_GLA */
>
374c745
< vmm_decode_instruction(struct vie *vie)
---
> vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie)
375a747
>
384a757,759
> if (decode_sib(vie))
> return (-1);
>
390a766,772
> #ifdef VERIFY_GLA
> if (verify_gla(vm, cpuid, gla, vie))
> return (-1);
> #endif
>
> vie->decoded = 1; /* success */
>
392a775
> #endif /* _KERNEL */