diff options
Diffstat (limited to 'usr/src/uts/intel/io/vmm/vmm_instruction_emul.c')
-rw-r--r-- | usr/src/uts/intel/io/vmm/vmm_instruction_emul.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c b/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c index 14c78f411d..9fbb923cd9 100644 --- a/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c +++ b/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c @@ -182,6 +182,7 @@ enum { VIE_OP_TYPE_TEST, VIE_OP_TYPE_BEXTR, VIE_OP_TYPE_CLTS, + VIE_OP_TYPE_MUL, VIE_OP_TYPE_LAST }; @@ -220,6 +221,10 @@ static const struct vie_op two_byte_opcodes[256] = { .op_byte = 0xAE, .op_type = VIE_OP_TYPE_TWOB_GRP15, }, + [0xAF] = { + .op_byte = 0xAF, + .op_type = VIE_OP_TYPE_MUL, + }, [0xB6] = { .op_byte = 0xB6, .op_type = VIE_OP_TYPE_MOVZX, @@ -419,6 +424,25 @@ static enum vm_reg_name gpr_map[16] = { VM_REG_GUEST_R15 }; +static const char *gpr_name_map[][16] = { + [1] = { + "a[hl]", "c[hl]", "d[hl]", "b[hl]", "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", + }, + [2] = { + "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", + }, + [4] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", + }, + [8] = { + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + }, +}; + static enum vm_reg_name cr_map[16] = { VM_REG_GUEST_CR0, VM_REG_LAST, @@ -477,6 +501,14 @@ vie_regnum_map(uint8_t regnum) return (gpr_map[regnum]); } +const char * +vie_regnum_name(uint8_t regnum, uint8_t size) +{ + VERIFY3U(regnum, <, 16); + VERIFY(size == 1 || size == 2 || size == 4 || size == 8); + return (gpr_name_map[size][regnum]); +} + static void vie_calc_bytereg(struct vie *vie, enum vm_reg_name *reg, int *lhbr) { @@ -679,6 +711,40 @@ getaddflags(int opsize, uint64_t x, uint64_t y) } /* + * Macro creation of functions getimulflags{16,32,64} + */ +/* BEGIN CSTYLED */ +#define GETIMULFLAGS(sz) \ +static ulong_t \ +getimulflags##sz(uint##sz##_t x, uint##sz##_t y) \ +{ \ + ulong_t rflags; \ + \ + __asm __volatile("imul %2,%1; pushfq; popq %0" : \ + "=r" (rflags), "+r" (x) : "m" (y)); \ + return (rflags); \ +} struct __hack +/* END CSTYLED */ + +GETIMULFLAGS(16); +GETIMULFLAGS(32); +GETIMULFLAGS(64); + +static ulong_t +getimulflags(int opsize, uint64_t x, uint64_t y) +{ + KASSERT(opsize == 2 || opsize == 4 || opsize == 8, + ("getimulflags: invalid operand size %d", opsize)); + + if (opsize == 2) + return (getimulflags16(x, y)); + else if (opsize == 4) + return (getimulflags32(x, y)); + else + return (getimulflags64(x, y)); +} + +/* * Return the status flags that would result from doing (x & y). */ /* BEGIN CSTYLED */ @@ -1843,6 +1909,76 @@ vie_emulate_sub(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa) } static int +vie_emulate_mul(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa) +{ + int error, size; + uint64_t rflags, rflags2, val1, val2; + __int128_t nval; + enum vm_reg_name reg; + ulong_t (*getflags)(int, uint64_t, uint64_t) = NULL; + + size = vie->opsize; + error = EINVAL; + + switch (vie->op.op_byte) { + case 0xAF: + /* + * Multiply the contents of a destination register by + * the contents of a register or memory operand and + * put the signed result in the destination register. + * + * AF/r IMUL r16, r/m16 + * AF/r IMUL r32, r/m32 + * REX.W + AF/r IMUL r64, r/m64 + */ + + getflags = getimulflags; + + /* get the first operand */ + reg = gpr_map[vie->reg]; + error = vm_get_register(vm, vcpuid, reg, &val1); + if (error != 0) + break; + + /* get the second operand */ + error = vie_mmio_read(vie, vm, vcpuid, gpa, &val2, size); + if (error != 0) + break; + + /* perform the operation and write the result */ + nval = (int64_t)val1 * (int64_t)val2; + + error = vie_update_register(vm, vcpuid, reg, nval, size); + + DTRACE_PROBE4(vie__imul, + const char *, vie_regnum_name(vie->reg, size), + uint64_t, val1, uint64_t, val2, __uint128_t, nval); + + break; + default: + break; + } + + if (error == 0) { + rflags2 = getflags(size, val1, val2); + error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, + &rflags); + if (error) + return (error); + + rflags &= ~RFLAGS_STATUS_BITS; + rflags |= rflags2 & RFLAGS_STATUS_BITS; + error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, + rflags, 8); + + DTRACE_PROBE2(vie__imul__rflags, + uint64_t, rflags, uint64_t, rflags2); + } + + return (error); +} + +static int vie_emulate_stack_op(struct vie *vie, struct vm *vm, int vcpuid, uint64_t gpa) { struct vm_copyinfo copyinfo[2]; @@ -2244,6 +2380,9 @@ vie_emulate_mmio(struct vie *vie, struct vm *vm, int vcpuid) case VIE_OP_TYPE_BEXTR: error = vie_emulate_bextr(vie, vm, vcpuid, gpa); break; + case VIE_OP_TYPE_MUL: + error = vie_emulate_mul(vie, vm, vcpuid, gpa); + break; default: error = EINVAL; break; |