mirror of https://github.com/torvalds/linux.git
196 lines
4.3 KiB
C
196 lines
4.3 KiB
C
// 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_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",
|
|
"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 */
|