* [PATCH v4 2/2][XTF] x86: extend FPU exception tests
2018-02-28 13:15 [PATCH v4 0/2][XTF] FPU test improvements Jan Beulich
2018-02-28 13:26 ` [PATCH v4 1/2][XTF] add FPU/SIMD register state test Jan Beulich
@ 2018-02-28 13:26 ` Jan Beulich
2018-02-28 16:49 ` [PATCH v4 0/2][XTF] FPU test improvements Andrew Cooper
2 siblings, 0 replies; 4+ messages in thread
From: Jan Beulich @ 2018-02-28 13:26 UTC (permalink / raw)
To: Andrew Cooper; +Cc: xen-devel
Also test #MF and #XM handling.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v4: Re-base.
v3: New.
--- /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,27 +22,37 @@
* 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>
+#include <arch/simd.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;
};
@@ -54,20 +64,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
@@ -77,6 +94,9 @@ exinfo_t probe_x87(bool force)
: [fep] "q" (force),
"X" (ex_record_fault_eax));
+ if ( control )
+ asm volatile ("fnclex");
+
return fault;
}
@@ -87,20 +107,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
@@ -110,26 +137,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;
@@ -145,10 +175,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
@@ -167,17 +243,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;
@@ -194,29 +278,41 @@ 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 cr0str[12];
+ char cr0str[12], cr4str[24];
snprintf(cr0str, sizeof(cr0str), "%s%s%s",
t->cr0 & X86_CR0_EM ? " EM" : "",
t->cr0 & X86_CR0_MP ? " MP" : "",
t->cr0 & X86_CR0_TS ? " TS" : "");
- xtf_failure(" Expected %pe, got %pe (cr0:%s)\n",
- _p(exp), _p(res), 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 %pe, got %pe (cr0:%s cr4:%s ctrl:%04x)\n",
+ _p(exp), _p(res), cr0str[0] ? cr0str : " - ",
+ cr4str[0] ? cr4str : " - ", t->control);
}
}
}
@@ -235,23 +331,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 )
@@ -259,19 +345,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);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
^ permalink raw reply [flat|nested] 4+ messages in thread