@@ -78,6 +78,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)
@@ -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:
+ */
@@ -20,6 +20,8 @@ and functionality.
@subpage test-fpu-exception-emulation - FPU Exception Emulation. Covers XSA-190.
+@subpage test-fpu-state - FPU state Emulation.
+
@subpage test-invlpg - `invlpg` instruction behaviour.
@subpage test-lbr-tsx-vmentry - Haswell and later LBR/TSX Vmentry failure test.
@@ -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;
@@ -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
@@ -0,0 +1,214 @@
+/**
+ * @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/exinfo.h>
+#include <arch/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),
+ "X" (ex_record_fault_eax) );
+
+ 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),
+ "X" (ex_record_fault_eax) );
+
+ 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:
+ */