]> xenbits.xensource.com Git - people/royger/xen.git/commitdiff
x86emul: correct FPU code/data pointers and opcode handling
authorJan Beulich <jbeulich@suse.com>
Tue, 21 Mar 2017 14:13:42 +0000 (15:13 +0100)
committerJan Beulich <jbeulich@suse.com>
Tue, 21 Mar 2017 14:13:42 +0000 (15:13 +0100)
Prevent leaking the hypervisor ones (stored by hardware during stub
execution), at once making sure the guest sees correct values there.
This piggybacks on the backout logic used to deal with write faults of
FPU insns.

Deliberately ignore the NO_FPU_SEL feature here: Honoring it would
merely mean extra code with no benefit (once we XRSTOR state, the
selector values will simply be lost anyway).

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

index 574be311ae81f7902eb8afe5e19e1509c4fe5df6..cea0595ae29b98be5b27f442b11b008f7d295e5f 100644 (file)
@@ -140,7 +140,8 @@ int emul_test_get_fpu(
 
 void emul_test_put_fpu(
     struct x86_emulate_ctxt *ctxt,
-    enum x86_emulate_fpu_type backout)
+    enum x86_emulate_fpu_type backout,
+    const struct x86_emul_fpu_aux *aux)
 {
     /* TBD */
 }
index f1857d119384c2d42132cf8b343427b24f9a2fed..b90101004febc212f5e3a011e779372c8a17d9ae 100644 (file)
@@ -181,4 +181,5 @@ int emul_test_get_fpu(
 
 void emul_test_put_fpu(
     struct x86_emulate_ctxt *ctxt,
-    enum x86_emulate_fpu_type backout);
+    enum x86_emulate_fpu_type backout,
+    const struct x86_emul_fpu_aux *aux);
index 3f118a06a8c7b88c627999ee6c2ab76a15d9831d..2d92957c1090b669de50410eb739a901acfdcf66 100644 (file)
@@ -1660,12 +1660,73 @@ static int hvmemul_get_fpu(
 
 static void hvmemul_put_fpu(
     struct x86_emulate_ctxt *ctxt,
-    enum x86_emulate_fpu_type backout)
+    enum x86_emulate_fpu_type backout,
+    const struct x86_emul_fpu_aux *aux)
 {
     struct vcpu *curr = current;
 
     curr->arch.hvm_vcpu.fpu_exception_callback = NULL;
 
+    if ( aux )
+    {
+        typeof(curr->arch.xsave_area->fpu_sse) *fpu_ctxt = curr->arch.fpu_ctxt;
+        bool dval = aux->dval;
+        int mode = hvm_guest_x86_mode(curr);
+
+        ASSERT(backout == X86EMUL_FPU_none);
+        /*
+         * Latch current register state so that we can replace FIP/FDP/FOP
+         * (which have values resulting from our own invocation of the FPU
+         * instruction during emulation).
+         * NB: See also the comment in hvmemul_get_fpu(); we don't need to
+         * set ->fpu_dirtied here as it is going to be cleared below, and
+         * we also don't need to reload FCW as we're forcing full state to
+         * be reloaded anyway.
+         */
+        save_fpu_enable();
+
+        if ( boot_cpu_has(X86_FEATURE_FDP_EXCP_ONLY) &&
+             !(fpu_ctxt->fsw & ~fpu_ctxt->fcw & 0x003f) )
+            dval = false;
+
+        switch ( mode )
+        {
+        case 8:
+            fpu_ctxt->fip.addr = aux->ip;
+            if ( dval )
+                fpu_ctxt->fdp.addr = aux->dp;
+            fpu_ctxt->x[FPU_WORD_SIZE_OFFSET] = 8;
+            break;
+
+        case 4: case 2:
+            fpu_ctxt->fip.offs = aux->ip;
+            fpu_ctxt->fip.sel  = aux->cs;
+            if ( dval )
+            {
+                fpu_ctxt->fdp.offs = aux->dp;
+                fpu_ctxt->fdp.sel  = aux->ds;
+            }
+            fpu_ctxt->x[FPU_WORD_SIZE_OFFSET] = mode;
+            break;
+
+        case 0: case 1:
+            fpu_ctxt->fip.addr = aux->ip | (aux->cs << 4);
+            if ( dval )
+                fpu_ctxt->fdp.addr = aux->dp | (aux->ds << 4);
+            fpu_ctxt->x[FPU_WORD_SIZE_OFFSET] = 2;
+            break;
+
+        default:
+            ASSERT_UNREACHABLE();
+            return;
+        }
+
+        fpu_ctxt->fop = aux->op;
+
+        /* Re-use backout code below. */
+        backout = X86EMUL_FPU_fpu;
+    }
+
     if ( backout == X86EMUL_FPU_fpu )
     {
         /*
index 19c38b24ab68c6dcc15f40f6afda647a5832c792..bb67be65bccf71108a594e1d4eaeb465454d597c 100644 (file)
@@ -536,6 +536,55 @@ struct operand {
         unsigned long    off;
     } mem;
 };
+
+struct x86_emulate_state {
+    unsigned int op_bytes, ad_bytes;
+
+    enum {
+        ext_none = vex_none,
+        ext_0f   = vex_0f,
+        ext_0f38 = vex_0f38,
+        ext_0f3a = vex_0f3a,
+        /*
+         * For XOP use values such that the respective instruction field
+         * can be used without adjustment.
+         */
+        ext_8f08 = 8,
+        ext_8f09,
+        ext_8f0a,
+    } ext;
+    uint8_t modrm, modrm_mod, modrm_reg, modrm_rm;
+    uint8_t rex_prefix;
+    bool lock_prefix;
+    bool not_64bit; /* Instruction not available in 64bit. */
+    bool fpu_ctrl;  /* Instruction is an FPU control one. */
+    opcode_desc_t desc;
+    union vex vex;
+    union evex evex;
+    enum simd_opsize simd_size;
+
+    /*
+     * Data operand effective address (usually computed from ModRM).
+     * Default is a memory operand relative to segment DS.
+     */
+    struct operand ea;
+
+    /* Immediate operand values, if any. Use otherwise unused fields. */
+#define imm1 ea.val
+#define imm2 ea.orig_val
+
+    unsigned long ip;
+    struct cpu_user_regs *regs;
+
+#ifndef NDEBUG
+    /*
+     * Track caller of x86_decode_insn() to spot missing as well as
+     * premature calls to x86_emulate_free_state().
+     */
+    void *caller;
+#endif
+};
+
 #ifdef __x86_64__
 #define PTR_POISON ((void *)0x8086000000008086UL) /* non-canonical */
 #else
@@ -1028,13 +1077,48 @@ do {                                                            \
 static void put_fpu(
     struct fpu_insn_ctxt *fic,
     bool failed_late,
+    const struct x86_emulate_state *state,
     struct x86_emulate_ctxt *ctxt,
     const struct x86_emulate_ops *ops)
 {
     if ( unlikely(failed_late) && fic->type == X86EMUL_FPU_fpu )
-        ops->put_fpu(ctxt, X86EMUL_FPU_fpu);
+        ops->put_fpu(ctxt, X86EMUL_FPU_fpu, NULL);
+    else if ( unlikely(fic->type == X86EMUL_FPU_fpu) && !state->fpu_ctrl )
+    {
+        struct x86_emul_fpu_aux aux = {
+            .ip = ctxt->regs->r(ip),
+            .cs = ctxt->regs->cs,
+            .op = ((ctxt->opcode & 7) << 8) | state->modrm,
+        };
+        struct segment_register sreg;
+
+        if ( ops->read_segment &&
+             ops->read_segment(x86_seg_cs, &sreg, ctxt) == X86EMUL_OKAY )
+            aux.cs = sreg.sel;
+        if ( state->ea.type == OP_MEM )
+        {
+            aux.dp = state->ea.mem.off;
+            if ( ops->read_segment &&
+                 ops->read_segment(state->ea.mem.seg, &sreg,
+                                   ctxt) == X86EMUL_OKAY )
+                aux.ds = sreg.sel;
+            else
+                switch ( state->ea.mem.seg )
+                {
+                case x86_seg_cs: aux.ds = ctxt->regs->cs; break;
+                case x86_seg_ds: aux.ds = ctxt->regs->ds; break;
+                case x86_seg_es: aux.ds = ctxt->regs->es; break;
+                case x86_seg_fs: aux.ds = ctxt->regs->fs; break;
+                case x86_seg_gs: aux.ds = ctxt->regs->gs; break;
+                case x86_seg_ss: aux.ds = ctxt->regs->ss; break;
+                default:         ASSERT_UNREACHABLE();    break;
+                }
+            aux.dval = true;
+        }
+        ops->put_fpu(ctxt, X86EMUL_FPU_none, &aux);
+    }
     else if ( fic->type != X86EMUL_FPU_none && ops->put_fpu )
-        ops->put_fpu(ctxt, X86EMUL_FPU_none);
+        ops->put_fpu(ctxt, X86EMUL_FPU_none, NULL);
     fic->type = X86EMUL_FPU_none;
 }
 
@@ -2088,53 +2172,6 @@ int x86emul_unhandleable_rw(
     return X86EMUL_UNHANDLEABLE;
 }
 
-struct x86_emulate_state {
-    unsigned int op_bytes, ad_bytes;
-
-    enum {
-        ext_none = vex_none,
-        ext_0f   = vex_0f,
-        ext_0f38 = vex_0f38,
-        ext_0f3a = vex_0f3a,
-        /*
-         * For XOP use values such that the respective instruction field
-         * can be used without adjustment.
-         */
-        ext_8f08 = 8,
-        ext_8f09,
-        ext_8f0a,
-    } ext;
-    uint8_t modrm, modrm_mod, modrm_reg, modrm_rm;
-    uint8_t rex_prefix;
-    bool lock_prefix;
-    bool not_64bit; /* Instruction not available in 64bit. */
-    opcode_desc_t desc;
-    union vex vex;
-    union evex evex;
-    enum simd_opsize simd_size;
-
-    /*
-     * Data operand effective address (usually computed from ModRM).
-     * Default is a memory operand relative to segment DS.
-     */
-    struct operand ea;
-
-    /* Immediate operand values, if any. Use otherwise unused fields. */
-#define imm1 ea.val
-#define imm2 ea.orig_val
-
-    unsigned long ip;
-    struct cpu_user_regs *regs;
-
-#ifndef NDEBUG
-    /*
-     * Track caller of x86_decode_insn() to spot missing as well as
-     * premature calls to x86_emulate_free_state().
-     */
-    void *caller;
-#endif
-};
-
 /* Helper definitions. */
 #define op_bytes (state->op_bytes)
 #define ad_bytes (state->ad_bytes)
@@ -4251,8 +4288,10 @@ x86_emulate(
                 dst.bytes = 4;
                 break;
             case 4: /* fldenv - TODO */
+                state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 5: /* fldcw m2byte */
+                state->fpu_ctrl = true;
                 if ( (rc = ops->read(ea.mem.seg, ea.mem.off, &src.val,
                                      2, ctxt)) != X86EMUL_OKAY )
                     goto done;
@@ -4260,8 +4299,10 @@ x86_emulate(
                 dst.type = OP_NONE;
                 break;
             case 6: /* fnstenv - TODO */
+                state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 7: /* fnstcw m2byte */
+                state->fpu_ctrl = true;
                 emulate_fpu_insn_memdst("fnstcw", dst.val);
                 dst.bytes = 2;
                 break;
@@ -4350,6 +4391,7 @@ x86_emulate(
         case 0xe3: /* fninit */
         case 0xe4: /* fnsetpm - 287 only, ignored by 387 */
         /* case 0xe5: frstpm - 287 only, #UD on 387 */
+            state->fpu_ctrl = true;
             emulate_fpu_insn_stub(0xdb, modrm);
             break;
         default:
@@ -4493,8 +4535,10 @@ x86_emulate(
                 break;
             case 4: /* frstor - TODO */
             case 6: /* fnsave - TODO */
+                state->fpu_ctrl = true;
                 goto cannot_emulate;
             case 7: /* fnstsw m2byte */
+                state->fpu_ctrl = true;
                 emulate_fpu_insn_memdst("fnstsw", dst.val);
                 dst.bytes = 2;
                 break;
@@ -4567,6 +4611,7 @@ x86_emulate(
         {
         case 0xe0:
             /* fnstsw %ax */
+            state->fpu_ctrl = true;
             dst.bytes = 2;
             dst.type = OP_REG;
             dst.reg = (void *)&_regs.ax;
@@ -7914,7 +7959,7 @@ x86_emulate(
     }
 
  complete_insn: /* Commit shadow register state. */
-    put_fpu(&fic, false, ctxt, ops);
+    put_fpu(&fic, false, state, ctxt, ops);
 
     /* Zero the upper 32 bits of %rip if not in 64-bit mode. */
     if ( !mode_64bit() )
@@ -7938,7 +7983,7 @@ x86_emulate(
     ctxt->regs->eflags &= ~X86_EFLAGS_RF;
 
  done:
-    put_fpu(&fic, fic.insn_bytes > 0 && dst.type == OP_MEM, ctxt, ops);
+    put_fpu(&fic, fic.insn_bytes > 0 && dst.type == OP_MEM, state, ctxt, ops);
     put_stub(stub);
     return rc;
 #undef state
index 00b3f828d20077afe8119a47a8f165f99c566101..9c5fcdee53d421634e4cea47b82122ebc344a405 100644 (file)
@@ -135,6 +135,13 @@ struct segment_register {
     uint64_t   base;
 };
 
+struct x86_emul_fpu_aux {
+    unsigned long ip, dp;
+    uint16_t cs, ds;
+    unsigned int op:11;
+    unsigned int dval:1;
+};
+
 /*
  * Return codes from state-accessor functions and from x86_emulate().
  */
@@ -439,10 +446,12 @@ struct x86_emulate_ops
      *  the get_fpu one having got called before!
      * @backout: Undo updates to the specified register file (can, besides
      *           X86EMUL_FPU_none, only be X86EMUL_FPU_fpu at present);
+     * @aux: Packaged up FIP/FDP/FOP values to load into FPU.
      */
     void (*put_fpu)(
         struct x86_emulate_ctxt *ctxt,
-        enum x86_emulate_fpu_type backout);
+        enum x86_emulate_fpu_type backout,
+        const struct x86_emul_fpu_aux *aux);
 
     /* invlpg: Invalidate paging structures which map addressed byte. */
     int (*invlpg)(