#define KVM_ARCH_FLAG_EL1_32BIT                                4
        /* PSCI SYSTEM_SUSPEND enabled for the guest */
 #define KVM_ARCH_FLAG_SYSTEM_SUSPEND_ENABLED           5
+       /* VM counter offset */
+#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET                        6
 
        unsigned long flags;
 
 
 long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
                                struct kvm_arm_copy_mte_tags *copy_tags);
+int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
+                                   struct kvm_arm_counter_offset *offset);
 
 /* Guest/host FPSIMD coordination helpers */
 int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu);
 
        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());
-       timer_set_offset(ptimer, 0);
+       /* Synchronize offsets across timers of a VM if not already provided */
+       if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &vcpu->kvm->arch.flags)) {
+               timer_set_offset(vtimer, kvm_phys_timer_read());
+               timer_set_offset(ptimer, 0);
+       }
 
        hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
        timer->bg_timer.function = kvm_bg_timer_expire;
                kvm_arm_timer_write(vcpu, timer, TIMER_REG_CTL, value);
                break;
        case KVM_REG_ARM_TIMER_CNT:
-               timer = vcpu_vtimer(vcpu);
-               timer_set_offset(timer, kvm_phys_timer_read() - value);
+               if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET,
+                             &vcpu->kvm->arch.flags)) {
+                       timer = vcpu_vtimer(vcpu);
+                       timer_set_offset(timer, kvm_phys_timer_read() - value);
+               }
                break;
        case KVM_REG_ARM_TIMER_CVAL:
                timer = vcpu_vtimer(vcpu);
                timer = vcpu_ptimer(vcpu);
                kvm_arm_timer_write(vcpu, timer, TIMER_REG_CTL, value);
                break;
+       case KVM_REG_ARM_PTIMER_CNT:
+               if (!test_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET,
+                             &vcpu->kvm->arch.flags)) {
+                       timer = vcpu_ptimer(vcpu);
+                       timer_set_offset(timer, kvm_phys_timer_read() - value);
+               }
+               break;
        case KVM_REG_ARM_PTIMER_CVAL:
                timer = vcpu_ptimer(vcpu);
                kvm_arm_timer_write(vcpu, timer, TIMER_REG_CVAL, value);
 
        return -ENXIO;
 }
+
+int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
+                                   struct kvm_arm_counter_offset *offset)
+{
+       int ret = 0;
+
+       if (offset->reserved)
+               return -EINVAL;
+
+       mutex_lock(&kvm->lock);
+
+       if (lock_all_vcpus(kvm)) {
+               set_bit(KVM_ARCH_FLAG_VM_COUNTER_OFFSET, &kvm->arch.flags);
+
+               /*
+                * If userspace decides to set the offset using this
+                * API rather than merely restoring the counter
+                * values, the offset applies to both the virtual and
+                * physical views.
+                */
+               kvm->arch.timer_data.voffset = offset->counter_offset;
+               kvm->arch.timer_data.poffset = offset->counter_offset;
+
+               unlock_all_vcpus(kvm);
+       } else {
+               ret = -EBUSY;
+       }
+
+       mutex_unlock(&kvm->lock);
+
+       return ret;
+}
 
        case KVM_CAP_VCPU_ATTRIBUTES:
        case KVM_CAP_PTP_KVM:
        case KVM_CAP_ARM_SYSTEM_SUSPEND:
+       case KVM_CAP_COUNTER_OFFSET:
                r = 1;
                break;
        case KVM_CAP_SET_GUEST_DEBUG2:
                        return -EFAULT;
                return kvm_vm_ioctl_mte_copy_tags(kvm, ©_tags);
        }
+       case KVM_ARM_SET_COUNTER_OFFSET: {
+               struct kvm_arm_counter_offset offset;
+
+               if (copy_from_user(&offset, argp, sizeof(offset)))
+                       return -EFAULT;
+               return kvm_vm_ioctl_set_counter_offset(kvm, &offset);
+       }
        default:
                return -EINVAL;
        }
 
 #define KVM_CAP_S390_PROTECTED_ASYNC_DISABLE 224
 #define KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP 225
 #define KVM_CAP_PMU_EVENT_MASKED_EVENTS 226
+#define KVM_CAP_COUNTER_OFFSET 227
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
 #define KVM_SET_PMU_EVENT_FILTER  _IOW(KVMIO,  0xb2, struct kvm_pmu_event_filter)
 #define KVM_PPC_SVM_OFF                  _IO(KVMIO,  0xb3)
 #define KVM_ARM_MTE_COPY_TAGS    _IOR(KVMIO,  0xb4, struct kvm_arm_copy_mte_tags)
+/* Available with KVM_CAP_COUNTER_OFFSET */
+#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO,  0xb5, struct kvm_arm_counter_offset)
 
 /* ioctl for vm fd */
 #define KVM_CREATE_DEVICE        _IOWR(KVMIO,  0xe0, struct kvm_create_device)