xref: /freebsd/tools/test/xregs_sig/xregs_sig.c (revision 0be13a45f7cb18f8b86c800a28457bf918e130f2)
18fdc9ce9SDmitry Chagin /* $Id: avx_sig.c,v 1.12 2021/12/11 22:47:09 kostik Exp $ */
28fdc9ce9SDmitry Chagin /*
38fdc9ce9SDmitry Chagin  * Naive test to check that context switches and signal delivery do
48fdc9ce9SDmitry Chagin  * not corrupt AVX registers file (%xmm).  Run until some
58fdc9ce9SDmitry Chagin  * inconsistency detected, then aborts.
68fdc9ce9SDmitry Chagin  *
78fdc9ce9SDmitry Chagin  * FreeBSD:
88fdc9ce9SDmitry Chagin  * ${CC} -Wall -Wextra -O -g -o avx_sig avx_sig.c -lpthread
98fdc9ce9SDmitry Chagin  * Linux
108fdc9ce9SDmitry Chagin  * ${CC} -D_GNU_SOURCE -Wall -Wextra -O -g -o avx_sig avx_sig.c -lbsd -lpthread
118fdc9ce9SDmitry Chagin  */
128fdc9ce9SDmitry Chagin 
138fdc9ce9SDmitry Chagin #include <sys/param.h>
148fdc9ce9SDmitry Chagin #include <sys/time.h>
158fdc9ce9SDmitry Chagin #include <sys/resource.h>
168fdc9ce9SDmitry Chagin #include <sys/syscall.h>
178fdc9ce9SDmitry Chagin #include <errno.h>
188fdc9ce9SDmitry Chagin #include <pthread.h>
198fdc9ce9SDmitry Chagin #ifdef __FreeBSD__
208fdc9ce9SDmitry Chagin #include <pthread_np.h>
218fdc9ce9SDmitry Chagin #endif
228fdc9ce9SDmitry Chagin #ifdef __linux__
238fdc9ce9SDmitry Chagin #ifdef __GLIBC__
248fdc9ce9SDmitry Chagin #include <gnu/libc-version.h>
258fdc9ce9SDmitry Chagin #endif
268fdc9ce9SDmitry Chagin #if !defined(__GLIBC__) || (__GLIBC__ * 100 + __GLIBC_MINOR__) < 236
278fdc9ce9SDmitry Chagin #include <bsd/stdlib.h>
288fdc9ce9SDmitry Chagin #endif
298fdc9ce9SDmitry Chagin #endif
308fdc9ce9SDmitry Chagin #include <signal.h>
318fdc9ce9SDmitry Chagin #include <stdatomic.h>
328fdc9ce9SDmitry Chagin #include <stdbool.h>
338fdc9ce9SDmitry Chagin #include <stdint.h>
348fdc9ce9SDmitry Chagin #include <stdio.h>
358fdc9ce9SDmitry Chagin #include <stdlib.h>
368fdc9ce9SDmitry Chagin #include <string.h>
378fdc9ce9SDmitry Chagin #include <unistd.h>
388fdc9ce9SDmitry Chagin 
398fdc9ce9SDmitry Chagin /* SIGALRM interval in seconds. */
408fdc9ce9SDmitry Chagin #ifndef TIMO
418fdc9ce9SDmitry Chagin #define	TIMO		5
428fdc9ce9SDmitry Chagin #endif
438fdc9ce9SDmitry Chagin 
448fdc9ce9SDmitry Chagin #ifndef __unused
458fdc9ce9SDmitry Chagin #define	__unused	__attribute__((__unused__))
468fdc9ce9SDmitry Chagin #endif
47*0be13a45SDmitry Chagin 
48*0be13a45SDmitry Chagin struct xregs_bank {
49*0be13a45SDmitry Chagin 	const char	*b_name;
50*0be13a45SDmitry Chagin 	const char	*r_name;
51*0be13a45SDmitry Chagin 	uint32_t	regs;
52*0be13a45SDmitry Chagin 	uint32_t	bytes;
53*0be13a45SDmitry Chagin 	void		(*x2c)(uint8_t *);
54*0be13a45SDmitry Chagin 	void		(*c2x)(uint8_t *);
55*0be13a45SDmitry Chagin };
568fdc9ce9SDmitry Chagin 
578fdc9ce9SDmitry Chagin #if defined(__amd64__)
58*0be13a45SDmitry Chagin void cpu_to_xmm(uint8_t *);
59*0be13a45SDmitry Chagin void xmm_to_cpu(uint8_t *);
60*0be13a45SDmitry Chagin 
61*0be13a45SDmitry Chagin static const struct xregs_bank xregs_banks[] = {
62*0be13a45SDmitry Chagin 	{
63*0be13a45SDmitry Chagin 		.b_name	= "SSE",
64*0be13a45SDmitry Chagin 		.r_name	= "xmm",
65*0be13a45SDmitry Chagin 		.regs	= 16,
66*0be13a45SDmitry Chagin 		.bytes	= 16,
67*0be13a45SDmitry Chagin 		.x2c	= xmm_to_cpu,
68*0be13a45SDmitry Chagin 		.c2x	= cpu_to_xmm,
69*0be13a45SDmitry Chagin 	},
70*0be13a45SDmitry Chagin };
718fdc9ce9SDmitry Chagin #elif defined(__aarch64__)
72*0be13a45SDmitry Chagin void cpu_to_vfp(uint8_t *);
73*0be13a45SDmitry Chagin void vfp_to_cpu(uint8_t *);
74*0be13a45SDmitry Chagin 
75*0be13a45SDmitry Chagin static const struct xregs_bank xregs_banks[] = {
76*0be13a45SDmitry Chagin 	{
77*0be13a45SDmitry Chagin 		.b_name	= "VFP",
78*0be13a45SDmitry Chagin 		.r_name	= "q",
79*0be13a45SDmitry Chagin 		.regs	= 32,
80*0be13a45SDmitry Chagin 		.bytes	= 16,
81*0be13a45SDmitry Chagin 		.x2c	= vfp_to_cpu,
82*0be13a45SDmitry Chagin 		.c2x	= cpu_to_vfp,
83*0be13a45SDmitry Chagin 	},
84*0be13a45SDmitry Chagin };
858fdc9ce9SDmitry Chagin #endif
868fdc9ce9SDmitry Chagin 
878fdc9ce9SDmitry Chagin static atomic_uint sigs;
88*0be13a45SDmitry Chagin static int max_bank_idx;
89*0be13a45SDmitry Chagin 
908fdc9ce9SDmitry Chagin 
918fdc9ce9SDmitry Chagin static void
928fdc9ce9SDmitry Chagin sigusr1_handler(int sig __unused, siginfo_t *si __unused, void *m __unused)
938fdc9ce9SDmitry Chagin {
948fdc9ce9SDmitry Chagin 	atomic_fetch_add_explicit(&sigs, 1, memory_order_relaxed);
958fdc9ce9SDmitry Chagin }
968fdc9ce9SDmitry Chagin 
978fdc9ce9SDmitry Chagin static void
988fdc9ce9SDmitry Chagin sigalrm_handler(int sig __unused)
998fdc9ce9SDmitry Chagin {
1008fdc9ce9SDmitry Chagin 	struct rusage r;
1018fdc9ce9SDmitry Chagin 
1028fdc9ce9SDmitry Chagin 	if (getrusage(RUSAGE_SELF, &r) == 0) {
1038fdc9ce9SDmitry Chagin 		printf("%lu vctx %lu nvctx %lu nsigs %u SIGUSR1\n",
1048fdc9ce9SDmitry Chagin 		    r.ru_nvcsw, r.ru_nivcsw, r.ru_nsignals, sigs);
1058fdc9ce9SDmitry Chagin 	}
1068fdc9ce9SDmitry Chagin 	alarm(TIMO);
1078fdc9ce9SDmitry Chagin }
1088fdc9ce9SDmitry Chagin 
1098fdc9ce9SDmitry Chagin 
1108fdc9ce9SDmitry Chagin static void
111*0be13a45SDmitry Chagin fill_xregs(uint8_t *xregs, int bank)
1128fdc9ce9SDmitry Chagin {
113*0be13a45SDmitry Chagin 	arc4random_buf(xregs, xregs_banks[bank].regs * xregs_banks[bank].bytes);
1148fdc9ce9SDmitry Chagin }
1158fdc9ce9SDmitry Chagin 
1168fdc9ce9SDmitry Chagin static void
117*0be13a45SDmitry Chagin dump_xregs(const uint8_t *r, int bank)
1188fdc9ce9SDmitry Chagin {
1198fdc9ce9SDmitry Chagin 	unsigned k;
1208fdc9ce9SDmitry Chagin 
121*0be13a45SDmitry Chagin 	for (k = 0; k < xregs_banks[bank].bytes; k++) {
1228fdc9ce9SDmitry Chagin 		if (k != 0)
1238fdc9ce9SDmitry Chagin 			printf(" ");
124*0be13a45SDmitry Chagin 		printf("%02x", r[k]);
1258fdc9ce9SDmitry Chagin 	}
1268fdc9ce9SDmitry Chagin 	printf("\n");
1278fdc9ce9SDmitry Chagin }
1288fdc9ce9SDmitry Chagin 
1298fdc9ce9SDmitry Chagin static pthread_mutex_t show_lock;
1308fdc9ce9SDmitry Chagin 
1318fdc9ce9SDmitry Chagin static void
132*0be13a45SDmitry Chagin show_diff(const uint8_t *xregs1, const uint8_t *xregs2, int bank)
1338fdc9ce9SDmitry Chagin {
134*0be13a45SDmitry Chagin 	const uint8_t *r1, *r2;
1358fdc9ce9SDmitry Chagin 	unsigned i, j;
1368fdc9ce9SDmitry Chagin 
1378fdc9ce9SDmitry Chagin #if defined(__FreeBSD__)
1388fdc9ce9SDmitry Chagin 	printf("thr %d\n", pthread_getthreadid_np());
1398fdc9ce9SDmitry Chagin #elif defined(__linux__)
1408fdc9ce9SDmitry Chagin 	printf("thr %ld\n", syscall(SYS_gettid));
1418fdc9ce9SDmitry Chagin #endif
142*0be13a45SDmitry Chagin 	for (i = 0; i < xregs_banks[bank].regs; i++) {
143*0be13a45SDmitry Chagin 		r1 = xregs1 + i * xregs_banks[bank].bytes;
144*0be13a45SDmitry Chagin 		r2 = xregs2 + i * xregs_banks[bank].bytes;
145*0be13a45SDmitry Chagin 		for (j = 0; j < xregs_banks[bank].bytes; j++) {
146*0be13a45SDmitry Chagin 			if (r1[j] != r2[j]) {
147*0be13a45SDmitry Chagin 				printf("%%%s%u\n", xregs_banks[bank].r_name, i);
148*0be13a45SDmitry Chagin 				dump_xregs(r1, bank);
149*0be13a45SDmitry Chagin 				dump_xregs(r2, bank);
1508fdc9ce9SDmitry Chagin 				break;
1518fdc9ce9SDmitry Chagin 			}
1528fdc9ce9SDmitry Chagin 		}
1538fdc9ce9SDmitry Chagin 	}
1548fdc9ce9SDmitry Chagin }
1558fdc9ce9SDmitry Chagin 
1568fdc9ce9SDmitry Chagin static void
1578fdc9ce9SDmitry Chagin my_pause(void)
1588fdc9ce9SDmitry Chagin {
1598fdc9ce9SDmitry Chagin 	usleep(0);
1608fdc9ce9SDmitry Chagin }
1618fdc9ce9SDmitry Chagin 
1628fdc9ce9SDmitry Chagin static void *
163*0be13a45SDmitry Chagin worker_thread(void *arg)
1648fdc9ce9SDmitry Chagin {
165*0be13a45SDmitry Chagin 	int bank = (uintptr_t)arg;
166*0be13a45SDmitry Chagin 	int sz = xregs_banks[bank].regs * xregs_banks[bank].bytes;
167*0be13a45SDmitry Chagin 	uint8_t xregs[sz], xregs_cpu[sz], zero_xregs[sz];
1688fdc9ce9SDmitry Chagin 
169*0be13a45SDmitry Chagin 	memset(zero_xregs, 0, sz);
170*0be13a45SDmitry Chagin 
171*0be13a45SDmitry Chagin 	fill_xregs(xregs, bank);
1728fdc9ce9SDmitry Chagin 	for (;;) {
173*0be13a45SDmitry Chagin 		xregs_banks[bank].x2c(xregs);
1748fdc9ce9SDmitry Chagin 		my_pause();
175*0be13a45SDmitry Chagin 		xregs_banks[bank].c2x(xregs_cpu);
176*0be13a45SDmitry Chagin 		if (memcmp(xregs, xregs_cpu, sz) != 0) {
1778fdc9ce9SDmitry Chagin 			pthread_mutex_lock(&show_lock);
178*0be13a45SDmitry Chagin 			show_diff(xregs, xregs_cpu, bank);
1798fdc9ce9SDmitry Chagin 			abort();
1808fdc9ce9SDmitry Chagin 			pthread_mutex_unlock(&show_lock);
1818fdc9ce9SDmitry Chagin 		}
1828fdc9ce9SDmitry Chagin 
183*0be13a45SDmitry Chagin 		xregs_banks[bank].x2c(zero_xregs);
1848fdc9ce9SDmitry Chagin 		my_pause();
185*0be13a45SDmitry Chagin 		xregs_banks[bank].c2x(xregs_cpu);
186*0be13a45SDmitry Chagin 		if (memcmp(zero_xregs, xregs_cpu, sz) != 0) {
1878fdc9ce9SDmitry Chagin 			pthread_mutex_lock(&show_lock);
188*0be13a45SDmitry Chagin 			show_diff(zero_xregs, xregs_cpu, bank);
1898fdc9ce9SDmitry Chagin 			abort();
1908fdc9ce9SDmitry Chagin 			pthread_mutex_unlock(&show_lock);
1918fdc9ce9SDmitry Chagin 		}
1928fdc9ce9SDmitry Chagin 	}
1938fdc9ce9SDmitry Chagin 	return (NULL);
1948fdc9ce9SDmitry Chagin }
1958fdc9ce9SDmitry Chagin 
1968fdc9ce9SDmitry Chagin int
1978fdc9ce9SDmitry Chagin main(void)
1988fdc9ce9SDmitry Chagin {
1998fdc9ce9SDmitry Chagin 	struct sigaction sa;
200*0be13a45SDmitry Chagin 	int error, i, ncpu, bank;
201*0be13a45SDmitry Chagin 
202*0be13a45SDmitry Chagin 	max_bank_idx = 0;
2038fdc9ce9SDmitry Chagin 
2048fdc9ce9SDmitry Chagin 	bzero(&sa, sizeof(sa));
2058fdc9ce9SDmitry Chagin 	sa.sa_handler = sigalrm_handler;
2068fdc9ce9SDmitry Chagin 	if (sigaction(SIGALRM, &sa, NULL) == -1) {
2078fdc9ce9SDmitry Chagin 		fprintf(stderr, "sigaction SIGALRM %s\n", strerror(errno));
2088fdc9ce9SDmitry Chagin 		exit(1);
2098fdc9ce9SDmitry Chagin 	}
2108fdc9ce9SDmitry Chagin 
2118fdc9ce9SDmitry Chagin 	bzero(&sa, sizeof(sa));
2128fdc9ce9SDmitry Chagin 	sa.sa_sigaction = sigusr1_handler;
2138fdc9ce9SDmitry Chagin 	sa.sa_flags = SA_SIGINFO;
2148fdc9ce9SDmitry Chagin 	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
2158fdc9ce9SDmitry Chagin 		fprintf(stderr, "sigaction SIGUSR1 %s\n", strerror(errno));
2168fdc9ce9SDmitry Chagin 		exit(1);
2178fdc9ce9SDmitry Chagin 	}
2188fdc9ce9SDmitry Chagin 
2198fdc9ce9SDmitry Chagin 	error = pthread_mutex_init(&show_lock, NULL);
2208fdc9ce9SDmitry Chagin 	if (error != 0) {
2218fdc9ce9SDmitry Chagin 		fprintf(stderr, "pthread_mutex_init %s\n", strerror(error));
2228fdc9ce9SDmitry Chagin 		exit(1);
2238fdc9ce9SDmitry Chagin 	}
2248fdc9ce9SDmitry Chagin 
2258fdc9ce9SDmitry Chagin 	ncpu = sysconf(_SC_NPROCESSORS_ONLN);
226*0be13a45SDmitry Chagin 	if (max_bank_idx == 0)
2278fdc9ce9SDmitry Chagin 		ncpu *= 2;
228*0be13a45SDmitry Chagin 	bank = 0;
2298fdc9ce9SDmitry Chagin 	pthread_t wt[ncpu];
230*0be13a45SDmitry Chagin nextbank:
231*0be13a45SDmitry Chagin 	printf("Starting %d threads for registers bank %s sized [%d][%d]\n", ncpu,
232*0be13a45SDmitry Chagin 	    xregs_banks[bank].b_name, xregs_banks[bank].regs, xregs_banks[bank].bytes);
2338fdc9ce9SDmitry Chagin 	for (i = 0; i < ncpu; i++) {
234*0be13a45SDmitry Chagin 		error = pthread_create(&wt[i], NULL, worker_thread,
235*0be13a45SDmitry Chagin 		    (void *)(uintptr_t)bank);
2368fdc9ce9SDmitry Chagin 		if (error != 0) {
2378fdc9ce9SDmitry Chagin 			fprintf(stderr, "pthread_create %s\n", strerror(error));
2388fdc9ce9SDmitry Chagin 		}
2398fdc9ce9SDmitry Chagin 	}
240*0be13a45SDmitry Chagin 	if (++bank <= max_bank_idx)
241*0be13a45SDmitry Chagin 		goto nextbank;
2428fdc9ce9SDmitry Chagin 
2438fdc9ce9SDmitry Chagin 	alarm(TIMO);
2448fdc9ce9SDmitry Chagin 	for (;;) {
2458fdc9ce9SDmitry Chagin 		for (i = 0; i < ncpu; i++) {
2468fdc9ce9SDmitry Chagin 			my_pause();
2478fdc9ce9SDmitry Chagin 			pthread_kill(wt[i], SIGUSR1);
2488fdc9ce9SDmitry Chagin 		}
2498fdc9ce9SDmitry Chagin 	}
2508fdc9ce9SDmitry Chagin }
251