1 // SPDX-License-Identifier: GPL-2.0-only
2 #ifndef __SELFTESTS_X86_XSTATE_H
3 #define __SELFTESTS_X86_XSTATE_H
4
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "kselftest.h"
10
11 #define XSAVE_HDR_OFFSET 512
12 #define XSAVE_HDR_SIZE 64
13
14 /*
15 * List of XSAVE features Linux knows about. Copied from
16 * arch/x86/include/asm/fpu/types.h
17 */
18 enum xfeature {
19 XFEATURE_FP,
20 XFEATURE_SSE,
21 XFEATURE_YMM,
22 XFEATURE_BNDREGS,
23 XFEATURE_BNDCSR,
24 XFEATURE_OPMASK,
25 XFEATURE_ZMM_Hi256,
26 XFEATURE_Hi16_ZMM,
27 XFEATURE_PT_UNIMPLEMENTED_SO_FAR,
28 XFEATURE_PKRU,
29 XFEATURE_PASID,
30 XFEATURE_CET_USER,
31 XFEATURE_CET_KERNEL_UNUSED,
32 XFEATURE_RSRVD_COMP_13,
33 XFEATURE_RSRVD_COMP_14,
34 XFEATURE_LBR,
35 XFEATURE_RSRVD_COMP_16,
36 XFEATURE_XTILECFG,
37 XFEATURE_XTILEDATA,
38 XFEATURE_APX,
39
40 XFEATURE_MAX,
41 };
42
43 /* Copied from arch/x86/kernel/fpu/xstate.c */
44 static const char *xfeature_names[] =
45 {
46 "x87 floating point registers",
47 "SSE registers",
48 "AVX registers",
49 "MPX bounds registers",
50 "MPX CSR",
51 "AVX-512 opmask",
52 "AVX-512 Hi256",
53 "AVX-512 ZMM_Hi256",
54 "Processor Trace (unused)",
55 "Protection Keys User registers",
56 "PASID state",
57 "Control-flow User registers",
58 "Control-flow Kernel registers (unused)",
59 "unknown xstate feature",
60 "unknown xstate feature",
61 "unknown xstate feature",
62 "unknown xstate feature",
63 "AMX Tile config",
64 "AMX Tile data",
65 "APX registers",
66 "unknown xstate feature",
67 };
68
69 struct xsave_buffer {
70 union {
71 struct {
72 char legacy[XSAVE_HDR_OFFSET];
73 char header[XSAVE_HDR_SIZE];
74 char extended[0];
75 };
76 char bytes[0];
77 };
78 };
79
xsave(struct xsave_buffer * xbuf,uint64_t rfbm)80 static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
81 {
82 uint32_t rfbm_hi = rfbm >> 32;
83 uint32_t rfbm_lo = rfbm;
84
85 asm volatile("xsave (%%rdi)"
86 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
87 : "memory");
88 }
89
xrstor(struct xsave_buffer * xbuf,uint64_t rfbm)90 static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
91 {
92 uint32_t rfbm_hi = rfbm >> 32;
93 uint32_t rfbm_lo = rfbm;
94
95 asm volatile("xrstor (%%rdi)"
96 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
97 }
98
xgetbv(uint32_t index)99 static inline uint64_t xgetbv(uint32_t index)
100 {
101 uint32_t eax, edx;
102
103 asm volatile("xgetbv" : "=a" (eax), "=d" (edx) : "c" (index));
104 return eax + ((uint64_t)edx << 32);
105 }
106
107 #define CPUID_LEAF_XSTATE 0xd
108 #define CPUID_SUBLEAF_XSTATE_USER 0x0
109
get_xbuf_size(void)110 static inline uint32_t get_xbuf_size(void)
111 {
112 uint32_t eax, ebx, ecx, edx;
113
114 __cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
115 eax, ebx, ecx, edx);
116
117 /*
118 * EBX enumerates the size (in bytes) required by the XSAVE
119 * instruction for an XSAVE area containing all the user state
120 * components corresponding to bits currently set in XCR0.
121 */
122 return ebx;
123 }
124
125 struct xstate_info {
126 const char *name;
127 uint32_t num;
128 uint32_t mask;
129 uint32_t xbuf_offset;
130 uint32_t size;
131 };
132
get_xstate_info(uint32_t xfeature_num)133 static inline struct xstate_info get_xstate_info(uint32_t xfeature_num)
134 {
135 struct xstate_info xstate = { };
136 uint32_t eax, ebx, ecx, edx;
137
138 if (xfeature_num >= XFEATURE_MAX) {
139 ksft_print_msg("unknown state\n");
140 return xstate;
141 }
142
143 xstate.name = xfeature_names[xfeature_num];
144 xstate.num = xfeature_num;
145 xstate.mask = 1 << xfeature_num;
146
147 __cpuid_count(CPUID_LEAF_XSTATE, xfeature_num,
148 eax, ebx, ecx, edx);
149 xstate.size = eax;
150 xstate.xbuf_offset = ebx;
151 return xstate;
152 }
153
alloc_xbuf(void)154 static inline struct xsave_buffer *alloc_xbuf(void)
155 {
156 uint32_t xbuf_size = get_xbuf_size();
157
158 /* XSAVE buffer should be 64B-aligned. */
159 return aligned_alloc(64, xbuf_size);
160 }
161
clear_xstate_header(struct xsave_buffer * xbuf)162 static inline void clear_xstate_header(struct xsave_buffer *xbuf)
163 {
164 memset(&xbuf->header, 0, sizeof(xbuf->header));
165 }
166
set_xstatebv(struct xsave_buffer * xbuf,uint64_t bv)167 static inline void set_xstatebv(struct xsave_buffer *xbuf, uint64_t bv)
168 {
169 /* XSTATE_BV is at the beginning of the header: */
170 *(uint64_t *)(&xbuf->header) = bv;
171 }
172
get_xstatebv(struct xsave_buffer * xbuf)173 static inline uint64_t get_xstatebv(struct xsave_buffer *xbuf)
174 {
175 return *(uint64_t *)(&xbuf->header);
176 }
177
178 /* See 'struct _fpx_sw_bytes' at sigcontext.h */
179 #define SW_BYTES_OFFSET 464
180 /* N.B. The struct's field name varies so read from the offset. */
181 #define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8)
182
get_fpx_sw_bytes(void * xbuf)183 static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *xbuf)
184 {
185 return xbuf + SW_BYTES_OFFSET;
186 }
187
get_fpx_sw_bytes_features(void * buffer)188 static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
189 {
190 return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
191 }
192
set_fpx_sw_bytes_features(void * buffer,uint64_t features)193 static inline void set_fpx_sw_bytes_features(void *buffer, uint64_t features)
194 {
195 *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET) = features;
196 }
197
set_rand_data(struct xstate_info * xstate,struct xsave_buffer * xbuf)198 static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer *xbuf)
199 {
200 int *ptr = (int *)&xbuf->bytes[xstate->xbuf_offset];
201 int data, i;
202
203 /*
204 * Ensure that 'data' is never 0. This ensures that
205 * the registers are never in their initial configuration
206 * and thus never tracked as being in the init state.
207 */
208 data = rand() | 1;
209
210 for (i = 0; i < xstate->size / sizeof(int); i++, ptr++)
211 *ptr = data;
212 }
213
214 /* Testing kernel's context switching and ABI support for the xstate. */
215 void test_xstate(uint32_t feature_num);
216
217 #endif /* __SELFTESTS_X86_XSTATE_H */
218