1 /* $NetBSD: t_signal_and_fpu.c,v 1.3 2026/07/10 20:36:11 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: t_signal_and_fpu.c,v 1.3 2026/07/10 20:36:11 riastradh Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 35 #include <sys/atomic.h> 36 37 #include <atf-c.h> 38 #include <float.h> 39 #include <pthread.h> 40 #include <signal.h> 41 42 #include "h_macros.h" 43 44 #ifdef HAVE_SIG_FPU_H 45 #include "sig_fpu.h" 46 #endif 47 48 static volatile bool ready_for_signal; 49 static volatile bool signal_delivered; 50 51 static pthread_t meddler_thread; 52 static pthread_t tester_thread; 53 54 static void (*current_trashfn)(void); 55 static int (*current_testfn)(volatile bool *, const volatile bool *); 56 57 static void 58 sigusr1_handler(int signo) 59 { 60 61 (*current_trashfn)(); 62 signal_delivered = true; 63 } 64 65 static void * 66 start_meddling(void *cookie) 67 { 68 69 while (!ready_for_signal) 70 membar_consumer(); 71 RZ(pthread_kill(tester_thread, SIGUSR1)); 72 return NULL; 73 } 74 75 static void * 76 start_testing(void *cookie) 77 { 78 struct sigaction sa; 79 int error; 80 81 /* 82 * Arrange to have SIGUSR1 trash the FPU state we're testing 83 * and then notify the tester that it's done. 84 */ 85 memset(&sa, 0, sizeof(sa)); 86 sa.sa_handler = &sigusr1_handler; 87 RL(sigfillset(&sa.sa_mask)); 88 sa.sa_flags = 0; 89 RL(sigaction(SIGUSR1, &sa, NULL)); 90 91 /* 92 * Run the test. It will set ready_for_signal = true when it's 93 * ready for the meddling thread to send a signal, and the 94 * signal handler will set signal_delivered = true so the 95 * tester will know when to stop. 96 */ 97 error = (*current_testfn)(&ready_for_signal, &signal_delivered); 98 99 return (void *)(intptr_t)error; 100 } 101 102 static void 103 test_signal_fpu(bool (*supportfn)(void), 104 int (*testfn)(volatile bool *, const volatile bool *), 105 void (*trashfn)(void), 106 const char *xfail) 107 { 108 unsigned i; 109 void *test_result; 110 111 if (supportfn && !(*supportfn)()) 112 atf_tc_skip("not supported on this machine"); 113 if (xfail) 114 atf_tc_expect_fail("%s", xfail); 115 116 /* 117 * Prepare global state. 118 */ 119 current_testfn = testfn; 120 current_trashfn = trashfn; 121 122 /* 123 * Do ten trials of each test, since they're often randomized, 124 * and each one should be quick. 125 */ 126 for (i = 0; i < 10; i++) { 127 /* 128 * Reset the state. 129 */ 130 ready_for_signal = false; 131 signal_delivered = false; 132 133 /* 134 * Create tester and meddler threads. As soon as the tester 135 * thread sets ready_for_signal, the meddler thread will send 136 * it a signal. 137 */ 138 RZ(pthread_create(&tester_thread, NULL, &start_testing, NULL)); 139 RZ(pthread_create(&meddler_thread, NULL, &start_meddling, 140 NULL)); 141 142 /* 143 * Verify both threads complete within 1sec, and verify the 144 * tester returned zero error. The error number can be used 145 * for machine-dependent diagnostics. 146 */ 147 REQUIRE_LIBC(alarm(1), (unsigned)-1); 148 RZ(pthread_join(meddler_thread, NULL)); 149 RZ(pthread_join(tester_thread, &test_result)); 150 ATF_REQUIRE_MSG((int)(intptr_t)test_result == 0, 151 "test_result=0x%x", (int)(intptr_t)test_result); 152 } 153 } 154 155 static int 156 test_double(volatile bool *ready, const volatile bool *done) 157 { 158 long long i; 159 volatile double one = 1; 160 double f0, f; 161 int error = 0; 162 163 i = 1; 164 f0 = one; 165 *ready = true; 166 f = f0; 167 while (!*done) { 168 for (i = 1, f = f0; 169 !*done && i < MIN(1LL << DBL_MANT_DIG, INT_MAX); 170 i++, f++) 171 continue; 172 } 173 if (f != (double)i) 174 error = i; 175 return error; 176 } 177 178 static void 179 trash_double(void) 180 { 181 volatile double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, 182 f13, f14, f15, f16; 183 184 f0 = (double)arc4random(); 185 f1 = f0 + (double)arc4random(); 186 f2 = f0 + f1 + (double)arc4random(); 187 f3 = f0 + f1 + f2 + (double)arc4random(); 188 f4 = f0 + f1 + f2 + f3 + (double)arc4random(); 189 f5 = f0 + f1 + f2 + f3 + f4 + (double)arc4random(); 190 f6 = f0 + f1 + f2 + f3 + f4 + f5 + (double)arc4random(); 191 f7 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + (double)arc4random(); 192 f8 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + (double)arc4random(); 193 f9 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + (double)arc4random(); 194 f10 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + 195 (double)arc4random(); 196 f11 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + 197 (double)arc4random(); 198 f12 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 199 (double)arc4random(); 200 f13 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 201 f12 + (double)arc4random(); 202 f14 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 203 f12 + f13 + (double)arc4random(); 204 f15 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 205 f12 + f13 + f14 + (double)arc4random(); 206 f16 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 207 f12 + f13 + f14 + f15 + (double)arc4random(); 208 (void)f16; 209 } 210 211 ATF_TC(double); 212 ATF_TC_HEAD(double, tc) 213 { 214 atf_tc_set_md_var(tc, "descr", "double"); 215 } 216 ATF_TC_BODY(double, tc) 217 { 218 test_signal_fpu(NULL, &test_double, &trash_double, NULL); 219 } 220 221 static int 222 test_float(volatile bool *ready, const volatile bool *done) 223 { 224 int i; 225 volatile float one = 1; 226 float f0, f; 227 int error = 0; 228 229 i = 1; 230 f0 = one; 231 *ready = true; 232 f = f0; 233 while (!*done) { 234 for (i = 1, f = f0; 235 !*done && i < MIN(1 << FLT_MANT_DIG, INT_MAX); 236 i++, f++) 237 continue; 238 } 239 if (f != (float)i) 240 error = i; 241 return error; 242 } 243 244 static void 245 trash_float(void) 246 { 247 volatile float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, 248 f13, f14, f15, f16; 249 250 f0 = (float)arc4random(); 251 f1 = f0 + (float)arc4random(); 252 f2 = f0 + f1 + (float)arc4random(); 253 f3 = f0 + f1 + f2 + (float)arc4random(); 254 f4 = f0 + f1 + f2 + f3 + (float)arc4random(); 255 f5 = f0 + f1 + f2 + f3 + f4 + (float)arc4random(); 256 f6 = f0 + f1 + f2 + f3 + f4 + f5 + (float)arc4random(); 257 f7 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + (float)arc4random(); 258 f8 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + (float)arc4random(); 259 f9 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + (float)arc4random(); 260 f10 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + 261 (float)arc4random(); 262 f11 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + 263 (float)arc4random(); 264 f12 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 265 (float)arc4random(); 266 f13 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 267 f12 + (float)arc4random(); 268 f14 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 269 f12 + f13 + (float)arc4random(); 270 f15 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 271 f12 + f13 + f14 + (float)arc4random(); 272 f16 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 273 f12 + f13 + f14 + f15 + (float)arc4random(); 274 (void)f16; 275 } 276 277 ATF_TC(float); 278 ATF_TC_HEAD(float, tc) 279 { 280 atf_tc_set_md_var(tc, "descr", "float"); 281 } 282 ATF_TC_BODY(float, tc) 283 { 284 test_signal_fpu(NULL, &test_float, &trash_float, NULL); 285 } 286 287 static int 288 test_ldouble(volatile bool *ready, const volatile bool *done) 289 { 290 long long i; 291 volatile long double one = 1; 292 long double f0, f; 293 int error = 0; 294 295 i = 1; 296 f0 = one; 297 *ready = true; 298 f = f0; 299 while (!*done) { 300 /* 301 * LDBL_MANT_DIG is too big, but we won't reach past 302 * 2^DBL_MANT_DIG anyway, so just use DBL_MANT_DIG. 303 */ 304 for (i = 1, f = f0; 305 !*done && i < MIN(1LL << DBL_MANT_DIG, LONG_MAX); 306 i++, f++) 307 continue; 308 } 309 if (f != (long double)i) 310 error = i; 311 return error; 312 } 313 314 static void 315 trash_ldouble(void) 316 { 317 volatile long double f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, 318 f12, f13, f14, f15, f16; 319 320 f0 = (long double)arc4random(); 321 f1 = f0 + (long double)arc4random(); 322 f2 = f0 + f1 + (long double)arc4random(); 323 f3 = f0 + f1 + f2 + (long double)arc4random(); 324 f4 = f0 + f1 + f2 + f3 + (long double)arc4random(); 325 f5 = f0 + f1 + f2 + f3 + f4 + (long double)arc4random(); 326 f6 = f0 + f1 + f2 + f3 + f4 + f5 + (long double)arc4random(); 327 f7 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + (long double)arc4random(); 328 f8 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + (long double)arc4random(); 329 f9 = f0 + f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + 330 (long double)arc4random(); 331 f10 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + 332 (long double)arc4random(); 333 f11 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + 334 (long double)arc4random(); 335 f12 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 336 (long double)arc4random(); 337 f13 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 338 f12 + (long double)arc4random(); 339 f14 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 340 f12 + f13 + (long double)arc4random(); 341 f15 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 342 f12 + f13 + f14 + (long double)arc4random(); 343 f16 = f0 + f1 + f2 + f3 + f4 + f4 + f6 + f7 + f8 + f9 + f10 + f11 + 344 f12 + f13 + f14 + f15 + (long double)arc4random(); 345 (void)f16; 346 } 347 348 ATF_TC(ldouble); 349 ATF_TC_HEAD(ldouble, tc) 350 { 351 atf_tc_set_md_var(tc, "descr", "long double"); 352 } 353 ATF_TC_BODY(ldouble, tc) 354 { 355 test_signal_fpu(NULL, &test_ldouble, &trash_ldouble, NULL); 356 } 357 358 #if defined __i386__ || defined __x86_64__ 359 360 ATF_TC(x87); 361 ATF_TC_HEAD(x87, tc) 362 { 363 atf_tc_set_md_var(tc, "descr", "x87"); 364 } 365 ATF_TC_BODY(x87, tc) 366 { 367 test_signal_fpu(&x87_supported, &test_x87, &trash_x87, NULL); 368 } 369 370 ATF_TC(xmm); 371 ATF_TC_HEAD(xmm, tc) 372 { 373 atf_tc_set_md_var(tc, "descr", "xmm"); 374 } 375 ATF_TC_BODY(xmm, tc) 376 { 377 test_signal_fpu(&xmm_supported, &test_xmm, &trash_xmm, NULL); 378 } 379 380 ATF_TC(ymm); 381 ATF_TC_HEAD(ymm, tc) 382 { 383 atf_tc_set_md_var(tc, "descr", "ymm"); 384 } 385 ATF_TC_BODY(ymm, tc) 386 { 387 test_signal_fpu(&ymm_supported, &test_ymm, &trash_ymm, NULL); 388 } 389 390 #endif 391 392 ATF_TP_ADD_TCS(tp) 393 { 394 395 ATF_TP_ADD_TC(tp, double); 396 ATF_TP_ADD_TC(tp, float); 397 ATF_TP_ADD_TC(tp, ldouble); 398 #if defined __i386__ || defined __x86_64__ 399 ATF_TP_ADD_TC(tp, x87); 400 ATF_TP_ADD_TC(tp, xmm); 401 ATF_TP_ADD_TC(tp, ymm); 402 #endif 403 return atf_no_error(); 404 } 405