From patchwork Wed Jun 21 13:12:22 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Beulich X-Patchwork-Id: 9801811 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id EC5BA6038C for ; Wed, 21 Jun 2017 13:14:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D602828420 for ; Wed, 21 Jun 2017 13:14:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C67222856F; Wed, 21 Jun 2017 13:14:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5628928420 for ; Wed, 21 Jun 2017 13:14:53 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dNfQr-0007Wj-CI; Wed, 21 Jun 2017 13:12:29 +0000 Received: from mail6.bemta3.messagelabs.com ([195.245.230.39]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dNfQp-0007WH-GH for xen-devel@lists.xenproject.org; Wed, 21 Jun 2017 13:12:27 +0000 Received: from [85.158.137.68] by server-13.bemta-3.messagelabs.com id F1/CE-17076-AB07A495; Wed, 21 Jun 2017 13:12:26 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrOIsWRWlGSWpSXmKPExsXS6fjDS3dngVe kwd5Xxhbft0xmcmD0OPzhCksAYxRrZl5SfkUCa0br3E3sBaf/Mlb8OVvawPhuC2MXIyeHkECe xNtTjWwgNq+AncT6W7/YQWwJAUOJ0wtvsoDYLAKqEkuvbwez2QTUJdqebWcFsUUE9CXmf/oJZ HNwMAsYSNz9bgISFhYwl5jdtZMNYnyRxMezZ1lASjgF7CX+7PAGMXkFBCX+7hCGaLSTOLi+aA IjzyyExCyExCygKcwCWhIPf91igbC1JZYtfM0MUSItsfwfB0TYQKLhx2s2VCUgtrXE4r8v2Rc wcqxi1ChOLSpLLdI1NNJLKspMzyjJTczM0TU0MNbLTS0uTkxPzUlMKtZLzs/dxAgMVAYg2MH4 qtv5EKMkB5OSKG+5t1ekEF9SfkplRmJxRnxRaU5q8SFGGQ4OJQle33ygnGBRanpqRVpmDjBmY NISHDxKIrxuAUBp3uKCxNzizHSI1ClGXY4Nq9d/YRJiycvPS5US53UEmSEAUpRRmgc3Aha/lx hlpYR5GYGOEuIpSC3KzSxBlX/FKM7BqCTMuzQPaApPZl4J3KZXQEcwAR3x4ogHyBEliQgpqQZ GyXj+8u7pQTVfFFX8Z4RGHH3IqLRmc7rumtiiK+Udzw5NbcpS+uaU3L4ygumBDRtP7+mJDRqr ft6cbT+PMdVlVbDVZJOrx74bT2/6tHN62q9oS93GdXGs8t+25S+RFY6MfrpoW9Ar8Z3PxD/qV EzmNMvqclsuFGp4QcAgwbKzoXCx/tRbcplKLMUZiYZazEXFiQBrxANQ2gIAAA== X-Env-Sender: JBeulich@suse.com X-Msg-Ref: server-8.tower-31.messagelabs.com!1498050743!106793927!1 X-Originating-IP: [137.65.248.74] X-SpamReason: No, hits=0.0 required=7.0 tests=UPPERCASE_25_50 X-StarScan-Received: X-StarScan-Version: 9.4.19; banners=-,-,- X-VirusChecked: Checked Received: (qmail 41270 invoked from network); 21 Jun 2017 13:12:25 -0000 Received: from prv-mh.provo.novell.com (HELO prv-mh.provo.novell.com) (137.65.248.74) by server-8.tower-31.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 21 Jun 2017 13:12:25 -0000 Received: from INET-PRV-MTA by prv-mh.provo.novell.com with Novell_GroupWise; Wed, 21 Jun 2017 07:12:23 -0600 Message-Id: <594A8CD602000078001654AB@prv-mh.provo.novell.com> X-Mailer: Novell GroupWise Internet Agent 14.2.2 Date: Wed, 21 Jun 2017 07:12:22 -0600 From: "Jan Beulich" To: "Andrew Cooper" References: <594A8C140200007800165494@prv-mh.provo.novell.com> <594A8C140200007800165494@prv-mh.provo.novell.com> In-Reply-To: <594A8C140200007800165494@prv-mh.provo.novell.com> Mime-Version: 1.0 Cc: xen-devel Subject: [Xen-devel] [PATCH v3 2/2][XTF] extend FPU exception tests X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Also test #MF and #XM handling. Signed-off-by: Jan Beulich --- v3: New. extend FPU exception tests Also test #MF and #XM handling. Signed-off-by: Jan Beulich --- 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 +#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 @@ -38,16 +37,27 @@ #include #include #include +#include #include +#include 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); --- /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 +#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 @@ -38,16 +37,27 @@ #include #include #include +#include #include +#include 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);