While Secure AVIC hardware accelerates End-of-Interrupt (EOI) processing for edge-triggered interrupts, it requires hypervisor assistance for level-triggered interrupts originating from the IOAPIC. For these interrupts, a guest write to the EOI MSR triggers a VM-Exit. The primary challenge in handling this exit is that the guest's real In-Service Register (ISR) is not visible to KVM. When KVM receives an EOI, it has no direct way of knowing which interrupt vector is being acknowledged. To solve this, use KVM's software vAPIC state as a shadow tracking mechanism for active, level-triggered interrupts. The implementation follows this flow: 1. On interrupt injection (sev_savic_set_requested_irr), check KVM's software vAPIC Trigger Mode Register (TMR) to identify if the interrupt is level-triggered. 2. If it is, set the corresponding vector in KVM's software shadow ISR. This marks the interrupt as "in-service" from KVM's perspective. 3. When the guest later issues an EOI, the APIC_EOI MSR write exit handler finds the highest vector set in this shadow ISR. 4. The handler then clears the vector from the shadow ISR and calls kvm_apic_set_eoi_accelerated() to propagate the EOI to the virtual IOAPIC, allowing it to de-assert the interrupt line. This enables correct EOI handling for level-triggered interrupts in Secure AVIC guests, despite the hardware-enforced opacity of the guest's APIC state. Signed-off-by: Neeraj Upadhyay --- arch/x86/kvm/svm/sev.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 3e9cc50f2705..5be2956fb812 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4474,7 +4474,9 @@ static void savic_handle_icr_write(struct kvm_vcpu *kvm_vcpu, u64 icr) static bool savic_handle_msr_exit(struct kvm_vcpu *vcpu) { + struct kvm_lapic *apic; u32 msr, reg; + int vec; msr = kvm_rcx_read(vcpu); reg = (msr - APIC_BASE_MSR) << 4; @@ -4492,6 +4494,12 @@ static bool savic_handle_msr_exit(struct kvm_vcpu *vcpu) return true; } break; + case APIC_EOI: + apic = vcpu->arch.apic; + vec = apic_find_highest_vector(apic->regs + APIC_ISR); + apic_clear_vector(vec, apic->regs + APIC_ISR); + kvm_apic_set_eoi_accelerated(vcpu, vec); + return true; default: break; } @@ -5379,6 +5387,8 @@ void sev_savic_set_requested_irr(struct vcpu_svm *svm, bool reinjected) vec = vec_start + vec_pos; apic_clear_vector(vec, apic->regs + APIC_IRR); val = val & ~BIT(vec_pos); + if (apic_test_vector(vec, apic->regs + APIC_TMR)) + apic_set_vector(vec, apic->regs + APIC_ISR); } while (val); } -- 2.34.1