summary refs log tree commit diff stats
path: root/hw/ppc
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-10-24 16:22:58 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-10-24 16:22:58 +0100
commit58560ad254fbda71d4daa6622d71683190070ee2 (patch)
treeb5fc3eb6758fc2ccbaab4506c8e11b27c0493353 /hw/ppc
parent81c1f71eeb874c4cbbb9c5c4d1a1dc0ba7391dff (diff)
parent97c00c54449b4ff349f85c6ce409dadd1b935a7d (diff)
downloadfocaccia-qemu-58560ad254fbda71d4daa6622d71683190070ee2.tar.gz
focaccia-qemu-58560ad254fbda71d4daa6622d71683190070ee2.zip
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-4.2-20191024' into staging
ppc patch queue 2019-10-24

Last pull request before soft freeze.
  * Lots of fixes and cleanups for spapr interrupt controllers
  * More SLOF updates to fix problems with full FDT rendering at CAS
    time (alas, more yet are to come)
  * A few other assorted changes

This isn't quite as well tested as I usually try to do before a pull
request.  But I've been sick and running into some other difficulties,
and wanted to get this sent out before heading towards KVM forum.

# gpg: Signature made Thu 24 Oct 2019 09:14:31 BST
# gpg:                using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full]
# gpg:                 aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full]
# gpg:                 aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full]
# gpg:                 aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown]
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E  87DC 6C38 CACA 20D9 B392

* remotes/dgibson/tags/ppc-for-4.2-20191024: (28 commits)
  spapr/xive: Set the OS CAM line at reset
  ppc/pnv: Fix naming of routines realizing the CPUs
  ppc: Reset the interrupt presenter from the CPU reset handler
  ppc/pnv: Add a PnvChip pointer to PnvCore
  ppc/pnv: Introduce a PnvCore reset handler
  spapr_cpu_core: Implement DeviceClass::reset
  spapr: move CPU reset after presenter creation
  spapr: Don't request to unplug the same core twice
  pseries: Update SLOF firmware image
  spapr: Move SpaprIrq::nr_xirqs to SpaprMachineClass
  spapr: Remove SpaprIrq::nr_msis
  spapr, xics, xive: Move SpaprIrq::post_load hook to backends
  spapr, xics, xive: Move SpaprIrq::reset hook logic into activate/deactivate
  spapr: Remove SpaprIrq::init_kvm hook
  spapr, xics, xive: Match signatures for XICS and XIVE KVM connect routines
  spapr, xics, xive: Move dt_populate from SpaprIrq to SpaprInterruptController
  spapr, xics, xive: Move print_info from SpaprIrq to SpaprInterruptController
  spapr, xics, xive: Move set_irq from SpaprIrq to SpaprInterruptController
  spapr: Formalize notion of active interrupt controller
  spapr, xics, xive: Move irq claim and free from SpaprIrq to SpaprInterruptController
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/pnv.c18
-rw-r--r--hw/ppc/pnv_core.c31
-rw-r--r--hw/ppc/pnv_psi.c15
-rw-r--r--hw/ppc/spapr.c26
-rw-r--r--hw/ppc/spapr_cpu_core.c47
-rw-r--r--hw/ppc/spapr_irq.c611
-rw-r--r--hw/ppc/spapr_pci.c7
7 files changed, 324 insertions, 431 deletions
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 7cf64b6d25..4a51fb65a8 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -778,6 +778,13 @@ static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu,
     pnv_cpu->intc = obj;
 }
 
+static void pnv_chip_power8_intc_reset(PnvChip *chip, PowerPCCPU *cpu)
+{
+    PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
+
+    icp_reset(ICP(pnv_cpu->intc));
+}
+
 /*
  *    0:48  Reserved - Read as zeroes
  *   49:52  Node ID
@@ -815,6 +822,13 @@ static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu,
     pnv_cpu->intc = obj;
 }
 
+static void pnv_chip_power9_intc_reset(PnvChip *chip, PowerPCCPU *cpu)
+{
+    PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
+
+    xive_tctx_reset(XIVE_TCTX(pnv_cpu->intc));
+}
+
 /*
  * Allowed core identifiers on a POWER8 Processor Chip :
  *
@@ -984,6 +998,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
     k->cores_mask = POWER8E_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
     k->intc_create = pnv_chip_power8_intc_create;
+    k->intc_reset = pnv_chip_power8_intc_reset;
     k->isa_create = pnv_chip_power8_isa_create;
     k->dt_populate = pnv_chip_power8_dt_populate;
     k->pic_print_info = pnv_chip_power8_pic_print_info;
@@ -1003,6 +1018,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
     k->cores_mask = POWER8_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
     k->intc_create = pnv_chip_power8_intc_create;
+    k->intc_reset = pnv_chip_power8_intc_reset;
     k->isa_create = pnv_chip_power8_isa_create;
     k->dt_populate = pnv_chip_power8_dt_populate;
     k->pic_print_info = pnv_chip_power8_pic_print_info;
@@ -1022,6 +1038,7 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
     k->cores_mask = POWER8_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p8;
     k->intc_create = pnv_chip_power8_intc_create;
+    k->intc_reset = pnv_chip_power8_intc_reset;
     k->isa_create = pnv_chip_power8nvl_isa_create;
     k->dt_populate = pnv_chip_power8_dt_populate;
     k->pic_print_info = pnv_chip_power8_pic_print_info;
@@ -1191,6 +1208,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
     k->cores_mask = POWER9_CORE_MASK;
     k->core_pir = pnv_chip_core_pir_p9;
     k->intc_create = pnv_chip_power9_intc_create;
+    k->intc_reset = pnv_chip_power9_intc_reset;
     k->isa_create = pnv_chip_power9_isa_create;
     k->dt_populate = pnv_chip_power9_dt_populate;
     k->pic_print_info = pnv_chip_power9_pic_print_info;
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index b1a7489e7a..e81cd3a3e0 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -40,11 +40,11 @@ static const char *pnv_core_cpu_typename(PnvCore *pc)
     return cpu_type;
 }
 
-static void pnv_cpu_reset(void *opaque)
+static void pnv_core_cpu_reset(PowerPCCPU *cpu, PnvChip *chip)
 {
-    PowerPCCPU *cpu = opaque;
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
+    PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
 
     cpu_reset(cs);
 
@@ -55,6 +55,8 @@ static void pnv_cpu_reset(void *opaque)
     env->gpr[3] = PNV_FDT_ADDR;
     env->nip = 0x10;
     env->msr |= MSR_HVB; /* Hypervisor mode */
+
+    pcc->intc_reset(chip, cpu);
 }
 
 /*
@@ -160,7 +162,7 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = {
     .endianness = DEVICE_BIG_ENDIAN,
 };
 
-static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
+static void pnv_core_cpu_realize(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
 {
     CPUPPCState *env = &cpu->env;
     int core_pir;
@@ -192,8 +194,17 @@ static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp)
 
     /* Set time-base frequency to 512 MHz */
     cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
+}
+
+static void pnv_core_reset(void *dev)
+{
+    CPUCore *cc = CPU_CORE(dev);
+    PnvCore *pc = PNV_CORE(dev);
+    int i;
 
-    qemu_register_reset(pnv_cpu_reset, cpu);
+    for (i = 0; i < cc->nr_threads; i++) {
+        pnv_core_cpu_reset(pc->threads[i], pc->chip);
+    }
 }
 
 static void pnv_core_realize(DeviceState *dev, Error **errp)
@@ -214,6 +225,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
                                 "required link 'chip' not found: ");
         return;
     }
+    pc->chip = PNV_CHIP(chip);
 
     pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
     for (i = 0; i < cc->nr_threads; i++) {
@@ -235,7 +247,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
     }
 
     for (j = 0; j < cc->nr_threads; j++) {
-        pnv_realize_vcpu(pc->threads[j], PNV_CHIP(chip), &local_err);
+        pnv_core_cpu_realize(pc->threads[j], pc->chip, &local_err);
         if (local_err) {
             goto err;
         }
@@ -244,6 +256,8 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
     snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
     pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops,
                           pc, name, PNV_XSCOM_EX_SIZE);
+
+    qemu_register_reset(pnv_core_reset, pc);
     return;
 
 err:
@@ -255,11 +269,10 @@ err:
     error_propagate(errp, local_err);
 }
 
-static void pnv_unrealize_vcpu(PowerPCCPU *cpu)
+static void pnv_core_cpu_unrealize(PowerPCCPU *cpu)
 {
     PnvCPUState *pnv_cpu = pnv_cpu_state(cpu);
 
-    qemu_unregister_reset(pnv_cpu_reset, cpu);
     object_unparent(OBJECT(pnv_cpu_state(cpu)->intc));
     cpu_remove_sync(CPU(cpu));
     cpu->machine_data = NULL;
@@ -273,8 +286,10 @@ static void pnv_core_unrealize(DeviceState *dev, Error **errp)
     CPUCore *cc = CPU_CORE(dev);
     int i;
 
+    qemu_unregister_reset(pnv_core_reset, pc);
+
     for (i = 0; i < cc->nr_threads; i++) {
-        pnv_unrealize_vcpu(pc->threads[i]);
+        pnv_core_cpu_unrealize(pc->threads[i]);
     }
     g_free(pc->threads);
 }
diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c
index a997f16bb4..68d0dfacfe 100644
--- a/hw/ppc/pnv_psi.c
+++ b/hw/ppc/pnv_psi.c
@@ -660,10 +660,19 @@ static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno)
 
     uint32_t offset =
         (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT);
-    uint64_t lisn = cpu_to_be64(offset + srcno);
+    uint64_t data = XIVE_TRIGGER_PQ | offset | srcno;
+    MemTxResult result;
 
-    if (valid) {
-        cpu_physical_memory_write(notify_addr, &lisn, sizeof(lisn));
+    if (!valid) {
+        return;
+    }
+
+    address_space_stq_be(&address_space_memory, notify_addr, data,
+                         MEMTXATTRS_UNSPECIFIED, &result);
+    if (result != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: trigger failed @%"
+                      HWADDR_PRIx "\n", __func__, notif_port);
+        return;
     }
 }
 
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 4eb97d3a9b..94f9d27096 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1247,8 +1247,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr)
     _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2));
 
     /* /interrupt controller */
-    spapr->irq->dt_populate(spapr, spapr_max_server_number(spapr), fdt,
-                          PHANDLE_INTC);
+    spapr_irq_dt(spapr, spapr_max_server_number(spapr), fdt, PHANDLE_INTC);
 
     ret = spapr_populate_memory(spapr, fdt);
     if (ret < 0) {
@@ -1268,7 +1267,7 @@ static void *spapr_build_fdt(SpaprMachineState *spapr)
     }
 
     QLIST_FOREACH(phb, &spapr->phbs, list) {
-        ret = spapr_dt_phb(phb, PHANDLE_INTC, fdt, spapr->irq->nr_msis, NULL);
+        ret = spapr_dt_phb(spapr, phb, PHANDLE_INTC, fdt, NULL);
         if (ret < 0) {
             error_report("couldn't setup PCI devices in fdt");
             exit(1);
@@ -2496,6 +2495,7 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
 static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp)
 {
     MachineState *ms = MACHINE(spapr);
+    SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
     Error *local_err = NULL;
     bool vsmt_user = !!spapr->vsmt;
     int kvm_smt = kvmppc_smt_threads();
@@ -2522,7 +2522,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp)
             goto out;
         }
         /* In this case, spapr->vsmt has been set by the command line */
-    } else {
+    } else if (!smc->smp_threads_vsmt) {
         /*
          * Default VSMT value is tricky, because we need it to be as
          * consistent as possible (for migration), but this requires
@@ -2531,6 +2531,8 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp)
          * overwhelmingly common case in production systems.
          */
         spapr->vsmt = MAX(8, smp_threads);
+    } else {
+        spapr->vsmt = smp_threads;
     }
 
     /* KVM: If necessary, set the SMT mode: */
@@ -3739,9 +3741,10 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev,
                           spapr_vcpu_id(spapr, cc->core_id));
     g_assert(drc);
 
-    spapr_drc_detach(drc);
-
-    spapr_hotplug_req_remove_by_index(drc);
+    if (!spapr_drc_unplug_requested(drc)) {
+        spapr_drc_detach(drc);
+        spapr_hotplug_req_remove_by_index(drc);
+    }
 }
 
 int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
@@ -3903,8 +3906,7 @@ int spapr_phb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
         return -1;
     }
 
-    if (spapr_dt_phb(sphb, intc_phandle, fdt, spapr->irq->nr_msis,
-                     fdt_start_offset)) {
+    if (spapr_dt_phb(spapr, sphb, intc_phandle, fdt, fdt_start_offset)) {
         error_setg(errp, "unable to create FDT node for PHB %d", sphb->index);
         return -1;
     }
@@ -4263,7 +4265,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj,
 {
     SpaprMachineState *spapr = SPAPR_MACHINE(obj);
 
-    spapr->irq->print_info(spapr, mon);
+    spapr_irq_print_info(spapr, mon);
     monitor_printf(mon, "irqchip: %s\n",
                    kvm_irqchip_in_kernel() ? "in-kernel" : "emulated");
 }
@@ -4438,6 +4440,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
     smc->irq = &spapr_irq_dual;
     smc->dr_phb_enabled = true;
     smc->linux_pci_probe = true;
+    smc->smp_threads_vsmt = true;
+    smc->nr_xirqs = SPAPR_NR_XIRQS;
 }
 
 static const TypeInfo spapr_machine_info = {
@@ -4505,6 +4509,7 @@ static void spapr_machine_4_1_class_options(MachineClass *mc)
 
     spapr_machine_4_2_class_options(mc);
     smc->linux_pci_probe = false;
+    smc->smp_threads_vsmt = false;
     compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len);
     compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
 }
@@ -4573,6 +4578,7 @@ static void spapr_machine_3_0_class_options(MachineClass *mc)
     compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len);
 
     smc->legacy_irq_allocation = true;
+    smc->nr_xirqs = 0x400;
     smc->irq = &spapr_irq_xics_legacy;
 }
 
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 1d93de8161..ef7b27a66d 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -25,14 +25,14 @@
 #include "sysemu/hw_accel.h"
 #include "qemu/error-report.h"
 
-static void spapr_cpu_reset(void *opaque)
+static void spapr_reset_vcpu(PowerPCCPU *cpu)
 {
-    PowerPCCPU *cpu = opaque;
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
     PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
     SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu);
     target_ulong lpcr;
+    SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
 
     cpu_reset(cs);
 
@@ -77,9 +77,11 @@ static void spapr_cpu_reset(void *opaque)
     spapr_cpu->dtl_addr = 0;
     spapr_cpu->dtl_size = 0;
 
-    spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu);
+    spapr_caps_cpu_apply(spapr, cpu);
 
     kvm_check_mmu(cpu, &error_fatal);
+
+    spapr_irq_cpu_intc_reset(spapr, cpu);
 }
 
 void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3)
@@ -193,7 +195,6 @@ static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc)
     if (!sc->pre_3_0_migration) {
         vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data);
     }
-    qemu_unregister_reset(spapr_cpu_reset, cpu);
     if (spapr_cpu_state(cpu)->icp) {
         object_unparent(OBJECT(spapr_cpu_state(cpu)->icp));
     }
@@ -204,12 +205,36 @@ static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc)
     object_unparent(OBJECT(cpu));
 }
 
+/*
+ * Called when CPUs are hot-plugged.
+ */
+static void spapr_cpu_core_reset(DeviceState *dev)
+{
+    CPUCore *cc = CPU_CORE(dev);
+    SpaprCpuCore *sc = SPAPR_CPU_CORE(dev);
+    int i;
+
+    for (i = 0; i < cc->nr_threads; i++) {
+        spapr_reset_vcpu(sc->threads[i]);
+    }
+}
+
+/*
+ * Called by the machine reset.
+ */
+static void spapr_cpu_core_reset_handler(void *opaque)
+{
+    spapr_cpu_core_reset(opaque);
+}
+
 static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp)
 {
     SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev));
     CPUCore *cc = CPU_CORE(dev);
     int i;
 
+    qemu_unregister_reset(spapr_cpu_core_reset_handler, sc);
+
     for (i = 0; i < cc->nr_threads; i++) {
         spapr_unrealize_vcpu(sc->threads[i], sc);
     }
@@ -234,12 +259,8 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr,
     cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));
     kvmppc_set_papr(cpu);
 
-    qemu_register_reset(spapr_cpu_reset, cpu);
-    spapr_cpu_reset(cpu);
-
-    spapr->irq->cpu_intc_create(spapr, cpu, &local_err);
-    if (local_err) {
-        goto error_unregister;
+    if (spapr_irq_cpu_intc_create(spapr, cpu, &local_err) < 0) {
+        goto error_intc_create;
     }
 
     if (!sc->pre_3_0_migration) {
@@ -249,8 +270,7 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr,
 
     return;
 
-error_unregister:
-    qemu_unregister_reset(spapr_cpu_reset, cpu);
+error_intc_create:
     cpu_remove_sync(CPU(cpu));
 error:
     error_propagate(errp, local_err);
@@ -337,6 +357,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
             goto err_unrealize;
         }
     }
+
+    qemu_register_reset(spapr_cpu_core_reset_handler, sc);
     return;
 
 err_unrealize:
@@ -365,6 +387,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
 
     dc->realize = spapr_cpu_core_realize;
     dc->unrealize = spapr_cpu_core_unrealize;
+    dc->reset = spapr_cpu_core_reset;
     dc->props = spapr_cpu_core_properties;
     scc->cpu_type = data;
 }
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)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 01ff41d4c4..cc0e7829b6 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -2277,8 +2277,8 @@ static void spapr_phb_pci_enumerate(SpaprPhbState *phb)
 
 }
 
-int spapr_dt_phb(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
-                 uint32_t nr_msis, int *node_offset)
+int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb,
+                 uint32_t intc_phandle, void *fdt, int *node_offset)
 {
     int bus_off, i, j, ret;
     uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
@@ -2343,7 +2343,8 @@ int spapr_dt_phb(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
     _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
     _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
     _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
-    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", nr_msis));
+    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi",
+                          spapr_irq_nr_msis(spapr)));
 
     /* Dynamic DMA window */
     if (phb->ddw_enabled) {