root/tools/testing/selftests/x86/xstate.h
// 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 */