146a010ddSJoerg Roedel // SPDX-License-Identifier: GPL-2.0-only 246a010ddSJoerg Roedel /* 346a010ddSJoerg Roedel * KVM PMU support for AMD 446a010ddSJoerg Roedel * 546a010ddSJoerg Roedel * Copyright 2015, Red Hat, Inc. and/or its affiliates. 646a010ddSJoerg Roedel * 746a010ddSJoerg Roedel * Author: 846a010ddSJoerg Roedel * Wei Huang <wei@redhat.com> 946a010ddSJoerg Roedel * 1046a010ddSJoerg Roedel * Implementation is based on pmu_intel.c file 1146a010ddSJoerg Roedel */ 128d20bd63SSean Christopherson #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138d20bd63SSean Christopherson 1446a010ddSJoerg Roedel #include <linux/types.h> 1546a010ddSJoerg Roedel #include <linux/kvm_host.h> 1646a010ddSJoerg Roedel #include <linux/perf_event.h> 1746a010ddSJoerg Roedel #include "x86.h" 1846a010ddSJoerg Roedel #include "cpuid.h" 1946a010ddSJoerg Roedel #include "lapic.h" 2046a010ddSJoerg Roedel #include "pmu.h" 21b1d66dadSLike Xu #include "svm.h" 2246a010ddSJoerg Roedel 2346a010ddSJoerg Roedel enum pmu_type { 2446a010ddSJoerg Roedel PMU_TYPE_COUNTER = 0, 2546a010ddSJoerg Roedel PMU_TYPE_EVNTSEL, 2646a010ddSJoerg Roedel }; 2746a010ddSJoerg Roedel 28ea5cbc9fSLike Xu static struct kvm_pmc *amd_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx) 2946a010ddSJoerg Roedel { 30ea5cbc9fSLike Xu unsigned int num_counters = pmu->nr_arch_gp_counters; 31ea5cbc9fSLike Xu 32ea5cbc9fSLike Xu if (pmc_idx >= num_counters) 33ea5cbc9fSLike Xu return NULL; 34ea5cbc9fSLike Xu 35ea5cbc9fSLike Xu return &pmu->gp_counters[array_index_nospec(pmc_idx, num_counters)]; 3646a010ddSJoerg Roedel } 3746a010ddSJoerg Roedel 3846a010ddSJoerg Roedel static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr, 3946a010ddSJoerg Roedel enum pmu_type type) 4046a010ddSJoerg Roedel { 411973caddSVitaly Kuznetsov struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu); 42ea5cbc9fSLike Xu unsigned int idx; 431973caddSVitaly Kuznetsov 44ba7bb663SDavid Dunn if (!vcpu->kvm->arch.enable_pmu) 45b1d66dadSLike Xu return NULL; 46b1d66dadSLike Xu 4746a010ddSJoerg Roedel switch (msr) { 48ea5cbc9fSLike Xu case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5: 491973caddSVitaly Kuznetsov if (!guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE)) 501973caddSVitaly Kuznetsov return NULL; 51ea5cbc9fSLike Xu /* 52ea5cbc9fSLike Xu * Each PMU counter has a pair of CTL and CTR MSRs. CTLn 53ea5cbc9fSLike Xu * MSRs (accessed via EVNTSEL) are even, CTRn MSRs are odd. 54ea5cbc9fSLike Xu */ 55ea5cbc9fSLike Xu idx = (unsigned int)((msr - MSR_F15H_PERF_CTL0) / 2); 56ea5cbc9fSLike Xu if (!(msr & 0x1) != (type == PMU_TYPE_EVNTSEL)) 57ea5cbc9fSLike Xu return NULL; 58ea5cbc9fSLike Xu break; 5946a010ddSJoerg Roedel case MSR_K7_EVNTSEL0 ... MSR_K7_EVNTSEL3: 6046a010ddSJoerg Roedel if (type != PMU_TYPE_EVNTSEL) 6146a010ddSJoerg Roedel return NULL; 62ea5cbc9fSLike Xu idx = msr - MSR_K7_EVNTSEL0; 6346a010ddSJoerg Roedel break; 6446a010ddSJoerg Roedel case MSR_K7_PERFCTR0 ... MSR_K7_PERFCTR3: 6546a010ddSJoerg Roedel if (type != PMU_TYPE_COUNTER) 6646a010ddSJoerg Roedel return NULL; 67ea5cbc9fSLike Xu idx = msr - MSR_K7_PERFCTR0; 6846a010ddSJoerg Roedel break; 6946a010ddSJoerg Roedel default: 7046a010ddSJoerg Roedel return NULL; 7146a010ddSJoerg Roedel } 7246a010ddSJoerg Roedel 73ea5cbc9fSLike Xu return amd_pmc_idx_to_pmc(pmu, idx); 7446a010ddSJoerg Roedel } 7546a010ddSJoerg Roedel 767aadaa98SLike Xu static bool amd_hw_event_available(struct kvm_pmc *pmc) 7746a010ddSJoerg Roedel { 787aadaa98SLike Xu return true; 7946a010ddSJoerg Roedel } 8046a010ddSJoerg Roedel 81e6cd31f1SJim Mattson static bool amd_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx) 8246a010ddSJoerg Roedel { 8346a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 8446a010ddSJoerg Roedel 8546a010ddSJoerg Roedel idx &= ~(3u << 30); 8646a010ddSJoerg Roedel 87e6cd31f1SJim Mattson return idx < pmu->nr_arch_gp_counters; 8846a010ddSJoerg Roedel } 8946a010ddSJoerg Roedel 9046a010ddSJoerg Roedel /* idx is the ECX register of RDPMC instruction */ 9146a010ddSJoerg Roedel static struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu, 9246a010ddSJoerg Roedel unsigned int idx, u64 *mask) 9346a010ddSJoerg Roedel { 945c6a67f4SLike Xu return amd_pmc_idx_to_pmc(vcpu_to_pmu(vcpu), idx & ~(3u << 30)); 9546a010ddSJoerg Roedel } 9646a010ddSJoerg Roedel 9746a010ddSJoerg Roedel static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr) 9846a010ddSJoerg Roedel { 9946a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 10046a010ddSJoerg Roedel struct kvm_pmc *pmc; 10146a010ddSJoerg Roedel 10246a010ddSJoerg Roedel pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER); 10346a010ddSJoerg Roedel pmc = pmc ? pmc : get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL); 10446a010ddSJoerg Roedel 10546a010ddSJoerg Roedel return pmc; 10646a010ddSJoerg Roedel } 10746a010ddSJoerg Roedel 108*4a277189SLike Xu static bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr) 109*4a277189SLike Xu { 110*4a277189SLike Xu struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 111*4a277189SLike Xu 112*4a277189SLike Xu switch (msr) { 113*4a277189SLike Xu case MSR_K7_EVNTSEL0 ... MSR_K7_PERFCTR3: 114*4a277189SLike Xu return pmu->version > 0; 115*4a277189SLike Xu case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5: 116*4a277189SLike Xu return guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE); 117*4a277189SLike Xu case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS: 118*4a277189SLike Xu case MSR_AMD64_PERF_CNTR_GLOBAL_CTL: 119*4a277189SLike Xu case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR: 120*4a277189SLike Xu return pmu->version > 1; 121*4a277189SLike Xu default: 122*4a277189SLike Xu if (msr > MSR_F15H_PERF_CTR5 && 123*4a277189SLike Xu msr < MSR_F15H_PERF_CTL0 + 2 * pmu->nr_arch_gp_counters) 124*4a277189SLike Xu return pmu->version > 1; 125*4a277189SLike Xu break; 126*4a277189SLike Xu } 127*4a277189SLike Xu 128*4a277189SLike Xu return amd_msr_idx_to_pmc(vcpu, msr); 129*4a277189SLike Xu } 130*4a277189SLike Xu 131cbd71758SWei Wang static int amd_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) 13246a010ddSJoerg Roedel { 13346a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 13446a010ddSJoerg Roedel struct kvm_pmc *pmc; 135cbd71758SWei Wang u32 msr = msr_info->index; 13646a010ddSJoerg Roedel 13746a010ddSJoerg Roedel /* MSR_PERFCTRn */ 13846a010ddSJoerg Roedel pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER); 13946a010ddSJoerg Roedel if (pmc) { 140cbd71758SWei Wang msr_info->data = pmc_read_counter(pmc); 14146a010ddSJoerg Roedel return 0; 14246a010ddSJoerg Roedel } 14346a010ddSJoerg Roedel /* MSR_EVNTSELn */ 14446a010ddSJoerg Roedel pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL); 14546a010ddSJoerg Roedel if (pmc) { 146cbd71758SWei Wang msr_info->data = pmc->eventsel; 14746a010ddSJoerg Roedel return 0; 14846a010ddSJoerg Roedel } 14946a010ddSJoerg Roedel 15046a010ddSJoerg Roedel return 1; 15146a010ddSJoerg Roedel } 15246a010ddSJoerg Roedel 15346a010ddSJoerg Roedel static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) 15446a010ddSJoerg Roedel { 15546a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 15646a010ddSJoerg Roedel struct kvm_pmc *pmc; 15746a010ddSJoerg Roedel u32 msr = msr_info->index; 15846a010ddSJoerg Roedel u64 data = msr_info->data; 15946a010ddSJoerg Roedel 16046a010ddSJoerg Roedel /* MSR_PERFCTRn */ 16146a010ddSJoerg Roedel pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER); 16246a010ddSJoerg Roedel if (pmc) { 16346a010ddSJoerg Roedel pmc->counter += data - pmc_read_counter(pmc); 16475189d1dSLike Xu pmc_update_sample_period(pmc); 16546a010ddSJoerg Roedel return 0; 16646a010ddSJoerg Roedel } 16746a010ddSJoerg Roedel /* MSR_EVNTSELn */ 16846a010ddSJoerg Roedel pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL); 16946a010ddSJoerg Roedel if (pmc) { 1709b026073SJim Mattson data &= ~pmu->reserved_bits; 171fb121aafSLike Xu if (data != pmc->eventsel) { 172fb121aafSLike Xu pmc->eventsel = data; 1734fa5843dSLike Xu kvm_pmu_request_counter_reprogram(pmc); 174fb121aafSLike Xu } 17546a010ddSJoerg Roedel return 0; 17646a010ddSJoerg Roedel } 17746a010ddSJoerg Roedel 17846a010ddSJoerg Roedel return 1; 17946a010ddSJoerg Roedel } 18046a010ddSJoerg Roedel 18146a010ddSJoerg Roedel static void amd_pmu_refresh(struct kvm_vcpu *vcpu) 18246a010ddSJoerg Roedel { 18346a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 184*4a277189SLike Xu union cpuid_0x80000022_ebx ebx; 18546a010ddSJoerg Roedel 186*4a277189SLike Xu pmu->version = 1; 187*4a277189SLike Xu if (guest_cpuid_has(vcpu, X86_FEATURE_PERFMON_V2)) { 188*4a277189SLike Xu pmu->version = 2; 189*4a277189SLike Xu /* 190*4a277189SLike Xu * Note, PERFMON_V2 is also in 0x80000022.0x0, i.e. the guest 191*4a277189SLike Xu * CPUID entry is guaranteed to be non-NULL. 192*4a277189SLike Xu */ 193*4a277189SLike Xu BUILD_BUG_ON(x86_feature_cpuid(X86_FEATURE_PERFMON_V2).function != 0x80000022 || 194*4a277189SLike Xu x86_feature_cpuid(X86_FEATURE_PERFMON_V2).index); 195*4a277189SLike Xu ebx.full = kvm_find_cpuid_entry_index(vcpu, 0x80000022, 0)->ebx; 196*4a277189SLike Xu pmu->nr_arch_gp_counters = ebx.split.num_core_pmc; 197*4a277189SLike Xu } else if (guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE)) { 19846a010ddSJoerg Roedel pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS_CORE; 199*4a277189SLike Xu } else { 20046a010ddSJoerg Roedel pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS; 201*4a277189SLike Xu } 20246a010ddSJoerg Roedel 2031c2bf8a6SLike Xu pmu->nr_arch_gp_counters = min_t(unsigned int, pmu->nr_arch_gp_counters, 2041c2bf8a6SLike Xu kvm_pmu_cap.num_counters_gp); 2051c2bf8a6SLike Xu 206*4a277189SLike Xu if (pmu->version > 1) { 207*4a277189SLike Xu pmu->global_ctrl_mask = ~((1ull << pmu->nr_arch_gp_counters) - 1); 208*4a277189SLike Xu pmu->global_status_mask = pmu->global_ctrl_mask; 209*4a277189SLike Xu } 210*4a277189SLike Xu 21146a010ddSJoerg Roedel pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1; 212cb1d220dSLike Xu pmu->reserved_bits = 0xfffffff000280000ull; 21395b065bfSJim Mattson pmu->raw_event_mask = AMD64_RAW_EVENT_MASK; 21446a010ddSJoerg Roedel /* not applicable to AMD; but clean them to prevent any fall out */ 21546a010ddSJoerg Roedel pmu->counter_bitmask[KVM_PMC_FIXED] = 0; 21646a010ddSJoerg Roedel pmu->nr_arch_fixed_counters = 0; 21746a010ddSJoerg Roedel bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); 21846a010ddSJoerg Roedel } 21946a010ddSJoerg Roedel 22046a010ddSJoerg Roedel static void amd_pmu_init(struct kvm_vcpu *vcpu) 22146a010ddSJoerg Roedel { 22246a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 22346a010ddSJoerg Roedel int i; 22446a010ddSJoerg Roedel 225556f3c9aSLike Xu BUILD_BUG_ON(KVM_AMD_PMC_MAX_GENERIC > AMD64_NUM_COUNTERS_CORE); 226556f3c9aSLike Xu BUILD_BUG_ON(KVM_AMD_PMC_MAX_GENERIC > INTEL_PMC_MAX_GENERIC); 22746a010ddSJoerg Roedel 228556f3c9aSLike Xu for (i = 0; i < KVM_AMD_PMC_MAX_GENERIC ; i++) { 22946a010ddSJoerg Roedel pmu->gp_counters[i].type = KVM_PMC_GP; 23046a010ddSJoerg Roedel pmu->gp_counters[i].vcpu = vcpu; 23146a010ddSJoerg Roedel pmu->gp_counters[i].idx = i; 23246a010ddSJoerg Roedel pmu->gp_counters[i].current_config = 0; 23346a010ddSJoerg Roedel } 23446a010ddSJoerg Roedel } 23546a010ddSJoerg Roedel 23646a010ddSJoerg Roedel static void amd_pmu_reset(struct kvm_vcpu *vcpu) 23746a010ddSJoerg Roedel { 23846a010ddSJoerg Roedel struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); 23946a010ddSJoerg Roedel int i; 24046a010ddSJoerg Roedel 241556f3c9aSLike Xu for (i = 0; i < KVM_AMD_PMC_MAX_GENERIC; i++) { 24246a010ddSJoerg Roedel struct kvm_pmc *pmc = &pmu->gp_counters[i]; 24346a010ddSJoerg Roedel 24446a010ddSJoerg Roedel pmc_stop_counter(pmc); 245de0f6195SLike Xu pmc->counter = pmc->prev_counter = pmc->eventsel = 0; 24646a010ddSJoerg Roedel } 247*4a277189SLike Xu 248*4a277189SLike Xu pmu->global_ctrl = pmu->global_status = 0; 24946a010ddSJoerg Roedel } 25046a010ddSJoerg Roedel 25134886e79SLike Xu struct kvm_pmu_ops amd_pmu_ops __initdata = { 2527aadaa98SLike Xu .hw_event_available = amd_hw_event_available, 25346a010ddSJoerg Roedel .pmc_idx_to_pmc = amd_pmc_idx_to_pmc, 25446a010ddSJoerg Roedel .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, 25546a010ddSJoerg Roedel .msr_idx_to_pmc = amd_msr_idx_to_pmc, 25646a010ddSJoerg Roedel .is_valid_rdpmc_ecx = amd_is_valid_rdpmc_ecx, 25746a010ddSJoerg Roedel .is_valid_msr = amd_is_valid_msr, 25846a010ddSJoerg Roedel .get_msr = amd_pmu_get_msr, 25946a010ddSJoerg Roedel .set_msr = amd_pmu_set_msr, 26046a010ddSJoerg Roedel .refresh = amd_pmu_refresh, 26146a010ddSJoerg Roedel .init = amd_pmu_init, 26246a010ddSJoerg Roedel .reset = amd_pmu_reset, 2636a5cba7bSAaron Lewis .EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT, 2648911ce66SSean Christopherson .MAX_NR_GP_COUNTERS = KVM_AMD_PMC_MAX_GENERIC, 2656a08083fSLike Xu .MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS, 26646a010ddSJoerg Roedel }; 267