Staging: Merge staging-next into Linus's tree
[sfrench/cifs-2.6.git] / arch / x86 / kvm / vmx.c
index 945265361885c7a7ba85220a0f99f11397395936..27a0222c29460d79e6e65992ac00fc2845f8f25b 100644 (file)
@@ -37,6 +37,8 @@
 #include <asm/vmx.h>
 #include <asm/virtext.h>
 #include <asm/mce.h>
+#include <asm/i387.h>
+#include <asm/xcr.h>
 
 #include "trace.h"
 
@@ -183,6 +185,7 @@ static void kvm_cpu_vmxoff(void);
 static DEFINE_PER_CPU(struct vmcs *, vmxarea);
 static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
 static DEFINE_PER_CPU(struct list_head, vcpus_on_cpu);
+static DEFINE_PER_CPU(struct desc_ptr, host_gdt);
 
 static unsigned long *vmx_io_bitmap_a;
 static unsigned long *vmx_io_bitmap_b;
@@ -365,6 +368,11 @@ static inline bool cpu_has_vmx_invvpid_single(void)
        return vmx_capability.vpid & VMX_VPID_EXTENT_SINGLE_CONTEXT_BIT;
 }
 
+static inline bool cpu_has_vmx_invvpid_global(void)
+{
+       return vmx_capability.vpid & VMX_VPID_EXTENT_GLOBAL_CONTEXT_BIT;
+}
+
 static inline bool cpu_has_vmx_ept(void)
 {
        return vmcs_config.cpu_based_2nd_exec_ctrl &
@@ -405,6 +413,12 @@ static inline bool cpu_has_virtual_nmis(void)
        return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS;
 }
 
+static inline bool cpu_has_vmx_wbinvd_exit(void)
+{
+       return vmcs_config.cpu_based_2nd_exec_ctrl &
+               SECONDARY_EXEC_WBINVD_EXITING;
+}
+
 static inline bool report_flexpriority(void)
 {
        return flexpriority_enabled;
@@ -504,7 +518,7 @@ static void vcpu_clear(struct vcpu_vmx *vmx)
        smp_call_function_single(vmx->vcpu.cpu, __vcpu_clear, vmx, 1);
 }
 
-static inline void vpid_sync_vcpu_all(struct vcpu_vmx *vmx)
+static inline void vpid_sync_vcpu_single(struct vcpu_vmx *vmx)
 {
        if (vmx->vpid == 0)
                return;
@@ -513,6 +527,20 @@ static inline void vpid_sync_vcpu_all(struct vcpu_vmx *vmx)
                __invvpid(VMX_VPID_EXTENT_SINGLE_CONTEXT, vmx->vpid, 0);
 }
 
+static inline void vpid_sync_vcpu_global(void)
+{
+       if (cpu_has_vmx_invvpid_global())
+               __invvpid(VMX_VPID_EXTENT_ALL_CONTEXT, 0, 0);
+}
+
+static inline void vpid_sync_context(struct vcpu_vmx *vmx)
+{
+       if (cpu_has_vmx_invvpid_single())
+               vpid_sync_vcpu_single(vmx);
+       else
+               vpid_sync_vcpu_global();
+}
+
 static inline void ept_sync_global(void)
 {
        if (cpu_has_vmx_invept_global())
@@ -844,6 +872,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
 #endif
        if (current_thread_info()->status & TS_USEDFPU)
                clts();
+       load_gdt(&__get_cpu_var(host_gdt));
 }
 
 static void vmx_load_host_state(struct vcpu_vmx *vmx)
@@ -878,7 +907,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
                unsigned long sysenter_esp;
 
                kvm_migrate_timers(vcpu);
-               set_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests);
+               kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
                local_irq_disable();
                list_add(&vmx->local_vcpus_link,
                         &per_cpu(vcpus_on_cpu, cpu));
@@ -1352,6 +1381,8 @@ static int hardware_enable(void *garbage)
                ept_sync_global();
        }
 
+       store_gdt(&__get_cpu_var(host_gdt));
+
        return 0;
 }
 
@@ -1669,7 +1700,7 @@ static gva_t rmode_tss_base(struct kvm *kvm)
                gfn_t base_gfn;
 
                slots = kvm_memslots(kvm);
-               base_gfn = kvm->memslots->memslots[0].base_gfn +
+               base_gfn = slots->memslots[0].base_gfn +
                                 kvm->memslots->memslots[0].npages - 3;
                return base_gfn << PAGE_SHIFT;
        }
@@ -1800,9 +1831,12 @@ static void exit_lmode(struct kvm_vcpu *vcpu)
 
 static void vmx_flush_tlb(struct kvm_vcpu *vcpu)
 {
-       vpid_sync_vcpu_all(to_vmx(vcpu));
-       if (enable_ept)
+       vpid_sync_context(to_vmx(vcpu));
+       if (enable_ept) {
+               if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
+                       return;
                ept_sync_context(construct_eptp(vcpu->arch.mmu.root_hpa));
+       }
 }
 
 static void vmx_decache_cr0_guest_bits(struct kvm_vcpu *vcpu)
@@ -2640,21 +2674,27 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
 
 static int init_rmode(struct kvm *kvm)
 {
+       int idx, ret = 0;
+
+       idx = srcu_read_lock(&kvm->srcu);
        if (!init_rmode_tss(kvm))
-               return 0;
+               goto exit;
        if (!init_rmode_identity_map(kvm))
-               return 0;
-       return 1;
+               goto exit;
+
+       ret = 1;
+exit:
+       srcu_read_unlock(&kvm->srcu, idx);
+       return ret;
 }
 
 static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        u64 msr;
-       int ret, idx;
+       int ret;
 
        vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP));
-       idx = srcu_read_lock(&vcpu->kvm->srcu);
        if (!init_rmode(vmx->vcpu.kvm)) {
                ret = -ENOMEM;
                goto out;
@@ -2756,7 +2796,7 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        vmx_fpu_activate(&vmx->vcpu);
        update_exception_bitmap(&vmx->vcpu);
 
-       vpid_sync_vcpu_all(vmx);
+       vpid_sync_context(vmx);
 
        ret = 0;
 
@@ -2764,7 +2804,6 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        vmx->emulation_required = 0;
 
 out:
-       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        return ret;
 }
 
@@ -3131,11 +3170,20 @@ vmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall)
        hypercall[2] = 0xc1;
 }
 
+static void complete_insn_gp(struct kvm_vcpu *vcpu, int err)
+{
+       if (err)
+               kvm_inject_gp(vcpu, 0);
+       else
+               skip_emulated_instruction(vcpu);
+}
+
 static int handle_cr(struct kvm_vcpu *vcpu)
 {
        unsigned long exit_qualification, val;
        int cr;
        int reg;
+       int err;
 
        exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
        cr = exit_qualification & 15;
@@ -3146,16 +3194,16 @@ static int handle_cr(struct kvm_vcpu *vcpu)
                trace_kvm_cr_write(cr, val);
                switch (cr) {
                case 0:
-                       kvm_set_cr0(vcpu, val);
-                       skip_emulated_instruction(vcpu);
+                       err = kvm_set_cr0(vcpu, val);
+                       complete_insn_gp(vcpu, err);
                        return 1;
                case 3:
-                       kvm_set_cr3(vcpu, val);
-                       skip_emulated_instruction(vcpu);
+                       err = kvm_set_cr3(vcpu, val);
+                       complete_insn_gp(vcpu, err);
                        return 1;
                case 4:
-                       kvm_set_cr4(vcpu, val);
-                       skip_emulated_instruction(vcpu);
+                       err = kvm_set_cr4(vcpu, val);
+                       complete_insn_gp(vcpu, err);
                        return 1;
                case 8: {
                                u8 cr8_prev = kvm_get_cr8(vcpu);
@@ -3362,7 +3410,17 @@ static int handle_invlpg(struct kvm_vcpu *vcpu)
 static int handle_wbinvd(struct kvm_vcpu *vcpu)
 {
        skip_emulated_instruction(vcpu);
-       /* TODO: Add support for VT-d/pass-through device */
+       kvm_emulate_wbinvd(vcpu);
+       return 1;
+}
+
+static int handle_xsetbv(struct kvm_vcpu *vcpu)
+{
+       u64 new_bv = kvm_read_edx_eax(vcpu);
+       u32 index = kvm_register_read(vcpu, VCPU_REGS_RCX);
+
+       if (kvm_set_xcr(vcpu, index, new_bv) == 0)
+               skip_emulated_instruction(vcpu);
        return 1;
 }
 
@@ -3644,6 +3702,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
        [EXIT_REASON_TPR_BELOW_THRESHOLD]     = handle_tpr_below_threshold,
        [EXIT_REASON_APIC_ACCESS]             = handle_apic_access,
        [EXIT_REASON_WBINVD]                  = handle_wbinvd,
+       [EXIT_REASON_XSETBV]                  = handle_xsetbv,
        [EXIT_REASON_TASK_SWITCH]             = handle_task_switch,
        [EXIT_REASON_MCE_DURING_VMENTRY]      = handle_machine_check,
        [EXIT_REASON_EPT_VIOLATION]           = handle_ept_violation,
@@ -4301,6 +4360,8 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .rdtscp_supported = vmx_rdtscp_supported,
 
        .set_supported_cpuid = vmx_set_supported_cpuid,
+
+       .has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
 };
 
 static int __init vmx_init(void)