summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorOliver Upton <oliver.upton@linux.dev>2025-07-26 08:50:06 -0700
committerOliver Upton <oliver.upton@linux.dev>2025-07-26 08:50:06 -0700
commit1f315e99bdd52001255cd26d822f23563f2fabb3 (patch)
treedbbd84c31b4acebca60970622a2a5a4dd9b49104 /drivers
parentccd73c57820eba4c4768c5c9e59eefe3e20e0870 (diff)
parentff2aa6495d4beafa84cfdd8c61fc00785fee3d9a (diff)
Merge branch 'kvm-arm64/gcie-legacy' into kvmarm/next
* kvm-arm64/gcie-legacy: : Support for GICv3 emulation on GICv5, courtesy of Sascha Bischoff : : FEAT_GCIE_LEGACY adds the necessary hardware for GICv5 systems to : support the legacy GICv3 for VMs, including a backwards-compatible VGIC : implementation that we all know and love. : : As a starting point for GICv5 enablement in KVM, enable + use the : GICv3-compatible feature when running VMs on GICv5 hardware. KVM: arm64: gic-v5: Probe for GICv5 KVM: arm64: gic-v5: Support GICv3 compat arm64/sysreg: Add ICH_VCTLR_EL2 irqchip/gic-v5: Populate struct gic_kvm_info irqchip/gic-v5: Skip deactivate for forwarded PPI interrupts Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/irqchip/irq-gic-v5.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index a9e3e75adb96..4bd224f359a7 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -13,6 +13,7 @@
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-v5.h>
+#include <linux/irqchip/arm-vgic-info.h>
#include <asm/cpufeature.h>
#include <asm/exception.h>
@@ -222,6 +223,12 @@ static void gicv5_hwirq_eoi(u32 hwirq_id, u8 hwirq_type)
static void gicv5_ppi_irq_eoi(struct irq_data *d)
{
+ /* Skip deactivate for forwarded PPI interrupts */
+ if (irqd_is_forwarded_to_vcpu(d)) {
+ gic_insn(0, CDEOI);
+ return;
+ }
+
gicv5_hwirq_eoi(d->hwirq, GICV5_HWIRQ_TYPE_PPI);
}
@@ -503,6 +510,16 @@ static bool gicv5_ppi_irq_is_level(irq_hw_number_t hwirq)
return !!(read_ppi_sysreg_s(hwirq, PPI_HM) & bit);
}
+static int gicv5_ppi_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
+{
+ if (vcpu)
+ irqd_set_forwarded_to_vcpu(d);
+ else
+ irqd_clr_forwarded_to_vcpu(d);
+
+ return 0;
+}
+
static const struct irq_chip gicv5_ppi_irq_chip = {
.name = "GICv5-PPI",
.irq_mask = gicv5_ppi_irq_mask,
@@ -510,6 +527,7 @@ static const struct irq_chip gicv5_ppi_irq_chip = {
.irq_eoi = gicv5_ppi_irq_eoi,
.irq_get_irqchip_state = gicv5_ppi_irq_get_irqchip_state,
.irq_set_irqchip_state = gicv5_ppi_irq_set_irqchip_state,
+ .irq_set_vcpu_affinity = gicv5_ppi_irq_set_vcpu_affinity,
.flags = IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};
@@ -1041,6 +1059,36 @@ static void gicv5_set_cpuif_idbits(void)
}
}
+#ifdef CONFIG_KVM
+static struct gic_kvm_info gic_v5_kvm_info __initdata;
+
+static bool __init gicv5_cpuif_has_gcie_legacy(void)
+{
+ u64 idr0 = read_sysreg_s(SYS_ICC_IDR0_EL1);
+ return !!FIELD_GET(ICC_IDR0_EL1_GCIE_LEGACY, idr0);
+}
+
+static void __init gic_of_setup_kvm_info(struct device_node *node)
+{
+ gic_v5_kvm_info.type = GIC_V5;
+ gic_v5_kvm_info.has_gcie_v3_compat = gicv5_cpuif_has_gcie_legacy();
+
+ /* GIC Virtual CPU interface maintenance interrupt */
+ gic_v5_kvm_info.no_maint_irq_mask = false;
+ gic_v5_kvm_info.maint_irq = irq_of_parse_and_map(node, 0);
+ if (!gic_v5_kvm_info.maint_irq) {
+ pr_warn("cannot find GICv5 virtual CPU interface maintenance interrupt\n");
+ return;
+ }
+
+ vgic_set_kvm_info(&gic_v5_kvm_info);
+}
+#else
+static inline void __init gic_of_setup_kvm_info(struct device_node *node)
+{
+}
+#endif // CONFIG_KVM
+
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
{
int ret = gicv5_irs_of_probe(node);
@@ -1073,6 +1121,8 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
gicv5_irs_its_probe();
+ gic_of_setup_kvm_info(node);
+
return 0;
out_int: