aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kvm/vgic.c
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2013-01-21 19:36:15 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2013-02-11 13:59:49 -0500
commita1fcb44e26b0d98ebe53e8299462bf84c5aff178 (patch)
treee86131a6cb09992df13f5d04f94f47be32368575 /arch/arm/kvm/vgic.c
parent9d949dce523df878f1fce9f4d7738a5834650927 (diff)
ARM: KVM: vgic: retire queued, disabled interrupts
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. Reviewed-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'arch/arm/kvm/vgic.c')
-rw-r--r--arch/arm/kvm/vgic.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
index 8f32702108b2..2d5e29f1c28f 100644
--- a/arch/arm/kvm/vgic.c
+++ b/arch/arm/kvm/vgic.c
@@ -71,6 +71,7 @@
71#define ACCESS_WRITE_VALUE (3 << 1) 71#define ACCESS_WRITE_VALUE (3 << 1)
72#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1)) 72#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
73 73
74static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
74static void vgic_update_state(struct kvm *kvm); 75static void vgic_update_state(struct kvm *kvm);
75static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); 76static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
76 77
@@ -353,6 +354,7 @@ static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
353 if (mmio->is_write) { 354 if (mmio->is_write) {
354 if (offset < 4) /* Force SGI enabled */ 355 if (offset < 4) /* Force SGI enabled */
355 *reg |= 0xffff; 356 *reg |= 0xffff;
357 vgic_retire_disabled_irqs(vcpu);
356 vgic_update_state(vcpu->kvm); 358 vgic_update_state(vcpu->kvm);
357 return true; 359 return true;
358 } 360 }
@@ -804,6 +806,34 @@ static void vgic_update_state(struct kvm *kvm)
804 (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) 806 (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
805#define MK_LR_PEND(src, irq) \ 807#define MK_LR_PEND(src, irq) \
806 (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq)) 808 (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
809
810/*
811 * An interrupt may have been disabled after being made pending on the
812 * CPU interface (the classic case is a timer running while we're
813 * rebooting the guest - the interrupt would kick as soon as the CPU
814 * interface gets enabled, with deadly consequences).
815 *
816 * The solution is to examine already active LRs, and check the
817 * interrupt is still enabled. If not, just retire it.
818 */
819static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
820{
821 struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
822 int lr;
823
824 for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
825 int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID;
826
827 if (!vgic_irq_is_enabled(vcpu, irq)) {
828 vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
829 clear_bit(lr, vgic_cpu->lr_used);
830 vgic_cpu->vgic_lr[lr] &= ~GICH_LR_STATE;
831 if (vgic_irq_is_active(vcpu, irq))
832 vgic_irq_clear_active(vcpu, irq);
833 }
834 }
835}
836
807/* 837/*
808 * Queue an interrupt to a CPU virtual interface. Return true on success, 838 * Queue an interrupt to a CPU virtual interface. Return true on success,
809 * or false if it wasn't possible to queue it. 839 * or false if it wasn't possible to queue it.