[RFC PATCH v2 13/17] KVM: SVM: Add IOAPIC EOI support for Secure AVIC guests

Neeraj Upadhyay posted 17 patches 1 week, 1 day ago
[RFC PATCH v2 13/17] KVM: SVM: Add IOAPIC EOI support for Secure AVIC guests
Posted by Neeraj Upadhyay 1 week, 1 day ago
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 <Neeraj.Upadhyay@amd.com>
---
 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
Re: [RFC PATCH v2 13/17] KVM: SVM: Add IOAPIC EOI support for Secure AVIC guests
Posted by Tom Lendacky 1 week, 1 day ago
On 9/23/25 00:03, Neeraj Upadhyay wrote:
> 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 <Neeraj.Upadhyay@amd.com>
> ---
>  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;

Do you need to ensure that this is truly a WRMSR being done vs a RDMSR?
Or are you guaranteed that it is a WRMSR at this point?

Thanks,
Tom

>  	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);
>  	}
>