162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * KVM PMU support for AMD
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2015, Red Hat, Inc. and/or its affiliates.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author:
862306a36Sopenharmony_ci *   Wei Huang <wei@redhat.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Implementation is based on pmu_intel.c file
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/kvm_host.h>
1662306a36Sopenharmony_ci#include <linux/perf_event.h>
1762306a36Sopenharmony_ci#include "x86.h"
1862306a36Sopenharmony_ci#include "cpuid.h"
1962306a36Sopenharmony_ci#include "lapic.h"
2062306a36Sopenharmony_ci#include "pmu.h"
2162306a36Sopenharmony_ci#include "svm.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cienum pmu_type {
2462306a36Sopenharmony_ci	PMU_TYPE_COUNTER = 0,
2562306a36Sopenharmony_ci	PMU_TYPE_EVNTSEL,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic struct kvm_pmc *amd_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned int num_counters = pmu->nr_arch_gp_counters;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (pmc_idx >= num_counters)
3362306a36Sopenharmony_ci		return NULL;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return &pmu->gp_counters[array_index_nospec(pmc_idx, num_counters)];
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr,
3962306a36Sopenharmony_ci					     enum pmu_type type)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu);
4262306a36Sopenharmony_ci	unsigned int idx;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!vcpu->kvm->arch.enable_pmu)
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	switch (msr) {
4862306a36Sopenharmony_ci	case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
4962306a36Sopenharmony_ci		if (!guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE))
5062306a36Sopenharmony_ci			return NULL;
5162306a36Sopenharmony_ci		/*
5262306a36Sopenharmony_ci		 * Each PMU counter has a pair of CTL and CTR MSRs. CTLn
5362306a36Sopenharmony_ci		 * MSRs (accessed via EVNTSEL) are even, CTRn MSRs are odd.
5462306a36Sopenharmony_ci		 */
5562306a36Sopenharmony_ci		idx = (unsigned int)((msr - MSR_F15H_PERF_CTL0) / 2);
5662306a36Sopenharmony_ci		if (!(msr & 0x1) != (type == PMU_TYPE_EVNTSEL))
5762306a36Sopenharmony_ci			return NULL;
5862306a36Sopenharmony_ci		break;
5962306a36Sopenharmony_ci	case MSR_K7_EVNTSEL0 ... MSR_K7_EVNTSEL3:
6062306a36Sopenharmony_ci		if (type != PMU_TYPE_EVNTSEL)
6162306a36Sopenharmony_ci			return NULL;
6262306a36Sopenharmony_ci		idx = msr - MSR_K7_EVNTSEL0;
6362306a36Sopenharmony_ci		break;
6462306a36Sopenharmony_ci	case MSR_K7_PERFCTR0 ... MSR_K7_PERFCTR3:
6562306a36Sopenharmony_ci		if (type != PMU_TYPE_COUNTER)
6662306a36Sopenharmony_ci			return NULL;
6762306a36Sopenharmony_ci		idx = msr - MSR_K7_PERFCTR0;
6862306a36Sopenharmony_ci		break;
6962306a36Sopenharmony_ci	default:
7062306a36Sopenharmony_ci		return NULL;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return amd_pmc_idx_to_pmc(pmu, idx);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic bool amd_hw_event_available(struct kvm_pmc *pmc)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	return true;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic bool amd_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	idx &= ~(3u << 30);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return idx < pmu->nr_arch_gp_counters;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* idx is the ECX register of RDPMC instruction */
9162306a36Sopenharmony_cistatic struct kvm_pmc *amd_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
9262306a36Sopenharmony_ci	unsigned int idx, u64 *mask)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return amd_pmc_idx_to_pmc(vcpu_to_pmu(vcpu), idx & ~(3u << 30));
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, u32 msr)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
10062306a36Sopenharmony_ci	struct kvm_pmc *pmc;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER);
10362306a36Sopenharmony_ci	pmc = pmc ? pmc : get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return pmc;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	switch (msr) {
11362306a36Sopenharmony_ci	case MSR_K7_EVNTSEL0 ... MSR_K7_PERFCTR3:
11462306a36Sopenharmony_ci		return pmu->version > 0;
11562306a36Sopenharmony_ci	case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
11662306a36Sopenharmony_ci		return guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE);
11762306a36Sopenharmony_ci	case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS:
11862306a36Sopenharmony_ci	case MSR_AMD64_PERF_CNTR_GLOBAL_CTL:
11962306a36Sopenharmony_ci	case MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR:
12062306a36Sopenharmony_ci		return pmu->version > 1;
12162306a36Sopenharmony_ci	default:
12262306a36Sopenharmony_ci		if (msr > MSR_F15H_PERF_CTR5 &&
12362306a36Sopenharmony_ci		    msr < MSR_F15H_PERF_CTL0 + 2 * pmu->nr_arch_gp_counters)
12462306a36Sopenharmony_ci			return pmu->version > 1;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return amd_msr_idx_to_pmc(vcpu, msr);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int amd_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
13462306a36Sopenharmony_ci	struct kvm_pmc *pmc;
13562306a36Sopenharmony_ci	u32 msr = msr_info->index;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* MSR_PERFCTRn */
13862306a36Sopenharmony_ci	pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER);
13962306a36Sopenharmony_ci	if (pmc) {
14062306a36Sopenharmony_ci		msr_info->data = pmc_read_counter(pmc);
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	/* MSR_EVNTSELn */
14462306a36Sopenharmony_ci	pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL);
14562306a36Sopenharmony_ci	if (pmc) {
14662306a36Sopenharmony_ci		msr_info->data = pmc->eventsel;
14762306a36Sopenharmony_ci		return 0;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 1;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
15662306a36Sopenharmony_ci	struct kvm_pmc *pmc;
15762306a36Sopenharmony_ci	u32 msr = msr_info->index;
15862306a36Sopenharmony_ci	u64 data = msr_info->data;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* MSR_PERFCTRn */
16162306a36Sopenharmony_ci	pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_COUNTER);
16262306a36Sopenharmony_ci	if (pmc) {
16362306a36Sopenharmony_ci		pmc_write_counter(pmc, data);
16462306a36Sopenharmony_ci		pmc_update_sample_period(pmc);
16562306a36Sopenharmony_ci		return 0;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci	/* MSR_EVNTSELn */
16862306a36Sopenharmony_ci	pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL);
16962306a36Sopenharmony_ci	if (pmc) {
17062306a36Sopenharmony_ci		data &= ~pmu->reserved_bits;
17162306a36Sopenharmony_ci		if (data != pmc->eventsel) {
17262306a36Sopenharmony_ci			pmc->eventsel = data;
17362306a36Sopenharmony_ci			kvm_pmu_request_counter_reprogram(pmc);
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci		return 0;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 1;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void amd_pmu_refresh(struct kvm_vcpu *vcpu)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
18462306a36Sopenharmony_ci	union cpuid_0x80000022_ebx ebx;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	pmu->version = 1;
18762306a36Sopenharmony_ci	if (guest_cpuid_has(vcpu, X86_FEATURE_PERFMON_V2)) {
18862306a36Sopenharmony_ci		pmu->version = 2;
18962306a36Sopenharmony_ci		/*
19062306a36Sopenharmony_ci		 * Note, PERFMON_V2 is also in 0x80000022.0x0, i.e. the guest
19162306a36Sopenharmony_ci		 * CPUID entry is guaranteed to be non-NULL.
19262306a36Sopenharmony_ci		 */
19362306a36Sopenharmony_ci		BUILD_BUG_ON(x86_feature_cpuid(X86_FEATURE_PERFMON_V2).function != 0x80000022 ||
19462306a36Sopenharmony_ci			     x86_feature_cpuid(X86_FEATURE_PERFMON_V2).index);
19562306a36Sopenharmony_ci		ebx.full = kvm_find_cpuid_entry_index(vcpu, 0x80000022, 0)->ebx;
19662306a36Sopenharmony_ci		pmu->nr_arch_gp_counters = ebx.split.num_core_pmc;
19762306a36Sopenharmony_ci	} else if (guest_cpuid_has(vcpu, X86_FEATURE_PERFCTR_CORE)) {
19862306a36Sopenharmony_ci		pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS_CORE;
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	pmu->nr_arch_gp_counters = min_t(unsigned int, pmu->nr_arch_gp_counters,
20462306a36Sopenharmony_ci					 kvm_pmu_cap.num_counters_gp);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (pmu->version > 1) {
20762306a36Sopenharmony_ci		pmu->global_ctrl_mask = ~((1ull << pmu->nr_arch_gp_counters) - 1);
20862306a36Sopenharmony_ci		pmu->global_status_mask = pmu->global_ctrl_mask;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1;
21262306a36Sopenharmony_ci	pmu->reserved_bits = 0xfffffff000280000ull;
21362306a36Sopenharmony_ci	pmu->raw_event_mask = AMD64_RAW_EVENT_MASK;
21462306a36Sopenharmony_ci	/* not applicable to AMD; but clean them to prevent any fall out */
21562306a36Sopenharmony_ci	pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
21662306a36Sopenharmony_ci	pmu->nr_arch_fixed_counters = 0;
21762306a36Sopenharmony_ci	bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void amd_pmu_init(struct kvm_vcpu *vcpu)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
22362306a36Sopenharmony_ci	int i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	BUILD_BUG_ON(KVM_AMD_PMC_MAX_GENERIC > AMD64_NUM_COUNTERS_CORE);
22662306a36Sopenharmony_ci	BUILD_BUG_ON(KVM_AMD_PMC_MAX_GENERIC > INTEL_PMC_MAX_GENERIC);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	for (i = 0; i < KVM_AMD_PMC_MAX_GENERIC ; i++) {
22962306a36Sopenharmony_ci		pmu->gp_counters[i].type = KVM_PMC_GP;
23062306a36Sopenharmony_ci		pmu->gp_counters[i].vcpu = vcpu;
23162306a36Sopenharmony_ci		pmu->gp_counters[i].idx = i;
23262306a36Sopenharmony_ci		pmu->gp_counters[i].current_config = 0;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistruct kvm_pmu_ops amd_pmu_ops __initdata = {
23762306a36Sopenharmony_ci	.hw_event_available = amd_hw_event_available,
23862306a36Sopenharmony_ci	.pmc_idx_to_pmc = amd_pmc_idx_to_pmc,
23962306a36Sopenharmony_ci	.rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
24062306a36Sopenharmony_ci	.msr_idx_to_pmc = amd_msr_idx_to_pmc,
24162306a36Sopenharmony_ci	.is_valid_rdpmc_ecx = amd_is_valid_rdpmc_ecx,
24262306a36Sopenharmony_ci	.is_valid_msr = amd_is_valid_msr,
24362306a36Sopenharmony_ci	.get_msr = amd_pmu_get_msr,
24462306a36Sopenharmony_ci	.set_msr = amd_pmu_set_msr,
24562306a36Sopenharmony_ci	.refresh = amd_pmu_refresh,
24662306a36Sopenharmony_ci	.init = amd_pmu_init,
24762306a36Sopenharmony_ci	.EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT,
24862306a36Sopenharmony_ci	.MAX_NR_GP_COUNTERS = KVM_AMD_PMC_MAX_GENERIC,
24962306a36Sopenharmony_ci	.MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS,
25062306a36Sopenharmony_ci};
251