soft_timer_start(&ctx->hrtimer, kvm_timer_compute_delta(ctx));
 }
 
+static void set_cntvoff(u64 cntvoff)
+{
+       kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff);
+}
+
 static void timer_save_state(struct arch_timer_context *ctx)
 {
        struct arch_timer_cpu *timer = vcpu_timer(ctx->vcpu);
                write_sysreg_el0(0, SYS_CNTV_CTL);
                isb();
 
+               /*
+                * The kernel may decide to run userspace after
+                * calling vcpu_put, so we reset cntvoff to 0 to
+                * ensure a consistent read between user accesses to
+                * the virtual counter and kernel access to the
+                * physical counter of non-VHE case.
+                *
+                * For VHE, the virtual counter uses a fixed virtual
+                * offset of zero, so no need to zero CNTVOFF_EL2
+                * register, but this is actually useful when switching
+                * between EL1/vEL2 with NV.
+                *
+                * Do it unconditionally, as this is either unavoidable
+                * or dirt cheap.
+                */
+               set_cntvoff(0);
                break;
        case TIMER_PTIMER:
                timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
 
        switch (index) {
        case TIMER_VTIMER:
+               set_cntvoff(timer_get_offset(ctx));
                write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
                isb();
                write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
        local_irq_restore(flags);
 }
 
-static void set_cntvoff(u64 cntvoff)
-{
-       kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff);
-}
-
 static inline void set_timer_irq_phys_active(struct arch_timer_context *ctx, bool active)
 {
        int r;
                kvm_timer_vcpu_load_nogic(vcpu);
        }
 
-       set_cntvoff(timer_get_offset(map.direct_vtimer));
-
        kvm_timer_unblocking(vcpu);
 
        timer_restore_state(map.direct_vtimer);
 
        if (kvm_vcpu_is_blocking(vcpu))
                kvm_timer_blocking(vcpu);
-
-       /*
-        * The kernel may decide to run userspace after calling vcpu_put, so
-        * we reset cntvoff to 0 to ensure a consistent read between user
-        * accesses to the virtual counter and kernel access to the physical
-        * counter of non-VHE case. For VHE, the virtual counter uses a fixed
-        * virtual offset of zero, so no need to zero CNTVOFF_EL2 register.
-        */
-       set_cntvoff(0);
 }
 
 /*
                              enum kvm_arch_timers tmr,
                              enum kvm_arch_timer_regs treg)
 {
+       struct arch_timer_context *timer;
+       struct timer_map map;
        u64 val;
 
+       get_timer_map(vcpu, &map);
+       timer = vcpu_get_timer(vcpu, tmr);
+
+       if (timer == map.emul_ptimer)
+               return kvm_arm_timer_read(vcpu, timer, treg);
+
        preempt_disable();
-       kvm_timer_vcpu_put(vcpu);
+       timer_save_state(timer);
 
-       val = kvm_arm_timer_read(vcpu, vcpu_get_timer(vcpu, tmr), treg);
+       val = kvm_arm_timer_read(vcpu, timer, treg);
 
-       kvm_timer_vcpu_load(vcpu);
+       timer_restore_state(timer);
        preempt_enable();
 
        return val;
                                enum kvm_arch_timer_regs treg,
                                u64 val)
 {
-       preempt_disable();
-       kvm_timer_vcpu_put(vcpu);
-
-       kvm_arm_timer_write(vcpu, vcpu_get_timer(vcpu, tmr), treg, val);
+       struct arch_timer_context *timer;
+       struct timer_map map;
 
-       kvm_timer_vcpu_load(vcpu);
-       preempt_enable();
+       get_timer_map(vcpu, &map);
+       timer = vcpu_get_timer(vcpu, tmr);
+       if (timer == map.emul_ptimer) {
+               soft_timer_cancel(&timer->hrtimer);
+               kvm_arm_timer_write(vcpu, timer, treg, val);
+               timer_emulate(timer);
+       } else {
+               preempt_disable();
+               timer_save_state(timer);
+               kvm_arm_timer_write(vcpu, timer, treg, val);
+               timer_restore_state(timer);
+               preempt_enable();
+       }
 }
 
 static int kvm_timer_starting_cpu(unsigned int cpu)