File Index
Files and directories
Sign in
FILE

amx.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/amx.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#define _GNU_SOURCE
  4#include <err.h>
  5#include <errno.h>
  6#include <setjmp.h>
  7#include <stdio.h>
  8#include <string.h>
  9#include <stdbool.h>
 10#include <unistd.h>
 11#include <x86intrin.h>
 12
 13#include <sys/auxv.h>
 14#include <sys/mman.h>
 15#include <sys/shm.h>
 16#include <sys/syscall.h>
 17#include <sys/wait.h>
 18
 19#include "helpers.h"
 20#include "xstate.h"
 21
 22#ifndef __x86_64__
 23# error This test is 64-bit only
 24#endif
 25
 26/* err() exits and will not return */
 27#define fatal_error(msg, ...)	err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
 28
 29#define XFEATURE_MASK_XTILECFG	(1 << XFEATURE_XTILECFG)
 30#define XFEATURE_MASK_XTILEDATA	(1 << XFEATURE_XTILEDATA)
 31#define XFEATURE_MASK_XTILE	(XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
 32
 33struct xstate_info xtiledata;
 34
 35/* The helpers for managing XSAVE buffer and tile states: */
 36
 37struct xsave_buffer *stashed_xsave;
 38
 39static void init_stashed_xsave(void)
 40{
 41	stashed_xsave = alloc_xbuf();
 42	if (!stashed_xsave)
 43		fatal_error("failed to allocate stashed_xsave\n");
 44	clear_xstate_header(stashed_xsave);
 45}
 46
 47static void free_stashed_xsave(void)
 48{
 49	free(stashed_xsave);
 50}
 51
 52/* Work around printf() being unsafe in signals: */
 53#define SIGNAL_BUF_LEN 1000
 54char signal_message_buffer[SIGNAL_BUF_LEN];
 55void sig_print(char *msg)
 56{
 57	int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
 58
 59	strncat(signal_message_buffer, msg, left);
 60}
 61
 62static volatile bool noperm_signaled;
 63static int noperm_errs;
 64/*
 65 * Signal handler for when AMX is used but
 66 * permission has not been obtained.
 67 */
 68static void handle_noperm(int sig, siginfo_t *si, void *ctx_void)
 69{
 70	ucontext_t *ctx = (ucontext_t *)ctx_void;
 71	void *xbuf = ctx->uc_mcontext.fpregs;
 72	struct _fpx_sw_bytes *sw_bytes;
 73	uint64_t features;
 74
 75	/* Reset the signal message buffer: */
 76	signal_message_buffer[0] = '\0';
 77	sig_print("\tAt SIGILL handler,\n");
 78
 79	if (si->si_code != ILL_ILLOPC) {
 80		noperm_errs++;
 81		sig_print("[FAIL]\tInvalid signal code.\n");
 82	} else {
 83		sig_print("[OK]\tValid signal code (ILL_ILLOPC).\n");
 84	}
 85
 86	sw_bytes = get_fpx_sw_bytes(xbuf);
 87	/*
 88	 * Without permission, the signal XSAVE buffer should not
 89	 * have room for AMX register state (aka. xtiledata).
 90	 * Check that the size does not overlap with where xtiledata
 91	 * will reside.
 92	 *
 93	 * This also implies that no state components *PAST*
 94	 * XTILEDATA (features >=19) can be present in the buffer.
 95	 */
 96	if (sw_bytes->xstate_size <= xtiledata.xbuf_offset) {
 97		sig_print("[OK]\tValid xstate size\n");
 98	} else {
 99		noperm_errs++;
100		sig_print("[FAIL]\tInvalid xstate size\n");
101	}
102
103	features = get_fpx_sw_bytes_features(xbuf);
104	/*
105	 * Without permission, the XTILEDATA feature
106	 * bit should not be set.
107	 */
108	if ((features & XFEATURE_MASK_XTILEDATA) == 0) {
109		sig_print("[OK]\tValid xstate mask\n");
110	} else {
111		noperm_errs++;
112		sig_print("[FAIL]\tInvalid xstate mask\n");
113	}
114
115	noperm_signaled = true;
116	ctx->uc_mcontext.gregs[REG_RIP] += 3; /* Skip the faulting XRSTOR */
117}
118
119/* Return true if XRSTOR is successful; otherwise, false. */
120static inline bool xrstor_safe(struct xsave_buffer *xbuf, uint64_t mask)
121{
122	noperm_signaled = false;
123	xrstor(xbuf, mask);
124
125	/* Print any messages produced by the signal code: */
126	printf("%s", signal_message_buffer);
127	/*
128	 * Reset the buffer to make sure any future printing
129	 * only outputs new messages:
130	 */
131	signal_message_buffer[0] = '\0';
132
133	if (noperm_errs)
134		fatal_error("saw %d errors in noperm signal handler\n", noperm_errs);
135
136	return !noperm_signaled;
137}
138
139/*
140 * Use XRSTOR to populate the XTILEDATA registers with
141 * random data.
142 *
143 * Return true if successful; otherwise, false.
144 */
145static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
146{
147	clear_xstate_header(xbuf);
148	set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
149	set_rand_data(&xtiledata, xbuf);
150	return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
151}
152
153enum expected_result { FAIL_EXPECTED, SUCCESS_EXPECTED };
154
155/* arch_prctl() and sigaltstack() test */
156
157#define ARCH_GET_XCOMP_SUPP	0x1021
158#define ARCH_GET_XCOMP_PERM	0x1022
159#define ARCH_REQ_XCOMP_PERM	0x1023
160
161static void req_xtiledata_perm(void)
162{
163	syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
164}
165
166static void validate_req_xcomp_perm(enum expected_result exp)
167{
168	unsigned long bitmask, expected_bitmask;
169	long rc;
170
171	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
172	if (rc) {
173		fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
174	} else if (!(bitmask & XFEATURE_MASK_XTILECFG)) {
175		fatal_error("ARCH_GET_XCOMP_PERM returns XFEATURE_XTILECFG off.");
176	}
177
178	rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
179	if (exp == FAIL_EXPECTED) {
180		if (rc) {
181			printf("[OK]\tARCH_REQ_XCOMP_PERM saw expected failure..\n");
182			return;
183		}
184
185		fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected success.\n");
186	} else if (rc) {
187		fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected failure.\n");
188	}
189
190	expected_bitmask = bitmask | XFEATURE_MASK_XTILEDATA;
191
192	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
193	if (rc) {
194		fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
195	} else if (bitmask != expected_bitmask) {
196		fatal_error("ARCH_REQ_XCOMP_PERM set a wrong bitmask: %lx, expected: %lx.\n",
197			    bitmask, expected_bitmask);
198	} else {
199		printf("\tARCH_REQ_XCOMP_PERM is successful.\n");
200	}
201}
202
203static void validate_xcomp_perm(enum expected_result exp)
204{
205	bool load_success = load_rand_tiledata(stashed_xsave);
206
207	if (exp == FAIL_EXPECTED) {
208		if (load_success) {
209			noperm_errs++;
210			printf("[FAIL]\tLoad tiledata succeeded.\n");
211		} else {
212			printf("[OK]\tLoad tiledata failed.\n");
213		}
214	} else if (exp == SUCCESS_EXPECTED) {
215		if (load_success) {
216			printf("[OK]\tLoad tiledata succeeded.\n");
217		} else {
218			noperm_errs++;
219			printf("[FAIL]\tLoad tiledata failed.\n");
220		}
221	}
222}
223
224#ifndef AT_MINSIGSTKSZ
225#  define AT_MINSIGSTKSZ	51
226#endif
227
228static void *alloc_altstack(unsigned int size)
229{
230	void *altstack;
231
232	altstack = mmap(NULL, size, PROT_READ | PROT_WRITE,
233			MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
234
235	if (altstack == MAP_FAILED)
236		fatal_error("mmap() for altstack");
237
238	return altstack;
239}
240
241static void setup_altstack(void *addr, unsigned long size, enum expected_result exp)
242{
243	stack_t ss;
244	int rc;
245
246	memset(&ss, 0, sizeof(ss));
247	ss.ss_size = size;
248	ss.ss_sp = addr;
249
250	rc = sigaltstack(&ss, NULL);
251
252	if (exp == FAIL_EXPECTED) {
253		if (rc) {
254			printf("[OK]\tsigaltstack() failed.\n");
255		} else {
256			fatal_error("sigaltstack() succeeded unexpectedly.\n");
257		}
258	} else if (rc) {
259		fatal_error("sigaltstack()");
260	}
261}
262
263static void test_dynamic_sigaltstack(void)
264{
265	unsigned int small_size, enough_size;
266	unsigned long minsigstksz;
267	void *altstack;
268
269	minsigstksz = getauxval(AT_MINSIGSTKSZ);
270	printf("\tAT_MINSIGSTKSZ = %lu\n", minsigstksz);
271	/*
272	 * getauxval() itself can return 0 for failure or
273	 * success.  But, in this case, AT_MINSIGSTKSZ
274	 * will always return a >=0 value if implemented.
275	 * Just check for 0.
276	 */
277	if (minsigstksz == 0) {
278		printf("no support for AT_MINSIGSTKSZ, skipping sigaltstack tests\n");
279		return;
280	}
281
282	enough_size = minsigstksz * 2;
283
284	altstack = alloc_altstack(enough_size);
285	printf("\tAllocate memory for altstack (%u bytes).\n", enough_size);
286
287	/*
288	 * Try setup_altstack() with a size which can not fit
289	 * XTILEDATA.  ARCH_REQ_XCOMP_PERM should fail.
290	 */
291	small_size = minsigstksz - xtiledata.size;
292	printf("\tAfter sigaltstack() with small size (%u bytes).\n", small_size);
293	setup_altstack(altstack, small_size, SUCCESS_EXPECTED);
294	validate_req_xcomp_perm(FAIL_EXPECTED);
295
296	/*
297	 * Try setup_altstack() with a size derived from
298	 * AT_MINSIGSTKSZ.  It should be more than large enough
299	 * and thus ARCH_REQ_XCOMP_PERM should succeed.
300	 */
301	printf("\tAfter sigaltstack() with enough size (%u bytes).\n", enough_size);
302	setup_altstack(altstack, enough_size, SUCCESS_EXPECTED);
303	validate_req_xcomp_perm(SUCCESS_EXPECTED);
304
305	/*
306	 * Try to coerce setup_altstack() to again accept a
307	 * too-small altstack.  This ensures that big-enough
308	 * sigaltstacks can not shrink to a too-small value
309	 * once XTILEDATA permission is established.
310	 */
311	printf("\tThen, sigaltstack() with small size (%u bytes).\n", small_size);
312	setup_altstack(altstack, small_size, FAIL_EXPECTED);
313}
314
315static void test_dynamic_state(void)
316{
317	pid_t parent, child, grandchild;
318
319	parent = fork();
320	if (parent < 0) {
321		/* fork() failed */
322		fatal_error("fork");
323	} else if (parent > 0) {
324		int status;
325		/* fork() succeeded.  Now in the parent. */
326
327		wait(&status);
328		if (!WIFEXITED(status) || WEXITSTATUS(status))
329			fatal_error("arch_prctl test parent exit");
330		return;
331	}
332	/* fork() succeeded.  Now in the child . */
333
334	printf("[RUN]\tCheck ARCH_REQ_XCOMP_PERM around process fork() and sigaltack() test.\n");
335
336	printf("\tFork a child.\n");
337	child = fork();
338	if (child < 0) {
339		fatal_error("fork");
340	} else if (child > 0) {
341		int status;
342
343		wait(&status);
344		if (!WIFEXITED(status) || WEXITSTATUS(status))
345			fatal_error("arch_prctl test child exit");
346		_exit(0);
347	}
348
349	/*
350	 * The permission request should fail without an
351	 * XTILEDATA-compatible signal stack
352	 */
353	printf("\tTest XCOMP_PERM at child.\n");
354	validate_xcomp_perm(FAIL_EXPECTED);
355
356	/*
357	 * Set up an XTILEDATA-compatible signal stack and
358	 * also obtain permission to populate XTILEDATA.
359	 */
360	printf("\tTest dynamic sigaltstack at child:\n");
361	test_dynamic_sigaltstack();
362
363	/* Ensure that XTILEDATA can be populated. */
364	printf("\tTest XCOMP_PERM again at child.\n");
365	validate_xcomp_perm(SUCCESS_EXPECTED);
366
367	printf("\tFork a grandchild.\n");
368	grandchild = fork();
369	if (grandchild < 0) {
370		/* fork() failed */
371		fatal_error("fork");
372	} else if (!grandchild) {
373		/* fork() succeeded.  Now in the (grand)child. */
374		printf("\tTest XCOMP_PERM at grandchild.\n");
375
376		/*
377		 * Ensure that the grandchild inherited
378		 * permission and a compatible sigaltstack:
379		 */
380		validate_xcomp_perm(SUCCESS_EXPECTED);
381	} else {
382		int status;
383		/* fork() succeeded.  Now in the parent. */
384
385		wait(&status);
386		if (!WIFEXITED(status) || WEXITSTATUS(status))
387			fatal_error("fork test grandchild");
388	}
389
390	_exit(0);
391}
392
393static inline int __compare_tiledata_state(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
394{
395	return memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
396		      &xbuf2->bytes[xtiledata.xbuf_offset],
397		      xtiledata.size);
398}
399
400/*
401 * Save current register state and compare it to @xbuf1.'
402 *
403 * Returns false if @xbuf1 matches the registers.
404 * Returns true  if @xbuf1 differs from the registers.
405 */
406static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
407{
408	struct xsave_buffer *xbuf2;
409	int ret;
410
411	xbuf2 = alloc_xbuf();
412	if (!xbuf2)
413		fatal_error("failed to allocate XSAVE buffer\n");
414
415	xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
416	ret = __compare_tiledata_state(xbuf1, xbuf2);
417
418	free(xbuf2);
419
420	if (ret == 0)
421		return false;
422	return true;
423}
424
425static inline void validate_tiledata_regs_changed(struct xsave_buffer *xbuf)
426{
427	int ret = __validate_tiledata_regs(xbuf);
428
429	if (ret == 0)
430		fatal_error("TILEDATA registers did not change");
431}
432
433/* tiledata inheritance test */
434
435static void test_fork(void)
436{
437	pid_t child, grandchild;
438
439	child = fork();
440	if (child < 0) {
441		/* fork() failed */
442		fatal_error("fork");
443	} else if (child > 0) {
444		/* fork() succeeded.  Now in the parent. */
445		int status;
446
447		wait(&status);
448		if (!WIFEXITED(status) || WEXITSTATUS(status))
449			fatal_error("fork test child");
450		return;
451	}
452	/* fork() succeeded.  Now in the child. */
453	printf("[RUN]\tCheck tile data inheritance.\n\tBefore fork(), load tiledata\n");
454
455	load_rand_tiledata(stashed_xsave);
456
457	grandchild = fork();
458	if (grandchild < 0) {
459		/* fork() failed */
460		fatal_error("fork");
461	} else if (grandchild > 0) {
462		/* fork() succeeded.  Still in the first child. */
463		int status;
464
465		wait(&status);
466		if (!WIFEXITED(status) || WEXITSTATUS(status))
467			fatal_error("fork test grand child");
468		_exit(0);
469	}
470	/* fork() succeeded.  Now in the (grand)child. */
471
472	/*
473	 * TILEDATA registers are not preserved across fork().
474	 * Ensure that their value has changed:
475	 */
476	validate_tiledata_regs_changed(stashed_xsave);
477
478	_exit(0);
479}
480
481int main(void)
482{
483	unsigned long features;
484	long rc;
485
486	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
487	if (rc || (features & XFEATURE_MASK_XTILE) != XFEATURE_MASK_XTILE) {
488		ksft_print_msg("no AMX support\n");
489		return KSFT_SKIP;
490	}
491
492	xtiledata = get_xstate_info(XFEATURE_XTILEDATA);
493	if (!xtiledata.size || !xtiledata.xbuf_offset) {
494		fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
495			    xtiledata.size, xtiledata.xbuf_offset);
496	}
497
498	init_stashed_xsave();
499	sethandler(SIGILL, handle_noperm, 0);
500
501	test_dynamic_state();
502
503	/* Request permission for the following tests */
504	req_xtiledata_perm();
505
506	test_fork();
507
508	/*
509	 * Perform generic xstate tests for context switching, ptrace,
510	 * and signal.
511	 */
512	test_xstate(XFEATURE_XTILEDATA);
513
514	clearhandler(SIGILL);
515	free_stashed_xsave();
516
517	return 0;
518}
~/ Read-only file preview.