Skip to content

Commit 9404673

Browse files
author
Marc Zyngier
committed
KVM: arm64: timers: Correctly handle TGE flip with CNTPOFF_EL2
Contrary to common belief, HCR_EL2.TGE has a direct and immediate effect on the way the EL0 physical counter is offset. Flipping TGE from 1 to 0 while at EL2 immediately changes the way the counter compared to the CVAL limit. This means that we cannot directly save/restore the guest's view of CVAL, but that we instead must treat it as if CNTPOFF didn't exist. Only in the world switch, once we figure out that we do have CNTPOFF, can we must the offset back and forth depending on the polarity of TGE. Fixes: 2b4825a ("KVM: arm64: timers: Use CNTPOFF_EL2 to offset the physical timer") Reported-by: Ganapatrao Kulkarni <[email protected]> Tested-by: Ganapatrao Kulkarni <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent 839d903 commit 9404673

File tree

3 files changed

+54
-10
lines changed

3 files changed

+54
-10
lines changed

arch/arm64/kvm/arch_timer.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ static struct irq_ops arch_timer_irq_ops = {
5555
.get_input_level = kvm_arch_timer_get_input_level,
5656
};
5757

58-
static bool has_cntpoff(void)
59-
{
60-
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
61-
}
62-
6358
static int nr_timers(struct kvm_vcpu *vcpu)
6459
{
6560
if (!vcpu_has_nv(vcpu))
@@ -180,7 +175,7 @@ u64 kvm_phys_timer_read(void)
180175
return timecounter->cc->read(timecounter->cc);
181176
}
182177

183-
static void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map)
178+
void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map)
184179
{
185180
if (vcpu_has_nv(vcpu)) {
186181
if (is_hyp_ctxt(vcpu)) {
@@ -548,8 +543,7 @@ static void timer_save_state(struct arch_timer_context *ctx)
548543
timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
549544
cval = read_sysreg_el0(SYS_CNTP_CVAL);
550545

551-
if (!has_cntpoff())
552-
cval -= timer_get_offset(ctx);
546+
cval -= timer_get_offset(ctx);
553547

554548
timer_set_cval(ctx, cval);
555549

@@ -636,8 +630,7 @@ static void timer_restore_state(struct arch_timer_context *ctx)
636630
cval = timer_get_cval(ctx);
637631
offset = timer_get_offset(ctx);
638632
set_cntpoff(offset);
639-
if (!has_cntpoff())
640-
cval += offset;
633+
cval += offset;
641634
write_sysreg_el0(cval, SYS_CNTP_CVAL);
642635
isb();
643636
write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL);

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ static void __activate_traps(struct kvm_vcpu *vcpu)
3939

4040
___activate_traps(vcpu);
4141

42+
if (has_cntpoff()) {
43+
struct timer_map map;
44+
45+
get_timer_map(vcpu, &map);
46+
47+
/*
48+
* We're entrering the guest. Reload the correct
49+
* values from memory now that TGE is clear.
50+
*/
51+
if (map.direct_ptimer == vcpu_ptimer(vcpu))
52+
val = __vcpu_sys_reg(vcpu, CNTP_CVAL_EL0);
53+
if (map.direct_ptimer == vcpu_hptimer(vcpu))
54+
val = __vcpu_sys_reg(vcpu, CNTHP_CVAL_EL2);
55+
56+
if (map.direct_ptimer) {
57+
write_sysreg_el0(val, SYS_CNTP_CVAL);
58+
isb();
59+
}
60+
}
61+
4262
val = read_sysreg(cpacr_el1);
4363
val |= CPACR_ELx_TTA;
4464
val &= ~(CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN |
@@ -77,6 +97,30 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
7797

7898
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
7999

100+
if (has_cntpoff()) {
101+
struct timer_map map;
102+
u64 val, offset;
103+
104+
get_timer_map(vcpu, &map);
105+
106+
/*
107+
* We're exiting the guest. Save the latest CVAL value
108+
* to memory and apply the offset now that TGE is set.
109+
*/
110+
val = read_sysreg_el0(SYS_CNTP_CVAL);
111+
if (map.direct_ptimer == vcpu_ptimer(vcpu))
112+
__vcpu_sys_reg(vcpu, CNTP_CVAL_EL0) = val;
113+
if (map.direct_ptimer == vcpu_hptimer(vcpu))
114+
__vcpu_sys_reg(vcpu, CNTHP_CVAL_EL2) = val;
115+
116+
offset = read_sysreg_s(SYS_CNTPOFF_EL2);
117+
118+
if (map.direct_ptimer && offset) {
119+
write_sysreg_el0(val + offset, SYS_CNTP_CVAL);
120+
isb();
121+
}
122+
}
123+
80124
/*
81125
* ARM errata 1165522 and 1530923 require the actual execution of the
82126
* above before we can switch to the EL2/EL0 translation regime used by

include/kvm/arm_arch_timer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ struct timer_map {
8282
struct arch_timer_context *emul_ptimer;
8383
};
8484

85+
void get_timer_map(struct kvm_vcpu *vcpu, struct timer_map *map);
86+
8587
struct arch_timer_cpu {
8688
struct arch_timer_context timers[NR_KVM_TIMERS];
8789

@@ -145,4 +147,9 @@ u64 timer_get_cval(struct arch_timer_context *ctxt);
145147
void kvm_timer_cpu_up(void);
146148
void kvm_timer_cpu_down(void);
147149

150+
static inline bool has_cntpoff(void)
151+
{
152+
return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
153+
}
154+
148155
#endif

0 commit comments

Comments
 (0)