summary refs log tree commit diff stats
path: root/hw/ppc
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-01-06 11:24:11 +0000
committerPeter Maydell <peter.maydell@linaro.org>2021-01-06 11:24:11 +0000
commit7a5fd9343d758d077b6f783d02aa30d9789c4989 (patch)
tree011ec201c61d5df469c2744c44ed62ae000f00bd /hw/ppc
parent2e0b5bbe813930021b2baab03c9d424c1c52d18b (diff)
parent5cbd51a5a58098444ffa246ece2013849be04299 (diff)
downloadfocaccia-qemu-7a5fd9343d758d077b6f783d02aa30d9789c4989.tar.gz
focaccia-qemu-7a5fd9343d758d077b6f783d02aa30d9789c4989.zip
Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.0-20210106' into staging
ppc patch queue 2021-01-06

First pull request for 2021, which has a bunch of things accumulated
over the holidays.  Includes:
  * A number of cleanups to sam460ex and ppc440 code from BALATON Zoltan
  * Several fixes for builds with --without-default-devices from Greg Kurz
  * Fixes for some DRC reset problems from Greg Kurz
  * QOM conversion of the PPC 4xx UIC devices from Peter Maydell
  * Some other assorted fixes and cleanups

# gpg: Signature made Wed 06 Jan 2021 03:33:19 GMT
# 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/dg-gitlab/tags/ppc-for-6.0-20210106: (22 commits)
  ppc440_pcix: Fix up pci config access
  ppc440_pcix: Fix register write trace event
  ppc440_pcix: Improve comment for IRQ mapping
  sam460ex: Remove FDT_PPC dependency from KConfig
  ppc4xx: Move common dependency on serial to common option
  pnv: Fix reverse dependency on PCI express root ports
  ppc: Simplify reverse dependencies of POWERNV and PSERIES on XICS and XIVE
  ppc: Fix build with --without-default-devices
  spapr: Add drc_ prefix to the DRC realize and unrealize functions
  spapr: Use spapr_drc_reset_all() at machine reset
  spapr: Introduce spapr_drc_reset_all()
  spapr: Fix reset of transient DR connectors
  spapr: Call spapr_drc_reset() for all DRCs at CAS
  spapr: Fix buffer overflow in spapr_numa_associativity_init()
  spapr: Allow memory unplug to always succeed
  spapr: Fix DR properties of the root node
  spapr/xive: Make spapr_xive_pic_print_info() static
  spapr: DRC lookup cannot fail
  hw/ppc/ppc440_bamboo: Drop use of ppcuic_init()
  hw/ppc/virtex_ml507: Drop use of ppcuic_init()
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/Kconfig29
-rw-r--r--hw/ppc/ppc440_bamboo.c38
-rw-r--r--hw/ppc/ppc440_pcix.c50
-rw-r--r--hw/ppc/ppc4xx_devs.c262
-rw-r--r--hw/ppc/spapr.c44
-rw-r--r--hw/ppc/spapr_drc.c63
-rw-r--r--hw/ppc/spapr_events.c3
-rw-r--r--hw/ppc/spapr_hcall.c33
-rw-r--r--hw/ppc/trace-events1
-rw-r--r--hw/ppc/virtex_ml507.c21
10 files changed, 189 insertions, 355 deletions
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index dd86e664d2..7e267d94a1 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -7,8 +7,8 @@ config PSERIES
     select PCI
     select SPAPR_VSCSI
     select VFIO if LINUX   # needed by spapr_pci_vfio.c
-    select XICS_SPAPR
-    select XIVE_SPAPR
+    select XICS
+    select XIVE
     select MSI_NONBROKEN
     select FDT_PPC
     select CHRP_NVRAM
@@ -29,15 +29,13 @@ config POWERNV
     select XICS
     select XIVE
     select FDT_PPC
-    select PCI_EXPRESS
-    select MSI_NONBROKEN
+    select PCI_POWERNV
 
 config PPC405
     bool
     select M48T59
     select PFLASH_CFI02
     select PPC4XX
-    select SERIAL
 
 config PPC440
     bool
@@ -46,13 +44,14 @@ config PPC440
     imply E1000_PCI
     select PCI_EXPRESS
     select PPC4XX
-    select SERIAL
     select FDT_PPC
 
 config PPC4XX
     bool
     select BITBANG_I2C
     select PCI
+    select PPC_UIC
+    select SERIAL
 
 config SAM460EX
     bool
@@ -61,12 +60,10 @@ config SAM460EX
     select IDE_SII3112
     select M41T80
     select PPC440
-    select SERIAL
     select SM501
     select SMBUS_EEPROM
     select USB_EHCI_SYSBUS
     select USB_OHCI
-    select FDT_PPC
 
 config PREP
     bool
@@ -123,26 +120,10 @@ config VIRTEX
     bool
     select PPC4XX
     select PFLASH_CFI01
-    select SERIAL
     select XILINX
     select XILINX_ETHLITE
     select FDT_PPC
 
-config XIVE
-    bool
-    depends on POWERNV || PSERIES
-
-config XIVE_SPAPR
-    bool
-    default y
-    depends on PSERIES
-    select XIVE
-
-config XIVE_KVM
-    bool
-    default y
-    depends on XIVE_SPAPR && KVM
-
 # Only used by 64-bit targets
 config FW_CFG_PPC
     bool
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 665bc1784e..b156bcb999 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -33,6 +33,9 @@
 #include "sysemu/qtest.h"
 #include "sysemu/reset.h"
 #include "hw/sysbus.h"
+#include "hw/intc/ppc-uic.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
 
 #define BINARY_DEVICE_TREE_FILE "bamboo.dtb"
 
@@ -168,13 +171,13 @@ static void bamboo_init(MachineState *machine)
     MemoryRegion *ram_memories = g_new(MemoryRegion, PPC440EP_SDRAM_NR_BANKS);
     hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS];
     hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS];
-    qemu_irq *pic;
-    qemu_irq *irqs;
     PCIBus *pcibus;
     PowerPCCPU *cpu;
     CPUPPCState *env;
     target_long initrd_size = 0;
     DeviceState *dev;
+    DeviceState *uicdev;
+    SysBusDevice *uicsbd;
     int success;
     int i;
 
@@ -192,10 +195,17 @@ static void bamboo_init(MachineState *machine)
     ppc_dcr_init(env, NULL, NULL);
 
     /* interrupt controller */
-    irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB);
-    irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
-    irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
-    pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+    uicdev = qdev_new(TYPE_PPC_UIC);
+    uicsbd = SYS_BUS_DEVICE(uicdev);
+
+    object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu),
+                             &error_fatal);
+    sysbus_realize_and_unref(uicsbd, &error_fatal);
+
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
+                       ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]);
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
+                       ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]);
 
     /* SDRAM controller */
     memset(ram_bases, 0, sizeof(ram_bases));
@@ -203,14 +213,18 @@ static void bamboo_init(MachineState *machine)
     ppc4xx_sdram_banks(machine->ram, PPC440EP_SDRAM_NR_BANKS, ram_memories,
                        ram_bases, ram_sizes, ppc440ep_sdram_bank_sizes);
     /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */
-    ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories,
+    ppc4xx_sdram_init(env,
+                      qdev_get_gpio_in(uicdev, 14),
+                      PPC440EP_SDRAM_NR_BANKS, ram_memories,
                       ram_bases, ram_sizes, 1);
 
     /* PCI */
     dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE,
                                 PPC440EP_PCI_CONFIG,
-                                pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]],
-                                pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]],
+                                qdev_get_gpio_in(uicdev, pci_irq_nrs[0]),
+                                qdev_get_gpio_in(uicdev, pci_irq_nrs[1]),
+                                qdev_get_gpio_in(uicdev, pci_irq_nrs[2]),
+                                qdev_get_gpio_in(uicdev, pci_irq_nrs[3]),
                                 NULL);
     pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
     if (!pcibus) {
@@ -223,12 +237,14 @@ static void bamboo_init(MachineState *machine)
     memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa);
 
     if (serial_hd(0) != NULL) {
-        serial_mm_init(address_space_mem, 0xef600300, 0, pic[0],
+        serial_mm_init(address_space_mem, 0xef600300, 0,
+                       qdev_get_gpio_in(uicdev, 0),
                        PPC_SERIAL_MM_BAUDBASE, serial_hd(0),
                        DEVICE_BIG_ENDIAN);
     }
     if (serial_hd(1) != NULL) {
-        serial_mm_init(address_space_mem, 0xef600400, 0, pic[1],
+        serial_mm_init(address_space_mem, 0xef600400, 0,
+                       qdev_get_gpio_in(uicdev, 1),
                        PPC_SERIAL_MM_BAUDBASE, serial_hd(1),
                        DEVICE_BIG_ENDIAN);
     }
diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c
index ee952314c8..91cbcd0504 100644
--- a/hw/ppc/ppc440_pcix.c
+++ b/hw/ppc/ppc440_pcix.c
@@ -169,7 +169,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr,
 {
     struct PPC440PCIXState *s = opaque;
 
-    trace_ppc440_pcix_reg_read(addr, val);
+    trace_ppc440_pcix_reg_write(addr, val, size);
     switch (addr) {
     case PCI_VENDOR_ID ... PCI_MAX_LAT:
         stl_le_p(s->dev->config + addr, val);
@@ -415,8 +415,15 @@ static void ppc440_pcix_reset(DeviceState *dev)
     s->sts = 0;
 }
 
-/* All pins from each slot are tied to a single board IRQ.
- * This may need further refactoring for other boards. */
+/*
+ * All four IRQ[ABCD] pins from all slots are tied to a single board
+ * IRQ, so our mapping function here maps everything to IRQ 0.
+ * The code in pci_change_irq_level() tracks the number of times
+ * the mapped IRQ is asserted and deasserted, so if multiple devices
+ * assert an IRQ at the same time the behaviour is correct.
+ *
+ * This may need further refactoring for boards that use multiple IRQ lines.
+ */
 static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num)
 {
     trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0);
@@ -442,28 +449,35 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn)
     return &s->bm_as;
 }
 
-/* The default pci_host_data_{read,write} functions in pci/pci_host.c
- * deny access to registers without bit 31 set but our clients want
- * this to work so we have to override these here */
-static void pci_host_data_write(void *opaque, hwaddr addr,
-                                uint64_t val, unsigned len)
+/*
+ * Some guests on sam460ex write all kinds of garbage here such as
+ * missing enable bit and low bits set and still expect this to work
+ * (apparently it does on real hardware because these boot there) so
+ * we have to override these ops here and fix it up
+ */
+static void pci_host_config_write(void *opaque, hwaddr addr,
+                                  uint64_t val, unsigned len)
 {
     PCIHostState *s = opaque;
-    pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+
+    if (addr != 0 || len != 4) {
+        return;
+    }
+    s->config_reg = (val & 0xfffffffcULL) | (1UL << 31);
 }
 
-static uint64_t pci_host_data_read(void *opaque,
-                                   hwaddr addr, unsigned len)
+static uint64_t pci_host_config_read(void *opaque, hwaddr addr,
+                                     unsigned len)
 {
     PCIHostState *s = opaque;
-    uint32_t val;
-    val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
+    uint32_t val = s->config_reg;
+
     return val;
 }
 
-const MemoryRegionOps ppc440_pcix_host_data_ops = {
-    .read = pci_host_data_read,
-    .write = pci_host_data_write,
+const MemoryRegionOps ppc440_pcix_host_conf_ops = {
+    .read = pci_host_config_read,
+    .write = pci_host_config_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
@@ -490,9 +504,9 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp)
     pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s);
 
     memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE);
-    memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops,
+    memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops,
                           h, "pci-conf-idx", 4);
-    memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops,
+    memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops,
                           h, "pci-conf-data", 4);
     memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s,
                           "pci.reg", PPC440_REG_SIZE);
diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c
index f1651e04d9..ffe4cf43e8 100644
--- a/hw/ppc/ppc4xx_devs.c
+++ b/hw/ppc/ppc4xx_devs.c
@@ -30,9 +30,12 @@
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/ppc4xx.h"
 #include "hw/boards.h"
+#include "hw/intc/ppc-uic.h"
+#include "hw/qdev-properties.h"
 #include "qemu/log.h"
 #include "exec/address-spaces.h"
 #include "qemu/error-report.h"
+#include "qapi/error.h"
 
 /*#define DEBUG_UIC*/
 
@@ -76,245 +79,40 @@ PowerPCCPU *ppc4xx_init(const char *cpu_type,
 
 /*****************************************************************************/
 /* "Universal" Interrupt controller */
-enum {
-    DCR_UICSR  = 0x000,
-    DCR_UICSRS = 0x001,
-    DCR_UICER  = 0x002,
-    DCR_UICCR  = 0x003,
-    DCR_UICPR  = 0x004,
-    DCR_UICTR  = 0x005,
-    DCR_UICMSR = 0x006,
-    DCR_UICVR  = 0x007,
-    DCR_UICVCR = 0x008,
-    DCR_UICMAX = 0x009,
-};
-
-#define UIC_MAX_IRQ 32
-typedef struct ppcuic_t ppcuic_t;
-struct ppcuic_t {
-    uint32_t dcr_base;
-    int use_vectors;
-    uint32_t level;  /* Remembers the state of level-triggered interrupts. */
-    uint32_t uicsr;  /* Status register */
-    uint32_t uicer;  /* Enable register */
-    uint32_t uiccr;  /* Critical register */
-    uint32_t uicpr;  /* Polarity register */
-    uint32_t uictr;  /* Triggering register */
-    uint32_t uicvcr; /* Vector configuration register */
-    uint32_t uicvr;
-    qemu_irq *irqs;
-};
-
-static void ppcuic_trigger_irq (ppcuic_t *uic)
-{
-    uint32_t ir, cr;
-    int start, end, inc, i;
-
-    /* Trigger interrupt if any is pending */
-    ir = uic->uicsr & uic->uicer & (~uic->uiccr);
-    cr = uic->uicsr & uic->uicer & uic->uiccr;
-    LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
-                " uiccr %08" PRIx32 "\n"
-                "   %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
-                __func__, uic->uicsr, uic->uicer, uic->uiccr,
-                uic->uicsr & uic->uicer, ir, cr);
-    if (ir != 0x0000000) {
-        LOG_UIC("Raise UIC interrupt\n");
-        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
-    } else {
-        LOG_UIC("Lower UIC interrupt\n");
-        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
-    }
-    /* Trigger critical interrupt if any is pending and update vector */
-    if (cr != 0x0000000) {
-        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
-        if (uic->use_vectors) {
-            /* Compute critical IRQ vector */
-            if (uic->uicvcr & 1) {
-                start = 31;
-                end = 0;
-                inc = -1;
-            } else {
-                start = 0;
-                end = 31;
-                inc = 1;
-            }
-            uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
-            for (i = start; i <= end; i += inc) {
-                if (cr & (1 << i)) {
-                    uic->uicvr += (i - start) * 512 * inc;
-                    break;
-                }
-            }
-        }
-        LOG_UIC("Raise UIC critical interrupt - "
-                    "vector %08" PRIx32 "\n", uic->uicvr);
-    } else {
-        LOG_UIC("Lower UIC critical interrupt\n");
-        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
-        uic->uicvr = 0x00000000;
-    }
-}
-
-static void ppcuic_set_irq (void *opaque, int irq_num, int level)
-{
-    ppcuic_t *uic;
-    uint32_t mask, sr;
-
-    uic = opaque;
-    mask = 1U << (31-irq_num);
-    LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
-                " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
-                __func__, irq_num, level,
-                uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
-    if (irq_num < 0 || irq_num > 31)
-        return;
-    sr = uic->uicsr;
-
-    /* Update status register */
-    if (uic->uictr & mask) {
-        /* Edge sensitive interrupt */
-        if (level == 1)
-            uic->uicsr |= mask;
-    } else {
-        /* Level sensitive interrupt */
-        if (level == 1) {
-            uic->uicsr |= mask;
-            uic->level |= mask;
-        } else {
-            uic->uicsr &= ~mask;
-            uic->level &= ~mask;
-        }
-    }
-    LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
-                "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
-    if (sr != uic->uicsr)
-        ppcuic_trigger_irq(uic);
-}
-
-static uint32_t dcr_read_uic (void *opaque, int dcrn)
-{
-    ppcuic_t *uic;
-    uint32_t ret;
-
-    uic = opaque;
-    dcrn -= uic->dcr_base;
-    switch (dcrn) {
-    case DCR_UICSR:
-    case DCR_UICSRS:
-        ret = uic->uicsr;
-        break;
-    case DCR_UICER:
-        ret = uic->uicer;
-        break;
-    case DCR_UICCR:
-        ret = uic->uiccr;
-        break;
-    case DCR_UICPR:
-        ret = uic->uicpr;
-        break;
-    case DCR_UICTR:
-        ret = uic->uictr;
-        break;
-    case DCR_UICMSR:
-        ret = uic->uicsr & uic->uicer;
-        break;
-    case DCR_UICVR:
-        if (!uic->use_vectors)
-            goto no_read;
-        ret = uic->uicvr;
-        break;
-    case DCR_UICVCR:
-        if (!uic->use_vectors)
-            goto no_read;
-        ret = uic->uicvcr;
-        break;
-    default:
-    no_read:
-        ret = 0x00000000;
-        break;
-    }
-
-    return ret;
-}
-
-static void dcr_write_uic (void *opaque, int dcrn, uint32_t val)
-{
-    ppcuic_t *uic;
-
-    uic = opaque;
-    dcrn -= uic->dcr_base;
-    LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
-    switch (dcrn) {
-    case DCR_UICSR:
-        uic->uicsr &= ~val;
-        uic->uicsr |= uic->level;
-        ppcuic_trigger_irq(uic);
-        break;
-    case DCR_UICSRS:
-        uic->uicsr |= val;
-        ppcuic_trigger_irq(uic);
-        break;
-    case DCR_UICER:
-        uic->uicer = val;
-        ppcuic_trigger_irq(uic);
-        break;
-    case DCR_UICCR:
-        uic->uiccr = val;
-        ppcuic_trigger_irq(uic);
-        break;
-    case DCR_UICPR:
-        uic->uicpr = val;
-        break;
-    case DCR_UICTR:
-        uic->uictr = val;
-        ppcuic_trigger_irq(uic);
-        break;
-    case DCR_UICMSR:
-        break;
-    case DCR_UICVR:
-        break;
-    case DCR_UICVCR:
-        uic->uicvcr = val & 0xFFFFFFFD;
-        ppcuic_trigger_irq(uic);
-        break;
-    }
-}
-
-static void ppcuic_reset (void *opaque)
-{
-    ppcuic_t *uic;
-
-    uic = opaque;
-    uic->uiccr = 0x00000000;
-    uic->uicer = 0x00000000;
-    uic->uicpr = 0x00000000;
-    uic->uicsr = 0x00000000;
-    uic->uictr = 0x00000000;
-    if (uic->use_vectors) {
-        uic->uicvcr = 0x00000000;
-        uic->uicvr = 0x0000000;
-    }
-}
 
 qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs,
                        uint32_t dcr_base, int has_ssr, int has_vr)
 {
-    ppcuic_t *uic;
+    DeviceState *uicdev = qdev_new(TYPE_PPC_UIC);
+    SysBusDevice *uicsbd = SYS_BUS_DEVICE(uicdev);
+    qemu_irq *uic_irqs;
     int i;
 
-    uic = g_malloc0(sizeof(ppcuic_t));
-    uic->dcr_base = dcr_base;
-    uic->irqs = irqs;
-    if (has_vr)
-        uic->use_vectors = 1;
-    for (i = 0; i < DCR_UICMAX; i++) {
-        ppc_dcr_register(env, dcr_base + i, uic,
-                         &dcr_read_uic, &dcr_write_uic);
+    qdev_prop_set_uint32(uicdev, "dcr-base", dcr_base);
+    qdev_prop_set_bit(uicdev, "use-vectors", has_vr);
+    object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(env_cpu(env)),
+                             &error_fatal);
+    sysbus_realize_and_unref(uicsbd, &error_fatal);
+
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT, irqs[PPCUIC_OUTPUT_INT]);
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT, irqs[PPCUIC_OUTPUT_CINT]);
+
+    /*
+     * Return an allocated array of the UIC's input IRQ lines.
+     * This is an ugly temporary API to retain compatibility with
+     * the ppcuic_init() interface from the pre-QOM-conversion UIC.
+     * None of the callers free this array, so it is leaked -- but
+     * so was the array allocated by qemu_allocate_irqs() in the
+     * old code.
+     *
+     * The callers should just instantiate the UIC and wire it up
+     * themselves rather than passing qemu_irq* in and out of this function.
+     */
+    uic_irqs = g_new0(qemu_irq, UIC_MAX_IRQ);
+    for (i = 0; i < UIC_MAX_IRQ; i++) {
+        uic_irqs[i] = qdev_get_gpio_in(uicdev, i);
     }
-    qemu_register_reset(ppcuic_reset, uic);
-
-    return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
+    return uic_irqs;
 }
 
 /*****************************************************************************/
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 489cefcb81..2c403b574e 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1120,6 +1120,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
     MachineState *machine = MACHINE(spapr);
     MachineClass *mc = MACHINE_GET_CLASS(machine);
     SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
+    uint32_t root_drc_type_mask = 0;
     int ret;
     void *fdt;
     SpaprPhbState *phb;
@@ -1194,8 +1195,18 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
 
     spapr_dt_cpus(fdt, spapr);
 
+    /* ibm,drc-indexes and friends */
     if (smc->dr_lmb_enabled) {
-        _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB));
+        root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB;
+    }
+    if (smc->dr_phb_enabled) {
+        root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB;
+    }
+    if (mc->nvdimm_supported) {
+        root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PMEM;
+    }
+    if (root_drc_type_mask) {
+        _FDT(spapr_dt_drc(fdt, 0, NULL, root_drc_type_mask));
     }
 
     if (mc->has_hotpluggable_cpus) {
@@ -1233,14 +1244,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space)
         }
     }
 
-    if (smc->dr_phb_enabled) {
-        ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB);
-        if (ret < 0) {
-            error_report("Couldn't set up PHB DR device tree properties");
-            exit(1);
-        }
-    }
-
     /* NVDIMM devices */
     if (mc->nvdimm_supported) {
         spapr_dt_persistent_memory(spapr, fdt);
@@ -1563,19 +1566,6 @@ void spapr_setup_hpt(SpaprMachineState *spapr)
     }
 }
 
-static int spapr_reset_drcs(Object *child, void *opaque)
-{
-    SpaprDrc *drc =
-        (SpaprDrc *) object_dynamic_cast(child,
-                                                 TYPE_SPAPR_DR_CONNECTOR);
-
-    if (drc) {
-        spapr_drc_reset(drc);
-    }
-
-    return 0;
-}
-
 static void spapr_machine_reset(MachineState *machine)
 {
     SpaprMachineState *spapr = SPAPR_MACHINE(machine);
@@ -1630,7 +1620,7 @@ static void spapr_machine_reset(MachineState *machine)
      * will crash QEMU if the DIMM holding the vring goes away). To avoid such
      * situations, we reset DRCs after all devices have been reset.
      */
-    object_child_foreach_recursive(object_get_root(), spapr_reset_drcs, NULL);
+    spapr_drc_reset_all(spapr);
 
     spapr_clear_pending_events(spapr);
 
@@ -3437,6 +3427,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
         if (dedicated_hp_event_source) {
             drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
                                   addr_start / SPAPR_MEMORY_BLOCK_SIZE);
+            g_assert(drc);
             spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
                                                    nr_lmbs,
                                                    spapr_drc_index(drc));
@@ -3677,6 +3668,7 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
 
     drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB,
                           addr_start / SPAPR_MEMORY_BLOCK_SIZE);
+    g_assert(drc);
     spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
                                               nr_lmbs, spapr_drc_index(drc));
 }
@@ -4064,7 +4056,8 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
     SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
 
     if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
-        if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
+        if (!smc->pre_6_0_memory_unplug ||
+            spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
             spapr_memory_unplug_request(hotplug_dev, dev, errp);
         } else {
             /* NOTE: this means there is a window after guest reset, prior to
@@ -4550,8 +4543,11 @@ DEFINE_SPAPR_MACHINE(6_0, "6.0", true);
  */
 static void spapr_machine_5_2_class_options(MachineClass *mc)
 {
+    SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+
     spapr_machine_6_0_class_options(mc);
     compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len);
+    smc->pre_6_0_memory_unplug = true;
 }
 
 DEFINE_SPAPR_MACHINE(5_2, "5.2", false);
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index f991cf89a0..8571d5bafe 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -417,9 +417,10 @@ void spapr_drc_detach(SpaprDrc *drc)
     spapr_drc_release(drc);
 }
 
-void spapr_drc_reset(SpaprDrc *drc)
+bool spapr_drc_reset(SpaprDrc *drc)
 {
     SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+    bool unplug_completed = false;
 
     trace_spapr_drc_reset(spapr_drc_index(drc));
 
@@ -428,6 +429,7 @@ void spapr_drc_reset(SpaprDrc *drc)
      */
     if (drc->unplug_requested) {
         spapr_drc_release(drc);
+        unplug_completed = true;
     }
 
     if (drc->dev) {
@@ -444,6 +446,8 @@ void spapr_drc_reset(SpaprDrc *drc)
         drc->ccs_offset = -1;
         drc->ccs_depth = -1;
     }
+
+    return unplug_completed;
 }
 
 static bool spapr_drc_unplug_requested_needed(void *opaque)
@@ -462,8 +466,9 @@ static const VMStateDescription vmstate_spapr_drc_unplug_requested = {
     }
 };
 
-bool spapr_drc_transient(SpaprDrc *drc)
+static bool spapr_drc_needed(void *opaque)
 {
+    SpaprDrc *drc = opaque;
     SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
 
     /*
@@ -483,11 +488,6 @@ bool spapr_drc_transient(SpaprDrc *drc)
         spapr_drc_unplug_requested(drc);
 }
 
-static bool spapr_drc_needed(void *opaque)
-{
-    return spapr_drc_transient(opaque);
-}
-
 static const VMStateDescription vmstate_spapr_drc = {
     .name = "spapr_drc",
     .version_id = 1,
@@ -503,7 +503,7 @@ static const VMStateDescription vmstate_spapr_drc = {
     }
 };
 
-static void realize(DeviceState *d, Error **errp)
+static void drc_realize(DeviceState *d, Error **errp)
 {
     SpaprDrc *drc = SPAPR_DR_CONNECTOR(d);
     Object *root_container;
@@ -530,7 +530,7 @@ static void realize(DeviceState *d, Error **errp)
     trace_spapr_drc_realize_complete(spapr_drc_index(drc));
 }
 
-static void unrealize(DeviceState *d)
+static void drc_unrealize(DeviceState *d)
 {
     SpaprDrc *drc = SPAPR_DR_CONNECTOR(d);
     Object *root_container;
@@ -579,8 +579,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
 {
     DeviceClass *dk = DEVICE_CLASS(k);
 
-    dk->realize = realize;
-    dk->unrealize = unrealize;
+    dk->realize = drc_realize;
+    dk->unrealize = drc_unrealize;
     /*
      * Reason: DR connector needs to be wired to either the machine or to a
      * PHB in spapr_dr_connector_new().
@@ -628,7 +628,7 @@ static void realize_physical(DeviceState *d, Error **errp)
     SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d);
     Error *local_err = NULL;
 
-    realize(d, &local_err);
+    drc_realize(d, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
         return;
@@ -644,7 +644,7 @@ static void unrealize_physical(DeviceState *d)
 {
     SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d);
 
-    unrealize(d);
+    drc_unrealize(d);
     vmstate_unregister(VMSTATE_IF(drcp), &vmstate_spapr_drc_physical, drcp);
     qemu_unregister_reset(drc_physical_reset, drcp);
 }
@@ -832,6 +832,12 @@ int spapr_dt_drc(void *fdt, int offset, Object *owner, uint32_t drc_type_mask)
     GString *drc_names, *drc_types;
     int ret;
 
+    /*
+     * This should really be only called once per node since it overwrites
+     * the OF properties if they already exist.
+     */
+    g_assert(!fdt_get_property(fdt, offset, "ibm,drc-indexes", NULL));
+
     /* the first entry of each properties is a 32-bit integer encoding
      * the number of elements in the array. we won't know this until
      * we complete the iteration through all the matching DRCs, but
@@ -943,6 +949,37 @@ out:
     return ret;
 }
 
+void spapr_drc_reset_all(SpaprMachineState *spapr)
+{
+    Object *drc_container;
+    ObjectProperty *prop;
+    ObjectPropertyIterator iter;
+
+    drc_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+restart:
+    object_property_iter_init(&iter, drc_container);
+    while ((prop = object_property_iter_next(&iter))) {
+        SpaprDrc *drc;
+
+        if (!strstart(prop->type, "link<", NULL)) {
+            continue;
+        }
+        drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container,
+                                                          prop->name,
+                                                          &error_abort));
+
+        /*
+         * This will complete any pending plug/unplug requests.
+         * In case of a unplugged PHB or PCI bridge, this will
+         * cause some DRCs to be destroyed and thus potentially
+         * invalidate the iterator.
+         */
+        if (spapr_drc_reset(drc)) {
+            goto restart;
+        }
+    }
+}
+
 /*
  * RTAS calls
  */
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 3f37b49fd8..6aedd988b3 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -658,7 +658,8 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
         /* we should not be using count_indexed value unless the guest
          * supports dedicated hotplug event source
          */
-        g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT));
+        g_assert(!SPAPR_MACHINE_GET_CLASS(spapr)->pre_6_0_memory_unplug ||
+                 spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT));
         hp->drc_id.count_indexed.count =
             cpu_to_be32(drc_id->count_indexed.count);
         hp->drc_id.count_indexed.index =
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index c0ea0bd579..e5dfc1ba7a 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1632,32 +1632,6 @@ static uint32_t cas_check_pvr(PowerPCCPU *cpu, uint32_t max_compat,
     return best_compat;
 }
 
-static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr)
-{
-    Object *drc_container;
-    ObjectProperty *prop;
-    ObjectPropertyIterator iter;
-
-    drc_container = container_get(object_get_root(), "/dr-connector");
-    object_property_iter_init(&iter, drc_container);
-    while ((prop = object_property_iter_next(&iter))) {
-        SpaprDrc *drc;
-
-        if (!strstart(prop->type, "link<", NULL)) {
-            continue;
-        }
-        drc = SPAPR_DR_CONNECTOR(object_property_get_link(drc_container,
-                                                          prop->name,
-                                                          &error_abort));
-
-        if (spapr_drc_transient(drc)) {
-            spapr_drc_reset(drc);
-        }
-    }
-
-    spapr_clear_pending_hotplug_events(spapr);
-}
-
 target_ulong do_client_architecture_support(PowerPCCPU *cpu,
                                             SpaprMachineState *spapr,
                                             target_ulong vec,
@@ -1815,7 +1789,12 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu,
 
     spapr_irq_update_active_intc(spapr);
 
-    spapr_handle_transient_dev_before_cas(spapr);
+    /*
+     * Process all pending hot-plug/unplug requests now. An updated full
+     * rendered FDT will be returned to the guest.
+     */
+    spapr_drc_reset_all(spapr);
+    spapr_clear_pending_hotplug_events(spapr);
 
     /*
      * If spapr_machine_reset() did not set up a HPT but one is necessary
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 6d8d095aa2..1e91984526 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -96,3 +96,4 @@ ppc440_pcix_set_irq(int irq_num) "PCI irq %d"
 ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64
 ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64
 ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32
+ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRIx64 " = 0x%" PRIx32 " size 0x%" PRIx32
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index 07fe49da0d..b26ff17767 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -43,6 +43,7 @@
 #include "qemu/option.h"
 #include "exec/address-spaces.h"
 
+#include "hw/intc/ppc-uic.h"
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/ppc4xx.h"
 #include "hw/qdev-properties.h"
@@ -95,7 +96,8 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk)
 {
     PowerPCCPU *cpu;
     CPUPPCState *env;
-    qemu_irq *irqs;
+    DeviceState *uicdev;
+    SysBusDevice *uicsbd;
 
     cpu = POWERPC_CPU(cpu_create(cpu_type));
     env = &cpu->env;
@@ -105,10 +107,19 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk)
     ppc_dcr_init(env, NULL, NULL);
 
     /* interrupt controller */
-    irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB);
-    irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
-    irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
-    ppcuic_init(env, irqs, 0x0C0, 0, 1);
+    uicdev = qdev_new(TYPE_PPC_UIC);
+    uicsbd = SYS_BUS_DEVICE(uicdev);
+
+    object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu),
+                             &error_fatal);
+    sysbus_realize_and_unref(uicsbd, &error_fatal);
+
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
+                       ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]);
+    sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
+                       ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]);
+
+    /* This board doesn't wire anything up to the inputs of the UIC. */
     return cpu;
 }