RE: [EXTERNAL] The problem of KVM checking Alternate Injection guest events

Jon Lange <[email protected]> Mon, 18 Aug 2025 04:52:53 +0000
Newsgroups dev.linux.lists.coconut-svsm
Message-ID <CH8PR21MB52220DBF67143C85CACC483CCA31A@CH8PR21MB5222.namprd21.prod.outlook.com>
I'm skeptical that you can ever make any of this work without the planes implementation in KVM, which is still under development.

Your example scenario is not clear about what vectors 33 and 236 mean.  Are they both interrupts targeted at VMPL 2?  Or are they interrupts targeted at the SVSM itself?  Or is one interrupt for each VMPL?

Targeting interrupts at multiple VMPLs is exactly what planes is supposed to enable, and without planes, this is impossible.

If the interrupts in your example are targeted to VMPL 2, then I would expect this to work because all of the injection required is performed by the SVSM up front, and thus the VMPL 2 state is fully configured in the emulated APIC.  When VMPL 2 executes EOI for the first interrupt, then this should cause the SVSM to execute (since EOI is an SVSM call) and SVSM should trigger the second interrupt as part of that processing.

If the concern is that a second interrupt arrives while VMPL 2 is executing, and that KVM doesn't know that it's supposed to invoke the SVSM for processing, then this is something that requires planes.

-Jon

-----Original Message-----
From: Melody Wang <[email protected]> 
Sent: Sunday, August 17, 2025 12:50 PM
To: Jon Lange <[email protected]>; Kaplan, David <[email protected]>; Lendacky, Thomas <[email protected]>; Rödel, Jörg <[email protected]>
Cc: [email protected]; Fontenot, Nathan <[email protected]>
Subject: [EXTERNAL] The problem of KVM checking Alternate Injection guest events

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