@@ -633,6 +633,34 @@ static void vgic_update_state(struct kvm *kvm)
#define LR_PHYSID(lr) (((lr) & VGIC_LR_PHYSID_CPUID) >> 10)
#define MK_LR_PEND(src, irq) (VGIC_LR_PENDING_BIT | ((src) << 10) | (irq))
+
+/*
+ * An interrupt may have been disabled after being made pending on the
+ * CPU interface (the classic case is a timer running while we're
+ * rebooting the guest - the interrupt would kick as soon as the CPU
+ * interface gets enabled, with deadly consequences).
+ *
+ * The solution is to examine already active LRs, and check the
+ * interrupt is still enabled. If not, just retire it.
+ */
+static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
+{
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+ int lr;
+
+ for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
+ int irq = vgic_cpu->vgic_lr[lr] & VGIC_LR_VIRTUALID;
+
+ if (!vgic_bitmap_get_irq_val(&dist->irq_enabled,
+ vcpu->vcpu_id, irq)) {
+ vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+ clear_bit(lr, vgic_cpu->lr_used);
+ vgic_cpu->vgic_lr[lr] &= ~VGIC_LR_STATE;
+ }
+ }
+}
+
/*
* Queue an interrupt to a CPU virtual interface. Return true on success,
* or false if it wasn't possible to queue it.
@@ -696,6 +724,8 @@ static void __kvm_vgic_sync_to_cpu(struct kvm_vcpu *vcpu)
vcpu_id = vcpu->vcpu_id;
+ vgic_retire_disabled_irqs(vcpu);
+
/*
* We may not have any pending interrupt, or the interrupts
* may have been serviced from another vcpu. In all cases,