summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/vmm/vmm_instruction_emul.c
diff options
context:
space:
mode:
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.c139
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;