1 // SPDX-License-Identifier: GPL-2.0
2
3 #define _GNU_SOURCE
4 #include <err.h>
5 #include <errno.h>
6 #include <pthread.h>
7 #include <setjmp.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdbool.h>
11 #include <unistd.h>
12 #include <x86intrin.h>
13
14 #include <sys/auxv.h>
15 #include <sys/mman.h>
16 #include <sys/shm.h>
17 #include <sys/syscall.h>
18 #include <sys/wait.h>
19
20 #include "../kselftest.h" /* For __cpuid_count() */
21
22 #ifndef __x86_64__
23 # error This test is 64-bit only
24 #endif
25
26 #define XSAVE_HDR_OFFSET 512
27 #define XSAVE_HDR_SIZE 64
28
29 struct xsave_buffer {
30 union {
31 struct {
32 char legacy[XSAVE_HDR_OFFSET];
33 char header[XSAVE_HDR_SIZE];
34 char extended[0];
35 };
36 char bytes[0];
37 };
38 };
39
xgetbv(uint32_t index)40 static inline uint64_t xgetbv(uint32_t index)
41 {
42 uint32_t eax, edx;
43
44 asm volatile("xgetbv;"
45 : "=a" (eax), "=d" (edx)
46 : "c" (index));
47 return eax + ((uint64_t)edx << 32);
48 }
49
xsave(struct xsave_buffer * xbuf,uint64_t rfbm)50 static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
51 {
52 uint32_t rfbm_lo = rfbm;
53 uint32_t rfbm_hi = rfbm >> 32;
54
55 asm volatile("xsave (%%rdi)"
56 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
57 : "memory");
58 }
59
xrstor(struct xsave_buffer * xbuf,uint64_t rfbm)60 static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
61 {
62 uint32_t rfbm_lo = rfbm;
63 uint32_t rfbm_hi = rfbm >> 32;
64
65 asm volatile("xrstor (%%rdi)"
66 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
67 }
68
69 /* err() exits and will not return */
70 #define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
71
sethandler(int sig,void (* handler)(int,siginfo_t *,void *),int flags)72 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
73 int flags)
74 {
75 struct sigaction sa;
76
77 memset(&sa, 0, sizeof(sa));
78 sa.sa_sigaction = handler;
79 sa.sa_flags = SA_SIGINFO | flags;
80 sigemptyset(&sa.sa_mask);
81 if (sigaction(sig, &sa, 0))
82 fatal_error("sigaction");
83 }
84
clearhandler(int sig)85 static void clearhandler(int sig)
86 {
87 struct sigaction sa;
88
89 memset(&sa, 0, sizeof(sa));
90 sa.sa_handler = SIG_DFL;
91 sigemptyset(&sa.sa_mask);
92 if (sigaction(sig, &sa, 0))
93 fatal_error("sigaction");
94 }
95
96 #define XFEATURE_XTILECFG 17
97 #define XFEATURE_XTILEDATA 18
98 #define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
99 #define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
100 #define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
101
102 #define CPUID_LEAF1_ECX_XSAVE_MASK (1 << 26)
103 #define CPUID_LEAF1_ECX_OSXSAVE_MASK (1 << 27)
check_cpuid_xsave(void)104 static inline void check_cpuid_xsave(void)
105 {
106 uint32_t eax, ebx, ecx, edx;
107
108 /*
109 * CPUID.1:ECX.XSAVE[bit 26] enumerates general
110 * support for the XSAVE feature set, including
111 * XGETBV.
112 */
113 __cpuid_count(1, 0, eax, ebx, ecx, edx);
114 if (!(ecx & CPUID_LEAF1_ECX_XSAVE_MASK))
115 fatal_error("cpuid: no CPU xsave support");
116 if (!(ecx & CPUID_LEAF1_ECX_OSXSAVE_MASK))
117 fatal_error("cpuid: no OS xsave support");
118 }
119
120 static uint32_t xbuf_size;
121
122 static struct {
123 uint32_t xbuf_offset;
124 uint32_t size;
125 } xtiledata;
126
127 #define CPUID_LEAF_XSTATE 0xd
128 #define CPUID_SUBLEAF_XSTATE_USER 0x0
129 #define TILE_CPUID 0x1d
130 #define TILE_PALETTE_ID 0x1
131
check_cpuid_xtiledata(void)132 static void check_cpuid_xtiledata(void)
133 {
134 uint32_t eax, ebx, ecx, edx;
135
136 __cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
137 eax, ebx, ecx, edx);
138
139 /*
140 * EBX enumerates the size (in bytes) required by the XSAVE
141 * instruction for an XSAVE area containing all the user state
142 * components corresponding to bits currently set in XCR0.
143 *
144 * Stash that off so it can be used to allocate buffers later.
145 */
146 xbuf_size = ebx;
147
148 __cpuid_count(CPUID_LEAF_XSTATE, XFEATURE_XTILEDATA,
149 eax, ebx, ecx, edx);
150 /*
151 * eax: XTILEDATA state component size
152 * ebx: XTILEDATA state component offset in user buffer
153 */
154 if (!eax || !ebx)
155 fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
156 eax, ebx);
157
158 xtiledata.size = eax;
159 xtiledata.xbuf_offset = ebx;
160 }
161
162 /* The helpers for managing XSAVE buffer and tile states: */
163
alloc_xbuf(void)164 struct xsave_buffer *alloc_xbuf(void)
165 {
166 struct xsave_buffer *xbuf;
167
168 /* XSAVE buffer should be 64B-aligned. */
169 xbuf = aligned_alloc(64, xbuf_size);
170 if (!xbuf)
171 fatal_error("aligned_alloc()");
172 return xbuf;
173 }
174
clear_xstate_header(struct xsave_buffer * buffer)175 static inline void clear_xstate_header(struct xsave_buffer *buffer)
176 {
177 memset(&buffer->header, 0, sizeof(buffer->header));
178 }
179
get_xstatebv(struct xsave_buffer * buffer)180 static inline uint64_t get_xstatebv(struct xsave_buffer *buffer)
181 {
182 /* XSTATE_BV is at the beginning of the header: */
183 return *(uint64_t *)&buffer->header;
184 }
185
set_xstatebv(struct xsave_buffer * buffer,uint64_t bv)186 static inline void set_xstatebv(struct xsave_buffer *buffer, uint64_t bv)
187 {
188 /* XSTATE_BV is at the beginning of the header: */
189 *(uint64_t *)(&buffer->header) = bv;
190 }
191
set_rand_tiledata(struct xsave_buffer * xbuf)192 static void set_rand_tiledata(struct xsave_buffer *xbuf)
193 {
194 int *ptr = (int *)&xbuf->bytes[xtiledata.xbuf_offset];
195 int data;
196 int i;
197
198 /*
199 * Ensure that 'data' is never 0. This ensures that
200 * the registers are never in their initial configuration
201 * and thus never tracked as being in the init state.
202 */
203 data = rand() | 1;
204
205 for (i = 0; i < xtiledata.size / sizeof(int); i++, ptr++)
206 *ptr = data;
207 }
208
209 struct xsave_buffer *stashed_xsave;
210
init_stashed_xsave(void)211 static void init_stashed_xsave(void)
212 {
213 stashed_xsave = alloc_xbuf();
214 if (!stashed_xsave)
215 fatal_error("failed to allocate stashed_xsave\n");
216 clear_xstate_header(stashed_xsave);
217 }
218
free_stashed_xsave(void)219 static void free_stashed_xsave(void)
220 {
221 free(stashed_xsave);
222 }
223
224 /* See 'struct _fpx_sw_bytes' at sigcontext.h */
225 #define SW_BYTES_OFFSET 464
226 /* N.B. The struct's field name varies so read from the offset. */
227 #define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8)
228
get_fpx_sw_bytes(void * buffer)229 static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *buffer)
230 {
231 return (struct _fpx_sw_bytes *)(buffer + SW_BYTES_OFFSET);
232 }
233
get_fpx_sw_bytes_features(void * buffer)234 static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
235 {
236 return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
237 }
238
239 /* Work around printf() being unsafe in signals: */
240 #define SIGNAL_BUF_LEN 1000
241 char signal_message_buffer[SIGNAL_BUF_LEN];
sig_print(char * msg)242 void sig_print(char *msg)
243 {
244 int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
245
246 strncat(signal_message_buffer, msg, left);
247 }
248
249 static volatile bool noperm_signaled;
250 static int noperm_errs;
251 /*
252 * Signal handler for when AMX is used but
253 * permission has not been obtained.
254 */
handle_noperm(int sig,siginfo_t * si,void * ctx_void)255 static void handle_noperm(int sig, siginfo_t *si, void *ctx_void)
256 {
257 ucontext_t *ctx = (ucontext_t *)ctx_void;
258 void *xbuf = ctx->uc_mcontext.fpregs;
259 struct _fpx_sw_bytes *sw_bytes;
260 uint64_t features;
261
262 /* Reset the signal message buffer: */
263 signal_message_buffer[0] = '\0';
264 sig_print("\tAt SIGILL handler,\n");
265
266 if (si->si_code != ILL_ILLOPC) {
267 noperm_errs++;
268 sig_print("[FAIL]\tInvalid signal code.\n");
269 } else {
270 sig_print("[OK]\tValid signal code (ILL_ILLOPC).\n");
271 }
272
273 sw_bytes = get_fpx_sw_bytes(xbuf);
274 /*
275 * Without permission, the signal XSAVE buffer should not
276 * have room for AMX register state (aka. xtiledata).
277 * Check that the size does not overlap with where xtiledata
278 * will reside.
279 *
280 * This also implies that no state components *PAST*
281 * XTILEDATA (features >=19) can be present in the buffer.
282 */
283 if (sw_bytes->xstate_size <= xtiledata.xbuf_offset) {
284 sig_print("[OK]\tValid xstate size\n");
285 } else {
286 noperm_errs++;
287 sig_print("[FAIL]\tInvalid xstate size\n");
288 }
289
290 features = get_fpx_sw_bytes_features(xbuf);
291 /*
292 * Without permission, the XTILEDATA feature
293 * bit should not be set.
294 */
295 if ((features & XFEATURE_MASK_XTILEDATA) == 0) {
296 sig_print("[OK]\tValid xstate mask\n");
297 } else {
298 noperm_errs++;
299 sig_print("[FAIL]\tInvalid xstate mask\n");
300 }
301
302 noperm_signaled = true;
303 ctx->uc_mcontext.gregs[REG_RIP] += 3; /* Skip the faulting XRSTOR */
304 }
305
306 /* Return true if XRSTOR is successful; otherwise, false. */
xrstor_safe(struct xsave_buffer * xbuf,uint64_t mask)307 static inline bool xrstor_safe(struct xsave_buffer *xbuf, uint64_t mask)
308 {
309 noperm_signaled = false;
310 xrstor(xbuf, mask);
311
312 /* Print any messages produced by the signal code: */
313 printf("%s", signal_message_buffer);
314 /*
315 * Reset the buffer to make sure any future printing
316 * only outputs new messages:
317 */
318 signal_message_buffer[0] = '\0';
319
320 if (noperm_errs)
321 fatal_error("saw %d errors in noperm signal handler\n", noperm_errs);
322
323 return !noperm_signaled;
324 }
325
326 /*
327 * Use XRSTOR to populate the XTILEDATA registers with
328 * random data.
329 *
330 * Return true if successful; otherwise, false.
331 */
load_rand_tiledata(struct xsave_buffer * xbuf)332 static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
333 {
334 clear_xstate_header(xbuf);
335 set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
336 set_rand_tiledata(xbuf);
337 return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
338 }
339
340 /* Return XTILEDATA to its initial configuration. */
init_xtiledata(void)341 static inline void init_xtiledata(void)
342 {
343 clear_xstate_header(stashed_xsave);
344 xrstor_safe(stashed_xsave, XFEATURE_MASK_XTILEDATA);
345 }
346
347 enum expected_result { FAIL_EXPECTED, SUCCESS_EXPECTED };
348
349 /* arch_prctl() and sigaltstack() test */
350
351 #define ARCH_GET_XCOMP_PERM 0x1022
352 #define ARCH_REQ_XCOMP_PERM 0x1023
353
req_xtiledata_perm(void)354 static void req_xtiledata_perm(void)
355 {
356 syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
357 }
358
validate_req_xcomp_perm(enum expected_result exp)359 static void validate_req_xcomp_perm(enum expected_result exp)
360 {
361 unsigned long bitmask, expected_bitmask;
362 long rc;
363
364 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
365 if (rc) {
366 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
367 } else if (!(bitmask & XFEATURE_MASK_XTILECFG)) {
368 fatal_error("ARCH_GET_XCOMP_PERM returns XFEATURE_XTILECFG off.");
369 }
370
371 rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
372 if (exp == FAIL_EXPECTED) {
373 if (rc) {
374 printf("[OK]\tARCH_REQ_XCOMP_PERM saw expected failure..\n");
375 return;
376 }
377
378 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected success.\n");
379 } else if (rc) {
380 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected failure.\n");
381 }
382
383 expected_bitmask = bitmask | XFEATURE_MASK_XTILEDATA;
384
385 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
386 if (rc) {
387 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
388 } else if (bitmask != expected_bitmask) {
389 fatal_error("ARCH_REQ_XCOMP_PERM set a wrong bitmask: %lx, expected: %lx.\n",
390 bitmask, expected_bitmask);
391 } else {
392 printf("\tARCH_REQ_XCOMP_PERM is successful.\n");
393 }
394 }
395
validate_xcomp_perm(enum expected_result exp)396 static void validate_xcomp_perm(enum expected_result exp)
397 {
398 bool load_success = load_rand_tiledata(stashed_xsave);
399
400 if (exp == FAIL_EXPECTED) {
401 if (load_success) {
402 noperm_errs++;
403 printf("[FAIL]\tLoad tiledata succeeded.\n");
404 } else {
405 printf("[OK]\tLoad tiledata failed.\n");
406 }
407 } else if (exp == SUCCESS_EXPECTED) {
408 if (load_success) {
409 printf("[OK]\tLoad tiledata succeeded.\n");
410 } else {
411 noperm_errs++;
412 printf("[FAIL]\tLoad tiledata failed.\n");
413 }
414 }
415 }
416
417 #ifndef AT_MINSIGSTKSZ
418 # define AT_MINSIGSTKSZ 51
419 #endif
420
alloc_altstack(unsigned int size)421 static void *alloc_altstack(unsigned int size)
422 {
423 void *altstack;
424
425 altstack = mmap(NULL, size, PROT_READ | PROT_WRITE,
426 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
427
428 if (altstack == MAP_FAILED)
429 fatal_error("mmap() for altstack");
430
431 return altstack;
432 }
433
setup_altstack(void * addr,unsigned long size,enum expected_result exp)434 static void setup_altstack(void *addr, unsigned long size, enum expected_result exp)
435 {
436 stack_t ss;
437 int rc;
438
439 memset(&ss, 0, sizeof(ss));
440 ss.ss_size = size;
441 ss.ss_sp = addr;
442
443 rc = sigaltstack(&ss, NULL);
444
445 if (exp == FAIL_EXPECTED) {
446 if (rc) {
447 printf("[OK]\tsigaltstack() failed.\n");
448 } else {
449 fatal_error("sigaltstack() succeeded unexpectedly.\n");
450 }
451 } else if (rc) {
452 fatal_error("sigaltstack()");
453 }
454 }
455
test_dynamic_sigaltstack(void)456 static void test_dynamic_sigaltstack(void)
457 {
458 unsigned int small_size, enough_size;
459 unsigned long minsigstksz;
460 void *altstack;
461
462 minsigstksz = getauxval(AT_MINSIGSTKSZ);
463 printf("\tAT_MINSIGSTKSZ = %lu\n", minsigstksz);
464 /*
465 * getauxval() itself can return 0 for failure or
466 * success. But, in this case, AT_MINSIGSTKSZ
467 * will always return a >=0 value if implemented.
468 * Just check for 0.
469 */
470 if (minsigstksz == 0) {
471 printf("no support for AT_MINSIGSTKSZ, skipping sigaltstack tests\n");
472 return;
473 }
474
475 enough_size = minsigstksz * 2;
476
477 altstack = alloc_altstack(enough_size);
478 printf("\tAllocate memory for altstack (%u bytes).\n", enough_size);
479
480 /*
481 * Try setup_altstack() with a size which can not fit
482 * XTILEDATA. ARCH_REQ_XCOMP_PERM should fail.
483 */
484 small_size = minsigstksz - xtiledata.size;
485 printf("\tAfter sigaltstack() with small size (%u bytes).\n", small_size);
486 setup_altstack(altstack, small_size, SUCCESS_EXPECTED);
487 validate_req_xcomp_perm(FAIL_EXPECTED);
488
489 /*
490 * Try setup_altstack() with a size derived from
491 * AT_MINSIGSTKSZ. It should be more than large enough
492 * and thus ARCH_REQ_XCOMP_PERM should succeed.
493 */
494 printf("\tAfter sigaltstack() with enough size (%u bytes).\n", enough_size);
495 setup_altstack(altstack, enough_size, SUCCESS_EXPECTED);
496 validate_req_xcomp_perm(SUCCESS_EXPECTED);
497
498 /*
499 * Try to coerce setup_altstack() to again accept a
500 * too-small altstack. This ensures that big-enough
501 * sigaltstacks can not shrink to a too-small value
502 * once XTILEDATA permission is established.
503 */
504 printf("\tThen, sigaltstack() with small size (%u bytes).\n", small_size);
505 setup_altstack(altstack, small_size, FAIL_EXPECTED);
506 }
507
test_dynamic_state(void)508 static void test_dynamic_state(void)
509 {
510 pid_t parent, child, grandchild;
511
512 parent = fork();
513 if (parent < 0) {
514 /* fork() failed */
515 fatal_error("fork");
516 } else if (parent > 0) {
517 int status;
518 /* fork() succeeded. Now in the parent. */
519
520 wait(&status);
521 if (!WIFEXITED(status) || WEXITSTATUS(status))
522 fatal_error("arch_prctl test parent exit");
523 return;
524 }
525 /* fork() succeeded. Now in the child . */
526
527 printf("[RUN]\tCheck ARCH_REQ_XCOMP_PERM around process fork() and sigaltack() test.\n");
528
529 printf("\tFork a child.\n");
530 child = fork();
531 if (child < 0) {
532 fatal_error("fork");
533 } else if (child > 0) {
534 int status;
535
536 wait(&status);
537 if (!WIFEXITED(status) || WEXITSTATUS(status))
538 fatal_error("arch_prctl test child exit");
539 _exit(0);
540 }
541
542 /*
543 * The permission request should fail without an
544 * XTILEDATA-compatible signal stack
545 */
546 printf("\tTest XCOMP_PERM at child.\n");
547 validate_xcomp_perm(FAIL_EXPECTED);
548
549 /*
550 * Set up an XTILEDATA-compatible signal stack and
551 * also obtain permission to populate XTILEDATA.
552 */
553 printf("\tTest dynamic sigaltstack at child:\n");
554 test_dynamic_sigaltstack();
555
556 /* Ensure that XTILEDATA can be populated. */
557 printf("\tTest XCOMP_PERM again at child.\n");
558 validate_xcomp_perm(SUCCESS_EXPECTED);
559
560 printf("\tFork a grandchild.\n");
561 grandchild = fork();
562 if (grandchild < 0) {
563 /* fork() failed */
564 fatal_error("fork");
565 } else if (!grandchild) {
566 /* fork() succeeded. Now in the (grand)child. */
567 printf("\tTest XCOMP_PERM at grandchild.\n");
568
569 /*
570 * Ensure that the grandchild inherited
571 * permission and a compatible sigaltstack:
572 */
573 validate_xcomp_perm(SUCCESS_EXPECTED);
574 } else {
575 int status;
576 /* fork() succeeded. Now in the parent. */
577
578 wait(&status);
579 if (!WIFEXITED(status) || WEXITSTATUS(status))
580 fatal_error("fork test grandchild");
581 }
582
583 _exit(0);
584 }
585
586 /*
587 * Save current register state and compare it to @xbuf1.'
588 *
589 * Returns false if @xbuf1 matches the registers.
590 * Returns true if @xbuf1 differs from the registers.
591 */
__validate_tiledata_regs(struct xsave_buffer * xbuf1)592 static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
593 {
594 struct xsave_buffer *xbuf2;
595 int ret;
596
597 xbuf2 = alloc_xbuf();
598 if (!xbuf2)
599 fatal_error("failed to allocate XSAVE buffer\n");
600
601 xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
602 ret = memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
603 &xbuf2->bytes[xtiledata.xbuf_offset],
604 xtiledata.size);
605
606 free(xbuf2);
607
608 if (ret == 0)
609 return false;
610 return true;
611 }
612
validate_tiledata_regs_same(struct xsave_buffer * xbuf)613 static inline void validate_tiledata_regs_same(struct xsave_buffer *xbuf)
614 {
615 int ret = __validate_tiledata_regs(xbuf);
616
617 if (ret != 0)
618 fatal_error("TILEDATA registers changed");
619 }
620
validate_tiledata_regs_changed(struct xsave_buffer * xbuf)621 static inline void validate_tiledata_regs_changed(struct xsave_buffer *xbuf)
622 {
623 int ret = __validate_tiledata_regs(xbuf);
624
625 if (ret == 0)
626 fatal_error("TILEDATA registers did not change");
627 }
628
629 /* tiledata inheritance test */
630
test_fork(void)631 static void test_fork(void)
632 {
633 pid_t child, grandchild;
634
635 child = fork();
636 if (child < 0) {
637 /* fork() failed */
638 fatal_error("fork");
639 } else if (child > 0) {
640 /* fork() succeeded. Now in the parent. */
641 int status;
642
643 wait(&status);
644 if (!WIFEXITED(status) || WEXITSTATUS(status))
645 fatal_error("fork test child");
646 return;
647 }
648 /* fork() succeeded. Now in the child. */
649 printf("[RUN]\tCheck tile data inheritance.\n\tBefore fork(), load tiledata\n");
650
651 load_rand_tiledata(stashed_xsave);
652
653 grandchild = fork();
654 if (grandchild < 0) {
655 /* fork() failed */
656 fatal_error("fork");
657 } else if (grandchild > 0) {
658 /* fork() succeeded. Still in the first child. */
659 int status;
660
661 wait(&status);
662 if (!WIFEXITED(status) || WEXITSTATUS(status))
663 fatal_error("fork test grand child");
664 _exit(0);
665 }
666 /* fork() succeeded. Now in the (grand)child. */
667
668 /*
669 * TILEDATA registers are not preserved across fork().
670 * Ensure that their value has changed:
671 */
672 validate_tiledata_regs_changed(stashed_xsave);
673
674 _exit(0);
675 }
676
677 /* Context switching test */
678
679 static struct _ctxtswtest_cfg {
680 unsigned int iterations;
681 unsigned int num_threads;
682 } ctxtswtest_config;
683
684 struct futex_info {
685 pthread_t thread;
686 int nr;
687 pthread_mutex_t mutex;
688 struct futex_info *next;
689 };
690
check_tiledata(void * info)691 static void *check_tiledata(void *info)
692 {
693 struct futex_info *finfo = (struct futex_info *)info;
694 struct xsave_buffer *xbuf;
695 int i;
696
697 xbuf = alloc_xbuf();
698 if (!xbuf)
699 fatal_error("unable to allocate XSAVE buffer");
700
701 /*
702 * Load random data into 'xbuf' and then restore
703 * it to the tile registers themselves.
704 */
705 load_rand_tiledata(xbuf);
706 for (i = 0; i < ctxtswtest_config.iterations; i++) {
707 pthread_mutex_lock(&finfo->mutex);
708
709 /*
710 * Ensure the register values have not
711 * diverged from those recorded in 'xbuf'.
712 */
713 validate_tiledata_regs_same(xbuf);
714
715 /* Load new, random values into xbuf and registers */
716 load_rand_tiledata(xbuf);
717
718 /*
719 * The last thread's last unlock will be for
720 * thread 0's mutex. However, thread 0 will
721 * have already exited the loop and the mutex
722 * will already be unlocked.
723 *
724 * Because this is not an ERRORCHECK mutex,
725 * that inconsistency will be silently ignored.
726 */
727 pthread_mutex_unlock(&finfo->next->mutex);
728 }
729
730 free(xbuf);
731 /*
732 * Return this thread's finfo, which is
733 * a unique value for this thread.
734 */
735 return finfo;
736 }
737
create_threads(int num,struct futex_info * finfo)738 static int create_threads(int num, struct futex_info *finfo)
739 {
740 int i;
741
742 for (i = 0; i < num; i++) {
743 int next_nr;
744
745 finfo[i].nr = i;
746 /*
747 * Thread 'i' will wait on this mutex to
748 * be unlocked. Lock it immediately after
749 * initialization:
750 */
751 pthread_mutex_init(&finfo[i].mutex, NULL);
752 pthread_mutex_lock(&finfo[i].mutex);
753
754 next_nr = (i + 1) % num;
755 finfo[i].next = &finfo[next_nr];
756
757 if (pthread_create(&finfo[i].thread, NULL, check_tiledata, &finfo[i]))
758 fatal_error("pthread_create()");
759 }
760 return 0;
761 }
762
affinitize_cpu0(void)763 static void affinitize_cpu0(void)
764 {
765 cpu_set_t cpuset;
766
767 CPU_ZERO(&cpuset);
768 CPU_SET(0, &cpuset);
769
770 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
771 fatal_error("sched_setaffinity to CPU 0");
772 }
773
test_context_switch(void)774 static void test_context_switch(void)
775 {
776 struct futex_info *finfo;
777 int i;
778
779 /* Affinitize to one CPU to force context switches */
780 affinitize_cpu0();
781
782 req_xtiledata_perm();
783
784 printf("[RUN]\tCheck tiledata context switches, %d iterations, %d threads.\n",
785 ctxtswtest_config.iterations,
786 ctxtswtest_config.num_threads);
787
788
789 finfo = malloc(sizeof(*finfo) * ctxtswtest_config.num_threads);
790 if (!finfo)
791 fatal_error("malloc()");
792
793 create_threads(ctxtswtest_config.num_threads, finfo);
794
795 /*
796 * This thread wakes up thread 0
797 * Thread 0 will wake up 1
798 * Thread 1 will wake up 2
799 * ...
800 * the last thread will wake up 0
801 *
802 * ... this will repeat for the configured
803 * number of iterations.
804 */
805 pthread_mutex_unlock(&finfo[0].mutex);
806
807 /* Wait for all the threads to finish: */
808 for (i = 0; i < ctxtswtest_config.num_threads; i++) {
809 void *thread_retval;
810 int rc;
811
812 rc = pthread_join(finfo[i].thread, &thread_retval);
813
814 if (rc)
815 fatal_error("pthread_join() failed for thread %d err: %d\n",
816 i, rc);
817
818 if (thread_retval != &finfo[i])
819 fatal_error("unexpected thread retval for thread %d: %p\n",
820 i, thread_retval);
821
822 }
823
824 printf("[OK]\tNo incorrect case was found.\n");
825
826 free(finfo);
827 }
828
main(void)829 int main(void)
830 {
831 /* Check hardware availability at first */
832 check_cpuid_xsave();
833 check_cpuid_xtiledata();
834
835 init_stashed_xsave();
836 sethandler(SIGILL, handle_noperm, 0);
837
838 test_dynamic_state();
839
840 /* Request permission for the following tests */
841 req_xtiledata_perm();
842
843 test_fork();
844
845 ctxtswtest_config.iterations = 10;
846 ctxtswtest_config.num_threads = 5;
847 test_context_switch();
848
849 clearhandler(SIGILL);
850 free_stashed_xsave();
851
852 return 0;
853 }
854