xref: /linux/tools/testing/selftests/x86/xstate.h (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
10f6d91a3SChang S. Bae // SPDX-License-Identifier: GPL-2.0-only
20f6d91a3SChang S. Bae #ifndef __SELFTESTS_X86_XSTATE_H
30f6d91a3SChang S. Bae #define __SELFTESTS_X86_XSTATE_H
40f6d91a3SChang S. Bae 
50f6d91a3SChang S. Bae #include <stdint.h>
60f6d91a3SChang S. Bae 
70f6d91a3SChang S. Bae #include "../kselftest.h"
80f6d91a3SChang S. Bae 
90f6d91a3SChang S. Bae #define XSAVE_HDR_OFFSET	512
100f6d91a3SChang S. Bae #define XSAVE_HDR_SIZE		64
110f6d91a3SChang S. Bae 
123fcb4d61SChang S. Bae /*
133fcb4d61SChang S. Bae  * List of XSAVE features Linux knows about. Copied from
143fcb4d61SChang S. Bae  * arch/x86/include/asm/fpu/types.h
153fcb4d61SChang S. Bae  */
163fcb4d61SChang S. Bae enum xfeature {
173fcb4d61SChang S. Bae 	XFEATURE_FP,
183fcb4d61SChang S. Bae 	XFEATURE_SSE,
193fcb4d61SChang S. Bae 	XFEATURE_YMM,
203fcb4d61SChang S. Bae 	XFEATURE_BNDREGS,
213fcb4d61SChang S. Bae 	XFEATURE_BNDCSR,
223fcb4d61SChang S. Bae 	XFEATURE_OPMASK,
233fcb4d61SChang S. Bae 	XFEATURE_ZMM_Hi256,
243fcb4d61SChang S. Bae 	XFEATURE_Hi16_ZMM,
253fcb4d61SChang S. Bae 	XFEATURE_PT_UNIMPLEMENTED_SO_FAR,
263fcb4d61SChang S. Bae 	XFEATURE_PKRU,
273fcb4d61SChang S. Bae 	XFEATURE_PASID,
283fcb4d61SChang S. Bae 	XFEATURE_CET_USER,
293fcb4d61SChang S. Bae 	XFEATURE_CET_KERNEL_UNUSED,
303fcb4d61SChang S. Bae 	XFEATURE_RSRVD_COMP_13,
313fcb4d61SChang S. Bae 	XFEATURE_RSRVD_COMP_14,
323fcb4d61SChang S. Bae 	XFEATURE_LBR,
333fcb4d61SChang S. Bae 	XFEATURE_RSRVD_COMP_16,
343fcb4d61SChang S. Bae 	XFEATURE_XTILECFG,
353fcb4d61SChang S. Bae 	XFEATURE_XTILEDATA,
363fcb4d61SChang S. Bae 
373fcb4d61SChang S. Bae 	XFEATURE_MAX,
383fcb4d61SChang S. Bae };
393fcb4d61SChang S. Bae 
403fcb4d61SChang S. Bae /* Copied from arch/x86/kernel/fpu/xstate.c */
413fcb4d61SChang S. Bae static const char *xfeature_names[] =
423fcb4d61SChang S. Bae {
433fcb4d61SChang S. Bae 	"x87 floating point registers",
443fcb4d61SChang S. Bae 	"SSE registers",
453fcb4d61SChang S. Bae 	"AVX registers",
463fcb4d61SChang S. Bae 	"MPX bounds registers",
473fcb4d61SChang S. Bae 	"MPX CSR",
483fcb4d61SChang S. Bae 	"AVX-512 opmask",
493fcb4d61SChang S. Bae 	"AVX-512 Hi256",
503fcb4d61SChang S. Bae 	"AVX-512 ZMM_Hi256",
513fcb4d61SChang S. Bae 	"Processor Trace (unused)",
523fcb4d61SChang S. Bae 	"Protection Keys User registers",
533fcb4d61SChang S. Bae 	"PASID state",
543fcb4d61SChang S. Bae 	"Control-flow User registers",
553fcb4d61SChang S. Bae 	"Control-flow Kernel registers (unused)",
563fcb4d61SChang S. Bae 	"unknown xstate feature",
573fcb4d61SChang S. Bae 	"unknown xstate feature",
583fcb4d61SChang S. Bae 	"unknown xstate feature",
593fcb4d61SChang S. Bae 	"unknown xstate feature",
603fcb4d61SChang S. Bae 	"AMX Tile config",
613fcb4d61SChang S. Bae 	"AMX Tile data",
623fcb4d61SChang S. Bae 	"unknown xstate feature",
633fcb4d61SChang S. Bae };
643fcb4d61SChang S. Bae 
650f6d91a3SChang S. Bae struct xsave_buffer {
660f6d91a3SChang S. Bae 	union {
670f6d91a3SChang S. Bae 		struct {
680f6d91a3SChang S. Bae 			char legacy[XSAVE_HDR_OFFSET];
690f6d91a3SChang S. Bae 			char header[XSAVE_HDR_SIZE];
700f6d91a3SChang S. Bae 			char extended[0];
710f6d91a3SChang S. Bae 		};
720f6d91a3SChang S. Bae 		char bytes[0];
730f6d91a3SChang S. Bae 	};
740f6d91a3SChang S. Bae };
750f6d91a3SChang S. Bae 
760f6d91a3SChang S. Bae static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
770f6d91a3SChang S. Bae {
780f6d91a3SChang S. Bae 	uint32_t rfbm_hi = rfbm >> 32;
790f6d91a3SChang S. Bae 	uint32_t rfbm_lo = rfbm;
800f6d91a3SChang S. Bae 
810f6d91a3SChang S. Bae 	asm volatile("xsave (%%rdi)"
820f6d91a3SChang S. Bae 		     : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
830f6d91a3SChang S. Bae 		     : "memory");
840f6d91a3SChang S. Bae }
850f6d91a3SChang S. Bae 
860f6d91a3SChang S. Bae static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
870f6d91a3SChang S. Bae {
880f6d91a3SChang S. Bae 	uint32_t rfbm_hi = rfbm >> 32;
890f6d91a3SChang S. Bae 	uint32_t rfbm_lo = rfbm;
900f6d91a3SChang S. Bae 
910f6d91a3SChang S. Bae 	asm volatile("xrstor (%%rdi)"
920f6d91a3SChang S. Bae 		     : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
930f6d91a3SChang S. Bae }
940f6d91a3SChang S. Bae 
950f6d91a3SChang S. Bae #define CPUID_LEAF_XSTATE		0xd
960f6d91a3SChang S. Bae #define CPUID_SUBLEAF_XSTATE_USER	0x0
970f6d91a3SChang S. Bae 
980f6d91a3SChang S. Bae static inline uint32_t get_xbuf_size(void)
990f6d91a3SChang S. Bae {
1000f6d91a3SChang S. Bae 	uint32_t eax, ebx, ecx, edx;
1010f6d91a3SChang S. Bae 
1020f6d91a3SChang S. Bae 	__cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
1030f6d91a3SChang S. Bae 		      eax, ebx, ecx, edx);
1040f6d91a3SChang S. Bae 
1050f6d91a3SChang S. Bae 	/*
1060f6d91a3SChang S. Bae 	 * EBX enumerates the size (in bytes) required by the XSAVE
1070f6d91a3SChang S. Bae 	 * instruction for an XSAVE area containing all the user state
1080f6d91a3SChang S. Bae 	 * components corresponding to bits currently set in XCR0.
1090f6d91a3SChang S. Bae 	 */
1100f6d91a3SChang S. Bae 	return ebx;
1110f6d91a3SChang S. Bae }
1120f6d91a3SChang S. Bae 
1130f6d91a3SChang S. Bae struct xstate_info {
1143fcb4d61SChang S. Bae 	const char *name;
1150f6d91a3SChang S. Bae 	uint32_t num;
1160f6d91a3SChang S. Bae 	uint32_t mask;
1170f6d91a3SChang S. Bae 	uint32_t xbuf_offset;
1180f6d91a3SChang S. Bae 	uint32_t size;
1190f6d91a3SChang S. Bae };
1200f6d91a3SChang S. Bae 
1210f6d91a3SChang S. Bae static inline struct xstate_info get_xstate_info(uint32_t xfeature_num)
1220f6d91a3SChang S. Bae {
1230f6d91a3SChang S. Bae 	struct xstate_info xstate = { };
1240f6d91a3SChang S. Bae 	uint32_t eax, ebx, ecx, edx;
1250f6d91a3SChang S. Bae 
1263fcb4d61SChang S. Bae 	if (xfeature_num >= XFEATURE_MAX) {
1273fcb4d61SChang S. Bae 		ksft_print_msg("unknown state\n");
1283fcb4d61SChang S. Bae 		return xstate;
1293fcb4d61SChang S. Bae 	}
1303fcb4d61SChang S. Bae 
1313fcb4d61SChang S. Bae 	xstate.name = xfeature_names[xfeature_num];
1320f6d91a3SChang S. Bae 	xstate.num  = xfeature_num;
1330f6d91a3SChang S. Bae 	xstate.mask = 1 << xfeature_num;
1340f6d91a3SChang S. Bae 
1350f6d91a3SChang S. Bae 	__cpuid_count(CPUID_LEAF_XSTATE, xfeature_num,
1360f6d91a3SChang S. Bae 		      eax, ebx, ecx, edx);
1370f6d91a3SChang S. Bae 	xstate.size        = eax;
1380f6d91a3SChang S. Bae 	xstate.xbuf_offset = ebx;
1390f6d91a3SChang S. Bae 	return xstate;
1400f6d91a3SChang S. Bae }
1410f6d91a3SChang S. Bae 
1420f6d91a3SChang S. Bae static inline struct xsave_buffer *alloc_xbuf(void)
1430f6d91a3SChang S. Bae {
1440f6d91a3SChang S. Bae 	uint32_t xbuf_size = get_xbuf_size();
1450f6d91a3SChang S. Bae 
1460f6d91a3SChang S. Bae 	/* XSAVE buffer should be 64B-aligned. */
1470f6d91a3SChang S. Bae 	return aligned_alloc(64, xbuf_size);
1480f6d91a3SChang S. Bae }
1490f6d91a3SChang S. Bae 
1500f6d91a3SChang S. Bae static inline void clear_xstate_header(struct xsave_buffer *xbuf)
1510f6d91a3SChang S. Bae {
1520f6d91a3SChang S. Bae 	memset(&xbuf->header, 0, sizeof(xbuf->header));
1530f6d91a3SChang S. Bae }
1540f6d91a3SChang S. Bae 
1550f6d91a3SChang S. Bae static inline void set_xstatebv(struct xsave_buffer *xbuf, uint64_t bv)
1560f6d91a3SChang S. Bae {
1570f6d91a3SChang S. Bae 	/* XSTATE_BV is at the beginning of the header: */
1580f6d91a3SChang S. Bae 	*(uint64_t *)(&xbuf->header) = bv;
1590f6d91a3SChang S. Bae }
1600f6d91a3SChang S. Bae 
1610f6d91a3SChang S. Bae /* See 'struct _fpx_sw_bytes' at sigcontext.h */
1620f6d91a3SChang S. Bae #define SW_BYTES_OFFSET		464
1630f6d91a3SChang S. Bae /* N.B. The struct's field name varies so read from the offset. */
1640f6d91a3SChang S. Bae #define SW_BYTES_BV_OFFSET	(SW_BYTES_OFFSET + 8)
1650f6d91a3SChang S. Bae 
1660f6d91a3SChang S. Bae static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *xbuf)
1670f6d91a3SChang S. Bae {
1680f6d91a3SChang S. Bae 	return xbuf + SW_BYTES_OFFSET;
1690f6d91a3SChang S. Bae }
1700f6d91a3SChang S. Bae 
1710f6d91a3SChang S. Bae static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
1720f6d91a3SChang S. Bae {
1730f6d91a3SChang S. Bae 	return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
1740f6d91a3SChang S. Bae }
1750f6d91a3SChang S. Bae 
1760f6d91a3SChang S. Bae static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer *xbuf)
1770f6d91a3SChang S. Bae {
1780f6d91a3SChang S. Bae 	int *ptr = (int *)&xbuf->bytes[xstate->xbuf_offset];
1790f6d91a3SChang S. Bae 	int data, i;
1800f6d91a3SChang S. Bae 
1810f6d91a3SChang S. Bae 	/*
1820f6d91a3SChang S. Bae 	 * Ensure that 'data' is never 0.  This ensures that
1830f6d91a3SChang S. Bae 	 * the registers are never in their initial configuration
1840f6d91a3SChang S. Bae 	 * and thus never tracked as being in the init state.
1850f6d91a3SChang S. Bae 	 */
1860f6d91a3SChang S. Bae 	data = rand() | 1;
1870f6d91a3SChang S. Bae 
1880f6d91a3SChang S. Bae 	for (i = 0; i < xstate->size / sizeof(int); i++, ptr++)
1890f6d91a3SChang S. Bae 		*ptr = data;
1900f6d91a3SChang S. Bae }
1910f6d91a3SChang S. Bae 
192*10d8a204SChang S. Bae /* Testing kernel's context switching and ABI support for the xstate. */
193*10d8a204SChang S. Bae void test_xstate(uint32_t feature_num);
19440f6852eSChang S. Bae 
1950f6d91a3SChang S. Bae #endif /* __SELFTESTS_X86_XSTATE_H */
196