KVM: x86 emulator: MMX support
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>
This commit is contained in:
committed by
Marcelo Tosatti
parent
3e114eb4db
commit
cbe2c9d30a
@@ -200,7 +200,7 @@ typedef u32 __attribute__((vector_size(16))) sse128_t;
|
|||||||
|
|
||||||
/* Type, address-of, and value of an instruction's operand. */
|
/* Type, address-of, and value of an instruction's operand. */
|
||||||
struct 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;
|
unsigned int bytes;
|
||||||
union {
|
union {
|
||||||
unsigned long orig_val;
|
unsigned long orig_val;
|
||||||
@@ -213,12 +213,14 @@ struct operand {
|
|||||||
unsigned seg;
|
unsigned seg;
|
||||||
} mem;
|
} mem;
|
||||||
unsigned xmm;
|
unsigned xmm;
|
||||||
|
unsigned mm;
|
||||||
} addr;
|
} addr;
|
||||||
union {
|
union {
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
u64 val64;
|
u64 val64;
|
||||||
char valptr[sizeof(unsigned long) + 2];
|
char valptr[sizeof(unsigned long) + 2];
|
||||||
sse128_t vec_val;
|
sse128_t vec_val;
|
||||||
|
u64 mm_val;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -142,6 +142,7 @@
|
|||||||
#define Src2FS (OpFS << Src2Shift)
|
#define Src2FS (OpFS << Src2Shift)
|
||||||
#define Src2GS (OpGS << Src2Shift)
|
#define Src2GS (OpGS << Src2Shift)
|
||||||
#define Src2Mask (OpMask << Src2Shift)
|
#define Src2Mask (OpMask << Src2Shift)
|
||||||
|
#define Mmx ((u64)1 << 40) /* MMX Vector instruction */
|
||||||
#define Aligned ((u64)1 << 41) /* Explicitly aligned (e.g. MOVDQA) */
|
#define Aligned ((u64)1 << 41) /* Explicitly aligned (e.g. MOVDQA) */
|
||||||
#define Unaligned ((u64)1 << 42) /* Explicitly unaligned (e.g. MOVDQU) */
|
#define Unaligned ((u64)1 << 42) /* Explicitly unaligned (e.g. MOVDQU) */
|
||||||
#define Avx ((u64)1 << 43) /* Advanced Vector Extensions */
|
#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);
|
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,
|
static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
|
||||||
struct operand *op)
|
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);
|
read_sse_reg(ctxt, &op->vec_val, reg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ctxt->d & Mmx) {
|
||||||
|
reg &= 7;
|
||||||
|
op->type = OP_MM;
|
||||||
|
op->bytes = 8;
|
||||||
|
op->addr.mm = reg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
op->type = OP_REG;
|
op->type = OP_REG;
|
||||||
if (ctxt->d & ByteOp) {
|
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);
|
read_sse_reg(ctxt, &op->vec_val, ctxt->modrm_rm);
|
||||||
return rc;
|
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);
|
fetch_register_operand(op);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1415,6 +1463,9 @@ static int writeback(struct x86_emulate_ctxt *ctxt)
|
|||||||
case OP_XMM:
|
case OP_XMM:
|
||||||
write_sse_reg(ctxt, &ctxt->dst.vec_val, ctxt->dst.addr.xmm);
|
write_sse_reg(ctxt, &ctxt->dst.vec_val, ctxt->dst.addr.xmm);
|
||||||
break;
|
break;
|
||||||
|
case OP_MM:
|
||||||
|
write_mmx_reg(ctxt, &ctxt->dst.mm_val, ctxt->dst.addr.mm);
|
||||||
|
break;
|
||||||
case OP_NONE:
|
case OP_NONE:
|
||||||
/* no writeback */
|
/* no writeback */
|
||||||
break;
|
break;
|
||||||
@@ -3987,6 +4038,8 @@ done_prefixes:
|
|||||||
|
|
||||||
if (ctxt->d & Sse)
|
if (ctxt->d & Sse)
|
||||||
ctxt->op_bytes = 16;
|
ctxt->op_bytes = 16;
|
||||||
|
else if (ctxt->d & Mmx)
|
||||||
|
ctxt->op_bytes = 8;
|
||||||
|
|
||||||
/* ModRM and SIB bytes. */
|
/* ModRM and SIB bytes. */
|
||||||
if (ctxt->d & ModRM) {
|
if (ctxt->d & ModRM) {
|
||||||
@@ -4057,6 +4110,35 @@ static bool string_insn_completed(struct x86_emulate_ctxt *ctxt)
|
|||||||
return false;
|
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)
|
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
|
||||||
{
|
{
|
||||||
struct x86_emulate_ops *ops = ctxt->ops;
|
struct x86_emulate_ops *ops = ctxt->ops;
|
||||||
@@ -4081,18 +4163,31 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ctxt->d & Sse)
|
if (((ctxt->d & (Sse|Mmx)) && ((ops->get_cr(ctxt, 0) & X86_CR0_EM)))
|
||||||
&& ((ops->get_cr(ctxt, 0) & X86_CR0_EM)
|
|| ((ctxt->d & Sse) && !(ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))) {
|
||||||
|| !(ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))) {
|
|
||||||
rc = emulate_ud(ctxt);
|
rc = emulate_ud(ctxt);
|
||||||
goto done;
|
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);
|
rc = emulate_nm(ctxt);
|
||||||
goto done;
|
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) {
|
if (unlikely(ctxt->guest_mode) && ctxt->intercept) {
|
||||||
rc = emulator_check_intercept(ctxt, ctxt->intercept,
|
rc = emulator_check_intercept(ctxt, ctxt->intercept,
|
||||||
X86_ICPT_PRE_EXCEPT);
|
X86_ICPT_PRE_EXCEPT);
|
||||||
|
|||||||
Reference in New Issue
Block a user