From: "Maciej S. Szmigiero" <maciej.szmigiero@xxxxxxxxxx> According to APM 15.7.1 "State Saved on Exit" the next_rip field can be zero after a VMEXIT in some cases. Yet, it is used by the CPU for the return address pushed on stack when injecting INT3 or INTO exception or a software interrupt. Restore this field to the L1-provided value if zeroed by the CPU when re-injecting a L1-provided event into L2. Signed-off-by: Maciej S. Szmigiero <maciej.szmigiero@xxxxxxxxxx> --- arch/x86/kvm/svm/svm.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 5b128baa5e57..760dd0e070ea 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -385,6 +385,44 @@ static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) return 1; } +/* + * According to APM 15.7.1 "State Saved on Exit" the next_rip field can + * be zero after a VMEXIT in some cases. + * Yet, it is used by the CPU for the return address pushed on stack when + * injecting INT3 or INTO exception or a software interrupt. + * + * Restore this field to the L1-provided value if zeroed by the CPU when + * re-injecting a L1-provided event into L2. + */ +static void maybe_fixup_next_rip(struct kvm_vcpu *vcpu, bool uses_err) +{ + struct vcpu_svm *svm = to_svm(vcpu); + u32 err_vmcb = uses_err ? svm->vmcb->control.event_inj_err : 0; + u32 err_inject = uses_err ? svm->nested.ctl.event_inj_err : 0; + + /* No nRIP Save feature? Then nothing to fix up. */ + if (!nrips) + return; + + /* The fix only applies to event injection into a L2. */ + if (!is_guest_mode(vcpu)) + return; + + /* + * If the current next_rip field is already non-zero assume the CPU had + * returned the correct address during the last VMEXIT. + */ + if (svm->vmcb->control.next_rip) + return; + + /* Is this a L1 -> L2 event re-injection? */ + if (!event_inj_same(svm->vmcb->control.event_inj, err_vmcb, + svm->nested.ctl.event_inj, err_inject)) + return; + + svm->vmcb->control.next_rip = svm->nested.ctl.next_rip; +} + static void svm_queue_exception(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -415,6 +453,9 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu) | (has_error_code ? SVM_EVTINJ_VALID_ERR : 0) | SVM_EVTINJ_TYPE_EXEPT; svm->vmcb->control.event_inj_err = error_code; + + if (kvm_exception_is_soft(nr)) + maybe_fixup_next_rip(vcpu, true); } static void svm_init_erratum_383(void) @@ -3331,6 +3372,8 @@ static void svm_inject_irq(struct kvm_vcpu *vcpu) SVM_EVTINJ_VALID; if (vcpu->arch.interrupt.soft) { svm->vmcb->control.event_inj |= SVM_EVTINJ_TYPE_SOFT; + + maybe_fixup_next_rip(vcpu, false); } else { svm->vmcb->control.event_inj |= SVM_EVTINJ_TYPE_INTR; }