Commit 93b01528 authored by Oliver Upton's avatar Oliver Upton
Browse files

KVM: arm64: Compute PMCEID from arm_pmu's event bitmaps



The PMUv3 driver populates a couple of bitmaps with the values of
PMCEID{0,1}, from which the guest's PMCEID{0,1} can be derived. This
is particularly convenient when virtualizing PMUv3 on IMP DEF hardware,
as reading the nonexistent PMCEID registers leads to a rather unpleasant
UNDEF.

Tested-by: default avatarJanne Grunau <j@jannau.net>
Reviewed-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250305202641.428114-4-oliver.upton@linux.dev


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 46573d94
Loading
Loading
Loading
Loading
+36 −11
Original line number Diff line number Diff line
@@ -842,22 +842,29 @@ static struct arm_pmu *kvm_pmu_probe_armpmu(void)
	return pmu;
}

u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)
{
	unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
	u64 val, mask = 0;
	int base, i, nr_events;
	u32 hi[2], lo[2];

	if (!kvm_vcpu_has_pmu(vcpu))
		return 0;
	bitmap_to_arr32(lo, pmu->pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);
	bitmap_to_arr32(hi, pmu->pmceid_ext_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);

	return ((u64)hi[pmceid1] << 32) | lo[pmceid1];
}

static u64 compute_pmceid0(struct arm_pmu *pmu)
{
	u64 val = __compute_pmceid(pmu, 0);

	if (!pmceid1) {
		val = read_sysreg(pmceid0_el0);
	/* always support CHAIN */
	val |= BIT(ARMV8_PMUV3_PERFCTR_CHAIN);
		base = 0;
	} else {
		val = read_sysreg(pmceid1_el0);
	return val;
}

static u64 compute_pmceid1(struct arm_pmu *pmu)
{
	u64 val = __compute_pmceid(pmu, 1);

	/*
	 * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled
	 * as RAZ
@@ -865,6 +872,24 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
	val &= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) |
		 BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) |
		 BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32));
	return val;
}

u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
{
	struct arm_pmu *cpu_pmu = vcpu->kvm->arch.arm_pmu;
	unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
	u64 val, mask = 0;
	int base, i, nr_events;

	if (!kvm_vcpu_has_pmu(vcpu))
		return 0;

	if (!pmceid1) {
		val = compute_pmceid0(cpu_pmu);
		base = 0;
	} else {
		val = compute_pmceid1(cpu_pmu);
		base = 32;
	}