Commit 03a405b7 authored by Aaron Lewis's avatar Aaron Lewis Committed by Sean Christopherson
Browse files

KVM: selftests: Add test to verify KVM's supported XCR0



Check both architectural rules and KVM's ABI for KVM_GET_SUPPORTED_CPUID
to ensure the supported xfeatures[1] don't violate any of them.

The architectural rules[2] and KVM's contract with userspace ensure for a
given feature, e.g. sse, avx, amx, etc... their associated xfeatures are
either all sets or none of them are set, and any dependencies are enabled
if needed.

[1] EDX:EAX of CPUID.(EAX=0DH,ECX=0)
[2] SDM vol 1, 13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
    FEATURES

Cc: Mingwei Zhang <mizhang@google.com>
Signed-off-by: default avatarAaron Lewis <aaronlewis@google.com>
[sean: expand comments, use a fancy X86_PROPERTY]
Reviewed-by: default avatarAaron Lewis <aaronlewis@google.com>
Tested-by: default avatarAaron Lewis <aaronlewis@google.com>
Link: https://lore.kernel.org/r/20230405004520.421768-7-seanjc@google.com


Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
parent 28f23025
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+20 −0
Original line number Diff line number Diff line
@@ -241,8 +241,11 @@ 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)
@@ -681,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]; })
@@ -1104,6 +1116,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,
+132 −0
Original line number Diff line number Diff line
// 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;
}