]> xenbits.xensource.com Git - people/pauldu/linux.git/commitdiff
KVM: arm64: Add PMU event filter bits required if EL3 is implemented
authorOliver Upton <oliver.upton@linux.dev>
Thu, 19 Oct 2023 18:56:18 +0000 (18:56 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Tue, 24 Oct 2023 19:26:14 +0000 (19:26 +0000)
Suzuki noticed that KVM's PMU emulation is oblivious to the NSU and NSK
event filter bits. On systems that have EL3 these bits modify the
filter behavior in non-secure EL0 and EL1, respectively. Even though the
kernel doesn't use these bits, it is entirely possible some other guest
OS does. Additionally, it would appear that these and the M bit are
required by the architecture if EL3 is implemented.

Allow the EL3 event filter bits to be set if EL3 is advertised in the
guest's ID register. Implement the behavior of NSU and NSK according to
the pseudocode, and entirely ignore the M bit for perf event creation.

Reported-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20231019185618.3442949-3-oliver.upton@linux.dev
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/pmu-emul.c
include/linux/perf/arm_pmuv3.h

index 32d83db9674ecfce7189807d796c8830e37423f8..e6be14e38ceee9865c92348f19a911481f25c9be 100644 (file)
@@ -69,6 +69,11 @@ u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
        if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL2, pfr0))
                mask |= ARMV8_PMU_INCLUDE_EL2;
 
+       if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL3, pfr0))
+               mask |= ARMV8_PMU_EXCLUDE_NS_EL0 |
+                       ARMV8_PMU_EXCLUDE_NS_EL1 |
+                       ARMV8_PMU_EXCLUDE_EL3;
+
        return mask;
 }
 
@@ -596,6 +601,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
        struct perf_event *event;
        struct perf_event_attr attr;
        u64 eventsel, reg, data;
+       bool p, u, nsk, nsu;
 
        reg = counter_index_to_evtreg(pmc->idx);
        data = __vcpu_sys_reg(vcpu, reg);
@@ -622,13 +628,18 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
            !test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
                return;
 
+       p = data & ARMV8_PMU_EXCLUDE_EL1;
+       u = data & ARMV8_PMU_EXCLUDE_EL0;
+       nsk = data & ARMV8_PMU_EXCLUDE_NS_EL1;
+       nsu = data & ARMV8_PMU_EXCLUDE_NS_EL0;
+
        memset(&attr, 0, sizeof(struct perf_event_attr));
        attr.type = arm_pmu->pmu.type;
        attr.size = sizeof(attr);
        attr.pinned = 1;
        attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
-       attr.exclude_user = data & ARMV8_PMU_EXCLUDE_EL0 ? 1 : 0;
-       attr.exclude_kernel = data & ARMV8_PMU_EXCLUDE_EL1 ? 1 : 0;
+       attr.exclude_user = (u != nsu);
+       attr.exclude_kernel = (p != nsk);
        attr.exclude_hv = 1; /* Don't count EL2 events */
        attr.exclude_host = 1; /* Don't count host events */
        attr.config = eventsel;
index e3899bd77f5cc5860e8c3182bd3797f1ca0002e7..9c226adf938a2a185f006533afeb01e347cceb4e 100644 (file)
 /*
  * Event filters for PMUv3
  */
-#define ARMV8_PMU_EXCLUDE_EL1  (1U << 31)
-#define ARMV8_PMU_EXCLUDE_EL0  (1U << 30)
-#define ARMV8_PMU_INCLUDE_EL2  (1U << 27)
+#define ARMV8_PMU_EXCLUDE_EL1          (1U << 31)
+#define ARMV8_PMU_EXCLUDE_EL0          (1U << 30)
+#define ARMV8_PMU_EXCLUDE_NS_EL1       (1U << 29)
+#define ARMV8_PMU_EXCLUDE_NS_EL0       (1U << 28)
+#define ARMV8_PMU_INCLUDE_EL2          (1U << 27)
+#define ARMV8_PMU_EXCLUDE_EL3          (1U << 26)
 
 /*
  * PMUSERENR: user enable reg