Skip to content

Commit 688c50a

Browse files
Christoffer Dallchazy
authored andcommitted
KVM: arm/arm64: Move timer save/restore out of the hyp code
As we are about to be lazy with saving and restoring the timer registers, we prepare by moving all possible timer configuration logic out of the hyp code. All virtual timer registers can be programmed from EL1 and since the arch timer is always a level triggered interrupt we can safely do this with interrupts disabled in the host kernel on the way to the guest without taking vtimer interrupts in the host kernel (yet). The downside is that the cntvoff register can only be programmed from hyp mode, so we jump into hyp mode and back to program it. This is also safe, because the host kernel doesn't use the virtual timer in the KVM code. It may add a little performance performance penalty, but only until following commits where we move this operation to vcpu load/put. Signed-off-by: Christoffer Dall <cdall@linaro.org> Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent f2a2129 commit 688c50a

File tree

8 files changed

+95
-52
lines changed

8 files changed

+95
-52
lines changed

arch/arm/include/asm/kvm_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
6868
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
6969
extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
7070

71+
extern void __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high);
72+
7173
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
7274

7375
extern void __init_stage2_translation(void);

arch/arm/include/asm/kvm_hyp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@
9898
#define cntvoff_el2 CNTVOFF
9999
#define cnthctl_el2 CNTHCTL
100100

101-
void __timer_save_state(struct kvm_vcpu *vcpu);
102-
void __timer_restore_state(struct kvm_vcpu *vcpu);
101+
void __timer_enable_traps(struct kvm_vcpu *vcpu);
102+
void __timer_disable_traps(struct kvm_vcpu *vcpu);
103103

104104
void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
105105
void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);

arch/arm/kvm/hyp/switch.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
174174
__activate_vm(vcpu);
175175

176176
__vgic_restore_state(vcpu);
177-
__timer_restore_state(vcpu);
177+
__timer_enable_traps(vcpu);
178178

179179
__sysreg_restore_state(guest_ctxt);
180180
__banked_restore_state(guest_ctxt);
@@ -191,7 +191,8 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
191191

192192
__banked_save_state(guest_ctxt);
193193
__sysreg_save_state(guest_ctxt);
194-
__timer_save_state(vcpu);
194+
__timer_disable_traps(vcpu);
195+
195196
__vgic_save_state(vcpu);
196197

197198
__deactivate_traps(vcpu);
@@ -237,7 +238,7 @@ void __hyp_text __noreturn __hyp_panic(int cause)
237238

238239
vcpu = (struct kvm_vcpu *)read_sysreg(HTPIDR);
239240
host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
240-
__timer_save_state(vcpu);
241+
__timer_disable_traps(vcpu);
241242
__deactivate_traps(vcpu);
242243
__deactivate_vm(vcpu);
243244
__banked_restore_state(host_ctxt);

arch/arm64/include/asm/kvm_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
5555
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
5656
extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
5757

58+
extern void __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high);
59+
5860
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
5961

6062
extern u64 __vgic_v3_get_ich_vtr_el2(void);

arch/arm64/include/asm/kvm_hyp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
129129
void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
130130
int __vgic_v3_perform_cpuif_access(struct kvm_vcpu *vcpu);
131131

132-
void __timer_save_state(struct kvm_vcpu *vcpu);
133-
void __timer_restore_state(struct kvm_vcpu *vcpu);
132+
void __timer_enable_traps(struct kvm_vcpu *vcpu);
133+
void __timer_disable_traps(struct kvm_vcpu *vcpu);
134134

135135
void __sysreg_save_host_state(struct kvm_cpu_context *ctxt);
136136
void __sysreg_restore_host_state(struct kvm_cpu_context *ctxt);

arch/arm64/kvm/hyp/switch.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
298298
__activate_vm(vcpu);
299299

300300
__vgic_restore_state(vcpu);
301-
__timer_restore_state(vcpu);
301+
__timer_enable_traps(vcpu);
302302

303303
/*
304304
* We must restore the 32-bit state before the sysregs, thanks
@@ -368,7 +368,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
368368

369369
__sysreg_save_guest_state(guest_ctxt);
370370
__sysreg32_save_state(vcpu);
371-
__timer_save_state(vcpu);
371+
__timer_disable_traps(vcpu);
372372
__vgic_save_state(vcpu);
373373

374374
__deactivate_traps(vcpu);
@@ -436,7 +436,7 @@ void __hyp_text __noreturn __hyp_panic(void)
436436

437437
vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
438438
host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
439-
__timer_save_state(vcpu);
439+
__timer_disable_traps(vcpu);
440440
__deactivate_traps(vcpu);
441441
__deactivate_vm(vcpu);
442442
__sysreg_restore_host_state(host_ctxt);

virt/kvm/arm/arch_timer.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,20 @@ static void phys_timer_emulate(struct kvm_vcpu *vcpu,
271271
soft_timer_start(&timer->phys_timer, kvm_timer_compute_delta(timer_ctx));
272272
}
273273

274+
static void timer_save_state(struct kvm_vcpu *vcpu)
275+
{
276+
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
277+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
278+
279+
if (timer->enabled) {
280+
vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
281+
vtimer->cnt_cval = read_sysreg_el0(cntv_cval);
282+
}
283+
284+
/* Disable the virtual timer */
285+
write_sysreg_el0(0, cntv_ctl);
286+
}
287+
274288
/*
275289
* Schedule the background timer before calling kvm_vcpu_block, so that this
276290
* thread is removed from its waitqueue and made runnable when there's a timer
@@ -304,13 +318,40 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu)
304318
soft_timer_start(&timer->bg_timer, kvm_timer_earliest_exp(vcpu));
305319
}
306320

321+
static void timer_restore_state(struct kvm_vcpu *vcpu)
322+
{
323+
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
324+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
325+
326+
if (timer->enabled) {
327+
write_sysreg_el0(vtimer->cnt_cval, cntv_cval);
328+
isb();
329+
write_sysreg_el0(vtimer->cnt_ctl, cntv_ctl);
330+
}
331+
}
332+
307333
void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
308334
{
309335
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
310336

311337
soft_timer_cancel(&timer->bg_timer, &timer->expired);
312338
}
313339

340+
static void set_cntvoff(u64 cntvoff)
341+
{
342+
u32 low = lower_32_bits(cntvoff);
343+
u32 high = upper_32_bits(cntvoff);
344+
345+
/*
346+
* Since kvm_call_hyp doesn't fully support the ARM PCS especially on
347+
* 32-bit systems, but rather passes register by register shifted one
348+
* place (we put the function address in r0/x0), we cannot simply pass
349+
* a 64-bit value as an argument, but have to split the value in two
350+
* 32-bit halves.
351+
*/
352+
kvm_call_hyp(__kvm_timer_set_cntvoff, low, high);
353+
}
354+
314355
static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu)
315356
{
316357
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
@@ -414,6 +455,7 @@ static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu)
414455
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
415456
{
416457
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
458+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
417459

418460
if (unlikely(!timer->enabled))
419461
return;
@@ -427,6 +469,9 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
427469
kvm_timer_flush_hwstate_user(vcpu);
428470
else
429471
kvm_timer_flush_hwstate_vgic(vcpu);
472+
473+
set_cntvoff(vtimer->cntvoff);
474+
timer_restore_state(vcpu);
430475
}
431476

432477
/**
@@ -446,6 +491,9 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
446491
*/
447492
soft_timer_cancel(&timer->phys_timer, NULL);
448493

494+
timer_save_state(vcpu);
495+
set_cntvoff(0);
496+
449497
/*
450498
* The guest could have modified the timer registers or the timer
451499
* could have expired, update the timer state.

virt/kvm/arm/hyp/timer-sr.c

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,58 +21,48 @@
2121

2222
#include <asm/kvm_hyp.h>
2323

24-
/* vcpu is already in the HYP VA space */
25-
void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
24+
void __hyp_text __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high)
25+
{
26+
u64 cntvoff = (u64)cntvoff_high << 32 | cntvoff_low;
27+
write_sysreg(cntvoff, cntvoff_el2);
28+
}
29+
30+
void __hyp_text enable_el1_phys_timer_access(void)
2631
{
27-
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
28-
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
2932
u64 val;
3033

31-
if (timer->enabled) {
32-
vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
33-
vtimer->cnt_cval = read_sysreg_el0(cntv_cval);
34-
}
34+
/* Allow physical timer/counter access for the host */
35+
val = read_sysreg(cnthctl_el2);
36+
val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
37+
write_sysreg(val, cnthctl_el2);
38+
}
3539

36-
/* Disable the virtual timer */
37-
write_sysreg_el0(0, cntv_ctl);
40+
void __hyp_text disable_el1_phys_timer_access(void)
41+
{
42+
u64 val;
3843

44+
/*
45+
* Disallow physical timer access for the guest
46+
* Physical counter access is allowed
47+
*/
48+
val = read_sysreg(cnthctl_el2);
49+
val &= ~CNTHCTL_EL1PCEN;
50+
val |= CNTHCTL_EL1PCTEN;
51+
write_sysreg(val, cnthctl_el2);
52+
}
53+
54+
void __hyp_text __timer_disable_traps(struct kvm_vcpu *vcpu)
55+
{
3956
/*
4057
* We don't need to do this for VHE since the host kernel runs in EL2
4158
* with HCR_EL2.TGE ==1, which makes those bits have no impact.
4259
*/
43-
if (!has_vhe()) {
44-
/* Allow physical timer/counter access for the host */
45-
val = read_sysreg(cnthctl_el2);
46-
val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
47-
write_sysreg(val, cnthctl_el2);
48-
}
49-
50-
/* Clear cntvoff for the host */
51-
write_sysreg(0, cntvoff_el2);
60+
if (!has_vhe())
61+
enable_el1_phys_timer_access();
5262
}
5363

54-
void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
64+
void __hyp_text __timer_enable_traps(struct kvm_vcpu *vcpu)
5565
{
56-
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
57-
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
58-
u64 val;
59-
60-
/* Those bits are already configured at boot on VHE-system */
61-
if (!has_vhe()) {
62-
/*
63-
* Disallow physical timer access for the guest
64-
* Physical counter access is allowed
65-
*/
66-
val = read_sysreg(cnthctl_el2);
67-
val &= ~CNTHCTL_EL1PCEN;
68-
val |= CNTHCTL_EL1PCTEN;
69-
write_sysreg(val, cnthctl_el2);
70-
}
71-
72-
if (timer->enabled) {
73-
write_sysreg(vtimer->cntvoff, cntvoff_el2);
74-
write_sysreg_el0(vtimer->cnt_cval, cntv_cval);
75-
isb();
76-
write_sysreg_el0(vtimer->cnt_ctl, cntv_ctl);
77-
}
66+
if (!has_vhe())
67+
disable_el1_phys_timer_access();
7868
}

0 commit comments

Comments
 (0)