On x1e KVM keeps CNTVOFF_EL2 at zero, traps the virtual timer whenever
it has an offset and programs the hardware with the offset applied.
kvm_hyp_handle_timer() returns that hardware value for a VHE guest
hypervisor's read of its own CNTV_CVAL_EL0, instead of the CVAL it
wrote. Its writes trap to the kernel and land in CNTHV_CVAL_EL2, so
return that, as the physical timer's read does without CNTPOFF_EL2.
Fixes: 0bc9a9e85fcf ("KVM: arm64: Work around x1e's CNTVOFF_EL2 bogosity")
Signed-off-by: Fuad Tabba <[email protected]>
---
arch/arm64/kvm/hyp/vhe/switch.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index 14aada311bac4..4c965f9b1e1de 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -322,10 +322,14 @@ static bool kvm_hyp_handle_timer(struct kvm_vcpu *vcpu,
u64 *exit_code)
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
break;
case SYS_CNTV_CVAL_EL0:
- if (vcpu_el2_e2h_is_set(vcpu))
- val = read_sysreg_el0(SYS_CNTV_CVAL);
- else
+ if (vcpu_el2_e2h_is_set(vcpu)) {
+ if (has_broken_cntvoff() &&
timer_get_offset(vcpu_hvtimer(vcpu)))
+ val = __vcpu_sys_reg(vcpu, CNTHV_CVAL_EL2);
+ else
+ val = read_sysreg_el0(SYS_CNTV_CVAL);
+ } else {
val = __vcpu_sys_reg(vcpu, CNTV_CVAL_EL0);
+ }
break;
case SYS_CNTVCT_EL0:
case SYS_CNTVCTSS_EL0:
--
2.39.5