Skip to content

Commit 64074ca

Browse files
akihikodakioupton
authored andcommitted
KVM: arm64: PMU: Fix SET_ONE_REG for vPMC regs
Reload the perf event when setting the vPMU counter (vPMC) registers (PMCCNTR_EL0 and PMEVCNTR<n>_EL0). This is a change corresponding to commit 9228b26 ("KVM: arm64: PMU: Fix GET_ONE_REG for vPMC regs to return the current value") but for SET_ONE_REG. Values of vPMC registers are saved in sysreg files on certain occasions. These saved values don't represent the current values of the vPMC registers if the perf events for the vPMCs count events after the save. The current values of those registers are the sum of the sysreg file value and the current perf event counter value. But, when userspace writes those registers (using KVM_SET_ONE_REG), KVM only updates the sysreg file value and leaves the current perf event counter value as is. It is also important to keep the correct state even if userspace writes them after first run, specifically when debugging Windows on QEMU with GDB; QEMU tries to write back all visible registers when resuming the VM execution with GDB, corrupting the PMU state. Windows always uses the PMU so this can cause adverse effects on that particular OS. Fix this by releasing the current perf event and trigger recreating one with KVM_REQ_RELOAD_PMU. Fixes: 051ff58 ("arm64: KVM: Add access handler for event counter register") Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com> Reviewed-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20250315-pmc-v5-3-ecee87dab216@daynix.com Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent be5ccac commit 64074ca

File tree

3 files changed

+35
-1
lines changed

3 files changed

+35
-1
lines changed

arch/arm64/kvm/pmu-emul.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,19 @@ void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
191191
kvm_pmu_set_pmc_value(kvm_vcpu_idx_to_pmc(vcpu, select_idx), val, false);
192192
}
193193

194+
/**
195+
* kvm_pmu_set_counter_value_user - set PMU counter value from user
196+
* @vcpu: The vcpu pointer
197+
* @select_idx: The counter index
198+
* @val: The counter value
199+
*/
200+
void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
201+
{
202+
kvm_pmu_release_perf_event(kvm_vcpu_idx_to_pmc(vcpu, select_idx));
203+
__vcpu_sys_reg(vcpu, counter_index_to_reg(select_idx)) = val;
204+
kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
205+
}
206+
194207
/**
195208
* kvm_pmu_release_perf_event - remove the perf event
196209
* @pmc: The PMU counter pointer

arch/arm64/kvm/sys_regs.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,22 @@ static int get_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
960960
return 0;
961961
}
962962

963+
static int set_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
964+
u64 val)
965+
{
966+
u64 idx;
967+
968+
if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 0)
969+
/* PMCCNTR_EL0 */
970+
idx = ARMV8_PMU_CYCLE_IDX;
971+
else
972+
/* PMEVCNTRn_EL0 */
973+
idx = ((r->CRm & 3) << 3) | (r->Op2 & 7);
974+
975+
kvm_pmu_set_counter_value_user(vcpu, idx, val);
976+
return 0;
977+
}
978+
963979
static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
964980
struct sys_reg_params *p,
965981
const struct sys_reg_desc *r)
@@ -1238,6 +1254,7 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
12381254
#define PMU_PMEVCNTR_EL0(n) \
12391255
{ PMU_SYS_REG(PMEVCNTRn_EL0(n)), \
12401256
.reset = reset_pmevcntr, .get_user = get_pmu_evcntr, \
1257+
.set_user = set_pmu_evcntr, \
12411258
.access = access_pmu_evcntr, .reg = (PMEVCNTR0_EL0 + n), }
12421259

12431260
/* Macro to expand the PMEVTYPERn_EL0 register */
@@ -2835,7 +2852,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
28352852
.access = access_pmceid, .reset = NULL },
28362853
{ PMU_SYS_REG(PMCCNTR_EL0),
28372854
.access = access_pmu_evcntr, .reset = reset_unknown,
2838-
.reg = PMCCNTR_EL0, .get_user = get_pmu_evcntr},
2855+
.reg = PMCCNTR_EL0, .get_user = get_pmu_evcntr,
2856+
.set_user = set_pmu_evcntr },
28392857
{ PMU_SYS_REG(PMXEVTYPER_EL0),
28402858
.access = access_pmu_evtyper, .reset = NULL },
28412859
{ PMU_SYS_REG(PMXEVCNTR_EL0),

include/kvm/arm_pmu.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static __always_inline bool kvm_arm_support_pmu_v3(void)
4747
#define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num >= VGIC_NR_SGIS)
4848
u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx);
4949
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
50+
void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu, u64 select_idx, u64 val);
5051
u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu);
5152
u64 kvm_pmu_accessible_counter_mask(struct kvm_vcpu *vcpu);
5253
u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1);
@@ -115,6 +116,8 @@ static inline u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu,
115116
}
116117
static inline void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu,
117118
u64 select_idx, u64 val) {}
119+
static inline void kvm_pmu_set_counter_value_user(struct kvm_vcpu *vcpu,
120+
u64 select_idx, u64 val) {}
118121
static inline u64 kvm_pmu_implemented_counter_mask(struct kvm_vcpu *vcpu)
119122
{
120123
return 0;

0 commit comments

Comments
 (0)