1 /* $NetBSD: sig_fpu.c,v 1.3 2026/07/15 02:08:18 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __RCSID("$NetBSD: sig_fpu.c,v 1.3 2026/07/15 02:08:18 riastradh Exp $"); 31 32 #include "sig_fpu.h" 33 34 #include <sys/types.h> 35 36 #include <x86/cpu_extended_state.h> 37 38 #include <cpuid.h> 39 #include <inttypes.h> 40 #include <sched.h> 41 #include <signal.h> 42 #include <stdbool.h> 43 #include <stdio.h> 44 45 #include "h_macros.h" 46 47 #ifdef __x86_64__ 48 # define NXMMREGS 16 49 # define NYMMREGS 16 50 # define NZMMREGS 32 51 #else /* 32-bit */ 52 # define NXMMREGS 8 53 # define NYMMREGS 8 54 # define NZMMREGS 8 55 #endif 56 57 #define X87_CW_RC __BITS(11,10) /* rounding control (mode) */ 58 #define X87_CW_PC __BITS(9,8) /* precision control */ 59 #define X87_CW_SW_EXC __BITS(5,0) /* exception bits */ 60 61 struct cpuid { 62 uint32_t edx; 63 uint32_t ecx; 64 uint32_t ebx; 65 uint32_t eax; 66 }; 67 68 static struct cpuid 69 cpuid(uint32_t eax_fn, uint32_t ecx_idx) 70 { 71 struct cpuid c; 72 73 if (!__get_cpuid_count(eax_fn, ecx_idx, 74 &c.eax, &c.ebx, &c.ecx, &c.edx)) 75 memset(&c, 0, sizeof(c)); 76 77 return c; 78 } 79 80 __printflike(4,5) 81 __noinline 82 static void 83 hexdump(FILE *fp, const void *buf, size_t len, const char *title, ...) 84 { 85 va_list va; 86 const uint8_t *p = buf; 87 size_t i; 88 89 fprintf(fp, "# "); 90 va_start(va, title); 91 vfprintf(fp, title, va); 92 va_end(va); 93 fprintf(fp, "\n"); 94 for (i = 0; i < len; i++) { 95 if ((i % 8) == 0) 96 fprintf(fp, " "); 97 fprintf(fp, " %02hhx", p[i]); 98 if ((i % 16) == 15) 99 fprintf(fp, "\n"); 100 } 101 if (i % 16) 102 fprintf(fp, "\n"); 103 } 104 105 bool 106 x87_supported(void) 107 { 108 #ifdef __x86_64__ 109 return true; 110 #else /* 32-bit */ 111 return cpuid(0x00000001, 0).edx & __BIT(0); 112 #endif 113 } 114 115 int 116 test_x87(volatile bool *ready, const volatile bool *done) 117 { 118 struct save87 s, s1; 119 uint32_t rcpc_exc; 120 int error = 0; 121 122 /* 123 * Gather the current FPU state so we have some reasonable 124 * content for the weird stuff when we restore from it. 125 */ 126 asm("wait\n" 127 " fnsave %0" 128 : /*out*/ "=m"(s)); 129 130 /* 131 * Randomize the floating-point stack entries ST(0),...,ST(7). 132 */ 133 arc4random_buf(s.s87_ac, sizeof(s.s87_ac)); 134 135 /* 136 * Randomize rounding control, precision control, and a set of 137 * exception bits. We don't want to raise any unmasked 138 * exceptions, so we'll raise _and_ mask them. 139 * 140 * XXX Consider testing the condition code, exception status, 141 * and top of stack bits in the status word too. 142 */ 143 rcpc_exc = arc4random() & (X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC); 144 if (__SHIFTOUT(rcpc_exc, X87_CW_PC) == 1) /* reserved */ 145 rcpc_exc |= __SHIFTIN(3, X87_CW_PC); /* extended */ 146 s.s87_cw &= ~(X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC); 147 s.s87_cw |= rcpc_exc; 148 s.s87_sw &= ~X87_CW_SW_EXC; 149 s.s87_sw |= rcpc_exc & X87_CW_SW_EXC; 150 151 /* 152 * Load up the registers, report that we're ready, busy-wait 153 * with the registers still live -- so no subroutine calls -- 154 * until we're done, and then store the registers back to 155 * memory so we can check them. 156 */ 157 asm("\n" 158 " frstor %[s]\n" 159 " lock\n" 160 " orb $1,%[ready]\n" 161 "0: pause\n" 162 " cmpb $0,%[done]\n" 163 " lfence\n" 164 " je 0b\n" 165 " wait\n" 166 " fnsave %[s1]\n" 167 : /*out*/ [ready]"=m"(*ready), [s1]"=m"(s1) 168 : /*in*/ [done]"m"(*done), [s]"m"(s)); 169 170 /* 171 * If there are any mismatches between the before and after 172 * ST(i) registers, or the control word, or the status word, 173 * print them and fail. 174 */ 175 if (memcmp(s.s87_ac, s1.s87_ac, sizeof(s.s87_ac)) != 0) { 176 unsigned i; 177 178 for (i = 0; i < 8; i++) { 179 if (memcmp(&s.s87_ac[i], &s1.s87_ac[i], 180 sizeof(s.s87_ac[i])) == 0) 181 continue; 182 fprintf(stderr, "ST(%u)=0x%08x%016"PRIx64"," 183 " expected 0x%08x%016"PRIx64"\n", 184 i, 185 s1.s87_ac[i].f87_exp_sign, 186 s1.s87_ac[i].f87_mantissa, 187 s.s87_ac[i].f87_exp_sign, 188 s.s87_ac[i].f87_mantissa); 189 error |= 1 << i; 190 } 191 } 192 if ((s1.s87_cw & (X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC)) != 193 (s.s87_cw & (X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC))) { 194 fprintf(stderr, "cw=0x%04x, expected 0x%04x," 195 " diff 0x%04x\n", 196 s1.s87_cw, s.s87_cw, 197 (uint32_t)((s1.s87_cw ^ s.s87_cw) & 198 (X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC))); 199 error |= 1 << 8; 200 } 201 if ((s1.s87_sw & X87_CW_SW_EXC) != 202 (s.s87_cw & X87_CW_SW_EXC)) { 203 fprintf(stderr, "sw=0x%04x, expected 0x%04x," 204 " diff 0x%04x\n", 205 s1.s87_sw, s.s87_sw, 206 (uint32_t)((s1.s87_sw ^ s.s87_sw) & 207 X87_CW_SW_EXC)); 208 error |= 1 << 9; 209 } 210 211 return error; 212 } 213 214 void 215 trash_x87(void) 216 { 217 struct save87 s; 218 uint32_t rcpc_exc; 219 220 /* 221 * Gather the current FPU state so we have some reasonable 222 * content for the weird stuff when we restore from it. 223 */ 224 asm("wait\n" 225 " fnsave %0" 226 : /*out*/ "=m"(s)); 227 228 /* 229 * Randomize the floating-point stack entries ST(0),...,ST(7). 230 */ 231 arc4random_buf(s.s87_ac, sizeof(s.s87_ac)); 232 233 /* 234 * Randomize rounding control, precision control, and a set of 235 * exception bits. We don't want to raise any unmasked 236 * exceptions, so we'll raise _and_ mask them. 237 * 238 * XXX Consider trashing the condition code, exception status, 239 * and top of stack bits in the status word too. 240 */ 241 rcpc_exc = arc4random() & (X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC); 242 s.s87_cw &= ~(X87_CW_RC | X87_CW_PC | X87_CW_SW_EXC); 243 s.s87_cw |= rcpc_exc; 244 s.s87_sw &= ~X87_CW_SW_EXC; 245 s.s87_sw |= rcpc_exc & X87_CW_SW_EXC; 246 247 /* 248 * Load the state into the FPU. 249 */ 250 asm volatile("frstor %0" :: /*in*/ "m"(s)); 251 } 252 253 #define MXCSR_MISALIGNEDEXC __BIT(17) /* misaligned exc fault */ 254 #define MXCSR_FTZ __BIT(15) /* flush to zero */ 255 #define MXCSR_RC __BITS(14,13) /* rounding control */ 256 #define MXCSR_EXCMASK __BITS(12,7) /* exceptions masked */ 257 #define MXCSR_DAZ __BIT(6) /* denormals are zero */ 258 #define MXCSR_EXCFLAG __BITS(5,0) /* exceptions raised */ 259 260 struct xmmregs { 261 struct { 262 uint8_t b[16]; 263 } __aligned(16) xmm[NXMMREGS]; 264 uint32_t mxcsr; 265 }; 266 267 bool 268 xmm_supported(void) 269 { 270 #ifdef __x86_64__ 271 return true; 272 #else /* 32-bit */ 273 return cpuid(0x00000001, 0).edx & __BIT(25); 274 #endif 275 } 276 277 __attribute__((target("sse"))) 278 int 279 test_xmm(volatile bool *ready, const volatile bool *done) 280 { 281 struct xmmregs before, after; 282 uint32_t exc; 283 unsigned i; 284 int error = 0; 285 286 /* 287 * Randomize the xmm register content. Randomize plausible 288 * bits for the mxcsr. 289 */ 290 arc4random_buf(before.xmm, sizeof(before.xmm)); 291 before.mxcsr = arc4random() & (MXCSR_FTZ | MXCSR_RC | MXCSR_DAZ); 292 exc = arc4random() & 0x3f; 293 before.mxcsr |= __SHIFTIN(exc, MXCSR_EXCMASK); 294 before.mxcsr |= __SHIFTIN(exc, MXCSR_EXCFLAG); 295 296 /* 297 * Load up the registers, report that we're ready, busy-wait 298 * with the registers still live -- so no subroutine calls -- 299 * until we're done, and then store the registers back to 300 * memory so we can check them. 301 */ 302 asm("\n" 303 " ldmxcsr %[mxcsr_before]\n" 304 " movdqa 0*16(%[xmm_before]),%%xmm0\n" 305 " movdqa 1*16(%[xmm_before]),%%xmm1\n" 306 " movdqa 2*16(%[xmm_before]),%%xmm2\n" 307 " movdqa 3*16(%[xmm_before]),%%xmm3\n" 308 " movdqa 4*16(%[xmm_before]),%%xmm4\n" 309 " movdqa 5*16(%[xmm_before]),%%xmm5\n" 310 " movdqa 6*16(%[xmm_before]),%%xmm6\n" 311 " movdqa 7*16(%[xmm_before]),%%xmm7\n" 312 #ifdef __x86_64__ 313 " movdqa 8*16(%[xmm_before]),%%xmm8\n" 314 " movdqa 9*16(%[xmm_before]),%%xmm9\n" 315 " movdqa 10*16(%[xmm_before]),%%xmm10\n" 316 " movdqa 11*16(%[xmm_before]),%%xmm11\n" 317 " movdqa 12*16(%[xmm_before]),%%xmm12\n" 318 " movdqa 13*16(%[xmm_before]),%%xmm13\n" 319 " movdqa 14*16(%[xmm_before]),%%xmm14\n" 320 " movdqa 15*16(%[xmm_before]),%%xmm15\n" 321 #endif 322 " lock\n" 323 " orb $1,%[ready]\n" 324 "0: pause\n" 325 " cmpb $0,%[done]\n" 326 " lfence\n" 327 " je 0b\n" 328 " movdqa %%xmm0,0*16(%[xmm_after])\n" 329 " movdqa %%xmm1,1*16(%[xmm_after])\n" 330 " movdqa %%xmm2,2*16(%[xmm_after])\n" 331 " movdqa %%xmm3,3*16(%[xmm_after])\n" 332 " movdqa %%xmm4,4*16(%[xmm_after])\n" 333 " movdqa %%xmm5,5*16(%[xmm_after])\n" 334 " movdqa %%xmm6,6*16(%[xmm_after])\n" 335 " movdqa %%xmm7,7*16(%[xmm_after])\n" 336 #ifdef __x86_64__ 337 " movdqa %%xmm8,8*16(%[xmm_after])\n" 338 " movdqa %%xmm9,9*16(%[xmm_after])\n" 339 " movdqa %%xmm10,10*16(%[xmm_after])\n" 340 " movdqa %%xmm11,11*16(%[xmm_after])\n" 341 " movdqa %%xmm12,12*16(%[xmm_after])\n" 342 " movdqa %%xmm13,13*16(%[xmm_after])\n" 343 " movdqa %%xmm14,14*16(%[xmm_after])\n" 344 " movdqa %%xmm15,15*16(%[xmm_after])\n" 345 #endif 346 " stmxcsr %[mxcsr_after]\n" 347 : /*out*/ [ready]"=m"(*ready), "=m"(after.xmm), 348 [mxcsr_after]"=m"(after.mxcsr) 349 : /*in*/ [done]"m"(*done), "m"(before.xmm), 350 [mxcsr_before]"m"(before.mxcsr), 351 [xmm_before]"r"(&before.xmm), [xmm_after]"r"(&after.xmm) 352 : /*clobber*/ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", 353 "xmm6", "xmm7" 354 #ifdef __x86_64__ 355 , "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", 356 "xmm13", "xmm14", "xmm15" 357 #endif 358 ); 359 360 /* 361 * If there are any mismatches between the before and after xmm 362 * registers, or the mxcsr, print them and fail. 363 */ 364 for (i = 0; i < __arraycount(before.xmm); i++) { 365 if (memcmp(&before.xmm[i], &after.xmm[i], 366 sizeof(before.xmm[i]))) { 367 fprintf(stderr, "xmm%u clobbered\n", i); 368 hexdump(stderr, &before.xmm[i], sizeof(before.xmm[i]), 369 "before"); 370 hexdump(stderr, &after.xmm[i], sizeof(after.xmm[i]), 371 "after"); 372 error |= 1 << i; 373 } 374 } 375 376 if (before.mxcsr != after.mxcsr) { 377 fprintf(stderr, "mxcsr clobbered:" 378 " before=0x%08"PRIx32", after=0x%08"PRIx32"\n", 379 before.mxcsr, after.mxcsr); 380 error |= 1 << 16; 381 } 382 383 return error; 384 } 385 386 __attribute__((target("sse"))) 387 void 388 trash_xmm(void) 389 { 390 struct xmmregs regs; 391 392 arc4random_buf(®s, sizeof(regs)); 393 394 asm("\n" 395 " movdqa 0*16(%[xmm]),%%xmm0\n" 396 " movdqa 1*16(%[xmm]),%%xmm1\n" 397 " movdqa 2*16(%[xmm]),%%xmm2\n" 398 " movdqa 3*16(%[xmm]),%%xmm3\n" 399 " movdqa 4*16(%[xmm]),%%xmm4\n" 400 " movdqa 5*16(%[xmm]),%%xmm5\n" 401 " movdqa 6*16(%[xmm]),%%xmm6\n" 402 " movdqa 7*16(%[xmm]),%%xmm7\n" 403 #ifdef __x86_64__ 404 " movdqa 8*16(%[xmm]),%%xmm8\n" 405 " movdqa 9*16(%[xmm]),%%xmm9\n" 406 " movdqa 10*16(%[xmm]),%%xmm10\n" 407 " movdqa 11*16(%[xmm]),%%xmm11\n" 408 " movdqa 12*16(%[xmm]),%%xmm12\n" 409 " movdqa 13*16(%[xmm]),%%xmm13\n" 410 " movdqa 14*16(%[xmm]),%%xmm14\n" 411 " movdqa 15*16(%[xmm]),%%xmm15\n" 412 #endif 413 : /*out*/ 414 : /*in*/ [xmm]"r"(regs.xmm), "m"(regs.xmm) 415 : /*clobber*/ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", 416 "xmm6", "xmm7" 417 #ifdef __x86_64__ 418 , "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", 419 "xmm13", "xmm14", "xmm15" 420 #endif 421 ); 422 } 423 424 bool 425 ymm_supported(void) 426 { 427 return cpuid(0x00000001, 0).ecx & __BIT(28); 428 } 429 430 struct ymmregs { 431 struct { 432 uint8_t b[32]; 433 } __aligned(32) ymm[NYMMREGS]; 434 }; 435 436 __attribute__((target("avx"))) 437 int 438 test_ymm(volatile bool *ready, const volatile bool *done) 439 { 440 struct ymmregs before, after; 441 unsigned i; 442 int error = 0; 443 444 /* 445 * Randomize the ymm register content. 446 */ 447 arc4random_buf(&before, sizeof(before)); 448 449 /* 450 * Load up the registers, report that we're ready, busy-wait 451 * with the registers still live -- so no subroutine calls -- 452 * until we're done, and then store the registers back to 453 * memory so we can check them. 454 */ 455 asm("\n" 456 " vmovdqa 0*32(%[before_ymm]),%%ymm0\n" 457 " vmovdqa 1*32(%[before_ymm]),%%ymm1\n" 458 " vmovdqa 2*32(%[before_ymm]),%%ymm2\n" 459 " vmovdqa 3*32(%[before_ymm]),%%ymm3\n" 460 " vmovdqa 4*32(%[before_ymm]),%%ymm4\n" 461 " vmovdqa 5*32(%[before_ymm]),%%ymm5\n" 462 " vmovdqa 6*32(%[before_ymm]),%%ymm6\n" 463 " vmovdqa 7*32(%[before_ymm]),%%ymm7\n" 464 #ifdef __x86_64__ 465 " vmovdqa 8*32(%[before_ymm]),%%ymm8\n" 466 " vmovdqa 9*32(%[before_ymm]),%%ymm9\n" 467 " vmovdqa 10*32(%[before_ymm]),%%ymm10\n" 468 " vmovdqa 11*32(%[before_ymm]),%%ymm11\n" 469 " vmovdqa 12*32(%[before_ymm]),%%ymm12\n" 470 " vmovdqa 13*32(%[before_ymm]),%%ymm13\n" 471 " vmovdqa 14*32(%[before_ymm]),%%ymm14\n" 472 " vmovdqa 15*32(%[before_ymm]),%%ymm15\n" 473 #endif 474 " lock\n" 475 " orb $1,%[ready]\n" 476 "0: pause\n" 477 " cmpb $0,%[done]\n" 478 " lfence\n" 479 " je 0b\n" 480 " vmovdqa %%ymm0,0*32(%[after_ymm])\n" 481 " vmovdqa %%ymm1,1*32(%[after_ymm])\n" 482 " vmovdqa %%ymm2,2*32(%[after_ymm])\n" 483 " vmovdqa %%ymm3,3*32(%[after_ymm])\n" 484 " vmovdqa %%ymm4,4*32(%[after_ymm])\n" 485 " vmovdqa %%ymm5,5*32(%[after_ymm])\n" 486 " vmovdqa %%ymm6,6*32(%[after_ymm])\n" 487 " vmovdqa %%ymm7,7*32(%[after_ymm])\n" 488 #ifdef __x86_64__ 489 " vmovdqa %%ymm8,8*32(%[after_ymm])\n" 490 " vmovdqa %%ymm9,9*32(%[after_ymm])\n" 491 " vmovdqa %%ymm10,10*32(%[after_ymm])\n" 492 " vmovdqa %%ymm11,11*32(%[after_ymm])\n" 493 " vmovdqa %%ymm12,12*32(%[after_ymm])\n" 494 " vmovdqa %%ymm13,13*32(%[after_ymm])\n" 495 " vmovdqa %%ymm14,14*32(%[after_ymm])\n" 496 " vmovdqa %%ymm15,15*32(%[after_ymm])\n" 497 #endif 498 : /*out*/ [ready]"=m"(*ready), "=m"(after.ymm) 499 : /*in*/ [done]"m"(*done), "m"(before.ymm), 500 [before_ymm]"r"(before.ymm), [after_ymm]"r"(after.ymm) 501 : /*clobber*/ "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", 502 "ymm6", "ymm7" 503 #ifdef __x86_64__ 504 , "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", 505 "ymm13", "ymm14", "ymm15" 506 #endif 507 ); 508 509 for (i = 0; i < __arraycount(before.ymm); i++) { 510 if (memcmp(&before.ymm[i], &after.ymm[i], 511 sizeof(before.ymm[i]))) { 512 fprintf(stderr, "ymm%u clobbered\n", i); 513 hexdump(stderr, &before.ymm[i], sizeof(before.ymm[i]), 514 "before"); 515 hexdump(stderr, &after.ymm[i], sizeof(after.ymm[i]), 516 "after"); 517 error |= 1 << i; 518 } 519 } 520 521 return error; 522 } 523 524 __attribute__((target("avx"))) 525 void 526 trash_ymm(void) 527 { 528 struct ymmregs regs; 529 530 arc4random_buf(®s, sizeof(regs)); 531 532 asm("\n" 533 " vmovdqa 0*32(%[ymm]),%%ymm0\n" 534 " vmovdqa 1*32(%[ymm]),%%ymm1\n" 535 " vmovdqa 2*32(%[ymm]),%%ymm2\n" 536 " vmovdqa 3*32(%[ymm]),%%ymm3\n" 537 " vmovdqa 4*32(%[ymm]),%%ymm4\n" 538 " vmovdqa 5*32(%[ymm]),%%ymm5\n" 539 " vmovdqa 6*32(%[ymm]),%%ymm6\n" 540 " vmovdqa 7*32(%[ymm]),%%ymm7\n" 541 #ifdef __x86_64__ 542 " vmovdqa 8*32(%[ymm]),%%ymm8\n" 543 " vmovdqa 9*32(%[ymm]),%%ymm9\n" 544 " vmovdqa 10*32(%[ymm]),%%ymm10\n" 545 " vmovdqa 11*32(%[ymm]),%%ymm11\n" 546 " vmovdqa 12*32(%[ymm]),%%ymm12\n" 547 " vmovdqa 13*32(%[ymm]),%%ymm13\n" 548 " vmovdqa 14*32(%[ymm]),%%ymm14\n" 549 " vmovdqa 15*32(%[ymm]),%%ymm15\n" 550 #endif 551 : /*out*/ 552 : /*in*/ "m"(regs.ymm), [ymm]"r"(regs.ymm) 553 : /*clobber*/ "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", 554 "ymm6", "ymm7" 555 #ifdef __x86_64__ 556 , "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", 557 "ymm13", "ymm14", "ymm15" 558 #endif 559 ); 560 } 561 562 bool 563 zmm_supported(void) 564 { 565 return cpuid(0x00000007, 0).ebx & __BIT(16); 566 } 567 568 struct zmmregs { 569 struct { 570 uint8_t b[64]; 571 } __aligned(64) zmm[NZMMREGS]; 572 }; 573 574 __attribute__((target("avx512f"))) 575 int 576 test_zmm(volatile bool *ready, const volatile bool *done) 577 { 578 struct zmmregs before, after; 579 unsigned i; 580 int error = 0; 581 582 /* 583 * Randomize the zmm register content. 584 */ 585 arc4random_buf(&before, sizeof(before)); 586 587 /* 588 * Load up the registers, report that we're ready, busy-wait 589 * with the registers still live -- so no subroutine calls -- 590 * until we're done, and then store the registers back to 591 * memory so we can check them. 592 */ 593 asm("\n" 594 " vmovdqa64 0*64(%[before_zmm]),%%zmm0\n" 595 " vmovdqa64 1*64(%[before_zmm]),%%zmm1\n" 596 " vmovdqa64 2*64(%[before_zmm]),%%zmm2\n" 597 " vmovdqa64 3*64(%[before_zmm]),%%zmm3\n" 598 " vmovdqa64 4*64(%[before_zmm]),%%zmm4\n" 599 " vmovdqa64 5*64(%[before_zmm]),%%zmm5\n" 600 " vmovdqa64 6*64(%[before_zmm]),%%zmm6\n" 601 " vmovdqa64 7*64(%[before_zmm]),%%zmm7\n" 602 #ifdef __x86_64__ 603 " vmovdqa64 8*64(%[before_zmm]),%%zmm8\n" 604 " vmovdqa64 9*64(%[before_zmm]),%%zmm9\n" 605 " vmovdqa64 10*64(%[before_zmm]),%%zmm10\n" 606 " vmovdqa64 11*64(%[before_zmm]),%%zmm11\n" 607 " vmovdqa64 12*64(%[before_zmm]),%%zmm12\n" 608 " vmovdqa64 13*64(%[before_zmm]),%%zmm13\n" 609 " vmovdqa64 14*64(%[before_zmm]),%%zmm14\n" 610 " vmovdqa64 15*64(%[before_zmm]),%%zmm15\n" 611 " vmovdqa64 16*64(%[before_zmm]),%%zmm16\n" 612 " vmovdqa64 17*64(%[before_zmm]),%%zmm17\n" 613 " vmovdqa64 18*64(%[before_zmm]),%%zmm18\n" 614 " vmovdqa64 19*64(%[before_zmm]),%%zmm19\n" 615 " vmovdqa64 20*64(%[before_zmm]),%%zmm20\n" 616 " vmovdqa64 21*64(%[before_zmm]),%%zmm21\n" 617 " vmovdqa64 22*64(%[before_zmm]),%%zmm22\n" 618 " vmovdqa64 23*64(%[before_zmm]),%%zmm23\n" 619 " vmovdqa64 24*64(%[before_zmm]),%%zmm24\n" 620 " vmovdqa64 25*64(%[before_zmm]),%%zmm25\n" 621 " vmovdqa64 26*64(%[before_zmm]),%%zmm26\n" 622 " vmovdqa64 27*64(%[before_zmm]),%%zmm27\n" 623 " vmovdqa64 28*64(%[before_zmm]),%%zmm28\n" 624 " vmovdqa64 29*64(%[before_zmm]),%%zmm29\n" 625 " vmovdqa64 30*64(%[before_zmm]),%%zmm30\n" 626 " vmovdqa64 31*64(%[before_zmm]),%%zmm31\n" 627 #endif 628 " lock\n" 629 " orb $1,%[ready]\n" 630 "0: pause\n" 631 " cmpb $0,%[done]\n" 632 " lfence\n" 633 " je 0b\n" 634 " vmovdqa64 %%zmm0,0*64(%[after_zmm])\n" 635 " vmovdqa64 %%zmm1,1*64(%[after_zmm])\n" 636 " vmovdqa64 %%zmm2,2*64(%[after_zmm])\n" 637 " vmovdqa64 %%zmm3,3*64(%[after_zmm])\n" 638 " vmovdqa64 %%zmm4,4*64(%[after_zmm])\n" 639 " vmovdqa64 %%zmm5,5*64(%[after_zmm])\n" 640 " vmovdqa64 %%zmm6,6*64(%[after_zmm])\n" 641 " vmovdqa64 %%zmm7,7*64(%[after_zmm])\n" 642 #ifdef __x86_64__ 643 " vmovdqa64 %%zmm8,8*64(%[after_zmm])\n" 644 " vmovdqa64 %%zmm9,9*64(%[after_zmm])\n" 645 " vmovdqa64 %%zmm10,10*64(%[after_zmm])\n" 646 " vmovdqa64 %%zmm11,11*64(%[after_zmm])\n" 647 " vmovdqa64 %%zmm12,12*64(%[after_zmm])\n" 648 " vmovdqa64 %%zmm13,13*64(%[after_zmm])\n" 649 " vmovdqa64 %%zmm14,14*64(%[after_zmm])\n" 650 " vmovdqa64 %%zmm15,15*64(%[after_zmm])\n" 651 " vmovdqa64 %%zmm16,16*64(%[after_zmm])\n" 652 " vmovdqa64 %%zmm17,17*64(%[after_zmm])\n" 653 " vmovdqa64 %%zmm18,18*64(%[after_zmm])\n" 654 " vmovdqa64 %%zmm19,19*64(%[after_zmm])\n" 655 " vmovdqa64 %%zmm20,20*64(%[after_zmm])\n" 656 " vmovdqa64 %%zmm21,21*64(%[after_zmm])\n" 657 " vmovdqa64 %%zmm22,22*64(%[after_zmm])\n" 658 " vmovdqa64 %%zmm23,23*64(%[after_zmm])\n" 659 " vmovdqa64 %%zmm24,24*64(%[after_zmm])\n" 660 " vmovdqa64 %%zmm25,25*64(%[after_zmm])\n" 661 " vmovdqa64 %%zmm26,26*64(%[after_zmm])\n" 662 " vmovdqa64 %%zmm27,27*64(%[after_zmm])\n" 663 " vmovdqa64 %%zmm28,28*64(%[after_zmm])\n" 664 " vmovdqa64 %%zmm29,29*64(%[after_zmm])\n" 665 " vmovdqa64 %%zmm30,30*64(%[after_zmm])\n" 666 " vmovdqa64 %%zmm31,31*64(%[after_zmm])\n" 667 #endif 668 : /*out*/ [ready]"=m"(*ready), "=m"(after.zmm) 669 : /*in*/ [done]"m"(*done), "m"(before.zmm), 670 [before_zmm]"r"(before.zmm), [after_zmm]"r"(after.zmm) 671 : /*clobber*/ "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", 672 "zmm6", "zmm7" 673 #ifdef __x86_64__ 674 , "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", 675 "zmm13", "zmm14", "zmm15", "zmm16", "zmm17", 676 "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", 677 "zmm23", "zmm24", "zmm25", "zmm26", "zmm27", 678 "zmm28", "zmm29", "zmm30", "zmm31" 679 #endif 680 ); 681 682 for (i = 0; i < __arraycount(before.zmm); i++) { 683 if (memcmp(&before.zmm[i], &after.zmm[i], 684 sizeof(before.zmm[i]))) { 685 fprintf(stderr, "zmm%u clobbered\n", i); 686 hexdump(stderr, &before.zmm[i], sizeof(before.zmm[i]), 687 "before"); 688 hexdump(stderr, &after.zmm[i], sizeof(after.zmm[i]), 689 "after"); 690 error |= 1 << i; 691 } 692 } 693 694 return error; 695 } 696 697 __attribute__((target("avx512f"))) 698 void 699 trash_zmm(void) 700 { 701 struct zmmregs regs; 702 703 arc4random_buf(®s, sizeof(regs)); 704 705 asm("\n" 706 " vmovdqa64 0*64(%[zmm]),%%zmm0\n" 707 " vmovdqa64 1*64(%[zmm]),%%zmm1\n" 708 " vmovdqa64 2*64(%[zmm]),%%zmm2\n" 709 " vmovdqa64 3*64(%[zmm]),%%zmm3\n" 710 " vmovdqa64 4*64(%[zmm]),%%zmm4\n" 711 " vmovdqa64 5*64(%[zmm]),%%zmm5\n" 712 " vmovdqa64 6*64(%[zmm]),%%zmm6\n" 713 " vmovdqa64 7*64(%[zmm]),%%zmm7\n" 714 #ifdef __x86_64__ 715 " vmovdqa64 8*64(%[zmm]),%%zmm8\n" 716 " vmovdqa64 9*64(%[zmm]),%%zmm9\n" 717 " vmovdqa64 10*64(%[zmm]),%%zmm10\n" 718 " vmovdqa64 11*64(%[zmm]),%%zmm11\n" 719 " vmovdqa64 12*64(%[zmm]),%%zmm12\n" 720 " vmovdqa64 13*64(%[zmm]),%%zmm13\n" 721 " vmovdqa64 14*64(%[zmm]),%%zmm14\n" 722 " vmovdqa64 15*64(%[zmm]),%%zmm15\n" 723 " vmovdqa64 16*64(%[zmm]),%%zmm16\n" 724 " vmovdqa64 17*64(%[zmm]),%%zmm17\n" 725 " vmovdqa64 18*64(%[zmm]),%%zmm18\n" 726 " vmovdqa64 19*64(%[zmm]),%%zmm19\n" 727 " vmovdqa64 20*64(%[zmm]),%%zmm20\n" 728 " vmovdqa64 21*64(%[zmm]),%%zmm21\n" 729 " vmovdqa64 22*64(%[zmm]),%%zmm22\n" 730 " vmovdqa64 23*64(%[zmm]),%%zmm23\n" 731 " vmovdqa64 24*64(%[zmm]),%%zmm24\n" 732 " vmovdqa64 25*64(%[zmm]),%%zmm25\n" 733 " vmovdqa64 26*64(%[zmm]),%%zmm26\n" 734 " vmovdqa64 27*64(%[zmm]),%%zmm27\n" 735 " vmovdqa64 28*64(%[zmm]),%%zmm28\n" 736 " vmovdqa64 29*64(%[zmm]),%%zmm29\n" 737 " vmovdqa64 30*64(%[zmm]),%%zmm30\n" 738 " vmovdqa64 31*64(%[zmm]),%%zmm31\n" 739 #endif 740 : /*out*/ 741 : /*in*/ "m"(regs.zmm), [zmm]"r"(regs.zmm) 742 : /*clobber*/ "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", 743 "zmm6", "zmm7" 744 #ifdef __x86_64__ 745 , "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", 746 "zmm13", "zmm14", "zmm15", "zmm16", "zmm17", 747 "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", 748 "zmm23", "zmm24", "zmm25", "zmm26", "zmm27", 749 "zmm28", "zmm29", "zmm30", "zmm31" 750 #endif 751 ); 752 } 753