Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/fpu/math-emu, selftests: Add tests for FCMOV and FCOMI insns

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1442494933-13798-1-git-send-email-dvlasenk@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Denys Vlasenko and committed by
Ingo Molnar
57ca6897 73477bbb

+427 -2
+3 -2
tools/testing/selftests/x86/Makefile
··· 5 5 .PHONY: all all_32 all_64 warn_32bit_failure clean 6 6 7 7 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt 8 - TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn 8 + TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn \ 9 + test_FCMOV test_FCOMI 9 10 10 11 TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) 11 12 BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) ··· 36 35 $(RM) $(BINARIES_32) $(BINARIES_64) 37 36 38 37 $(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c 39 - $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl 38 + $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm 40 39 41 40 $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c 42 41 $(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
+93
tools/testing/selftests/x86/test_FCMOV.c
··· 1 + #undef _GNU_SOURCE 2 + #define _GNU_SOURCE 1 3 + #undef __USE_GNU 4 + #define __USE_GNU 1 5 + #include <unistd.h> 6 + #include <stdlib.h> 7 + #include <string.h> 8 + #include <stdio.h> 9 + #include <signal.h> 10 + #include <sys/types.h> 11 + #include <sys/select.h> 12 + #include <sys/time.h> 13 + #include <sys/wait.h> 14 + 15 + #define TEST(insn) \ 16 + long double __attribute__((noinline)) insn(long flags) \ 17 + { \ 18 + long double out; \ 19 + asm ("\n" \ 20 + " push %1""\n" \ 21 + " popf""\n" \ 22 + " fldpi""\n" \ 23 + " fld1""\n" \ 24 + " " #insn " %%st(1), %%st" "\n" \ 25 + " ffree %%st(1)" "\n" \ 26 + : "=t" (out) \ 27 + : "r" (flags) \ 28 + ); \ 29 + return out; \ 30 + } 31 + 32 + TEST(fcmovb) 33 + TEST(fcmove) 34 + TEST(fcmovbe) 35 + TEST(fcmovu) 36 + TEST(fcmovnb) 37 + TEST(fcmovne) 38 + TEST(fcmovnbe) 39 + TEST(fcmovnu) 40 + 41 + enum { 42 + CF = 1 << 0, 43 + PF = 1 << 2, 44 + ZF = 1 << 6, 45 + }; 46 + 47 + void sighandler(int sig) 48 + { 49 + printf("[FAIL]\tGot signal %d, exiting\n", sig); 50 + exit(1); 51 + } 52 + 53 + int main(int argc, char **argv, char **envp) 54 + { 55 + int err = 0; 56 + 57 + /* SIGILL triggers on 32-bit kernels w/o fcomi emulation 58 + * when run with "no387 nofxsr". Other signals are caught 59 + * just in case. 60 + */ 61 + signal(SIGILL, sighandler); 62 + signal(SIGFPE, sighandler); 63 + signal(SIGSEGV, sighandler); 64 + 65 + printf("[RUN]\tTesting fcmovCC instructions\n"); 66 + /* If fcmovCC() returns 1.0, the move wasn't done */ 67 + err |= !(fcmovb(0) == 1.0); err |= !(fcmovnb(0) != 1.0); 68 + err |= !(fcmove(0) == 1.0); err |= !(fcmovne(0) != 1.0); 69 + err |= !(fcmovbe(0) == 1.0); err |= !(fcmovnbe(0) != 1.0); 70 + err |= !(fcmovu(0) == 1.0); err |= !(fcmovnu(0) != 1.0); 71 + 72 + err |= !(fcmovb(CF) != 1.0); err |= !(fcmovnb(CF) == 1.0); 73 + err |= !(fcmove(CF) == 1.0); err |= !(fcmovne(CF) != 1.0); 74 + err |= !(fcmovbe(CF) != 1.0); err |= !(fcmovnbe(CF) == 1.0); 75 + err |= !(fcmovu(CF) == 1.0); err |= !(fcmovnu(CF) != 1.0); 76 + 77 + err |= !(fcmovb(ZF) == 1.0); err |= !(fcmovnb(ZF) != 1.0); 78 + err |= !(fcmove(ZF) != 1.0); err |= !(fcmovne(ZF) == 1.0); 79 + err |= !(fcmovbe(ZF) != 1.0); err |= !(fcmovnbe(ZF) == 1.0); 80 + err |= !(fcmovu(ZF) == 1.0); err |= !(fcmovnu(ZF) != 1.0); 81 + 82 + err |= !(fcmovb(PF) == 1.0); err |= !(fcmovnb(PF) != 1.0); 83 + err |= !(fcmove(PF) == 1.0); err |= !(fcmovne(PF) != 1.0); 84 + err |= !(fcmovbe(PF) == 1.0); err |= !(fcmovnbe(PF) != 1.0); 85 + err |= !(fcmovu(PF) != 1.0); err |= !(fcmovnu(PF) == 1.0); 86 + 87 + if (!err) 88 + printf("[OK]\tfcmovCC\n"); 89 + else 90 + printf("[FAIL]\tfcmovCC errors: %d\n", err); 91 + 92 + return err; 93 + }
+331
tools/testing/selftests/x86/test_FCOMI.c
··· 1 + #undef _GNU_SOURCE 2 + #define _GNU_SOURCE 1 3 + #undef __USE_GNU 4 + #define __USE_GNU 1 5 + #include <unistd.h> 6 + #include <stdlib.h> 7 + #include <string.h> 8 + #include <stdio.h> 9 + #include <signal.h> 10 + #include <sys/types.h> 11 + #include <sys/select.h> 12 + #include <sys/time.h> 13 + #include <sys/wait.h> 14 + #include <fenv.h> 15 + 16 + enum { 17 + CF = 1 << 0, 18 + PF = 1 << 2, 19 + ZF = 1 << 6, 20 + ARITH = CF | PF | ZF, 21 + }; 22 + 23 + long res_fcomi_pi_1; 24 + long res_fcomi_1_pi; 25 + long res_fcomi_1_1; 26 + long res_fcomi_nan_1; 27 + /* sNaN is s|111 1111 1|1xx xxxx xxxx xxxx xxxx xxxx */ 28 + /* qNaN is s|111 1111 1|0xx xxxx xxxx xxxx xxxx xxxx (some x must be nonzero) */ 29 + int snan = 0x7fc11111; 30 + int qnan = 0x7f811111; 31 + unsigned short snan1[5]; 32 + /* sNaN80 is s|111 1111 1111 1111 |10xx xx...xx (some x must be nonzero) */ 33 + unsigned short snan80[5] = { 0x1111, 0x1111, 0x1111, 0x8111, 0x7fff }; 34 + 35 + int test(long flags) 36 + { 37 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 38 + 39 + asm ("\n" 40 + 41 + " push %0""\n" 42 + " popf""\n" 43 + " fld1""\n" 44 + " fldpi""\n" 45 + " fcomi %%st(1), %%st" "\n" 46 + " ffree %%st(0)" "\n" 47 + " ffree %%st(1)" "\n" 48 + " pushf""\n" 49 + " pop res_fcomi_1_pi""\n" 50 + 51 + " push %0""\n" 52 + " popf""\n" 53 + " fldpi""\n" 54 + " fld1""\n" 55 + " fcomi %%st(1), %%st" "\n" 56 + " ffree %%st(0)" "\n" 57 + " ffree %%st(1)" "\n" 58 + " pushf""\n" 59 + " pop res_fcomi_pi_1""\n" 60 + 61 + " push %0""\n" 62 + " popf""\n" 63 + " fld1""\n" 64 + " fld1""\n" 65 + " fcomi %%st(1), %%st" "\n" 66 + " ffree %%st(0)" "\n" 67 + " ffree %%st(1)" "\n" 68 + " pushf""\n" 69 + " pop res_fcomi_1_1""\n" 70 + : 71 + : "r" (flags) 72 + ); 73 + if ((res_fcomi_1_pi & ARITH) != (0)) { 74 + printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); 75 + return 1; 76 + } 77 + if ((res_fcomi_pi_1 & ARITH) != (CF)) { 78 + printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); 79 + return 1; 80 + } 81 + if ((res_fcomi_1_1 & ARITH) != (ZF)) { 82 + printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); 83 + return 1; 84 + } 85 + if (fetestexcept(FE_INVALID) != 0) { 86 + printf("[BAD]\tFE_INVALID is set in %s\n", __func__); 87 + return 1; 88 + } 89 + return 0; 90 + } 91 + 92 + int test_qnan(long flags) 93 + { 94 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 95 + 96 + asm ("\n" 97 + " push %0""\n" 98 + " popf""\n" 99 + " flds qnan""\n" 100 + " fld1""\n" 101 + " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it 102 + " fcomi %%st(1), %%st" "\n" 103 + " ffree %%st(0)" "\n" 104 + " ffree %%st(1)" "\n" 105 + " pushf""\n" 106 + " pop res_fcomi_nan_1""\n" 107 + : 108 + : "r" (flags) 109 + ); 110 + if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { 111 + printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); 112 + return 1; 113 + } 114 + if (fetestexcept(FE_INVALID) != FE_INVALID) { 115 + printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); 116 + return 1; 117 + } 118 + return 0; 119 + } 120 + 121 + int testu_qnan(long flags) 122 + { 123 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 124 + 125 + asm ("\n" 126 + " push %0""\n" 127 + " popf""\n" 128 + " flds qnan""\n" 129 + " fld1""\n" 130 + " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it 131 + " fucomi %%st(1), %%st" "\n" 132 + " ffree %%st(0)" "\n" 133 + " ffree %%st(1)" "\n" 134 + " pushf""\n" 135 + " pop res_fcomi_nan_1""\n" 136 + : 137 + : "r" (flags) 138 + ); 139 + if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { 140 + printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); 141 + return 1; 142 + } 143 + if (fetestexcept(FE_INVALID) != 0) { 144 + printf("[BAD]\tFE_INVALID is set in %s\n", __func__); 145 + return 1; 146 + } 147 + return 0; 148 + } 149 + 150 + int testu_snan(long flags) 151 + { 152 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 153 + 154 + asm ("\n" 155 + " push %0""\n" 156 + " popf""\n" 157 + // " flds snan""\n" // WRONG, this will convert 32-bit fp snan to a *qnan* in 80-bit fp register! 158 + // " fstpt snan1""\n" // if uncommented, it prints "snan1:7fff c111 1100 0000 0000" - c111, not 8111! 159 + // " fnclex""\n" // flds of a snan raised FE_INVALID, clear it 160 + " fldt snan80""\n" // fldt never raise FE_INVALID 161 + " fld1""\n" 162 + " fucomi %%st(1), %%st" "\n" 163 + " ffree %%st(0)" "\n" 164 + " ffree %%st(1)" "\n" 165 + " pushf""\n" 166 + " pop res_fcomi_nan_1""\n" 167 + : 168 + : "r" (flags) 169 + ); 170 + if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { 171 + printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); 172 + return 1; 173 + } 174 + // printf("snan:%x snan1:%04x %04x %04x %04x %04x\n", snan, snan1[4], snan1[3], snan1[2], snan1[1], snan1[0]); 175 + if (fetestexcept(FE_INVALID) != FE_INVALID) { 176 + printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); 177 + return 1; 178 + } 179 + return 0; 180 + } 181 + 182 + int testp(long flags) 183 + { 184 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 185 + 186 + asm ("\n" 187 + 188 + " push %0""\n" 189 + " popf""\n" 190 + " fld1""\n" 191 + " fldpi""\n" 192 + " fcomip %%st(1), %%st" "\n" 193 + " ffree %%st(0)" "\n" 194 + " pushf""\n" 195 + " pop res_fcomi_1_pi""\n" 196 + 197 + " push %0""\n" 198 + " popf""\n" 199 + " fldpi""\n" 200 + " fld1""\n" 201 + " fcomip %%st(1), %%st" "\n" 202 + " ffree %%st(0)" "\n" 203 + " pushf""\n" 204 + " pop res_fcomi_pi_1""\n" 205 + 206 + " push %0""\n" 207 + " popf""\n" 208 + " fld1""\n" 209 + " fld1""\n" 210 + " fcomip %%st(1), %%st" "\n" 211 + " ffree %%st(0)" "\n" 212 + " pushf""\n" 213 + " pop res_fcomi_1_1""\n" 214 + : 215 + : "r" (flags) 216 + ); 217 + if ((res_fcomi_1_pi & ARITH) != (0)) { 218 + printf("[BAD]\tfcomi_1_pi with flags:%lx\n", flags); 219 + return 1; 220 + } 221 + if ((res_fcomi_pi_1 & ARITH) != (CF)) { 222 + printf("[BAD]\tfcomi_pi_1 with flags:%lx->%lx\n", flags, res_fcomi_pi_1 & ARITH); 223 + return 1; 224 + } 225 + if ((res_fcomi_1_1 & ARITH) != (ZF)) { 226 + printf("[BAD]\tfcomi_1_1 with flags:%lx\n", flags); 227 + return 1; 228 + } 229 + if (fetestexcept(FE_INVALID) != 0) { 230 + printf("[BAD]\tFE_INVALID is set in %s\n", __func__); 231 + return 1; 232 + } 233 + return 0; 234 + } 235 + 236 + int testp_qnan(long flags) 237 + { 238 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 239 + 240 + asm ("\n" 241 + " push %0""\n" 242 + " popf""\n" 243 + " flds qnan""\n" 244 + " fld1""\n" 245 + " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it 246 + " fcomip %%st(1), %%st" "\n" 247 + " ffree %%st(0)" "\n" 248 + " pushf""\n" 249 + " pop res_fcomi_nan_1""\n" 250 + : 251 + : "r" (flags) 252 + ); 253 + if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { 254 + printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); 255 + return 1; 256 + } 257 + if (fetestexcept(FE_INVALID) != FE_INVALID) { 258 + printf("[BAD]\tFE_INVALID is not set in %s\n", __func__); 259 + return 1; 260 + } 261 + return 0; 262 + } 263 + 264 + int testup_qnan(long flags) 265 + { 266 + feclearexcept(FE_DIVBYZERO|FE_INEXACT|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); 267 + 268 + asm ("\n" 269 + " push %0""\n" 270 + " popf""\n" 271 + " flds qnan""\n" 272 + " fld1""\n" 273 + " fnclex""\n" // fld of a qnan raised FE_INVALID, clear it 274 + " fucomip %%st(1), %%st" "\n" 275 + " ffree %%st(0)" "\n" 276 + " pushf""\n" 277 + " pop res_fcomi_nan_1""\n" 278 + : 279 + : "r" (flags) 280 + ); 281 + if ((res_fcomi_nan_1 & ARITH) != (ZF|CF|PF)) { 282 + printf("[BAD]\tfcomi_qnan_1 with flags:%lx\n", flags); 283 + return 1; 284 + } 285 + if (fetestexcept(FE_INVALID) != 0) { 286 + printf("[BAD]\tFE_INVALID is set in %s\n", __func__); 287 + return 1; 288 + } 289 + return 0; 290 + } 291 + 292 + void sighandler(int sig) 293 + { 294 + printf("[FAIL]\tGot signal %d, exiting\n", sig); 295 + exit(1); 296 + } 297 + 298 + int main(int argc, char **argv, char **envp) 299 + { 300 + int err = 0; 301 + 302 + /* SIGILL triggers on 32-bit kernels w/o fcomi emulation 303 + * when run with "no387 nofxsr". Other signals are caught 304 + * just in case. 305 + */ 306 + signal(SIGILL, sighandler); 307 + signal(SIGFPE, sighandler); 308 + signal(SIGSEGV, sighandler); 309 + 310 + printf("[RUN]\tTesting f[u]comi[p] instructions\n"); 311 + err |= test(0); 312 + err |= test_qnan(0); 313 + err |= testu_qnan(0); 314 + err |= testu_snan(0); 315 + err |= test(CF|ZF|PF); 316 + err |= test_qnan(CF|ZF|PF); 317 + err |= testu_qnan(CF|ZF|PF); 318 + err |= testu_snan(CF|ZF|PF); 319 + err |= testp(0); 320 + err |= testp_qnan(0); 321 + err |= testup_qnan(0); 322 + err |= testp(CF|ZF|PF); 323 + err |= testp_qnan(CF|ZF|PF); 324 + err |= testup_qnan(CF|ZF|PF); 325 + if (!err) 326 + printf("[OK]\tf[u]comi[p]\n"); 327 + else 328 + printf("[FAIL]\tf[u]comi[p] errors: %d\n", err); 329 + 330 + return err; 331 + }