File Index
Files and directories
Sign in
FILE

xstate.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/xstate.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#define _GNU_SOURCE
  4
  5#include <elf.h>
  6#include <pthread.h>
  7#include <stdbool.h>
  8
  9#include <asm/prctl.h>
 10#include <sys/ptrace.h>
 11#include <sys/syscall.h>
 12#include <sys/uio.h>
 13#include <sys/wait.h>
 14
 15#include "helpers.h"
 16#include "xstate.h"
 17
 18/*
 19 * The userspace xstate test suite is designed to be generic and operates
 20 * with randomized xstate data. However, some states require special handling:
 21 *
 22 * - PKRU and XTILECFG need specific adjustments, such as modifying
 23 *   randomization behavior or using fixed values.
 24 * - But, PKRU already has a dedicated test suite in /tools/selftests/mm.
 25 * - Legacy states (FP and SSE) are excluded, as they are not considered
 26 *   part of extended states (xstates) and their usage is already deeply
 27 *   integrated into user-space libraries.
 28 */
 29#define XFEATURE_MASK_TEST_SUPPORTED	\
 30	((1 << XFEATURE_YMM) |		\
 31	 (1 << XFEATURE_OPMASK)	|	\
 32	 (1 << XFEATURE_ZMM_Hi256) |	\
 33	 (1 << XFEATURE_Hi16_ZMM) |	\
 34	 (1 << XFEATURE_XTILEDATA) |	\
 35	 (1 << XFEATURE_APX))
 36
 37static inline uint64_t xgetbv(uint32_t index)
 38{
 39	uint32_t eax, edx;
 40
 41	asm volatile("xgetbv" : "=a" (eax), "=d" (edx) : "c" (index));
 42	return eax + ((uint64_t)edx << 32);
 43}
 44
 45static inline uint64_t get_xstatebv(struct xsave_buffer *xbuf)
 46{
 47	return *(uint64_t *)(&xbuf->header);
 48}
 49
 50static struct xstate_info xstate;
 51
 52struct futex_info {
 53	unsigned int iterations;
 54	struct futex_info *next;
 55	pthread_mutex_t mutex;
 56	pthread_t thread;
 57	bool valid;
 58	int nr;
 59};
 60
 61static inline void load_rand_xstate(struct xstate_info *xstate, struct xsave_buffer *xbuf)
 62{
 63	clear_xstate_header(xbuf);
 64	set_xstatebv(xbuf, xstate->mask);
 65	set_rand_data(xstate, xbuf);
 66	xrstor(xbuf, xstate->mask);
 67}
 68
 69static inline void load_init_xstate(struct xstate_info *xstate, struct xsave_buffer *xbuf)
 70{
 71	clear_xstate_header(xbuf);
 72	xrstor(xbuf, xstate->mask);
 73}
 74
 75static inline void copy_xstate(struct xsave_buffer *xbuf_dst, struct xsave_buffer *xbuf_src)
 76{
 77	memcpy(&xbuf_dst->bytes[xstate.xbuf_offset],
 78	       &xbuf_src->bytes[xstate.xbuf_offset],
 79	       xstate.size);
 80}
 81
 82static inline bool validate_xstate_same(struct xsave_buffer *xbuf1, struct xsave_buffer *xbuf2)
 83{
 84	int ret;
 85
 86	ret = memcmp(&xbuf1->bytes[xstate.xbuf_offset],
 87		     &xbuf2->bytes[xstate.xbuf_offset],
 88		     xstate.size);
 89	return ret == 0;
 90}
 91
 92static inline bool validate_xregs_same(struct xsave_buffer *xbuf1)
 93{
 94	struct xsave_buffer *xbuf2;
 95	bool ret;
 96
 97	xbuf2 = alloc_xbuf();
 98	if (!xbuf2)
 99		ksft_exit_fail_msg("failed to allocate XSAVE buffer\n");
100
101	xsave(xbuf2, xstate.mask);
102	ret = validate_xstate_same(xbuf1, xbuf2);
103
104	free(xbuf2);
105	return ret;
106}
107
108/* Context switching test */
109
110static void *check_xstate(void *info)
111{
112	struct futex_info *finfo = (struct futex_info *)info;
113	struct xsave_buffer *xbuf;
114	int i;
115
116	xbuf = alloc_xbuf();
117	if (!xbuf)
118		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
119
120	/*
121	 * Load random data into 'xbuf' and then restore it to the xstate
122	 * registers.
123	 */
124	load_rand_xstate(&xstate, xbuf);
125	finfo->valid = true;
126
127	for (i = 0; i < finfo->iterations; i++) {
128		pthread_mutex_lock(&finfo->mutex);
129
130		/*
131		 * Ensure the register values have not diverged from the
132		 * record. Then reload a new random value.  If it failed
133		 * ever before, skip it.
134		 */
135		if (finfo->valid) {
136			finfo->valid = validate_xregs_same(xbuf);
137			load_rand_xstate(&xstate, xbuf);
138		}
139
140		/*
141		 * The last thread's last unlock will be for thread 0's
142		 * mutex. However, thread 0 will have already exited the
143		 * loop and the mutex will already be unlocked.
144		 *
145		 * Because this is not an ERRORCHECK mutex, that
146		 * inconsistency will be silently ignored.
147		 */
148		pthread_mutex_unlock(&finfo->next->mutex);
149	}
150
151	free(xbuf);
152	return finfo;
153}
154
155static void create_threads(uint32_t num_threads, uint32_t iterations, struct futex_info *finfo)
156{
157	int i;
158
159	for (i = 0; i < num_threads; i++) {
160		int next_nr;
161
162		finfo[i].nr = i;
163		finfo[i].iterations = iterations;
164
165		/*
166		 * Thread 'i' will wait on this mutex to be unlocked.
167		 * Lock it immediately after initialization:
168		 */
169		pthread_mutex_init(&finfo[i].mutex, NULL);
170		pthread_mutex_lock(&finfo[i].mutex);
171
172		next_nr = (i + 1) % num_threads;
173		finfo[i].next = &finfo[next_nr];
174
175		if (pthread_create(&finfo[i].thread, NULL, check_xstate, &finfo[i]))
176			ksft_exit_fail_msg("pthread_create() failed\n");
177	}
178}
179
180static bool checkout_threads(uint32_t num_threads, struct futex_info *finfo)
181{
182	void *thread_retval;
183	bool valid = true;
184	int err, i;
185
186	for (i = 0; i < num_threads; i++) {
187		err = pthread_join(finfo[i].thread, &thread_retval);
188		if (err)
189			ksft_exit_fail_msg("pthread_join() failed for thread %d err: %d\n", i, err);
190
191		if (thread_retval != &finfo[i]) {
192			ksft_exit_fail_msg("unexpected thread retval for thread %d: %p\n",
193					   i, thread_retval);
194		}
195
196		valid &= finfo[i].valid;
197	}
198
199	return valid;
200}
201
202static void affinitize_cpu0(void)
203{
204	cpu_set_t cpuset;
205
206	CPU_ZERO(&cpuset);
207	CPU_SET(0, &cpuset);
208
209	if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
210		ksft_exit_fail_msg("sched_setaffinity to CPU 0 failed\n");
211}
212
213static void test_context_switch(uint32_t num_threads, uint32_t iterations)
214{
215	struct futex_info *finfo;
216
217	/* Affinitize to one CPU to force context switches */
218	affinitize_cpu0();
219
220	printf("[RUN]\t%s: check context switches, %d iterations, %d threads.\n",
221	       xstate.name, iterations, num_threads);
222
223	finfo = malloc(sizeof(*finfo) * num_threads);
224	if (!finfo)
225		ksft_exit_fail_msg("unable allocate memory\n");
226
227	create_threads(num_threads, iterations, finfo);
228
229	/*
230	 * This thread wakes up thread 0
231	 * Thread 0 will wake up 1
232	 * Thread 1 will wake up 2
233	 * ...
234	 * The last thread will wake up 0
235	 *
236	 * This will repeat for the configured
237	 * number of iterations.
238	 */
239	pthread_mutex_unlock(&finfo[0].mutex);
240
241	/* Wait for all the threads to finish: */
242	if (checkout_threads(num_threads, finfo))
243		printf("[OK]\tNo incorrect case was found.\n");
244	else
245		printf("[FAIL]\tFailed with context switching test.\n");
246
247	free(finfo);
248}
249
250/*
251 * Ptrace test for the ABI format as described in arch/x86/include/asm/user.h
252 */
253
254/*
255 * Make sure the ptracee has the expanded kernel buffer on the first use.
256 * Then, initialize the state before performing the state injection from
257 * the ptracer. For non-dynamic states, this is benign.
258 */
259static inline void ptracee_touch_xstate(void)
260{
261	struct xsave_buffer *xbuf;
262
263	xbuf = alloc_xbuf();
264
265	load_rand_xstate(&xstate, xbuf);
266	load_init_xstate(&xstate, xbuf);
267
268	free(xbuf);
269}
270
271/*
272 * Ptracer injects the randomized xstate data. It also reads before and
273 * after that, which will execute the kernel's state copy functions.
274 */
275static void ptracer_inject_xstate(pid_t target)
276{
277	uint32_t xbuf_size = get_xbuf_size();
278	struct xsave_buffer *xbuf1, *xbuf2;
279	struct iovec iov;
280
281	/*
282	 * Allocate buffers to keep data while ptracer can write the
283	 * other buffer
284	 */
285	xbuf1 = alloc_xbuf();
286	xbuf2 = alloc_xbuf();
287	if (!xbuf1 || !xbuf2)
288		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
289
290	iov.iov_base = xbuf1;
291	iov.iov_len  = xbuf_size;
292
293	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
294		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
295
296	printf("[RUN]\t%s: inject xstate via ptrace().\n", xstate.name);
297
298	load_rand_xstate(&xstate, xbuf1);
299	copy_xstate(xbuf2, xbuf1);
300
301	if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
302		ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
303
304	if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
305		ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
306
307	if (*(uint64_t *)get_fpx_sw_bytes(xbuf1) == xgetbv(0))
308		printf("[OK]\t'xfeatures' in SW reserved area was correctly written\n");
309	else
310		printf("[FAIL]\t'xfeatures' in SW reserved area was not correctly written\n");
311
312	if (validate_xstate_same(xbuf2, xbuf1))
313		printf("[OK]\txstate was correctly updated.\n");
314	else
315		printf("[FAIL]\txstate was not correctly updated.\n");
316
317	free(xbuf1);
318	free(xbuf2);
319}
320
321static void test_ptrace(void)
322{
323	pid_t child;
324	int status;
325
326	child = fork();
327	if (child < 0) {
328		ksft_exit_fail_msg("fork() failed\n");
329	} else if (!child) {
330		if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
331			ksft_exit_fail_msg("PTRACE_TRACEME failed\n");
332
333		ptracee_touch_xstate();
334
335		raise(SIGTRAP);
336		_exit(0);
337	}
338
339	do {
340		wait(&status);
341	} while (WSTOPSIG(status) != SIGTRAP);
342
343	ptracer_inject_xstate(child);
344
345	ptrace(PTRACE_DETACH, child, NULL, NULL);
346	wait(&status);
347	if (!WIFEXITED(status) || WEXITSTATUS(status))
348		ksft_exit_fail_msg("ptracee exit error\n");
349}
350
351/*
352 * Test signal delivery for the ABI compatibility.
353 * See the ABI format: arch/x86/include/uapi/asm/sigcontext.h
354 */
355
356/*
357 * Avoid using printf() in signal handlers as it is not
358 * async-signal-safe.
359 */
360#define SIGNAL_BUF_LEN 1000
361static char signal_message_buffer[SIGNAL_BUF_LEN];
362static void sig_print(char *msg)
363{
364	int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
365
366	strncat(signal_message_buffer, msg, left);
367}
368
369static struct xsave_buffer *stashed_xbuf;
370
371static void validate_sigfpstate(int sig, siginfo_t *si, void *ctx_void)
372{
373	ucontext_t *ctx = (ucontext_t *)ctx_void;
374	void *xbuf = ctx->uc_mcontext.fpregs;
375	struct _fpx_sw_bytes *sw_bytes;
376	uint32_t magic2;
377
378	/* Reset the signal message buffer: */
379	signal_message_buffer[0] = '\0';
380
381	sw_bytes = get_fpx_sw_bytes(xbuf);
382	if (sw_bytes->magic1 == FP_XSTATE_MAGIC1)
383		sig_print("[OK]\t'magic1' is valid\n");
384	else
385		sig_print("[FAIL]\t'magic1' is not valid\n");
386
387	if (get_fpx_sw_bytes_features(xbuf) & xstate.mask)
388		sig_print("[OK]\t'xfeatures' in SW reserved area is valid\n");
389	else
390		sig_print("[FAIL]\t'xfeatures' in SW reserved area is not valid\n");
391
392	if (get_xstatebv(xbuf) & xstate.mask)
393		sig_print("[OK]\t'xfeatures' in XSAVE header is valid\n");
394	else
395		sig_print("[FAIL]\t'xfeatures' in XSAVE header is not valid\n");
396
397	if (validate_xstate_same(stashed_xbuf, xbuf))
398		sig_print("[OK]\txstate delivery was successful\n");
399	else
400		sig_print("[FAIL]\txstate delivery was not successful\n");
401
402	magic2 = *(uint32_t *)(xbuf + sw_bytes->xstate_size);
403	if (magic2 == FP_XSTATE_MAGIC2)
404		sig_print("[OK]\t'magic2' is valid\n");
405	else
406		sig_print("[FAIL]\t'magic2' is not valid\n");
407
408	set_rand_data(&xstate, xbuf);
409	copy_xstate(stashed_xbuf, xbuf);
410}
411
412static void test_signal(void)
413{
414	bool valid_xstate;
415
416	/*
417	 * The signal handler will access this to verify xstate context
418	 * preservation.
419	 */
420	stashed_xbuf = alloc_xbuf();
421	if (!stashed_xbuf)
422		ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
423
424	printf("[RUN]\t%s: load xstate and raise SIGUSR1\n", xstate.name);
425
426	sethandler(SIGUSR1, validate_sigfpstate, 0);
427
428	load_rand_xstate(&xstate, stashed_xbuf);
429
430	raise(SIGUSR1);
431
432	/*
433	 * Immediately record the test result, deferring printf() to
434	 * prevent unintended state contamination by that.
435	 */
436	valid_xstate = validate_xregs_same(stashed_xbuf);
437	printf("%s", signal_message_buffer);
438
439	printf("[RUN]\t%s: load new xstate from sighandler and check it after sigreturn\n",
440	       xstate.name);
441
442	if (valid_xstate)
443		printf("[OK]\txstate was restored correctly\n");
444	else
445		printf("[FAIL]\txstate restoration failed\n");
446
447	clearhandler(SIGUSR1);
448	free(stashed_xbuf);
449}
450
451void test_xstate(uint32_t feature_num)
452{
453	const unsigned int ctxtsw_num_threads = 5, ctxtsw_iterations = 10;
454	unsigned long features;
455	long rc;
456
457	if (!(XFEATURE_MASK_TEST_SUPPORTED & (1 << feature_num))) {
458		ksft_print_msg("The xstate test does not fully support the component %u, yet.\n",
459			       feature_num);
460		return;
461	}
462
463	rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
464	if (rc || !(features & (1 << feature_num))) {
465		ksft_print_msg("The kernel does not support feature number: %u\n", feature_num);
466		return;
467	}
468
469	xstate = get_xstate_info(feature_num);
470	if (!xstate.size || !xstate.xbuf_offset) {
471		ksft_exit_fail_msg("invalid state size/offset (%d/%d)\n",
472				   xstate.size, xstate.xbuf_offset);
473	}
474
475	test_context_switch(ctxtsw_num_threads, ctxtsw_iterations);
476	test_ptrace();
477	test_signal();
478}
~/ Read-only file preview.