]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/x86/kvm/svm.c
KVM: x86: Convert TSC writes to TSC offset writes
[karo-tx-linux.git] / arch / x86 / kvm / svm.c
index bc5b9b8d4a33117259882835bfb884f4f8f37656..e06f00d1f15c53262d1d1b1c831576561a998c04 100644 (file)
@@ -701,6 +701,20 @@ static void init_sys_seg(struct vmcb_seg *seg, uint32_t type)
        seg->base = 0;
 }
 
+static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+       u64 g_tsc_offset = 0;
+
+       if (is_nested(svm)) {
+               g_tsc_offset = svm->vmcb->control.tsc_offset -
+                              svm->nested.hsave->control.tsc_offset;
+               svm->nested.hsave->control.tsc_offset = offset;
+       }
+
+       svm->vmcb->control.tsc_offset = offset + g_tsc_offset;
+}
+
 static void init_vmcb(struct vcpu_svm *svm)
 {
        struct vmcb_control_area *control = &svm->vmcb->control;
@@ -766,7 +780,6 @@ static void init_vmcb(struct vcpu_svm *svm)
 
        control->iopm_base_pa = iopm_base;
        control->msrpm_base_pa = __pa(svm->msrpm);
-       control->tsc_offset = 0;
        control->int_ctl = V_INTR_MASKING_MASK;
 
        init_seg(&save->es);
@@ -902,6 +915,7 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
        svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
        svm->asid_generation = 0;
        init_vmcb(svm);
+       svm_write_tsc_offset(&svm->vcpu, 0-native_read_tsc());
 
        err = fx_init(&svm->vcpu);
        if (err)
@@ -1896,6 +1910,7 @@ static int nested_svm_vmexit(struct vcpu_svm *svm)
        nested_vmcb->save.ds     = vmcb->save.ds;
        nested_vmcb->save.gdtr   = vmcb->save.gdtr;
        nested_vmcb->save.idtr   = vmcb->save.idtr;
+       nested_vmcb->save.efer   = svm->vcpu.arch.efer;
        nested_vmcb->save.cr0    = kvm_read_cr0(&svm->vcpu);
        nested_vmcb->save.cr3    = svm->vcpu.arch.cr3;
        nested_vmcb->save.cr2    = vmcb->save.cr2;
@@ -1917,6 +1932,7 @@ static int nested_svm_vmexit(struct vcpu_svm *svm)
        nested_vmcb->control.exit_info_2       = vmcb->control.exit_info_2;
        nested_vmcb->control.exit_int_info     = vmcb->control.exit_int_info;
        nested_vmcb->control.exit_int_info_err = vmcb->control.exit_int_info_err;
+       nested_vmcb->control.next_rip          = vmcb->control.next_rip;
 
        /*
         * If we emulate a VMRUN/#VMEXIT in the same host #vmexit cycle we have
@@ -2012,6 +2028,17 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
        return true;
 }
 
+static bool nested_vmcb_checks(struct vmcb *vmcb)
+{
+       if ((vmcb->control.intercept & (1ULL << INTERCEPT_VMRUN)) == 0)
+               return false;
+
+       if (vmcb->control.asid == 0)
+               return false;
+
+       return true;
+}
+
 static bool nested_svm_vmrun(struct vcpu_svm *svm)
 {
        struct vmcb *nested_vmcb;
@@ -2026,6 +2053,17 @@ static bool nested_svm_vmrun(struct vcpu_svm *svm)
        if (!nested_vmcb)
                return false;
 
+       if (!nested_vmcb_checks(nested_vmcb)) {
+               nested_vmcb->control.exit_code    = SVM_EXIT_ERR;
+               nested_vmcb->control.exit_code_hi = 0;
+               nested_vmcb->control.exit_info_1  = 0;
+               nested_vmcb->control.exit_info_2  = 0;
+
+               nested_svm_unmap(page);
+
+               return false;
+       }
+
        trace_kvm_nested_vmrun(svm->vmcb->save.rip - 3, vmcb_gpa,
                               nested_vmcb->save.rip,
                               nested_vmcb->control.int_ctl,
@@ -2542,20 +2580,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
        struct vcpu_svm *svm = to_svm(vcpu);
 
        switch (ecx) {
-       case MSR_IA32_TSC: {
-               u64 tsc_offset = data - native_read_tsc();
-               u64 g_tsc_offset = 0;
-
-               if (is_nested(svm)) {
-                       g_tsc_offset = svm->vmcb->control.tsc_offset -
-                                      svm->nested.hsave->control.tsc_offset;
-                       svm->nested.hsave->control.tsc_offset = tsc_offset;
-               }
-
-               svm->vmcb->control.tsc_offset = tsc_offset + g_tsc_offset;
-
+       case MSR_IA32_TSC:
+               svm_write_tsc_offset(vcpu, data - native_read_tsc());
                break;
-       }
        case MSR_STAR:
                svm->vmcb->save.star = data;
                break;
@@ -3163,8 +3190,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
        sync_lapic_to_cr8(vcpu);
 
        save_host_msrs(vcpu);
-       fs_selector = kvm_read_fs();
-       gs_selector = kvm_read_gs();
+       savesegment(fs, fs_selector);
+       savesegment(gs, gs_selector);
        ldt_selector = kvm_read_ldt();
        svm->vmcb->save.cr2 = vcpu->arch.cr2;
        /* required for live migration with NPT */
@@ -3251,10 +3278,15 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
        vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
        vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
 
-       kvm_load_fs(fs_selector);
-       kvm_load_gs(gs_selector);
-       kvm_load_ldt(ldt_selector);
        load_host_msrs(vcpu);
+       loadsegment(fs, fs_selector);
+#ifdef CONFIG_X86_64
+       load_gs_index(gs_selector);
+       wrmsrl(MSR_KERNEL_GS_BASE, current->thread.gs);
+#else
+       loadsegment(gs, gs_selector);
+#endif
+       kvm_load_ldt(ldt_selector);
 
        reload_tss(vcpu);
 
@@ -3354,7 +3386,12 @@ static void svm_set_supported_cpuid(u32 func, struct kvm_cpuid_entry2 *entry)
                entry->ebx = 8; /* Lets support 8 ASIDs in case we add proper
                                   ASID emulation to nested SVM */
                entry->ecx = 0; /* Reserved */
-               entry->edx = 0; /* Do not support any additional features */
+               entry->edx = 0; /* Per default do not support any
+                                  additional features */
+
+               /* Support next_rip if host supports it */
+               if (svm_has(SVM_FEATURE_NRIP))
+                       entry->edx |= SVM_FEATURE_NRIP;
 
                break;
        }