[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2][XTF] add FPU/SIMD register state test



Add tests to verify that
- FPU insns leave correct (guest) values in FIP/FDP/FOP/FCS/FDS (at the
  example for FSTPS),
- FPU insns writing memory don't update FPU register state when the
  write faults (at the example of FISTPS),
- VCVTPS2PH (once implemented in the emulator) doesn't update MXCSR
  if its write faults (VCVTPS2PH is one of the very few SIMD insns
  writing to memory _and_ updating register state; the scatter family
  of insns also fall into this category, but we're quite far yet from
  supporting AVX-512 insns).

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
---
v2: Introduce and use x87.h. Tolerate VCVTPS2PH misbehavior on Intel
    hardware. Tolerate AMD oddities in probe_fstp() and probe_fistp().

--- a/include/arch/x86/cpuid.h
+++ b/include/arch/x86/cpuid.h
@@ -77,6 +77,7 @@ static inline bool cpu_has(unsigned int
 #define cpu_has_pcid            cpu_has(X86_FEATURE_PCID)
 #define cpu_has_xsave           cpu_has(X86_FEATURE_XSAVE)
 #define cpu_has_avx             cpu_has(X86_FEATURE_AVX)
+#define cpu_has_f16c            cpu_has(X86_FEATURE_F16C)
 
 #define cpu_has_syscall         cpu_has(X86_FEATURE_SYSCALL)
 #define cpu_has_nx              cpu_has(X86_FEATURE_NX)
--- /dev/null
+++ b/include/arch/x86/x87.h
@@ -0,0 +1,27 @@
+#ifndef XTF_X86_X87_H
+#define XTF_X86_X87_H
+
+#include <xtf/types.h>
+
+struct x87_env_pm32 {
+    uint16_t cw, :16;
+    uint16_t sw, :16;
+    uint16_t tw, :16;
+    uint32_t ip;
+    uint16_t cs;
+    uint16_t op:11, :5;
+    uint32_t dp;
+    uint16_t ds, :16;
+};
+
+#endif /* XTF_X86_X87_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- a/include/xen/arch-x86/xen.h
+++ b/include/xen/arch-x86/xen.h
@@ -15,6 +15,16 @@
 
 #include "cpuid.h"
 
+/*
+ * A number of GDT entries are reserved by Xen. These are not situated at the
+ * start of the GDT because some stupid OSes export hard-coded selector values
+ * in their ABI. These hard-coded values are always near the start of the GDT,
+ * so Xen places itself out of the way, at the far end of the GDT.
+ *
+ * NB The LDT is set using the MMUEXT_SET_LDT op of HYPERVISOR_mmuext_op
+ */
+#define FIRST_RESERVED_GDT_PAGE  14
+
 #ifndef __ASSEMBLY__
 typedef unsigned long xen_pfn_t;
 
--- /dev/null
+++ b/tests/fpu-state/Makefile
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME      := fpu-state
+CATEGORY  := functional
+TEST-ENVS := hvm64 hvm32pse
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
--- /dev/null
+++ b/tests/fpu-state/main.c
@@ -0,0 +1,212 @@
+/**
+ * @file tests/fpu-state/main.c
+ * @ref test-fpu-state - Emulation of FPU state
+ *
+ * @page test-fpu-state FPU State Emulation
+ *
+ * FPU code/data pointers and opcode must not be the ones resulting
+ * from the stub execution in the hypervisor.
+ *
+ * FPU and SIMD instructions faulting during memory write must not
+ * update the respective register files.
+ *
+ * @see tests/fpu-state/main.c
+ */
+#include <xtf.h>
+
+#include <arch/x86/exinfo.h>
+#include <arch/x86/x87.h>
+
+const char test_title[] = "FPU State";
+
+void probe_fstp(bool force)
+{
+    const uint8_t *fstp_offs;
+    uint32_t flt;
+    struct x87_env_pm32 fenv;
+
+    fenv.cw = 0x35f; /* unmask PE */
+    asm volatile ( "fninit;"
+                   "fldcw %[cw];"
+                   "fldpi;"
+                   "mov $1f, %[offs];"
+                   "test %[fep], %[fep];"
+                   "jz 1f;"
+                   _ASM_XEN_FEP
+                   "1: fstps %[data]; 2:"
+                   : [offs] "=&g" (fstp_offs), [data] "=m" (flt)
+                   : [cw] "m" (fenv.cw), [fep] "q" (force) );
+
+    asm ( "fnstenv %0" : "=m" (fenv) );
+    if ( fenv.ip != (unsigned long)fstp_offs )
+        xtf_failure("Fail: FIP wrong (%08x)\n", fenv.ip);
+    if ( fenv.cs && fenv.cs != __KERN_CS )
+    {
+#ifdef __x86_64__
+        /*
+         * Tolerate CS being in the hypervisor reserved selector range on
+         * AMD hardware, as their 64-bit {F,}XRSTOR do not appear to clear
+         * FCS/FDS.
+         */
+        if ( vendor_is_amd && !(fenv.cs & X86_SEL_LDT) &&
+             (fenv.cs >> PAGE_SHIFT) == FIRST_RESERVED_GDT_PAGE )
+            xtf_warning("Warning: FCS wrong (%04x)\n", fenv.cs);
+        else
+#endif
+            xtf_failure("Fail: FCS wrong (%04x)\n", fenv.cs);
+    }
+    if ( fenv.dp != (unsigned long)&flt )
+        xtf_failure("Fail: FDP wrong (%08x)\n", fenv.dp);
+    if ( fenv.ds && fenv.ds != __KERN_DS )
+        xtf_failure("Fail: FDS wrong (%04x)\n", fenv.ds);
+    /* Skip possible opcode prefixes before checking the opcode. */
+    while ( (fstp_offs[0] & ~7) != 0xd8 )
+        ++fstp_offs;
+    if ( fenv.op && fenv.op != (((fstp_offs[0] & 7) << 8) | fstp_offs[1]) )
+        xtf_failure("Fail: FOP wrong (%03x)\n", fenv.op);
+}
+
+void probe_fistp(bool force)
+{
+    unsigned long fldpi_offs;
+    exinfo_t fault = 0;
+    uint16_t fsw;
+    struct x87_env_pm32 fenv;
+    typeof(xtf_failure) *diagfn;
+    const char *prefix;
+
+    asm volatile ( "fninit;"
+                   "0: fldpi;"
+                   "mov $0b, %[offs];"
+                   "test %[fep], %[fep];"
+                   "jz 1f;"
+                   _ASM_XEN_FEP
+                   "1: fistps (%[ptr]); 2:"
+                   _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                   : [offs] "=&g" (fldpi_offs), "+a" (fault)
+                   : [ptr] "r" (0), [fep] "q" (force) );
+
+    if ( !fault )
+        xtf_error("Error: FISTP to NULL did not fault\n");
+
+    asm ( "fnstsw %0" : "=am" (fsw) );
+    if ( fsw != 0x3800 )
+        xtf_failure("Fail: FSW changed unexpectedly (%04x)\n", fsw);
+
+    asm ( "fnstenv %0" : "=m" (fenv) );
+    /*
+     * The AMD-specific FPU pointer leak workaround in Xen (using FISTPL,
+     * which we check for below) causes all the remaining checks to fail.
+     */
+    if ( !vendor_is_amd || (fenv.op & 0x738) != 0x300 )
+    {
+        diagfn = xtf_failure;
+        prefix = "Fail";
+    }
+    else
+    {
+        diagfn = xtf_warning;
+        prefix = "Warning";
+    }
+    if ( fenv.ip != fldpi_offs )
+        diagfn("%s: FIP changed unexpectedly (%08x)\n", prefix, fenv.ip);
+    if ( fenv.cs && fenv.cs != __KERN_CS )
+        diagfn("%s: FCS changed unexpectedly (%04x)\n", prefix, fenv.cs);
+    if ( fenv.dp )
+        diagfn("%s: FDP changed unexpectedly (%08x)\n", prefix, fenv.dp);
+    if ( fenv.ds )
+        diagfn("%s: FDS changed unexpectedly (%04x)\n", prefix, fenv.ds);
+    if ( fenv.op && fenv.op != 0x1eb )
+        diagfn("%s: FOP changed unexpectedly (%03x)\n", prefix, fenv.op);
+}
+
+void probe_vcvtps2ph(bool force)
+{
+    exinfo_t fault = 0;
+    uint32_t mxcsr = 0x1f80;
+
+    asm volatile ( "vldmxcsr %[mxcsr];"
+                   "vpcmpeqb %%xmm0,%%xmm0,%%xmm0;"
+                   "vpcmpgtb %%xmm0,%%xmm0,%%xmm1;"
+                   "vpunpcklbw %%xmm1,%%xmm0,%%xmm2;"
+                   "test %[fep], %[fep];"
+                   "jz 1f;"
+                   _ASM_XEN_FEP
+                   "1: vcvtps2ph $0,%%xmm2,(%[ptr]); 2:"
+                   _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_eax)
+                   : "+a" (fault)
+                   : [ptr] "r" (0), [mxcsr] "m" (mxcsr), [fep] "q" (force) );
+
+    if ( !fault )
+        xtf_error("Error: VCVTPS2PH to NULL did not fault\n");
+    else if ( exinfo_vec(fault) == X86_EXC_UD )
+    {
+        if ( force )
+            xtf_skip("Emulator does not support VCVTPS2PH\n");
+        else
+            xtf_failure("Fail: VCVTPS2PH did #UD\n");
+    }
+
+    asm ( "vstmxcsr %0" : "=m" (mxcsr) );
+    if ( mxcsr != 0x1f80 )
+    {
+        /*
+         * Expect AMD hardware and emulation to behave correctly, but tolerate
+         * unexpected behavior on Intel hardware.
+         */
+        if ( force || vendor_is_amd )
+            xtf_failure("Fail: MXCSR changed unexpectedly (%08x)\n", mxcsr);
+        else
+            xtf_warning("Warning: MXCSR changed unexpectedly (%08x)\n", mxcsr);
+    }
+}
+
+void run_tests(bool force)
+{
+    if ( cpu_has_fpu )
+    {
+        printk("Testing%s FSTP\n", force ? " emulated" : "");
+        probe_fstp(force);
+
+        printk("Testing%s FISTP (to NULL)\n", force ? " emulated" : "");
+        probe_fistp(force);
+    }
+
+    if ( cpu_has_f16c )
+    {
+        unsigned long cr4 = read_cr4();
+        unsigned long xcr0;
+
+        write_cr4(cr4 | X86_CR4_OSXSAVE);
+        xcr0 = read_xcr0();
+        write_xcr0(xcr0 | XSTATE_SSE | XSTATE_YMM);
+
+        printk("Testing%s VCVTPS2PH (to NULL)\n", force ? " emulated" : "");
+        probe_vcvtps2ph(force);
+
+        write_xcr0(xcr0);
+        write_cr4(cr4);
+    }
+}
+
+void test_main(void)
+{
+    run_tests(false);
+
+    if ( !xtf_has_fep )
+        xtf_skip("FEP support not detected - some tests will be skipped\n");
+    else
+        run_tests(true);
+
+    xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */


Attachment: xtf-FPU-state.patch
Description: Text document

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.