Message ID | 20220402010903.727604-7-seanjc@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: SVM: Fix soft int/ex re-injection | expand |
On Sat, Apr 02, 2022, Sean Christopherson wrote: > Re-inject INTn software interrupts instead of retrying the instruction if > the CPU encountered an intercepted exception while vectoring the INTn, > e.g. if KVM intercepted a #PF when utilizing shadow paging. Retrying the > instruction is architecturally wrong e.g. will result in a spurious #DB > if there's a code breakpoint on the INT3/O, and lack of re-injection also > breaks nested virtualization, e.g. if L1 injects a software interrupt and > vectoring the injected interrupt encounters an exception that is > intercepted by L0 but not L1. > > Signed-off-by: Sean Christopherson <seanjc@google.com> > --- > arch/x86/kvm/svm/svm.c | 16 +++++++++++++++- > 1 file changed, 15 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c > index ecc828d6921e..00b1399681d1 100644 > --- a/arch/x86/kvm/svm/svm.c > +++ b/arch/x86/kvm/svm/svm.c > @@ -3425,14 +3425,24 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) > static void svm_inject_irq(struct kvm_vcpu *vcpu) > { > struct vcpu_svm *svm = to_svm(vcpu); > + u32 type; > > WARN_ON(!gif_set(svm)); > > + if (vcpu->arch.interrupt.soft) { > + if (svm_update_soft_interrupt_rip(vcpu)) > + return; > + > + type = SVM_EVTINJ_TYPE_SOFT; > + } else { > + type = SVM_EVTINJ_TYPE_INTR; > + } > + > trace_kvm_inj_virq(vcpu->arch.interrupt.nr); > ++vcpu->stat.irq_injections; > > svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | > - SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR; > + SVM_EVTINJ_VALID | type; > } > > void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, > @@ -3787,9 +3797,13 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) > case SVM_EXITINTINFO_TYPE_INTR: > kvm_queue_interrupt(vcpu, vector, false); > break; > + case SVM_EXITINTINFO_TYPE_SOFT: > + kvm_queue_interrupt(vcpu, vector, true); I believe this patch is wrong, it needs to also tweak the soft_int_injected logic to look for SVM_EXITINTINFO_TYPE_EXEPT _or_ SVM_EXITINTINFO_TYPE_SOFT, e.g. if the SOFT-type interrupt doesn't complete due to a non-legacy-exception exit, e.g. #NPF. > + break; > default: > break; > } > + > }
On 2.04.2022 03:09, Sean Christopherson wrote: > Re-inject INTn software interrupts instead of retrying the instruction if > the CPU encountered an intercepted exception while vectoring the INTn, > e.g. if KVM intercepted a #PF when utilizing shadow paging. Retrying the > instruction is architecturally wrong e.g. will result in a spurious #DB > if there's a code breakpoint on the INT3/O, and lack of re-injection also > breaks nested virtualization, e.g. if L1 injects a software interrupt and > vectoring the injected interrupt encounters an exception that is > intercepted by L0 but not L1. > > Signed-off-by: Sean Christopherson <seanjc@google.com> > --- > arch/x86/kvm/svm/svm.c | 16 +++++++++++++++- > 1 file changed, 15 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c > index ecc828d6921e..00b1399681d1 100644 > --- a/arch/x86/kvm/svm/svm.c > +++ b/arch/x86/kvm/svm/svm.c > @@ -3425,14 +3425,24 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) > static void svm_inject_irq(struct kvm_vcpu *vcpu) > { > struct vcpu_svm *svm = to_svm(vcpu); > + u32 type; > > WARN_ON(!gif_set(svm)); > > + if (vcpu->arch.interrupt.soft) { It should be possible to inject soft interrupts even with GIF masked, looked at the relevant code at patch 3 from my series [1]. Thanks, Maciej [1]: https://lore.kernel.org/kvm/a28577564a7583c32f0029f2307f63ca8869cf22.1646944472.git.maciej.szmigiero@oracle.com/
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index ecc828d6921e..00b1399681d1 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3425,14 +3425,24 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) static void svm_inject_irq(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + u32 type; WARN_ON(!gif_set(svm)); + if (vcpu->arch.interrupt.soft) { + if (svm_update_soft_interrupt_rip(vcpu)) + return; + + type = SVM_EVTINJ_TYPE_SOFT; + } else { + type = SVM_EVTINJ_TYPE_INTR; + } + trace_kvm_inj_virq(vcpu->arch.interrupt.nr); ++vcpu->stat.irq_injections; svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | - SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR; + SVM_EVTINJ_VALID | type; } void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, @@ -3787,9 +3797,13 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) case SVM_EXITINTINFO_TYPE_INTR: kvm_queue_interrupt(vcpu, vector, false); break; + case SVM_EXITINTINFO_TYPE_SOFT: + kvm_queue_interrupt(vcpu, vector, true); + break; default: break; } + } static void svm_cancel_injection(struct kvm_vcpu *vcpu)
Re-inject INTn software interrupts instead of retrying the instruction if the CPU encountered an intercepted exception while vectoring the INTn, e.g. if KVM intercepted a #PF when utilizing shadow paging. Retrying the instruction is architecturally wrong e.g. will result in a spurious #DB if there's a code breakpoint on the INT3/O, and lack of re-injection also breaks nested virtualization, e.g. if L1 injects a software interrupt and vectoring the injected interrupt encounters an exception that is intercepted by L0 but not L1. Signed-off-by: Sean Christopherson <seanjc@google.com> --- arch/x86/kvm/svm/svm.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)