diff options
Diffstat (limited to 'tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c')
-rw-r--r-- | tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c | 231 |
1 files changed, 196 insertions, 35 deletions
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 c280ba1e6572..4c90f76930f9 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 @@ -14,12 +14,11 @@ #define _GNU_SOURCE /* for program_invocation_short_name */ #include <sys/ioctl.h> +#include <linux/bitmap.h> + #include "kvm_util.h" #include "vmx.h" -#define PMU_CAP_FW_WRITES (1ULL << 13) -#define PMU_CAP_LBR_FMT 0x3f - union perf_capabilities { struct { u64 lbr_format:6; @@ -36,59 +35,221 @@ union perf_capabilities { u64 capabilities; }; -static void guest_code(void) +/* + * The LBR format and most PEBS features are immutable, all other features are + * fungible (if supported by the host and KVM). + */ +static const union perf_capabilities immutable_caps = { + .lbr_format = -1, + .pebs_trap = 1, + .pebs_arch_reg = 1, + .pebs_format = -1, + .pebs_baseline = 1, +}; + +static const union perf_capabilities format_caps = { + .lbr_format = -1, + .pebs_format = -1, +}; + +static void guest_code(uint64_t current_val) { - wrmsr(MSR_IA32_PERF_CAPABILITIES, PMU_CAP_LBR_FMT); + uint8_t vector; + int i; + + vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, current_val); + GUEST_ASSERT_2(vector == GP_VECTOR, current_val, vector); + + vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, 0); + GUEST_ASSERT_2(vector == GP_VECTOR, 0, vector); + + for (i = 0; i < 64; i++) { + vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, + current_val ^ BIT_ULL(i)); + GUEST_ASSERT_2(vector == GP_VECTOR, + current_val ^ BIT_ULL(i), vector); + } + + GUEST_DONE(); } -int main(int argc, char *argv[]) +/* + * Verify that guest WRMSRs to PERF_CAPABILITIES #GP regardless of the value + * written, that the guest always sees the userspace controlled value, and that + * PERF_CAPABILITIES is immutable after KVM_RUN. + */ +static void test_guest_wrmsr_perf_capabilities(union perf_capabilities host_cap) { - struct kvm_vm *vm; struct kvm_vcpu *vcpu; - int ret; - union perf_capabilities host_cap; - uint64_t val; + struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, guest_code); + struct ucall uc; + int r, i; - host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES); - host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT); + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vcpu); - /* Create VM */ - vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); - TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM)); + vcpu_args_set(vcpu, 1, host_cap.capabilities); + vcpu_run(vcpu); - TEST_REQUIRE(kvm_cpu_has_p(X86_PROPERTY_PMU_VERSION)); - TEST_REQUIRE(kvm_cpu_property(X86_PROPERTY_PMU_VERSION) > 0); + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT_2(uc, "val = 0x%lx, vector = %lu"); + break; + case UCALL_DONE: + break; + default: + TEST_FAIL("Unexpected ucall: %lu", uc.cmd); + } - /* testcase 1, set capabilities when we have PDCM bit */ - vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, PMU_CAP_FW_WRITES); + ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), host_cap.capabilities); - /* check capabilities can be retrieved with KVM_GET_MSR */ - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); - /* check whatever we write with KVM_SET_MSR is _not_ modified */ - vcpu_run(vcpu); - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); + r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0); + TEST_ASSERT(!r, "Post-KVM_RUN write '0' didn't fail"); + + for (i = 0; i < 64; i++) { + r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, + host_cap.capabilities ^ BIT_ULL(i)); + TEST_ASSERT(!r, "Post-KVM_RUN write '0x%llx'didn't fail", + host_cap.capabilities ^ BIT_ULL(i)); + } + + kvm_vm_free(vm); +} + +/* + * Verify KVM allows writing PERF_CAPABILITIES with all KVM-supported features + * enabled, as well as '0' (to disable all features). + */ +static void test_basic_perf_capabilities(union perf_capabilities host_cap) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL); - /* testcase 2, check valid LBR formats are accepted */ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0); - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), 0); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); + + kvm_vm_free(vm); +} - vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.lbr_format); - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format); +static void test_fungible_perf_capabilities(union perf_capabilities host_cap) +{ + const uint64_t fungible_caps = host_cap.capabilities & ~immutable_caps.capabilities; + + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL); + int bit; + + for_each_set_bit(bit, &fungible_caps, 64) { + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, BIT_ULL(bit)); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, + host_cap.capabilities & ~BIT_ULL(bit)); + } + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); + + kvm_vm_free(vm); +} + +/* + * Verify KVM rejects attempts to set unsupported and/or immutable features in + * PERF_CAPABILITIES. Note, LBR format and PEBS format need to be validated + * separately as they are multi-bit values, e.g. toggling or setting a single + * bit can generate a false positive without dedicated safeguards. + */ +static void test_immutable_perf_capabilities(union perf_capabilities host_cap) +{ + const uint64_t reserved_caps = (~host_cap.capabilities | + immutable_caps.capabilities) & + ~format_caps.capabilities; + + struct kvm_vcpu *vcpu; + struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL); + union perf_capabilities val = host_cap; + int r, bit; + + for_each_set_bit(bit, &reserved_caps, 64) { + r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, + host_cap.capabilities ^ BIT_ULL(bit)); + TEST_ASSERT(!r, "%s immutable feature 0x%llx (bit %d) didn't fail", + host_cap.capabilities & BIT_ULL(bit) ? "Setting" : "Clearing", + BIT_ULL(bit), bit); + } /* - * Testcase 3, check that an "invalid" LBR format is rejected. Only an - * exact match of the host's format (and 0/disabled) is allowed. + * KVM only supports the host's native LBR format, as well as '0' (to + * disable LBR support). Verify KVM rejects all other LBR formats. */ - for (val = 1; val <= PMU_CAP_LBR_FMT; val++) { - if (val == (host_cap.capabilities & PMU_CAP_LBR_FMT)) + for (val.lbr_format = 1; val.lbr_format; val.lbr_format++) { + if (val.lbr_format == host_cap.lbr_format) continue; - ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val); - TEST_ASSERT(!ret, "Bad LBR FMT = 0x%lx didn't fail", val); + r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val.capabilities); + TEST_ASSERT(!r, "Bad LBR FMT = 0x%x didn't fail, host = 0x%x", + val.lbr_format, host_cap.lbr_format); } - printf("Completed perf capability tests.\n"); + /* Ditto for the PEBS format. */ + for (val.pebs_format = 1; val.pebs_format; val.pebs_format++) { + if (val.pebs_format == host_cap.pebs_format) + continue; + + r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val.capabilities); + TEST_ASSERT(!r, "Bad PEBS FMT = 0x%x didn't fail, host = 0x%x", + val.pebs_format, host_cap.pebs_format); + } + + kvm_vm_free(vm); +} + +/* + * Test that LBR MSRs are writable when LBRs are enabled, and then verify that + * disabling the vPMU via CPUID also disables LBR support. Set bits 2:0 of + * LBR_TOS as those bits are writable across all uarch implementations (arch + * LBRs will need to poke a different MSR). + */ +static void test_lbr_perf_capabilities(union perf_capabilities host_cap) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int r; + + if (!host_cap.lbr_format) + return; + + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); + vcpu_set_msr(vcpu, MSR_LBR_TOS, 7); + + vcpu_clear_cpuid_entry(vcpu, X86_PROPERTY_PMU_VERSION.function); + + r = _vcpu_set_msr(vcpu, MSR_LBR_TOS, 7); + TEST_ASSERT(!r, "Writing LBR_TOS should fail after disabling vPMU"); + kvm_vm_free(vm); } + +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)); + TEST_REQUIRE(kvm_cpu_property(X86_PROPERTY_PMU_VERSION) > 0); + + host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES); + + TEST_ASSERT(host_cap.full_width_write, + "Full-width writes should always be supported"); + + test_basic_perf_capabilities(host_cap); + test_fungible_perf_capabilities(host_cap); + test_immutable_perf_capabilities(host_cap); + test_guest_wrmsr_perf_capabilities(host_cap); + test_lbr_perf_capabilities(host_cap); +} |