]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: gic-v5: Trap and mask guest ICC_PPI_ENABLERx_EL1 writes
authorSascha Bischoff <Sascha.Bischoff@arm.com>
Thu, 19 Mar 2026 15:55:57 +0000 (15:55 +0000)
committerMarc Zyngier <maz@kernel.org>
Thu, 19 Mar 2026 18:21:28 +0000 (18:21 +0000)
A guest should not be able to detect if a PPI that is not exposed to
the guest is implemented or not. Avoid the guest enabling any PPIs
that are not implemented as far as the guest is concerned by trapping
and masking writes to the two ICC_PPI_ENABLERx_EL1 registers.

When a guest writes these registers, the write is masked with the set
of PPIs actually exposed to the guest, and the state is written back
to KVM's shadow state. As there is now no way for the guest to change
the PPI enable state without it being trapped, saving of the PPI
Enable state is dropped from guest exit.

Reads for the above registers are not masked. When the guest is
running and reads from the above registers, it is presented with what
KVM provides in the ICH_PPI_ENABLERx_EL2 registers, which is the
masked version of what the guest last wrote.

Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Link: https://patch.msgid.link/20260319154937.3619520-25-sascha.bischoff@arm.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/config.c
arch/arm64/kvm/hyp/vgic-v5-sr.c
arch/arm64/kvm/sys_regs.c

index c4a172b7020636c2dc5c37d9b7859741a1de237a..a7dc0aac3b93412c732e65dc310f479851f431c9 100644 (file)
@@ -814,7 +814,6 @@ struct kvm_host_data {
 
                /* The saved state of the regs when leaving the guest */
                DECLARE_BITMAP(activer_exit, VGIC_V5_NR_PRIVATE_IRQS);
-               DECLARE_BITMAP(enabler_exit, VGIC_V5_NR_PRIVATE_IRQS);
        } vgic_v5_ppi_state;
 };
 
index 5663f25905e8399dad5c07bcd37c1be219c5b0ec..e14685343191b2dde1810a4627fad4e12a0aca0d 100644 (file)
@@ -1699,6 +1699,17 @@ static void __compute_ich_hfgrtr(struct kvm_vcpu *vcpu)
                                             ICH_HFGRTR_EL2_ICC_IDRn_EL1);
 }
 
+static void __compute_ich_hfgwtr(struct kvm_vcpu *vcpu)
+{
+       __compute_fgt(vcpu, ICH_HFGWTR_EL2);
+
+       /*
+        * We present a different subset of PPIs the guest from what
+        * exist in real hardware. We only trap writes, not reads.
+        */
+       *vcpu_fgt(vcpu, ICH_HFGWTR_EL2) &= ~(ICH_HFGWTR_EL2_ICC_PPI_ENABLERn_EL1);
+}
+
 void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
 {
        if (!cpus_have_final_cap(ARM64_HAS_FGT))
@@ -1721,7 +1732,7 @@ void kvm_vcpu_load_fgt(struct kvm_vcpu *vcpu)
 
        if (cpus_have_final_cap(ARM64_HAS_GICV5_CPUIF)) {
                __compute_ich_hfgrtr(vcpu);
-               __compute_fgt(vcpu, ICH_HFGWTR_EL2);
+               __compute_ich_hfgwtr(vcpu);
                __compute_fgt(vcpu, ICH_HFGITR_EL2);
        }
 }
index f34ea219cc4e9027922121a948f35469e979625d..2c4304ffa9f3310ab4d783df49568a9e171ccc36 100644 (file)
@@ -37,8 +37,6 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if)
 
        bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit,
                     read_sysreg_s(SYS_ICH_PPI_ACTIVER0_EL2), 0, 64);
-       bitmap_write(host_data_ptr(vgic_v5_ppi_state)->enabler_exit,
-                    read_sysreg_s(SYS_ICH_PPI_ENABLER0_EL2), 0, 64);
        bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit,
                     read_sysreg_s(SYS_ICH_PPI_PENDR0_EL2), 0, 64);
 
@@ -54,8 +52,6 @@ void __vgic_v5_save_ppi_state(struct vgic_v5_cpu_if *cpu_if)
        if (VGIC_V5_NR_PRIVATE_IRQS == 128) {
                bitmap_write(host_data_ptr(vgic_v5_ppi_state)->activer_exit,
                             read_sysreg_s(SYS_ICH_PPI_ACTIVER1_EL2), 64, 64);
-               bitmap_write(host_data_ptr(vgic_v5_ppi_state)->enabler_exit,
-                            read_sysreg_s(SYS_ICH_PPI_ENABLER1_EL2), 64, 64);
                bitmap_write(host_data_ptr(vgic_v5_ppi_state)->pendr_exit,
                             read_sysreg_s(SYS_ICH_PPI_PENDR1_EL2), 64, 64);
 
index 85300e76bbe46c019257c9a2db77a678524bb813..e1001544d4f40c0eb3c51fd90526b8aaf61c9a30 100644 (file)
@@ -718,6 +718,54 @@ static bool access_gicv5_iaffid(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
        return true;
 }
 
+static bool access_gicv5_ppi_enabler(struct kvm_vcpu *vcpu,
+                                    struct sys_reg_params *p,
+                                    const struct sys_reg_desc *r)
+{
+       unsigned long *mask = vcpu->kvm->arch.vgic.gicv5_vm.vgic_ppi_mask;
+       struct vgic_v5_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v5;
+       int i;
+
+       /* We never expect to get here with a read! */
+       if (WARN_ON_ONCE(!p->is_write))
+               return undef_access(vcpu, p, r);
+
+       /*
+        * If we're only handling architected PPIs and the guest writes to the
+        * enable for the non-architected PPIs, we just return as there's
+        * nothing to do at all. We don't even allocate the storage for them in
+        * this case.
+        */
+       if (VGIC_V5_NR_PRIVATE_IRQS == 64 && p->Op2 % 2)
+               return true;
+
+       /*
+        * Merge the raw guest write into out bitmap at an offset of either 0 or
+        * 64, then and it with our PPI mask.
+        */
+       bitmap_write(cpu_if->vgic_ppi_enabler, p->regval, 64 * (p->Op2 % 2), 64);
+       bitmap_and(cpu_if->vgic_ppi_enabler, cpu_if->vgic_ppi_enabler, mask,
+                  VGIC_V5_NR_PRIVATE_IRQS);
+
+       /*
+        * Sync the change in enable states to the vgic_irqs. We consider all
+        * PPIs as we don't expose many to the guest.
+        */
+       for_each_set_bit(i, mask, VGIC_V5_NR_PRIVATE_IRQS) {
+               u32 intid = vgic_v5_make_ppi(i);
+               struct vgic_irq *irq;
+
+               irq = vgic_get_vcpu_irq(vcpu, intid);
+
+               scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
+                       irq->enabled = test_bit(i, cpu_if->vgic_ppi_enabler);
+
+               vgic_put_irq(vcpu->kvm, irq);
+       }
+
+       return true;
+}
+
 static bool trap_raz_wi(struct kvm_vcpu *vcpu,
                        struct sys_reg_params *p,
                        const struct sys_reg_desc *r)
@@ -3444,6 +3492,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        { SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access },
        { SYS_DESC(SYS_ICC_IDR0_EL1), access_gicv5_idr0 },
        { SYS_DESC(SYS_ICC_IAFFIDR_EL1), access_gicv5_iaffid },
+       { SYS_DESC(SYS_ICC_PPI_ENABLER0_EL1), access_gicv5_ppi_enabler },
+       { SYS_DESC(SYS_ICC_PPI_ENABLER1_EL1), access_gicv5_ppi_enabler },
        { SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir },
        { SYS_DESC(SYS_ICC_RPR_EL1), undef_access },
        { SYS_DESC(SYS_ICC_SGI1R_EL1), access_gic_sgi },