]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: vgic: Use common accessor for writes to ICPENDR
authorOliver Upton <oliver.upton@linux.dev>
Tue, 19 Dec 2023 06:58:54 +0000 (06:58 +0000)
committerMarc Zyngier <maz@kernel.org>
Fri, 22 Dec 2023 09:34:17 +0000 (09:34 +0000)
Fold MMIO and user accessors into a common helper while maintaining the
distinction between the two.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231219065855.1019608-3-oliver.upton@linux.dev
arch/arm64/kvm/vgic/vgic-mmio.c

index 273912083056a862d0346ca002353f7c68bc40cd..cf76523a219456dda0891ac9e6f21fb4eadf7e48 100644 (file)
@@ -386,9 +386,9 @@ static void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
                vgic_irq_set_phys_active(irq, false);
 }
 
-void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
-                             gpa_t addr, unsigned int len,
-                             unsigned long val)
+static void __clear_pending(struct kvm_vcpu *vcpu,
+                           gpa_t addr, unsigned int len,
+                           unsigned long val, bool is_user)
 {
        u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
        int i;
@@ -397,14 +397,22 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
        for_each_set_bit(i, &val, len * 8) {
                struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
-               /* GICD_ICPENDR0 SGI bits are WI */
-               if (is_vgic_v2_sgi(vcpu, irq)) {
+               /* GICD_ICPENDR0 SGI bits are WI when written from the guest. */
+               if (is_vgic_v2_sgi(vcpu, irq) && !is_user) {
                        vgic_put_irq(vcpu->kvm, irq);
                        continue;
                }
 
                raw_spin_lock_irqsave(&irq->irq_lock, flags);
 
+               /*
+                * More fun with GICv2 SGIs! If we're clearing one of them
+                * from userspace, which source vcpu to clear? Let's not
+                * even think of it, and blow the whole set.
+                */
+               if (is_vgic_v2_sgi(vcpu, irq))
+                       irq->source = 0;
+
                if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
                        /* HW SGI? Ask the GIC to clear its pending bit */
                        int err;
@@ -419,7 +427,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
                        continue;
                }
 
-               if (irq->hw)
+               if (irq->hw && !is_user)
                        vgic_hw_irq_cpending(vcpu, irq);
                else
                        irq->pending_latch = false;
@@ -429,33 +437,18 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
        }
 }
 
+void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
+                             gpa_t addr, unsigned int len,
+                             unsigned long val)
+{
+       __clear_pending(vcpu, addr, len, val, false);
+}
+
 int vgic_uaccess_write_cpending(struct kvm_vcpu *vcpu,
                                gpa_t addr, unsigned int len,
                                unsigned long val)
 {
-       u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
-       int i;
-       unsigned long flags;
-
-       for_each_set_bit(i, &val, len * 8) {
-               struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
-
-               raw_spin_lock_irqsave(&irq->irq_lock, flags);
-               /*
-                * More fun with GICv2 SGIs! If we're clearing one of them
-                * from userspace, which source vcpu to clear? Let's not
-                * even think of it, and blow the whole set.
-                */
-               if (is_vgic_v2_sgi(vcpu, irq))
-                       irq->source = 0;
-
-               irq->pending_latch = false;
-
-               raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
-
-               vgic_put_irq(vcpu->kvm, irq);
-       }
-
+       __clear_pending(vcpu, addr, len, val, true);
        return 0;
 }