KVM: arm64: vgic-v4: Restore pending state on host userspace write
authorMarc Zyngier <maz@kernel.org>
Sun, 17 Dec 2023 11:15:09 +0000 (11:15 +0000)
committerMarc Zyngier <maz@kernel.org>
Fri, 22 Dec 2023 09:27:36 +0000 (09:27 +0000)
When the VMM writes to ISPENDR0 to set the state pending state of
an SGI, we fail to convey this to the HW if this SGI is already
backed by a GICv4.1 vSGI.

This is a bit of a corner case, as this would only occur if the
vgic state is changed on an already running VM, but this can
apparently happen across a guest reset driven by the VMM.

Fix this by always writing out the pending_latch value to the
HW, and reseting it to false.

Reported-by: Kunkun Jiang <jiangkunkun@huawei.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Zenghui Yu <yuzenghui@huawei.com>
Cc: stable@vger.kernel.org # 5.10+
Link: https://lore.kernel.org/r/7e7f2c0c-448b-10a9-8929-4b8f4f6e2a32@huawei.com
arch/arm64/kvm/vgic/vgic-mmio-v3.c

index 89117ba2528a0ac9dced4caf1ee7699c92efc4de..111bd7f4272926fea506dc141c2aa5106ded9716 100644 (file)
@@ -365,19 +365,26 @@ static int vgic_v3_uaccess_write_pending(struct kvm_vcpu *vcpu,
                struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
 
                raw_spin_lock_irqsave(&irq->irq_lock, flags);
-               if (test_bit(i, &val)) {
-                       /*
-                        * pending_latch is set irrespective of irq type
-                        * (level or edge) to avoid dependency that VM should
-                        * restore irq config before pending info.
-                        */
-                       irq->pending_latch = true;
-                       vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
-               } else {
+
+               /*
+                * pending_latch is set irrespective of irq type
+                * (level or edge) to avoid dependency that VM should
+                * restore irq config before pending info.
+                */
+               irq->pending_latch = test_bit(i, &val);
+
+               if (irq->hw && vgic_irq_is_sgi(irq->intid)) {
+                       irq_set_irqchip_state(irq->host_irq,
+                                             IRQCHIP_STATE_PENDING,
+                                             irq->pending_latch);
                        irq->pending_latch = false;
-                       raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
                }
 
+               if (irq->pending_latch)
+                       vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
+               else
+                       raw_spin_unlock_irqrestore(&irq->irq_lock, flags);
+
                vgic_put_irq(vcpu->kvm, irq);
        }