When a counter is disabled, its value is sampled before the event is being disabled, and the value written back in the shadow register. In that process, the value gets truncated to 32bit, which is adequate for any counter but the cycle counter, which can be configured to hold a 64bit value. This obviously results in a corrupted counter, and things like "perf record -e cycles" not working at all when run in a guest... Make the truncation conditional on the counter not being 64bit. Fixes: 80f393a23be6 ("KVM: arm/arm64: Support chained PMU counters") Cc: Andrew Murray <andrew.murray@xxxxxxx> Reported-by: Julien Thierry Julien Thierry <julien.thierry.kdev@xxxxxxxxx> Signed-off-by: Marc Zyngier <maz@xxxxxxxxxx> --- virt/kvm/arm/pmu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/virt/kvm/arm/pmu.c b/virt/kvm/arm/pmu.c index 362a01886bab..d716aef2bae9 100644 --- a/virt/kvm/arm/pmu.c +++ b/virt/kvm/arm/pmu.c @@ -206,9 +206,11 @@ static void kvm_pmu_stop_counter(struct kvm_vcpu *vcpu, struct kvm_pmc *pmc) __vcpu_sys_reg(vcpu, reg) = lower_32_bits(counter); __vcpu_sys_reg(vcpu, reg + 1) = upper_32_bits(counter); } else { + if (!kvm_pmu_idx_is_64bit(vcpu, pmc->idx)) + counter = lower_32_bits(counter); reg = (pmc->idx == ARMV8_PMU_CYCLE_IDX) ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + pmc->idx; - __vcpu_sys_reg(vcpu, reg) = lower_32_bits(counter); + __vcpu_sys_reg(vcpu, reg) = counter; } kvm_pmu_release_perf_event(pmc); -- 2.20.1