| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Test for VMX-pmu perf capability msr |
| * |
| * Copyright (C) 2021 Intel Corporation |
| * |
| * Test to check the effect of various CPUID settings on |
| * MSR_IA32_PERF_CAPABILITIES MSR, and check that what |
| * we write with KVM_SET_MSR is _not_ modified by the guest |
| * and check it can be retrieved with KVM_GET_MSR, also test |
| * the invalid LBR formats are rejected. |
| */ |
| |
| #define _GNU_SOURCE /* for program_invocation_short_name */ |
| #include <sys/ioctl.h> |
| |
| #include "kvm_util.h" |
| #include "vmx.h" |
| |
| #define PMU_CAP_FW_WRITES (1ULL << 13) |
| #define PMU_CAP_LBR_FMT 0x3f |
| |
| union cpuid10_eax { |
| struct { |
| unsigned int version_id:8; |
| unsigned int num_counters:8; |
| unsigned int bit_width:8; |
| unsigned int mask_length:8; |
| } split; |
| unsigned int full; |
| }; |
| |
| union perf_capabilities { |
| struct { |
| u64 lbr_format:6; |
| u64 pebs_trap:1; |
| u64 pebs_arch_reg:1; |
| u64 pebs_format:4; |
| u64 smm_freeze:1; |
| u64 full_width_write:1; |
| u64 pebs_baseline:1; |
| u64 perf_metrics:1; |
| u64 pebs_output_pt_available:1; |
| u64 anythread_deprecated:1; |
| }; |
| u64 capabilities; |
| }; |
| |
| static void guest_code(void) |
| { |
| wrmsr(MSR_IA32_PERF_CAPABILITIES, PMU_CAP_LBR_FMT); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const struct kvm_cpuid_entry2 *entry_a_0; |
| struct kvm_vm *vm; |
| struct kvm_vcpu *vcpu; |
| int ret; |
| union cpuid10_eax eax; |
| union perf_capabilities host_cap; |
| uint64_t val; |
| |
| host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES); |
| host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT); |
| |
| /* Create VM */ |
| vm = vm_create_with_one_vcpu(&vcpu, guest_code); |
| |
| TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM)); |
| |
| TEST_REQUIRE(kvm_get_cpuid_max_basic() >= 0xa); |
| entry_a_0 = kvm_get_supported_cpuid_entry(0xa); |
| |
| eax.full = entry_a_0->eax; |
| __TEST_REQUIRE(eax.split.version_id, "PMU is not supported by the vCPU"); |
| |
| /* testcase 1, set capabilities when we have PDCM bit */ |
| vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, PMU_CAP_FW_WRITES); |
| |
| /* check capabilities can be retrieved with KVM_GET_MSR */ |
| ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); |
| |
| /* 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); |
| |
| /* 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.lbr_format); |
| ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format); |
| |
| /* |
| * Testcase 3, check that an "invalid" LBR format is rejected. Only an |
| * exact match of the host's format (and 0/disabled) is allowed. |
| */ |
| for (val = 1; val <= PMU_CAP_LBR_FMT; val++) { |
| if (val == (host_cap.capabilities & PMU_CAP_LBR_FMT)) |
| continue; |
| |
| ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val); |
| TEST_ASSERT(!ret, "Bad LBR FMT = 0x%lx didn't fail", val); |
| } |
| |
| printf("Completed perf capability tests.\n"); |
| kvm_vm_free(vm); |
| } |