KVM: VMX: Allow single-stepping when uninterruptible
[sfrench/cifs-2.6.git] / arch / x86 / kvm / vmx.c
index 7611af576829d820eba8de4a79d389e8baef7759..c776868ffe41feb1c86a75bd74918bf06bf9347c 100644 (file)
@@ -189,21 +189,21 @@ static inline int is_page_fault(u32 intr_info)
 {
        return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK |
                             INTR_INFO_VALID_MASK)) ==
-               (INTR_TYPE_EXCEPTION | PF_VECTOR | INTR_INFO_VALID_MASK);
+               (INTR_TYPE_HARD_EXCEPTION | PF_VECTOR | INTR_INFO_VALID_MASK);
 }
 
 static inline int is_no_device(u32 intr_info)
 {
        return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK |
                             INTR_INFO_VALID_MASK)) ==
-               (INTR_TYPE_EXCEPTION | NM_VECTOR | INTR_INFO_VALID_MASK);
+               (INTR_TYPE_HARD_EXCEPTION | NM_VECTOR | INTR_INFO_VALID_MASK);
 }
 
 static inline int is_invalid_opcode(u32 intr_info)
 {
        return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK |
                             INTR_INFO_VALID_MASK)) ==
-               (INTR_TYPE_EXCEPTION | UD_VECTOR | INTR_INFO_VALID_MASK);
+               (INTR_TYPE_HARD_EXCEPTION | UD_VECTOR | INTR_INFO_VALID_MASK);
 }
 
 static inline int is_external_interrupt(u32 intr_info)
@@ -480,8 +480,13 @@ static void update_exception_bitmap(struct kvm_vcpu *vcpu)
        eb = (1u << PF_VECTOR) | (1u << UD_VECTOR);
        if (!vcpu->fpu_active)
                eb |= 1u << NM_VECTOR;
-       if (vcpu->guest_debug.enabled)
-               eb |= 1u << DB_VECTOR;
+       if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
+               if (vcpu->guest_debug &
+                   (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
+                       eb |= 1u << DB_VECTOR;
+               if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+                       eb |= 1u << BP_VECTOR;
+       }
        if (vcpu->arch.rmode.active)
                eb = ~0;
        if (vm_need_ept())
@@ -747,29 +752,33 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
                                bool has_error_code, u32 error_code)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
+       u32 intr_info = nr | INTR_INFO_VALID_MASK;
 
-       if (has_error_code)
+       if (has_error_code) {
                vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
+               intr_info |= INTR_INFO_DELIVER_CODE_MASK;
+       }
 
        if (vcpu->arch.rmode.active) {
                vmx->rmode.irq.pending = true;
                vmx->rmode.irq.vector = nr;
                vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-               if (nr == BP_VECTOR)
+               if (nr == BP_VECTOR || nr == OF_VECTOR)
                        vmx->rmode.irq.rip++;
-               vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-                            nr | INTR_TYPE_SOFT_INTR
-                            | (has_error_code ? INTR_INFO_DELIVER_CODE_MASK : 0)
-                            | INTR_INFO_VALID_MASK);
+               intr_info |= INTR_TYPE_SOFT_INTR;
+               vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
                vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
                kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
                return;
        }
 
-       vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-                    nr | INTR_TYPE_EXCEPTION
-                    | (has_error_code ? INTR_INFO_DELIVER_CODE_MASK : 0)
-                    | INTR_INFO_VALID_MASK);
+       if (nr == BP_VECTOR || nr == OF_VECTOR) {
+               vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
+               intr_info |= INTR_TYPE_SOFT_EXCEPTION;
+       } else
+               intr_info |= INTR_TYPE_HARD_EXCEPTION;
+
+       vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
 }
 
 static bool vmx_exception_injected(struct kvm_vcpu *vcpu)
@@ -999,40 +1008,23 @@ static void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
        }
 }
 
-static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg)
+static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg)
 {
-       unsigned long dr7 = 0x400;
-       int old_singlestep;
-
-       old_singlestep = vcpu->guest_debug.singlestep;
-
-       vcpu->guest_debug.enabled = dbg->enabled;
-       if (vcpu->guest_debug.enabled) {
-               int i;
-
-               dr7 |= 0x200;  /* exact */
-               for (i = 0; i < 4; ++i) {
-                       if (!dbg->breakpoints[i].enabled)
-                               continue;
-                       vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address;
-                       dr7 |= 2 << (i*2);    /* global enable */
-                       dr7 |= 0 << (i*4+16); /* execution breakpoint */
-               }
-
-               vcpu->guest_debug.singlestep = dbg->singlestep;
-       } else
-               vcpu->guest_debug.singlestep = 0;
+       int old_debug = vcpu->guest_debug;
+       unsigned long flags;
 
-       if (old_singlestep && !vcpu->guest_debug.singlestep) {
-               unsigned long flags;
+       vcpu->guest_debug = dbg->control;
+       if (!(vcpu->guest_debug & KVM_GUESTDBG_ENABLE))
+               vcpu->guest_debug = 0;
 
-               flags = vmcs_readl(GUEST_RFLAGS);
+       flags = vmcs_readl(GUEST_RFLAGS);
+       if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+               flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
+       else if (old_debug & KVM_GUESTDBG_SINGLESTEP)
                flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_RF);
-               vmcs_writel(GUEST_RFLAGS, flags);
-       }
+       vmcs_writel(GUEST_RFLAGS, flags);
 
        update_exception_bitmap(vcpu);
-       vmcs_writel(GUEST_DR7, dr7);
 
        return 0;
 }
@@ -2486,6 +2478,11 @@ static void do_interrupt_requests(struct kvm_vcpu *vcpu,
 {
        vmx_update_window_states(vcpu);
 
+       if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+               vmcs_clear_bits(GUEST_INTERRUPTIBILITY_INFO,
+                               GUEST_INTR_STATE_STI |
+                               GUEST_INTR_STATE_MOV_SS);
+
        if (vcpu->arch.nmi_pending && !vcpu->arch.nmi_injected) {
                if (vcpu->arch.interrupt.pending) {
                        enable_nmi_window(vcpu);
@@ -2536,24 +2533,6 @@ static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr)
        return 0;
 }
 
-static void kvm_guest_debug_pre(struct kvm_vcpu *vcpu)
-{
-       struct kvm_guest_debug *dbg = &vcpu->guest_debug;
-
-       set_debugreg(dbg->bp[0], 0);
-       set_debugreg(dbg->bp[1], 1);
-       set_debugreg(dbg->bp[2], 2);
-       set_debugreg(dbg->bp[3], 3);
-
-       if (dbg->singlestep) {
-               unsigned long flags;
-
-               flags = vmcs_readl(GUEST_RFLAGS);
-               flags |= X86_EFLAGS_TF | X86_EFLAGS_RF;
-               vmcs_writel(GUEST_RFLAGS, flags);
-       }
-}
-
 static int handle_rmode_exception(struct kvm_vcpu *vcpu,
                                  int vec, u32 err_code)
 {
@@ -2570,9 +2549,17 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu,
         *        the required debugging infrastructure rework.
         */
        switch (vec) {
-       case DE_VECTOR:
        case DB_VECTOR:
+               if (vcpu->guest_debug &
+                   (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
+                       return 0;
+               kvm_queue_exception(vcpu, vec);
+               return 1;
        case BP_VECTOR:
+               if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
+                       return 0;
+               /* fall through */
+       case DE_VECTOR:
        case OF_VECTOR:
        case BR_VECTOR:
        case UD_VECTOR:
@@ -2589,7 +2576,7 @@ 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;
+       u32 intr_info, ex_no, error_code;
        unsigned long cr2, rip;
        u32 vect_info;
        enum emulation_result er;
@@ -2649,14 +2636,16 @@ static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                return 1;
        }
 
-       if ((intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VECTOR_MASK)) ==
-           (INTR_TYPE_EXCEPTION | 1)) {
+       ex_no = intr_info & INTR_INFO_VECTOR_MASK;
+       if (ex_no == DB_VECTOR || ex_no == BP_VECTOR) {
                kvm_run->exit_reason = KVM_EXIT_DEBUG;
-               return 0;
+               kvm_run->debug.arch.pc = vmcs_readl(GUEST_CS_BASE) + rip;
+               kvm_run->debug.arch.exception = ex_no;
+       } else {
+               kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
+               kvm_run->ex.exception = ex_no;
+               kvm_run->ex.error_code = error_code;
        }
-       kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
-       kvm_run->ex.exception = intr_info & INTR_INFO_VECTOR_MASK;
-       kvm_run->ex.error_code = error_code;
        return 0;
 }
 
@@ -3238,7 +3227,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx)
                        vmx->vcpu.arch.nmi_injected = false;
        }
        kvm_clear_exception_queue(&vmx->vcpu);
-       if (idtv_info_valid && type == INTR_TYPE_EXCEPTION) {
+       if (idtv_info_valid && (type == INTR_TYPE_HARD_EXCEPTION ||
+                               type == INTR_TYPE_SOFT_EXCEPTION)) {
                if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) {
                        error = vmcs_read32(IDT_VECTORING_ERROR_CODE);
                        kvm_queue_exception_e(&vmx->vcpu, vector, error);
@@ -3259,6 +3249,11 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
 
        vmx_update_window_states(vcpu);
 
+       if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+               vmcs_clear_bits(GUEST_INTERRUPTIBILITY_INFO,
+                               GUEST_INTR_STATE_STI |
+                               GUEST_INTR_STATE_MOV_SS);
+
        if (vcpu->arch.nmi_pending && !vcpu->arch.nmi_injected) {
                if (vcpu->arch.interrupt.pending) {
                        enable_nmi_window(vcpu);
@@ -3595,7 +3590,6 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .vcpu_put = vmx_vcpu_put,
 
        .set_guest_debug = set_guest_debug,
-       .guest_debug_pre = kvm_guest_debug_pre,
        .get_msr = vmx_get_msr,
        .set_msr = vmx_set_msr,
        .get_segment_base = vmx_get_segment_base,