Skip to content

Commit 0f6d91a

Browse files
ChangSeokBaeIngo Molnar
authored andcommitted
selftests/x86/xstate: Refactor XSAVE helpers for general use
The AMX test introduced several XSAVE-related helper functions, but so far, it has been the only user of them. These helpers can be generalized for broader test of multiple xstate features. Move most XSAVE-related code into xsave.h, making it shareable. The restructuring includes: * Establishing low-level XSAVE helpers for saving and restoring register states, as well as handling XSAVE buffers. * Generalizing state data manipuldations: set_rand_data() * Introducing a generic feature query helper: get_xstate_info() While doing so, remove unused defines in amx.c. Signed-off-by: Chang S. Bae <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent dbd6b64 commit 0f6d91a

File tree

2 files changed

+142
-132
lines changed

2 files changed

+142
-132
lines changed

tools/testing/selftests/x86/amx.c

Lines changed: 10 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -19,46 +19,13 @@
1919
#include <sys/wait.h>
2020
#include <sys/uio.h>
2121

22-
#include "../kselftest.h" /* For __cpuid_count() */
2322
#include "helpers.h"
23+
#include "xstate.h"
2424

2525
#ifndef __x86_64__
2626
# error This test is 64-bit only
2727
#endif
2828

29-
#define XSAVE_HDR_OFFSET 512
30-
#define XSAVE_HDR_SIZE 64
31-
32-
struct xsave_buffer {
33-
union {
34-
struct {
35-
char legacy[XSAVE_HDR_OFFSET];
36-
char header[XSAVE_HDR_SIZE];
37-
char extended[0];
38-
};
39-
char bytes[0];
40-
};
41-
};
42-
43-
static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
44-
{
45-
uint32_t rfbm_lo = rfbm;
46-
uint32_t rfbm_hi = rfbm >> 32;
47-
48-
asm volatile("xsave (%%rdi)"
49-
: : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
50-
: "memory");
51-
}
52-
53-
static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
54-
{
55-
uint32_t rfbm_lo = rfbm;
56-
uint32_t rfbm_hi = rfbm >> 32;
57-
58-
asm volatile("xrstor (%%rdi)"
59-
: : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
60-
}
61-
6229
/* err() exits and will not return */
6330
#define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
6431

@@ -68,92 +35,12 @@ static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
6835
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
6936
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
7037

71-
#define CPUID_LEAF1_ECX_XSAVE_MASK (1 << 26)
72-
#define CPUID_LEAF1_ECX_OSXSAVE_MASK (1 << 27)
73-
7438
static uint32_t xbuf_size;
7539

76-
static struct {
77-
uint32_t xbuf_offset;
78-
uint32_t size;
79-
} xtiledata;
80-
81-
#define CPUID_LEAF_XSTATE 0xd
82-
#define CPUID_SUBLEAF_XSTATE_USER 0x0
83-
#define TILE_CPUID 0x1d
84-
#define TILE_PALETTE_ID 0x1
85-
86-
static void check_cpuid_xtiledata(void)
87-
{
88-
uint32_t eax, ebx, ecx, edx;
89-
90-
__cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
91-
eax, ebx, ecx, edx);
92-
93-
/*
94-
* EBX enumerates the size (in bytes) required by the XSAVE
95-
* instruction for an XSAVE area containing all the user state
96-
* components corresponding to bits currently set in XCR0.
97-
*
98-
* Stash that off so it can be used to allocate buffers later.
99-
*/
100-
xbuf_size = ebx;
101-
102-
__cpuid_count(CPUID_LEAF_XSTATE, XFEATURE_XTILEDATA,
103-
eax, ebx, ecx, edx);
104-
/*
105-
* eax: XTILEDATA state component size
106-
* ebx: XTILEDATA state component offset in user buffer
107-
*/
108-
if (!eax || !ebx)
109-
fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
110-
eax, ebx);
111-
112-
xtiledata.size = eax;
113-
xtiledata.xbuf_offset = ebx;
114-
}
40+
struct xstate_info xtiledata;
11541

11642
/* The helpers for managing XSAVE buffer and tile states: */
11743

118-
struct xsave_buffer *alloc_xbuf(void)
119-
{
120-
struct xsave_buffer *xbuf;
121-
122-
/* XSAVE buffer should be 64B-aligned. */
123-
xbuf = aligned_alloc(64, xbuf_size);
124-
if (!xbuf)
125-
fatal_error("aligned_alloc()");
126-
return xbuf;
127-
}
128-
129-
static inline void clear_xstate_header(struct xsave_buffer *buffer)
130-
{
131-
memset(&buffer->header, 0, sizeof(buffer->header));
132-
}
133-
134-
static inline void set_xstatebv(struct xsave_buffer *buffer, uint64_t bv)
135-
{
136-
/* XSTATE_BV is at the beginning of the header: */
137-
*(uint64_t *)(&buffer->header) = bv;
138-
}
139-
140-
static void set_rand_tiledata(struct xsave_buffer *xbuf)
141-
{
142-
int *ptr = (int *)&xbuf->bytes[xtiledata.xbuf_offset];
143-
int data;
144-
int i;
145-
146-
/*
147-
* Ensure that 'data' is never 0. This ensures that
148-
* the registers are never in their initial configuration
149-
* and thus never tracked as being in the init state.
150-
*/
151-
data = rand() | 1;
152-
153-
for (i = 0; i < xtiledata.size / sizeof(int); i++, ptr++)
154-
*ptr = data;
155-
}
156-
15744
struct xsave_buffer *stashed_xsave;
15845

15946
static void init_stashed_xsave(void)
@@ -169,21 +56,6 @@ static void free_stashed_xsave(void)
16956
free(stashed_xsave);
17057
}
17158

172-
/* See 'struct _fpx_sw_bytes' at sigcontext.h */
173-
#define SW_BYTES_OFFSET 464
174-
/* N.B. The struct's field name varies so read from the offset. */
175-
#define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8)
176-
177-
static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *buffer)
178-
{
179-
return (struct _fpx_sw_bytes *)(buffer + SW_BYTES_OFFSET);
180-
}
181-
182-
static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
183-
{
184-
return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
185-
}
186-
18759
/* Work around printf() being unsafe in signals: */
18860
#define SIGNAL_BUF_LEN 1000
18961
char signal_message_buffer[SIGNAL_BUF_LEN];
@@ -281,7 +153,7 @@ static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
281153
{
282154
clear_xstate_header(xbuf);
283155
set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
284-
set_rand_tiledata(xbuf);
156+
set_rand_data(&xtiledata, xbuf);
285157
return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
286158
}
287159

@@ -884,7 +756,13 @@ int main(void)
884756
return KSFT_SKIP;
885757
}
886758

887-
check_cpuid_xtiledata();
759+
xbuf_size = get_xbuf_size();
760+
761+
xtiledata = get_xstate_info(XFEATURE_XTILEDATA);
762+
if (!xtiledata.size || !xtiledata.xbuf_offset) {
763+
fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
764+
xtiledata.size, xtiledata.xbuf_offset);
765+
}
888766

889767
init_stashed_xsave();
890768
sethandler(SIGILL, handle_noperm, 0);

tools/testing/selftests/x86/xstate.h

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
7+
#include "../kselftest.h"
8+
9+
#define XSAVE_HDR_OFFSET 512
10+
#define XSAVE_HDR_SIZE 64
11+
12+
struct xsave_buffer {
13+
union {
14+
struct {
15+
char legacy[XSAVE_HDR_OFFSET];
16+
char header[XSAVE_HDR_SIZE];
17+
char extended[0];
18+
};
19+
char bytes[0];
20+
};
21+
};
22+
23+
static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
24+
{
25+
uint32_t rfbm_hi = rfbm >> 32;
26+
uint32_t rfbm_lo = rfbm;
27+
28+
asm volatile("xsave (%%rdi)"
29+
: : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
30+
: "memory");
31+
}
32+
33+
static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
34+
{
35+
uint32_t rfbm_hi = rfbm >> 32;
36+
uint32_t rfbm_lo = rfbm;
37+
38+
asm volatile("xrstor (%%rdi)"
39+
: : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
40+
}
41+
42+
#define CPUID_LEAF_XSTATE 0xd
43+
#define CPUID_SUBLEAF_XSTATE_USER 0x0
44+
45+
static inline uint32_t get_xbuf_size(void)
46+
{
47+
uint32_t eax, ebx, ecx, edx;
48+
49+
__cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
50+
eax, ebx, ecx, edx);
51+
52+
/*
53+
* EBX enumerates the size (in bytes) required by the XSAVE
54+
* instruction for an XSAVE area containing all the user state
55+
* components corresponding to bits currently set in XCR0.
56+
*/
57+
return ebx;
58+
}
59+
60+
struct xstate_info {
61+
uint32_t num;
62+
uint32_t mask;
63+
uint32_t xbuf_offset;
64+
uint32_t size;
65+
};
66+
67+
static inline struct xstate_info get_xstate_info(uint32_t xfeature_num)
68+
{
69+
struct xstate_info xstate = { };
70+
uint32_t eax, ebx, ecx, edx;
71+
72+
xstate.num = xfeature_num;
73+
xstate.mask = 1 << xfeature_num;
74+
75+
__cpuid_count(CPUID_LEAF_XSTATE, xfeature_num,
76+
eax, ebx, ecx, edx);
77+
xstate.size = eax;
78+
xstate.xbuf_offset = ebx;
79+
return xstate;
80+
}
81+
82+
static inline struct xsave_buffer *alloc_xbuf(void)
83+
{
84+
uint32_t xbuf_size = get_xbuf_size();
85+
86+
/* XSAVE buffer should be 64B-aligned. */
87+
return aligned_alloc(64, xbuf_size);
88+
}
89+
90+
static inline void clear_xstate_header(struct xsave_buffer *xbuf)
91+
{
92+
memset(&xbuf->header, 0, sizeof(xbuf->header));
93+
}
94+
95+
static inline void set_xstatebv(struct xsave_buffer *xbuf, uint64_t bv)
96+
{
97+
/* XSTATE_BV is at the beginning of the header: */
98+
*(uint64_t *)(&xbuf->header) = bv;
99+
}
100+
101+
/* See 'struct _fpx_sw_bytes' at sigcontext.h */
102+
#define SW_BYTES_OFFSET 464
103+
/* N.B. The struct's field name varies so read from the offset. */
104+
#define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8)
105+
106+
static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *xbuf)
107+
{
108+
return xbuf + SW_BYTES_OFFSET;
109+
}
110+
111+
static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
112+
{
113+
return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
114+
}
115+
116+
static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer *xbuf)
117+
{
118+
int *ptr = (int *)&xbuf->bytes[xstate->xbuf_offset];
119+
int data, i;
120+
121+
/*
122+
* Ensure that 'data' is never 0. This ensures that
123+
* the registers are never in their initial configuration
124+
* and thus never tracked as being in the init state.
125+
*/
126+
data = rand() | 1;
127+
128+
for (i = 0; i < xstate->size / sizeof(int); i++, ptr++)
129+
*ptr = data;
130+
}
131+
132+
#endif /* __SELFTESTS_X86_XSTATE_H */

0 commit comments

Comments
 (0)