diff --git a/arch/arm64/kvm/pmu-direct.c b/arch/arm64/kvm/pmu-direct.c
index 92eec0fa33867..a22c9258c2452 100644
--- a/arch/arm64/kvm/pmu-direct.c
+++ b/arch/arm64/kvm/pmu-direct.c
@@ -169,6 +169,82 @@ u64 kvm_pmu_guest_counter_mask(void)
return kvm_vcpu_pmu_guest_counter_mask(kvm_get_running_vcpu());
}
+/**
+ * kvm_pmu_apply_single_event_filter() - Apply event filter to a single counter
+ * @vcpu: Pointer to vcpu struct
+ * @idx: Counter index
+ *
+ * Compute the filtered event value and write it directly to the hardware register.
+ */
+void kvm_pmu_apply_single_event_filter(struct kvm_vcpu *vcpu, u8 idx)
+{
+ struct arm_pmu *pmu = vcpu->kvm->arch.arm_pmu;
+ u64 guest_counters;
+ u64 evtyper_set = ARMV8_PMU_EXCLUDE_EL0 |
+ ARMV8_PMU_EXCLUDE_EL1;
+ u64 evtyper_clr = ARMV8_PMU_INCLUDE_EL2;
+ bool guest_include_el2;
+ u64 val;
+ u64 evsel;
+
+ if (!pmu || vcpu != kvm_get_running_vcpu())
+ return;
+
+ guest_counters = kvm_vcpu_pmu_guest_counter_mask(vcpu);
+ if (!test_bit(idx, (unsigned long *)&guest_counters))
+ return;
+
+ if (idx == ARMV8_PMU_CYCLE_IDX) {
+ val = __vcpu_sys_reg(vcpu, PMCCFILTR_EL0);
+ evsel = ARMV8_PMUV3_PERFCTR_CPU_CYCLES;
+ } else {
+ val = __vcpu_sys_reg(vcpu, PMEVTYPER0_EL0 + idx);
+ evsel = val & kvm_pmu_event_mask(vcpu->kvm);
+ }
+
+ guest_include_el2 = (val & ARMV8_PMU_INCLUDE_EL2);
+ val &= ~evtyper_clr;
+
+ if (unlikely(is_hyp_ctxt(vcpu))) {
+ if (guest_include_el2)
+ val &= ~ARMV8_PMU_EXCLUDE_EL1;
+ else
+ val |= ARMV8_PMU_EXCLUDE_EL1;
+ }
+
+ if (vcpu->kvm->arch.pmu_filter &&
+ !test_bit(evsel, vcpu->kvm->arch.pmu_filter))
+ val |= evtyper_set;
+
+ if (idx == ARMV8_PMU_CYCLE_IDX)
+ write_pmccfiltr(val);
+ else
+ write_pmevtypern(idx, val);
+}
+
+/**
+ * kvm_pmu_apply_event_filter() - Apply event filter to all guest counters
+ * @vcpu: Pointer to vcpu struct
+ *
+ * To uphold the guarantee of the KVM PMU event filter, we must ensure
+ * no counter counts if the event is filtered. Accomplish this by
+ * filtering all exception levels if the event is filtered.
+ */
+static void kvm_pmu_apply_event_filter(struct kvm_vcpu *vcpu)
+{
+ struct arm_pmu *pmu = vcpu->kvm->arch.arm_pmu;
+ unsigned long guest_counters;
+ u8 i;
+
+ if (!pmu)
+ return;
+
+ guest_counters = kvm_vcpu_pmu_guest_counter_mask(vcpu);
+
+ for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS)
+ kvm_pmu_apply_single_event_filter(vcpu, i);
+}
+
/**
* kvm_pmu_load() - Load untrapped PMU registers
* @vcpu: Pointer to struct kvm_vcpu@@ -194,6 +270,7 @@ void kvm_pmu_load(struct kvm_vcpu *vcpu)
preempt_disable();
guest_counters = kvm_vcpu_pmu_guest_counter_mask(vcpu);
+ kvm_pmu_apply_event_filter(vcpu);
for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) {
val = __vcpu_sys_reg(vcpu, PMEVCNTR0_EL0 + i);diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index ebcf52261df65..c4aa6a448b6ca 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1137,6 +1137,7 @@ static void pmu_reg_write(struct kvm_vcpu *vcpu, enum vcpu_sysreg reg, u64 val,
if (kvm_pmu_is_partitioned(vcpu->kvm)) {
mask = kvm_pmu_evtyper_mask(vcpu->kvm);
__vcpu_assign_sys_reg(vcpu, reg, val & mask);
+ kvm_pmu_apply_single_event_filter(vcpu, idx);
} else {
kvm_pmu_set_counter_event_type(vcpu, val, idx);
kvm_vcpu_pmu_restore_guest(vcpu);diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 2604a6a46d5f3..ddbbbb050b9de 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -104,6 +104,7 @@ u64 kvm_pmu_host_counter_mask(void);
u64 kvm_pmu_guest_counter_mask(void);
void kvm_pmu_load(struct kvm_vcpu *vcpu);
void kvm_pmu_put(struct kvm_vcpu *vcpu);
+void kvm_pmu_apply_single_event_filter(struct kvm_vcpu *vcpu, u8 idx);
/*
* Updates the vcpu's view of the pmu events for this cpu.
@@ -263,6 +264,8 @@ static inline u64 kvm_pmu_guest_counter_mask(void)
return 0;
}
+static inline void kvm_pmu_apply_single_event_filter(struct kvm_vcpu *vcpu, u8 idx) {}
+
static inline bool has_kvm_pmu_partition_support(void)
{
return false;--
2.56.0.rc1.310.g51773c2048-goog