diff options
Diffstat (limited to 'arch/arm/kvm/vgic.c')
-rw-r--r-- | arch/arm/kvm/vgic.c | 30 |
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 | ||
74 | static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu); | ||
74 | static void vgic_update_state(struct kvm *kvm); | 75 | static void vgic_update_state(struct kvm *kvm); |
75 | static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); | 76 | static 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 | */ | ||
819 | static 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. |