KVM: x86 emulator: MMX support
Avi Kivity [Mon, 9 Apr 2012 15:40:02 +0000 (18:40 +0300)]
General support for the MMX instruction set.  Special care is taken
to trap pending x87 exceptions so that they are properly reflected
to the guest.

Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>

arch/x86/include/asm/kvm_emulate.h
arch/x86/kvm/emulate.c

index c222e1a..1ac46c2 100644 (file)
@@ -200,7 +200,7 @@ typedef u32 __attribute__((vector_size(16))) sse128_t;
 
 /* Type, address-of, and value of an instruction's operand. */
 struct operand {
-       enum { OP_REG, OP_MEM, OP_IMM, OP_XMM, OP_NONE } type;
+       enum { OP_REG, OP_MEM, OP_IMM, OP_XMM, OP_MM, OP_NONE } type;
        unsigned int bytes;
        union {
                unsigned long orig_val;
@@ -213,12 +213,14 @@ struct operand {
                        unsigned seg;
                } mem;
                unsigned xmm;
+               unsigned mm;
        } addr;
        union {
                unsigned long val;
                u64 val64;
                char valptr[sizeof(unsigned long) + 2];
                sse128_t vec_val;
+               u64 mm_val;
        };
 };
 
index fb39e0b..0011b4a 100644 (file)
 #define Src2FS      (OpFS << Src2Shift)
 #define Src2GS      (OpGS << Src2Shift)
 #define Src2Mask    (OpMask << Src2Shift)
+#define Mmx         ((u64)1 << 40)  /* MMX Vector instruction */
 #define Aligned     ((u64)1 << 41)  /* Explicitly aligned (e.g. MOVDQA) */
 #define Unaligned   ((u64)1 << 42)  /* Explicitly unaligned (e.g. MOVDQU) */
 #define Avx         ((u64)1 << 43)  /* Advanced Vector Extensions */
@@ -887,6 +888,40 @@ static void write_sse_reg(struct x86_emulate_ctxt *ctxt, sse128_t *data,
        ctxt->ops->put_fpu(ctxt);
 }
 
+static void read_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
+{
+       ctxt->ops->get_fpu(ctxt);
+       switch (reg) {
+       case 0: asm("movq %%mm0, %0" : "=m"(*data)); break;
+       case 1: asm("movq %%mm1, %0" : "=m"(*data)); break;
+       case 2: asm("movq %%mm2, %0" : "=m"(*data)); break;
+       case 3: asm("movq %%mm3, %0" : "=m"(*data)); break;
+       case 4: asm("movq %%mm4, %0" : "=m"(*data)); break;
+       case 5: asm("movq %%mm5, %0" : "=m"(*data)); break;
+       case 6: asm("movq %%mm6, %0" : "=m"(*data)); break;
+       case 7: asm("movq %%mm7, %0" : "=m"(*data)); break;
+       default: BUG();
+       }
+       ctxt->ops->put_fpu(ctxt);
+}
+
+static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg)
+{
+       ctxt->ops->get_fpu(ctxt);
+       switch (reg) {
+       case 0: asm("movq %0, %%mm0" : : "m"(*data)); break;
+       case 1: asm("movq %0, %%mm1" : : "m"(*data)); break;
+       case 2: asm("movq %0, %%mm2" : : "m"(*data)); break;
+       case 3: asm("movq %0, %%mm3" : : "m"(*data)); break;
+       case 4: asm("movq %0, %%mm4" : : "m"(*data)); break;
+       case 5: asm("movq %0, %%mm5" : : "m"(*data)); break;
+       case 6: asm("movq %0, %%mm6" : : "m"(*data)); break;
+       case 7: asm("movq %0, %%mm7" : : "m"(*data)); break;
+       default: BUG();
+       }
+       ctxt->ops->put_fpu(ctxt);
+}
+
 static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
                                    struct operand *op)
 {
@@ -903,6 +938,13 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
                read_sse_reg(ctxt, &op->vec_val, reg);
                return;
        }
+       if (ctxt->d & Mmx) {
+               reg &= 7;
+               op->type = OP_MM;
+               op->bytes = 8;
+               op->addr.mm = reg;
+               return;
+       }
 
        op->type = OP_REG;
        if (ctxt->d & ByteOp) {
@@ -948,6 +990,12 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
                        read_sse_reg(ctxt, &op->vec_val, ctxt->modrm_rm);
                        return rc;
                }
+               if (ctxt->d & Mmx) {
+                       op->type = OP_MM;
+                       op->bytes = 8;
+                       op->addr.xmm = ctxt->modrm_rm & 7;
+                       return rc;
+               }
                fetch_register_operand(op);
                return rc;
        }
@@ -1415,6 +1463,9 @@ static int writeback(struct x86_emulate_ctxt *ctxt)
        case OP_XMM:
                write_sse_reg(ctxt, &ctxt->dst.vec_val, ctxt->dst.addr.xmm);
                break;
+       case OP_MM:
+               write_mmx_reg(ctxt, &ctxt->dst.mm_val, ctxt->dst.addr.mm);
+               break;
        case OP_NONE:
                /* no writeback */
                break;
@@ -3987,6 +4038,8 @@ done_prefixes:
 
        if (ctxt->d & Sse)
                ctxt->op_bytes = 16;
+       else if (ctxt->d & Mmx)
+               ctxt->op_bytes = 8;
 
        /* ModRM and SIB bytes. */
        if (ctxt->d & ModRM) {
@@ -4057,6 +4110,35 @@ static bool string_insn_completed(struct x86_emulate_ctxt *ctxt)
        return false;
 }
 
+static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
+{
+       bool fault = false;
+
+       ctxt->ops->get_fpu(ctxt);
+       asm volatile("1: fwait \n\t"
+                    "2: \n\t"
+                    ".pushsection .fixup,\"ax\" \n\t"
+                    "3: \n\t"
+                    "movb $1, %[fault] \n\t"
+                    "jmp 2b \n\t"
+                    ".popsection \n\t"
+                    _ASM_EXTABLE(1b, 3b)
+                    : [fault]"+rm"(fault));
+       ctxt->ops->put_fpu(ctxt);
+
+       if (unlikely(fault))
+               return emulate_exception(ctxt, MF_VECTOR, 0, false);
+
+       return X86EMUL_CONTINUE;
+}
+
+static void fetch_possible_mmx_operand(struct x86_emulate_ctxt *ctxt,
+                                      struct operand *op)
+{
+       if (op->type == OP_MM)
+               read_mmx_reg(ctxt, &op->mm_val, op->addr.mm);
+}
+
 int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 {
        struct x86_emulate_ops *ops = ctxt->ops;
@@ -4081,18 +4163,31 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
                goto done;
        }
 
-       if ((ctxt->d & Sse)
-           && ((ops->get_cr(ctxt, 0) & X86_CR0_EM)
-               || !(ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))) {
+       if (((ctxt->d & (Sse|Mmx)) && ((ops->get_cr(ctxt, 0) & X86_CR0_EM)))
+           || ((ctxt->d & Sse) && !(ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))) {
                rc = emulate_ud(ctxt);
                goto done;
        }
 
-       if ((ctxt->d & Sse) && (ops->get_cr(ctxt, 0) & X86_CR0_TS)) {
+       if ((ctxt->d & (Sse|Mmx)) && (ops->get_cr(ctxt, 0) & X86_CR0_TS)) {
                rc = emulate_nm(ctxt);
                goto done;
        }
 
+       if (ctxt->d & Mmx) {
+               rc = flush_pending_x87_faults(ctxt);
+               if (rc != X86EMUL_CONTINUE)
+                       goto done;
+               /*
+                * Now that we know the fpu is exception safe, we can fetch
+                * operands from it.
+                */
+               fetch_possible_mmx_operand(ctxt, &ctxt->src);
+               fetch_possible_mmx_operand(ctxt, &ctxt->src2);
+               if (!(ctxt->d & Mov))
+                       fetch_possible_mmx_operand(ctxt, &ctxt->dst);
+       }
+
        if (unlikely(ctxt->guest_mode) && ctxt->intercept) {
                rc = emulator_check_intercept(ctxt, ctxt->intercept,
                                              X86_ICPT_PRE_EXCEPT);