From patchwork Tue Feb 16 09:50:14 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 79567 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1G9oblx017915 for ; Tue, 16 Feb 2010 09:50:37 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756831Ab0BPJuf (ORCPT ); Tue, 16 Feb 2010 04:50:35 -0500 Received: from goliath.siemens.de ([192.35.17.28]:16548 "EHLO goliath.siemens.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752762Ab0BPJue (ORCPT ); Tue, 16 Feb 2010 04:50:34 -0500 Received: from mail3.siemens.de (localhost [127.0.0.1]) by goliath.siemens.de (8.12.11.20060308/8.12.11) with ESMTP id o1G9oFM5012573; Tue, 16 Feb 2010 10:50:15 +0100 Received: from [139.25.109.167] (mchn012c.mchp.siemens.de [139.25.109.167] (may be forged)) by mail3.siemens.de (8.12.11.20060308/8.12.11) with ESMTP id o1G9oE1c015358; Tue, 16 Feb 2010 10:50:15 +0100 Message-ID: <4B7A6A56.3040409@siemens.com> Date: Tue, 16 Feb 2010 10:50:14 +0100 From: Jan Kiszka User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.1.12) Gecko/20080226 SUSE/2.0.0.12-1.1 Thunderbird/2.0.0.12 Mnenhy/0.7.5.666 MIME-Version: 1.0 To: Avi Kivity , Marcelo Tosatti CC: kvm , Gleb Natapov Subject: [PATCH v2 1/2] KVM: SVM: Emulate nRIP feature when reinjecting INT3 References: <79170b70424440dfbede5b6eac85f734aa8785e8.1266257833.git.jan.kiszka@siemens.com> In-Reply-To: <79170b70424440dfbede5b6eac85f734aa8785e8.1266257833.git.jan.kiszka@siemens.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 16 Feb 2010 09:50:37 +0000 (UTC) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 84c838d..9b040bc 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -46,6 +46,7 @@ MODULE_LICENSE("GPL"); #define SVM_FEATURE_NPT (1 << 0) #define SVM_FEATURE_LBRV (1 << 1) #define SVM_FEATURE_SVML (1 << 2) +#define SVM_FEATURE_NRIP (1 << 3) #define SVM_FEATURE_PAUSE_FILTER (1 << 10) #define NESTED_EXIT_HOST 0 /* Exit handled on host level */ @@ -109,6 +110,10 @@ struct vcpu_svm { struct nested_state nested; bool nmi_singlestep; + + unsigned int3_injected; + u16 int3_cs; + u64 int3_rip; }; /* enable NPT for AMD64 and X86 with PAE */ @@ -234,23 +239,6 @@ static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer) vcpu->arch.efer = efer; } -static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr, - bool has_error_code, u32 error_code) -{ - struct vcpu_svm *svm = to_svm(vcpu); - - /* If we are within a nested VM we'd better #VMEXIT and let the - guest handle the exception */ - if (nested_svm_check_exception(svm, nr, has_error_code, error_code)) - return; - - svm->vmcb->control.event_inj = nr - | SVM_EVTINJ_VALID - | (has_error_code ? SVM_EVTINJ_VALID_ERR : 0) - | SVM_EVTINJ_TYPE_EXEPT; - svm->vmcb->control.event_inj_err = error_code; -} - static int is_external_interrupt(u32 info) { info &= SVM_EVTINJ_TYPE_MASK | SVM_EVTINJ_VALID; @@ -296,6 +284,39 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu) kvm_rip_write(vcpu, svm->next_rip); } +static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr, + bool has_error_code, u32 error_code) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + /* If we are within a nested VM we'd better #VMEXIT and let the + guest handle the exception */ + if (nested_svm_check_exception(svm, nr, has_error_code, error_code)) + return; + + if (nr == BP_VECTOR && !svm_has(SVM_FEATURE_NRIP)) { + u64 old_rip = kvm_rip_read(&svm->vcpu); + + /* + * For guest debugging where we have to reinject #BP if some + * INT3 is guest-owned: + * Emulate nRIP by moving RIP forward. Will fail if injection + * raises a fault that is not intercepted. Still better than + * failing in all cases. + */ + skip_emulated_instruction(&svm->vcpu); + svm->int3_cs = svm->vmcb->save.cs.selector; + svm->int3_rip = kvm_rip_read(&svm->vcpu); + svm->int3_injected = svm->int3_rip - old_rip; + } + + svm->vmcb->control.event_inj = nr + | SVM_EVTINJ_VALID + | (has_error_code ? SVM_EVTINJ_VALID_ERR : 0) + | SVM_EVTINJ_TYPE_EXEPT; + svm->vmcb->control.event_inj_err = error_code; +} + static int has_svm(void) { const char *msg; @@ -2637,8 +2658,10 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) kvm_clear_exception_queue(&svm->vcpu); kvm_clear_interrupt_queue(&svm->vcpu); - if (!(exitintinfo & SVM_EXITINTINFO_VALID)) + if (!(exitintinfo & SVM_EXITINTINFO_VALID)) { + svm->int3_injected = 0; return; + } vector = exitintinfo & SVM_EXITINTINFO_VEC_MASK; type = exitintinfo & SVM_EXITINTINFO_TYPE_MASK; @@ -2648,12 +2671,20 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) svm->vcpu.arch.nmi_injected = true; break; case SVM_EXITINTINFO_TYPE_EXEPT: - /* In case of software exception do not reinject an exception - vector, but re-execute and instruction instead */ if (is_nested(svm)) break; if (kvm_exception_is_soft(vector)) + if (vector == BP_VECTOR && svm->int3_injected && + svm->vmcb->save.cs.selector == svm->int3_cs && + kvm_rip_read(&svm->vcpu) == svm->int3_rip) + kvm_rip_write(&svm->vcpu, + kvm_rip_read(&svm->vcpu) - + svm->int3_injected); break; + /* + * In case of other software exceptions, do not reinject the + * vector, but re-execute the instruction instead. + */ if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) { u32 err = svm->vmcb->control.exit_int_info_err; kvm_queue_exception_e(&svm->vcpu, vector, err); @@ -2667,6 +2698,7 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) default: break; } + svm->int3_injected = 0; } #ifdef CONFIG_X86_64