The problem of KVM checking Alternate Injection guest events

Melody Wang <[email protected]> Sun, 17 Aug 2025 12:50:28 -0700
Newsgroups dev.linux.lists.coconut-svsm
Message-ID <[email protected]>
I found one issue when multiple interrupts are being handled in the SVSM 
but I think it can happen also with a single one.

The gist of the observation is, that SVSM queues the interrupt into the 
VMPL2 guest's VMSA but if the hypervisor doesn't run the VMPL2 guest due 
to a lack of pending events - it cannot know that - then that interrupt 
won't get injected, leading to a stop.

Below are the details:

Interrupt 33 and 236 are in the doorbell page as multiple interrupts,

12796.893994: sev_snp_ai_update_doorbell: vector: 236, no_further: 1, 
ai_vector: 80, si_vector: 33
12796.893994: sev_snp_ai_update_doorbell: doorbell after:
12796.893994: dump_hvdb: pending_events:
12796.893995: dump_hvdb: vector: 80, nmi: 0, mce: 0, no_further_signal: 1
12796.893995: dump_hvdb: second_word: 0x200
12796.893995: dump_hvdb:  VMPL IRQs: 1: 0 2: 1 3: 0
12796.893997: dump_hvdb: irq_info: 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00
12796.893997: print_ext_irq_desc.constprop.0: VMPL2
12796.893997: print_ext_irq_desc.constprop.0:  ext_word0: 0x4000, 
si_vec: 0x0, mult: 1, ext_word1: 0x0, counter: 1226
12796.893998: print_ext_irq_desc.constprop.0:  irq_vec: 0002 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000

These two interrupts will be delivered to the SVSM, SVSM consumes these 
two interrupts, and marks them in the emulated IRR in the SVSM APIC:

12796.894031: __kvm_set_msr: guest: post_interrupt(33)
12796.894036: __kvm_set_msr: guest: post_interrupt(236)

Then the SVSM injects 236 which is the highest priority interrupt, which 
will be cleared in the IRR and moved to the ISR until the eoi is sent 
back from the guest, i.e. the interrupt has been serviced:

12796.894046: __kvm_set_msr: guest: deliver_immed: shadow: 0, 
irqs_enabled: 0, irq: 236
12796.894051: __kvm_set_msr: guest: queue_interrupt(236) into VintrCtrl: 
v_irq: 1
12796.894055: __kvm_set_msr: guest: Mark ISR, queued: 1, ISR idx: 1, 
delivered: 0

After 236 is written into the VintrCtrl, the SVSM will switch to VMPL2:

12796.894067: sev_handle_vmgexit: vmpl: 0, ghcb_info: 0x16
12796.894067: __sev_run_vmpl_vmsa: parent: 0xff445aee23690ac0, current: 
0xff445aee2cb83940, current_vmpl: 0
12796.894068: __sev_run_vmpl_vmsa: new_vmpl: 2

And VMPL2 runs:

12796.894069: svm_vcpu_run: VMPL switch: 0 -> 2, immed_ex: 0
12796.894071: svm_vcpu_run: parent: 0xff445aee23690ac0, clear switch_pending
12796.894071: kvm_exit: vcpu 0 reason npf rip 0x0 info1 
0x0000000500000014 info2 0x0000000001881000 intr_info 0x00000000 
error_code 0x00000000
12796.894071: kvm_page_fault: vcpu 0 rip 0x0 address 0x0000000001881000 
error_code 0x2000500000014
12796.894071: apic_has_pending_timer: lapic_timer.pending: 0
12796.894072: kvm_entry: vcpu 0, rip 0x0, vcpu_vmpl 2, current_vmpl 2, 
target_vmpl 2
12796.894073: kvm_exit: vcpu 0 reason npf rip 0x0 info1 
0x0000000500000006 info2 0x0000000277c1f000 intr_info 0x00000000 
error_code 0x00000000
12796.894073: kvm_page_fault: vcpu 0 rip 0x0 address 0x0000000277c1f000 
error_code 0x2000500000006
12796.894074: apic_has_pending_timer: lapic_timer.pending: 0
12796.894074: kvm_entry: vcpu 0, rip 0x0, vcpu_vmpl 2, current_vmpl 2, 
target_vmpl 2
12796.894076: kvm_exit: vcpu 0 reason npf rip 0x0 info1 
0x0000000500000014 info2 0x0000000002297000 intr_info 0x00000000 
error_code 0x00000000
12796.894076: kvm_page_fault: vcpu 0 rip 0x0 address 0x0000000002297000 
error_code 0x2000500000014
12796.894077: apic_has_pending_timer: lapic_timer.pending: 0
12796.894077: kvm_entry: vcpu 0, rip 0x0, vcpu_vmpl 2, current_vmpl 2, 
target_vmpl 2
12796.894079: kvm_exit: vcpu 0 reason hlt rip 0x0 info1 
0x0000000000000000 info2 0x0000000000000000 intr_info 0x00000000 
error_code 0x00000000
12796.894079: apic_has_pending_timer: lapic_timer.pending: 0

kvm_vcpu_has_events() is KVM's way to ask whether the guest needs to be 
run. It did not find there is any event in IRR (encrypted in the SVSM), 
so KVM does not run VMPL2 anymore even the interrupt is not handled. But 
in fact 33 is in SVSM's irr now which KVM does not know now, otherwise 
it will be able to trigger VMPL2 to run.

12796.894086: kvm_vcpu_has_events: none

So KVM needs a way to know whether to run the guests again - SVSM or 
VMPL2. I don't think we have such a provision in the spec so maybe we 
need something there.

Right?

-- 
Thanks,
Melody