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