--- /dev/null
+++ b/arch/x86/include/arch/simd.h
@@ -0,0 +1,32 @@
+#ifndef XTF_X86_SIMD_H
+#define XTF_X86_SIMD_H
+
+#define X86_MXCSR_IE      0x00000001
+#define X86_MXCSR_DE      0x00000002
+#define X86_MXCSR_ZE      0x00000004
+#define X86_MXCSR_OE      0x00000008
+#define X86_MXCSR_UE      0x00000010
+#define X86_MXCSR_PE      0x00000020
+#define X86_MXCSR_DAZ     0x00000040
+#define X86_MXCSR_IM      0x00000080
+#define X86_MXCSR_DM      0x00000100
+#define X86_MXCSR_ZM      0x00000200
+#define X86_MXCSR_OM      0x00000400
+#define X86_MXCSR_UM      0x00000800
+#define X86_MXCSR_PM      0x00001000
+#define X86_MXCSR_RC_MASK 0x00006000
+#define X86_MXCSR_FZ      0x00008000
+
+#define X86_MXCSR_DEFAULT 0x1f80
+
+#endif /* XTF_X86_SIMD_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- a/arch/x86/include/arch/x87.h
+++ b/arch/x86/include/arch/x87.h
@@ -3,6 +3,27 @@
 
 #include <xtf/types.h>
 
+#define X86_FCW_IM      0x0001
+#define X86_FCW_DM      0x0002
+#define X86_FCW_ZM      0x0004
+#define X86_FCW_OM      0x0008
+#define X86_FCW_UM      0x0010
+#define X86_FCW_PM      0x0020
+#define X86_FCW_PC_MASK 0x0300
+#define X86_FCW_RC_MASK 0x0c00
+
+#define X86_PC_SINGLE   0
+#define X86_PC_DOUBLE   2
+#define X86_PC_EXTENDED 3
+
+/* These also apply to MXCSR. */
+#define X86_RC_NEAREST  0
+#define X86_RC_DOWN     1
+#define X86_RC_UP       2
+#define X86_RC_ZERO     3
+
+#define X86_FCW_DEFAULT 0x037f
+
 struct x87_env_pm32 {
     uint16_t cw, :16;
     uint16_t sw, :16;
--- a/tests/fpu-exception-emulation/main.c
+++ b/tests/fpu-exception-emulation/main.c
@@ -22,15 +22,14 @@
  * checking that appropriate exceptions are raised (@#NM or @#UD), or that no
  * exception is raised.
  *
+ * Additionally #MF and #XM behavior is being tested.
+ *
  * Each test is run against real hardware, and forced through the x86
  * instruction emulator (if FEP is available).
  *
  * This test covers XSA-190, where @#NM was not being raised appropriately,
  * therefore interfering with lazy FPU task switching in the guest.
  *
- * @todo Extend to include unmasked pending exceptions.  There is definitely
- * work required in the instruction emulator to support this properly.
- *
  * @see tests/fpu-exception-emulation/main.c
  */
 #include <xtf.h>
@@ -38,16 +37,27 @@
 #include <arch/decode.h>
 #include <arch/exinfo.h>
 #include <arch/processor.h>
+#include <arch/simd.h>
 #include <arch/symbolic-const.h>
+#include <arch/x87.h>
 
 const char test_title[] = "FPU Exception Emulation";
 
 #define CR0_SYM(...) TOK_OR(X86_CR0_, ##__VA_ARGS__)
-#define CR0_MASK CR0_SYM(EM, MP, TS)
+#define CR0_MASK CR0_SYM(EM, MP, TS, NE)
+
+#define CR4_SYM(...) TOK_OR(X86_CR4_OS, ##__VA_ARGS__)
+#define CR4_MASK CR4_SYM(FXSR, XMMEXCPT, XSAVE)
+
+#define FCW_SYM(...) TOK_OR(X86_FCW_, ##__VA_ARGS__)
+
+#define MXCSR_SYM(...) TOK_OR(X86_MXCSR_, ##__VA_ARGS__)
 
 struct test_cfg
 {
     unsigned long cr0;
+    unsigned long cr4;
+    unsigned int control;
     exinfo_t fault;
 };
 
@@ -59,20 +69,27 @@ static unsigned long default_cr0;
  */
 static const struct test_cfg x87[] =
 {
-    { CR0_SYM(          ), 0 },
-    { CR0_SYM(        TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(    MP    ), 0 },
-    { CR0_SYM(    MP, TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM        ), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM,     TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM, MP    ), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM, MP, TS), EXINFO_SYM(NM, 0) },
+    { CR0_SYM(          ), 0, 0,           0 },
+    { CR0_SYM(        TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(    MP    ), 0, 0,           0 },
+    { CR0_SYM(    MP, TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM        ), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM,     TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM, MP    ), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM, MP, TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(NE),         0, FCW_SYM(IM), EXINFO_SYM(MF, 0) },
 };
 
-exinfo_t probe_x87(bool force)
+exinfo_t probe_x87(unsigned int control, bool force)
 {
     exinfo_t fault = 0;
 
+    if ( control )
+    {
+        control = X86_FCW_DEFAULT & ~control;
+        asm volatile ("fninit; fldcw %0; faddp" :: "m" (control));
+    }
+
     asm volatile ("test %[fep], %[fep];"
                   "jz 1f;"
                   _ASM_XEN_FEP
@@ -82,6 +99,9 @@ exinfo_t probe_x87(bool force)
                   : [fep] "q" (force),
                     "X" (ex_record_fault_eax));
 
+    if ( control )
+        asm volatile ("fnclex");
+
     return fault;
 }
 
@@ -92,20 +112,27 @@ exinfo_t probe_x87(bool force)
  */
 static const struct test_cfg x87_wait[] =
 {
-    { CR0_SYM(          ), 0 },
-    { CR0_SYM(        TS), 0 },
-    { CR0_SYM(    MP    ), 0 },
-    { CR0_SYM(    MP, TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM        ), 0 },
-    { CR0_SYM(EM,     TS), 0 },
-    { CR0_SYM(EM, MP    ), 0 },
-    { CR0_SYM(EM, MP, TS), EXINFO_SYM(NM, 0) },
+    { CR0_SYM(          ), 0, 0,           0 },
+    { CR0_SYM(        TS), 0, 0,           0 },
+    { CR0_SYM(    MP    ), 0, 0,           0 },
+    { CR0_SYM(    MP, TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM        ), 0, 0,           0 },
+    { CR0_SYM(EM,     TS), 0, 0,           0 },
+    { CR0_SYM(EM, MP    ), 0, 0,           0 },
+    { CR0_SYM(EM, MP, TS), 0, 0,           EXINFO_SYM(NM, 0) },
+    { CR0_SYM(NE),         0, FCW_SYM(IM), EXINFO_SYM(MF, 0) },
 };
 
-exinfo_t probe_x87_wait(bool force)
+exinfo_t probe_x87_wait(unsigned int control, bool force)
 {
     exinfo_t fault = 0;
 
+    if ( control )
+    {
+        control = X86_FCW_DEFAULT & ~control;
+        asm volatile ("fninit; fldcw %0; faddp" :: "m" (control));
+    }
+
     asm volatile ("test %[fep], %[fep];"
                   "jz 1f;"
                   _ASM_XEN_FEP
@@ -115,26 +142,29 @@ exinfo_t probe_x87_wait(bool force)
                   : [fep] "q" (force),
                     "X" (ex_record_fault_eax));
 
+    if ( control )
+        asm volatile ("fnclex");
+
     return fault;
 }
 
 /**
- * MMX and SSE instructions.  Emulation is unsupported (thus raising @#UD),
+ * MMX instructions.  Emulation is unsupported (thus raising @#UD),
  * but @#NM should be raised if the task has been switched.
  */
-static const struct test_cfg mmx_sse[] =
+static const struct test_cfg mmx[] =
 {
-    { CR0_SYM(          ), 0 },
-    { CR0_SYM(        TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(    MP    ), 0 },
-    { CR0_SYM(    MP, TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM        ), EXINFO_SYM(UD, 0) },
-    { CR0_SYM(EM,     TS), EXINFO_SYM(UD, 0) },
-    { CR0_SYM(EM, MP    ), EXINFO_SYM(UD, 0) },
-    { CR0_SYM(EM, MP, TS), EXINFO_SYM(UD, 0) },
+    { CR0_SYM(          ), 0, 0, 0 },
+    { CR0_SYM(        TS), 0, 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(    MP    ), 0, 0, 0 },
+    { CR0_SYM(    MP, TS), 0, 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM        ), 0, 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM,     TS), 0, 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP    ), 0, 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP, TS), 0, 0, EXINFO_SYM(UD, 0) },
 };
 
-exinfo_t probe_mmx(bool force)
+exinfo_t probe_mmx(unsigned int control, bool force)
 {
     exinfo_t fault = 0;
 
@@ -150,10 +180,56 @@ exinfo_t probe_mmx(bool force)
     return fault;
 }
 
-exinfo_t probe_sse(bool force)
+/**
+ * SSE instructions.  Emulation is unsupported (thus raising @#UD),
+ * but @#NM should be raised if the task has been switched.
+ */
+static const struct test_cfg sse[] =
+{
+    { CR0_SYM(          ), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(        TS), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP    ), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP, TS), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM        ), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM,     TS), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP    ), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP, TS), 0,             0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(          ), CR4_SYM(FXSR), 0, 0 },
+    { CR0_SYM(        TS), CR4_SYM(FXSR), 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(    MP    ), CR4_SYM(FXSR), 0, 0 },
+    { CR0_SYM(    MP, TS), CR4_SYM(FXSR), 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM        ), CR4_SYM(FXSR), 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM,     TS), CR4_SYM(FXSR), 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP    ), CR4_SYM(FXSR), 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP, TS), CR4_SYM(FXSR), 0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP    ), CR4_SYM(FXSR),
+      MXCSR_SYM(IM),                         EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP    ), CR4_SYM(FXSR, XMMEXCPT),
+      MXCSR_SYM(IM),                         EXINFO_SYM(XM, 0) },
+};
+
+exinfo_t probe_sse(unsigned int control, bool force)
 {
     exinfo_t fault = 0;
 
+    if ( control )
+    {
+        control = X86_MXCSR_DEFAULT & ~control;
+        asm volatile ("0: xorps %%xmm0, %%xmm0;"
+                      "ldmxcsr %[mxcsr];"
+                      "test %[fep], %[fep];"
+                      "jz 1f;"
+                      _ASM_XEN_FEP
+                      "1: divps %%xmm0, %%xmm0; 2:"
+                      _ASM_EXTABLE_HANDLER(0b, 2b, ex_record_fault_eax)
+                      _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                      : "+a" (fault)
+                      : [mxcsr] "m" (control),
+                        [fep] "q" (force),
+                        "X" (ex_record_fault_eax));
+        return fault;
+    }
+
     asm volatile ("test %[fep], %[fep];"
                   "jz 1f;"
                   _ASM_XEN_FEP
@@ -172,17 +248,25 @@ exinfo_t probe_sse(bool force)
  */
 static const struct test_cfg avx[] =
 {
-    { CR0_SYM(          ), 0 },
-    { CR0_SYM(        TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(    MP    ), 0 },
-    { CR0_SYM(    MP, TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM        ), 0 },
-    { CR0_SYM(EM,     TS), EXINFO_SYM(NM, 0) },
-    { CR0_SYM(EM, MP    ), 0 },
-    { CR0_SYM(EM, MP, TS), EXINFO_SYM(NM, 0) },
+    { CR0_SYM(          ), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(        TS), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP    ), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(    MP, TS), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM        ), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM,     TS), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP    ), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(EM, MP, TS), 0,              0, EXINFO_SYM(UD, 0) },
+    { CR0_SYM(          ), CR4_SYM(XSAVE), 0, 0 },
+    { CR0_SYM(        TS), CR4_SYM(XSAVE), 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(    MP    ), CR4_SYM(XSAVE), 0, 0 },
+    { CR0_SYM(    MP, TS), CR4_SYM(XSAVE), 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM        ), CR4_SYM(XSAVE), 0, 0 },
+    { CR0_SYM(EM,     TS), CR4_SYM(XSAVE), 0, EXINFO_SYM(NM, 0) },
+    { CR0_SYM(EM, MP    ), CR4_SYM(XSAVE), 0, 0 },
+    { CR0_SYM(EM, MP, TS), CR4_SYM(XSAVE), 0, EXINFO_SYM(NM, 0) },
 };
 
-static exinfo_t probe_avx(bool force)
+static exinfo_t probe_avx(unsigned int control, bool force)
 {
     exinfo_t fault = 0;
 
@@ -199,21 +283,27 @@ static exinfo_t probe_avx(bool force)
 }
 
 void run_sequence(const struct test_cfg *seq, unsigned int nr,
-                  unsigned int (*fn)(bool), bool force, exinfo_t override)
+                  unsigned int (*fn)(unsigned int, bool), bool force,
+                  exinfo_t override)
 {
     unsigned int i;
 
     for ( i = 0; i < nr; ++i )
     {
         const struct test_cfg *t = &seq[i];
+        unsigned long cr4 = read_cr4();
         exinfo_t res, exp = override ?: t->fault;
 
         write_cr0((default_cr0 & ~CR0_MASK) | t->cr0);
-        res = fn(force);
+        write_cr4((cr4 & ~CR4_MASK) | t->cr4);
+
+        res = fn(t->control, force);
+
+        write_cr4(cr4);
 
         if ( res != exp )
         {
-            char expstr[12], gotstr[12], cr0str[12];
+            char expstr[12], gotstr[12], cr0str[12], cr4str[24];
 
             x86_decode_exinfo(expstr, ARRAY_SIZE(expstr), exp);
             x86_decode_exinfo(gotstr, ARRAY_SIZE(gotstr), res);
@@ -223,8 +313,14 @@ void run_sequence(const struct test_cfg
                      t->cr0 & X86_CR0_MP ? " MP" : "",
                      t->cr0 & X86_CR0_TS ? " TS" : "");
 
-            xtf_failure("  Expected %s, got %s (cr0:%s)\n",
-                        expstr, gotstr, cr0str[0] ? cr0str : " - ");
+            snprintf(cr4str, sizeof(cr4str), "%s%s%s",
+                     t->cr4 & X86_CR4_OSFXSR     ? " FXSR"    : "",
+                     t->cr4 & X86_CR4_OSXMMEXCPT ? " XMMXCPT" : "",
+                     t->cr4 & X86_CR4_OSXSAVE    ? " XSAVE"   : "");
+
+            xtf_failure("  Expected %s, got %s (cr0:%s cr4:%s ctrl:%04x)\n",
+                        expstr, gotstr, cr0str[0] ? cr0str : " - ",
+                        cr4str[0] ? cr4str : " - ", t->control);
         }
     }
 }
@@ -243,23 +339,13 @@ void run_tests(bool force)
     if ( cpu_has_mmx )
     {
         printk("Testing%s MMX\n", force ? " emulated" : "");
-        run_sequence(mmx_sse, ARRAY_SIZE(mmx_sse), probe_mmx, force, 0);
+        run_sequence(mmx, ARRAY_SIZE(mmx), probe_mmx, force, 0);
     }
 
     if ( cpu_has_sse )
     {
-        unsigned long cr4 = read_cr4();
-
         printk("Testing%s SSE\n", force ? " emulated" : "");
-        write_cr4(cr4 & ~X86_CR4_OSFXSR);
-        run_sequence(mmx_sse, ARRAY_SIZE(mmx_sse), probe_sse, force,
-                     EXINFO_SYM(UD, 0));
-
-        printk("Testing%s SSE (CR4.OSFXSR)\n", force ? " emulated" : "");
-        write_cr4(cr4 | X86_CR4_OSFXSR);
-        run_sequence(mmx_sse, ARRAY_SIZE(mmx_sse), probe_sse, force, 0);
-
-        write_cr4(cr4);
+        run_sequence(sse, ARRAY_SIZE(sse), probe_sse, force, 0);
     }
 
     if ( cpu_has_avx )
@@ -267,19 +353,15 @@ void run_tests(bool force)
         unsigned long cr4 = read_cr4();
         unsigned long xcr0;
 
-        printk("Testing%s AVX\n", force ? " emulated" : "");
-        write_cr4(cr4 & ~X86_CR4_OSXSAVE);
-        run_sequence(avx, ARRAY_SIZE(avx), probe_avx, force,
-                     EXINFO_SYM(UD, 0));
-
-        printk("Testing%s AVX (CR4.OSXSAVE)\n", force ? " emulated" : "");
         write_cr4(cr4 | X86_CR4_OSXSAVE);
         xcr0 = read_xcr0();
+
+        printk("Testing%s AVX\n", force ? " emulated" : "");
         write_xcr0(xcr0 & ~XSTATE_YMM);
         run_sequence(avx, ARRAY_SIZE(avx), probe_avx, force,
                      EXINFO_SYM(UD, 0));
 
-        printk("Testing%s AVX (CR4.OSXSAVE+XCR0.YMM)\n", force ? " emulated" : "");
+        printk("Testing%s AVX (XCR0.YMM)\n", force ? " emulated" : "");
         write_xcr0(xcr0 | XSTATE_SSE | XSTATE_YMM);
         run_sequence(avx, ARRAY_SIZE(avx), probe_avx, force, 0);
 
