| // SPDX-License-Identifier: GPL-2.0-only | |
| #ifndef __SELFTESTS_X86_XSTATE_H | |
| #define __SELFTESTS_X86_XSTATE_H | |
| #include <stdint.h> | |
| #include "kselftest.h" | |
| #define XSAVE_HDR_OFFSET 512 | |
| #define XSAVE_HDR_SIZE 64 | |
| /* | |
| * List of XSAVE features Linux knows about. Copied from | |
| * arch/x86/include/asm/fpu/types.h | |
| */ | |
| enum xfeature { | |
| XFEATURE_FP, | |
| XFEATURE_SSE, | |
| XFEATURE_YMM, | |
| XFEATURE_BNDREGS, | |
| XFEATURE_BNDCSR, | |
| XFEATURE_OPMASK, | |
| XFEATURE_ZMM_Hi256, | |
| XFEATURE_Hi16_ZMM, | |
| XFEATURE_PT_UNIMPLEMENTED_SO_FAR, | |
| XFEATURE_PKRU, | |
| XFEATURE_PASID, | |
| XFEATURE_CET_USER, | |
| XFEATURE_CET_KERNEL_UNUSED, | |
| XFEATURE_RSRVD_COMP_13, | |
| XFEATURE_RSRVD_COMP_14, | |
| XFEATURE_LBR, | |
| XFEATURE_RSRVD_COMP_16, | |
| XFEATURE_XTILECFG, | |
| XFEATURE_XTILEDATA, | |
| XFEATURE_APX, | |
| XFEATURE_MAX, | |
| }; | |
| /* Copied from arch/x86/kernel/fpu/xstate.c */ | |
| static const char *xfeature_names[] = | |
| { | |
| "x87 floating point registers", | |
| "SSE registers", | |
| "AVX registers", | |
| "MPX bounds registers", | |
| "MPX CSR", | |
| "AVX-512 opmask", | |
| "AVX-512 Hi256", | |
| "AVX-512 ZMM_Hi256", | |
| "Processor Trace (unused)", | |
| "Protection Keys User registers", | |
| "PASID state", | |
| "Control-flow User registers", | |
| "Control-flow Kernel registers (unused)", | |
| "unknown xstate feature", | |
| "unknown xstate feature", | |
| "unknown xstate feature", | |
| "unknown xstate feature", | |
| "AMX Tile config", | |
| "AMX Tile data", | |
| "APX registers", | |
| "unknown xstate feature", | |
| }; | |
| struct xsave_buffer { | |
| union { | |
| struct { | |
| char legacy[XSAVE_HDR_OFFSET]; | |
| char header[XSAVE_HDR_SIZE]; | |
| char extended[0]; | |
| }; | |
| char bytes[0]; | |
| }; | |
| }; | |
| static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm) | |
| { | |
| uint32_t rfbm_hi = rfbm >> 32; | |
| uint32_t rfbm_lo = rfbm; | |
| asm volatile("xsave (%%rdi)" | |
| : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi) | |
| : "memory"); | |
| } | |
| static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm) | |
| { | |
| uint32_t rfbm_hi = rfbm >> 32; | |
| uint32_t rfbm_lo = rfbm; | |
| asm volatile("xrstor (%%rdi)" | |
| : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)); | |
| } | |
| #define CPUID_LEAF_XSTATE 0xd | |
| #define CPUID_SUBLEAF_XSTATE_USER 0x0 | |
| static inline uint32_t get_xbuf_size(void) | |
| { | |
| uint32_t eax, ebx, ecx, edx; | |
| __cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER, | |
| eax, ebx, ecx, edx); | |
| /* | |
| * EBX enumerates the size (in bytes) required by the XSAVE | |
| * instruction for an XSAVE area containing all the user state | |
| * components corresponding to bits currently set in XCR0. | |
| */ | |
| return ebx; | |
| } | |
| struct xstate_info { | |
| const char *name; | |
| uint32_t num; | |
| uint32_t mask; | |
| uint32_t xbuf_offset; | |
| uint32_t size; | |
| }; | |
| static inline struct xstate_info get_xstate_info(uint32_t xfeature_num) | |
| { | |
| struct xstate_info xstate = { }; | |
| uint32_t eax, ebx, ecx, edx; | |
| if (xfeature_num >= XFEATURE_MAX) { | |
| ksft_print_msg("unknown state\n"); | |
| return xstate; | |
| } | |
| xstate.name = xfeature_names[xfeature_num]; | |
| xstate.num = xfeature_num; | |
| xstate.mask = 1 << xfeature_num; | |
| __cpuid_count(CPUID_LEAF_XSTATE, xfeature_num, | |
| eax, ebx, ecx, edx); | |
| xstate.size = eax; | |
| xstate.xbuf_offset = ebx; | |
| return xstate; | |
| } | |
| static inline struct xsave_buffer *alloc_xbuf(void) | |
| { | |
| uint32_t xbuf_size = get_xbuf_size(); | |
| /* XSAVE buffer should be 64B-aligned. */ | |
| return aligned_alloc(64, xbuf_size); | |
| } | |
| static inline void clear_xstate_header(struct xsave_buffer *xbuf) | |
| { | |
| memset(&xbuf->header, 0, sizeof(xbuf->header)); | |
| } | |
| static inline void set_xstatebv(struct xsave_buffer *xbuf, uint64_t bv) | |
| { | |
| /* XSTATE_BV is at the beginning of the header: */ | |
| *(uint64_t *)(&xbuf->header) = bv; | |
| } | |
| /* See 'struct _fpx_sw_bytes' at sigcontext.h */ | |
| #define SW_BYTES_OFFSET 464 | |
| /* N.B. The struct's field name varies so read from the offset. */ | |
| #define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8) | |
| static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *xbuf) | |
| { | |
| return xbuf + SW_BYTES_OFFSET; | |
| } | |
| static inline uint64_t get_fpx_sw_bytes_features(void *buffer) | |
| { | |
| return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET); | |
| } | |
| static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer *xbuf) | |
| { | |
| int *ptr = (int *)&xbuf->bytes[xstate->xbuf_offset]; | |
| int data, i; | |
| /* | |
| * Ensure that 'data' is never 0. This ensures that | |
| * the registers are never in their initial configuration | |
| * and thus never tracked as being in the init state. | |
| */ | |
| data = rand() | 1; | |
| for (i = 0; i < xstate->size / sizeof(int); i++, ptr++) | |
| *ptr = data; | |
| } | |
| /* Testing kernel's context switching and ABI support for the xstate. */ | |
| void test_xstate(uint32_t feature_num); | |
| #endif /* __SELFTESTS_X86_XSTATE_H */ |