/* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9 */
 #define X86_FEATURE_FSGSBASE   (9*32+ 0) /* {RD/WR}{FS/GS}BASE instructions*/
+#define X86_FEATURE_TSC_ADJUST (9*32+ 1) /* TSC adjustment MSR 0x3b */
 #define X86_FEATURE_BMI1       (9*32+ 3) /* 1st group bit manipulation extensions */
 #define X86_FEATURE_HLE                (9*32+ 4) /* Hardware Lock Elision */
 #define X86_FEATURE_AVX2       (9*32+ 5) /* AVX2 instructions */
 
        s8 virtual_tsc_shift;
        u32 virtual_tsc_mult;
        u32 virtual_tsc_khz;
+       s64 ia32_tsc_adjust_msr;
 
        atomic_t nmi_queued;  /* unprocessed asynchronous NMIs */
        unsigned nmi_pending; /* NMI queued after currently running handler */
        bool (*has_wbinvd_exit)(void);
 
        void (*set_tsc_khz)(struct kvm_vcpu *vcpu, u32 user_tsc_khz, bool scale);
+       u64 (*read_tsc_offset)(struct kvm_vcpu *vcpu);
        void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
 
        u64 (*compute_tsc_offset)(struct kvm_vcpu *vcpu, u64 target_tsc);
 
 #define MSR_IA32_EBL_CR_POWERON                0x0000002a
 #define MSR_EBC_FREQUENCY_ID           0x0000002c
 #define MSR_IA32_FEATURE_CONTROL        0x0000003a
+#define MSR_IA32_TSC_ADJUST             0x0000003b
 
 #define FEATURE_CONTROL_LOCKED                         (1<<0)
 #define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX       (1<<1)
 
                if (index == 0) {
                        entry->ebx &= kvm_supported_word9_x86_features;
                        cpuid_mask(&entry->ebx, 9);
+                       // TSC_ADJUST is emulated
+                       entry->ebx |= F(TSC_ADJUST);
                } else
                        entry->ebx = 0;
                entry->eax = 0;
 
        return best && (best->ecx & bit(X86_FEATURE_XSAVE));
 }
 
+static inline bool guest_cpuid_has_tsc_adjust(struct kvm_vcpu *vcpu)
+{
+       struct kvm_cpuid_entry2 *best;
+
+       best = kvm_find_cpuid_entry(vcpu, 7, 0);
+       return best && (best->ebx & bit(X86_FEATURE_TSC_ADJUST));
+}
+
 static inline bool guest_cpuid_has_smep(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpuid_entry2 *best;
 
        svm->tsc_ratio             = ratio;
 }
 
+static u64 svm_read_tsc_offset(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       return svm->vmcb->control.tsc_offset;
+}
+
 static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
        .has_wbinvd_exit = svm_has_wbinvd_exit,
 
        .set_tsc_khz = svm_set_tsc_khz,
+       .read_tsc_offset = svm_read_tsc_offset,
        .write_tsc_offset = svm_write_tsc_offset,
        .adjust_tsc_offset = svm_adjust_tsc_offset,
        .compute_tsc_offset = svm_compute_tsc_offset,
 
                WARN(1, "user requested TSC rate below hardware speed\n");
 }
 
+static u64 vmx_read_tsc_offset(struct kvm_vcpu *vcpu)
+{
+       return vmcs_read64(TSC_OFFSET);
+}
+
 /*
  * writes 'offset' into guest's timestamp counter offset register
  */
                }
                ret = kvm_set_msr_common(vcpu, msr_info);
                break;
+       case MSR_IA32_TSC_ADJUST:
+               ret = kvm_set_msr_common(vcpu, msr_info);
+               break;
        case MSR_TSC_AUX:
                if (!vmx->rdtscp_enabled)
                        return 1;
        .has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
 
        .set_tsc_khz = vmx_set_tsc_khz,
+       .read_tsc_offset = vmx_read_tsc_offset,
        .write_tsc_offset = vmx_write_tsc_offset,
        .adjust_tsc_offset = vmx_adjust_tsc_offset,
        .compute_tsc_offset = vmx_compute_tsc_offset,
 
 static unsigned num_msrs_to_save;
 
 static const u32 emulated_msrs[] = {
+       MSR_IA32_TSC_ADJUST,
        MSR_IA32_TSCDEADLINE,
        MSR_IA32_MISC_ENABLE,
        MSR_IA32_MCG_STATUS,
 #endif
 }
 
+static void update_ia32_tsc_adjust_msr(struct kvm_vcpu *vcpu, s64 offset)
+{
+       u64 curr_offset = kvm_x86_ops->read_tsc_offset(vcpu);
+       vcpu->arch.ia32_tsc_adjust_msr += offset - curr_offset;
+}
+
 void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr)
 {
        struct kvm *kvm = vcpu->kvm;
        vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec;
        vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write;
 
+       if (guest_cpuid_has_tsc_adjust(vcpu) && !msr->host_initiated)
+               update_ia32_tsc_adjust_msr(vcpu, offset);
        kvm_x86_ops->write_tsc_offset(vcpu, offset);
        raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags);
 
        case MSR_IA32_TSCDEADLINE:
                kvm_set_lapic_tscdeadline_msr(vcpu, data);
                break;
+       case MSR_IA32_TSC_ADJUST:
+               if (guest_cpuid_has_tsc_adjust(vcpu)) {
+                       if (!msr_info->host_initiated) {
+                               u64 adj = data - vcpu->arch.ia32_tsc_adjust_msr;
+                               kvm_x86_ops->adjust_tsc_offset(vcpu, adj, true);
+                       }
+                       vcpu->arch.ia32_tsc_adjust_msr = data;
+               }
+               break;
        case MSR_IA32_MISC_ENABLE:
                vcpu->arch.ia32_misc_enable_msr = data;
                break;
        case MSR_IA32_TSCDEADLINE:
                data = kvm_get_lapic_tscdeadline_msr(vcpu);
                break;
+       case MSR_IA32_TSC_ADJUST:
+               data = (u64)vcpu->arch.ia32_tsc_adjust_msr;
+               break;
        case MSR_IA32_MISC_ENABLE:
                data = vcpu->arch.ia32_misc_enable_msr;
                break;
        if (!zalloc_cpumask_var(&vcpu->arch.wbinvd_dirty_mask, GFP_KERNEL))
                goto fail_free_mce_banks;
 
+       vcpu->arch.ia32_tsc_adjust_msr = 0x0;
        kvm_async_pf_hash_reset(vcpu);
        kvm_pmu_init(vcpu);