2022-04-04 21:23:49

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 6/8] KVM: SVM: Re-inject INTn instead of retrying the insn on "failure"

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 <[email protected]>
---
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);
+ break;
default:
break;
}
+
}

static void svm_cancel_injection(struct kvm_vcpu *vcpu)
--
2.35.1.1094.g7c7d902a7c-goog


2022-04-05 02:10:40

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH 6/8] KVM: SVM: Re-inject INTn instead of retrying the insn on "failure"

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 <[email protected]>
> ---
> 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;
> }
> +
> }

2022-04-05 03:12:05

by Maciej S. Szmigiero

[permalink] [raw]
Subject: Re: [PATCH 6/8] KVM: SVM: Re-inject INTn instead of retrying the insn on "failure"

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 <[email protected]>
> ---
> 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/