[PATCH 1/2] KVM: selftests: Test consistency of setting MSR_IA32_DS_AREA

From: Jinrong Liang
Date: Thu Jun 08 2023 - 07:34:55 EST


From: Jinrong Liang <cloudliang@xxxxxxxxxxx>

Tests have been added to this commit to check if setting
MSR_IA32_DS_AREA with a non-classical address causes a fault. By
verifying that KVM is correctly faulting non-classical addresses
in MSR_IA32_DS_AREA, it helps ensure the accuracy and stability
of the KVM subsystem.

Signed-off-by: Jinrong Liang <cloudliang@xxxxxxxxxxx>
---
.../selftests/kvm/x86_64/vmx_pmu_caps_test.c | 100 ++++++++++++++++++
1 file changed, 100 insertions(+)

diff --git a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
index 4c90f76930f9..02903084598f 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
@@ -19,6 +19,9 @@
#include "kvm_util.h"
#include "vmx.h"

+#define MAX_LINEAR_ADDR_MASK GENMASK_ULL(15, 8)
+#define ADDR_OFS_BIT 8
+
union perf_capabilities {
struct {
u64 lbr_format:6;
@@ -232,6 +235,102 @@ static void test_lbr_perf_capabilities(union perf_capabilities host_cap)
kvm_vm_free(vm);
}

+/*
+ * Generate a non-canonical address for a given number of address bits.
+ * @addr_bits: The number of address bits used in the system.
+ *
+ * This function calculates a non-canonical address by setting the most
+ * significant bit to 1 and adding an offset equal to the maximum value
+ * that can be represented by the remaining bits. This ensures that the
+ * generated address is outside the valid address range and is consistent.
+ */
+static inline uint64_t non_canonical_address(unsigned int addr_bits)
+{
+ return (1ULL << (addr_bits - 1)) + ((1ULL << (addr_bits - 1)) - 1);
+}
+
+static uint64_t get_addr_bits(struct kvm_vcpu *vcpu)
+{
+ const struct kvm_cpuid_entry2 *kvm_entry;
+ unsigned int addr_bits;
+ struct kvm_sregs sregs;
+
+ kvm_entry = get_cpuid_entry(kvm_get_supported_cpuid(), 0x80000008, 0);
+ addr_bits = (kvm_entry->eax & MAX_LINEAR_ADDR_MASK) >> ADDR_OFS_BIT;
+ /*
+ * Get the size of the virtual address space by checking the LA57 bit
+ * in the CR4 control register. If the LA57 bit is set, then the virtual
+ * address space is 57 bits. Otherwise, it's 48 bits.
+ */
+ if (addr_bits != 32) {
+ vcpu_sregs_get(vcpu, &sregs);
+ addr_bits = (sregs.cr4 & X86_CR4_LA57) ? 57 : 48;
+ }
+
+ return addr_bits;
+}
+
+static void test_ds_guest_code(uint64_t bad_addr)
+{
+ uint8_t vector = 0;
+
+ vector = wrmsr_safe(MSR_IA32_DS_AREA, bad_addr);
+ GUEST_SYNC(vector);
+}
+
+/* Check if setting MSR_IA32_DS_AREA in guest and kvm userspace will fail. */
+static void test_ds_area_noncanonical_address(union perf_capabilities host_cap)
+{
+ struct kvm_vm *vm;
+ struct kvm_vcpu *vcpu;
+ unsigned int r, addr_bits;
+ uint64_t bad_addr, without_pebs_fmt_caps;
+ struct ucall uc;
+
+ vm = vm_create_with_one_vcpu(&vcpu, test_ds_guest_code);
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+
+ /* Check that Setting MSR_IA32_DS_AREA with 0 should succeed. */
+ r = _vcpu_set_msr(vcpu, MSR_IA32_DS_AREA, 0);
+ TEST_ASSERT(r, "Setting MSR_IA32_DS_AREA with 0 should succeed.");
+
+ /*
+ * Check that if PEBS_FMT is not set setting MSR_IA32_DS_AREA will
+ * succeed.
+ */
+ without_pebs_fmt_caps = host_cap.capabilities & ~PERF_CAP_PEBS_FORMAT;
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, without_pebs_fmt_caps);
+ r = _vcpu_set_msr(vcpu, MSR_IA32_DS_AREA, 1);
+ TEST_ASSERT(r, "Setting MSR_IA32_DS_AREA with bad addr should fail.");
+
+ /*
+ * Check that setting MSR_IA32_DS_AREA in kvm userspace to use a
+ * non-canonical address should fail.
+ */
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
+ addr_bits = get_addr_bits(vcpu);
+ bad_addr = non_canonical_address(addr_bits);
+ r = _vcpu_set_msr(vcpu, MSR_IA32_DS_AREA, bad_addr);
+ TEST_ASSERT(!r, "Setting MSR_IA32_DS_AREA with bad addr should fail.");
+
+ /*
+ * Check that setting MSR_IA32_DS_AREA in guest to use a non-canonical
+ * address should cause the #GP.
+ */
+ vcpu_args_set(vcpu, 1, bad_addr);
+ vcpu_run(vcpu);
+
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ get_ucall(vcpu, &uc);
+ TEST_ASSERT(uc.cmd == UCALL_SYNC,
+ "Received ucall other than UCALL_SYNC: %lu", uc.cmd);
+ TEST_ASSERT(uc.args[1] == GP_VECTOR,
+ "Setting MSR_IA32_DS_AREA with bad addr should fail.");
+
+ kvm_vm_free(vm);
+}
+
int main(int argc, char *argv[])
{
union perf_capabilities host_cap;
@@ -252,4 +351,5 @@ int main(int argc, char *argv[])
test_immutable_perf_capabilities(host_cap);
test_guest_wrmsr_perf_capabilities(host_cap);
test_lbr_perf_capabilities(host_cap);
+ test_ds_area_noncanonical_address(host_cap);
}
--
2.31.1