|
@@ -531,6 +531,34 @@ bool vgic_handle_set_pending_reg(struct kvm *kvm,
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * If a mapped interrupt's state has been modified by the guest such that it
|
|
|
|
+ * is no longer active or pending, without it have gone through the sync path,
|
|
|
|
+ * then the map->active field must be cleared so the interrupt can be taken
|
|
|
|
+ * again.
|
|
|
|
+ */
|
|
|
|
+static void vgic_handle_clear_mapped_irq(struct kvm_vcpu *vcpu)
|
|
|
|
+{
|
|
|
|
+ struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
|
|
+ struct list_head *root;
|
|
|
|
+ struct irq_phys_map_entry *entry;
|
|
|
|
+ struct irq_phys_map *map;
|
|
|
|
+
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+
|
|
|
|
+ /* Check for PPIs */
|
|
|
|
+ root = &vgic_cpu->irq_phys_map_list;
|
|
|
|
+ list_for_each_entry_rcu(entry, root, entry) {
|
|
|
|
+ map = &entry->map;
|
|
|
|
+
|
|
|
|
+ if (!vgic_dist_irq_is_pending(vcpu, map->virt_irq) &&
|
|
|
|
+ !vgic_irq_is_active(vcpu, map->virt_irq))
|
|
|
|
+ map->active = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+}
|
|
|
|
+
|
|
bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
|
bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
|
struct kvm_exit_mmio *mmio,
|
|
struct kvm_exit_mmio *mmio,
|
|
phys_addr_t offset, int vcpu_id)
|
|
phys_addr_t offset, int vcpu_id)
|
|
@@ -561,6 +589,7 @@ bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
|
vcpu_id, offset);
|
|
vcpu_id, offset);
|
|
vgic_reg_access(mmio, reg, offset, mode);
|
|
vgic_reg_access(mmio, reg, offset, mode);
|
|
|
|
|
|
|
|
+ vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id));
|
|
vgic_update_state(kvm);
|
|
vgic_update_state(kvm);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
@@ -598,6 +627,7 @@ bool vgic_handle_clear_active_reg(struct kvm *kvm,
|
|
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
|
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
|
|
|
|
|
if (mmio->is_write) {
|
|
if (mmio->is_write) {
|
|
|
|
+ vgic_handle_clear_mapped_irq(kvm_get_vcpu(kvm, vcpu_id));
|
|
vgic_update_state(kvm);
|
|
vgic_update_state(kvm);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
@@ -982,6 +1012,12 @@ static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
|
|
pend_percpu = vcpu->arch.vgic_cpu.pending_percpu;
|
|
pend_percpu = vcpu->arch.vgic_cpu.pending_percpu;
|
|
pend_shared = vcpu->arch.vgic_cpu.pending_shared;
|
|
pend_shared = vcpu->arch.vgic_cpu.pending_shared;
|
|
|
|
|
|
|
|
+ if (!dist->enabled) {
|
|
|
|
+ bitmap_zero(pend_percpu, VGIC_NR_PRIVATE_IRQS);
|
|
|
|
+ bitmap_zero(pend_shared, nr_shared);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id);
|
|
pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id);
|
|
enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
|
|
enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
|
|
bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS);
|
|
bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS);
|
|
@@ -1009,11 +1045,6 @@ void vgic_update_state(struct kvm *kvm)
|
|
struct kvm_vcpu *vcpu;
|
|
struct kvm_vcpu *vcpu;
|
|
int c;
|
|
int c;
|
|
|
|
|
|
- if (!dist->enabled) {
|
|
|
|
- set_bit(0, dist->irq_pending_on_cpu);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
kvm_for_each_vcpu(c, vcpu, kvm) {
|
|
kvm_for_each_vcpu(c, vcpu, kvm) {
|
|
if (compute_pending_for_cpu(vcpu))
|
|
if (compute_pending_for_cpu(vcpu))
|
|
set_bit(c, dist->irq_pending_on_cpu);
|
|
set_bit(c, dist->irq_pending_on_cpu);
|
|
@@ -1092,6 +1123,15 @@ static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu)
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
|
struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * We must transfer the pending state back to the distributor before
|
|
|
|
+ * retiring the LR, otherwise we may loose edge-triggered interrupts.
|
|
|
|
+ */
|
|
|
|
+ if (vlr.state & LR_STATE_PENDING) {
|
|
|
|
+ vgic_dist_irq_set_pending(vcpu, irq);
|
|
|
|
+ vlr.hwirq = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
vlr.state = 0;
|
|
vlr.state = 0;
|
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
|
vgic_set_lr(vcpu, lr_nr, vlr);
|
|
clear_bit(lr_nr, vgic_cpu->lr_used);
|
|
clear_bit(lr_nr, vgic_cpu->lr_used);
|
|
@@ -1132,7 +1172,8 @@ static void vgic_queue_irq_to_lr(struct kvm_vcpu *vcpu, int irq,
|
|
kvm_debug("Set active, clear distributor: 0x%x\n", vlr.state);
|
|
kvm_debug("Set active, clear distributor: 0x%x\n", vlr.state);
|
|
vgic_irq_clear_active(vcpu, irq);
|
|
vgic_irq_clear_active(vcpu, irq);
|
|
vgic_update_state(vcpu->kvm);
|
|
vgic_update_state(vcpu->kvm);
|
|
- } else if (vgic_dist_irq_is_pending(vcpu, irq)) {
|
|
|
|
|
|
+ } else {
|
|
|
|
+ WARN_ON(!vgic_dist_irq_is_pending(vcpu, irq));
|
|
vlr.state |= LR_STATE_PENDING;
|
|
vlr.state |= LR_STATE_PENDING;
|
|
kvm_debug("Set pending: 0x%x\n", vlr.state);
|
|
kvm_debug("Set pending: 0x%x\n", vlr.state);
|
|
}
|
|
}
|
|
@@ -1240,7 +1281,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
|
unsigned long *pa_percpu, *pa_shared;
|
|
unsigned long *pa_percpu, *pa_shared;
|
|
- int i, vcpu_id, lr, ret;
|
|
|
|
|
|
+ int i, vcpu_id;
|
|
int overflow = 0;
|
|
int overflow = 0;
|
|
int nr_shared = vgic_nr_shared_irqs(dist);
|
|
int nr_shared = vgic_nr_shared_irqs(dist);
|
|
|
|
|
|
@@ -1295,31 +1336,6 @@ epilog:
|
|
*/
|
|
*/
|
|
clear_bit(vcpu_id, dist->irq_pending_on_cpu);
|
|
clear_bit(vcpu_id, dist->irq_pending_on_cpu);
|
|
}
|
|
}
|
|
-
|
|
|
|
- for (lr = 0; lr < vgic->nr_lr; lr++) {
|
|
|
|
- struct vgic_lr vlr;
|
|
|
|
-
|
|
|
|
- if (!test_bit(lr, vgic_cpu->lr_used))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- vlr = vgic_get_lr(vcpu, lr);
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * If we have a mapping, and the virtual interrupt is
|
|
|
|
- * presented to the guest (as pending or active), then we must
|
|
|
|
- * set the state to active in the physical world. See
|
|
|
|
- * Documentation/virtual/kvm/arm/vgic-mapped-irqs.txt.
|
|
|
|
- */
|
|
|
|
- if (vlr.state & LR_HW) {
|
|
|
|
- struct irq_phys_map *map;
|
|
|
|
- map = vgic_irq_map_search(vcpu, vlr.irq);
|
|
|
|
-
|
|
|
|
- ret = irq_set_irqchip_state(map->irq,
|
|
|
|
- IRQCHIP_STATE_ACTIVE,
|
|
|
|
- true);
|
|
|
|
- WARN_ON(ret);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|
static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|
@@ -1421,7 +1437,7 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
map = vgic_irq_map_search(vcpu, vlr.irq);
|
|
map = vgic_irq_map_search(vcpu, vlr.irq);
|
|
- BUG_ON(!map || !map->active);
|
|
|
|
|
|
+ BUG_ON(!map);
|
|
|
|
|
|
ret = irq_get_irqchip_state(map->irq,
|
|
ret = irq_get_irqchip_state(map->irq,
|
|
IRQCHIP_STATE_ACTIVE,
|
|
IRQCHIP_STATE_ACTIVE,
|
|
@@ -1429,13 +1445,8 @@ static int vgic_sync_hwirq(struct kvm_vcpu *vcpu, struct vgic_lr vlr)
|
|
|
|
|
|
WARN_ON(ret);
|
|
WARN_ON(ret);
|
|
|
|
|
|
- if (map->active) {
|
|
|
|
- ret = irq_set_irqchip_state(map->irq,
|
|
|
|
- IRQCHIP_STATE_ACTIVE,
|
|
|
|
- false);
|
|
|
|
- WARN_ON(ret);
|
|
|
|
|
|
+ if (map->active)
|
|
return 0;
|
|
return 0;
|
|
- }
|
|
|
|
|
|
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
@@ -1607,8 +1618,12 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
|
|
} else {
|
|
} else {
|
|
if (level_triggered) {
|
|
if (level_triggered) {
|
|
vgic_dist_irq_clear_level(vcpu, irq_num);
|
|
vgic_dist_irq_clear_level(vcpu, irq_num);
|
|
- if (!vgic_dist_irq_soft_pend(vcpu, irq_num))
|
|
|
|
|
|
+ if (!vgic_dist_irq_soft_pend(vcpu, irq_num)) {
|
|
vgic_dist_irq_clear_pending(vcpu, irq_num);
|
|
vgic_dist_irq_clear_pending(vcpu, irq_num);
|
|
|
|
+ vgic_cpu_irq_clear(vcpu, irq_num);
|
|
|
|
+ if (!compute_pending_for_cpu(vcpu))
|
|
|
|
+ clear_bit(cpuid, dist->irq_pending_on_cpu);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
ret = false;
|
|
ret = false;
|