[PATCH v5 27/49] KVM: arm64: gic-v5: Support SPI injection
Sascha Bischoff
Sascha.Bischoff at arm.com
Fri Aug 7 04:26:20 PDT 2026
GICv5 SPI lifecycle is handled by the GICv5 hardware once the pending
state has been injected.
This change adds support for injecting SPIs to the core VGIC code and
GICv5 code. First of all, allow GICv5 SPIs to be looked up by ID via
vgic_get_irq(). Previously, only PPIs were supported.
Two irq_ops are used for GICv5 SPI injection. The set_pending_state()
irq_op is used to inject the SPI's pending state into the guest. The
queue_irq_unlock() irq_op releases the IRQ lock without queueing the
SPI on a per-vCPU AP list, as GICv5 does not track in-flight SPI state
in KVM. Tracking the affinity and lifecycle of individual SPIs in
software would negate much of the benefit of their lifecycle being
hardware managed.
Limit injection to the configured per-VM SPI range. Since GICv5 SPI
IDs start at zero, KVM_IRQ_LINE accepts IDs from zero through one less
than the configured number of SPIs, up to 1023. Update the
KVM_IRQ_LINE documentation accordingly.
Signed-off-by: Sascha Bischoff <sascha.bischoff at arm.com>
---
Documentation/virt/kvm/api.rst | 3 ++-
arch/arm64/kvm/vgic/vgic-irs-v5.c | 1 +
arch/arm64/kvm/vgic/vgic-v5.c | 41 +++++++++++++++++++++++++++++++
arch/arm64/kvm/vgic/vgic.c | 28 +++++++++++++++------
arch/arm64/kvm/vgic/vgic.h | 2 ++
5 files changed, 66 insertions(+), 9 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e3003a241d5b0..8647980e81c1f 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -909,7 +909,8 @@ The irq_type field has the following values:
- KVM_ARM_IRQ_TYPE_SPI:
in-kernel GICv2/GICv3: SPI, irq_id between 32 and 1019 (incl.)
(the vcpu_index field is ignored)
- in-kernel GICv5: SPI, irq_id between 0 and 65535 (incl.)
+ in-kernel GICv5: SPI, irq_id between 0 and the configured
+ number of SPIs minus one (1023 maximum)
- KVM_ARM_IRQ_TYPE_PPI:
in-kernel GICv2/GICv3: PPI, irq_id between 16 and 31 (incl.)
in-kernel GICv5: PPI, irq_id between 0 and 127 (incl.)
diff --git a/arch/arm64/kvm/vgic/vgic-irs-v5.c b/arch/arm64/kvm/vgic/vgic-irs-v5.c
index c6280795ab551..095046c7959fa 100644
--- a/arch/arm64/kvm/vgic/vgic-irs-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-irs-v5.c
@@ -680,6 +680,7 @@ int kvm_vgic_v5_irs_init(struct kvm *kvm, unsigned int nr_spis)
* view it is always enabled.
*/
irq->enabled = 1;
+ vgic_v5_set_spi_ops(irq);
}
nr_spi_bits = fls(roundup_pow_of_two(nr_spis)) - 1;
diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
index b401e80d74354..62e069c21367a 100644
--- a/arch/arm64/kvm/vgic/vgic-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-v5.c
@@ -1061,6 +1061,47 @@ void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu)
VGIC_V5_NR_PRIVATE_IRQS);
}
+static bool vgic_v5_set_spi_pending_state(struct kvm_vcpu *vcpu,
+ struct vgic_irq *irq)
+{
+ vgic_v5_set_irq_pend(irq->target_vcpu, irq);
+ return true;
+}
+
+static bool vgic_v5_spi_queue_irq_unlock(struct kvm *kvm,
+ struct vgic_irq *irq,
+ unsigned long flags)
+ __releases(&irq->irq_lock)
+{
+ lockdep_assert_held(&irq->irq_lock);
+
+ raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
+ return true;
+}
+
+static const struct irq_ops vgic_v5_spi_irq_ops = {
+ .set_pending_state = vgic_v5_set_spi_pending_state,
+ .queue_irq_unlock = vgic_v5_spi_queue_irq_unlock,
+};
+
+void vgic_v5_set_spi_ops(struct vgic_irq *irq)
+{
+ if (WARN_ON(!irq) || WARN_ON(irq->ops))
+ return;
+
+ irq->ops = &vgic_v5_spi_irq_ops;
+}
+
+/* Set the pending state for GICv5 SPIs and LPIs */
+void vgic_v5_set_irq_pend(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
+{
+ if (WARN_ON(__irq_is_ppi(KVM_DEV_TYPE_ARM_VGIC_V5, irq->intid)))
+ return;
+
+ kvm_call_hyp(__vgic_v5_vdpend, irq->intid, irq_is_pending(irq),
+ vcpu->kvm->arch.vgic.gicv5_vm.vm_id);
+}
+
void vgic_v5_load(struct kvm_vcpu *vcpu)
{
bool irichppidis = !READ_ONCE(vcpu->kvm->arch.vgic.enabled);
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 8f1994ec37311..5f10c0d9aa1c4 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -86,19 +86,31 @@ static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
*/
struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid)
{
- /* Non-private IRQs are not yet implemented for GICv5 */
- if (vgic_is_v5(kvm))
- return NULL;
+ enum kvm_device_type type = kvm->arch.vgic.vgic_model;
/* SPIs */
- if (intid >= VGIC_NR_PRIVATE_IRQS &&
- intid < (kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) {
- intid = array_index_nospec(intid, kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS);
- return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
+ if (__irq_is_spi(type, intid)) {
+ switch (type) {
+ case KVM_DEV_TYPE_ARM_VGIC_V5:
+ intid = vgic_v5_get_hwirq_id(intid);
+
+ if (intid >= kvm->arch.vgic.nr_spis)
+ return NULL;
+
+ intid = array_index_nospec(intid, kvm->arch.vgic.nr_spis);
+ return &kvm->arch.vgic.spis[intid];
+ default: {
+ u32 max_intid = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
+
+ if (intid < max_intid) {
+ intid = array_index_nospec(intid, max_intid);
+ return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
+ }
+ }}
}
/* LPIs */
- if (irq_is_lpi(kvm, intid))
+ if (__irq_is_lpi(type, intid))
return vgic_get_lpi(kvm, intid);
return NULL;
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 282278e4a6c19..107737c77e4d5 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -370,6 +370,8 @@ int kvm_vgic_v5_irs_init(struct kvm *kvm, unsigned int nr_spis);
void vgic_v5_teardown(struct kvm *kvm);
int vgic_v5_map_resources(struct kvm *kvm);
void vgic_v5_set_ppi_ops(struct kvm_vcpu *vcpu, u32 vintid);
+void vgic_v5_set_spi_ops(struct vgic_irq *irq);
+void vgic_v5_set_irq_pend(struct kvm_vcpu *vcpu, struct vgic_irq *irq);
bool vgic_v5_has_pending_ppi(struct kvm_vcpu *vcpu);
void vgic_v5_flush_ppi_state(struct kvm_vcpu *vcpu);
void vgic_v5_fold_ppi_state(struct kvm_vcpu *vcpu);
--
2.34.1
More information about the linux-arm-kernel
mailing list