]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/kvm/vmx.c
KVM: Portability: Add vcpu and hardware management arch hooks
[mirror_ubuntu-artful-kernel.git] / drivers / kvm / vmx.c
index d2c25e25d3aad90e57941a01f9750ceff8da0f28..c3bde7532714e7528236a5639a56b5aab85ef54d 100644 (file)
@@ -48,6 +48,7 @@ struct vcpu_vmx {
        struct kvm_vcpu       vcpu;
        int                   launched;
        u8                    fail;
+       u32                   idt_vectoring_info;
        struct kvm_msr_entry *guest_msrs;
        struct kvm_msr_entry *host_msrs;
        int                   nmsrs;
@@ -64,7 +65,13 @@ struct vcpu_vmx {
                int           fs_reload_needed;
                int           guest_efer_loaded;
        } host_state;
-
+       struct {
+               struct {
+                       bool pending;
+                       u8 vector;
+                       unsigned rip;
+               } irq;
+       } rmode;
 };
 
 static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
@@ -863,9 +870,10 @@ static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
 
 static int vmx_get_irq(struct kvm_vcpu *vcpu)
 {
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
        u32 idtv_info_field;
 
-       idtv_info_field = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+       idtv_info_field = vmx->idt_vectoring_info;
        if (idtv_info_field & INTR_INFO_VALID_MASK) {
                if (is_external_interrupt(idtv_info_field))
                        return idtv_info_field & VECTORING_INFO_VECTOR_MASK;
@@ -972,7 +980,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
 #endif
        if (_cpu_based_exec_control & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS) {
                min = 0;
-               opt = SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
+               opt = SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES |
+                       SECONDARY_EXEC_WBINVD_EXITING;
                if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS2,
                                        &_cpu_based_2nd_exec_control) < 0)
                        return -EIO;
@@ -1711,11 +1720,16 @@ out:
 
 static void vmx_inject_irq(struct kvm_vcpu *vcpu, int irq)
 {
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+
        if (vcpu->rmode.active) {
+               vmx->rmode.irq.pending = true;
+               vmx->rmode.irq.vector = irq;
+               vmx->rmode.irq.rip = vmcs_readl(GUEST_RIP);
                vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
                             irq | INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK);
                vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-               vmcs_writel(GUEST_RIP, vmcs_readl(GUEST_RIP) - 1);
+               vmcs_writel(GUEST_RIP, vmx->rmode.irq.rip - 1);
                return;
        }
        vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
@@ -1817,12 +1831,13 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu,
 
 static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
        u32 intr_info, error_code;
        unsigned long cr2, rip;
        u32 vect_info;
        enum emulation_result er;
 
-       vect_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+       vect_info = vmx->idt_vectoring_info;
        intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
 
        if ((vect_info & VECTORING_INFO_VALID_MASK) &&
@@ -2119,6 +2134,13 @@ static int handle_vmcall(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        return 1;
 }
 
+static int handle_wbinvd(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+       skip_emulated_instruction(vcpu);
+       /* TODO: Add support for VT-d/pass-through device */
+       return 1;
+}
+
 static int handle_apic_access(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        u64 exit_qualification;
@@ -2160,6 +2182,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
        [EXIT_REASON_VMCALL]                  = handle_vmcall,
        [EXIT_REASON_TPR_BELOW_THRESHOLD]     = handle_tpr_below_threshold,
        [EXIT_REASON_APIC_ACCESS]             = handle_apic_access,
+       [EXIT_REASON_WBINVD]                  = handle_wbinvd,
 };
 
 static const int kvm_vmx_max_exit_handlers =
@@ -2171,9 +2194,9 @@ static const int kvm_vmx_max_exit_handlers =
  */
 static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 {
-       u32 vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
        u32 exit_reason = vmcs_read32(VM_EXIT_REASON);
        struct vcpu_vmx *vmx = to_vmx(vcpu);
+       u32 vectoring_info = vmx->idt_vectoring_info;
 
        if (unlikely(vmx->fail)) {
                kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
@@ -2228,6 +2251,7 @@ static void enable_irq_window(struct kvm_vcpu *vcpu)
 
 static void vmx_intr_assist(struct kvm_vcpu *vcpu)
 {
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
        u32 idtv_info_field, intr_info_field;
        int has_ext_irq, interrupt_window_open;
        int vector;
@@ -2236,7 +2260,7 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
 
        has_ext_irq = kvm_cpu_has_interrupt(vcpu);
        intr_info_field = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD);
-       idtv_info_field = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+       idtv_info_field = vmx->idt_vectoring_info;
        if (intr_info_field & INTR_INFO_VALID_MASK) {
                if (idtv_info_field & INTR_INFO_VALID_MASK) {
                        /* TODO: fault when IDT_Vectoring */
@@ -2247,6 +2271,17 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
                return;
        }
        if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) {
+               if ((idtv_info_field & VECTORING_INFO_TYPE_MASK)
+                   == INTR_TYPE_EXT_INTR
+                   && vcpu->rmode.active) {
+                       u8 vect = idtv_info_field & VECTORING_INFO_VECTOR_MASK;
+
+                       vmx_inject_irq(vcpu, vect);
+                       if (unlikely(has_ext_irq))
+                               enable_irq_window(vcpu);
+                       return;
+               }
+
                vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
                vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
                                vmcs_read32(VM_EXIT_INSTRUCTION_LEN));
@@ -2271,6 +2306,29 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
                enable_irq_window(vcpu);
 }
 
+/*
+ * Failure to inject an interrupt should give us the information
+ * in IDT_VECTORING_INFO_FIELD.  However, if the failure occurs
+ * when fetching the interrupt redirection bitmap in the real-mode
+ * tss, this doesn't happen.  So we do it ourselves.
+ */
+static void fixup_rmode_irq(struct vcpu_vmx *vmx)
+{
+       vmx->rmode.irq.pending = 0;
+       if (vmcs_readl(GUEST_RIP) + 1 != vmx->rmode.irq.rip)
+               return;
+       vmcs_writel(GUEST_RIP, vmx->rmode.irq.rip);
+       if (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK) {
+               vmx->idt_vectoring_info &= ~VECTORING_INFO_TYPE_MASK;
+               vmx->idt_vectoring_info |= INTR_TYPE_EXT_INTR;
+               return;
+       }
+       vmx->idt_vectoring_info =
+               VECTORING_INFO_VALID_MASK
+               | INTR_TYPE_EXT_INTR
+               | vmx->rmode.irq.vector;
+}
+
 static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -2396,6 +2454,10 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 #endif
              );
 
+       vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+       if (vmx->rmode.irq.pending)
+               fixup_rmode_irq(vmx);
+
        vcpu->interrupt_window_open =
                (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0;
 
@@ -2413,7 +2475,8 @@ static void vmx_inject_page_fault(struct kvm_vcpu *vcpu,
                                  unsigned long addr,
                                  u32 err_code)
 {
-       u32 vect_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       u32 vect_info = vmx->idt_vectoring_info;
 
        ++vcpu->stat.pf_guest;