]> xenbits.xensource.com Git - people/pauldu/linux.git/commitdiff
KVM: arm64: vgic: Use common accessor for writes to ISPENDR
authorOliver Upton <oliver.upton@linux.dev>
Tue, 19 Dec 2023 06:58:53 +0000 (06:58 +0000)
committerMarc Zyngier <maz@kernel.org>
Fri, 22 Dec 2023 09:33:54 +0000 (09:33 +0000)
Perhaps unsurprisingly, there is a considerable amount of duplicate
code between the MMIO and user accessors for ISPENDR. At the same
time there are some important differences between user and guest
MMIO, like how SGIs can only be made pending from userspace.

Fold user and MMIO accessors into a common helper, 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-2-oliver.upton@linux.dev
arch/arm64/kvm/vgic/vgic-mmio.c

index ff558c05e990c728abd5361054cb04ef44083818..273912083056a862d0346ca002353f7c68bc40cd 100644 (file)
@@ -301,9 +301,8 @@ static bool is_vgic_v2_sgi(struct kvm_vcpu *vcpu, struct vgic_irq *irq)
                vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2);
 }
 
-void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
-                             gpa_t addr, unsigned int len,
-                             unsigned long val)
+static void __set_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;
@@ -312,14 +311,22 @@ void vgic_mmio_write_spending(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_ISPENDR0 SGI bits are WI */
-               if (is_vgic_v2_sgi(vcpu, irq)) {
+               /* GICD_ISPENDR0 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);
 
+               /*
+                * GICv2 SGIs are terribly broken. We can't restore
+                * the source of the interrupt, so just pick the vcpu
+                * itself as the source...
+                */
+               if (is_vgic_v2_sgi(vcpu, irq))
+                       irq->source |= BIT(vcpu->vcpu_id);
+
                if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
                        /* HW SGI? Ask the GIC to inject it */
                        int err;
@@ -335,7 +342,7 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
                }
 
                irq->pending_latch = true;
-               if (irq->hw)
+               if (irq->hw && !is_user)
                        vgic_irq_set_phys_active(irq, true);
 
                vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
@@ -343,33 +350,18 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
        }
 }
 
+void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
+                             gpa_t addr, unsigned int len,
+                             unsigned long val)
+{
+       __set_pending(vcpu, addr, len, val, false);
+}
+
 int vgic_uaccess_write_spending(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);
-               irq->pending_latch = true;
-
-               /*
-                * GICv2 SGIs are terribly broken. We can't restore
-                * the source of the interrupt, so just pick the vcpu
-                * itself as the source...
-                */
-               if (is_vgic_v2_sgi(vcpu, irq))
-                       irq->source |= BIT(vcpu->vcpu_id);
-
-               vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
-
-               vgic_put_irq(vcpu->kvm, irq);
-       }
-
+       __set_pending(vcpu, addr, len, val, true);
        return 0;
 }