summary refs log tree commit diff stats
path: root/hw/ppc/spapr_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc/spapr_irq.c')
-rw-r--r--hw/ppc/spapr_irq.c611
1 files changed, 216 insertions, 395 deletions
diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c
index 457eabe24c..b941608b69 100644
--- a/hw/ppc/spapr_irq.c
+++ b/hw/ppc/spapr_irq.c
@@ -23,9 +23,20 @@
 
 #include "trace.h"
 
-void spapr_irq_msi_init(SpaprMachineState *spapr, uint32_t nr_msis)
+static const TypeInfo spapr_intc_info = {
+    .name = TYPE_SPAPR_INTC,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(SpaprInterruptControllerClass),
+};
+
+static void spapr_irq_msi_init(SpaprMachineState *spapr)
 {
-    spapr->irq_map_nr = nr_msis;
+    if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
+        /* Legacy mode doesn't use this allocator */
+        return;
+    }
+
+    spapr->irq_map_nr = spapr_irq_nr_msis(spapr);
     spapr->irq_map = bitmap_new(spapr->irq_map_nr);
 }
 
@@ -59,262 +70,53 @@ void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num)
     bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num);
 }
 
-static void spapr_irq_init_kvm(SpaprMachineState *spapr,
-                                  SpaprIrq *irq, Error **errp)
+int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **),
+                       SpaprInterruptController *intc,
+                       Error **errp)
 {
-    MachineState *machine = MACHINE(spapr);
+    MachineState *machine = MACHINE(qdev_get_machine());
     Error *local_err = NULL;
 
     if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) {
-        irq->init_kvm(spapr, &local_err);
-        if (local_err && machine_kernel_irqchip_required(machine)) {
-            error_prepend(&local_err,
-                          "kernel_irqchip requested but unavailable: ");
-            error_propagate(errp, local_err);
-            return;
-        }
+        if (fn(intc, &local_err) < 0) {
+            if (machine_kernel_irqchip_required(machine)) {
+                error_prepend(&local_err,
+                              "kernel_irqchip requested but unavailable: ");
+                error_propagate(errp, local_err);
+                return -1;
+            }
 
-        if (!local_err) {
-            return;
+            /*
+             * We failed to initialize the KVM device, fallback to
+             * emulated mode
+             */
+            error_prepend(&local_err,
+                          "kernel_irqchip allowed but unavailable: ");
+            error_append_hint(&local_err,
+                              "Falling back to kernel-irqchip=off\n");
+            warn_report_err(local_err);
         }
-
-        /*
-         * We failed to initialize the KVM device, fallback to
-         * emulated mode
-         */
-        error_prepend(&local_err, "kernel_irqchip allowed but unavailable: ");
-        error_append_hint(&local_err, "Falling back to kernel-irqchip=off\n");
-        warn_report_err(local_err);
     }
+
+    return 0;
 }
 
 /*
  * XICS IRQ backend.
  */
 
-static int spapr_irq_claim_xics(SpaprMachineState *spapr, int irq, bool lsi,
-                                Error **errp)
-{
-    ICSState *ics = spapr->ics;
-
-    assert(ics);
-    assert(ics_valid_irq(ics, irq));
-
-    if (!ics_irq_free(ics, irq - ics->offset)) {
-        error_setg(errp, "IRQ %d is not free", irq);
-        return -1;
-    }
-
-    ics_set_irq_type(ics, irq - ics->offset, lsi);
-    return 0;
-}
-
-static void spapr_irq_free_xics(SpaprMachineState *spapr, int irq)
-{
-    ICSState *ics = spapr->ics;
-    uint32_t srcno = irq - ics->offset;
-
-    assert(ics_valid_irq(ics, irq));
-
-    memset(&ics->irqs[srcno], 0, sizeof(ICSIRQState));
-}
-
-static void spapr_irq_print_info_xics(SpaprMachineState *spapr, Monitor *mon)
-{
-    CPUState *cs;
-
-    CPU_FOREACH(cs) {
-        PowerPCCPU *cpu = POWERPC_CPU(cs);
-
-        icp_pic_print_info(spapr_cpu_state(cpu)->icp, mon);
-    }
-
-    ics_pic_print_info(spapr->ics, mon);
-}
-
-static void spapr_irq_cpu_intc_create_xics(SpaprMachineState *spapr,
-                                           PowerPCCPU *cpu, Error **errp)
-{
-    Error *local_err = NULL;
-    Object *obj;
-    SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
-
-    obj = icp_create(OBJECT(cpu), TYPE_ICP, XICS_FABRIC(spapr),
-                     &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    spapr_cpu->icp = ICP(obj);
-}
-
-static int spapr_irq_post_load_xics(SpaprMachineState *spapr, int version_id)
-{
-    if (!kvm_irqchip_in_kernel()) {
-        CPUState *cs;
-        CPU_FOREACH(cs) {
-            PowerPCCPU *cpu = POWERPC_CPU(cs);
-            icp_resend(spapr_cpu_state(cpu)->icp);
-        }
-    }
-    return 0;
-}
-
-static void spapr_irq_set_irq_xics(void *opaque, int irq, int val)
-{
-    SpaprMachineState *spapr = opaque;
-    uint32_t srcno = irq - spapr->ics->offset;
-
-    ics_set_irq(spapr->ics, srcno, val);
-}
-
-static void spapr_irq_reset_xics(SpaprMachineState *spapr, Error **errp)
-{
-    Error *local_err = NULL;
-
-    spapr_irq_init_kvm(spapr, &spapr_irq_xics, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-}
-
-static void spapr_irq_init_kvm_xics(SpaprMachineState *spapr, Error **errp)
-{
-    if (kvm_enabled()) {
-        xics_kvm_connect(spapr, errp);
-    }
-}
-
 SpaprIrq spapr_irq_xics = {
-    .nr_xirqs    = SPAPR_NR_XIRQS,
-    .nr_msis     = SPAPR_NR_MSIS,
     .xics        = true,
     .xive        = false,
-
-    .claim       = spapr_irq_claim_xics,
-    .free        = spapr_irq_free_xics,
-    .print_info  = spapr_irq_print_info_xics,
-    .dt_populate = spapr_dt_xics,
-    .cpu_intc_create = spapr_irq_cpu_intc_create_xics,
-    .post_load   = spapr_irq_post_load_xics,
-    .reset       = spapr_irq_reset_xics,
-    .set_irq     = spapr_irq_set_irq_xics,
-    .init_kvm    = spapr_irq_init_kvm_xics,
 };
 
 /*
  * XIVE IRQ backend.
  */
 
-static int spapr_irq_claim_xive(SpaprMachineState *spapr, int irq, bool lsi,
-                                Error **errp)
-{
-    return spapr_xive_irq_claim(spapr->xive, irq, lsi, errp);
-}
-
-static void spapr_irq_free_xive(SpaprMachineState *spapr, int irq)
-{
-    spapr_xive_irq_free(spapr->xive, irq);
-}
-
-static void spapr_irq_print_info_xive(SpaprMachineState *spapr,
-                                      Monitor *mon)
-{
-    CPUState *cs;
-
-    CPU_FOREACH(cs) {
-        PowerPCCPU *cpu = POWERPC_CPU(cs);
-
-        xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, mon);
-    }
-
-    spapr_xive_pic_print_info(spapr->xive, mon);
-}
-
-static void spapr_irq_cpu_intc_create_xive(SpaprMachineState *spapr,
-                                           PowerPCCPU *cpu, Error **errp)
-{
-    Error *local_err = NULL;
-    Object *obj;
-    SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
-
-    obj = xive_tctx_create(OBJECT(cpu), XIVE_ROUTER(spapr->xive), &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    spapr_cpu->tctx = XIVE_TCTX(obj);
-
-    /*
-     * (TCG) Early setting the OS CAM line for hotplugged CPUs as they
-     * don't beneficiate from the reset of the XIVE IRQ backend
-     */
-    spapr_xive_set_tctx_os_cam(spapr_cpu->tctx);
-}
-
-static int spapr_irq_post_load_xive(SpaprMachineState *spapr, int version_id)
-{
-    return spapr_xive_post_load(spapr->xive, version_id);
-}
-
-static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp)
-{
-    CPUState *cs;
-    Error *local_err = NULL;
-
-    CPU_FOREACH(cs) {
-        PowerPCCPU *cpu = POWERPC_CPU(cs);
-
-        /* (TCG) Set the OS CAM line of the thread interrupt context. */
-        spapr_xive_set_tctx_os_cam(spapr_cpu_state(cpu)->tctx);
-    }
-
-    spapr_irq_init_kvm(spapr, &spapr_irq_xive, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    /* Activate the XIVE MMIOs */
-    spapr_xive_mmio_set_enabled(spapr->xive, true);
-}
-
-static void spapr_irq_set_irq_xive(void *opaque, int irq, int val)
-{
-    SpaprMachineState *spapr = opaque;
-
-    if (kvm_irqchip_in_kernel()) {
-        kvmppc_xive_source_set_irq(&spapr->xive->source, irq, val);
-    } else {
-        xive_source_set_irq(&spapr->xive->source, irq, val);
-    }
-}
-
-static void spapr_irq_init_kvm_xive(SpaprMachineState *spapr, Error **errp)
-{
-    if (kvm_enabled()) {
-        kvmppc_xive_connect(spapr->xive, errp);
-    }
-}
-
 SpaprIrq spapr_irq_xive = {
-    .nr_xirqs    = SPAPR_NR_XIRQS,
-    .nr_msis     = SPAPR_NR_MSIS,
     .xics        = false,
     .xive        = true,
-
-    .claim       = spapr_irq_claim_xive,
-    .free        = spapr_irq_free_xive,
-    .print_info  = spapr_irq_print_info_xive,
-    .dt_populate = spapr_dt_xive,
-    .cpu_intc_create = spapr_irq_cpu_intc_create_xive,
-    .post_load   = spapr_irq_post_load_xive,
-    .reset       = spapr_irq_reset_xive,
-    .set_irq     = spapr_irq_set_irq_xive,
-    .init_kvm    = spapr_irq_init_kvm_xive,
 };
 
 /*
@@ -327,138 +129,11 @@ SpaprIrq spapr_irq_xive = {
  */
 
 /*
- * Returns the sPAPR IRQ backend negotiated by CAS. XICS is the
- * default.
- */
-static SpaprIrq *spapr_irq_current(SpaprMachineState *spapr)
-{
-    return spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT) ?
-        &spapr_irq_xive : &spapr_irq_xics;
-}
-
-static int spapr_irq_claim_dual(SpaprMachineState *spapr, int irq, bool lsi,
-                                Error **errp)
-{
-    Error *local_err = NULL;
-    int ret;
-
-    ret = spapr_irq_xics.claim(spapr, irq, lsi, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return ret;
-    }
-
-    ret = spapr_irq_xive.claim(spapr, irq, lsi, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return ret;
-    }
-
-    return ret;
-}
-
-static void spapr_irq_free_dual(SpaprMachineState *spapr, int irq)
-{
-    spapr_irq_xics.free(spapr, irq);
-    spapr_irq_xive.free(spapr, irq);
-}
-
-static void spapr_irq_print_info_dual(SpaprMachineState *spapr, Monitor *mon)
-{
-    spapr_irq_current(spapr)->print_info(spapr, mon);
-}
-
-static void spapr_irq_dt_populate_dual(SpaprMachineState *spapr,
-                                       uint32_t nr_servers, void *fdt,
-                                       uint32_t phandle)
-{
-    spapr_irq_current(spapr)->dt_populate(spapr, nr_servers, fdt, phandle);
-}
-
-static void spapr_irq_cpu_intc_create_dual(SpaprMachineState *spapr,
-                                           PowerPCCPU *cpu, Error **errp)
-{
-    Error *local_err = NULL;
-
-    spapr_irq_xive.cpu_intc_create(spapr, cpu, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    spapr_irq_xics.cpu_intc_create(spapr, cpu, errp);
-}
-
-static int spapr_irq_post_load_dual(SpaprMachineState *spapr, int version_id)
-{
-    /*
-     * Force a reset of the XIVE backend after migration. The machine
-     * defaults to XICS at startup.
-     */
-    if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) {
-        if (kvm_irqchip_in_kernel()) {
-            xics_kvm_disconnect(spapr, &error_fatal);
-        }
-        spapr_irq_xive.reset(spapr, &error_fatal);
-    }
-
-    return spapr_irq_current(spapr)->post_load(spapr, version_id);
-}
-
-static void spapr_irq_reset_dual(SpaprMachineState *spapr, Error **errp)
-{
-    Error *local_err = NULL;
-
-    /*
-     * Deactivate the XIVE MMIOs. The XIVE backend will reenable them
-     * if selected.
-     */
-    spapr_xive_mmio_set_enabled(spapr->xive, false);
-
-    /* Destroy all KVM devices */
-    if (kvm_irqchip_in_kernel()) {
-        xics_kvm_disconnect(spapr, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            error_prepend(errp, "KVM XICS disconnect failed: ");
-            return;
-        }
-        kvmppc_xive_disconnect(spapr->xive, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            error_prepend(errp, "KVM XIVE disconnect failed: ");
-            return;
-        }
-    }
-
-    spapr_irq_current(spapr)->reset(spapr, errp);
-}
-
-static void spapr_irq_set_irq_dual(void *opaque, int irq, int val)
-{
-    SpaprMachineState *spapr = opaque;
-
-    spapr_irq_current(spapr)->set_irq(spapr, irq, val);
-}
-
-/*
  * Define values in sync with the XIVE and XICS backend
  */
 SpaprIrq spapr_irq_dual = {
-    .nr_xirqs    = SPAPR_NR_XIRQS,
-    .nr_msis     = SPAPR_NR_MSIS,
     .xics        = true,
     .xive        = true,
-
-    .claim       = spapr_irq_claim_dual,
-    .free        = spapr_irq_free_dual,
-    .print_info  = spapr_irq_print_info_dual,
-    .dt_populate = spapr_irq_dt_populate_dual,
-    .cpu_intc_create = spapr_irq_cpu_intc_create_dual,
-    .post_load   = spapr_irq_post_load_dual,
-    .reset       = spapr_irq_reset_dual,
-    .set_irq     = spapr_irq_set_irq_dual,
-    .init_kvm    = NULL, /* should not be used */
 };
 
 
@@ -521,9 +196,85 @@ static int spapr_irq_check(SpaprMachineState *spapr, Error **errp)
 /*
  * sPAPR IRQ frontend routines for devices
  */
+#define ALL_INTCS(spapr_) \
+    { SPAPR_INTC((spapr_)->ics), SPAPR_INTC((spapr_)->xive), }
+
+int spapr_irq_cpu_intc_create(SpaprMachineState *spapr,
+                              PowerPCCPU *cpu, Error **errp)
+{
+    SpaprInterruptController *intcs[] = ALL_INTCS(spapr);
+    int i;
+    int rc;
+
+    for (i = 0; i < ARRAY_SIZE(intcs); i++) {
+        SpaprInterruptController *intc = intcs[i];
+        if (intc) {
+            SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc);
+            rc = sicc->cpu_intc_create(intc, cpu, errp);
+            if (rc < 0) {
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+void spapr_irq_cpu_intc_reset(SpaprMachineState *spapr, PowerPCCPU *cpu)
+{
+    SpaprInterruptController *intcs[] = ALL_INTCS(spapr);
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(intcs); i++) {
+        SpaprInterruptController *intc = intcs[i];
+        if (intc) {
+            SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc);
+            sicc->cpu_intc_reset(intc, cpu);
+        }
+    }
+}
+
+static void spapr_set_irq(void *opaque, int irq, int level)
+{
+    SpaprMachineState *spapr = SPAPR_MACHINE(opaque);
+    SpaprInterruptControllerClass *sicc
+        = SPAPR_INTC_GET_CLASS(spapr->active_intc);
+
+    sicc->set_irq(spapr->active_intc, irq, level);
+}
+
+void spapr_irq_print_info(SpaprMachineState *spapr, Monitor *mon)
+{
+    SpaprInterruptControllerClass *sicc
+        = SPAPR_INTC_GET_CLASS(spapr->active_intc);
+
+    sicc->print_info(spapr->active_intc, mon);
+}
+
+void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers,
+                  void *fdt, uint32_t phandle)
+{
+    SpaprInterruptControllerClass *sicc
+        = SPAPR_INTC_GET_CLASS(spapr->active_intc);
+
+    sicc->dt(spapr->active_intc, nr_servers, fdt, phandle);
+}
+
+uint32_t spapr_irq_nr_msis(SpaprMachineState *spapr)
+{
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+
+    if (smc->legacy_irq_allocation) {
+        return smc->nr_xirqs;
+    } else {
+        return SPAPR_XIRQ_BASE + smc->nr_xirqs - SPAPR_IRQ_MSI;
+    }
+}
+
 void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
 {
     MachineState *machine = MACHINE(spapr);
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
 
     if (machine_kernel_irqchip_split(machine)) {
         error_setg(errp, "kernel_irqchip split mode not supported on pseries");
@@ -541,9 +292,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
     }
 
     /* Initialize the MSI IRQ allocator. */
-    if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) {
-        spapr_irq_msi_init(spapr, spapr->irq->nr_msis);
-    }
+    spapr_irq_msi_init(spapr);
 
     if (spapr->irq->xics) {
         Error *local_err = NULL;
@@ -563,8 +312,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
             return;
         }
 
-        object_property_set_int(obj, spapr->irq->nr_xirqs, "nr-irqs",
-                                &local_err);
+        object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             return;
@@ -585,8 +333,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
         int i;
 
         dev = qdev_create(NULL, TYPE_SPAPR_XIVE);
-        qdev_prop_set_uint32(dev, "nr-irqs",
-                             spapr->irq->nr_xirqs + SPAPR_XIRQ_BASE);
+        qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_XIRQ_BASE);
         /*
          * 8 XIVE END structures per CPU. One for each available
          * priority
@@ -598,8 +345,11 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
 
         /* Enable the CPU IPIs */
         for (i = 0; i < nr_servers; ++i) {
-            if (spapr_xive_irq_claim(spapr->xive, SPAPR_IRQ_IPI + i,
-                                     false, errp) < 0) {
+            SpaprInterruptControllerClass *sicc
+                = SPAPR_INTC_GET_CLASS(spapr->xive);
+
+            if (sicc->claim_irq(SPAPR_INTC(spapr->xive), SPAPR_IRQ_IPI + i,
+                                false, errp) < 0) {
                 return;
             }
         }
@@ -607,32 +357,60 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
         spapr_xive_hcall_init(spapr);
     }
 
-    spapr->qirqs = qemu_allocate_irqs(spapr->irq->set_irq, spapr,
-                                      spapr->irq->nr_xirqs + SPAPR_XIRQ_BASE);
+    spapr->qirqs = qemu_allocate_irqs(spapr_set_irq, spapr,
+                                      smc->nr_xirqs + SPAPR_XIRQ_BASE);
 }
 
 int spapr_irq_claim(SpaprMachineState *spapr, int irq, bool lsi, Error **errp)
 {
+    SpaprInterruptController *intcs[] = ALL_INTCS(spapr);
+    int i;
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+    int rc;
+
     assert(irq >= SPAPR_XIRQ_BASE);
-    assert(irq < (spapr->irq->nr_xirqs + SPAPR_XIRQ_BASE));
+    assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE));
+
+    for (i = 0; i < ARRAY_SIZE(intcs); i++) {
+        SpaprInterruptController *intc = intcs[i];
+        if (intc) {
+            SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(intc);
+            rc = sicc->claim_irq(intc, irq, lsi, errp);
+            if (rc < 0) {
+                return rc;
+            }
+        }
+    }
 
-    return spapr->irq->claim(spapr, irq, lsi, errp);
+    return 0;
 }
 
 void spapr_irq_free(SpaprMachineState *spapr, int irq, int num)
 {
-    int i;
+    SpaprInterruptController *intcs[] = ALL_INTCS(spapr);
+    int i, j;
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
 
     assert(irq >= SPAPR_XIRQ_BASE);
-    assert((irq + num) <= (spapr->irq->nr_xirqs + SPAPR_XIRQ_BASE));
+    assert((irq + num) <= (smc->nr_xirqs + SPAPR_XIRQ_BASE));
 
     for (i = irq; i < (irq + num); i++) {
-        spapr->irq->free(spapr, i);
+        for (j = 0; j < ARRAY_SIZE(intcs); j++) {
+            SpaprInterruptController *intc = intcs[j];
+
+            if (intc) {
+                SpaprInterruptControllerClass *sicc
+                    = SPAPR_INTC_GET_CLASS(intc);
+                sicc->free_irq(intc, i);
+            }
+        }
     }
 }
 
 qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq)
 {
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+
     /*
      * This interface is basically for VIO and PHB devices to find the
      * right qemu_irq to manipulate, so we only allow access to the
@@ -641,7 +419,7 @@ qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq)
      * interfaces, we can change this if we need to in future.
      */
     assert(irq >= SPAPR_XIRQ_BASE);
-    assert(irq < (spapr->irq->nr_xirqs + SPAPR_XIRQ_BASE));
+    assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE));
 
     if (spapr->ics) {
         assert(ics_valid_irq(spapr->ics, irq));
@@ -656,16 +434,18 @@ qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq)
 
 int spapr_irq_post_load(SpaprMachineState *spapr, int version_id)
 {
-    return spapr->irq->post_load(spapr, version_id);
+    SpaprInterruptControllerClass *sicc;
+
+    spapr_irq_update_active_intc(spapr);
+    sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc);
+    return sicc->post_load(spapr->active_intc, version_id);
 }
 
 void spapr_irq_reset(SpaprMachineState *spapr, Error **errp)
 {
     assert(!spapr->irq_map || bitmap_empty(spapr->irq_map, spapr->irq_map_nr));
 
-    if (spapr->irq->reset) {
-        spapr->irq->reset(spapr, errp);
-    }
+    spapr_irq_update_active_intc(spapr);
 }
 
 int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp)
@@ -689,6 +469,54 @@ int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp)
     return phandle;
 }
 
+static void set_active_intc(SpaprMachineState *spapr,
+                            SpaprInterruptController *new_intc)
+{
+    SpaprInterruptControllerClass *sicc;
+
+    assert(new_intc);
+
+    if (new_intc == spapr->active_intc) {
+        /* Nothing to do */
+        return;
+    }
+
+    if (spapr->active_intc) {
+        sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc);
+        if (sicc->deactivate) {
+            sicc->deactivate(spapr->active_intc);
+        }
+    }
+
+    sicc = SPAPR_INTC_GET_CLASS(new_intc);
+    if (sicc->activate) {
+        sicc->activate(new_intc, &error_fatal);
+    }
+
+    spapr->active_intc = new_intc;
+}
+
+void spapr_irq_update_active_intc(SpaprMachineState *spapr)
+{
+    SpaprInterruptController *new_intc;
+
+    if (!spapr->ics) {
+        /*
+         * XXX before we run CAS, ov5_cas is initialized empty, which
+         * indicates XICS, even if we have ic-mode=xive.  TODO: clean
+         * up the CAS path so that we have a clearer way of handling
+         * this.
+         */
+        new_intc = SPAPR_INTC(spapr->xive);
+    } else if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) {
+        new_intc = SPAPR_INTC(spapr->xive);
+    } else {
+        new_intc = SPAPR_INTC(spapr->ics);
+    }
+
+    set_active_intc(spapr, new_intc);
+}
+
 /*
  * XICS legacy routines - to deprecate one day
  */
@@ -744,21 +572,14 @@ int spapr_irq_find(SpaprMachineState *spapr, int num, bool align, Error **errp)
     return first + ics->offset;
 }
 
-#define SPAPR_IRQ_XICS_LEGACY_NR_XIRQS     0x400
-
 SpaprIrq spapr_irq_xics_legacy = {
-    .nr_xirqs    = SPAPR_IRQ_XICS_LEGACY_NR_XIRQS,
-    .nr_msis     = SPAPR_IRQ_XICS_LEGACY_NR_XIRQS,
     .xics        = true,
     .xive        = false,
-
-    .claim       = spapr_irq_claim_xics,
-    .free        = spapr_irq_free_xics,
-    .print_info  = spapr_irq_print_info_xics,
-    .dt_populate = spapr_dt_xics,
-    .cpu_intc_create = spapr_irq_cpu_intc_create_xics,
-    .post_load   = spapr_irq_post_load_xics,
-    .reset       = spapr_irq_reset_xics,
-    .set_irq     = spapr_irq_set_irq_xics,
-    .init_kvm    = spapr_irq_init_kvm_xics,
 };
+
+static void spapr_irq_register_types(void)
+{
+    type_register_static(&spapr_intc_info);
+}
+
+type_init(spapr_irq_register_types)