]> xenbits.xensource.com Git - xen.git/commitdiff
x86emul: honor guest CR0.TS and CR0.EM
authorJan Beulich <jbeulich@suse.com>
Tue, 4 Oct 2016 13:04:46 +0000 (14:04 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 4 Oct 2016 13:04:46 +0000 (14:04 +0100)
We must not emulate any instructions accessing respective registers
when either of these flags is set in the guest view of the register, or
else we may do so on data not belonging to the guest's current task.

Being architecturally required behavior, the logic gets placed in the
instruction emulator instead of hvmemul_get_fpu(). It should be noted,
though, that hvmemul_get_fpu() being the only current handler for the
get_fpu() callback, we don't have an active problem with CR4: Both
CR4.OSFXSR and CR4.OSXSAVE get handled as necessary by that function.

This is XSA-190.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
tools/tests/x86_emulator/test_x86_emulator.c
xen/arch/x86/hvm/emulate.c
xen/arch/x86/x86_emulate/x86_emulate.c
xen/arch/x86/x86_emulate/x86_emulate.h

index d1ad06bf84f07cbe295a168507f79bc4b4b259f7..78323a2e4eeb7315b0b73eee244846eefa7198ad 100644 (file)
@@ -158,6 +158,22 @@ static inline uint64_t xgetbv(uint32_t xcr)
     (ebx & (1U << 5)) != 0; \
 })
 
+static int read_cr(
+    unsigned int reg,
+    unsigned long *val,
+    struct x86_emulate_ctxt *ctxt)
+{
+    /* Fake just enough state for the emulator's _get_fpu() to be happy. */
+    switch ( reg )
+    {
+    case 0:
+        *val = 0x00000001; /* PE */
+        return X86EMUL_OKAY;
+    }
+
+    return X86EMUL_UNHANDLEABLE;
+}
+
 int get_fpu(
     void (*exception_callback)(void *, struct cpu_user_regs *),
     void *exception_callback_arg,
@@ -189,6 +205,7 @@ static struct x86_emulate_ops emulops = {
     .write      = write,
     .cmpxchg    = cmpxchg,
     .cpuid      = cpuid,
+    .read_cr    = read_cr,
     .get_fpu    = get_fpu,
 };
 
index 03d1eec8caea855ad824ba2ab84b3434ce22b5ba..a21ae3c8247275df535573095810ab7535daf6bd 100644 (file)
@@ -1628,14 +1628,14 @@ static int hvmemul_get_fpu(
     switch ( type )
     {
     case X86EMUL_FPU_fpu:
+    case X86EMUL_FPU_wait:
         break;
     case X86EMUL_FPU_mmx:
         if ( !cpu_has_mmx )
             return X86EMUL_UNHANDLEABLE;
         break;
     case X86EMUL_FPU_xmm:
-        if ( (curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_EM) ||
-             !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) )
+        if ( !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) )
             return X86EMUL_UNHANDLEABLE;
         break;
     case X86EMUL_FPU_ymm:
index 142892ca86a3ce8e3d141fd1adb1e7247ee9c650..36e5229622d8abfa38566bb6b92d056a280afe68 100644 (file)
@@ -418,6 +418,9 @@ typedef union {
 
 /* Control register flags. */
 #define CR0_PE    (1<<0)
+#define CR0_MP    (1<<1)
+#define CR0_EM    (1<<2)
+#define CR0_TS    (1<<3)
 #define CR4_TSD   (1<<2)
 #define CR4_UMIP  (1<<11)
 
@@ -446,6 +449,7 @@ typedef union {
 #define EXC_OF  4
 #define EXC_BR  5
 #define EXC_UD  6
+#define EXC_NM  7
 #define EXC_TS 10
 #define EXC_NP 11
 #define EXC_SS 12
@@ -745,10 +749,45 @@ static void fpu_handle_exception(void *_fic, struct cpu_user_regs *regs)
     regs->eip += fic->insn_bytes;
 }
 
+static int _get_fpu(
+    enum x86_emulate_fpu_type type,
+    struct fpu_insn_ctxt *fic,
+    struct x86_emulate_ctxt *ctxt,
+    const struct x86_emulate_ops *ops)
+{
+    int rc;
+
+    fic->exn_raised = 0;
+
+    fail_if(!ops->get_fpu);
+    rc = ops->get_fpu(fpu_handle_exception, fic, type, ctxt);
+
+    if ( rc == X86EMUL_OKAY )
+    {
+        unsigned long cr0;
+
+        fail_if(!ops->read_cr);
+        rc = ops->read_cr(0, &cr0, ctxt);
+        if ( rc != X86EMUL_OKAY )
+            return rc;
+        if ( cr0 & CR0_EM )
+        {
+            generate_exception_if(type == X86EMUL_FPU_fpu, EXC_NM, -1);
+            generate_exception_if(type == X86EMUL_FPU_mmx, EXC_UD, -1);
+            generate_exception_if(type == X86EMUL_FPU_xmm, EXC_UD, -1);
+        }
+        generate_exception_if((cr0 & CR0_TS) &&
+                              (type != X86EMUL_FPU_wait || (cr0 & CR0_MP)),
+                              EXC_NM, -1);
+    }
+
+ done:
+    return rc;
+}
+
 #define get_fpu(_type, _fic)                                    \
-do{ (_fic)->exn_raised = 0;                                     \
-    fail_if(ops->get_fpu == NULL);                              \
-    rc = ops->get_fpu(fpu_handle_exception, _fic, _type, ctxt); \
+do {                                                            \
+    rc = _get_fpu(_type, _fic, ctxt, ops);                      \
     if ( rc ) goto done;                                        \
 } while (0)
 #define _put_fpu()                                              \
@@ -2980,8 +3019,14 @@ x86_emulate(
         break;
 
     case 0x9b:  /* wait/fwait */
-        emulate_fpu_insn("fwait");
+    {
+        struct fpu_insn_ctxt fic = { .insn_bytes = 1 };
+
+        get_fpu(X86EMUL_FPU_wait, &fic);
+        asm volatile ( "fwait" ::: "memory" );
+        put_fpu(&fic);
         break;
+    }
 
     case 0x9c: /* pushf */
         src.val = _regs.eflags;
index 31b3353c99942cce0f2fa188a412f5077fa5f3a2..641711e87dc703859e49206f1e50d77a7f11de8b 100644 (file)
@@ -115,6 +115,7 @@ struct __packed segment_register {
 /* FPU sub-types which may be requested via ->get_fpu(). */
 enum x86_emulate_fpu_type {
     X86EMUL_FPU_fpu, /* Standard FPU coprocessor instruction set */
+    X86EMUL_FPU_wait, /* WAIT/FWAIT instruction */
     X86EMUL_FPU_mmx, /* MMX instruction set (%mm0-%mm7) */
     X86EMUL_FPU_xmm, /* SSE instruction set (%xmm0-%xmm7/15) */
     X86EMUL_FPU_ymm  /* AVX/XOP instruction set (%ymm0-%ymm7/15) */