KVM: arm64: nv: Sync nested timer state with FEAT_NV2
Emulating the timers with FEAT_NV2 is a bit odd, as the timers can be reconfigured behind our back without the hypervisor even noticing. In the VHE case, that's an actual regression in the architecture... Co-developed-by: Christoffer Dall <christoffer.dall@arm.com> Signed-off-by: Christoffer Dall <christoffer.dall@arm.com> Acked-by: Oliver Upton <oliver.upton@linux.dev> Link: https://lore.kernel.org/r/20241217142321.763801-3-maz@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
parent
b59dbb91f7
commit
4bad3068cf
3 changed files with 48 additions and 0 deletions
|
@ -905,6 +905,50 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
|
|||
kvm_timer_blocking(vcpu);
|
||||
}
|
||||
|
||||
void kvm_timer_sync_nested(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* When NV2 is on, guest hypervisors have their EL1 timer register
|
||||
* accesses redirected to the VNCR page. Any guest action taken on
|
||||
* the timer is postponed until the next exit, leading to a very
|
||||
* poor quality of emulation.
|
||||
*/
|
||||
if (!is_hyp_ctxt(vcpu))
|
||||
return;
|
||||
|
||||
if (!vcpu_el2_e2h_is_set(vcpu)) {
|
||||
/*
|
||||
* A non-VHE guest hypervisor doesn't have any direct access
|
||||
* to its timers: the EL2 registers trap (and the HW is
|
||||
* fully emulated), while the EL0 registers access memory
|
||||
* despite the access being notionally direct. Boo.
|
||||
*
|
||||
* We update the hardware timer registers with the
|
||||
* latest value written by the guest to the VNCR page
|
||||
* and let the hardware take care of the rest.
|
||||
*/
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CTL_EL0), SYS_CNTV_CTL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTV_CVAL_EL0), SYS_CNTV_CVAL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTP_CTL_EL0), SYS_CNTP_CTL);
|
||||
write_sysreg_el0(__vcpu_sys_reg(vcpu, CNTP_CVAL_EL0), SYS_CNTP_CVAL);
|
||||
} else {
|
||||
/*
|
||||
* For a VHE guest hypervisor, the EL2 state is directly
|
||||
* stored in the host EL1 timers, while the emulated EL0
|
||||
* state is stored in the VNCR page. The latter could have
|
||||
* been updated behind our back, and we must reset the
|
||||
* emulation of the timers.
|
||||
*/
|
||||
struct timer_map map;
|
||||
get_timer_map(vcpu, &map);
|
||||
|
||||
soft_timer_cancel(&map.emul_vtimer->hrtimer);
|
||||
soft_timer_cancel(&map.emul_ptimer->hrtimer);
|
||||
timer_emulate(map.emul_vtimer);
|
||||
timer_emulate(map.emul_ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* With a userspace irqchip we have to check if the guest de-asserted the
|
||||
* timer and if so, unmask the timer irq signal on the host interrupt
|
||||
|
|
|
@ -1228,6 +1228,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
|
||||
kvm_timer_sync_user(vcpu);
|
||||
|
||||
if (vcpu_has_nv(vcpu))
|
||||
kvm_timer_sync_nested(vcpu);
|
||||
|
||||
kvm_arch_vcpu_ctxsync_fp(vcpu);
|
||||
|
||||
/*
|
||||
|
|
|
@ -98,6 +98,7 @@ int __init kvm_timer_hyp_init(bool has_gic);
|
|||
int kvm_timer_enable(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_nested(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_sync_user(struct kvm_vcpu *vcpu);
|
||||
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
|
||||
void kvm_timer_update_run(struct kvm_vcpu *vcpu);
|
||||
|
|
Loading…
Add table
Reference in a new issue