[PATCH 11/21] KVM: arm64: Context swap Partitioned PMU guest registers
Colton Lewis
coltonlewis at google.com
Tue Jul 21 16:05:06 PDT 2026
James Clark <james.clark at linaro.org> writes:
> 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 <coltonlewis at google.com>
>> ---
>> 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.
My bad. I'll correct that.
More information about the linux-arm-kernel
mailing list