]> xenbits.xensource.com Git - xen.git/commitdiff
x86_emulate: Implement a more dynamic interface for handling FPU
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 16 Apr 2008 15:10:41 +0000 (16:10 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 16 Apr 2008 15:10:41 +0000 (16:10 +0100)
exceptions, which will allow emulation stubs to be built dynamically
in a future patch.
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
tools/tests/x86_emulate.c
xen/arch/x86/Makefile
xen/arch/x86/hvm/emulate.c
xen/arch/x86/traps.c
xen/arch/x86/x86_emulate.c
xen/arch/x86/x86_emulate/x86_emulate.c
xen/arch/x86/x86_emulate/x86_emulate.h
xen/include/asm-x86/hvm/vcpu.h

index d58f65a38e7a0dfa878d5490b3b3706e272385b4..a58a7b81789699a59d6e44908674ee591c5afa4c 100644 (file)
@@ -4,10 +4,4 @@
 #include <public/xen.h>
 
 #include "x86_emulate/x86_emulate.h"
-
-#define __emulate_fpu_insn(_op)                 \
-do{ rc = X86EMUL_UNHANDLEABLE;                  \
-    goto done;                                  \
-} while (0)
-
 #include "x86_emulate/x86_emulate.c"
index 334a996eb61885f751f738432cff2b510aebc3a4..b94fb78d214a56f2d69d1f7bc85593bab01db6c2 100644 (file)
@@ -52,6 +52,8 @@ obj-y += tboot.o
 
 obj-$(crash_debug) += gdbstub.o
 
+x86_emulate.o: x86_emulate/x86_emulate.c x86_emulate/x86_emulate.h
+
 $(TARGET): $(TARGET)-syms boot/mkelf32
        ./boot/mkelf32 $(TARGET)-syms $(TARGET) 0x100000 \
        `$(NM) -nr $(TARGET)-syms | head -n 1 | sed -e 's/^\([^ ]*\).*/0x\1/'`
index b1d4bb80347b60a0e4cad36e1c97e319e1c89089..5e50fc6f61127d852bf16d487ebefdf4ee63ad91 100644 (file)
@@ -674,11 +674,23 @@ static int hvmemul_inject_sw_interrupt(
     return X86EMUL_OKAY;
 }
 
-static void hvmemul_load_fpu_ctxt(
+static void hvmemul_get_fpu(
+    void (*exception_callback)(void *, struct cpu_user_regs *),
+    void *exception_callback_arg,
     struct x86_emulate_ctxt *ctxt)
 {
-    if ( !current->fpu_dirtied )
+    struct vcpu *curr = current;
+    if ( !curr->fpu_dirtied )
         hvm_funcs.fpu_dirty_intercept();
+    curr->arch.hvm_vcpu.fpu_exception_callback = exception_callback;
+    curr->arch.hvm_vcpu.fpu_exception_callback_arg = exception_callback_arg;
+}
+
+static void hvmemul_put_fpu(
+    struct x86_emulate_ctxt *ctxt)
+{
+    struct vcpu *curr = current;
+    curr->arch.hvm_vcpu.fpu_exception_callback = NULL;
 }
 
 static int hvmemul_invlpg(
@@ -720,7 +732,8 @@ static struct x86_emulate_ops hvm_emulate_ops = {
     .cpuid         = hvmemul_cpuid,
     .inject_hw_exception = hvmemul_inject_hw_exception,
     .inject_sw_interrupt = hvmemul_inject_sw_interrupt,
-    .load_fpu_ctxt = hvmemul_load_fpu_ctxt,
+    .get_fpu       = hvmemul_get_fpu,
+    .put_fpu       = hvmemul_put_fpu,
     .invlpg        = hvmemul_invlpg
 };
 
index 94fb3a3d1fa3dc9b381be9ef9d4240fbf20d3bbc..75997500bc0fb8bb6282f7177d651ec4caef9baa 100644 (file)
@@ -479,6 +479,7 @@ asmlinkage int set_guest_nmi_trapbounce(void)
 static inline void do_trap(
     int trapnr, struct cpu_user_regs *regs, int use_error_code)
 {
+    struct vcpu *curr = current;
     unsigned long fixup;
 
     DEBUGGER_trap_entry(trapnr, regs);
@@ -497,6 +498,14 @@ static inline void do_trap(
         return;
     }
 
+    if ( ((trapnr == TRAP_copro_error) || (trapnr == TRAP_simd_error)) &&
+         is_hvm_vcpu(curr) && curr->arch.hvm_vcpu.fpu_exception_callback )
+    {
+        curr->arch.hvm_vcpu.fpu_exception_callback(
+            curr->arch.hvm_vcpu.fpu_exception_callback_arg, regs);
+        return;
+    }
+
     DEBUGGER_trap_fatal(trapnr, regs);
 
     show_execution_state(regs);
index f743650a5d35a1d17057c3900c413c49346b551c..e69deb1ed883b81999e99dc3934e9febcfdb1ac5 100644 (file)
 
 #include <asm/x86_emulate.h>
 
+/* Avoid namespace pollution. */
 #undef cmpxchg
 
-#define __emulate_fpu_insn(_op)                 \
-do{ int _exn;                                   \
-    asm volatile (                              \
-        "1: " _op "\n"                          \
-        "2: \n"                                 \
-        ".section .fixup,\"ax\"\n"              \
-        "3: mov $1,%0\n"                        \
-        "   jmp 2b\n"                           \
-        ".previous\n"                           \
-        ".section __ex_table,\"a\"\n"           \
-        "   "__FIXUP_ALIGN"\n"                  \
-        "   "__FIXUP_WORD" 1b,3b\n"             \
-        ".previous"                             \
-        : "=r" (_exn) : "0" (0) );              \
-    generate_exception_if(_exn, EXC_MF, -1);    \
-} while (0)
-
 #include "x86_emulate/x86_emulate.c"
index 9983a729046d3cf4d977679f731689afdae391f1..42e5301aea54fccb8b2c8887c39388780333eede 100644 (file)
@@ -546,6 +546,45 @@ do {                                                                    \
                      ? (uint16_t)_regs.eip : (uint32_t)_regs.eip);      \
 } while (0)
 
+struct fpu_insn_ctxt {
+    uint8_t insn_bytes;
+    uint8_t exn_raised;
+};
+
+static void fpu_handle_exception(void *_fic, struct cpu_user_regs *regs)
+{
+    struct fpu_insn_ctxt *fic = _fic;
+    fic->exn_raised = 1;
+    regs->eip += fic->insn_bytes;
+}
+
+#define __emulate_fpu_insn(_op)                         \
+do{ struct fpu_insn_ctxt fic = { 0 };                   \
+    fail_if(ops->get_fpu == NULL);                      \
+    ops->get_fpu(fpu_handle_exception, &fic, ctxt);     \
+    asm volatile (                                      \
+        "movb $2f-1f,%0 \n"                             \
+        "1: " _op "     \n"                             \
+        "2:             \n"                             \
+        : "=m" (fic.insn_bytes) : : "memory" );         \
+    ops->put_fpu(ctxt);                                 \
+    generate_exception_if(fic.exn_raised, EXC_MF, -1);  \
+} while (0)
+
+#define __emulate_fpu_insn_memdst(_op, _arg)            \
+do{ struct fpu_insn_ctxt fic = { 0 };                   \
+    fail_if(ops->get_fpu == NULL);                      \
+    ops->get_fpu(fpu_handle_exception, &fic, ctxt);     \
+    asm volatile (                                      \
+        "movb $2f-1f,%0 \n"                             \
+        "1: " _op " %1  \n"                             \
+        "2:             \n"                             \
+        : "=m" (fic.insn_bytes), "=m" (_arg)            \
+        : : "memory" );                                 \
+    ops->put_fpu(ctxt);                                 \
+    generate_exception_if(fic.exn_raised, EXC_MF, -1);  \
+} while (0)
+
 static unsigned long __get_rep_prefix(
     struct cpu_user_regs *int_regs,
     struct cpu_user_regs *ext_regs,
@@ -2399,8 +2438,6 @@ x86_emulate(
     }
 
     case 0x9b:  /* wait/fwait */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         __emulate_fpu_insn("fwait");
         break;
 
@@ -2721,53 +2758,46 @@ x86_emulate(
     }
 
     case 0xd9: /* FPU 0xd9 */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         switch ( modrm )
         {
-        case 0xc0: __emulate_fpu_insn(".byte 0xd9,0xc0"); break;
-        case 0xc1: __emulate_fpu_insn(".byte 0xd9,0xc1"); break;
-        case 0xc2: __emulate_fpu_insn(".byte 0xd9,0xc2"); break;
-        case 0xc3: __emulate_fpu_insn(".byte 0xd9,0xc3"); break;
-        case 0xc4: __emulate_fpu_insn(".byte 0xd9,0xc4"); break;
-        case 0xc5: __emulate_fpu_insn(".byte 0xd9,0xc5"); break;
-        case 0xc6: __emulate_fpu_insn(".byte 0xd9,0xc6"); break;
-        case 0xc7: __emulate_fpu_insn(".byte 0xd9,0xc7"); break;
-        case 0xe0: __emulate_fpu_insn(".byte 0xd9,0xe0"); break;
-        case 0xe8: __emulate_fpu_insn(".byte 0xd9,0xe8"); break;
-        case 0xee: __emulate_fpu_insn(".byte 0xd9,0xee"); break;
+        case 0xc0: __emulate_fpu_insn(".byte 0xd9,0xc0"); break; /* fld %st0 */
+        case 0xc1: __emulate_fpu_insn(".byte 0xd9,0xc1"); break; /* fld %st1 */
+        case 0xc2: __emulate_fpu_insn(".byte 0xd9,0xc2"); break; /* fld %st2 */
+        case 0xc3: __emulate_fpu_insn(".byte 0xd9,0xc3"); break; /* fld %st3 */
+        case 0xc4: __emulate_fpu_insn(".byte 0xd9,0xc4"); break; /* fld %st4 */
+        case 0xc5: __emulate_fpu_insn(".byte 0xd9,0xc5"); break; /* fld %st5 */
+        case 0xc6: __emulate_fpu_insn(".byte 0xd9,0xc6"); break; /* fld %st6 */
+        case 0xc7: __emulate_fpu_insn(".byte 0xd9,0xc7"); break; /* fld %st7 */
+        case 0xe0: __emulate_fpu_insn(".byte 0xd9,0xe0"); break; /* fchs */
+        case 0xe1: __emulate_fpu_insn(".byte 0xd9,0xe1"); break; /* fabs */
+        case 0xe8: __emulate_fpu_insn(".byte 0xd9,0xe8"); break; /* fld1 */
+        case 0xee: __emulate_fpu_insn(".byte 0xd9,0xee"); break; /* fldz */
         default:
             fail_if((modrm_reg & 7) != 7);
             fail_if(modrm >= 0xc0);
             /* fnstcw m2byte */
             ea.bytes = 2;
             dst = ea;
-            asm volatile ( "fnstcw %0" : "=m" (dst.val) );
+            __emulate_fpu_insn_memdst("fnstcw", dst.val);
         }
         break;
 
     case 0xdb: /* FPU 0xdb */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         fail_if(modrm != 0xe3);
         /* fninit */
-        asm volatile ( "fninit" );
+        __emulate_fpu_insn("fninit");
         break;
 
     case 0xdd: /* FPU 0xdd */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         fail_if((modrm_reg & 7) != 7);
         fail_if(modrm >= 0xc0);
         /* fnstsw m2byte */
         ea.bytes = 2;
         dst = ea;
-        asm volatile ( "fnstsw %0" : "=m" (dst.val) );
+        __emulate_fpu_insn_memdst("fnstsw", dst.val);
         break;
 
     case 0xde: /* FPU 0xde */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         switch ( modrm )
         {
         case 0xd9: __emulate_fpu_insn(".byte 0xde,0xd9"); break;
@@ -2784,14 +2814,12 @@ x86_emulate(
         break;
 
     case 0xdf: /* FPU 0xdf */
-        fail_if(ops->load_fpu_ctxt == NULL);
-        ops->load_fpu_ctxt(ctxt);
         fail_if(modrm != 0xe0);
         /* fnstsw %ax */
         dst.bytes = 2;
         dst.type = OP_REG;
         dst.reg = (unsigned long *)&_regs.eax;
-        asm volatile ( "fnstsw %0" : "=m" (dst.val) );
+        __emulate_fpu_insn_memdst("fnstsw", dst.val);
         break;
 
     case 0xe0 ... 0xe2: /* loop{,z,nz} */ {
index 4ffdac75f3755e46ebb2a3df1bbb664170dd6035..09856d3cca90e38c58ed73be39742a6637d1b03f 100644 (file)
@@ -342,8 +342,18 @@ struct x86_emulate_ops
         uint8_t insn_len,
         struct x86_emulate_ctxt *ctxt);
 
-    /* load_fpu_ctxt: Load emulated environment's FPU state onto processor. */
-    void (*load_fpu_ctxt)(
+    /*
+     * get_fpu: Load emulated environment's FPU state onto processor.
+     *  @exn_callback: On any FPU or SIMD exception, pass control to
+     *                 (*exception_callback)(exception_callback_arg, regs).
+     */
+    void (*get_fpu)(
+        void (*exception_callback)(void *, struct cpu_user_regs *),
+        void *exception_callback_arg,
+        struct x86_emulate_ctxt *ctxt);
+
+    /* put_fpu: Relinquish the FPU. Unhook from FPU/SIMD exception handlers. */
+    void (*put_fpu)(
         struct x86_emulate_ctxt *ctxt);
 
     /* invlpg: Invalidate paging structures which map addressed byte. */
index c4592d9c1e1ac00cf64e5aaaecbb925ccc1bb984..6ff8de5fc4ecf0887805085e5e8e441143671201 100644 (file)
@@ -83,6 +83,9 @@ struct hvm_vcpu {
      */
     unsigned long       mmio_gva;
     unsigned long       mmio_gpfn;
+
+    void (*fpu_exception_callback)(void *, struct cpu_user_regs *);
+    void *fpu_exception_callback_arg;
 };
 
 #endif /* __ASM_X86_HVM_VCPU_H__ */