[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