]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: gic: Introduce interrupt type helpers
authorSascha Bischoff <Sascha.Bischoff@arm.com>
Thu, 19 Mar 2026 15:52:03 +0000 (15:52 +0000)
committerMarc Zyngier <maz@kernel.org>
Thu, 19 Mar 2026 18:21:27 +0000 (18:21 +0000)
GICv5 has moved from using interrupt ranges for different interrupt
types to using some of the upper bits of the interrupt ID to denote
the interrupt type. This is not compatible with older GICs (which rely
on ranges of interrupts to determine the type), and hence a set of
helpers is introduced. These helpers take a struct kvm*, and use the
vgic model to determine how to interpret the interrupt ID.

Helpers are introduced for PPIs, SPIs, and LPIs. Additionally, a
helper is introduced to determine if an interrupt is private - SGIs
and PPIs for older GICs, and PPIs only for GICv5.

Additionally, vgic_is_v5() is introduced (which unsurpisingly returns
true when running a GICv5 guest), and the existing vgic_is_v3() check
is moved from vgic.h to arm_vgic.h (to live alongside the vgic_is_v5()
one), and has been converted into a macro.

The helpers are plumbed into the core vgic code, as well as the Arch
Timer and PMU code.

There should be no functional changes as part of this change.

Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Reviewed-by: Joey Gouly <joey.gouly@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20260319154937.3619520-10-sascha.bischoff@arm.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/arch_timer.c
arch/arm64/kvm/pmu-emul.c
arch/arm64/kvm/vgic/vgic-kvm-device.c
arch/arm64/kvm/vgic/vgic.c
arch/arm64/kvm/vgic/vgic.h
include/kvm/arm_vgic.h

index d31bc1e7a13c2cb0d056ab3e307e0f1cff514ea2..92870ee6dacd8c819a13509cf87f660502b1d312 100644 (file)
@@ -1603,7 +1603,7 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
        if (get_user(irq, uaddr))
                return -EFAULT;
 
-       if (!(irq_is_ppi(irq)))
+       if (!(irq_is_ppi(vcpu->kvm, irq)))
                return -EINVAL;
 
        mutex_lock(&vcpu->kvm->arch.config_lock);
index 93cc9bbb5cecdf67f95a643d9ffe65a9def7004b..41a3c5dc2bcac7d8e21888e510bd0117703ddf8f 100644 (file)
@@ -939,7 +939,8 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
                 * number against the dimensions of the vgic and make sure
                 * it's valid.
                 */
-               if (!irq_is_ppi(irq) && !vgic_valid_spi(vcpu->kvm, irq))
+               if (!irq_is_ppi(vcpu->kvm, irq) &&
+                   !vgic_valid_spi(vcpu->kvm, irq))
                        return -EINVAL;
        } else if (kvm_arm_pmu_irq_initialized(vcpu)) {
                   return -EINVAL;
@@ -991,7 +992,7 @@ static bool pmu_irq_is_valid(struct kvm *kvm, int irq)
                if (!kvm_arm_pmu_irq_initialized(vcpu))
                        continue;
 
-               if (irq_is_ppi(irq)) {
+               if (irq_is_ppi(vcpu->kvm, irq)) {
                        if (vcpu->arch.pmu.irq_num != irq)
                                return false;
                } else {
@@ -1142,7 +1143,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
                        return -EFAULT;
 
                /* The PMU overflow interrupt can be a PPI or a valid SPI. */
-               if (!(irq_is_ppi(irq) || irq_is_spi(irq)))
+               if (!(irq_is_ppi(vcpu->kvm, irq) || irq_is_spi(vcpu->kvm, irq)))
                        return -EINVAL;
 
                if (!pmu_irq_is_valid(kvm, irq))
index 3d1a776b716d7425ed509ec6a5594e3880c28f61..b12ba99a423e52469c265915a29cfc7446c78cb3 100644 (file)
@@ -639,7 +639,7 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
                if (vgic_initialized(dev->kvm))
                        return -EBUSY;
 
-               if (!irq_is_ppi(val))
+               if (!irq_is_ppi(dev->kvm, val))
                        return -EINVAL;
 
                dev->kvm->arch.vgic.mi_intid = val;
index 9e0d26348a2a1654cdc5529a5771d6388c09db90..2f3f892cbddc24a70e7da7c268e81a21eec61554 100644 (file)
@@ -94,7 +94,7 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid)
        }
 
        /* LPIs */
-       if (intid >= VGIC_MIN_LPI)
+       if (irq_is_lpi(kvm, intid))
                return vgic_get_lpi(kvm, intid);
 
        return NULL;
@@ -123,7 +123,7 @@ static void vgic_release_lpi_locked(struct vgic_dist *dist, struct vgic_irq *irq
 
 static __must_check bool __vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
 {
-       if (irq->intid < VGIC_MIN_LPI)
+       if (!irq_is_lpi(kvm, irq->intid))
                return false;
 
        return refcount_dec_and_test(&irq->refcount);
@@ -148,7 +148,7 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
         * Acquire/release it early on lockdep kernels to make locking issues
         * in rare release paths a bit more obvious.
         */
-       if (IS_ENABLED(CONFIG_LOCKDEP) && irq->intid >= VGIC_MIN_LPI) {
+       if (IS_ENABLED(CONFIG_LOCKDEP) && irq_is_lpi(kvm, irq->intid)) {
                guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock);
        }
 
@@ -186,7 +186,7 @@ void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
        raw_spin_lock_irqsave(&vgic_cpu->ap_list_lock, flags);
 
        list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) {
-               if (irq->intid >= VGIC_MIN_LPI) {
+               if (irq_is_lpi(vcpu->kvm, irq->intid)) {
                        raw_spin_lock(&irq->irq_lock);
                        list_del(&irq->ap_list);
                        irq->vcpu = NULL;
@@ -521,12 +521,12 @@ int kvm_vgic_inject_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
        if (ret)
                return ret;
 
-       if (!vcpu && intid < VGIC_NR_PRIVATE_IRQS)
+       if (!vcpu && irq_is_private(kvm, intid))
                return -EINVAL;
 
        trace_vgic_update_irq_pending(vcpu ? vcpu->vcpu_idx : 0, intid, level);
 
-       if (intid < VGIC_NR_PRIVATE_IRQS)
+       if (irq_is_private(kvm, intid))
                irq = vgic_get_vcpu_irq(vcpu, intid);
        else
                irq = vgic_get_irq(kvm, intid);
@@ -700,7 +700,7 @@ int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner)
                return -EAGAIN;
 
        /* SGIs and LPIs cannot be wired up to any device */
-       if (!irq_is_ppi(intid) && !vgic_valid_spi(vcpu->kvm, intid))
+       if (!irq_is_ppi(vcpu->kvm, intid) && !vgic_valid_spi(vcpu->kvm, intid))
                return -EINVAL;
 
        irq = vgic_get_vcpu_irq(vcpu, intid);
index 0bb8fa10bb4eff758edb16ec503bfd5817373853..f2924f821197492052340dbe495ffde6df6ebb3c 100644 (file)
@@ -454,11 +454,6 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu);
 void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu);
 void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu);
 
-static inline bool vgic_is_v3(struct kvm *kvm)
-{
-       return kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3;
-}
-
 static inline bool vgic_host_has_gicv3(void)
 {
        /*
index 46262d1433bcab432540216163ff7ce94fa48b5f..b8011b395796d2af65cd42254d58b17a097adfda 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/jump_label.h>
 
 #include <linux/irqchip/arm-gic-v4.h>
+#include <linux/irqchip/arm-gic-v5.h>
 
 #define VGIC_V3_MAX_CPUS       512
 #define VGIC_V2_MAX_CPUS       8
 #define VGIC_MIN_LPI           8192
 #define KVM_IRQCHIP_NUM_PINS   (1020 - 32)
 
-#define irq_is_ppi(irq) ((irq) >= VGIC_NR_SGIS && (irq) < VGIC_NR_PRIVATE_IRQS)
-#define irq_is_spi(irq) ((irq) >= VGIC_NR_PRIVATE_IRQS && \
-                        (irq) <= VGIC_MAX_SPI)
+#define is_v5_type(t, i)       (FIELD_GET(GICV5_HWIRQ_TYPE, (i)) == (t))
+
+#define __irq_is_sgi(t, i)                                             \
+       ({                                                              \
+               bool __ret;                                             \
+                                                                       \
+               switch (t) {                                            \
+               case KVM_DEV_TYPE_ARM_VGIC_V5:                          \
+                       __ret = false;                                  \
+                       break;                                          \
+               default:                                                \
+                       __ret  = (i) < VGIC_NR_SGIS;                    \
+               }                                                       \
+                                                                       \
+               __ret;                                                  \
+       })
+
+#define __irq_is_ppi(t, i)                                             \
+       ({                                                              \
+               bool __ret;                                             \
+                                                                       \
+               switch (t) {                                            \
+               case KVM_DEV_TYPE_ARM_VGIC_V5:                          \
+                       __ret = is_v5_type(GICV5_HWIRQ_TYPE_PPI, (i));  \
+                       break;                                          \
+               default:                                                \
+                       __ret  = (i) >= VGIC_NR_SGIS;                   \
+                       __ret &= (i) < VGIC_NR_PRIVATE_IRQS;            \
+               }                                                       \
+                                                                       \
+               __ret;                                                  \
+       })
+
+#define __irq_is_spi(t, i)                                             \
+       ({                                                              \
+               bool __ret;                                             \
+                                                                       \
+               switch (t) {                                            \
+               case KVM_DEV_TYPE_ARM_VGIC_V5:                          \
+                       __ret = is_v5_type(GICV5_HWIRQ_TYPE_SPI, (i));  \
+                       break;                                          \
+               default:                                                \
+                       __ret  = (i) <= VGIC_MAX_SPI;                   \
+                       __ret &= (i) >= VGIC_NR_PRIVATE_IRQS;           \
+               }                                                       \
+                                                                       \
+               __ret;                                                  \
+       })
+
+#define __irq_is_lpi(t, i)                                             \
+       ({                                                              \
+               bool __ret;                                             \
+                                                                       \
+               switch (t) {                                            \
+               case KVM_DEV_TYPE_ARM_VGIC_V5:                          \
+                       __ret = is_v5_type(GICV5_HWIRQ_TYPE_LPI, (i));  \
+                       break;                                          \
+               default:                                                \
+                       __ret  = (i) >= 8192;                           \
+               }                                                       \
+                                                                       \
+               __ret;                                                  \
+       })
+
+#define irq_is_sgi(k, i) __irq_is_sgi((k)->arch.vgic.vgic_model, i)
+#define irq_is_ppi(k, i) __irq_is_ppi((k)->arch.vgic.vgic_model, i)
+#define irq_is_spi(k, i) __irq_is_spi((k)->arch.vgic.vgic_model, i)
+#define irq_is_lpi(k, i) __irq_is_lpi((k)->arch.vgic.vgic_model, i)
+
+#define irq_is_private(k, i) (irq_is_ppi(k, i) || irq_is_sgi(k, i))
+
+#define vgic_v5_get_hwirq_id(x) FIELD_GET(GICV5_HWIRQ_ID, (x))
+#define vgic_v5_set_hwirq_id(x) FIELD_PREP(GICV5_HWIRQ_ID, (x))
+
+#define __vgic_v5_set_type(t) (FIELD_PREP(GICV5_HWIRQ_TYPE, GICV5_HWIRQ_TYPE_##t))
+#define vgic_v5_make_ppi(x) (__vgic_v5_set_type(PPI) | vgic_v5_set_hwirq_id(x))
+#define vgic_v5_make_spi(x) (__vgic_v5_set_type(SPI) | vgic_v5_set_hwirq_id(x))
+#define vgic_v5_make_lpi(x) (__vgic_v5_set_type(LPI) | vgic_v5_set_hwirq_id(x))
+
+#define __vgic_is_v(k, v) ((k)->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V##v)
+#define vgic_is_v3(k) (__vgic_is_v(k, 3))
+#define vgic_is_v5(k) (__vgic_is_v(k, 5))
 
 enum vgic_type {
        VGIC_V2,                /* Good ol' GICv2 */
@@ -417,8 +497,20 @@ u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu);
 
 #define irqchip_in_kernel(k)   (!!((k)->arch.vgic.in_kernel))
 #define vgic_initialized(k)    ((k)->arch.vgic.initialized)
-#define vgic_valid_spi(k, i)   (((i) >= VGIC_NR_PRIVATE_IRQS) && \
-                       ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS))
+#define vgic_valid_spi(k, i)                                           \
+       ({                                                              \
+               bool __ret = irq_is_spi(k, i);                          \
+                                                                       \
+               switch ((k)->arch.vgic.vgic_model) {                    \
+               case KVM_DEV_TYPE_ARM_VGIC_V5:                          \
+                       __ret &= FIELD_GET(GICV5_HWIRQ_ID, i) < (k)->arch.vgic.nr_spis; \
+                       break;                                          \
+               default:                                                \
+                       __ret &= (i) < ((k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS); \
+               }                                                       \
+                                                                       \
+               __ret;                                                  \
+       })
 
 bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu);
 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu);