| // 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 */ |