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


Reply via email to