]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: gic-v5: Finalize GICv5 PPIs and generate mask
authorSascha Bischoff <Sascha.Bischoff@arm.com>
Thu, 19 Mar 2026 15:54:23 +0000 (15:54 +0000)
committerMarc Zyngier <maz@kernel.org>
Thu, 19 Mar 2026 18:21:28 +0000 (18:21 +0000)
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@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20260319154937.3619520-19-sascha.bischoff@arm.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/arm.c
arch/arm64/kvm/vgic/vgic-v5.c
include/kvm/arm_vgic.h

index aa69fd5b372fdbef6fcffc9f180e2fa388b7e8a0..5bbc1adb705e205dcc5c6de5dc84412e8eafe88d 100644 (file)
@@ -934,6 +934,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)
index 41317e1d94a2f8fd2db1d6a7d7a032fa41df1781..07f416fbc4bc853eaba0bd5013610afe80f0d5fd 100644 (file)
@@ -87,6 +87,41 @@ int vgic_v5_probe(const struct gic_kvm_info *info)
        return 0;
 }
 
+int vgic_v5_finalize_ppi_state(struct kvm *kvm)
+{
+       struct kvm_vcpu *vcpu0;
+       int i;
+
+       if (!vgic_is_v5(kvm))
+               return 0;
+
+       /* The PPI state for all VCPUs should be the same. Pick the first. */
+       vcpu0 = kvm_get_vcpu(kvm, 0);
+
+       bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS);
+       bitmap_zero(kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr, VGIC_V5_NR_PRIVATE_IRQS);
+
+       for_each_set_bit(i, ppi_caps.impl_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS) {
+               const u32 intid = vgic_v5_make_ppi(i);
+               struct vgic_irq *irq;
+
+               irq = vgic_get_vcpu_irq(vcpu0, intid);
+
+               /* Expose PPIs with an owner or the SW_PPI, only */
+               scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) {
+                       if (irq->owner || i == GICV5_ARCH_PPI_SW_PPI) {
+                               __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_mask, 1);
+                               __assign_bit(i, kvm->arch.vgic.gicv5_vm.vgic_ppi_hmr,
+                                            irq->config == VGIC_CONFIG_LEVEL);
+                       }
+               }
+
+               vgic_put_irq(vcpu0->kvm, irq);
+       }
+
+       return 0;
+}
+
 void vgic_v5_load(struct kvm_vcpu *vcpu)
 {
        struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5;
index b27bfc463a31111a14d3fe02a0e5fd509d09bac4..fdad0263499babddb984bd63aebd44b847bf7850 100644 (file)
@@ -326,6 +326,23 @@ struct vgic_redist_region {
        struct list_head list;
 };
 
+struct vgic_v5_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.
+        */
+       DECLARE_BITMAP(vgic_ppi_mask, VGIC_V5_NR_PRIVATE_IRQS);
+
+       /*
+        * 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).
+        */
+       DECLARE_BITMAP(vgic_ppi_hmr, VGIC_V5_NR_PRIVATE_IRQS);
+};
+
 struct vgic_dist {
        bool                    in_kernel;
        bool                    ready;
@@ -398,6 +415,11 @@ struct vgic_dist {
         * else.
         */
        struct its_vm           its_vm;
+
+       /*
+        * GICv5 per-VM data.
+        */
+       struct vgic_v5_vm       gicv5_vm;
 };
 
 struct vgic_v2_cpu_if {
@@ -588,6 +610,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 */