|
@@ -94,9 +94,12 @@ static struct device_node *vgic_node;
|
|
|
#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1))
|
|
|
|
|
|
static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu);
|
|
|
+static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu);
|
|
|
static void vgic_update_state(struct kvm *kvm);
|
|
|
static void vgic_kick_vcpus(struct kvm *kvm);
|
|
|
static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg);
|
|
|
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr);
|
|
|
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc);
|
|
|
static u32 vgic_nr_lr;
|
|
|
|
|
|
static unsigned int vgic_maint_irq;
|
|
@@ -593,18 +596,6 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-#define LR_CPUID(lr) \
|
|
|
- (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT)
|
|
|
-#define LR_IRQID(lr) \
|
|
|
- ((lr) & GICH_LR_VIRTUALID)
|
|
|
-
|
|
|
-static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu)
|
|
|
-{
|
|
|
- clear_bit(lr_nr, vgic_cpu->lr_used);
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr_nr] &= ~GICH_LR_STATE;
|
|
|
- vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* vgic_unqueue_irqs - move pending IRQs from LRs to the distributor
|
|
|
* @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs
|
|
@@ -622,13 +613,10 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
|
|
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
|
int vcpu_id = vcpu->vcpu_id;
|
|
|
- int i, irq, source_cpu;
|
|
|
- u32 *lr;
|
|
|
+ int i;
|
|
|
|
|
|
for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
|
|
- lr = &vgic_cpu->vgic_v2.vgic_lr[i];
|
|
|
- irq = LR_IRQID(*lr);
|
|
|
- source_cpu = LR_CPUID(*lr);
|
|
|
+ struct vgic_lr lr = vgic_get_lr(vcpu, i);
|
|
|
|
|
|
/*
|
|
|
* There are three options for the state bits:
|
|
@@ -640,7 +628,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
|
|
* If the LR holds only an active interrupt (not pending) then
|
|
|
* just leave it alone.
|
|
|
*/
|
|
|
- if ((*lr & GICH_LR_STATE) == GICH_LR_ACTIVE_BIT)
|
|
|
+ if ((lr.state & LR_STATE_MASK) == LR_STATE_ACTIVE)
|
|
|
continue;
|
|
|
|
|
|
/*
|
|
@@ -649,18 +637,19 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu)
|
|
|
* is fine, then we are only setting a few bits that were
|
|
|
* already set.
|
|
|
*/
|
|
|
- vgic_dist_irq_set(vcpu, irq);
|
|
|
- if (irq < VGIC_NR_SGIS)
|
|
|
- dist->irq_sgi_sources[vcpu_id][irq] |= 1 << source_cpu;
|
|
|
- *lr &= ~GICH_LR_PENDING_BIT;
|
|
|
+ vgic_dist_irq_set(vcpu, lr.irq);
|
|
|
+ if (lr.irq < VGIC_NR_SGIS)
|
|
|
+ dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source;
|
|
|
+ lr.state &= ~LR_STATE_PENDING;
|
|
|
+ vgic_set_lr(vcpu, i, lr);
|
|
|
|
|
|
/*
|
|
|
* If there's no state left on the LR (it could still be
|
|
|
* active), then the LR does not hold any useful info and can
|
|
|
* be marked as free for other use.
|
|
|
*/
|
|
|
- if (!(*lr & GICH_LR_STATE))
|
|
|
- vgic_retire_lr(i, irq, vgic_cpu);
|
|
|
+ if (!(lr.state & LR_STATE_MASK))
|
|
|
+ vgic_retire_lr(i, lr.irq, vcpu);
|
|
|
|
|
|
/* Finally update the VGIC state. */
|
|
|
vgic_update_state(vcpu->kvm);
|
|
@@ -989,8 +978,69 @@ static void vgic_update_state(struct kvm *kvm)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#define MK_LR_PEND(src, irq) \
|
|
|
- (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq))
|
|
|
+static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
|
|
+{
|
|
|
+ struct vgic_lr lr_desc;
|
|
|
+ u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr];
|
|
|
+
|
|
|
+ lr_desc.irq = val & GICH_LR_VIRTUALID;
|
|
|
+ if (lr_desc.irq <= 15)
|
|
|
+ lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7;
|
|
|
+ else
|
|
|
+ lr_desc.source = 0;
|
|
|
+ lr_desc.state = 0;
|
|
|
+
|
|
|
+ if (val & GICH_LR_PENDING_BIT)
|
|
|
+ lr_desc.state |= LR_STATE_PENDING;
|
|
|
+ if (val & GICH_LR_ACTIVE_BIT)
|
|
|
+ lr_desc.state |= LR_STATE_ACTIVE;
|
|
|
+ if (val & GICH_LR_EOI)
|
|
|
+ lr_desc.state |= LR_EOI_INT;
|
|
|
+
|
|
|
+ return lr_desc;
|
|
|
+}
|
|
|
+
|
|
|
+static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr,
|
|
|
+ struct vgic_lr lr_desc)
|
|
|
+{
|
|
|
+ u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq;
|
|
|
+
|
|
|
+ if (lr_desc.state & LR_STATE_PENDING)
|
|
|
+ lr_val |= GICH_LR_PENDING_BIT;
|
|
|
+ if (lr_desc.state & LR_STATE_ACTIVE)
|
|
|
+ lr_val |= GICH_LR_ACTIVE_BIT;
|
|
|
+ if (lr_desc.state & LR_EOI_INT)
|
|
|
+ lr_val |= GICH_LR_EOI;
|
|
|
+
|
|
|
+ vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct vgic_ops vgic_ops = {
|
|
|
+ .get_lr = vgic_v2_get_lr,
|
|
|
+ .set_lr = vgic_v2_set_lr,
|
|
|
+};
|
|
|
+
|
|
|
+static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr)
|
|
|
+{
|
|
|
+ return vgic_ops.get_lr(vcpu, lr);
|
|
|
+}
|
|
|
+
|
|
|
+static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr,
|
|
|
+ struct vgic_lr vlr)
|
|
|
+{
|
|
|
+ vgic_ops.set_lr(vcpu, lr, vlr);
|
|
|
+}
|
|
|
+
|
|
|
+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_lr vlr = vgic_get_lr(vcpu, lr_nr);
|
|
|
+
|
|
|
+ vlr.state = 0;
|
|
|
+ vgic_set_lr(vcpu, lr_nr, vlr);
|
|
|
+ clear_bit(lr_nr, vgic_cpu->lr_used);
|
|
|
+ vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* An interrupt may have been disabled after being made pending on the
|
|
@@ -1007,12 +1057,12 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
|
|
|
int lr;
|
|
|
|
|
|
for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) {
|
|
|
- int irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
|
|
|
+ struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
|
|
|
|
|
- if (!vgic_irq_is_enabled(vcpu, irq)) {
|
|
|
- vgic_retire_lr(lr, irq, vgic_cpu);
|
|
|
- if (vgic_irq_is_active(vcpu, irq))
|
|
|
- vgic_irq_clear_active(vcpu, irq);
|
|
|
+ if (!vgic_irq_is_enabled(vcpu, vlr.irq)) {
|
|
|
+ vgic_retire_lr(lr, vlr.irq, vcpu);
|
|
|
+ if (vgic_irq_is_active(vcpu, vlr.irq))
|
|
|
+ vgic_irq_clear_active(vcpu, vlr.irq);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1024,6 +1074,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu)
|
|
|
static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
|
|
{
|
|
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
|
|
+ struct vgic_lr vlr;
|
|
|
int lr;
|
|
|
|
|
|
/* Sanitize the input... */
|
|
@@ -1036,13 +1087,15 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
|
|
lr = vgic_cpu->vgic_irq_lr_map[irq];
|
|
|
|
|
|
/* Do we have an active interrupt for the same CPUID? */
|
|
|
- if (lr != LR_EMPTY &&
|
|
|
- (LR_CPUID(vgic_cpu->vgic_v2.vgic_lr[lr]) == sgi_source_id)) {
|
|
|
- kvm_debug("LR%d piggyback for IRQ%d %x\n",
|
|
|
- lr, irq, vgic_cpu->vgic_v2.vgic_lr[lr]);
|
|
|
- BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_PENDING_BIT;
|
|
|
- return true;
|
|
|
+ if (lr != LR_EMPTY) {
|
|
|
+ vlr = vgic_get_lr(vcpu, lr);
|
|
|
+ if (vlr.source == sgi_source_id) {
|
|
|
+ kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq);
|
|
|
+ BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
|
|
|
+ vlr.state |= LR_STATE_PENDING;
|
|
|
+ vgic_set_lr(vcpu, lr, vlr);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* Try to use another LR for this interrupt */
|
|
@@ -1052,12 +1105,16 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq)
|
|
|
return false;
|
|
|
|
|
|
kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id);
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq);
|
|
|
vgic_cpu->vgic_irq_lr_map[irq] = lr;
|
|
|
set_bit(lr, vgic_cpu->lr_used);
|
|
|
|
|
|
+ vlr.irq = irq;
|
|
|
+ vlr.source = sgi_source_id;
|
|
|
+ vlr.state = LR_STATE_PENDING;
|
|
|
if (!vgic_irq_is_edge(vcpu, irq))
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_EOI;
|
|
|
+ vlr.state |= LR_EOI_INT;
|
|
|
+
|
|
|
+ vgic_set_lr(vcpu, lr, vlr);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -1180,21 +1237,23 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|
|
* Some level interrupts have been EOIed. Clear their
|
|
|
* active bit.
|
|
|
*/
|
|
|
- int lr, irq;
|
|
|
+ int lr;
|
|
|
|
|
|
for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_eisr,
|
|
|
vgic_cpu->nr_lr) {
|
|
|
- irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
|
|
|
+ struct vgic_lr vlr = vgic_get_lr(vcpu, lr);
|
|
|
|
|
|
- vgic_irq_clear_active(vcpu, irq);
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_EOI;
|
|
|
+ vgic_irq_clear_active(vcpu, vlr.irq);
|
|
|
+ WARN_ON(vlr.state & LR_STATE_MASK);
|
|
|
+ vlr.state = 0;
|
|
|
+ vgic_set_lr(vcpu, lr, vlr);
|
|
|
|
|
|
/* Any additional pending interrupt? */
|
|
|
- if (vgic_dist_irq_is_pending(vcpu, irq)) {
|
|
|
- vgic_cpu_irq_set(vcpu, irq);
|
|
|
+ if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) {
|
|
|
+ vgic_cpu_irq_set(vcpu, vlr.irq);
|
|
|
level_pending = true;
|
|
|
} else {
|
|
|
- vgic_cpu_irq_clear(vcpu, irq);
|
|
|
+ vgic_cpu_irq_clear(vcpu, vlr.irq);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1202,7 +1261,6 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
|
|
|
* been marked as empty.
|
|
|
*/
|
|
|
set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr);
|
|
|
- vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1228,15 +1286,15 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu)
|
|
|
/* Clear mappings for empty LRs */
|
|
|
for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr,
|
|
|
vgic_cpu->nr_lr) {
|
|
|
- int irq;
|
|
|
+ struct vgic_lr vlr;
|
|
|
|
|
|
if (!test_and_clear_bit(lr, vgic_cpu->lr_used))
|
|
|
continue;
|
|
|
|
|
|
- irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID;
|
|
|
+ vlr = vgic_get_lr(vcpu, lr);
|
|
|
|
|
|
- BUG_ON(irq >= VGIC_NR_IRQS);
|
|
|
- vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
|
|
|
+ BUG_ON(vlr.irq >= VGIC_NR_IRQS);
|
|
|
+ vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY;
|
|
|
}
|
|
|
|
|
|
/* Check if we still have something up our sleeve... */
|