On 12/06/2026 20:28, Colton Lewis wrote:
Save and restore newly untrapped registers that can be directly
accessed by the guest when the PMU is partitioned.

* PMEVCNTRn_EL0
* PMCCNTR_EL0
* PMSELR_EL0
* PMCR_EL0
* PMCNTEN_EL0
* PMINTEN_EL1

If we know we are not partitioned (that is, using the emulated vPMU),
then return immediately. A later patch will make this lazy so the
context swaps don't happen unless the guest has accessed the PMU.

PMEVTYPER is handled in a following patch since we must apply the KVM
event filter before writing values to hardware.

PMOVS guest counters are cleared to avoid the possibility of
generating spurious interrupts when PMINTEN is written. This is fine
because the virtual register for PMOVS is always the canonical value.

Signed-off-by: Colton Lewis <[email protected]>
---
  arch/arm/include/asm/arm_pmuv3.h |   4 +
  arch/arm64/kvm/arm.c             |   2 +
  arch/arm64/kvm/pmu-direct.c      | 183 +++++++++++++++++++++++++++++++
  include/kvm/arm_pmu.h            |  16 +++
  4 files changed, 205 insertions(+)


[...]

+
+/**
+ * kvm_pmu_put() - Put untrapped PMU registers
+ * @vcpu: Pointer to struct kvm_vcpu
+ *
+ * Put all untrapped PMU registers from the VCPU into the PCPU. Mask
+ * to only bits belonging to guest-reserved counters and leave
+ * host-reserved counters alone in bitmask registers.
+ */
+void kvm_pmu_put(struct kvm_vcpu *vcpu)
+{
+       struct arm_pmu *pmu;
+       unsigned long guest_counters;
+       unsigned long flags;
+       u64 mask;
+       u8 i;
+       u64 val;
+
+       /*
+        * If we aren't guest-owned then we know the guest is not
+        * accessing the PMU anyway, so no need to bother with the
+        * swap.
+        */
+       if (!kvm_pmu_is_partitioned(vcpu->kvm))
+               return;
+
+       preempt_disable();
+
+       pmu = vcpu->kvm->arch.arm_pmu;
+       guest_counters = kvm_pmu_guest_counter_mask(pmu);
+
+       for_each_set_bit(i, &guest_counters, ARMPMU_MAX_HWEVENTS) {
+               if (i == ARMV8_PMU_CYCLE_IDX)
+                       val = read_pmccntr();
+               else
+                       val = read_pmevcntrn(i);
+
+               __vcpu_assign_sys_reg(vcpu, PMEVCNTR0_EL0 + i, val);
+       }
+
+       val = read_sysreg(pmselr_el0);
+       __vcpu_assign_sys_reg(vcpu, PMSELR_EL0, val);
+
+       val = read_sysreg(pmcr_el0);
+       __vcpu_assign_sys_reg(vcpu, PMCR_EL0, val);
+
+       /* Mask these to only save the guest relevant bits. */
+       mask = kvm_pmu_guest_counter_mask(pmu);
+
+       val = read_sysreg(pmcntenset_el0);
+       __vcpu_assign_sys_reg(vcpu, PMCNTENSET_EL0, val & mask);
+
+       val = read_sysreg(pmintenset_el1);
+       __vcpu_assign_sys_reg(vcpu, PMINTENSET_EL1, val & mask);
+
+       /* Save pending guest hardware overflows. */
+       local_irq_save(flags);
+       val = read_sysreg(pmovsset_el0);
+       __vcpu_rmw_sys_reg(vcpu, PMOVSSET_EL0, |=, val & mask);
+       write_sysreg(val & mask, pmovsclr_el0);
+       local_irq_restore(flags);
+
+       /* Stop guest counters and disable interrupts in hardware. */
+       write_sysreg(mask, pmcntenclr_el0);
+       write_sysreg(mask, pmintenclr_el1);
+
+       kvm_pmu_set_guest_counters(pmu, 0);

Hi Colton,

This function doesn't get added until "KVM: arm64: Apply dynamic guest counter reservations" a few commits later.

kvm_pmu_guest_counter_mask() is also used in a commit before it's added.


Reply via email to