Merge tag 'kvm-x86-selftests-6.4' of https://github.com/kvm-x86/linux into HEAD
KVM selftests, and an AMX/XCR0 bugfix, for 6.4: - Don't advertise XTILE_CFG in KVM_GET_SUPPORTED_CPUID if XTILE_DATA is not being reported due to userspace not opting in via prctl() - Overhaul the AMX selftests to improve coverage and cleanup the test - Misc cleanups
This commit is contained in:
@@ -996,7 +996,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
|
||||
entry->eax = entry->ebx = entry->ecx = 0;
|
||||
break;
|
||||
case 0xd: {
|
||||
u64 permitted_xcr0 = kvm_caps.supported_xcr0 & xstate_get_guest_group_perm();
|
||||
u64 permitted_xcr0 = kvm_get_filtered_xcr0();
|
||||
u64 permitted_xss = kvm_caps.supported_xss;
|
||||
|
||||
entry->eax &= permitted_xcr0;
|
||||
|
||||
+1
-3
@@ -4593,9 +4593,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
r = 0;
|
||||
break;
|
||||
case KVM_CAP_XSAVE2: {
|
||||
u64 guest_perm = xstate_get_guest_group_perm();
|
||||
|
||||
r = xstate_required_size(kvm_caps.supported_xcr0 & guest_perm, false);
|
||||
r = xstate_required_size(kvm_get_filtered_xcr0(), false);
|
||||
if (r < sizeof(struct kvm_xsave))
|
||||
r = sizeof(struct kvm_xsave);
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define ARCH_X86_KVM_X86_H
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/fpu/xstate.h>
|
||||
#include <asm/mce.h>
|
||||
#include <asm/pvclock.h>
|
||||
#include "kvm_cache_regs.h"
|
||||
@@ -328,6 +329,34 @@ extern struct kvm_caps kvm_caps;
|
||||
|
||||
extern bool enable_pmu;
|
||||
|
||||
/*
|
||||
* Get a filtered version of KVM's supported XCR0 that strips out dynamic
|
||||
* features for which the current process doesn't (yet) have permission to use.
|
||||
* This is intended to be used only when enumerating support to userspace,
|
||||
* e.g. in KVM_GET_SUPPORTED_CPUID and KVM_CAP_XSAVE2, it does NOT need to be
|
||||
* used to check/restrict guest behavior as KVM rejects KVM_SET_CPUID{2} if
|
||||
* userspace attempts to enable unpermitted features.
|
||||
*/
|
||||
static inline u64 kvm_get_filtered_xcr0(void)
|
||||
{
|
||||
u64 permitted_xcr0 = kvm_caps.supported_xcr0;
|
||||
|
||||
BUILD_BUG_ON(XFEATURE_MASK_USER_DYNAMIC != XFEATURE_MASK_XTILE_DATA);
|
||||
|
||||
if (permitted_xcr0 & XFEATURE_MASK_USER_DYNAMIC) {
|
||||
permitted_xcr0 &= xstate_get_guest_group_perm();
|
||||
|
||||
/*
|
||||
* Treat XTILE_CFG as unsupported if the current process isn't
|
||||
* allowed to use XTILE_DATA, as attempting to set XTILE_CFG in
|
||||
* XCR0 without setting XTILE_DATA is architecturally illegal.
|
||||
*/
|
||||
if (!(permitted_xcr0 & XFEATURE_MASK_XTILE_DATA))
|
||||
permitted_xcr0 &= ~XFEATURE_MASK_XTILE_CFG;
|
||||
}
|
||||
return permitted_xcr0;
|
||||
}
|
||||
|
||||
static inline bool kvm_mpx_supported(void)
|
||||
{
|
||||
return (kvm_caps.supported_xcr0 & (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR))
|
||||
|
||||
@@ -105,6 +105,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
|
||||
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
|
||||
|
||||
@@ -194,7 +194,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
|
||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||
pr_info("Overall demand paging rate: %f pgs/sec\n",
|
||||
memstress_args.vcpu_args[0].pages * nr_vcpus /
|
||||
((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
||||
((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / NSEC_PER_SEC));
|
||||
|
||||
memstress_destroy_vm(vm);
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ extern const struct vm_guest_mode_params vm_guest_mode_params[];
|
||||
int open_path_or_exit(const char *path, int flags);
|
||||
int open_kvm_dev_path_or_exit(void);
|
||||
|
||||
bool get_kvm_param_bool(const char *param);
|
||||
bool get_kvm_intel_param_bool(const char *param);
|
||||
bool get_kvm_amd_param_bool(const char *param);
|
||||
|
||||
|
||||
@@ -48,6 +48,35 @@ extern bool host_cpu_is_amd;
|
||||
#define X86_CR4_SMAP (1ul << 21)
|
||||
#define X86_CR4_PKE (1ul << 22)
|
||||
|
||||
struct xstate_header {
|
||||
u64 xstate_bv;
|
||||
u64 xcomp_bv;
|
||||
u64 reserved[6];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct xstate {
|
||||
u8 i387[512];
|
||||
struct xstate_header header;
|
||||
u8 extended_state_area[0];
|
||||
} __attribute__ ((packed, aligned (64)));
|
||||
|
||||
#define XFEATURE_MASK_FP BIT_ULL(0)
|
||||
#define XFEATURE_MASK_SSE BIT_ULL(1)
|
||||
#define XFEATURE_MASK_YMM BIT_ULL(2)
|
||||
#define XFEATURE_MASK_BNDREGS BIT_ULL(3)
|
||||
#define XFEATURE_MASK_BNDCSR BIT_ULL(4)
|
||||
#define XFEATURE_MASK_OPMASK BIT_ULL(5)
|
||||
#define XFEATURE_MASK_ZMM_Hi256 BIT_ULL(6)
|
||||
#define XFEATURE_MASK_Hi16_ZMM BIT_ULL(7)
|
||||
#define XFEATURE_MASK_XTILE_CFG BIT_ULL(17)
|
||||
#define XFEATURE_MASK_XTILE_DATA BIT_ULL(18)
|
||||
|
||||
#define XFEATURE_MASK_AVX512 (XFEATURE_MASK_OPMASK | \
|
||||
XFEATURE_MASK_ZMM_Hi256 | \
|
||||
XFEATURE_MASK_Hi16_ZMM)
|
||||
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILE_DATA | \
|
||||
XFEATURE_MASK_XTILE_CFG)
|
||||
|
||||
/* Note, these are ordered alphabetically to match kvm_cpuid_entry2. Eww. */
|
||||
enum cpuid_output_regs {
|
||||
KVM_CPUID_EAX,
|
||||
@@ -131,6 +160,7 @@ struct kvm_x86_cpu_feature {
|
||||
#define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18)
|
||||
#define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3)
|
||||
#define X86_FEATURE_XFD KVM_X86_CPU_FEATURE(0xD, 1, EAX, 4)
|
||||
#define X86_FEATURE_XTILEDATA_XFD KVM_X86_CPU_FEATURE(0xD, 18, ECX, 2)
|
||||
|
||||
/*
|
||||
* Extended Leafs, a.k.a. AMD defined
|
||||
@@ -211,10 +241,14 @@ struct kvm_x86_cpu_property {
|
||||
#define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15)
|
||||
#define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31)
|
||||
|
||||
#define X86_PROPERTY_SUPPORTED_XCR0_LO KVM_X86_CPU_PROPERTY(0xd, 0, EAX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_MAX_SIZE_XCR0 KVM_X86_CPU_PROPERTY(0xd, 0, EBX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_MAX_SIZE KVM_X86_CPU_PROPERTY(0xd, 0, ECX, 0, 31)
|
||||
#define X86_PROPERTY_SUPPORTED_XCR0_HI KVM_X86_CPU_PROPERTY(0xd, 0, EDX, 0, 31)
|
||||
|
||||
#define X86_PROPERTY_XSTATE_TILE_SIZE KVM_X86_CPU_PROPERTY(0xd, 18, EAX, 0, 31)
|
||||
#define X86_PROPERTY_XSTATE_TILE_OFFSET KVM_X86_CPU_PROPERTY(0xd, 18, EBX, 0, 31)
|
||||
#define X86_PROPERTY_AMX_MAX_PALETTE_TABLES KVM_X86_CPU_PROPERTY(0x1d, 0, EAX, 0, 31)
|
||||
#define X86_PROPERTY_AMX_TOTAL_TILE_BYTES KVM_X86_CPU_PROPERTY(0x1d, 1, EAX, 0, 15)
|
||||
#define X86_PROPERTY_AMX_BYTES_PER_TILE KVM_X86_CPU_PROPERTY(0x1d, 1, EAX, 16, 31)
|
||||
#define X86_PROPERTY_AMX_BYTES_PER_ROW KVM_X86_CPU_PROPERTY(0x1d, 1, EBX, 0, 15)
|
||||
@@ -496,6 +530,24 @@ static inline void set_cr4(uint64_t val)
|
||||
__asm__ __volatile__("mov %0, %%cr4" : : "r" (val) : "memory");
|
||||
}
|
||||
|
||||
static inline u64 xgetbv(u32 index)
|
||||
{
|
||||
u32 eax, edx;
|
||||
|
||||
__asm__ __volatile__("xgetbv;"
|
||||
: "=a" (eax), "=d" (edx)
|
||||
: "c" (index));
|
||||
return eax | ((u64)edx << 32);
|
||||
}
|
||||
|
||||
static inline void xsetbv(u32 index, u64 value)
|
||||
{
|
||||
u32 eax = value;
|
||||
u32 edx = value >> 32;
|
||||
|
||||
__asm__ __volatile__("xsetbv" :: "a" (eax), "d" (edx), "c" (index));
|
||||
}
|
||||
|
||||
static inline struct desc_ptr get_gdt(void)
|
||||
{
|
||||
struct desc_ptr gdt;
|
||||
@@ -632,6 +684,15 @@ static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature)
|
||||
!this_cpu_has(feature.anti_feature);
|
||||
}
|
||||
|
||||
static __always_inline uint64_t this_cpu_supported_xcr0(void)
|
||||
{
|
||||
if (!this_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO))
|
||||
return 0;
|
||||
|
||||
return this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) |
|
||||
((uint64_t)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32);
|
||||
}
|
||||
|
||||
typedef u32 __attribute__((vector_size(16))) sse128_t;
|
||||
#define __sse128_u union { sse128_t vec; u64 as_u64[2]; u32 as_u32[4]; }
|
||||
#define sse128_lo(x) ({ __sse128_u t; t.vec = x; t.as_u64[0]; })
|
||||
@@ -1086,6 +1147,14 @@ static inline uint8_t wrmsr_safe(uint32_t msr, uint64_t val)
|
||||
return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr));
|
||||
}
|
||||
|
||||
static inline uint8_t xsetbv_safe(uint32_t index, uint64_t value)
|
||||
{
|
||||
u32 eax = value;
|
||||
u32 edx = value >> 32;
|
||||
|
||||
return kvm_asm_safe("xsetbv", "a" (eax), "d" (edx), "c" (index));
|
||||
}
|
||||
|
||||
bool kvm_is_tdp_enabled(void);
|
||||
|
||||
uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr,
|
||||
@@ -1097,10 +1166,10 @@ uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
|
||||
void xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
|
||||
|
||||
void __vm_xsave_require_permission(int bit, const char *name);
|
||||
void __vm_xsave_require_permission(uint64_t xfeature, const char *name);
|
||||
|
||||
#define vm_xsave_require_permission(perm) \
|
||||
__vm_xsave_require_permission(perm, #perm)
|
||||
#define vm_xsave_require_permission(xfeature) \
|
||||
__vm_xsave_require_permission(xfeature, #xfeature)
|
||||
|
||||
enum pg_level {
|
||||
PG_LEVEL_NONE,
|
||||
@@ -1137,14 +1206,6 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
|
||||
#define X86_CR0_CD (1UL<<30) /* Cache Disable */
|
||||
#define X86_CR0_PG (1UL<<31) /* Paging */
|
||||
|
||||
#define XSTATE_XTILE_CFG_BIT 17
|
||||
#define XSTATE_XTILE_DATA_BIT 18
|
||||
|
||||
#define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT)
|
||||
#define XSTATE_XTILE_DATA_MASK (1ULL << XSTATE_XTILE_DATA_BIT)
|
||||
#define XFEATURE_XTILE_MASK (XSTATE_XTILE_CFG_MASK | \
|
||||
XSTATE_XTILE_DATA_MASK)
|
||||
|
||||
#define PFERR_PRESENT_BIT 0
|
||||
#define PFERR_WRITE_BIT 1
|
||||
#define PFERR_USER_BIT 2
|
||||
|
||||
@@ -80,6 +80,11 @@ static bool get_module_param_bool(const char *module_name, const char *param)
|
||||
TEST_FAIL("Unrecognized value '%c' for boolean module param", value);
|
||||
}
|
||||
|
||||
bool get_kvm_param_bool(const char *param)
|
||||
{
|
||||
return get_module_param_bool("kvm", param);
|
||||
}
|
||||
|
||||
bool get_kvm_intel_param_bool(const char *param)
|
||||
{
|
||||
return get_module_param_bool("kvm_intel", param);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) 2018, Google LLC.
|
||||
*/
|
||||
|
||||
#include "linux/bitmap.h"
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
@@ -573,6 +574,21 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
DEFAULT_GUEST_STACK_VADDR_MIN,
|
||||
MEM_REGION_DATA);
|
||||
|
||||
stack_vaddr += DEFAULT_STACK_PGS * getpagesize();
|
||||
|
||||
/*
|
||||
* Align stack to match calling sequence requirements in section "The
|
||||
* Stack Frame" of the System V ABI AMD64 Architecture Processor
|
||||
* Supplement, which requires the value (%rsp + 8) to be a multiple of
|
||||
* 16 when control is transferred to the function entry point.
|
||||
*
|
||||
* If this code is ever used to launch a vCPU with 32-bit entry point it
|
||||
* may need to subtract 4 bytes instead of 8 bytes.
|
||||
*/
|
||||
TEST_ASSERT(IS_ALIGNED(stack_vaddr, PAGE_SIZE),
|
||||
"__vm_vaddr_alloc() did not provide a page-aligned address");
|
||||
stack_vaddr -= 8;
|
||||
|
||||
vcpu = __vm_vcpu_add(vm, vcpu_id);
|
||||
vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
|
||||
vcpu_setup(vm, vcpu);
|
||||
@@ -580,7 +596,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
/* Setup guest general purpose registers */
|
||||
vcpu_regs_get(vcpu, ®s);
|
||||
regs.rflags = regs.rflags | 0x2;
|
||||
regs.rsp = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize());
|
||||
regs.rsp = stack_vaddr;
|
||||
regs.rip = (unsigned long) guest_code;
|
||||
vcpu_regs_set(vcpu, ®s);
|
||||
|
||||
@@ -681,7 +697,7 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index)
|
||||
return buffer.entry.data;
|
||||
}
|
||||
|
||||
void __vm_xsave_require_permission(int bit, const char *name)
|
||||
void __vm_xsave_require_permission(uint64_t xfeature, const char *name)
|
||||
{
|
||||
int kvm_fd;
|
||||
u64 bitmask;
|
||||
@@ -689,12 +705,15 @@ void __vm_xsave_require_permission(int bit, const char *name)
|
||||
struct kvm_device_attr attr = {
|
||||
.group = 0,
|
||||
.attr = KVM_X86_XCOMP_GUEST_SUPP,
|
||||
.addr = (unsigned long) &bitmask
|
||||
.addr = (unsigned long) &bitmask,
|
||||
};
|
||||
|
||||
TEST_ASSERT(!kvm_supported_cpuid,
|
||||
"kvm_get_supported_cpuid() cannot be used before ARCH_REQ_XCOMP_GUEST_PERM");
|
||||
|
||||
TEST_ASSERT(is_power_of_2(xfeature),
|
||||
"Dynamic XFeatures must be enabled one at a time");
|
||||
|
||||
kvm_fd = open_kvm_dev_path_or_exit();
|
||||
rc = __kvm_ioctl(kvm_fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||
close(kvm_fd);
|
||||
@@ -704,16 +723,16 @@ void __vm_xsave_require_permission(int bit, const char *name)
|
||||
|
||||
TEST_ASSERT(rc == 0, "KVM_GET_DEVICE_ATTR(0, KVM_X86_XCOMP_GUEST_SUPP) error: %ld", rc);
|
||||
|
||||
__TEST_REQUIRE(bitmask & (1ULL << bit),
|
||||
__TEST_REQUIRE(bitmask & xfeature,
|
||||
"Required XSAVE feature '%s' not supported", name);
|
||||
|
||||
TEST_REQUIRE(!syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, bit));
|
||||
TEST_REQUIRE(!syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, ilog2(xfeature)));
|
||||
|
||||
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_GUEST_PERM, &bitmask);
|
||||
TEST_ASSERT(rc == 0, "prctl(ARCH_GET_XCOMP_GUEST_PERM) error: %ld", rc);
|
||||
TEST_ASSERT(bitmask & (1ULL << bit),
|
||||
"prctl(ARCH_REQ_XCOMP_GUEST_PERM) failure bitmask=0x%lx",
|
||||
bitmask);
|
||||
TEST_ASSERT(bitmask & xfeature,
|
||||
"'%s' (0x%lx) not permitted after prctl(ARCH_REQ_XCOMP_GUEST_PERM) permitted=0x%lx",
|
||||
name, xfeature, bitmask);
|
||||
}
|
||||
|
||||
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid)
|
||||
@@ -954,6 +973,7 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu)
|
||||
vcpu_run_complete_io(vcpu);
|
||||
|
||||
state = malloc(sizeof(*state) + msr_list->nmsrs * sizeof(state->msrs.entries[0]));
|
||||
TEST_ASSERT(state, "-ENOMEM when allocating kvm state");
|
||||
|
||||
vcpu_events_get(vcpu, &state->events);
|
||||
vcpu_mp_state_get(vcpu, &state->mp_state);
|
||||
|
||||
@@ -30,21 +30,12 @@
|
||||
#define XSAVE_SIZE ((NUM_TILES * TILE_SIZE) + PAGE_SIZE)
|
||||
|
||||
/* Tile configuration associated: */
|
||||
#define PALETTE_TABLE_INDEX 1
|
||||
#define MAX_TILES 16
|
||||
#define RESERVED_BYTES 14
|
||||
|
||||
#define XFEATURE_XTILECFG 17
|
||||
#define XFEATURE_XTILEDATA 18
|
||||
#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
|
||||
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
|
||||
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
|
||||
|
||||
#define XSAVE_HDR_OFFSET 512
|
||||
|
||||
struct xsave_data {
|
||||
u8 area[XSAVE_SIZE];
|
||||
} __aligned(64);
|
||||
|
||||
struct tile_config {
|
||||
u8 palette_id;
|
||||
u8 start_row;
|
||||
@@ -68,24 +59,6 @@ struct xtile_info {
|
||||
|
||||
static struct xtile_info xtile;
|
||||
|
||||
static inline u64 __xgetbv(u32 index)
|
||||
{
|
||||
u32 eax, edx;
|
||||
|
||||
asm volatile("xgetbv;"
|
||||
: "=a" (eax), "=d" (edx)
|
||||
: "c" (index));
|
||||
return eax + ((u64)edx << 32);
|
||||
}
|
||||
|
||||
static inline void __xsetbv(u32 index, u64 value)
|
||||
{
|
||||
u32 eax = value;
|
||||
u32 edx = value >> 32;
|
||||
|
||||
asm volatile("xsetbv" :: "a" (eax), "d" (edx), "c" (index));
|
||||
}
|
||||
|
||||
static inline void __ldtilecfg(void *cfg)
|
||||
{
|
||||
asm volatile(".byte 0xc4,0xe2,0x78,0x49,0x00"
|
||||
@@ -103,27 +76,16 @@ static inline void __tilerelease(void)
|
||||
asm volatile(".byte 0xc4, 0xe2, 0x78, 0x49, 0xc0" ::);
|
||||
}
|
||||
|
||||
static inline void __xsavec(struct xsave_data *data, uint64_t rfbm)
|
||||
static inline void __xsavec(struct xstate *xstate, uint64_t rfbm)
|
||||
{
|
||||
uint32_t rfbm_lo = rfbm;
|
||||
uint32_t rfbm_hi = rfbm >> 32;
|
||||
|
||||
asm volatile("xsavec (%%rdi)"
|
||||
: : "D" (data), "a" (rfbm_lo), "d" (rfbm_hi)
|
||||
: : "D" (xstate), "a" (rfbm_lo), "d" (rfbm_hi)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
static inline void check_cpuid_xsave(void)
|
||||
{
|
||||
GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE));
|
||||
GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE));
|
||||
}
|
||||
|
||||
static bool check_xsave_supports_xtile(void)
|
||||
{
|
||||
return __xgetbv(0) & XFEATURE_MASK_XTILE;
|
||||
}
|
||||
|
||||
static void check_xtile_info(void)
|
||||
{
|
||||
GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_XSTATE_MAX_SIZE_XCR0));
|
||||
@@ -135,6 +97,10 @@ static void check_xtile_info(void)
|
||||
GUEST_ASSERT(xtile.xsave_size == 8192);
|
||||
GUEST_ASSERT(sizeof(struct tile_data) >= xtile.xsave_size);
|
||||
|
||||
GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_MAX_PALETTE_TABLES));
|
||||
GUEST_ASSERT(this_cpu_property(X86_PROPERTY_AMX_MAX_PALETTE_TABLES) >=
|
||||
PALETTE_TABLE_INDEX);
|
||||
|
||||
GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_NR_TILE_REGS));
|
||||
xtile.max_names = this_cpu_property(X86_PROPERTY_AMX_NR_TILE_REGS);
|
||||
GUEST_ASSERT(xtile.max_names == 8);
|
||||
@@ -158,37 +124,29 @@ static void set_tilecfg(struct tile_config *cfg)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_xstatebv(void *data, uint64_t bv)
|
||||
{
|
||||
*(uint64_t *)(data + XSAVE_HDR_OFFSET) = bv;
|
||||
}
|
||||
|
||||
static u64 get_xstatebv(void *data)
|
||||
{
|
||||
return *(u64 *)(data + XSAVE_HDR_OFFSET);
|
||||
}
|
||||
|
||||
static void init_regs(void)
|
||||
{
|
||||
uint64_t cr4, xcr0;
|
||||
|
||||
GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE));
|
||||
|
||||
/* turn on CR4.OSXSAVE */
|
||||
cr4 = get_cr4();
|
||||
cr4 |= X86_CR4_OSXSAVE;
|
||||
set_cr4(cr4);
|
||||
GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE));
|
||||
|
||||
xcr0 = __xgetbv(0);
|
||||
xcr0 = xgetbv(0);
|
||||
xcr0 |= XFEATURE_MASK_XTILE;
|
||||
__xsetbv(0x0, xcr0);
|
||||
xsetbv(0x0, xcr0);
|
||||
GUEST_ASSERT((xgetbv(0) & XFEATURE_MASK_XTILE) == XFEATURE_MASK_XTILE);
|
||||
}
|
||||
|
||||
static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
|
||||
struct tile_data *tiledata,
|
||||
struct xsave_data *xsave_data)
|
||||
struct xstate *xstate)
|
||||
{
|
||||
init_regs();
|
||||
check_cpuid_xsave();
|
||||
check_xsave_supports_xtile();
|
||||
check_xtile_info();
|
||||
GUEST_SYNC(1);
|
||||
|
||||
@@ -204,15 +162,29 @@ static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
|
||||
GUEST_SYNC(4);
|
||||
__tilerelease();
|
||||
GUEST_SYNC(5);
|
||||
/* bit 18 not in the XCOMP_BV after xsavec() */
|
||||
set_xstatebv(xsave_data, XFEATURE_MASK_XTILEDATA);
|
||||
__xsavec(xsave_data, XFEATURE_MASK_XTILEDATA);
|
||||
GUEST_ASSERT((get_xstatebv(xsave_data) & XFEATURE_MASK_XTILEDATA) == 0);
|
||||
/*
|
||||
* After XSAVEC, XTILEDATA is cleared in the xstate_bv but is set in
|
||||
* the xcomp_bv.
|
||||
*/
|
||||
xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;
|
||||
__xsavec(xstate, XFEATURE_MASK_XTILE_DATA);
|
||||
GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));
|
||||
GUEST_ASSERT(xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA);
|
||||
|
||||
/* xfd=0x40000, disable amx tiledata */
|
||||
wrmsr(MSR_IA32_XFD, XFEATURE_MASK_XTILEDATA);
|
||||
wrmsr(MSR_IA32_XFD, XFEATURE_MASK_XTILE_DATA);
|
||||
|
||||
/*
|
||||
* XTILEDATA is cleared in xstate_bv but set in xcomp_bv, this property
|
||||
* remains the same even when amx tiledata is disabled by IA32_XFD.
|
||||
*/
|
||||
xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;
|
||||
__xsavec(xstate, XFEATURE_MASK_XTILE_DATA);
|
||||
GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));
|
||||
GUEST_ASSERT((xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA));
|
||||
|
||||
GUEST_SYNC(6);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILEDATA);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
|
||||
set_tilecfg(amx_cfg);
|
||||
__ldtilecfg(amx_cfg);
|
||||
/* Trigger #NM exception */
|
||||
@@ -224,11 +196,14 @@ static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
|
||||
|
||||
void guest_nm_handler(struct ex_regs *regs)
|
||||
{
|
||||
/* Check if #NM is triggered by XFEATURE_MASK_XTILEDATA */
|
||||
/* Check if #NM is triggered by XFEATURE_MASK_XTILE_DATA */
|
||||
GUEST_SYNC(7);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILEDATA);
|
||||
GUEST_ASSERT(!(get_cr0() & X86_CR0_TS));
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
|
||||
GUEST_SYNC(8);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILEDATA);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);
|
||||
GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
|
||||
/* Clear xfd_err */
|
||||
wrmsr(MSR_IA32_XFD_ERR, 0);
|
||||
/* xfd=0, enable amx */
|
||||
@@ -243,7 +218,7 @@ int main(int argc, char *argv[])
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_x86_state *state;
|
||||
int xsave_restore_size;
|
||||
vm_vaddr_t amx_cfg, tiledata, xsavedata;
|
||||
vm_vaddr_t amx_cfg, tiledata, xstate;
|
||||
struct ucall uc;
|
||||
u32 amx_offset;
|
||||
int stage, ret;
|
||||
@@ -252,13 +227,14 @@ int main(int argc, char *argv[])
|
||||
* Note, all off-by-default features must be enabled before anything
|
||||
* caches KVM_GET_SUPPORTED_CPUID, e.g. before using kvm_cpu_has().
|
||||
*/
|
||||
vm_xsave_require_permission(XSTATE_XTILE_DATA_BIT);
|
||||
vm_xsave_require_permission(XFEATURE_MASK_XTILE_DATA);
|
||||
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XFD));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_AMX_TILE));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILECFG));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA_XFD));
|
||||
|
||||
/* Create VM */
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
@@ -282,10 +258,10 @@ int main(int argc, char *argv[])
|
||||
tiledata = vm_vaddr_alloc_pages(vm, 2);
|
||||
memset(addr_gva2hva(vm, tiledata), rand() | 1, 2 * getpagesize());
|
||||
|
||||
/* xsave data for guest_code */
|
||||
xsavedata = vm_vaddr_alloc_pages(vm, 3);
|
||||
memset(addr_gva2hva(vm, xsavedata), 0, 3 * getpagesize());
|
||||
vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xsavedata);
|
||||
/* XSAVE state for guest_code */
|
||||
xstate = vm_vaddr_alloc_pages(vm, DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));
|
||||
memset(addr_gva2hva(vm, xstate), 0, PAGE_SIZE * DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));
|
||||
vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xstate);
|
||||
|
||||
for (stage = 1; ; stage++) {
|
||||
vcpu_run(vcpu);
|
||||
|
||||
@@ -792,6 +792,7 @@ int main(int argc, char *argv[])
|
||||
struct kvm_vcpu *vcpu, *vcpu2 = NULL;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
TEST_REQUIRE(get_kvm_param_bool("enable_pmu"));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER));
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_MASKED_EVENTS));
|
||||
|
||||
|
||||
@@ -126,12 +126,16 @@ static void stable_tsc_check_supported(void)
|
||||
goto skip_test;
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL)
|
||||
goto skip_test;
|
||||
goto close_fp;
|
||||
|
||||
if (strncmp(buf, "tsc", sizeof(buf)))
|
||||
goto skip_test;
|
||||
goto close_fp;
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
|
||||
close_fp:
|
||||
fclose(fp);
|
||||
skip_test:
|
||||
print_skip("Kernel does not use TSC clocksource - assuming that host TSC is not stable");
|
||||
exit(KSFT_SKIP);
|
||||
|
||||
@@ -236,6 +236,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
union perf_capabilities host_cap;
|
||||
|
||||
TEST_REQUIRE(get_kvm_param_bool("enable_pmu"));
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM));
|
||||
|
||||
TEST_REQUIRE(kvm_cpu_has_p(X86_PROPERTY_PMU_VERSION));
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* XCR0 cpuid test
|
||||
*
|
||||
* Copyright (C) 2022, Google LLC.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
|
||||
/*
|
||||
* Assert that architectural dependency rules are satisfied, e.g. that AVX is
|
||||
* supported if and only if SSE is supported.
|
||||
*/
|
||||
#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \
|
||||
do { \
|
||||
uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \
|
||||
\
|
||||
GUEST_ASSERT_3((__supported & (xfeatures)) != (xfeatures) || \
|
||||
__supported == ((xfeatures) | (dependencies)), \
|
||||
__supported, (xfeatures), (dependencies)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Assert that KVM reports a sane, usable as-is XCR0. Architecturally, a CPU
|
||||
* isn't strictly required to _support_ all XFeatures related to a feature, but
|
||||
* at the same time XSETBV will #GP if bundled XFeatures aren't enabled and
|
||||
* disabled coherently. E.g. a CPU can technically enumerate supported for
|
||||
* XTILE_CFG but not XTILE_DATA, but attempting to enable XTILE_CFG without
|
||||
* XTILE_DATA will #GP.
|
||||
*/
|
||||
#define ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0, xfeatures) \
|
||||
do { \
|
||||
uint64_t __supported = (supported_xcr0) & (xfeatures); \
|
||||
\
|
||||
GUEST_ASSERT_2(!__supported || __supported == (xfeatures), \
|
||||
__supported, (xfeatures)); \
|
||||
} while (0)
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
uint64_t xcr0_reset;
|
||||
uint64_t supported_xcr0;
|
||||
int i, vector;
|
||||
|
||||
set_cr4(get_cr4() | X86_CR4_OSXSAVE);
|
||||
|
||||
xcr0_reset = xgetbv(0);
|
||||
supported_xcr0 = this_cpu_supported_xcr0();
|
||||
|
||||
GUEST_ASSERT(xcr0_reset == XFEATURE_MASK_FP);
|
||||
|
||||
/* Check AVX */
|
||||
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
|
||||
XFEATURE_MASK_YMM,
|
||||
XFEATURE_MASK_SSE);
|
||||
|
||||
/* Check MPX */
|
||||
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
|
||||
XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
|
||||
|
||||
/* Check AVX-512 */
|
||||
ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
|
||||
XFEATURE_MASK_AVX512,
|
||||
XFEATURE_MASK_SSE | XFEATURE_MASK_YMM);
|
||||
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
|
||||
XFEATURE_MASK_AVX512);
|
||||
|
||||
/* Check AMX */
|
||||
ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
|
||||
XFEATURE_MASK_XTILE);
|
||||
|
||||
vector = xsetbv_safe(0, supported_xcr0);
|
||||
GUEST_ASSERT_2(!vector, supported_xcr0, vector);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (supported_xcr0 & BIT_ULL(i))
|
||||
continue;
|
||||
|
||||
vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i));
|
||||
GUEST_ASSERT_3(vector == GP_VECTOR, supported_xcr0, vector, BIT_ULL(i));
|
||||
}
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_run *run;
|
||||
struct kvm_vm *vm;
|
||||
struct ucall uc;
|
||||
|
||||
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
|
||||
run = vcpu->run;
|
||||
|
||||
vm_init_descriptor_tables(vm);
|
||||
vcpu_init_descriptor_tables(vcpu);
|
||||
|
||||
while (1) {
|
||||
vcpu_run(vcpu);
|
||||
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"Unexpected exit reason: %u (%s),\n",
|
||||
run->exit_reason,
|
||||
exit_reason_str(run->exit_reason));
|
||||
|
||||
switch (get_ucall(vcpu, &uc)) {
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT_3(uc, "0x%lx 0x%lx 0x%lx");
|
||||
break;
|
||||
case UCALL_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_FAIL("Unknown ucall %lu", uc.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
kvm_vm_free(vm);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user