[PATCH v3 17/36] KVM: arm64: gic-v5: Finalize GICv5 PPIs and generate mask

Sascha Bischoff Sascha.Bischoff at arm.com
Fri Jan 9 09:04:44 PST 2026


We only want to expose a subset of the PPIs to a guest. If a PPI does
not have an owner, it is not being actively driven by a device. The
SW_PPI is a special case, as it is likely for userspace to wish to
inject that.

Therefore, just prior to running the guest for the first time, we need
to finalize the PPIs. A mask is generated which, when combined with
trapping a guest's PPI accesses, allows for the guest's view of the
PPI to be filtered. This mask is global to the VM as all VCPUs PPI
configurations must match.

In addition, the PPI HMR is calculated.

Signed-off-by: Sascha Bischoff <sascha.bischoff at arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron at huawei.com>
---
 arch/arm64/kvm/arm.c               |  4 +++
 arch/arm64/kvm/vgic/vgic-v5.c      | 49 ++++++++++++++++++++++++++++++
 include/kvm/arm_vgic.h             |  9 ++++++
 include/linux/irqchip/arm-gic-v5.h | 17 +++++++++++
 4 files changed, 79 insertions(+)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index b7cf9d86aabb7..94f8d13ab3b58 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -888,6 +888,10 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
 			return ret;
 	}
 
+	ret = vgic_v5_finalize_ppi_state(kvm);
+	if (ret)
+		return ret;
+
 	if (is_protected_kvm_enabled()) {
 		ret = pkvm_create_hyp_vm(kvm);
 		if (ret)
diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
index 68cf60fc7aa0c..bf2c77bafa1d3 100644
--- a/arch/arm64/kvm/vgic/vgic-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-v5.c
@@ -56,6 +56,55 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
 	return 0;
 }
 
+int vgic_v5_finalize_ppi_state(struct kvm *kvm)
+{
+	struct kvm_vcpu *vcpu;
+
+	if (!vgic_is_v5(kvm))
+		return 0;
+
+	if (!ppi_caps)
+		return -ENXIO;
+
+	/* The PPI state for all VCPUs should be the same. Pick the first. */
+	vcpu = kvm_get_vcpu(kvm, 0);
+
+	vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask[0] = 0;
+	vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask[1] = 0;
+	vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr[0] = 0;
+	vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr[1] = 0;
+
+	for (int i = 0; i < VGIC_V5_NR_PRIVATE_IRQS; i++) {
+		int reg = i / 64;
+		u64 bit = BIT_ULL(i % 64);
+		struct vgic_irq *irq = &vcpu->arch.vgic_cpu.private_irqs[i];
+
+		guard(raw_spinlock_irqsave)(&irq->irq_lock);
+
+		/*
+		 * We only expose PPIs with an owner or the SW_PPI to the
+		 * guest.
+		 */
+		if (!irq->owner &&
+		    FIELD_GET(GICV5_HWIRQ_ID, irq->intid) != GICV5_ARCH_PPI_SW_PPI)
+			continue;
+
+		/*
+		 * If the PPI isn't implemented, we can't pass it through to a
+		 * guest anyhow.
+		 */
+		if (!(ppi_caps->impl_ppi_mask[reg] & bit))
+			continue;
+
+		vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask[reg] |= bit;
+
+		if (irq->config == VGIC_CONFIG_LEVEL)
+			vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr[reg] |= bit;
+	}
+
+	return 0;
+}
+
 /*
  * Not all PPIs are guaranteed to be implemented for GICv5. Deterermine which
  * ones are, and generate a mask.
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index f3281bbf98454..50f5e3ffda6bd 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -32,6 +32,8 @@
 #define VGIC_MIN_LPI		8192
 #define KVM_IRQCHIP_NUM_PINS	(1020 - 32)
 
+#define VGIC_V5_NR_PRIVATE_IRQS	128
+
 #define is_v5_type(t, i)	(FIELD_GET(GICV5_HWIRQ_TYPE, (i)) == (t))
 
 #define __irq_is_sgi(t, i)						\
@@ -385,6 +387,11 @@ struct vgic_dist {
 	 * else.
 	 */
 	struct its_vm		its_vm;
+
+	/*
+	 * GICv5 per-VM data.
+	 */
+	struct gicv5_vm		gicv5_vm;
 };
 
 struct vgic_v2_cpu_if {
@@ -571,6 +578,8 @@ int vgic_v4_load(struct kvm_vcpu *vcpu);
 void vgic_v4_commit(struct kvm_vcpu *vcpu);
 int vgic_v4_put(struct kvm_vcpu *vcpu);
 
+int vgic_v5_finalize_ppi_state(struct kvm *kvm);
+
 bool vgic_state_is_nested(struct kvm_vcpu *vcpu);
 
 /* CPU HP callbacks */
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index f557dc7f250b8..21ac38147687b 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -369,6 +369,23 @@ struct gicv5_vpe {
 	bool			resident;
 };
 
+struct gicv5_vm {
+	/*
+	 * We only expose a subset of PPIs to the guest. This subset
+	 * is a combination of the PPIs that are actually implemented
+	 * and what we actually choose to expose.
+	 */
+	u64			vgic_ppi_mask[2];
+
+	/*
+	 * The HMR itself is handled by the hardware, but we still need to have
+	 * a mask that we can use when merging in pending state (only the state
+	 * of Edge PPIs is merged back in from the guest an the HMR provides a
+	 * convenient way to do that).
+	 */
+	u64			vgic_ppi_hmr[2];
+};
+
 struct gicv5_its_devtab_cfg {
 	union {
 		struct {
-- 
2.34.1



More information about the linux-arm-kernel mailing list