mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 11:03:43 +02:00
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 <chang.seok.bae@intel.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lore.kernel.org/r/20250226010731.2456-3-chang.seok.bae@intel.com
This commit is contained in:
parent
dbd6b649e7
commit
0f6d91a327
|
|
@ -19,46 +19,13 @@
|
|||
#include <sys/wait.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "../kselftest.h" /* For __cpuid_count() */
|
||||
#include "helpers.h"
|
||||
#include "xstate.h"
|
||||
|
||||
#ifndef __x86_64__
|
||||
# error This test is 64-bit only
|
||||
#endif
|
||||
|
||||
#define XSAVE_HDR_OFFSET 512
|
||||
#define XSAVE_HDR_SIZE 64
|
||||
|
||||
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_lo = rfbm;
|
||||
uint32_t rfbm_hi = rfbm >> 32;
|
||||
|
||||
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_lo = rfbm;
|
||||
uint32_t rfbm_hi = rfbm >> 32;
|
||||
|
||||
asm volatile("xrstor (%%rdi)"
|
||||
: : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
|
||||
}
|
||||
|
||||
/* err() exits and will not return */
|
||||
#define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
|
||||
|
||||
|
|
@ -68,92 +35,12 @@ static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
|
|||
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
|
||||
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
|
||||
|
||||
#define CPUID_LEAF1_ECX_XSAVE_MASK (1 << 26)
|
||||
#define CPUID_LEAF1_ECX_OSXSAVE_MASK (1 << 27)
|
||||
|
||||
static uint32_t xbuf_size;
|
||||
|
||||
static struct {
|
||||
uint32_t xbuf_offset;
|
||||
uint32_t size;
|
||||
} xtiledata;
|
||||
|
||||
#define CPUID_LEAF_XSTATE 0xd
|
||||
#define CPUID_SUBLEAF_XSTATE_USER 0x0
|
||||
#define TILE_CPUID 0x1d
|
||||
#define TILE_PALETTE_ID 0x1
|
||||
|
||||
static void check_cpuid_xtiledata(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.
|
||||
*
|
||||
* Stash that off so it can be used to allocate buffers later.
|
||||
*/
|
||||
xbuf_size = ebx;
|
||||
|
||||
__cpuid_count(CPUID_LEAF_XSTATE, XFEATURE_XTILEDATA,
|
||||
eax, ebx, ecx, edx);
|
||||
/*
|
||||
* eax: XTILEDATA state component size
|
||||
* ebx: XTILEDATA state component offset in user buffer
|
||||
*/
|
||||
if (!eax || !ebx)
|
||||
fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
|
||||
eax, ebx);
|
||||
|
||||
xtiledata.size = eax;
|
||||
xtiledata.xbuf_offset = ebx;
|
||||
}
|
||||
struct xstate_info xtiledata;
|
||||
|
||||
/* The helpers for managing XSAVE buffer and tile states: */
|
||||
|
||||
struct xsave_buffer *alloc_xbuf(void)
|
||||
{
|
||||
struct xsave_buffer *xbuf;
|
||||
|
||||
/* XSAVE buffer should be 64B-aligned. */
|
||||
xbuf = aligned_alloc(64, xbuf_size);
|
||||
if (!xbuf)
|
||||
fatal_error("aligned_alloc()");
|
||||
return xbuf;
|
||||
}
|
||||
|
||||
static inline void clear_xstate_header(struct xsave_buffer *buffer)
|
||||
{
|
||||
memset(&buffer->header, 0, sizeof(buffer->header));
|
||||
}
|
||||
|
||||
static inline void set_xstatebv(struct xsave_buffer *buffer, uint64_t bv)
|
||||
{
|
||||
/* XSTATE_BV is at the beginning of the header: */
|
||||
*(uint64_t *)(&buffer->header) = bv;
|
||||
}
|
||||
|
||||
static void set_rand_tiledata(struct xsave_buffer *xbuf)
|
||||
{
|
||||
int *ptr = (int *)&xbuf->bytes[xtiledata.xbuf_offset];
|
||||
int data;
|
||||
int 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 < xtiledata.size / sizeof(int); i++, ptr++)
|
||||
*ptr = data;
|
||||
}
|
||||
|
||||
struct xsave_buffer *stashed_xsave;
|
||||
|
||||
static void init_stashed_xsave(void)
|
||||
|
|
@ -169,21 +56,6 @@ static void free_stashed_xsave(void)
|
|||
free(stashed_xsave);
|
||||
}
|
||||
|
||||
/* 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 *buffer)
|
||||
{
|
||||
return (struct _fpx_sw_bytes *)(buffer + SW_BYTES_OFFSET);
|
||||
}
|
||||
|
||||
static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
|
||||
{
|
||||
return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
|
||||
}
|
||||
|
||||
/* Work around printf() being unsafe in signals: */
|
||||
#define SIGNAL_BUF_LEN 1000
|
||||
char signal_message_buffer[SIGNAL_BUF_LEN];
|
||||
|
|
@ -281,7 +153,7 @@ static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
|
|||
{
|
||||
clear_xstate_header(xbuf);
|
||||
set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
|
||||
set_rand_tiledata(xbuf);
|
||||
set_rand_data(&xtiledata, xbuf);
|
||||
return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
|
||||
}
|
||||
|
||||
|
|
@ -884,7 +756,13 @@ int main(void)
|
|||
return KSFT_SKIP;
|
||||
}
|
||||
|
||||
check_cpuid_xtiledata();
|
||||
xbuf_size = get_xbuf_size();
|
||||
|
||||
xtiledata = get_xstate_info(XFEATURE_XTILEDATA);
|
||||
if (!xtiledata.size || !xtiledata.xbuf_offset) {
|
||||
fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
|
||||
xtiledata.size, xtiledata.xbuf_offset);
|
||||
}
|
||||
|
||||
init_stashed_xsave();
|
||||
sethandler(SIGILL, handle_noperm, 0);
|
||||
|
|
|
|||
132
tools/testing/selftests/x86/xstate.h
Normal file
132
tools/testing/selftests/x86/xstate.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// 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
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /* __SELFTESTS_X86_XSTATE_H */
|
||||
Loading…
Reference in New Issue
Block a user