Skip to content

Commit 26d2d05

Browse files
author
Marc Zyngier
committed
KVM: arm64: PMU: Do not let AArch32 change the counters' top 32 bits
Even when using PMUv3p5 (which implies 64bit counters), there is no way for AArch32 to write to the top 32 bits of the counters. The only way to influence these bits (other than by counting events) is by writing PMCR.P==1. Make sure we obey the architecture and preserve the top 32 bits on a counter update. Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9917264 commit 26d2d05

File tree

1 file changed

+27
-8
lines changed

1 file changed

+27
-8
lines changed

arch/arm64/kvm/pmu-emul.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,8 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx)
119119
return counter;
120120
}
121121

122-
/**
123-
* kvm_pmu_set_counter_value - set PMU counter value
124-
* @vcpu: The vcpu pointer
125-
* @select_idx: The counter index
126-
* @val: The counter value
127-
*/
128-
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
122+
static void kvm_pmu_set_counter(struct kvm_vcpu *vcpu, u64 select_idx, u64 val,
123+
bool force)
129124
{
130125
u64 reg;
131126

@@ -135,12 +130,36 @@ void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
135130
kvm_pmu_release_perf_event(&vcpu->arch.pmu.pmc[select_idx]);
136131

137132
reg = counter_index_to_reg(select_idx);
133+
134+
if (vcpu_mode_is_32bit(vcpu) && select_idx != ARMV8_PMU_CYCLE_IDX &&
135+
!force) {
136+
/*
137+
* Even with PMUv3p5, AArch32 cannot write to the top
138+
* 32bit of the counters. The only possible course of
139+
* action is to use PMCR.P, which will reset them to
140+
* 0 (the only use of the 'force' parameter).
141+
*/
142+
val = __vcpu_sys_reg(vcpu, reg) & GENMASK(63, 32);
143+
val |= lower_32_bits(val);
144+
}
145+
138146
__vcpu_sys_reg(vcpu, reg) = val;
139147

140148
/* Recreate the perf event to reflect the updated sample_period */
141149
kvm_pmu_create_perf_event(vcpu, select_idx);
142150
}
143151

152+
/**
153+
* kvm_pmu_set_counter_value - set PMU counter value
154+
* @vcpu: The vcpu pointer
155+
* @select_idx: The counter index
156+
* @val: The counter value
157+
*/
158+
void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val)
159+
{
160+
kvm_pmu_set_counter(vcpu, select_idx, val, false);
161+
}
162+
144163
/**
145164
* kvm_pmu_release_perf_event - remove the perf event
146165
* @pmc: The PMU counter pointer
@@ -533,7 +552,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
533552
unsigned long mask = kvm_pmu_valid_counter_mask(vcpu);
534553
mask &= ~BIT(ARMV8_PMU_CYCLE_IDX);
535554
for_each_set_bit(i, &mask, 32)
536-
kvm_pmu_set_counter_value(vcpu, i, 0);
555+
kvm_pmu_set_counter(vcpu, i, 0, true);
537556
}
538557
}
539558

0 commit comments

Comments
 (0)