KVM: arm64: timers: Use CNTPOFF_EL2 to offset the physical timer
authorMarc Zyngier <maz@kernel.org>
Thu, 30 Mar 2023 17:47:44 +0000 (18:47 +0100)
committerMarc Zyngier <maz@kernel.org>
Thu, 30 Mar 2023 18:01:09 +0000 (19:01 +0100)
With ECV and CNTPOFF_EL2, it is very easy to offer an offset for
the physical timer. So let's do just that.

Nothing can set the offset yet, so this should have no effect
whatsoever (famous last words...).

Reviewed-by: Colton Lewis <coltonlewis@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20230330174800.2677007-5-maz@kernel.org
arch/arm64/kvm/arch_timer.c
arch/arm64/kvm/hypercalls.c
include/clocksource/arm_arch_timer.h
include/kvm/arm_arch_timer.h

index 9515c645f03d190bd68319d149cbcaf6cff12b9c..3118ea0a1b4147e9bc3a4ac7ffecca717b6b9cee 100644 (file)
@@ -52,6 +52,11 @@ static u64 kvm_arm_timer_read(struct kvm_vcpu *vcpu,
                              struct arch_timer_context *timer,
                              enum kvm_arch_timer_regs treg);
 
+static bool has_cntpoff(void)
+{
+       return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF));
+}
+
 u32 timer_get_ctl(struct arch_timer_context *ctxt)
 {
        struct kvm_vcpu *vcpu = ctxt->vcpu;
@@ -84,7 +89,7 @@ u64 timer_get_cval(struct arch_timer_context *ctxt)
 
 static u64 timer_get_offset(struct arch_timer_context *ctxt)
 {
-       if (ctxt->offset.vm_offset)
+       if (ctxt && ctxt->offset.vm_offset)
                return *ctxt->offset.vm_offset;
 
        return 0;
@@ -432,6 +437,12 @@ static void set_cntvoff(u64 cntvoff)
        kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff);
 }
 
+static void set_cntpoff(u64 cntpoff)
+{
+       if (has_cntpoff())
+               write_sysreg_s(cntpoff, SYS_CNTPOFF_EL2);
+}
+
 static void timer_save_state(struct arch_timer_context *ctx)
 {
        struct arch_timer_cpu *timer = vcpu_timer(ctx->vcpu);
@@ -480,6 +491,7 @@ static void timer_save_state(struct arch_timer_context *ctx)
                write_sysreg_el0(0, SYS_CNTP_CTL);
                isb();
 
+               set_cntpoff(0);
                break;
        case NR_KVM_TIMERS:
                BUG();
@@ -550,6 +562,7 @@ static void timer_restore_state(struct arch_timer_context *ctx)
                write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
                break;
        case TIMER_PTIMER:
+               set_cntpoff(timer_get_offset(ctx));
                write_sysreg_el0(timer_get_cval(ctx), SYS_CNTP_CVAL);
                isb();
                write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL);
@@ -767,6 +780,7 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
        vtimer->vcpu = vcpu;
        vtimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.voffset;
        ptimer->vcpu = vcpu;
+       ptimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.poffset;
 
        /* Synchronize cntvoff across all vtimers of a VM. */
        timer_set_offset(vtimer, kvm_phys_timer_read());
@@ -1297,6 +1311,8 @@ void kvm_timer_init_vhe(void)
        val = read_sysreg(cnthctl_el2);
        val |= (CNTHCTL_EL1PCEN << cnthctl_shift);
        val |= (CNTHCTL_EL1PCTEN << cnthctl_shift);
+       if (cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF))
+               val |= CNTHCTL_ECV;
        write_sysreg(val, cnthctl_el2);
 }
 
index 5da884e11337a6d420e3dc71456b469057300d1c..39a4707e081d7a6081705d99f39e1208a22a2d43 100644 (file)
@@ -47,7 +47,7 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
                cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.voffset;
                break;
        case KVM_PTP_PHYS_COUNTER:
-               cycles = systime_snapshot.cycles;
+               cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.poffset;
                break;
        default:
                return;
index 057c8964aefba144d48348a84d11ece09775a130..cbbc9a6dc571587db49b5e9b965f9e3610b83c55 100644 (file)
@@ -21,6 +21,7 @@
 #define CNTHCTL_EVNTEN                 (1 << 2)
 #define CNTHCTL_EVNTDIR                        (1 << 3)
 #define CNTHCTL_EVNTI                  (0xF << 4)
+#define CNTHCTL_ECV                    (1 << 12)
 
 enum arch_timer_reg {
        ARCH_TIMER_REG_CTRL,
index 70d47c4adc6a1aad44d8bd52b52a50677c78e5ad..2dd0fd2406fbc17234b383ed0047aed4cfc526af 100644 (file)
@@ -34,6 +34,8 @@ struct arch_timer_offset {
 struct arch_timer_vm_data {
        /* Offset applied to the virtual timer/counter */
        u64     voffset;
+       /* Offset applied to the physical timer/counter */
+       u64     poffset;
 };
 
 struct arch_timer_context {