From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753474AbbIPTvj (ORCPT ); Wed, 16 Sep 2015 15:51:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46163 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752435AbbIPTvh (ORCPT ); Wed, 16 Sep 2015 15:51:37 -0400 Message-ID: <55F9C847.5070104@redhat.com> Date: Wed, 16 Sep 2015 21:51:35 +0200 From: Denys Vlasenko User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0 MIME-Version: 1.0 To: Ingo Molnar CC: Denys Vlasenko , Borislav Petkov , "H. Peter Anvin" , Andy Lutomirski , Kees Cook , x86@kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH 1/2] x86/math-emu: Add support for FCMOVcc and F[U]COMI[P] insns References: <1442432914-27022-1-git-send-email-dvlasenk@redhat.com> In-Reply-To: <1442432914-27022-1-git-send-email-dvlasenk@redhat.com> Content-Type: multipart/mixed; boundary="------------090704050608060606090903" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This is a multi-part message in MIME format. --------------090704050608060606090903 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit On 09/16/2015 09:48 PM, Denys Vlasenko wrote: > Run-tested by booting with "no387 nofxsr" and running test programs: > > # ./test_FCMOV > [RUN] Testing fcmovCC instructions > [OK] fcmovCC > # ./test_FCOMI > [RUN] Testing f[u]comi[p] instructions > [OK] f[u]comi[p] The sources for these test programs are in this email. Ingo, let me know if you want a patch which adds them to tools/testing/selftests/x86/* --------------090704050608060606090903 Content-Type: text/x-csrc; name="test_FCMOV.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="test_FCMOV.c" #undef _GNU_SOURCE #define _GNU_SOURCE 1 #undef __USE_GNU #define __USE_GNU 1 #include #include #include #include #include #include #include #include #include //#include #include #define TEST(insn) \ long double __attribute__((noinline)) insn(long flags) \ { \ long double out; \ asm ("\n" \ " push %1""\n" \ " popf""\n" \ " fldpi""\n" \ " fld1""\n" \ " " #insn " %%st(1), %%st" "\n" \ " ffree %%st(1)" "\n" \ : "=t" (out) \ : "r" (flags) \ ); \ return out; \ } TEST(fcmovb) TEST(fcmove) TEST(fcmovbe) TEST(fcmovu) TEST(fcmovnb) TEST(fcmovne) TEST(fcmovnbe) TEST(fcmovnu) enum { CF = 1 << 0, PF = 1 << 2, ZF = 1 << 6, }; int main(int argc, char **argv, char **envp) { int err = 0; printf("[RUN]\tTesting fcmovCC instructions\n"); /* If fcmovCC() returns 1.0, the move wasn't done */ err |= !(fcmovb(0) == 1.0); err |= !(fcmovnb(0) != 1.0); err |= !(fcmove(0) == 1.0); err |= !(fcmovne(0) != 1.0); err |= !(fcmovbe(0) == 1.0); err |= !(fcmovnbe(0) != 1.0); err |= !(fcmovu(0) == 1.0); err |= !(fcmovnu(0) != 1.0); err |= !(fcmovb(CF) != 1.0); err |= !(fcmovnb(CF) == 1.0); err |= !(fcmove(CF) == 1.0); err |= !(fcmovne(CF) != 1.0); err |= !(fcmovbe(CF) != 1.0); err |= !(fcmovnbe(CF) == 1.0); err |= !(fcmovu(CF) == 1.0); err |= !(fcmovnu(CF) != 1.0); err |= !(fcmovb(ZF) == 1.0); err |= !(fcmovnb(ZF) != 1.0); err |= !(fcmove(ZF) != 1.0); err |= !(fcmovne(ZF) == 1.0); err |= !(fcmovbe(ZF) != 1.0); err |= !(fcmovnbe(ZF) == 1.0); err |= !(fcmovu(ZF) == 1.0); err |= !(fcmovnu(ZF) != 1.0); err |= !(fcmovb(PF) == 1.0); err |= !(fcmovnb(PF) != 1.0); err |= !(fcmove(PF) == 1.0); err |= !(fcmovne(PF) != 1.0); err |= !(fcmovbe(PF) == 1.0); err |= !(fcmovnbe(PF) != 1.0); err |= !(fcmovu(PF) != 1.0); err |= !(fcmovnu(PF) == 1.0); if (!err) printf("[OK]\tfcmovCC\n"); else printf("[FAIL]\tfcmovCC errors: %d\n", err); return err; } --------------090704050608060606090903 Content-Type: text/x-csrc; name="test_FCOMI.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="test_FCOMI.c" #undef _GNU_SOURCE #define _GNU_SOURCE 1 #undef __USE_GNU #define __USE_GNU 1 #include #include #include #include #include #include #include #include #include //#include #include #include enum { CF = 1 << 0, PF = 1 << 2, ZF = 1 << 6, ARITH = CF | PF | ZF, }; long res_fcomi_pi_1; long res_fcomi_1_pi; long res_fcomi_1_1; long res_fcomi_nan_1; /* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */ /* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */ int snan = 0x7fc11111; int qnan = 0x7f811111; unsigned short snan1[5]; /* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */ unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff }; int test(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " fld1""\n" " fldpi""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_1_pi""\n" " push %0""\n" " popf""\n" " fldpi""\n" " fld1""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_pi_1""\n" " push %0""\n" " popf""\n" " fld1""\n" " fld1""\n" " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_1_1""\n" : : "r" (flags) ); if ((res_fcomi_1_pi & ARITH) != (0)) { printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); return 1; } if ((res_fcomi_pi_1 & ARITH) != (CF)) { printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); return 1; } if ((res_fcomi_1_1 & ARITH) != (ZF)) { printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int test_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fcomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testu_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fucomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int testu_snan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" // " flds snan""\n" // WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register! // " fstpt snan1""\n" // if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111! // " fnclex""\n" // flds of a snan raised FE_INVALID, clear it " fldt snan80""\n" // fldt never raise FE_INVALID " fld1""\n" " fucomi %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " ffree %%st(1)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } // printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]); if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testp(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " fld1""\n" " fldpi""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_1_pi""\n" " push %0""\n" " popf""\n" " fldpi""\n" " fld1""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_pi_1""\n" " push %0""\n" " popf""\n" " fld1""\n" " fld1""\n" " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_1_1""\n" : : "r" (flags) ); if ((res_fcomi_1_pi & ARITH) != (0)) { printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); return 1; } if ((res_fcomi_pi_1 & ARITH) != (CF)) { printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); return 1; } if ((res_fcomi_1_1 & ARITH) != (ZF)) { printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int testp_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fcomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != FE_INVALID) { printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); return 1; } return 0; } int testup_qnan(long flags) { feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); asm ("\n" " push %0""\n" " popf""\n" " flds qnan""\n" " fld1""\n" " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it " fucomip %%st(1), %%st" "\n" " ffree %%st(0)" "\n" " pushf""\n" " pop res_fcomi_nan_1""\n" : : "r" (flags) ); if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); return 1; } if (fetestexcept(FE_INVALID) != 0) { printf("[BAD]\tFE_INVALID is set in %s\n", __func__); return 1; } return 0; } int main(int argc, char **argv, char **envp) { int err = 0; printf("[RUN]\tTesting f[u]comi[p] instructions\n"); err |= test(0); err |= test_qnan(0); err |= testu_qnan(0); err |= testu_snan(0); err |= test(CF|ZF|PF); err |= test_qnan(CF|ZF|PF); err |= testu_qnan(CF|ZF|PF); err |= testu_snan(CF|ZF|PF); err |= testp(0); err |= testp_qnan(0); err |= testup_qnan(0); err |= testp(CF|ZF|PF); err |= testp_qnan(CF|ZF|PF); err |= testup_qnan(CF|ZF|PF); if (!err) printf("[OK]\tf[u]comi[p]\n"); else printf("[FAIL]\tf[u]comi[p] errors: %d\n", err); return err; } --------------090704050608060606090903--