]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Compute PMCEID from arm_pmu's event bitmaps
authorOliver Upton <oliver.upton@linux.dev>
Wed, 5 Mar 2025 20:26:30 +0000 (12:26 -0800)
committerOliver Upton <oliver.upton@linux.dev>
Tue, 11 Mar 2025 19:52:32 +0000 (12:52 -0700)
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: Janne Grunau <j@jannau.net>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250305202641.428114-4-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/pmu-emul.c

index 6c5950b9ceac884e30fae742b6c20b1c1a2b22f5..104672a0c5a21bf415cc932b8904cb92c321f5c4 100644 (file)
@@ -842,8 +842,42 @@ static struct arm_pmu *kvm_pmu_probe_armpmu(void)
        return pmu;
 }
 
+static u64 __compute_pmceid(struct arm_pmu *pmu, bool pmceid1)
+{
+       u32 hi[2], lo[2];
+
+       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);
+
+       /* always support CHAIN */
+       val |= BIT(ARMV8_PMUV3_PERFCTR_CHAIN);
+       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
+        */
+       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;
@@ -852,19 +886,10 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
                return 0;
 
        if (!pmceid1) {
-               val = read_sysreg(pmceid0_el0);
-               /* always support CHAIN */
-               val |= BIT(ARMV8_PMUV3_PERFCTR_CHAIN);
+               val = compute_pmceid0(cpu_pmu);
                base = 0;
        } else {
-               val = read_sysreg(pmceid1_el0);
-               /*
-                * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled
-                * as RAZ
-                */
-               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));
+               val = compute_pmceid1(cpu_pmu);
                base = 32;
        }