summaryrefslogtreecommitdiff
path: root/arch/powerpc/kvm/mpic.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kvm/mpic.c')
-rw-r--r--arch/powerpc/kvm/mpic.c111
1 files changed, 110 insertions, 1 deletions
diff --git a/arch/powerpc/kvm/mpic.c b/arch/powerpc/kvm/mpic.c
index 10bc08a246fd..89fe1d66a7fb 100644
--- a/arch/powerpc/kvm/mpic.c
+++ b/arch/powerpc/kvm/mpic.c
@@ -1076,7 +1076,9 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
case 0xA0: /* IACK */
/* Read-only register */
break;
- case 0xB0: /* EOI */
+ case 0xB0: { /* EOI */
+ int notify_eoi;
+
pr_debug("EOI\n");
s_IRQ = IRQ_get_next(opp, &dst->servicing);
@@ -1087,6 +1089,8 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
}
IRQ_resetbit(&dst->servicing, s_IRQ);
+ /* Notify listeners that the IRQ is over */
+ notify_eoi = s_IRQ;
/* Set up next servicing IRQ */
s_IRQ = IRQ_get_next(opp, &dst->servicing);
/* Check queued interrupts. */
@@ -1099,7 +1103,13 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
idx, n_IRQ);
mpic_irq_raise(opp, dst, ILR_INTTGT_INT);
}
+
+ spin_unlock(&opp->lock);
+ kvm_notify_acked_irq(opp->kvm, 0, notify_eoi);
+ spin_lock(&opp->lock);
+
break;
+ }
default:
break;
}
@@ -1639,14 +1649,34 @@ static void mpic_destroy(struct kvm_device *dev)
unmap_mmio(opp);
}
+ dev->kvm->arch.mpic = NULL;
kfree(opp);
}
+static int mpic_set_default_irq_routing(struct openpic *opp)
+{
+ struct kvm_irq_routing_entry *routing;
+
+ /* Create a nop default map, so that dereferencing it still works */
+ routing = kzalloc((sizeof(*routing)), GFP_KERNEL);
+ if (!routing)
+ return -ENOMEM;
+
+ kvm_set_irq_routing(opp->kvm, routing, 0, 0);
+
+ kfree(routing);
+ return 0;
+}
+
static int mpic_create(struct kvm_device *dev, u32 type)
{
struct openpic *opp;
int ret;
+ /* We only support one MPIC at a time for now */
+ if (dev->kvm->arch.mpic)
+ return -EINVAL;
+
opp = kzalloc(sizeof(struct openpic), GFP_KERNEL);
if (!opp)
return -ENOMEM;
@@ -1691,7 +1721,15 @@ static int mpic_create(struct kvm_device *dev, u32 type)
goto err;
}
+ ret = mpic_set_default_irq_routing(opp);
+ if (ret)
+ goto err;
+
openpic_reset(opp);
+
+ smp_wmb();
+ dev->kvm->arch.mpic = opp;
+
return 0;
err:
@@ -1761,3 +1799,74 @@ void kvmppc_mpic_disconnect_vcpu(struct openpic *opp, struct kvm_vcpu *vcpu)
opp->dst[vcpu->arch.irq_cpu_id].vcpu = NULL;
kvm_device_put(opp->dev);
}
+
+/*
+ * Return value:
+ * < 0 Interrupt was ignored (masked or not delivered for other reasons)
+ * = 0 Interrupt was coalesced (previous irq is still pending)
+ * > 0 Number of CPUs interrupt was delivered to
+ */
+static int mpic_set_irq(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm, int irq_source_id, int level,
+ bool line_status)
+{
+ u32 irq = e->irqchip.pin;
+ struct openpic *opp = kvm->arch.mpic;
+ unsigned long flags;
+
+ spin_lock_irqsave(&opp->lock, flags);
+ openpic_set_irq(opp, irq, level);
+ spin_unlock_irqrestore(&opp->lock, flags);
+
+ /* All code paths we care about don't check for the return value */
+ return 0;
+}
+
+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm, int irq_source_id, int level, bool line_status)
+{
+ struct openpic *opp = kvm->arch.mpic;
+ unsigned long flags;
+
+ spin_lock_irqsave(&opp->lock, flags);
+
+ /*
+ * XXX We ignore the target address for now, as we only support
+ * a single MSI bank.
+ */
+ openpic_msi_write(kvm->arch.mpic, MSIIR_OFFSET, e->msi.data);
+ spin_unlock_irqrestore(&opp->lock, flags);
+
+ /* All code paths we care about don't check for the return value */
+ return 0;
+}
+
+int kvm_set_routing_entry(struct kvm_irq_routing_table *rt,
+ struct kvm_kernel_irq_routing_entry *e,
+ const struct kvm_irq_routing_entry *ue)
+{
+ int r = -EINVAL;
+
+ switch (ue->type) {
+ case KVM_IRQ_ROUTING_IRQCHIP:
+ e->set = mpic_set_irq;
+ e->irqchip.irqchip = ue->u.irqchip.irqchip;
+ e->irqchip.pin = ue->u.irqchip.pin;
+ if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS)
+ goto out;
+ rt->chip[ue->u.irqchip.irqchip][e->irqchip.pin] = ue->gsi;
+ break;
+ case KVM_IRQ_ROUTING_MSI:
+ e->set = kvm_set_msi;
+ e->msi.address_lo = ue->u.msi.address_lo;
+ e->msi.address_hi = ue->u.msi.address_hi;
+ e->msi.data = ue->u.msi.data;
+ break;
+ default:
+ goto out;
+ }
+
+ r = 0;
+out:
+ return r;
+}