Home | History | Annotate | Line # | Download | only in kernel
      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