Commit 4bad3068 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

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: default avatarChristoffer Dall <christoffer.dall@arm.com>
Signed-off-by: default avatarChristoffer Dall <christoffer.dall@arm.com>
Acked-by: default avatarOliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20241217142321.763801-3-maz@kernel.org


Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent b59dbb91
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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);

		/*
+1 −0
Original line number Diff line number Diff line
@@ -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);