Commit a9ac6c37 authored by Atish Patra's avatar Atish Patra Committed by Anup Patel
Browse files

RISC-V: KVM: Implement trap & emulate for hpmcounters



As the KVM guests only see the virtual PMU counters, all hpmcounter
access should trap and KVM emulates the read access on behalf of guests.

Reviewed-by: default avatarAnup Patel <anup@brainfault.org>
Reviewed-by: default avatarAndrew Jones <ajones@ventanamicro.com>
Signed-off-by: default avatarAtish Patra <atishp@rivosinc.com>
Signed-off-by: default avatarAnup Patel <anup@brainfault.org>
parent f04bafb5
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -44,6 +44,19 @@ struct kvm_pmu {
#define vcpu_to_pmu(vcpu) (&(vcpu)->arch.pmu_context)
#define pmu_to_vcpu(pmu)  (container_of((pmu), struct kvm_vcpu, arch.pmu_context))

#if defined(CONFIG_32BIT)
#define KVM_RISCV_VCPU_HPMCOUNTER_CSR_FUNCS \
{.base = CSR_CYCLEH,	.count = 31,	.func = kvm_riscv_vcpu_pmu_read_hpm }, \
{.base = CSR_CYCLE,	.count = 31,	.func = kvm_riscv_vcpu_pmu_read_hpm },
#else
#define KVM_RISCV_VCPU_HPMCOUNTER_CSR_FUNCS \
{.base = CSR_CYCLE,	.count = 31,	.func = kvm_riscv_vcpu_pmu_read_hpm },
#endif

int kvm_riscv_vcpu_pmu_read_hpm(struct kvm_vcpu *vcpu, unsigned int csr_num,
				unsigned long *val, unsigned long new_val,
				unsigned long wr_mask);

int kvm_riscv_vcpu_pmu_num_ctrs(struct kvm_vcpu *vcpu, struct kvm_vcpu_sbi_return *retdata);
int kvm_riscv_vcpu_pmu_ctr_info(struct kvm_vcpu *vcpu, unsigned long cidx,
				struct kvm_vcpu_sbi_return *retdata);
@@ -67,6 +80,9 @@ void kvm_riscv_vcpu_pmu_reset(struct kvm_vcpu *vcpu);
struct kvm_pmu {
};

#define KVM_RISCV_VCPU_HPMCOUNTER_CSR_FUNCS \
{.base = 0,	.count = 0,	.func = NULL },

static inline void kvm_riscv_vcpu_pmu_init(struct kvm_vcpu *vcpu) {}
static inline void kvm_riscv_vcpu_pmu_deinit(struct kvm_vcpu *vcpu) {}
static inline void kvm_riscv_vcpu_pmu_reset(struct kvm_vcpu *vcpu) {}
+3 −1
Original line number Diff line number Diff line
@@ -213,7 +213,9 @@ struct csr_func {
		    unsigned long wr_mask);
};

static const struct csr_func csr_funcs[] = { };
static const struct csr_func csr_funcs[] = {
	KVM_RISCV_VCPU_HPMCOUNTER_CSR_FUNCS
};

/**
 * kvm_riscv_vcpu_csr_return -- Handle CSR read/write after user space
+58 −1
Original line number Diff line number Diff line
@@ -17,6 +17,58 @@

#define kvm_pmu_num_counters(pmu) ((pmu)->num_hw_ctrs + (pmu)->num_fw_ctrs)

static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
			unsigned long *out_val)
{
	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
	struct kvm_pmc *pmc;
	u64 enabled, running;

	pmc = &kvpmu->pmc[cidx];
	if (!pmc->perf_event)
		return -EINVAL;

	pmc->counter_val += perf_event_read_value(pmc->perf_event, &enabled, &running);
	*out_val = pmc->counter_val;

	return 0;
}

int kvm_riscv_vcpu_pmu_read_hpm(struct kvm_vcpu *vcpu, unsigned int csr_num,
				unsigned long *val, unsigned long new_val,
				unsigned long wr_mask)
{
	struct kvm_pmu *kvpmu = vcpu_to_pmu(vcpu);
	int cidx, ret = KVM_INSN_CONTINUE_NEXT_SEPC;

	if (!kvpmu || !kvpmu->init_done) {
		/*
		 * In absence of sscofpmf in the platform, the guest OS may use
		 * the legacy PMU driver to read cycle/instret. In that case,
		 * just return 0 to avoid any illegal trap. However, any other
		 * hpmcounter access should result in illegal trap as they must
		 * be access through SBI PMU only.
		 */
		if (csr_num == CSR_CYCLE || csr_num == CSR_INSTRET) {
			*val = 0;
			return ret;
		} else {
			return KVM_INSN_ILLEGAL_TRAP;
		}
	}

	/* The counter CSR are read only. Thus, any write should result in illegal traps */
	if (wr_mask)
		return KVM_INSN_ILLEGAL_TRAP;

	cidx = csr_num - CSR_CYCLE;

	if (pmu_ctr_read(vcpu, cidx, val) < 0)
		return KVM_INSN_ILLEGAL_TRAP;

	return ret;
}

int kvm_riscv_vcpu_pmu_num_ctrs(struct kvm_vcpu *vcpu,
				struct kvm_vcpu_sbi_return *retdata)
{
@@ -70,7 +122,12 @@ int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsigned long ctr_ba
int kvm_riscv_vcpu_pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
				struct kvm_vcpu_sbi_return *retdata)
{
	/* TODO */
	int ret;

	ret = pmu_ctr_read(vcpu, cidx, &retdata->out_val);
	if (ret == -EINVAL)
		retdata->err_val = SBI_ERR_INVALID_PARAM;

	return 0;
}