162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2023 Rivos Inc
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *     Atish Patra <atishp@rivosinc.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	"riscv-kvm-pmu: " fmt
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/kvm_host.h>
1362306a36Sopenharmony_ci#include <linux/perf/riscv_pmu.h>
1462306a36Sopenharmony_ci#include <asm/csr.h>
1562306a36Sopenharmony_ci#include <asm/kvm_vcpu_sbi.h>
1662306a36Sopenharmony_ci#include <asm/kvm_vcpu_pmu.h>
1762306a36Sopenharmony_ci#include <linux/bitops.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define kvm_pmu_num_counters(pmu) ((pmu)->num_hw_ctrs + (pmu)->num_fw_ctrs)
2062306a36Sopenharmony_ci#define get_event_type(x) (((x) & SBI_PMU_EVENT_IDX_TYPE_MASK) >> 16)
2162306a36Sopenharmony_ci#define get_event_code(x) ((x) & SBI_PMU_EVENT_IDX_CODE_MASK)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic enum perf_hw_id hw_event_perf_map[SBI_PMU_HW_GENERAL_MAX] = {
2462306a36Sopenharmony_ci	[SBI_PMU_HW_CPU_CYCLES] = PERF_COUNT_HW_CPU_CYCLES,
2562306a36Sopenharmony_ci	[SBI_PMU_HW_INSTRUCTIONS] = PERF_COUNT_HW_INSTRUCTIONS,
2662306a36Sopenharmony_ci	[SBI_PMU_HW_CACHE_REFERENCES] = PERF_COUNT_HW_CACHE_REFERENCES,
2762306a36Sopenharmony_ci	[SBI_PMU_HW_CACHE_MISSES] = PERF_COUNT_HW_CACHE_MISSES,
2862306a36Sopenharmony_ci	[SBI_PMU_HW_BRANCH_INSTRUCTIONS] = PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
2962306a36Sopenharmony_ci	[SBI_PMU_HW_BRANCH_MISSES] = PERF_COUNT_HW_BRANCH_MISSES,
3062306a36Sopenharmony_ci	[SBI_PMU_HW_BUS_CYCLES] = PERF_COUNT_HW_BUS_CYCLES,
3162306a36Sopenharmony_ci	[SBI_PMU_HW_STALLED_CYCLES_FRONTEND] = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
3262306a36Sopenharmony_ci	[SBI_PMU_HW_STALLED_CYCLES_BACKEND] = PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
3362306a36Sopenharmony_ci	[SBI_PMU_HW_REF_CPU_CYCLES] = PERF_COUNT_HW_REF_CPU_CYCLES,
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic u64 kvm_pmu_get_sample_period(struct kvm_pmc *pmc)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	u64 counter_val_mask = GENMASK(pmc->cinfo.width, 0);
3962306a36Sopenharmony_ci	u64 sample_period;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!pmc->counter_val)
4262306a36Sopenharmony_ci		sample_period = counter_val_mask + 1;
4362306a36Sopenharmony_ci	else
4462306a36Sopenharmony_ci		sample_period = (-pmc->counter_val) & counter_val_mask;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return sample_period;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic u32 kvm_pmu_get_perf_event_type(unsigned long eidx)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	enum sbi_pmu_event_type etype = get_event_type(eidx);
5262306a36Sopenharmony_ci	u32 type = PERF_TYPE_MAX;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	switch (etype) {
5562306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_HW:
5662306a36Sopenharmony_ci		type = PERF_TYPE_HARDWARE;
5762306a36Sopenharmony_ci		break;
5862306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_CACHE:
5962306a36Sopenharmony_ci		type = PERF_TYPE_HW_CACHE;
6062306a36Sopenharmony_ci		break;
6162306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_RAW:
6262306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_FW:
6362306a36Sopenharmony_ci		type = PERF_TYPE_RAW;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	default:
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return type;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic bool kvm_pmu_is_fw_event(unsigned long eidx)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	return get_event_type(eidx) == SBI_PMU_EVENT_TYPE_FW;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void kvm_pmu_release_perf_event(struct kvm_pmc *pmc)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	if (pmc->perf_event) {
8062306a36Sopenharmony_ci		perf_event_disable(pmc->perf_event);
8162306a36Sopenharmony_ci		perf_event_release_kernel(pmc->perf_event);
8262306a36Sopenharmony_ci		pmc->perf_event = NULL;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic u64 kvm_pmu_get_perf_event_hw_config(u32 sbi_event_code)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	return hw_event_perf_map[sbi_event_code];
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic u64 kvm_pmu_get_perf_event_cache_config(u32 sbi_event_code)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	u64 config = U64_MAX;
9462306a36Sopenharmony_ci	unsigned int cache_type, cache_op, cache_result;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* All the cache event masks lie within 0xFF. No separate masking is necessary */
9762306a36Sopenharmony_ci	cache_type = (sbi_event_code & SBI_PMU_EVENT_CACHE_ID_CODE_MASK) >>
9862306a36Sopenharmony_ci		      SBI_PMU_EVENT_CACHE_ID_SHIFT;
9962306a36Sopenharmony_ci	cache_op = (sbi_event_code & SBI_PMU_EVENT_CACHE_OP_ID_CODE_MASK) >>
10062306a36Sopenharmony_ci		    SBI_PMU_EVENT_CACHE_OP_SHIFT;
10162306a36Sopenharmony_ci	cache_result = sbi_event_code & SBI_PMU_EVENT_CACHE_RESULT_ID_CODE_MASK;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (cache_type >= PERF_COUNT_HW_CACHE_MAX ||
10462306a36Sopenharmony_ci	    cache_op >= PERF_COUNT_HW_CACHE_OP_MAX ||
10562306a36Sopenharmony_ci	    cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
10662306a36Sopenharmony_ci		return config;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	config = cache_type | (cache_op << 8) | (cache_result << 16);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return config;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic u64 kvm_pmu_get_perf_event_config(unsigned long eidx, uint64_t evt_data)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	enum sbi_pmu_event_type etype = get_event_type(eidx);
11662306a36Sopenharmony_ci	u32 ecode = get_event_code(eidx);
11762306a36Sopenharmony_ci	u64 config = U64_MAX;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	switch (etype) {
12062306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_HW:
12162306a36Sopenharmony_ci		if (ecode < SBI_PMU_HW_GENERAL_MAX)
12262306a36Sopenharmony_ci			config = kvm_pmu_get_perf_event_hw_config(ecode);
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_CACHE:
12562306a36Sopenharmony_ci		config = kvm_pmu_get_perf_event_cache_config(ecode);
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_RAW:
12862306a36Sopenharmony_ci		config = evt_data & RISCV_PMU_RAW_EVENT_MASK;
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	case SBI_PMU_EVENT_TYPE_FW:
13162306a36Sopenharmony_ci		if (ecode < SBI_PMU_FW_MAX)
13262306a36Sopenharmony_ci			config = (1ULL << 63) | ecode;
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	default:
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return config;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int kvm_pmu_get_fixed_pmc_index(unsigned long eidx)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	u32 etype = kvm_pmu_get_perf_event_type(eidx);
14462306a36Sopenharmony_ci	u32 ecode = get_event_code(eidx);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (etype != SBI_PMU_EVENT_TYPE_HW)
14762306a36Sopenharmony_ci		return -EINVAL;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (ecode == SBI_PMU_HW_CPU_CYCLES)
15062306a36Sopenharmony_ci		return 0;
15162306a36Sopenharmony_ci	else if (ecode == SBI_PMU_HW_INSTRUCTIONS)
15262306a36Sopenharmony_ci		return 2;
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		return -EINVAL;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int kvm_pmu_get_programmable_pmc_index(struct kvm_pmu *kvpmu, unsigned long eidx,
15862306a36Sopenharmony_ci					      unsigned long cbase, unsigned long cmask)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int ctr_idx = -1;
16162306a36Sopenharmony_ci	int i, pmc_idx;
16262306a36Sopenharmony_ci	int min, max;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (kvm_pmu_is_fw_event(eidx)) {
16562306a36Sopenharmony_ci		/* Firmware counters are mapped 1:1 starting from num_hw_ctrs for simplicity */
16662306a36Sopenharmony_ci		min = kvpmu->num_hw_ctrs;
16762306a36Sopenharmony_ci		max = min + kvpmu->num_fw_ctrs;
16862306a36Sopenharmony_ci	} else {
16962306a36Sopenharmony_ci		/* First 3 counters are reserved for fixed counters */
17062306a36Sopenharmony_ci		min = 3;
17162306a36Sopenharmony_ci		max = kvpmu->num_hw_ctrs;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	for_each_set_bit(i, &cmask, BITS_PER_LONG) {
17562306a36Sopenharmony_ci		pmc_idx = i + cbase;
17662306a36Sopenharmony_ci		if ((pmc_idx >= min && pmc_idx < max) &&
17762306a36Sopenharmony_ci		    !test_bit(pmc_idx, kvpmu->pmc_in_use)) {
17862306a36Sopenharmony_ci			ctr_idx = pmc_idx;
17962306a36Sopenharmony_ci			break;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return ctr_idx;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int pmu_get_pmc_index(struct kvm_pmu *pmu, unsigned long eidx,
18762306a36Sopenharmony_ci			     unsigned long cbase, unsigned long cmask)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	int ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Fixed counters need to be have fixed mapping as they have different width */
19262306a36Sopenharmony_ci	ret = kvm_pmu_get_fixed_pmc_index(eidx);
19362306a36Sopenharmony_ci	if (ret >= 0)
19462306a36Sopenharmony_ci		return ret;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return kvm_pmu_get_programmable_pmc_index(pmu, eidx, cbase, cmask);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
20062306a36Sopenharmony_ci			unsigned long *out_val)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
20362306a36Sopenharmony_ci	struct kvm_pmc *pmc;
20462306a36Sopenharmony_ci	u64 enabled, running;
20562306a36Sopenharmony_ci	int fevent_code;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	pmc = &kvpmu->pmc[cidx];
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
21062306a36Sopenharmony_ci		fevent_code = get_event_code(pmc->event_idx);
21162306a36Sopenharmony_ci		pmc->counter_val = kvpmu->fw_event[fevent_code].value;
21262306a36Sopenharmony_ci	} else if (pmc->perf_event) {
21362306a36Sopenharmony_ci		pmc->counter_val += perf_event_read_value(pmc->perf_event, &enabled, &running);
21462306a36Sopenharmony_ci	} else {
21562306a36Sopenharmony_ci		return -EINVAL;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	*out_val = pmc->counter_val;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int kvm_pmu_validate_counter_mask(struct kvm_pmu *kvpmu, unsigned long ctr_base,
22362306a36Sopenharmony_ci					 unsigned long ctr_mask)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	/* Make sure the we have a valid counter mask requested from the caller */
22662306a36Sopenharmony_ci	if (!ctr_mask || (ctr_base + __fls(ctr_mask) >= kvm_pmu_num_counters(kvpmu)))
22762306a36Sopenharmony_ci		return -EINVAL;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int kvm_pmu_create_perf_event(struct kvm_pmc *pmc, struct perf_event_attr *attr,
23362306a36Sopenharmony_ci				     unsigned long flags, unsigned long eidx, unsigned long evtdata)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct perf_event *event;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	kvm_pmu_release_perf_event(pmc);
23862306a36Sopenharmony_ci	attr->config = kvm_pmu_get_perf_event_config(eidx, evtdata);
23962306a36Sopenharmony_ci	if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE) {
24062306a36Sopenharmony_ci		//TODO: Do we really want to clear the value in hardware counter
24162306a36Sopenharmony_ci		pmc->counter_val = 0;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * Set the default sample_period for now. The guest specified value
24662306a36Sopenharmony_ci	 * will be updated in the start call.
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	attr->sample_period = kvm_pmu_get_sample_period(pmc);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	event = perf_event_create_kernel_counter(attr, -1, current, NULL, pmc);
25162306a36Sopenharmony_ci	if (IS_ERR(event)) {
25262306a36Sopenharmony_ci		pr_err("kvm pmu event creation failed for eidx %lx: %ld\n", eidx, PTR_ERR(event));
25362306a36Sopenharmony_ci		return PTR_ERR(event);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	pmc->perf_event = event;
25762306a36Sopenharmony_ci	if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
25862306a36Sopenharmony_ci		perf_event_enable(pmc->perf_event);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_incr_fw(struct kvm_vcpu *vcpu, unsigned long fid)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
26662306a36Sopenharmony_ci	struct kvm_fw_event *fevent;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!kvpmu || fid >= SBI_PMU_FW_MAX)
26962306a36Sopenharmony_ci		return -EINVAL;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	fevent = &kvpmu->fw_event[fid];
27262306a36Sopenharmony_ci	if (fevent->started)
27362306a36Sopenharmony_ci		fevent->value++;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_read_hpm(struct kvm_vcpu *vcpu, unsigned int csr_num,
27962306a36Sopenharmony_ci				unsigned long *val, unsigned long new_val,
28062306a36Sopenharmony_ci				unsigned long wr_mask)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
28362306a36Sopenharmony_ci	int cidx, ret = KVM_INSN_CONTINUE_NEXT_SEPC;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!kvpmu || !kvpmu->init_done) {
28662306a36Sopenharmony_ci		/*
28762306a36Sopenharmony_ci		 * In absence of sscofpmf in the platform, the guest OS may use
28862306a36Sopenharmony_ci		 * the legacy PMU driver to read cycle/instret. In that case,
28962306a36Sopenharmony_ci		 * just return 0 to avoid any illegal trap. However, any other
29062306a36Sopenharmony_ci		 * hpmcounter access should result in illegal trap as they must
29162306a36Sopenharmony_ci		 * be access through SBI PMU only.
29262306a36Sopenharmony_ci		 */
29362306a36Sopenharmony_ci		if (csr_num == CSR_CYCLE || csr_num == CSR_INSTRET) {
29462306a36Sopenharmony_ci			*val = 0;
29562306a36Sopenharmony_ci			return ret;
29662306a36Sopenharmony_ci		} else {
29762306a36Sopenharmony_ci			return KVM_INSN_ILLEGAL_TRAP;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* The counter CSR are read only. Thus, any write should result in illegal traps */
30262306a36Sopenharmony_ci	if (wr_mask)
30362306a36Sopenharmony_ci		return KVM_INSN_ILLEGAL_TRAP;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	cidx = csr_num - CSR_CYCLE;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (pmu_ctr_read(vcpu, cidx, val) < 0)
30862306a36Sopenharmony_ci		return KVM_INSN_ILLEGAL_TRAP;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return ret;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_num_ctrs(struct kvm_vcpu *vcpu,
31462306a36Sopenharmony_ci				struct kvm_vcpu_sbi_return *retdata)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	retdata->out_val = kvm_pmu_num_counters(kvpmu);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_ctr_info(struct kvm_vcpu *vcpu, unsigned long cidx,
32462306a36Sopenharmony_ci				struct kvm_vcpu_sbi_return *retdata)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (cidx > RISCV_KVM_MAX_COUNTERS || cidx == 1) {
32962306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_INVALID_PARAM;
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	retdata->out_val = kvpmu->pmc[cidx].cinfo.value;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_ctr_start(struct kvm_vcpu *vcpu, unsigned long ctr_base,
33962306a36Sopenharmony_ci				 unsigned long ctr_mask, unsigned long flags, u64 ival,
34062306a36Sopenharmony_ci				 struct kvm_vcpu_sbi_return *retdata)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
34362306a36Sopenharmony_ci	int i, pmc_index, sbiret = 0;
34462306a36Sopenharmony_ci	struct kvm_pmc *pmc;
34562306a36Sopenharmony_ci	int fevent_code;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (kvm_pmu_validate_counter_mask(kvpmu, ctr_base, ctr_mask) < 0) {
34862306a36Sopenharmony_ci		sbiret = SBI_ERR_INVALID_PARAM;
34962306a36Sopenharmony_ci		goto out;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Start the counters that have been configured and requested by the guest */
35362306a36Sopenharmony_ci	for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
35462306a36Sopenharmony_ci		pmc_index = i + ctr_base;
35562306a36Sopenharmony_ci		if (!test_bit(pmc_index, kvpmu->pmc_in_use))
35662306a36Sopenharmony_ci			continue;
35762306a36Sopenharmony_ci		pmc = &kvpmu->pmc[pmc_index];
35862306a36Sopenharmony_ci		if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
35962306a36Sopenharmony_ci			pmc->counter_val = ival;
36062306a36Sopenharmony_ci		if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
36162306a36Sopenharmony_ci			fevent_code = get_event_code(pmc->event_idx);
36262306a36Sopenharmony_ci			if (fevent_code >= SBI_PMU_FW_MAX) {
36362306a36Sopenharmony_ci				sbiret = SBI_ERR_INVALID_PARAM;
36462306a36Sopenharmony_ci				goto out;
36562306a36Sopenharmony_ci			}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci			/* Check if the counter was already started for some reason */
36862306a36Sopenharmony_ci			if (kvpmu->fw_event[fevent_code].started) {
36962306a36Sopenharmony_ci				sbiret = SBI_ERR_ALREADY_STARTED;
37062306a36Sopenharmony_ci				continue;
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci			kvpmu->fw_event[fevent_code].started = true;
37462306a36Sopenharmony_ci			kvpmu->fw_event[fevent_code].value = pmc->counter_val;
37562306a36Sopenharmony_ci		} else if (pmc->perf_event) {
37662306a36Sopenharmony_ci			if (unlikely(pmc->started)) {
37762306a36Sopenharmony_ci				sbiret = SBI_ERR_ALREADY_STARTED;
37862306a36Sopenharmony_ci				continue;
37962306a36Sopenharmony_ci			}
38062306a36Sopenharmony_ci			perf_event_period(pmc->perf_event, kvm_pmu_get_sample_period(pmc));
38162306a36Sopenharmony_ci			perf_event_enable(pmc->perf_event);
38262306a36Sopenharmony_ci			pmc->started = true;
38362306a36Sopenharmony_ci		} else {
38462306a36Sopenharmony_ci			sbiret = SBI_ERR_INVALID_PARAM;
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ciout:
38962306a36Sopenharmony_ci	retdata->err_val = sbiret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,
39562306a36Sopenharmony_ci				unsigned long ctr_mask, unsigned long flags,
39662306a36Sopenharmony_ci				struct kvm_vcpu_sbi_return *retdata)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
39962306a36Sopenharmony_ci	int i, pmc_index, sbiret = 0;
40062306a36Sopenharmony_ci	u64 enabled, running;
40162306a36Sopenharmony_ci	struct kvm_pmc *pmc;
40262306a36Sopenharmony_ci	int fevent_code;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (kvm_pmu_validate_counter_mask(kvpmu, ctr_base, ctr_mask) < 0) {
40562306a36Sopenharmony_ci		sbiret = SBI_ERR_INVALID_PARAM;
40662306a36Sopenharmony_ci		goto out;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Stop the counters that have been configured and requested by the guest */
41062306a36Sopenharmony_ci	for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
41162306a36Sopenharmony_ci		pmc_index = i + ctr_base;
41262306a36Sopenharmony_ci		if (!test_bit(pmc_index, kvpmu->pmc_in_use))
41362306a36Sopenharmony_ci			continue;
41462306a36Sopenharmony_ci		pmc = &kvpmu->pmc[pmc_index];
41562306a36Sopenharmony_ci		if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
41662306a36Sopenharmony_ci			fevent_code = get_event_code(pmc->event_idx);
41762306a36Sopenharmony_ci			if (fevent_code >= SBI_PMU_FW_MAX) {
41862306a36Sopenharmony_ci				sbiret = SBI_ERR_INVALID_PARAM;
41962306a36Sopenharmony_ci				goto out;
42062306a36Sopenharmony_ci			}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci			if (!kvpmu->fw_event[fevent_code].started)
42362306a36Sopenharmony_ci				sbiret = SBI_ERR_ALREADY_STOPPED;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci			kvpmu->fw_event[fevent_code].started = false;
42662306a36Sopenharmony_ci		} else if (pmc->perf_event) {
42762306a36Sopenharmony_ci			if (pmc->started) {
42862306a36Sopenharmony_ci				/* Stop counting the counter */
42962306a36Sopenharmony_ci				perf_event_disable(pmc->perf_event);
43062306a36Sopenharmony_ci				pmc->started = false;
43162306a36Sopenharmony_ci			} else {
43262306a36Sopenharmony_ci				sbiret = SBI_ERR_ALREADY_STOPPED;
43362306a36Sopenharmony_ci			}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci			if (flags & SBI_PMU_STOP_FLAG_RESET) {
43662306a36Sopenharmony_ci				/* Relase the counter if this is a reset request */
43762306a36Sopenharmony_ci				pmc->counter_val += perf_event_read_value(pmc->perf_event,
43862306a36Sopenharmony_ci									  &enabled, &running);
43962306a36Sopenharmony_ci				kvm_pmu_release_perf_event(pmc);
44062306a36Sopenharmony_ci			}
44162306a36Sopenharmony_ci		} else {
44262306a36Sopenharmony_ci			sbiret = SBI_ERR_INVALID_PARAM;
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci		if (flags & SBI_PMU_STOP_FLAG_RESET) {
44562306a36Sopenharmony_ci			pmc->event_idx = SBI_PMU_EVENT_IDX_INVALID;
44662306a36Sopenharmony_ci			clear_bit(pmc_index, kvpmu->pmc_in_use);
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ciout:
45162306a36Sopenharmony_ci	retdata->err_val = sbiret;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return 0;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsigned long ctr_base,
45762306a36Sopenharmony_ci				     unsigned long ctr_mask, unsigned long flags,
45862306a36Sopenharmony_ci				     unsigned long eidx, u64 evtdata,
45962306a36Sopenharmony_ci				     struct kvm_vcpu_sbi_return *retdata)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	int ctr_idx, ret, sbiret = 0;
46262306a36Sopenharmony_ci	bool is_fevent;
46362306a36Sopenharmony_ci	unsigned long event_code;
46462306a36Sopenharmony_ci	u32 etype = kvm_pmu_get_perf_event_type(eidx);
46562306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
46662306a36Sopenharmony_ci	struct kvm_pmc *pmc = NULL;
46762306a36Sopenharmony_ci	struct perf_event_attr attr = {
46862306a36Sopenharmony_ci		.type = etype,
46962306a36Sopenharmony_ci		.size = sizeof(struct perf_event_attr),
47062306a36Sopenharmony_ci		.pinned = true,
47162306a36Sopenharmony_ci		/*
47262306a36Sopenharmony_ci		 * It should never reach here if the platform doesn't support the sscofpmf
47362306a36Sopenharmony_ci		 * extension as mode filtering won't work without it.
47462306a36Sopenharmony_ci		 */
47562306a36Sopenharmony_ci		.exclude_host = true,
47662306a36Sopenharmony_ci		.exclude_hv = true,
47762306a36Sopenharmony_ci		.exclude_user = !!(flags & SBI_PMU_CFG_FLAG_SET_UINH),
47862306a36Sopenharmony_ci		.exclude_kernel = !!(flags & SBI_PMU_CFG_FLAG_SET_SINH),
47962306a36Sopenharmony_ci		.config1 = RISCV_PMU_CONFIG1_GUEST_EVENTS,
48062306a36Sopenharmony_ci	};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (kvm_pmu_validate_counter_mask(kvpmu, ctr_base, ctr_mask) < 0) {
48362306a36Sopenharmony_ci		sbiret = SBI_ERR_INVALID_PARAM;
48462306a36Sopenharmony_ci		goto out;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	event_code = get_event_code(eidx);
48862306a36Sopenharmony_ci	is_fevent = kvm_pmu_is_fw_event(eidx);
48962306a36Sopenharmony_ci	if (is_fevent && event_code >= SBI_PMU_FW_MAX) {
49062306a36Sopenharmony_ci		sbiret = SBI_ERR_NOT_SUPPORTED;
49162306a36Sopenharmony_ci		goto out;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/*
49562306a36Sopenharmony_ci	 * SKIP_MATCH flag indicates the caller is aware of the assigned counter
49662306a36Sopenharmony_ci	 * for this event. Just do a sanity check if it already marked used.
49762306a36Sopenharmony_ci	 */
49862306a36Sopenharmony_ci	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
49962306a36Sopenharmony_ci		if (!test_bit(ctr_base + __ffs(ctr_mask), kvpmu->pmc_in_use)) {
50062306a36Sopenharmony_ci			sbiret = SBI_ERR_FAILURE;
50162306a36Sopenharmony_ci			goto out;
50262306a36Sopenharmony_ci		}
50362306a36Sopenharmony_ci		ctr_idx = ctr_base + __ffs(ctr_mask);
50462306a36Sopenharmony_ci	} else  {
50562306a36Sopenharmony_ci		ctr_idx = pmu_get_pmc_index(kvpmu, eidx, ctr_base, ctr_mask);
50662306a36Sopenharmony_ci		if (ctr_idx < 0) {
50762306a36Sopenharmony_ci			sbiret = SBI_ERR_NOT_SUPPORTED;
50862306a36Sopenharmony_ci			goto out;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	pmc = &kvpmu->pmc[ctr_idx];
51362306a36Sopenharmony_ci	pmc->idx = ctr_idx;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (is_fevent) {
51662306a36Sopenharmony_ci		if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
51762306a36Sopenharmony_ci			kvpmu->fw_event[event_code].started = true;
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		ret = kvm_pmu_create_perf_event(pmc, &attr, flags, eidx, evtdata);
52062306a36Sopenharmony_ci		if (ret)
52162306a36Sopenharmony_ci			return ret;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	set_bit(ctr_idx, kvpmu->pmc_in_use);
52562306a36Sopenharmony_ci	pmc->event_idx = eidx;
52662306a36Sopenharmony_ci	retdata->out_val = ctr_idx;
52762306a36Sopenharmony_ciout:
52862306a36Sopenharmony_ci	retdata->err_val = sbiret;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ciint kvm_riscv_vcpu_pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
53462306a36Sopenharmony_ci				struct kvm_vcpu_sbi_return *retdata)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int ret;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ret = pmu_ctr_read(vcpu, cidx, &retdata->out_val);
53962306a36Sopenharmony_ci	if (ret == -EINVAL)
54062306a36Sopenharmony_ci		retdata->err_val = SBI_ERR_INVALID_PARAM;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_civoid kvm_riscv_vcpu_pmu_init(struct kvm_vcpu *vcpu)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	int i = 0, ret, num_hw_ctrs = 0, hpm_width = 0;
54862306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
54962306a36Sopenharmony_ci	struct kvm_pmc *pmc;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/*
55262306a36Sopenharmony_ci	 * PMU functionality should be only available to guests if privilege mode
55362306a36Sopenharmony_ci	 * filtering is available in the host. Otherwise, guest will always count
55462306a36Sopenharmony_ci	 * events while the execution is in hypervisor mode.
55562306a36Sopenharmony_ci	 */
55662306a36Sopenharmony_ci	if (!riscv_isa_extension_available(NULL, SSCOFPMF))
55762306a36Sopenharmony_ci		return;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	ret = riscv_pmu_get_hpm_info(&hpm_width, &num_hw_ctrs);
56062306a36Sopenharmony_ci	if (ret < 0 || !hpm_width || !num_hw_ctrs)
56162306a36Sopenharmony_ci		return;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/*
56462306a36Sopenharmony_ci	 * Increase the number of hardware counters to offset the time counter.
56562306a36Sopenharmony_ci	 */
56662306a36Sopenharmony_ci	kvpmu->num_hw_ctrs = num_hw_ctrs + 1;
56762306a36Sopenharmony_ci	kvpmu->num_fw_ctrs = SBI_PMU_FW_MAX;
56862306a36Sopenharmony_ci	memset(&kvpmu->fw_event, 0, SBI_PMU_FW_MAX * sizeof(struct kvm_fw_event));
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (kvpmu->num_hw_ctrs > RISCV_KVM_MAX_HW_CTRS) {
57162306a36Sopenharmony_ci		pr_warn_once("Limiting the hardware counters to 32 as specified by the ISA");
57262306a36Sopenharmony_ci		kvpmu->num_hw_ctrs = RISCV_KVM_MAX_HW_CTRS;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/*
57662306a36Sopenharmony_ci	 * There is no correlation between the logical hardware counter and virtual counters.
57762306a36Sopenharmony_ci	 * However, we need to encode a hpmcounter CSR in the counter info field so that
57862306a36Sopenharmony_ci	 * KVM can trap n emulate the read. This works well in the migration use case as
57962306a36Sopenharmony_ci	 * KVM doesn't care if the actual hpmcounter is available in the hardware or not.
58062306a36Sopenharmony_ci	 */
58162306a36Sopenharmony_ci	for (i = 0; i < kvm_pmu_num_counters(kvpmu); i++) {
58262306a36Sopenharmony_ci		/* TIME CSR shouldn't be read from perf interface */
58362306a36Sopenharmony_ci		if (i == 1)
58462306a36Sopenharmony_ci			continue;
58562306a36Sopenharmony_ci		pmc = &kvpmu->pmc[i];
58662306a36Sopenharmony_ci		pmc->idx = i;
58762306a36Sopenharmony_ci		pmc->event_idx = SBI_PMU_EVENT_IDX_INVALID;
58862306a36Sopenharmony_ci		if (i < kvpmu->num_hw_ctrs) {
58962306a36Sopenharmony_ci			pmc->cinfo.type = SBI_PMU_CTR_TYPE_HW;
59062306a36Sopenharmony_ci			if (i < 3)
59162306a36Sopenharmony_ci				/* CY, IR counters */
59262306a36Sopenharmony_ci				pmc->cinfo.width = 63;
59362306a36Sopenharmony_ci			else
59462306a36Sopenharmony_ci				pmc->cinfo.width = hpm_width;
59562306a36Sopenharmony_ci			/*
59662306a36Sopenharmony_ci			 * The CSR number doesn't have any relation with the logical
59762306a36Sopenharmony_ci			 * hardware counters. The CSR numbers are encoded sequentially
59862306a36Sopenharmony_ci			 * to avoid maintaining a map between the virtual counter
59962306a36Sopenharmony_ci			 * and CSR number.
60062306a36Sopenharmony_ci			 */
60162306a36Sopenharmony_ci			pmc->cinfo.csr = CSR_CYCLE + i;
60262306a36Sopenharmony_ci		} else {
60362306a36Sopenharmony_ci			pmc->cinfo.type = SBI_PMU_CTR_TYPE_FW;
60462306a36Sopenharmony_ci			pmc->cinfo.width = BITS_PER_LONG - 1;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	kvpmu->init_done = true;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_civoid kvm_riscv_vcpu_pmu_deinit(struct kvm_vcpu *vcpu)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
61462306a36Sopenharmony_ci	struct kvm_pmc *pmc;
61562306a36Sopenharmony_ci	int i;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (!kvpmu)
61862306a36Sopenharmony_ci		return;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	for_each_set_bit(i, kvpmu->pmc_in_use, RISCV_MAX_COUNTERS) {
62162306a36Sopenharmony_ci		pmc = &kvpmu->pmc[i];
62262306a36Sopenharmony_ci		pmc->counter_val = 0;
62362306a36Sopenharmony_ci		kvm_pmu_release_perf_event(pmc);
62462306a36Sopenharmony_ci		pmc->event_idx = SBI_PMU_EVENT_IDX_INVALID;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci	bitmap_zero(kvpmu->pmc_in_use, RISCV_MAX_COUNTERS);
62762306a36Sopenharmony_ci	memset(&kvpmu->fw_event, 0, SBI_PMU_FW_MAX * sizeof(struct kvm_fw_event));
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_civoid kvm_riscv_vcpu_pmu_reset(struct kvm_vcpu *vcpu)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	kvm_riscv_vcpu_pmu_deinit(vcpu);
63362306a36Sopenharmony_ci}
634