diff mbox series

[v2,5/8] KVM: selftests: Test consistency of CPUID with num of gp counters

Message ID 20230530134248.23998-6-cloudliang@tencent.com (mailing list archive)
State New, archived
Headers show
Series KVM: selftests: Test the consistency of the PMU's CPUID and its features | expand

Commit Message

Jinrong Liang May 30, 2023, 1:42 p.m. UTC
From: Like Xu <likexu@tencent.com>

Add test to check if non-existent counters can be accessed in guest after
determining the number of Intel generic performance counters by CPUID.
When the num of counters is less than 3, KVM does not emulate #GP if
a counter isn't present due to compatibility MSR_P6_PERFCTRx handling.
Nor will the KVM emulate more counters than it can support.

Co-developed-by: Jinrong Liang <cloudliang@tencent.com>
Signed-off-by: Jinrong Liang <cloudliang@tencent.com>
Signed-off-by: Like Xu <likexu@tencent.com>
---
 .../kvm/x86_64/pmu_basic_functionality_test.c | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
diff mbox series

Patch

diff --git a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c
index 81029d05367a..116437ac2095 100644
--- a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c
+++ b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c
@@ -16,6 +16,17 @@ 
 /* Guest payload for any performance counter counting */
 #define NUM_BRANCHES 10
 
+/*
+ * KVM implements the first two non-existent counters (MSR_P6_PERFCTRx)
+ * via kvm_pr_unimpl_wrmsr() instead of #GP.
+ */
+#define MSR_INTEL_ARCH_PMU_GPCTR (MSR_IA32_PERFCTR0 + 2)
+
+static const uint64_t perf_caps[] = {
+	0,
+	PMU_CAP_FW_WRITES,
+};
+
 static struct kvm_vm *pmu_vm_create_with_one_vcpu(struct kvm_vcpu **vcpu,
 						  void *guest_code)
 {
@@ -169,9 +180,86 @@  static void intel_test_arch_events(void)
 	}
 }
 
+static void guest_wr_and_rd_msrs(uint32_t base, uint64_t value,
+				 uint8_t begin, uint8_t offset)
+{
+	unsigned int i;
+	uint8_t wr_vector, rd_vector;
+	uint64_t msr_val;
+
+	for (i = begin; i < begin + offset; i++) {
+		wr_vector = wrmsr_safe(base + i, value);
+		rd_vector = rdmsr_safe(base + i, &msr_val);
+		if (wr_vector == GP_VECTOR || rd_vector == GP_VECTOR)
+			GUEST_SYNC(GP_VECTOR);
+		else
+			GUEST_SYNC(msr_val);
+	}
+
+	GUEST_DONE();
+}
+
+/* Access the first out-of-range counter register to trigger #GP */
+static void test_oob_gp_counter(uint8_t eax_gp_num, uint8_t offset,
+				uint64_t perf_cap, uint64_t exported)
+{
+	struct kvm_vm *vm;
+	struct kvm_vcpu *vcpu;
+	struct kvm_cpuid_entry2 *entry;
+	uint32_t ctr_msr = MSR_IA32_PERFCTR0;
+	uint64_t msr_val;
+
+	vm = pmu_vm_create_with_one_vcpu(&vcpu, guest_wr_and_rd_msrs);
+
+	entry = vcpu_get_cpuid_entry(vcpu, 0xa);
+	entry->eax = (entry->eax & ~GP_CTR_NUM_MASK) |
+		(eax_gp_num << GP_CTR_NUM_OFS_BIT);
+	vcpu_set_cpuid(vcpu);
+
+	if (perf_cap & PMU_CAP_FW_WRITES)
+		ctr_msr = MSR_IA32_PMC0;
+
+	vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, perf_cap);
+	vcpu_args_set(vcpu, 4, ctr_msr, 0xffff, eax_gp_num, offset);
+	while (run_vcpu(vcpu, &msr_val) != UCALL_DONE)
+		TEST_ASSERT(msr_val == exported,
+			    "Unexpected when testing gp counter num.");
+
+	kvm_vm_free(vm);
+}
+
+static void intel_test_counters_num(void)
+{
+	unsigned int i;
+	uint8_t kvm_gp_num = X86_INTEL_MAX_GP_CTR_NUM;
+
+	TEST_REQUIRE(kvm_gp_num > 2);
+
+	for (i = 0; i < ARRAY_SIZE(perf_caps); i++) {
+		/*
+		 * For compatibility reasons, KVM does not emulate #GP
+		 * when MSR_P6_PERFCTR[0|1] is not present, but it doesn't
+		 * affect checking the presence of MSR_IA32_PMCx with #GP.
+		 */
+		if (perf_caps[i] & PMU_CAP_FW_WRITES)
+			test_oob_gp_counter(0, 1, perf_caps[i], GP_VECTOR);
+
+		test_oob_gp_counter(2, 1, perf_caps[i], GP_VECTOR);
+		test_oob_gp_counter(kvm_gp_num, 1, perf_caps[i], GP_VECTOR);
+
+		/* KVM doesn't emulate more counters than it can support. */
+		test_oob_gp_counter(kvm_gp_num + 1, 1, perf_caps[i], GP_VECTOR);
+
+		/* Test that KVM drops writes to MSR_P6_PERFCTR[0|1]. */
+		if (perf_caps[i] == 0)
+			test_oob_gp_counter(0, 2, perf_caps[i], 0);
+	}
+}
+
 static void intel_test_pmu_cpuid(void)
 {
 	intel_test_arch_events();
+	intel_test_counters_num();
 }
 
 int main(int argc, char *argv[])