[RFC PATCH 2/5] iommu/amd: Configure IRTE to use the GAPPI for posted interrupts
From: Sairaj Kodilkar
Date: Fri Jun 26 2026 - 07:00:32 EST
When GAPPI is enabled, a guest-mode IRTE with is_run clear delivers a
GAPPI interrupt to the physical APIC ID in the IRTE destination fields,
with the wake vector carried in ga_tag. Program the guest-activated IRTE
accordingly.
Reuse the POSTED_INTR_WAKEUP_VECTOR for gappi which is already reserved
for posted interrupt wakeup handler on x86 and is handled by IDTE entry
sysvec_kvm_posted_intr_wakeup_ipi. Intel VMX already uses same vector
for the wakeup case.
Signed-off-by: Sairaj Kodilkar <sarunkod@xxxxxxx>
---
arch/x86/kvm/svm/avic.c | 5 +++++
drivers/iommu/amd/amd_iommu.h | 1 +
drivers/iommu/amd/amd_iommu_types.h | 4 +++-
drivers/iommu/amd/init.c | 3 +++
drivers/iommu/amd/iommu.c | 21 +++++++++++++++++----
include/linux/amd-iommu.h | 1 +
6 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 7862b13c5409..b666efb5d91c 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -955,6 +955,11 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
} else {
posted_intr = !!(entry & AVIC_PHYSICAL_ID_ENTRY_GA_LOG_INTR);
pi_data.flags = posted_intr << AMD_IOMMU_FLAG_POSTED_INTR_SHIFT;
+ /* GAPPI is disabled at this point (amd_iommu_gappi is
+ * enabled in the following patches) hence keep the
+ * apicid as 0.
+ */
+ pi_data.apicid = 0;
}
ret = irq_set_vcpu_affinity(host_irq, &pi_data);
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 834d8fabfba3..044179cab12e 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -41,6 +41,7 @@ int amd_iommu_enable(void);
void amd_iommu_disable(void);
int amd_iommu_reenable(int mode);
int amd_iommu_enable_faulting(unsigned int cpu);
+extern bool amd_iommu_gappi;
extern int amd_iommu_guest_ir;
extern enum protection_domain_mode amd_iommu_pgtable;
extern int amd_iommu_gpt_level;
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index f9f718087893..26d7a9796e64 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -113,6 +113,7 @@
/* Extended Feature 2 Bits */
#define FEATURE_SEVSNPIO_SUP BIT_ULL(1)
#define FEATURE_GCR3TRPMODE BIT_ULL(3)
+#define FEATURE_GAPPIDISSUP BIT_ULL(4)
#define FEATURE_SNPAVICSUP GENMASK_ULL(7, 5)
#define FEATURE_SNPAVICSUP_GAM(x) \
(FIELD_GET(FEATURE_SNPAVICSUP, x) == 0x1)
@@ -1004,7 +1005,8 @@ union irte_ga_lo {
no_fault : 1,
/* ------ */
ga_log_intr : 1,
- rsvd1 : 3,
+ rsvd1 : 2,
+ gappi_dis : 1,
is_run : 1,
/* ------ */
guest_mode : 1,
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 3bdb380d23e9..2e1889f8a9e4 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -160,6 +160,9 @@ u8 amd_iommu_hpt_level;
/* Guest page table level */
int amd_iommu_gpt_level = PAGE_MODE_4_LEVEL;
+bool amd_iommu_gappi;
+EXPORT_SYMBOL(amd_iommu_gappi);
+
int amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_VAPIC;
static int amd_iommu_xt_mode = IRQ_REMAP_XAPIC_MODE;
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 76f0e469490e..4690cecc9aa7 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3969,9 +3969,18 @@ static void __amd_iommu_update_ga(struct irte_ga *entry, int apicid, int flags)
entry->lo.fields_vapic.is_run = true;
entry->lo.fields_vapic.ga_log_intr = false;
} else {
- entry->lo.fields_vapic.is_run = false;
- entry->lo.fields_vapic.ga_log_intr = !!(flags &
- AMD_IOMMU_FLAG_POSTED_INTR);
+ bool posted_intr = !!(flags & AMD_IOMMU_FLAG_POSTED_INTR);
+ if (amd_iommu_gappi) {
+ entry->lo.fields_vapic.gappi_dis = !posted_intr &&
+ check_feature2(FEATURE_GAPPIDISSUP);
+ entry->lo.fields_vapic.is_run = false;
+ entry->lo.fields_vapic.destination =
+ APICID_TO_IRTE_DEST_LO(apicid);
+ entry->hi.fields.destination = APICID_TO_IRTE_DEST_HI(apicid);
+ } else {
+ entry->lo.fields_vapic.is_run = false;
+ entry->lo.fields_vapic.ga_log_intr = posted_intr;
+ }
}
}
@@ -4034,7 +4043,11 @@ int amd_iommu_activate_guest_mode(void *data, int apicid, int flags)
entry->lo.fields_vapic.guest_mode = 1;
entry->hi.fields.ga_root_ptr = ir_data->ga_root_ptr;
entry->hi.fields.vector = ir_data->ga_vector;
- entry->lo.fields_vapic.ga_tag = ir_data->ga_tag;
+
+ if (amd_iommu_gappi)
+ entry->lo.fields_vapic.ga_tag = POSTED_INTR_WAKEUP_VECTOR;
+ else
+ entry->lo.fields_vapic.ga_tag = ir_data->ga_tag;
__amd_iommu_update_ga(entry, apicid, flags);
diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h
index 3dd9074e5967..87e76f617ea1 100644
--- a/include/linux/amd-iommu.h
+++ b/include/linux/amd-iommu.h
@@ -82,4 +82,5 @@ static inline bool amd_iommu_sev_tio_supported(void) { return false; }
#define AMD_IOMMU_FLAG_POSTED_INTR_SHIFT 1
#define AMD_IOMMU_FLAG_POSTED_INTR BIT(1)
+extern bool amd_iommu_gappi;
#endif /* _ASM_X86_AMD_IOMMU_H */
--
2.34.1