|
@@ -1140,6 +1140,39 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq,
|
|
if (!vgic_irq_is_edge(vcpu, irq))
|
|
if (!vgic_irq_is_edge(vcpu, irq))
|
|
vlr.state |= LR_EOI_INT;
|
|
vlr.state |= LR_EOI_INT;
|
|
|
|
|
|
|
|
+ if (vlr.irq >= VGIC_NR_SGIS) {
|
|
|
|
+ struct irq_phys_map *map;
|
|
|
|
+ map = vgic_irq_map_search(vcpu, irq);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If we have a mapping, and the virtual interrupt is
|
|
|
|
+ * being injected, then we must set the state to
|
|
|
|
+ * active in the physical world. Otherwise the
|
|
|
|
+ * physical interrupt will fire and the guest will
|
|
|
|
+ * exit before processing the virtual interrupt.
|
|
|
|
+ */
|
|
|
|
+ if (map) {
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ BUG_ON(!map->active);
|
|
|
|
+ vlr.hwirq = map->phys_irq;
|
|
|
|
+ vlr.state |= LR_HW;
|
|
|
|
+ vlr.state &= ~LR_EOI_INT;
|
|
|
|
+
|
|
|
|
+ ret = irq_set_irqchip_state(map->irq,
|
|
|
|
+ IRQCHIP_STATE_ACTIVE,
|
|
|
|
+ true);
|
|
|
|
+ WARN_ON(ret);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Make sure we're not going to sample this
|
|
|
|
+ * again, as a HW-backed interrupt cannot be
|
|
|
|
+ * in the PENDING_ACTIVE stage.
|
|
|
|
+ */
|
|
|
|
+ vgic_irq_set_queued(vcpu, irq);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
|
vgic_sync_lr_elrsr(vcpu, lr_nr, vlr);
|
|
vgic_sync_lr_elrsr(vcpu, lr_nr, vlr);
|
|
}
|
|
}
|
|
@@ -1364,6 +1397,39 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|
return level_pending;
|
|
return level_pending;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Save the physical active state, and reset it to inactive.
|
|
|
|
+ *
|
|
|
|
+ * Return 1 if HW interrupt went from active to inactive, and 0 otherwise.
|
|
|
|
+ */
|
|
|
|
+static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
|
|
|
+{
|
|
|
|
+ struct irq_phys_map *map;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!(vlr.state & LR_HW))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ map = vgic_irq_map_search(vcpu, vlr.irq);
|
|
|
|
+ BUG_ON(!map || !map->active);
|
|
|
|
+
|
|
|
|
+ ret = irq_get_irqchip_state(map->irq,
|
|
|
|
+ IRQCHIP_STATE_ACTIVE,
|
|
|
|
+ &map->active);
|
|
|
|
+
|
|
|
|
+ WARN_ON(ret);
|
|
|
|
+
|
|
|
|
+ if (map->active) {
|
|
|
|
+ ret = irq_set_irqchip_state(map->irq,
|
|
|
|
+ IRQCHIP_STATE_ACTIVE,
|
|
|
|
+ false);
|
|
|
|
+ WARN_ON(ret);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
/* Sync back the VGIC state after a guest run */
|
|
/* Sync back the VGIC state after a guest run */
|
|
static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
|
static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
|
{
|
|
{
|
|
@@ -1378,14 +1444,31 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
|
elrsr = vgic_get_elrsr(vcpu);
|
|
elrsr = vgic_get_elrsr(vcpu);
|
|
elrsr_ptr = u64_to_bitmask(&elrsr);
|
|
elrsr_ptr = u64_to_bitmask(&elrsr);
|
|
|
|
|
|
- /* Clear mappings for empty LRs */
|
|
|
|
- for_each_set_bit(lr, elrsr_ptr, vgic->nr_lr) {
|
|
|
|
|
|
+ /* Deal with HW interrupts, and clear mappings for empty LRs */
|
|
|
|
+ for (lr = 0; lr < vgic->nr_lr; lr++) {
|
|
struct vgic_lr vlr;
|
|
struct vgic_lr vlr;
|
|
|
|
|
|
- if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
|
|
|
|
|
|
+ if (!test_bit(lr, vgic_cpu->lr_used))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
vlr = vgic_get_lr(vcpu, lr);
|
|
vlr = vgic_get_lr(vcpu, lr);
|
|
|
|
+ if (vgic_sync_hwirq(vcpu, vlr)) {
|
|
|
|
+ /*
|
|
|
|
+ * So this is a HW interrupt that the guest
|
|
|
|
+ * EOI-ed. Clean the LR state and allow the
|
|
|
|
+ * interrupt to be sampled again.
|
|
|
|
+ */
|
|
|
|
+ vlr.state = 0;
|
|
|
|
+ vlr.hwirq = 0;
|
|
|
|
+ vgic_set_lr(vcpu, lr, vlr);
|
|
|
|
+ vgic_irq_clear_queued(vcpu, vlr.irq);
|
|
|
|
+ set_bit(lr, elrsr_ptr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!test_bit(lr, elrsr_ptr))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ clear_bit(lr, vgic_cpu->lr_used);
|
|
|
|
|
|
BUG_ON(vlr.irq >= dist->nr_irqs);
|
|
BUG_ON(vlr.irq >= dist->nr_irqs);
|
|
vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
|
|
vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
|