[PATCH 28/43] KVM: arm64: gic-v5: Introduce SPI AP list

Sascha Bischoff
2026-04-27T16:16:43Z

As a general rule, GICv5 works a bit differently to previous
generation GICs. When it comes to virtual interrupts, as much as
possible is handled directly by the hardware and requires minimal
software interaction.

So far, the GICv5 support has been limited to PPIs. These are handled
via a set of ICH_PPI_*_EL2 registers, which are used by the hypervisor
to manage the PPI state exposed to the guest. They effectively take
the role of the ICH_LR*_EL2 registers found in earlier GICs, but do so
for EVERY PPI in parallel. For this reason, the GICv5 PPI support
doesn't use AP lists at all - all PPI state is always presented to the
guest.

The lifecycle of a virtual SPI is largely handled by the hardware with
GICv5. GICv5 itself provides a set of system instructions that act
upon the virtual domain. One of these, GIC VDPEND, can be used to make
a specified interrupt pending for a guest. The state of guest
interrupts is tracked by ISTs, which are allocated by the hypervisor
and provided directly by the hardware. The enable state for SPIs and
LPIs is driven directly by the guest (using the GIC CDEN/CDDIS system
instructions). Priority, affinity are also driven by the guest.

All of the above means that it is in theory possible to handle virtual
SPIs from KVM by just executing GIC VDPEND whenever new state is to be
injected into the guest. Of course, reality is a little bit more
complicated.

KVM itself provides an interface to register a notifier on interrupt
deactivation - specifically intended for use with SPIs on Arm-based
systems. This notifier requires KVM to track when an interrupt has
been consumed by the guest, so that the notifier can be called.

SPIs are not per-vcpu - they are effectively global to the VM (even if
they are affine to a specific VCPU, KVM doesn't need to know this
information). Therefore, this change introduces a per-VM AP list
specifically for tracking SPIs for a GICv5 guest. The intent is that
while an SPI is in-flight (pending/active) it remains on this list,
such that KVM knows to track the state of said SPI. Once the interrupt
has been consumed by the guest, it can be popped off the list.

Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
---
arch/arm64/kvm/vgic/vgic-irs-v5.c | 3 +++
include/kvm/arm_vgic.h | 14 ++++++++++++++
2 files changed, 17 insertions(+)

diff --git a/arch/arm64/kvm/vgic/vgic-irs-v5.c b/arch/arm64/kvm/vgic/vgic-irs-v5.c
index 729a3a3aca3a3..8e69b624194d5 100644
--- a/arch/arm64/kvm/vgic/vgic-irs-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-irs-v5.c
@@ -685,6 +685,9 @@ int kvm_vgic_v5_irs_init(struct kvm *kvm, unsigned int nr_spis)
int ret;
int i;

+ INIT_LIST_HEAD(&dist->vgic_v5_spi_ap_list_head);
+ raw_spin_lock_init(&dist->vgic_v5_spi_ap_list_lock);
+
/*
* We (KVM) allocate an Interrupt State Table (IST) for SPIs. The
* hardware mandates that lower 6 bits of the address are 0. Each ISTE
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 3a2dfbd5210f2..812ad325b01be 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -544,6 +544,20 @@ struct vgic_dist {
* GICv5 IRS data. Dynamically allocated due to the size.
*/
struct vgic_v5_irs *vgic_v5_irs_data;
+
+ /*
+ * The GICv5 SPI AP list is global to the VM. This spinlock ensures that
+ * we don't do anything untoward!
+ */
+ raw_spinlock_t vgic_v5_spi_ap_list_lock;
+
+ /*
+ * List of global (non-private) IRQs that must be tracked because they
+ * are either Active or Pending (hence the name; AP list). This list
+ * will only ever contain SPIs or LPIs. All private IRQs must go into a
+ * specific vcpu's AP list.
+ */
+ struct list_head vgic_v5_spi_ap_list_head;
};

struct vgic_v2_cpu_if {
--
2.34.1