summary refs log tree commit diff stats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitmodules3
-rw-r--r--Makefile4
-rw-r--r--default-configs/ppc-softmmu.mak2
-rw-r--r--default-configs/ppcemb-softmmu.mak1
-rw-r--r--hw/input/adb-kbd.c4
-rw-r--r--hw/input/adb-mouse.c5
-rw-r--r--hw/input/trace-events5
-rw-r--r--hw/intc/heathrow_pic.c166
-rw-r--r--hw/intc/openpic.c157
-rw-r--r--hw/intc/openpic_kvm.c1
-rw-r--r--hw/intc/trace-events5
-rw-r--r--hw/misc/macio/macio.c150
-rw-r--r--hw/ppc/Makefile.objs3
-rw-r--r--hw/ppc/e500.c124
-rw-r--r--hw/ppc/mac.h10
-rw-r--r--hw/ppc/mac_newworld.c56
-rw-r--r--hw/ppc/mac_oldworld.c50
-rw-r--r--hw/ppc/ppc440_pcix.c528
-rw-r--r--hw/ppc/ppc440_uc.c3
-rw-r--r--hw/ppc/sam460ex.c603
-rw-r--r--hw/ppc/spapr.c176
-rw-r--r--hw/ppc/spapr_caps.c153
-rw-r--r--hw/ppc/spapr_hcall.c5
-rw-r--r--hw/ppc/trace-events8
-rw-r--r--include/hw/intc/heathrow_pic.h49
-rw-r--r--include/hw/misc/macio/macio.h79
-rw-r--r--include/hw/ppc/openpic.h160
-rw-r--r--include/hw/ppc/openpic_kvm.h7
-rw-r--r--include/hw/ppc/spapr.h5
-rw-r--r--pc-bios/canyonlands.dtbbin0 -> 9779 bytes
-rw-r--r--pc-bios/canyonlands.dts566
-rwxr-xr-xpc-bios/u-boot-sam460-20100605.binbin0 -> 524288 bytes
-rw-r--r--roms/Makefile7
m---------roms/u-boot-sam460ex0
-rw-r--r--target/ppc/kvm-stub.c2
-rw-r--r--target/ppc/kvm.c6
-rw-r--r--target/ppc/translate_init.c2
37 files changed, 2532 insertions, 573 deletions
diff --git a/.gitmodules b/.gitmodules
index 7a8282df46..b76fb450a4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -43,3 +43,6 @@
 [submodule "roms/seabios-hppa"]
 	path = roms/seabios-hppa
 	url = git://github.com/hdeller/seabios-hppa.git
+[submodule "roms/u-boot-sam460ex"]
+	path = roms/u-boot-sam460ex
+	url = git://github.com/zbalaton/u-boot-sam460ex
diff --git a/Makefile b/Makefile
index 4df1f67fe4..9a75c48ae0 100644
--- a/Makefile
+++ b/Makefile
@@ -779,12 +779,12 @@ efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
 efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \
 efi-e1000e.rom efi-vmxnet3.rom \
 qemu-icon.bmp qemu_logo_no_text.svg \
-bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
+bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
 multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \
 s390-ccw.img s390-netboot.img \
 spapr-rtas.bin slof.bin skiboot.lid \
 palcode-clipper \
-u-boot.e500 \
+u-boot.e500 u-boot-sam460-20100605.bin \
 qemu_vga.ndrv \
 hppa-firmware.img
 else
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 76e29cfa14..4d7be45ac5 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -21,6 +21,8 @@ CONFIG_E500=y
 CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM))
 CONFIG_PLATFORM_BUS=y
 CONFIG_ETSEC=y
+# For Sam460ex
+CONFIG_USB_EHCI_SYSBUS=y
 CONFIG_SM501=y
 CONFIG_IDE_SII3112=y
 CONFIG_I2C=y
diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak
index bc5e1b3ffe..67d18b2e0e 100644
--- a/default-configs/ppcemb-softmmu.mak
+++ b/default-configs/ppcemb-softmmu.mak
@@ -15,6 +15,7 @@ CONFIG_PTIMER=y
 CONFIG_I8259=y
 CONFIG_XILINX=y
 CONFIG_XILINX_ETHLITE=y
+CONFIG_USB_EHCI_SYSBUS=y
 CONFIG_SM501=y
 CONFIG_IDE_SII3112=y
 CONFIG_I2C=y
diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c
index 266aed1b7b..50b62712c8 100644
--- a/hw/input/adb-kbd.c
+++ b/hw/input/adb-kbd.c
@@ -258,6 +258,7 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
             case ADB_CMD_CHANGE_ID_AND_ACT:
             case ADB_CMD_CHANGE_ID_AND_ENABLE:
                 d->devaddr = buf[1] & 0xf;
+                trace_adb_kbd_request_change_addr(d->devaddr);
                 break;
             default:
                 d->devaddr = buf[1] & 0xf;
@@ -269,6 +270,9 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
                 if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) {
                     d->handler = buf[2];
                 }
+
+                trace_adb_kbd_request_change_addr_and_handler(d->devaddr,
+                                                              d->handler);
                 break;
             }
         }
diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c
index 47e88faf25..3ba6027d33 100644
--- a/hw/input/adb-mouse.c
+++ b/hw/input/adb-mouse.c
@@ -118,6 +118,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
         s->dx = 0;
         s->dy = 0;
         s->dz = 0;
+        trace_adb_mouse_flush();
         return 0;
     }
 
@@ -138,6 +139,7 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
             case ADB_CMD_CHANGE_ID_AND_ACT:
             case ADB_CMD_CHANGE_ID_AND_ENABLE:
                 d->devaddr = buf[1] & 0xf;
+                trace_adb_mouse_request_change_addr(d->devaddr);
                 break;
             default:
                 d->devaddr = buf[1] & 0xf;
@@ -155,6 +157,9 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
                 if (buf[2] == 1 || buf[2] == 2) {
                     d->handler = buf[2];
                 }
+
+                trace_adb_mouse_request_change_addr_and_handler(d->devaddr,
+                                                                d->handler);
                 break;
             }
         }
diff --git a/hw/input/trace-events b/hw/input/trace-events
index 5affabc81d..db72484a25 100644
--- a/hw/input/trace-events
+++ b/hw/input/trace-events
@@ -4,10 +4,15 @@
 adb_kbd_no_key(void) "Ignoring NO_KEY"
 adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
 adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
+adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x"
+adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
 
 # hw/input/adb-mouse.c
+adb_mouse_flush(void) "flush"
 adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x"
 adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x"
+adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x"
+adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x"
 
 # hw/input/ps2.c
 ps2_put_keycode(void *opaque, int keycode) "%p keycode 0x%02x"
diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c
index 171f5ed814..393fdd7326 100644
--- a/hw/intc/heathrow_pic.c
+++ b/hw/intc/heathrow_pic.c
@@ -25,78 +25,58 @@
 #include "qemu/osdep.h"
 #include "hw/hw.h"
 #include "hw/ppc/mac.h"
+#include "hw/intc/heathrow_pic.h"
+#include "trace.h"
 
-/* debug PIC */
-//#define DEBUG_PIC
-
-#ifdef DEBUG_PIC
-#define PIC_DPRINTF(fmt, ...)                                   \
-    do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define PIC_DPRINTF(fmt, ...)
-#endif
-
-typedef struct HeathrowPIC {
-    uint32_t events;
-    uint32_t mask;
-    uint32_t levels;
-    uint32_t level_triggered;
-} HeathrowPIC;
-
-typedef struct HeathrowPICS {
-    MemoryRegion mem;
-    HeathrowPIC pics[2];
-    qemu_irq *irqs;
-} HeathrowPICS;
-
-static inline int check_irq(HeathrowPIC *pic)
+static inline int heathrow_check_irq(HeathrowPICState *pic)
 {
     return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
 }
 
 /* update the CPU irq state */
-static void heathrow_pic_update(HeathrowPICS *s)
+static void heathrow_update_irq(HeathrowState *s)
 {
-    if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
+    if (heathrow_check_irq(&s->pics[0]) ||
+            heathrow_check_irq(&s->pics[1])) {
         qemu_irq_raise(s->irqs[0]);
     } else {
         qemu_irq_lower(s->irqs[0]);
     }
 }
 
-static void pic_write(void *opaque, hwaddr addr,
-                      uint64_t value, unsigned size)
+static void heathrow_write(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
 {
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
+    HeathrowState *s = opaque;
+    HeathrowPICState *pic;
     unsigned int n;
 
     n = ((addr & 0xfff) - 0x10) >> 4;
-    PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+    trace_heathrow_write(addr, n, value);
     if (n >= 2)
         return;
     pic = &s->pics[n];
     switch(addr & 0xf) {
     case 0x04:
         pic->mask = value;
-        heathrow_pic_update(s);
+        heathrow_update_irq(s);
         break;
     case 0x08:
         /* do not reset level triggered IRQs */
         value &= ~pic->level_triggered;
         pic->events &= ~value;
-        heathrow_pic_update(s);
+        heathrow_update_irq(s);
         break;
     default:
         break;
     }
 }
 
-static uint64_t pic_read(void *opaque, hwaddr addr,
-                         unsigned size)
+static uint64_t heathrow_read(void *opaque, hwaddr addr,
+                              unsigned size)
 {
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
+    HeathrowState *s = opaque;
+    HeathrowPICState *pic;
     unsigned int n;
     uint32_t value;
 
@@ -120,40 +100,39 @@ static uint64_t pic_read(void *opaque, hwaddr addr,
             break;
         }
     }
-    PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+    trace_heathrow_read(addr, n, value);
     return value;
 }
 
-static const MemoryRegionOps heathrow_pic_ops = {
-    .read = pic_read,
-    .write = pic_write,
+static const MemoryRegionOps heathrow_ops = {
+    .read = heathrow_read,
+    .write = heathrow_write,
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
-static void heathrow_pic_set_irq(void *opaque, int num, int level)
+static void heathrow_set_irq(void *opaque, int num, int level)
 {
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
+    HeathrowState *s = opaque;
+    HeathrowPICState *pic;
     unsigned int irq_bit;
+    int last_level;
 
-#if defined(DEBUG)
-    {
-        static int last_level[64];
-        if (last_level[num] != level) {
-            PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level);
-            last_level[num] = level;
-        }
-    }
-#endif
     pic = &s->pics[1 - (num >> 5)];
     irq_bit = 1 << (num & 0x1f);
+    last_level = (pic->levels & irq_bit) ? 1 : 0;
+
     if (level) {
         pic->events |= irq_bit & ~pic->level_triggered;
         pic->levels |= irq_bit;
     } else {
         pic->levels &= ~irq_bit;
     }
-    heathrow_pic_update(s);
+
+    if (last_level != level) {
+        trace_heathrow_set_irq(num, level);
+    }
+
+    heathrow_update_irq(s);
 }
 
 static const VMStateDescription vmstate_heathrow_pic_one = {
@@ -161,54 +140,81 @@ static const VMStateDescription vmstate_heathrow_pic_one = {
     .version_id = 0,
     .minimum_version_id = 0,
     .fields = (VMStateField[]) {
-        VMSTATE_UINT32(events, HeathrowPIC),
-        VMSTATE_UINT32(mask, HeathrowPIC),
-        VMSTATE_UINT32(levels, HeathrowPIC),
-        VMSTATE_UINT32(level_triggered, HeathrowPIC),
+        VMSTATE_UINT32(events, HeathrowPICState),
+        VMSTATE_UINT32(mask, HeathrowPICState),
+        VMSTATE_UINT32(levels, HeathrowPICState),
+        VMSTATE_UINT32(level_triggered, HeathrowPICState),
         VMSTATE_END_OF_LIST()
     }
 };
 
-static const VMStateDescription vmstate_heathrow_pic = {
+static const VMStateDescription vmstate_heathrow = {
     .name = "heathrow_pic",
     .version_id = 1,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1,
-                             vmstate_heathrow_pic_one, HeathrowPIC),
+        VMSTATE_STRUCT_ARRAY(pics, HeathrowState, 2, 1,
+                             vmstate_heathrow_pic_one, HeathrowPICState),
         VMSTATE_END_OF_LIST()
     }
 };
 
-static void heathrow_pic_reset_one(HeathrowPIC *s)
+static void heathrow_reset(DeviceState *d)
 {
-    memset(s, '\0', sizeof(HeathrowPIC));
+    HeathrowState *s = HEATHROW(d);
+
+    s->pics[0].level_triggered = 0;
+    s->pics[1].level_triggered = 0x1ff00000;
 }
 
-static void heathrow_pic_reset(void *opaque)
+static void heathrow_init(Object *obj)
 {
-    HeathrowPICS *s = opaque;
-
-    heathrow_pic_reset_one(&s->pics[0]);
-    heathrow_pic_reset_one(&s->pics[1]);
+    HeathrowState *s = HEATHROW(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
-    s->pics[0].level_triggered = 0;
-    s->pics[1].level_triggered = 0x1ff00000;
+    memory_region_init_io(&s->mem, OBJECT(s), &heathrow_ops, s,
+                          "heathrow-pic", 0x1000);
+    sysbus_init_mmio(sbd, &s->mem);
 }
 
-qemu_irq *heathrow_pic_init(MemoryRegion **pmem,
-                            int nb_cpus, qemu_irq **irqs)
+DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs,
+                               qemu_irq **pic_irqs)
 {
-    HeathrowPICS *s;
+    DeviceState *d;
+    HeathrowState *s;
 
-    s = g_malloc0(sizeof(HeathrowPICS));
+    d = qdev_create(NULL, TYPE_HEATHROW);
+    qdev_init_nofail(d);
+
+    s = HEATHROW(d);
     /* only 1 CPU */
     s->irqs = irqs[0];
-    memory_region_init_io(&s->mem, NULL, &heathrow_pic_ops, s,
-                          "heathrow-pic", 0x1000);
-    *pmem = &s->mem;
 
-    vmstate_register(NULL, -1, &vmstate_heathrow_pic, s);
-    qemu_register_reset(heathrow_pic_reset, s);
-    return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64);
+    *pic_irqs = qemu_allocate_irqs(heathrow_set_irq, s, HEATHROW_NUM_IRQS);
+
+    return d;
+}
+
+static void heathrow_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->reset = heathrow_reset;
+    dc->vmsd = &vmstate_heathrow;
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 }
+
+static const TypeInfo heathrow_type_info = {
+    .name = TYPE_HEATHROW,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(HeathrowState),
+    .instance_init = heathrow_init,
+    .class_init = heathrow_class_init,
+};
+
+static void heathrow_register_types(void)
+{
+    type_register_static(&heathrow_type_info);
+}
+
+type_init(heathrow_register_types)
diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c
index 9159a06f07..811cee9b26 100644
--- a/hw/intc/openpic.c
+++ b/hw/intc/openpic.c
@@ -63,10 +63,6 @@ static int get_current_cpu(void);
         } \
     } while (0)
 
-#define MAX_CPU     32
-#define MAX_MSI     8
-#define VID         0x03 /* MPIC version ID */
-
 /* OpenPIC capability flags */
 #define OPENPIC_FLAG_IDR_CRIT     (1 << 0)
 #define OPENPIC_FLAG_ILR          (2 << 0)
@@ -85,35 +81,6 @@ static int get_current_cpu(void);
 #define OPENPIC_CPU_REG_START        0x20000
 #define OPENPIC_CPU_REG_SIZE         0x100 + ((MAX_CPU - 1) * 0x1000)
 
-/* Raven */
-#define RAVEN_MAX_CPU      2
-#define RAVEN_MAX_EXT     48
-#define RAVEN_MAX_IRQ     64
-#define RAVEN_MAX_TMR      OPENPIC_MAX_TMR
-#define RAVEN_MAX_IPI      OPENPIC_MAX_IPI
-
-/* KeyLargo */
-#define KEYLARGO_MAX_CPU  4
-#define KEYLARGO_MAX_EXT  64
-#define KEYLARGO_MAX_IPI  4
-#define KEYLARGO_MAX_IRQ  (64 + KEYLARGO_MAX_IPI)
-#define KEYLARGO_MAX_TMR  0
-#define KEYLARGO_IPI_IRQ  (KEYLARGO_MAX_EXT) /* First IPI IRQ */
-/* Timers don't exist but this makes the code happy... */
-#define KEYLARGO_TMR_IRQ  (KEYLARGO_IPI_IRQ + KEYLARGO_MAX_IPI)
-
-/* Interrupt definitions */
-#define RAVEN_FE_IRQ     (RAVEN_MAX_EXT)     /* Internal functional IRQ */
-#define RAVEN_ERR_IRQ    (RAVEN_MAX_EXT + 1) /* Error IRQ */
-#define RAVEN_TMR_IRQ    (RAVEN_MAX_EXT + 2) /* First timer IRQ */
-#define RAVEN_IPI_IRQ    (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */
-/* First doorbell IRQ */
-#define RAVEN_DBL_IRQ    (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI))
-
-typedef struct FslMpicInfo {
-    int max_ext;
-} FslMpicInfo;
-
 static FslMpicInfo fsl_mpic_20 = {
     .max_ext = 12,
 };
@@ -211,55 +178,6 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
                                        uint32_t val, int idx);
 static void openpic_reset(DeviceState *d);
 
-typedef enum IRQType {
-    IRQ_TYPE_NORMAL = 0,
-    IRQ_TYPE_FSLINT,        /* FSL internal interrupt -- level only */
-    IRQ_TYPE_FSLSPECIAL,    /* FSL timer/IPI interrupt, edge, no polarity */
-} IRQType;
-
-/* Round up to the nearest 64 IRQs so that the queue length
- * won't change when moving between 32 and 64 bit hosts.
- */
-#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63)
-
-typedef struct IRQQueue {
-    unsigned long *queue;
-    int32_t queue_size; /* Only used for VMSTATE_BITMAP */
-    int next;
-    int priority;
-} IRQQueue;
-
-typedef struct IRQSource {
-    uint32_t ivpr;  /* IRQ vector/priority register */
-    uint32_t idr;   /* IRQ destination register */
-    uint32_t destmask; /* bitmap of CPU destinations */
-    int last_cpu;
-    int output;     /* IRQ level, e.g. OPENPIC_OUTPUT_INT */
-    int pending;    /* TRUE if IRQ is pending */
-    IRQType type;
-    bool level:1;   /* level-triggered */
-    bool nomask:1;  /* critical interrupts ignore mask on some FSL MPICs */
-} IRQSource;
-
-#define IVPR_MASK_SHIFT       31
-#define IVPR_MASK_MASK        (1U << IVPR_MASK_SHIFT)
-#define IVPR_ACTIVITY_SHIFT   30
-#define IVPR_ACTIVITY_MASK    (1U << IVPR_ACTIVITY_SHIFT)
-#define IVPR_MODE_SHIFT       29
-#define IVPR_MODE_MASK        (1U << IVPR_MODE_SHIFT)
-#define IVPR_POLARITY_SHIFT   23
-#define IVPR_POLARITY_MASK    (1U << IVPR_POLARITY_SHIFT)
-#define IVPR_SENSE_SHIFT      22
-#define IVPR_SENSE_MASK       (1U << IVPR_SENSE_SHIFT)
-
-#define IVPR_PRIORITY_MASK     (0xFU << 16)
-#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16))
-#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask)
-
-/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */
-#define IDR_EP      0x80000000  /* external pin */
-#define IDR_CI      0x40000000  /* critical interrupt */
-
 /* Convert between openpic clock ticks and nanosecs.  In the hardware the clock
    frequency is driven by board inputs to the PIC which the PIC would then
    divide by 4 or 8.  For now hard code to 25MZ.
@@ -275,81 +193,6 @@ static inline uint64_t ticks_to_ns(uint64_t ticks)
     return ticks * OPENPIC_TIMER_NS_PER_TICK;
 }
 
-typedef struct OpenPICTimer {
-    uint32_t tccr;  /* Global timer current count register */
-    uint32_t tbcr;  /* Global timer base count register */
-    int                   n_IRQ;
-    bool                  qemu_timer_active; /* Is the qemu_timer is running? */
-    struct QEMUTimer     *qemu_timer;
-    struct OpenPICState  *opp;          /* Device timer is part of. */
-    /* The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last
-       current_count written or read, only defined if qemu_timer_active. */
-    uint64_t              origin_time;
-} OpenPICTimer;
-
-typedef struct OpenPICMSI {
-    uint32_t msir;   /* Shared Message Signaled Interrupt Register */
-} OpenPICMSI;
-
-typedef struct IRQDest {
-    int32_t ctpr; /* CPU current task priority */
-    IRQQueue raised;
-    IRQQueue servicing;
-    qemu_irq *irqs;
-
-    /* Count of IRQ sources asserting on non-INT outputs */
-    uint32_t outputs_active[OPENPIC_OUTPUT_NB];
-} IRQDest;
-
-#define OPENPIC(obj) OBJECT_CHECK(OpenPICState, (obj), TYPE_OPENPIC)
-
-typedef struct OpenPICState {
-    /*< private >*/
-    SysBusDevice parent_obj;
-    /*< public >*/
-
-    MemoryRegion mem;
-
-    /* Behavior control */
-    FslMpicInfo *fsl;
-    uint32_t model;
-    uint32_t flags;
-    uint32_t nb_irqs;
-    uint32_t vid;
-    uint32_t vir; /* Vendor identification register */
-    uint32_t vector_mask;
-    uint32_t tfrr_reset;
-    uint32_t ivpr_reset;
-    uint32_t idr_reset;
-    uint32_t brr1;
-    uint32_t mpic_mode_mask;
-
-    /* Sub-regions */
-    MemoryRegion sub_io_mem[6];
-
-    /* Global registers */
-    uint32_t frr; /* Feature reporting register */
-    uint32_t gcr; /* Global configuration register  */
-    uint32_t pir; /* Processor initialization register */
-    uint32_t spve; /* Spurious vector register */
-    uint32_t tfrr; /* Timer frequency reporting register */
-    /* Source registers */
-    IRQSource src[OPENPIC_MAX_IRQ];
-    /* Local registers per output pin */
-    IRQDest dst[MAX_CPU];
-    uint32_t nb_cpus;
-    /* Timer registers */
-    OpenPICTimer timers[OPENPIC_MAX_TMR];
-    uint32_t max_tmr;
-
-    /* Shared MSI registers */
-    OpenPICMSI msi[MAX_MSI];
-    uint32_t max_irq;
-    uint32_t irq_ipi0;
-    uint32_t irq_tim0;
-    uint32_t irq_msi;
-} OpenPICState;
-
 static inline void IRQ_setbit(IRQQueue *q, int n_IRQ)
 {
     set_bit(n_IRQ, q->queue);
diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c
index fa83420254..f1a59e5a85 100644
--- a/hw/intc/openpic_kvm.c
+++ b/hw/intc/openpic_kvm.c
@@ -30,6 +30,7 @@
 #include "exec/address-spaces.h"
 #include "hw/hw.h"
 #include "hw/ppc/openpic.h"
+#include "hw/ppc/openpic_kvm.h"
 #include "hw/pci/msi.h"
 #include "hw/sysbus.h"
 #include "sysemu/kvm.h"
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 4092d2825e..55e8c2570c 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -186,3 +186,8 @@ nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)"
 nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
 nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
 nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
+
+# hw/intc/heathrow_pic.c
+heathrow_write(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64
+heathrow_read(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64
+heathrow_set_irq(int num, int level) "set_irq: num=0x%02x level=%d"
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index 024f8557ab..af1bd46b4b 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -30,48 +30,11 @@
 #include "hw/pci/pci.h"
 #include "hw/ppc/mac_dbdma.h"
 #include "hw/char/escc.h"
+#include "hw/misc/macio/macio.h"
+#include "hw/intc/heathrow_pic.h"
 
-#define TYPE_MACIO "macio"
-#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO)
-
-typedef struct MacIOState
-{
-    /*< private >*/
-    PCIDevice parent;
-    /*< public >*/
-
-    MemoryRegion bar;
-    CUDAState cuda;
-    DBDMAState *dbdma;
-    MemoryRegion *pic_mem;
-    MemoryRegion *escc_mem;
-    uint64_t frequency;
-} MacIOState;
-
-#define OLDWORLD_MACIO(obj) \
-    OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO)
-
-typedef struct OldWorldMacIOState {
-    /*< private >*/
-    MacIOState parent_obj;
-    /*< public >*/
-
-    qemu_irq irqs[5];
-
-    MacIONVRAMState nvram;
-    MACIOIDEState ide[2];
-} OldWorldMacIOState;
-
-#define NEWWORLD_MACIO(obj) \
-    OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO)
-
-typedef struct NewWorldMacIOState {
-    /*< private >*/
-    MacIOState parent_obj;
-    /*< public >*/
-    qemu_irq irqs[5];
-    MACIOIDEState ide[2];
-} NewWorldMacIOState;
+/* Note: this code is strongly inspirated from the corresponding code
+ * in PearPC */
 
 /*
  * The mac-io has two interfaces to the ESCC. One is called "escc-legacy",
@@ -84,10 +47,12 @@ typedef struct NewWorldMacIOState {
  *
  * Reference: ftp://ftp.software.ibm.com/rs6000/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf
  */
-static void macio_escc_legacy_setup(MacIOState *macio_state)
+static void macio_escc_legacy_setup(MacIOState *s)
 {
+    ESCCState *escc = ESCC(&s->escc);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(escc);
     MemoryRegion *escc_legacy = g_new(MemoryRegion, 1);
-    MemoryRegion *bar = &macio_state->bar;
+    MemoryRegion *bar = &s->bar;
     int i;
     static const int maps[] = {
         0x00, 0x00, /* Command B */
@@ -102,25 +67,26 @@ static void macio_escc_legacy_setup(MacIOState *macio_state)
         0xb0, 0xb0, /* Detect AB */
     };
 
-    memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256);
+    memory_region_init(escc_legacy, OBJECT(s), "escc-legacy", 256);
     for (i = 0; i < ARRAY_SIZE(maps); i += 2) {
         MemoryRegion *port = g_new(MemoryRegion, 1);
-        memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port",
-                                 macio_state->escc_mem, maps[i+1], 0x2);
+        memory_region_init_alias(port, OBJECT(s), "escc-legacy-port",
+                                 sysbus_mmio_get_region(sbd, 0),
+                                 maps[i + 1], 0x2);
         memory_region_add_subregion(escc_legacy, maps[i], port);
     }
 
     memory_region_add_subregion(bar, 0x12000, escc_legacy);
 }
 
-static void macio_bar_setup(MacIOState *macio_state)
+static void macio_bar_setup(MacIOState *s)
 {
-    MemoryRegion *bar = &macio_state->bar;
+    ESCCState *escc = ESCC(&s->escc);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(escc);
+    MemoryRegion *bar = &s->bar;
 
-    if (macio_state->escc_mem) {
-        memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem);
-        macio_escc_legacy_setup(macio_state);
-    }
+    memory_region_add_subregion(bar, 0x13000, sysbus_mmio_get_region(sbd, 0));
+    macio_escc_legacy_setup(s);
 }
 
 static void macio_common_realize(PCIDevice *d, Error **errp)
@@ -129,15 +95,17 @@ static void macio_common_realize(PCIDevice *d, Error **errp)
     SysBusDevice *sysbus_dev;
     Error *err = NULL;
 
-    object_property_set_bool(OBJECT(s->dbdma), true, "realized", &err);
+    object_property_set_bool(OBJECT(&s->dbdma), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
         return;
     }
-    sysbus_dev = SYS_BUS_DEVICE(s->dbdma);
+    sysbus_dev = SYS_BUS_DEVICE(&s->dbdma);
     memory_region_add_subregion(&s->bar, 0x08000,
                                 sysbus_mmio_get_region(sysbus_dev, 0));
 
+    qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency",
+                         s->frequency);
     object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
@@ -147,6 +115,12 @@ static void macio_common_realize(PCIDevice *d, Error **errp)
     memory_region_add_subregion(&s->bar, 0x16000,
                                 sysbus_mmio_get_region(sysbus_dev, 0));
 
+    object_property_set_bool(OBJECT(&s->escc), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
     macio_bar_setup(s);
     pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
 }
@@ -161,7 +135,7 @@ static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide,
     sysbus_connect_irq(sysbus_dev, 0, irq0);
     sysbus_connect_irq(sysbus_dev, 1, irq1);
     qdev_prop_set_uint32(DEVICE(ide), "channel", dmaid);
-    object_property_set_link(OBJECT(ide), OBJECT(s->dbdma), "dbdma", errp);
+    object_property_set_link(OBJECT(ide), OBJECT(&s->dbdma), "dbdma", errp);
     macio_ide_register_dma(ide);
 
     object_property_set_bool(OBJECT(ide), true, "realized", errp);
@@ -185,6 +159,10 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp)
     sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
     sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
 
+    sysbus_dev = SYS_BUS_DEVICE(&s->escc);
+    sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
+    sysbus_connect_irq(sysbus_dev, 1, os->irqs[cur_irq++]);
+
     object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err);
     if (err) {
         error_propagate(errp, err);
@@ -195,10 +173,10 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp)
                                 sysbus_mmio_get_region(sysbus_dev, 0));
     pmac_format_nvram_partition(&os->nvram, os->nvram.size);
 
-    if (s->pic_mem) {
-        /* Heathrow PIC */
-        memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem);
-    }
+    /* Heathrow PIC */
+    sysbus_dev = SYS_BUS_DEVICE(os->pic);
+    memory_region_add_subregion(&s->bar, 0x0,
+                                sysbus_mmio_get_region(sysbus_dev, 0));
 
     /* IDE buses */
     for (i = 0; i < ARRAY_SIZE(os->ide); i++) {
@@ -236,6 +214,11 @@ static void macio_oldworld_init(Object *obj)
 
     qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs));
 
+    object_property_add_link(obj, "pic", TYPE_HEATHROW,
+                             (Object **) &os->pic,
+                             qdev_prop_allow_set_link_before_realize,
+                             0, NULL);
+
     object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM);
     dev = DEVICE(&os->nvram);
     qdev_prop_set_uint32(dev, "size", 0x2000);
@@ -297,10 +280,14 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp)
     sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
     sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]);
 
-    if (s->pic_mem) {
-        /* OpenPIC */
-        memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem);
-    }
+    sysbus_dev = SYS_BUS_DEVICE(&s->escc);
+    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]);
+    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[cur_irq++]);
+
+    /* OpenPIC */
+    sysbus_dev = SYS_BUS_DEVICE(ns->pic);
+    memory_region_add_subregion(&s->bar, 0x40000,
+                                sysbus_mmio_get_region(sysbus_dev, 0));
 
     /* IDE buses */
     for (i = 0; i < ARRAY_SIZE(ns->ide); i++) {
@@ -329,6 +316,11 @@ static void macio_newworld_init(Object *obj)
 
     qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs));
 
+    object_property_add_link(obj, "pic", TYPE_OPENPIC,
+                             (Object **) &ns->pic,
+                             qdev_prop_allow_set_link_before_realize,
+                             0, NULL);
+
     for (i = 0; i < 2; i++) {
         macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i);
     }
@@ -344,8 +336,20 @@ static void macio_instance_init(Object *obj)
     qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
     object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
 
-    s->dbdma = MAC_DBDMA(object_new(TYPE_MAC_DBDMA));
-    object_property_add_child(obj, "dbdma", OBJECT(s->dbdma), NULL);
+    object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA);
+    qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default());
+    object_property_add_child(obj, "dbdma", OBJECT(&s->dbdma), NULL);
+
+    object_initialize(&s->escc, sizeof(s->escc), TYPE_ESCC);
+    qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0);
+    qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK);
+    qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4);
+    qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hds[0]);
+    qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hds[1]);
+    qdev_prop_set_uint32(DEVICE(&s->escc), "chnBtype", escc_serial);
+    qdev_prop_set_uint32(DEVICE(&s->escc), "chnAtype", escc_serial);
+    qdev_set_parent_bus(DEVICE(&s->escc), sysbus_get_default());
+    object_property_add_child(obj, "escc", OBJECT(&s->escc), NULL);
 }
 
 static const VMStateDescription vmstate_macio_oldworld = {
@@ -441,19 +445,3 @@ static void macio_register_types(void)
 }
 
 type_init(macio_register_types)
-
-void macio_init(PCIDevice *d,
-                MemoryRegion *pic_mem,
-                MemoryRegion *escc_mem)
-{
-    MacIOState *macio_state = MACIO(d);
-
-    macio_state->pic_mem = pic_mem;
-    macio_state->escc_mem = escc_mem;
-    /* Note: this code is strongly inspirated from the corresponding code
-       in PearPC */
-    qdev_prop_set_uint64(DEVICE(&macio_state->cuda), "timebase-frequency",
-                         macio_state->frequency);
-
-    qdev_init_nofail(DEVICE(d));
-}
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index ad1928c5d8..86d82a6ec3 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -13,7 +13,8 @@ endif
 obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
 # PowerPC 4xx boards
 obj-y += ppc4xx_devs.o ppc405_uc.o
-obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o ppc440_bamboo.o
+obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o
+obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o
 # PReP
 obj-$(CONFIG_PREP) += prep.o
 obj-$(CONFIG_PREP) += prep_systemio.o
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index a40d3ec3e3..43c15d18c4 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -29,6 +29,7 @@
 #include "kvm_ppc.h"
 #include "sysemu/device_tree.h"
 #include "hw/ppc/openpic.h"
+#include "hw/ppc/openpic_kvm.h"
 #include "hw/ppc/ppc.h"
 #include "hw/loader.h"
 #include "elf.h"
@@ -119,7 +120,14 @@ static void dt_serial_create(void *fdt, unsigned long long offset,
     qemu_fdt_setprop_string(fdt, "/aliases", alias, ser);
 
     if (defcon) {
+        /*
+         * "linux,stdout-path" and "stdout" properties are deprecated by linux
+         * kernel. New platforms should only use the "stdout-path" property. Set
+         * the new property and continue using older property to remain
+         * compatible with the existing firmware.
+         */
         qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser);
+        qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", ser);
     }
 }
 
@@ -784,8 +792,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     int initrd_size = 0;
     hwaddr cur_base = 0;
     char *filename;
+    const char *payload_name;
+    bool kernel_as_payload;
     hwaddr bios_entry = 0;
-    target_long bios_size;
+    target_long payload_size;
     struct boot_info *boot_info;
     int dt_size;
     int i;
@@ -913,11 +923,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     /* Register spinning region */
     sysbus_create_simple("e500-spin", params->spin_base, NULL);
 
-    if (cur_base < (32 * 1024 * 1024)) {
-        /* u-boot occupies memory up to 32MB, so load blobs above */
-        cur_base = (32 * 1024 * 1024);
-    }
-
     if (params->has_mpc8xxx_gpio) {
         qemu_irq poweroff_irq;
 
@@ -952,8 +957,61 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                                     sysbus_mmio_get_region(s, 0));
     }
 
-    /* Load kernel. */
-    if (machine->kernel_filename) {
+    /*
+     * Smart firmware defaults ahead!
+     *
+     * We follow the following table to select which payload we execute.
+     *
+     *  -kernel | -bios | payload
+     * ---------+-------+---------
+     *     N    |   Y   | u-boot
+     *     N    |   N   | u-boot
+     *     Y    |   Y   | u-boot
+     *     Y    |   N   | kernel
+     *
+     * This ensures backwards compatibility with how we used to expose
+     * -kernel to users but allows them to run through u-boot as well.
+     */
+    kernel_as_payload = false;
+    if (bios_name == NULL) {
+        if (machine->kernel_filename) {
+            payload_name = machine->kernel_filename;
+            kernel_as_payload = true;
+        } else {
+            payload_name = "u-boot.e500";
+        }
+    } else {
+        payload_name = bios_name;
+    }
+
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, payload_name);
+
+    payload_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL,
+                            1, PPC_ELF_MACHINE, 0, 0);
+    if (payload_size < 0) {
+        /*
+         * Hrm. No ELF image? Try a uImage, maybe someone is giving us an
+         * ePAPR compliant kernel
+         */
+        payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL,
+                                   NULL, NULL);
+        if (payload_size < 0) {
+            error_report("qemu: could not load firmware '%s'", filename);
+            exit(1);
+        }
+    }
+
+    g_free(filename);
+
+    if (kernel_as_payload) {
+        kernel_base = loadaddr;
+        kernel_size = payload_size;
+    }
+
+    cur_base = loadaddr + payload_size;
+
+    /* Load bare kernel only if no bios/u-boot has been provided */
+    if (machine->kernel_filename && !kernel_as_payload) {
         kernel_base = cur_base;
         kernel_size = load_image_targphys(machine->kernel_filename,
                                           cur_base,
@@ -967,6 +1025,11 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         cur_base += kernel_size;
     }
 
+    if (cur_base < (32 * 1024 * 1024)) {
+        /* u-boot occupies memory up to 32MB, so load blobs above */
+        cur_base = (32 * 1024 * 1024);
+    }
+
     /* Load initrd. */
     if (machine->initrd_filename) {
         initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK;
@@ -983,47 +1046,16 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     }
 
     /*
-     * Smart firmware defaults ahead!
-     *
-     * We follow the following table to select which payload we execute.
-     *
-     *  -kernel | -bios | payload
-     * ---------+-------+---------
-     *     N    |   Y   | u-boot
-     *     N    |   N   | u-boot
-     *     Y    |   Y   | u-boot
-     *     Y    |   N   | kernel
-     *
-     * This ensures backwards compatibility with how we used to expose
-     * -kernel to users but allows them to run through u-boot as well.
+     * Reserve space for dtb behind the kernel image because Linux has a bug
+     * where it can only handle the dtb if it's within the first 64MB of where
+     * <kernel> starts. dtb cannot not reach initrd_base because INITRD_LOAD_PAD
+     * ensures enough space between kernel and initrd.
      */
-    if (bios_name == NULL) {
-        if (machine->kernel_filename) {
-            bios_name = machine->kernel_filename;
-        } else {
-            bios_name = "u-boot.e500";
-        }
-    }
-    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
-
-    bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL,
-                         1, PPC_ELF_MACHINE, 0, 0);
-    if (bios_size < 0) {
-        /*
-         * Hrm. No ELF image? Try a uImage, maybe someone is giving us an
-         * ePAPR compliant kernel
-         */
-        kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL,
-                                  NULL, NULL);
-        if (kernel_size < 0) {
-            error_report("could not load firmware '%s'", filename);
+    dt_base = (loadaddr + payload_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
+    if (dt_base + DTB_MAX_SIZE > ram_size) {
+            error_report("qemu: not enough memory for device tree");
             exit(1);
-        }
     }
-    g_free(filename);
-
-    /* Reserve space for dtb */
-    dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
 
     dt_size = ppce500_prep_device_tree(machine, params, dt_base,
                                        initrd_base, initrd_size,
diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h
index 4702194f3f..a02f797598 100644
--- a/hw/ppc/mac.h
+++ b/hw/ppc/mac.h
@@ -47,9 +47,6 @@
 
 
 /* MacIO */
-#define TYPE_OLDWORLD_MACIO "macio-oldworld"
-#define TYPE_NEWWORLD_MACIO "macio-newworld"
-
 #define TYPE_MACIO_IDE "macio-ide"
 #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE)
 
@@ -76,12 +73,11 @@ void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table);
 void macio_ide_register_dma(MACIOIDEState *ide);
 
 void macio_init(PCIDevice *dev,
-                MemoryRegion *pic_mem,
-                MemoryRegion *escc_mem);
+                MemoryRegion *pic_mem);
 
 /* Heathrow PIC */
-qemu_irq *heathrow_pic_init(MemoryRegion **pmem,
-                            int nb_cpus, qemu_irq **irqs);
+DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs,
+                               qemu_irq **pic_irqs);
 
 /* Grackle PCI */
 #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost"
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 4e1298ee50..a749e2565d 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -60,6 +60,7 @@
 #include "hw/boards.h"
 #include "hw/nvram/fw_cfg.h"
 #include "hw/char/escc.h"
+#include "hw/misc/macio/macio.h"
 #include "hw/ppc/openpic.h"
 #include "hw/ide.h"
 #include "hw/loader.h"
@@ -153,20 +154,18 @@ static void ppc_core99_init(MachineState *machine)
     hwaddr kernel_base, initrd_base, cmdline_base = 0;
     long kernel_size, initrd_size;
     PCIBus *pci_bus;
-    PCIDevice *macio;
+    NewWorldMacIOState *macio;
     MACIOIDEState *macio_ide;
     BusState *adb_bus;
     MacIONVRAMState *nvr;
     int bios_size, ndrv_size;
     uint8_t *ndrv_file;
-    MemoryRegion *pic_mem, *escc_mem;
-    MemoryRegion *escc_bar = g_new(MemoryRegion, 1);
     int ppc_boot_device;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     void *fw_cfg;
     int machine_arch;
     SysBusDevice *s;
-    DeviceState *dev;
+    DeviceState *dev, *pic_dev;
     int *token = g_new(int, 1);
     hwaddr nvram_addr = 0xFFF04000;
     uint64_t tbfreq;
@@ -333,11 +332,10 @@ static void ppc_core99_init(MachineState *machine)
 
     pic = g_new0(qemu_irq, 64);
 
-    dev = qdev_create(NULL, TYPE_OPENPIC);
-    qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_KEYLARGO);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    pic_mem = s->mmio[0].memory;
+    pic_dev = qdev_create(NULL, TYPE_OPENPIC);
+    qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO);
+    qdev_init_nofail(pic_dev);
+    s = SYS_BUS_DEVICE(pic_dev);
     k = 0;
     for (i = 0; i < smp_cpus; i++) {
         for (j = 0; j < OPENPIC_OUTPUT_NB; j++) {
@@ -346,7 +344,7 @@ static void ppc_core99_init(MachineState *machine)
     }
 
     for (i = 0; i < 64; i++) {
-        pic[i] = qdev_get_gpio_in(dev, i);
+        pic[i] = qdev_get_gpio_in(pic_dev, i);
     }
 
     if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) {
@@ -368,36 +366,20 @@ static void ppc_core99_init(MachineState *machine)
         tbfreq = TBFREQ;
     }
 
-    /* init basic PC hardware */
-
-    dev = qdev_create(NULL, TYPE_ESCC);
-    qdev_prop_set_uint32(dev, "disabled", 0);
-    qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK);
-    qdev_prop_set_uint32(dev, "it_shift", 4);
-    qdev_prop_set_chr(dev, "chrA", serial_hds[0]);
-    qdev_prop_set_chr(dev, "chrB", serial_hds[1]);
-    qdev_prop_set_uint32(dev, "chnAtype", escc_serial);
-    qdev_prop_set_uint32(dev, "chnBtype", escc_serial);
-    qdev_init_nofail(dev);
-
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_connect_irq(s, 0, pic[0x24]);
-    sysbus_connect_irq(s, 1, pic[0x25]);
-
-    escc_mem = &ESCC(s)->mmio;
-
-    memory_region_init_alias(escc_bar, NULL, "escc-bar",
-                             escc_mem, 0, memory_region_size(escc_mem));
-
-    macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO);
+    /* MacIO */
+    macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO));
     dev = DEVICE(macio);
     qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */
-    qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */
-    qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */
-    qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */
-    qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */
+    qdev_connect_gpio_out(dev, 1, pic[0x24]); /* ESCC-B */
+    qdev_connect_gpio_out(dev, 2, pic[0x25]); /* ESCC-A */
+    qdev_connect_gpio_out(dev, 3, pic[0x0d]); /* IDE */
+    qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */
+    qdev_connect_gpio_out(dev, 5, pic[0x0e]); /* IDE */
+    qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE DMA */
     qdev_prop_set_uint64(dev, "frequency", tbfreq);
-    macio_init(macio, pic_mem, escc_bar);
+    object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic",
+                             &error_abort);
+    qdev_init_nofail(dev);
 
     /* We only emulate 2 out of 3 IDE controllers for now */
     ide_drive_get(hd, ARRAY_SIZE(hd));
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index d0d21d2392..935493c966 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -37,6 +37,7 @@
 #include "hw/boards.h"
 #include "hw/nvram/fw_cfg.h"
 #include "hw/char/escc.h"
+#include "hw/misc/macio/macio.h"
 #include "hw/ide.h"
 #include "hw/loader.h"
 #include "elf.h"
@@ -92,19 +93,16 @@ static void ppc_heathrow_init(MachineState *machine)
     uint32_t kernel_base, initrd_base, cmdline_base = 0;
     int32_t kernel_size, initrd_size;
     PCIBus *pci_bus;
-    PCIDevice *macio;
+    OldWorldMacIOState *macio;
     MACIOIDEState *macio_ide;
-    DeviceState *dev;
+    DeviceState *dev, *pic_dev;
     BusState *adb_bus;
     int bios_size, ndrv_size;
     uint8_t *ndrv_file;
-    MemoryRegion *pic_mem;
-    MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1);
     uint16_t ppc_boot_device;
     DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
     void *fw_cfg;
     uint64_t tbfreq;
-    SysBusDevice *s;
 
     linux_boot = (kernel_filename != NULL);
 
@@ -259,46 +257,32 @@ static void ppc_heathrow_init(MachineState *machine)
         error_report("Only 6xx bus is supported on heathrow machine");
         exit(1);
     }
-    pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs);
+    pic_dev = heathrow_pic_init(1, heathrow_irqs, &pic);
     pci_bus = pci_grackle_init(0xfec00000, pic,
                                get_system_memory(),
                                get_system_io());
     pci_vga_init(pci_bus);
 
-    dev = qdev_create(NULL, TYPE_ESCC);
-    qdev_prop_set_uint32(dev, "disabled", 0);
-    qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK);
-    qdev_prop_set_uint32(dev, "it_shift", 4);
-    qdev_prop_set_chr(dev, "chrA", serial_hds[0]);
-    qdev_prop_set_chr(dev, "chrB", serial_hds[1]);
-    qdev_prop_set_uint32(dev, "chnBtype", escc_serial);
-    qdev_prop_set_uint32(dev, "chnAtype", escc_serial);
-    qdev_init_nofail(dev);
-
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_connect_irq(s, 0, pic[0x10]);
-    sysbus_connect_irq(s, 1, pic[0x0f]);
-
-    escc_mem = &ESCC(s)->mmio;
-
-    memory_region_init_alias(escc_bar, NULL, "escc-bar",
-                             escc_mem, 0, memory_region_size(escc_mem));
-
-    for(i = 0; i < nb_nics; i++)
+    for (i = 0; i < nb_nics; i++) {
         pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
-
+    }
 
     ide_drive_get(hd, ARRAY_SIZE(hd));
 
-    macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO);
+    /* MacIO */
+    macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO));
     dev = DEVICE(macio);
     qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */
-    qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE-0 */
-    qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */
-    qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */
-    qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */
+    qdev_connect_gpio_out(dev, 1, pic[0x10]); /* ESCC-B */
+    qdev_connect_gpio_out(dev, 2, pic[0x0F]); /* ESCC-A */
+    qdev_connect_gpio_out(dev, 3, pic[0x0D]); /* IDE-0 */
+    qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE-0 DMA */
+    qdev_connect_gpio_out(dev, 5, pic[0x0E]); /* IDE-1 */
+    qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE-1 DMA */
     qdev_prop_set_uint64(dev, "frequency", tbfreq);
-    macio_init(macio, pic_mem, escc_bar);
+    object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic",
+                             &error_abort);
+    qdev_init_nofail(dev);
 
     macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio),
                                                         "ide[0]"));
diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c
new file mode 100644
index 0000000000..ab2626a9de
--- /dev/null
+++ b/hw/ppc/ppc440_pcix.c
@@ -0,0 +1,528 @@
+/*
+ * Emulation of the ibm,plb-pcix PCI controller
+ * This is found in some 440 SoCs e.g. the 460EX.
+ *
+ * Copyright (c) 2016-2018 BALATON Zoltan
+ *
+ * Derived from ppc4xx_pci.c and pci-host/ppce500.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "hw/hw.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/ppc4xx.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "exec/address-spaces.h"
+#include "trace.h"
+
+struct PLBOutMap {
+    uint64_t la;
+    uint64_t pcia;
+    uint32_t sa;
+    MemoryRegion mr;
+};
+
+struct PLBInMap {
+    uint64_t sa;
+    uint64_t la;
+    MemoryRegion mr;
+};
+
+#define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host"
+#define PPC440_PCIX_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(PPC440PCIXState, (obj), TYPE_PPC440_PCIX_HOST_BRIDGE)
+
+#define PPC440_PCIX_NR_POMS 3
+#define PPC440_PCIX_NR_PIMS 3
+
+typedef struct PPC440PCIXState {
+    PCIHostState parent_obj;
+
+    PCIDevice *dev;
+    struct PLBOutMap pom[PPC440_PCIX_NR_POMS];
+    struct PLBInMap pim[PPC440_PCIX_NR_PIMS];
+    uint32_t sts;
+    qemu_irq irq[PCI_NUM_PINS];
+    AddressSpace bm_as;
+    MemoryRegion bm;
+
+    MemoryRegion container;
+    MemoryRegion iomem;
+    MemoryRegion busmem;
+} PPC440PCIXState;
+
+#define PPC440_REG_BASE     0x80000
+#define PPC440_REG_SIZE     0xff
+
+#define PCIC0_CFGADDR       0x0
+#define PCIC0_CFGDATA       0x4
+
+#define PCIX0_POM0LAL       0x68
+#define PCIX0_POM0LAH       0x6c
+#define PCIX0_POM0SA        0x70
+#define PCIX0_POM0PCIAL     0x74
+#define PCIX0_POM0PCIAH     0x78
+#define PCIX0_POM1LAL       0x7c
+#define PCIX0_POM1LAH       0x80
+#define PCIX0_POM1SA        0x84
+#define PCIX0_POM1PCIAL     0x88
+#define PCIX0_POM1PCIAH     0x8c
+#define PCIX0_POM2SA        0x90
+
+#define PCIX0_PIM0SAL       0x98
+#define PCIX0_PIM0LAL       0x9c
+#define PCIX0_PIM0LAH       0xa0
+#define PCIX0_PIM1SA        0xa4
+#define PCIX0_PIM1LAL       0xa8
+#define PCIX0_PIM1LAH       0xac
+#define PCIX0_PIM2SAL       0xb0
+#define PCIX0_PIM2LAL       0xb4
+#define PCIX0_PIM2LAH       0xb8
+#define PCIX0_PIM0SAH       0xf8
+#define PCIX0_PIM2SAH       0xfc
+
+#define PCIX0_STS           0xe0
+
+#define PCI_ALL_SIZE        (PPC440_REG_BASE + PPC440_REG_SIZE)
+
+static void ppc440_pcix_clear_region(MemoryRegion *parent,
+                                     MemoryRegion *mem)
+{
+    if (memory_region_is_mapped(mem)) {
+        memory_region_del_subregion(parent, mem);
+        object_unparent(OBJECT(mem));
+    }
+}
+
+/* DMA mapping */
+static void ppc440_pcix_update_pim(PPC440PCIXState *s, int idx)
+{
+    MemoryRegion *mem = &s->pim[idx].mr;
+    char *name;
+    uint64_t size;
+
+    /* Before we modify anything, unmap and destroy the region */
+    ppc440_pcix_clear_region(&s->bm, mem);
+
+    if (!(s->pim[idx].sa & 1)) {
+        /* Not enabled, nothing to do */
+        return;
+    }
+
+    name = g_strdup_printf("PCI Inbound Window %d", idx);
+    size = ~(s->pim[idx].sa & ~7ULL) + 1;
+    memory_region_init_alias(mem, OBJECT(s), name, get_system_memory(),
+                             s->pim[idx].la, size);
+    memory_region_add_subregion_overlap(&s->bm, 0, mem, -1);
+    g_free(name);
+
+    trace_ppc440_pcix_update_pim(idx, size, s->pim[idx].la);
+}
+
+/* BAR mapping */
+static void ppc440_pcix_update_pom(PPC440PCIXState *s, int idx)
+{
+    MemoryRegion *mem = &s->pom[idx].mr;
+    MemoryRegion *address_space_mem = get_system_memory();
+    char *name;
+    uint32_t size;
+
+    /* Before we modify anything, unmap and destroy the region */
+    ppc440_pcix_clear_region(address_space_mem, mem);
+
+    if (!(s->pom[idx].sa & 1)) {
+        /* Not enabled, nothing to do */
+        return;
+    }
+
+    name = g_strdup_printf("PCI Outbound Window %d", idx);
+    size = ~(s->pom[idx].sa & 0xfffffffe) + 1;
+    if (!size) {
+        size = 0xffffffff;
+    }
+    memory_region_init_alias(mem, OBJECT(s), name, &s->busmem,
+                             s->pom[idx].pcia, size);
+    memory_region_add_subregion(address_space_mem, s->pom[idx].la, mem);
+    g_free(name);
+
+    trace_ppc440_pcix_update_pom(idx, size, s->pom[idx].la, s->pom[idx].pcia);
+}
+
+static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr,
+                                   uint64_t val, unsigned size)
+{
+    struct PPC440PCIXState *s = opaque;
+
+    trace_ppc440_pcix_reg_read(addr, val);
+    switch (addr) {
+    case PCI_VENDOR_ID ... PCI_MAX_LAT:
+        stl_le_p(s->dev->config + addr, val);
+        break;
+
+    case PCIX0_POM0LAL:
+        s->pom[0].la &= 0xffffffff00000000ULL;
+        s->pom[0].la |= val;
+        ppc440_pcix_update_pom(s, 0);
+        break;
+    case PCIX0_POM0LAH:
+        s->pom[0].la &= 0xffffffffULL;
+        s->pom[0].la |= val << 32;
+        ppc440_pcix_update_pom(s, 0);
+        break;
+    case PCIX0_POM0SA:
+        s->pom[0].sa = val;
+        ppc440_pcix_update_pom(s, 0);
+        break;
+    case PCIX0_POM0PCIAL:
+        s->pom[0].pcia &= 0xffffffff00000000ULL;
+        s->pom[0].pcia |= val;
+        ppc440_pcix_update_pom(s, 0);
+        break;
+    case PCIX0_POM0PCIAH:
+        s->pom[0].pcia &= 0xffffffffULL;
+        s->pom[0].pcia |= val << 32;
+        ppc440_pcix_update_pom(s, 0);
+        break;
+    case PCIX0_POM1LAL:
+        s->pom[1].la &= 0xffffffff00000000ULL;
+        s->pom[1].la |= val;
+        ppc440_pcix_update_pom(s, 1);
+        break;
+    case PCIX0_POM1LAH:
+        s->pom[1].la &= 0xffffffffULL;
+        s->pom[1].la |= val << 32;
+        ppc440_pcix_update_pom(s, 1);
+        break;
+    case PCIX0_POM1SA:
+        s->pom[1].sa = val;
+        ppc440_pcix_update_pom(s, 1);
+        break;
+    case PCIX0_POM1PCIAL:
+        s->pom[1].pcia &= 0xffffffff00000000ULL;
+        s->pom[1].pcia |= val;
+        ppc440_pcix_update_pom(s, 1);
+        break;
+    case PCIX0_POM1PCIAH:
+        s->pom[1].pcia &= 0xffffffffULL;
+        s->pom[1].pcia |= val << 32;
+        ppc440_pcix_update_pom(s, 1);
+        break;
+    case PCIX0_POM2SA:
+        s->pom[2].sa = val;
+        break;
+
+    case PCIX0_PIM0SAL:
+        s->pim[0].sa &= 0xffffffff00000000ULL;
+        s->pim[0].sa |= val;
+        ppc440_pcix_update_pim(s, 0);
+        break;
+    case PCIX0_PIM0LAL:
+        s->pim[0].la &= 0xffffffff00000000ULL;
+        s->pim[0].la |= val;
+        ppc440_pcix_update_pim(s, 0);
+        break;
+    case PCIX0_PIM0LAH:
+        s->pim[0].la &= 0xffffffffULL;
+        s->pim[0].la |= val << 32;
+        ppc440_pcix_update_pim(s, 0);
+        break;
+    case PCIX0_PIM1SA:
+        s->pim[1].sa = val;
+        ppc440_pcix_update_pim(s, 1);
+        break;
+    case PCIX0_PIM1LAL:
+        s->pim[1].la &= 0xffffffff00000000ULL;
+        s->pim[1].la |= val;
+        ppc440_pcix_update_pim(s, 1);
+        break;
+    case PCIX0_PIM1LAH:
+        s->pim[1].la &= 0xffffffffULL;
+        s->pim[1].la |= val << 32;
+        ppc440_pcix_update_pim(s, 1);
+        break;
+    case PCIX0_PIM2SAL:
+        s->pim[2].sa &= 0xffffffff00000000ULL;
+        s->pim[2].sa = val;
+        ppc440_pcix_update_pim(s, 2);
+        break;
+    case PCIX0_PIM2LAL:
+        s->pim[2].la &= 0xffffffff00000000ULL;
+        s->pim[2].la |= val;
+        ppc440_pcix_update_pim(s, 2);
+        break;
+    case PCIX0_PIM2LAH:
+        s->pim[2].la &= 0xffffffffULL;
+        s->pim[2].la |= val << 32;
+        ppc440_pcix_update_pim(s, 2);
+        break;
+
+    case PCIX0_STS:
+        s->sts = val;
+        break;
+
+    case PCIX0_PIM0SAH:
+        s->pim[0].sa &= 0xffffffffULL;
+        s->pim[0].sa |= val << 32;
+        ppc440_pcix_update_pim(s, 0);
+        break;
+    case PCIX0_PIM2SAH:
+        s->pim[2].sa &= 0xffffffffULL;
+        s->pim[2].sa |= val << 32;
+        ppc440_pcix_update_pim(s, 2);
+        break;
+
+    default:
+        error_report("%s: unhandled PCI internal register 0x%lx", __func__,
+                     (unsigned long)addr);
+        break;
+    }
+}
+
+static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr,
+                                     unsigned size)
+{
+    struct PPC440PCIXState *s = opaque;
+    uint32_t val;
+
+    switch (addr) {
+    case PCI_VENDOR_ID ... PCI_MAX_LAT:
+        val = ldl_le_p(s->dev->config + addr);
+        break;
+
+    case PCIX0_POM0LAL:
+        val = s->pom[0].la;
+        break;
+    case PCIX0_POM0LAH:
+        val = s->pom[0].la >> 32;
+        break;
+    case PCIX0_POM0SA:
+        val = s->pom[0].sa;
+        break;
+    case PCIX0_POM0PCIAL:
+        val = s->pom[0].pcia;
+        break;
+    case PCIX0_POM0PCIAH:
+        val = s->pom[0].pcia >> 32;
+        break;
+    case PCIX0_POM1LAL:
+        val = s->pom[1].la;
+        break;
+    case PCIX0_POM1LAH:
+        val = s->pom[1].la >> 32;
+        break;
+    case PCIX0_POM1SA:
+        val = s->pom[1].sa;
+        break;
+    case PCIX0_POM1PCIAL:
+        val = s->pom[1].pcia;
+        break;
+    case PCIX0_POM1PCIAH:
+        val = s->pom[1].pcia >> 32;
+        break;
+    case PCIX0_POM2SA:
+        val = s->pom[2].sa;
+        break;
+
+    case PCIX0_PIM0SAL:
+        val = s->pim[0].sa;
+        break;
+    case PCIX0_PIM0LAL:
+        val = s->pim[0].la;
+        break;
+    case PCIX0_PIM0LAH:
+        val = s->pim[0].la >> 32;
+        break;
+    case PCIX0_PIM1SA:
+        val = s->pim[1].sa;
+        break;
+    case PCIX0_PIM1LAL:
+        val = s->pim[1].la;
+        break;
+    case PCIX0_PIM1LAH:
+        val = s->pim[1].la >> 32;
+        break;
+    case PCIX0_PIM2SAL:
+        val = s->pim[2].sa;
+        break;
+    case PCIX0_PIM2LAL:
+        val = s->pim[2].la;
+        break;
+    case PCIX0_PIM2LAH:
+        val = s->pim[2].la >> 32;
+        break;
+
+    case PCIX0_STS:
+        val = s->sts;
+        break;
+
+    case PCIX0_PIM0SAH:
+        val = s->pim[0].sa  >> 32;
+        break;
+    case PCIX0_PIM2SAH:
+        val = s->pim[2].sa  >> 32;
+        break;
+
+    default:
+        error_report("%s: invalid PCI internal register 0x%lx", __func__,
+                     (unsigned long)addr);
+        val = 0;
+    }
+
+    trace_ppc440_pcix_reg_read(addr, val);
+    return val;
+}
+
+static const MemoryRegionOps pci_reg_ops = {
+    .read = ppc440_pcix_reg_read4,
+    .write = ppc440_pcix_reg_write4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ppc440_pcix_reset(DeviceState *dev)
+{
+    struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev);
+    int i;
+
+    for (i = 0; i < PPC440_PCIX_NR_POMS; i++) {
+        ppc440_pcix_clear_region(get_system_memory(), &s->pom[i].mr);
+    }
+    for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) {
+        ppc440_pcix_clear_region(&s->bm, &s->pim[i].mr);
+    }
+    memset(s->pom, 0, sizeof(s->pom));
+    memset(s->pim, 0, sizeof(s->pim));
+    for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) {
+        s->pim[i].sa = 0xffffffff00000000ULL;
+    }
+    s->sts = 0;
+}
+
+/* All pins from each slot are tied to a single board IRQ.
+ * This may need further refactoring for other boards. */
+static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    int slot = pci_dev->devfn >> 3;
+    trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, slot);
+    return slot - 1;
+}
+
+static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pci_irqs = opaque;
+
+    trace_ppc440_pcix_set_irq(irq_num);
+    if (irq_num < 0) {
+        error_report("%s: PCI irq %d", __func__, irq_num);
+        return;
+    }
+    qemu_set_irq(pci_irqs[irq_num], level);
+}
+
+static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn)
+{
+    PPC440PCIXState *s = opaque;
+
+    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)
+{
+    PCIHostState *s = opaque;
+    pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+}
+
+static uint64_t pci_host_data_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);
+    return val;
+}
+
+const MemoryRegionOps ppc440_pcix_host_data_ops = {
+    .read = pci_host_data_read,
+    .write = pci_host_data_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int ppc440_pcix_initfn(SysBusDevice *dev)
+{
+    PPC440PCIXState *s;
+    PCIHostState *h;
+    int i;
+
+    h = PCI_HOST_BRIDGE(dev);
+    s = PPC440_PCIX_HOST_BRIDGE(dev);
+
+    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+
+    memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX);
+    h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq,
+                         ppc440_pcix_map_irq, s->irq, &s->busmem,
+                         get_system_io(), PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS);
+
+    s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge");
+
+    memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX);
+    memory_region_add_subregion(&s->bm, 0x0, &s->busmem);
+    address_space_init(&s->bm_as, &s->bm, "pci-bm");
+    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,
+                          h, "pci-conf-idx", 4);
+    memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops,
+                          h, "pci-conf-data", 4);
+    memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s,
+                          "pci.reg", PPC440_REG_SIZE);
+    memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem);
+    memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem);
+    memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem);
+    sysbus_init_mmio(dev, &s->container);
+
+    return 0;
+}
+
+static void ppc440_pcix_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = ppc440_pcix_initfn;
+    dc->reset = ppc440_pcix_reset;
+}
+
+static const TypeInfo ppc440_pcix_info = {
+    .name          = TYPE_PPC440_PCIX_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(PPC440PCIXState),
+    .class_init    = ppc440_pcix_class_init,
+};
+
+static void ppc440_pcix_register_types(void)
+{
+    type_register_static(&ppc440_pcix_info);
+}
+
+type_init(ppc440_pcix_register_types)
diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c
index 4e2523a64f..976ab2b5d8 100644
--- a/hw/ppc/ppc440_uc.c
+++ b/hw/ppc/ppc440_uc.c
@@ -1050,6 +1050,9 @@ static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp)
     case DCRN_PCIE1_BASE:
         id = 1;
         break;
+    default:
+        error_setg(errp, "invalid PCIe DCRN base");
+        return;
     }
     snprintf(buf, sizeof(buf), "pcie%d-io", id);
     memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX);
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
new file mode 100644
index 0000000000..70b8e76d9c
--- /dev/null
+++ b/hw/ppc/sam460ex.c
@@ -0,0 +1,603 @@
+/*
+ * QEMU aCube Sam460ex board emulation
+ *
+ * Copyright (c) 2012 François Revol
+ * Copyright (c) 2016-2018 BALATON Zoltan
+ *
+ * This file is derived from hw/ppc440_bamboo.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "sysemu/blockdev.h"
+#include "hw/boards.h"
+#include "sysemu/kvm.h"
+#include "kvm_ppc.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/block-backend.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "hw/ppc/ppc440.h"
+#include "hw/ppc/ppc405.h"
+#include "hw/block/flash.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "hw/i2c/ppc4xx_i2c.h"
+#include "hw/i2c/smbus.h"
+#include "hw/usb/hcd-ehci.h"
+
+#define BINARY_DEVICE_TREE_FILE "canyonlands.dtb"
+#define UBOOT_FILENAME "u-boot-sam460-20100605.bin"
+/* to extract the official U-Boot bin from the updater: */
+/* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \
+     if=updater/updater-460 of=u-boot-sam460-20100605.bin */
+
+/* from Sam460 U-Boot include/configs/Sam460ex.h */
+#define FLASH_BASE             0xfff00000
+#define FLASH_BASE_H           0x4
+#define FLASH_SIZE             (1 << 20)
+#define UBOOT_LOAD_BASE        0xfff80000
+#define UBOOT_SIZE             0x00080000
+#define UBOOT_ENTRY            0xfffffffc
+
+/* from U-Boot */
+#define EPAPR_MAGIC           (0x45504150)
+#define KERNEL_ADDR           0x1000000
+#define FDT_ADDR              0x1800000
+#define RAMDISK_ADDR          0x1900000
+
+/* Sam460ex IRQ MAP:
+   IRQ0  = ETH_INT
+   IRQ1  = FPGA_INT
+   IRQ2  = PCI_INT (PCIA, PCIB, PCIC, PCIB)
+   IRQ3  = FPGA_INT2
+   IRQ11 = RTC_INT
+   IRQ12 = SM502_INT
+*/
+
+#define SDRAM_NR_BANKS 4
+
+/* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */
+static const unsigned int ppc460ex_sdram_bank_sizes[] = {
+    1024 << 20, 512 << 20, 256 << 20, 128 << 20, 64 << 20, 32 << 20, 0
+};
+
+struct boot_info {
+    uint32_t dt_base;
+    uint32_t dt_size;
+    uint32_t entry;
+};
+
+/*****************************************************************************/
+/* SPD eeprom content from mips_malta.c */
+
+struct _eeprom24c0x_t {
+  uint8_t tick;
+  uint8_t address;
+  uint8_t command;
+  uint8_t ack;
+  uint8_t scl;
+  uint8_t sda;
+  uint8_t data;
+  uint8_t contents[256];
+};
+
+typedef struct _eeprom24c0x_t eeprom24c0x_t;
+
+static eeprom24c0x_t spd_eeprom = {
+    .contents = {
+        /* 00000000: */ 0x80, 0x08, 0xFF, 0x0D, 0x0A, 0xFF, 0x40, 0x00,
+        /* 00000008: */ 0x04, 0x75, 0x54, 0x00, 0x82, 0x08, 0x00, 0x01,
+        /* 00000010: */ 0x8F, 0x04, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
+        /* 00000018: */ 0x00, 0x00, 0x00, 0x14, 0x0F, 0x14, 0x2D, 0xFF,
+        /* 00000020: */ 0x15, 0x08, 0x15, 0x08, 0x00, 0x00, 0x00, 0x00,
+        /* 00000028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xD0,
+        /* 00000040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        /* 00000078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xF4,
+    },
+};
+
+static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size)
+{
+    enum { SDR = 0x4, DDR1 = 0x7, DDR2 = 0x8 } type;
+    uint8_t *spd = spd_eeprom.contents;
+    uint8_t nbanks = 0;
+    uint16_t density = 0;
+    int i;
+
+    /* work in terms of MB */
+    ram_size >>= 20;
+
+    while ((ram_size >= 4) && (nbanks <= 2)) {
+        int sz_log2 = MIN(31 - clz32(ram_size), 14);
+        nbanks++;
+        density |= 1 << (sz_log2 - 2);
+        ram_size -= 1 << sz_log2;
+    }
+
+    /* split to 2 banks if possible */
+    if ((nbanks == 1) && (density > 1)) {
+        nbanks++;
+        density >>= 1;
+    }
+
+    if (density & 0xff00) {
+        density = (density & 0xe0) | ((density >> 8) & 0x1f);
+        type = DDR2;
+    } else if (!(density & 0x1f)) {
+        type = DDR2;
+    } else {
+        type = SDR;
+    }
+
+    if (ram_size) {
+        warn_report("SPD cannot represent final " RAM_ADDR_FMT "MB"
+                    " of SDRAM", ram_size);
+    }
+
+    /* fill in SPD memory information */
+    spd[2] = type;
+    spd[5] = nbanks;
+    spd[31] = density;
+
+    /* XXX: this is totally random */
+    spd[9] = 0x10; /* CAS tcyc */
+    spd[18] = 0x20; /* CAS bit */
+    spd[23] = 0x10; /* CAS tcyc */
+    spd[25] = 0x10; /* CAS tcyc */
+
+    /* checksum */
+    spd[63] = 0;
+    for (i = 0; i < 63; i++) {
+        spd[63] += spd[i];
+    }
+
+    /* copy for SMBUS */
+    memcpy(eeprom, spd, sizeof(spd_eeprom.contents));
+}
+
+static void generate_eeprom_serial(uint8_t *eeprom)
+{
+    int i, pos = 0;
+    uint8_t mac[6] = { 0x00 };
+    uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 };
+
+    /* version */
+    eeprom[pos++] = 0x01;
+
+    /* count */
+    eeprom[pos++] = 0x02;
+
+    /* MAC address */
+    eeprom[pos++] = 0x01; /* MAC */
+    eeprom[pos++] = 0x06; /* length */
+    memcpy(&eeprom[pos], mac, sizeof(mac));
+    pos += sizeof(mac);
+
+    /* serial number */
+    eeprom[pos++] = 0x02; /* serial */
+    eeprom[pos++] = 0x05; /* length */
+    memcpy(&eeprom[pos], sn, sizeof(sn));
+    pos += sizeof(sn);
+
+    /* checksum */
+    eeprom[pos] = 0;
+    for (i = 0; i < pos; i++) {
+        eeprom[pos] += eeprom[i];
+    }
+}
+
+/*****************************************************************************/
+
+static int sam460ex_load_uboot(void)
+{
+    DriveInfo *dinfo;
+    BlockBackend *blk = NULL;
+    hwaddr base = FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32);
+    long bios_size = FLASH_SIZE;
+    int fl_sectors;
+
+    dinfo = drive_get(IF_PFLASH, 0, 0);
+    if (dinfo) {
+        blk = blk_by_legacy_dinfo(dinfo);
+        bios_size = blk_getlength(blk);
+    }
+    fl_sectors = (bios_size + 65535) >> 16;
+
+    if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size,
+                               blk, (64 * 1024), fl_sectors,
+                               1, 0x89, 0x18, 0x0000, 0x0, 1)) {
+        error_report("qemu: Error registering flash memory.");
+        /* XXX: return an error instead? */
+        exit(1);
+    }
+
+    if (!blk) {
+        /*error_report("No flash image given with the 'pflash' parameter,"
+                " using default u-boot image");*/
+        base = UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32);
+        rom_add_file_fixed(UBOOT_FILENAME, base, -1);
+    }
+
+    return 0;
+}
+
+static int sam460ex_load_device_tree(hwaddr addr,
+                                     uint32_t ramsize,
+                                     hwaddr initrd_base,
+                                     hwaddr initrd_size,
+                                     const char *kernel_cmdline)
+{
+    int ret = -1;
+    uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) };
+    char *filename;
+    int fdt_size;
+    void *fdt;
+    uint32_t tb_freq = 50000000;
+    uint32_t clock_freq = 50000000;
+
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+    if (!filename) {
+        goto out;
+    }
+    fdt = load_device_tree(filename, &fdt_size);
+    g_free(filename);
+    if (fdt == NULL) {
+        goto out;
+    }
+
+    /* Manipulate device tree in memory. */
+
+    ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property,
+                               sizeof(mem_reg_property));
+    if (ret < 0) {
+        error_report("couldn't set /memory/reg");
+    }
+
+    /* default FDT doesn't have a /chosen node... */
+    qemu_fdt_add_subnode(fdt, "/chosen");
+
+    ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+                                    initrd_base);
+    if (ret < 0) {
+        error_report("couldn't set /chosen/linux,initrd-start");
+    }
+
+    ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                                    (initrd_base + initrd_size));
+    if (ret < 0) {
+        error_report("couldn't set /chosen/linux,initrd-end");
+    }
+
+    ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
+                                      kernel_cmdline);
+    if (ret < 0) {
+        error_report("couldn't set /chosen/bootargs");
+    }
+
+    /* Copy data from the host device tree into the guest. Since the guest can
+     * directly access the timebase without host involvement, we must expose
+     * the correct frequencies. */
+    if (kvm_enabled()) {
+        tb_freq = kvmppc_get_tbfreq();
+        clock_freq = kvmppc_get_clockfreq();
+    }
+
+    qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
+                              clock_freq);
+    qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
+                              tb_freq);
+
+    rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
+    g_free(fdt);
+    ret = fdt_size;
+
+out:
+
+    return ret;
+}
+
+/* Create reset TLB entries for BookE, mapping only the flash memory.  */
+static void mmubooke_create_initial_mapping_uboot(CPUPPCState *env)
+{
+    ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
+
+    /* on reset the flash is mapped by a shadow TLB,
+     * but since we don't implement them we need to use
+     * the same values U-Boot will use to avoid a fault.
+     */
+    tlb->attr = 0;
+    tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+    tlb->size = 0x10000000; /* up to 0xffffffff  */
+    tlb->EPN = 0xf0000000 & TARGET_PAGE_MASK;
+    tlb->RPN = (0xf0000000 & TARGET_PAGE_MASK) | 0x4;
+    tlb->PID = 0;
+}
+
+/* Create reset TLB entries for BookE, spanning the 32bit addr space.  */
+static void mmubooke_create_initial_mapping(CPUPPCState *env,
+                                     target_ulong va,
+                                     hwaddr pa)
+{
+    ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
+
+    tlb->attr = 0;
+    tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+    tlb->size = 1 << 31; /* up to 0x80000000  */
+    tlb->EPN = va & TARGET_PAGE_MASK;
+    tlb->RPN = pa & TARGET_PAGE_MASK;
+    tlb->PID = 0;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUPPCState *env = &cpu->env;
+    struct boot_info *bi = env->load_info;
+
+    cpu_reset(CPU(cpu));
+
+    /* either we have a kernel to boot or we jump to U-Boot */
+    if (bi->entry != UBOOT_ENTRY) {
+        env->gpr[1] = (16 << 20) - 8;
+        env->gpr[3] = FDT_ADDR;
+        env->nip = bi->entry;
+
+        /* Create a mapping for the kernel.  */
+        mmubooke_create_initial_mapping(env, 0, 0);
+        env->gpr[6] = tswap32(EPAPR_MAGIC);
+        env->gpr[7] = (16 << 20) - 8; /*bi->ima_size;*/
+
+    } else {
+        env->nip = UBOOT_ENTRY;
+        mmubooke_create_initial_mapping_uboot(env);
+    }
+}
+
+static void sam460ex_init(MachineState *machine)
+{
+    MemoryRegion *address_space_mem = get_system_memory();
+    MemoryRegion *isa = g_new(MemoryRegion, 1);
+    MemoryRegion *ram_memories = g_new(MemoryRegion, SDRAM_NR_BANKS);
+    hwaddr ram_bases[SDRAM_NR_BANKS];
+    hwaddr ram_sizes[SDRAM_NR_BANKS];
+    MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1);
+    qemu_irq *irqs, *uic[4];
+    PCIBus *pci_bus;
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    PPC4xxI2CState *i2c[2];
+    hwaddr entry = UBOOT_ENTRY;
+    hwaddr loadaddr = 0;
+    target_long initrd_size = 0;
+    DeviceState *dev;
+    SysBusDevice *sbdev;
+    int success;
+    int i;
+    struct boot_info *boot_info;
+    const size_t smbus_eeprom_size = 8 * 256;
+    uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size);
+
+    cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
+    env = &cpu->env;
+    if (env->mmu_model != POWERPC_MMU_BOOKE) {
+        error_report("Only MMU model BookE is supported by this machine.");
+        exit(1);
+    }
+
+#ifdef TARGET_PPCEMB
+    if (!qtest_enabled()) {
+        warn_report("qemu-system-ppcemb is deprecated, "
+                    "please use qemu-system-ppc instead.");
+    }
+#endif
+
+    qemu_register_reset(main_cpu_reset, cpu);
+    boot_info = g_malloc0(sizeof(*boot_info));
+    env->load_info = boot_info;
+
+    ppc_booke_timers_init(cpu, 50000000, 0);
+    ppc_dcr_init(env, NULL, NULL);
+
+    /* PLB arbitrer */
+    ppc4xx_plb_init(env);
+
+    /* interrupt controllers */
+    irqs = g_malloc0(sizeof(*irqs) * 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];
+    uic[0] = ppcuic_init(env, irqs, 0xc0, 0, 1);
+    uic[1] = ppcuic_init(env, &uic[0][30], 0xd0, 0, 1);
+    uic[2] = ppcuic_init(env, &uic[0][10], 0xe0, 0, 1);
+    uic[3] = ppcuic_init(env, &uic[0][16], 0xf0, 0, 1);
+
+    /* SDRAM controller */
+    memset(ram_bases, 0, sizeof(ram_bases));
+    memset(ram_sizes, 0, sizeof(ram_sizes));
+    /* put all RAM on first bank because board has one slot
+     * and firmware only checks that */
+    machine->ram_size = ppc4xx_sdram_adjust(machine->ram_size, 1,
+                                   ram_memories, ram_bases, ram_sizes,
+                                   ppc460ex_sdram_bank_sizes);
+
+    /* FIXME: does 460EX have ECC interrupts? */
+    ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories,
+                      ram_bases, ram_sizes, 1);
+
+    /* generate SPD EEPROM data */
+    for (i = 0; i < SDRAM_NR_BANKS; i++) {
+        generate_eeprom_spd(&smbus_eeprom_buf[i * 256], ram_sizes[i]);
+    }
+    generate_eeprom_serial(&smbus_eeprom_buf[4 * 256]);
+    generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
+
+    /* IIC controllers */
+    dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600700, uic[0][2]);
+    i2c[0] = PPC4xx_I2C(dev);
+    object_property_set_bool(OBJECT(dev), true, "realized", NULL);
+    smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size);
+    g_free(smbus_eeprom_buf);
+
+    dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]);
+    i2c[1] = PPC4xx_I2C(dev);
+
+    /* External bus controller */
+    ppc405_ebc_init(env);
+
+    /* CPR */
+    ppc4xx_cpr_init(env);
+
+    /* PLB to AHB bridge */
+    ppc4xx_ahb_init(env);
+
+    /* System DCRs */
+    ppc4xx_sdr_init(env);
+
+    /* MAL */
+    ppc4xx_mal_init(env, 4, 16, &uic[2][3]);
+
+    /* 256K of L2 cache as memory */
+    ppc4xx_l2sram_init(env);
+    /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */
+    memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 << 10,
+                           &error_abort);
+    memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram);
+
+    /* USB */
+    sysbus_create_simple(TYPE_PPC4xx_EHCI, 0x4bffd0400, uic[2][29]);
+    dev = qdev_create(NULL, "sysbus-ohci");
+    qdev_prop_set_string(dev, "masterbus", "usb-bus.0");
+    qdev_prop_set_uint32(dev, "num-ports", 6);
+    qdev_init_nofail(dev);
+    sbdev = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(sbdev, 0, 0x4bffd0000);
+    sysbus_connect_irq(sbdev, 0, uic[2][30]);
+    usb_create_simple(usb_bus_find(-1), "usb-kbd");
+    usb_create_simple(usb_bus_find(-1), "usb-mouse");
+
+    /* PCI bus */
+    ppc460ex_pcie_init(env);
+    /* FIXME: is this correct? */
+    dev = sysbus_create_varargs("ppc440-pcix-host", 0xc0ec00000,
+                                uic[1][0], uic[1][20], uic[1][21], uic[1][22],
+                                NULL);
+    pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
+    if (!pci_bus) {
+        error_report("couldn't create PCI controller!");
+        exit(1);
+    }
+    memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(),
+                             0, 0x10000);
+    memory_region_add_subregion(get_system_memory(), 0xc08000000, isa);
+
+    /* PCI devices */
+    pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501");
+    /* SoC has a single SATA port but we don't emulate that yet
+     * However, firmware and usual clients have driver for SiI311x
+     * so add one for convenience by default */
+    if (defaults_enabled()) {
+        pci_create_simple(pci_bus, -1, "sii3112");
+    }
+
+    /* SoC has 4 UARTs
+     * but board has only one wired and two are present in fdt */
+    if (serial_hds[0] != NULL) {
+        serial_mm_init(address_space_mem, 0x4ef600300, 0, uic[1][1],
+                       PPC_SERIAL_MM_BAUDBASE, serial_hds[0],
+                       DEVICE_BIG_ENDIAN);
+    }
+    if (serial_hds[1] != NULL) {
+        serial_mm_init(address_space_mem, 0x4ef600400, 0, uic[0][1],
+                       PPC_SERIAL_MM_BAUDBASE, serial_hds[1],
+                       DEVICE_BIG_ENDIAN);
+    }
+
+    /* Load U-Boot image. */
+    if (!machine->kernel_filename) {
+        success = sam460ex_load_uboot();
+        if (success < 0) {
+            error_report("qemu: could not load firmware");
+            exit(1);
+        }
+    }
+
+    /* Load kernel. */
+    if (machine->kernel_filename) {
+        success = load_uimage(machine->kernel_filename, &entry, &loadaddr,
+                              NULL, NULL, NULL);
+        if (success < 0) {
+            uint64_t elf_entry, elf_lowaddr;
+
+            success = load_elf(machine->kernel_filename, NULL, NULL, &elf_entry,
+                               &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0);
+            entry = elf_entry;
+            loadaddr = elf_lowaddr;
+        }
+        /* XXX try again as binary */
+        if (success < 0) {
+            error_report("qemu: could not load kernel '%s'",
+                    machine->kernel_filename);
+            exit(1);
+        }
+    }
+
+    /* Load initrd. */
+    if (machine->initrd_filename) {
+        initrd_size = load_image_targphys(machine->initrd_filename,
+                                          RAMDISK_ADDR,
+                                          machine->ram_size - RAMDISK_ADDR);
+        if (initrd_size < 0) {
+            error_report("qemu: could not load ram disk '%s' at %x",
+                    machine->initrd_filename, RAMDISK_ADDR);
+            exit(1);
+        }
+    }
+
+    /* If we're loading a kernel directly, we must load the device tree too. */
+    if (machine->kernel_filename) {
+        int dt_size;
+
+        dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size,
+                                    RAMDISK_ADDR, initrd_size,
+                                    machine->kernel_cmdline);
+        if (dt_size < 0) {
+            error_report("couldn't load device tree");
+            exit(1);
+        }
+
+        boot_info->dt_base = FDT_ADDR;
+        boot_info->dt_size = dt_size;
+    }
+
+    boot_info->entry = entry;
+}
+
+static void sam460ex_machine_init(MachineClass *mc)
+{
+    mc->desc = "aCube Sam460ex";
+    mc->init = sam460ex_init;
+    mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb");
+    mc->default_ram_size = 512 * M_BYTE;
+}
+
+DEFINE_MACHINE("sam460ex", sam460ex_machine_init)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 83c9d66dd5..7e1c858566 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -105,12 +105,14 @@
  */
 static int spapr_vcpu_id(sPAPRMachineState *spapr, int cpu_index)
 {
+    assert(spapr->vsmt);
     return
         (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads;
 }
 static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr,
                                       PowerPCCPU *cpu)
 {
+    assert(spapr->vsmt);
     return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0;
 }
 
@@ -177,13 +179,13 @@ static void pre_2_10_vmstate_unregister_dummy_icp(int i)
 
 static int xics_max_server_number(sPAPRMachineState *spapr)
 {
+    assert(spapr->vsmt);
     return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads);
 }
 
 static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
-    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
 
     if (kvm_enabled()) {
         if (machine_kernel_irqchip_allowed(machine) &&
@@ -205,17 +207,6 @@ static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp)
             return;
         }
     }
-
-    if (smc->pre_2_10_has_unused_icps) {
-        int i;
-
-        for (i = 0; i < xics_max_server_number(spapr); i++) {
-            /* Dummy entries get deregistered when real ICPState objects
-             * are registered during CPU core hotplug.
-             */
-            pre_2_10_vmstate_register_dummy_icp(i);
-        }
-    }
 }
 
 static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
@@ -1062,7 +1053,14 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt)
     }
 
     if (!spapr->has_graphics && stdout_path) {
+        /*
+         * "linux,stdout-path" and "stdout" properties are deprecated by linux
+         * kernel. New platforms should only use the "stdout-path" property. Set
+         * the new property and continue using older property to remain
+         * compatible with the existing firmware.
+         */
         _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path));
+        _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path));
     }
 
     spapr_dt_ov5_platform_support(fdt, chosen);
@@ -2232,61 +2230,6 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
     return &ms->possible_cpus->cpus[index];
 }
 
-static void spapr_init_cpus(sPAPRMachineState *spapr)
-{
-    MachineState *machine = MACHINE(spapr);
-    MachineClass *mc = MACHINE_GET_CLASS(machine);
-    const char *type = spapr_get_cpu_core_type(machine->cpu_type);
-    const CPUArchIdList *possible_cpus;
-    int boot_cores_nr = smp_cpus / smp_threads;
-    int i;
-
-    possible_cpus = mc->possible_cpu_arch_ids(machine);
-    if (mc->has_hotpluggable_cpus) {
-        if (smp_cpus % smp_threads) {
-            error_report("smp_cpus (%u) must be multiple of threads (%u)",
-                         smp_cpus, smp_threads);
-            exit(1);
-        }
-        if (max_cpus % smp_threads) {
-            error_report("max_cpus (%u) must be multiple of threads (%u)",
-                         max_cpus, smp_threads);
-            exit(1);
-        }
-    } else {
-        if (max_cpus != smp_cpus) {
-            error_report("This machine version does not support CPU hotplug");
-            exit(1);
-        }
-        boot_cores_nr = possible_cpus->len;
-    }
-
-    for (i = 0; i < possible_cpus->len; i++) {
-        int core_id = i * smp_threads;
-
-        if (mc->has_hotpluggable_cpus) {
-            spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU,
-                                   spapr_vcpu_id(spapr, core_id));
-        }
-
-        if (i < boot_cores_nr) {
-            Object *core  = object_new(type);
-            int nr_threads = smp_threads;
-
-            /* Handle the partially filled core for older machine types */
-            if ((i + 1) * smp_threads >= smp_cpus) {
-                nr_threads = smp_cpus - i * smp_threads;
-            }
-
-            object_property_set_int(core, nr_threads, "nr-threads",
-                                    &error_fatal);
-            object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
-                                    &error_fatal);
-            object_property_set_bool(core, true, "realized", &error_fatal);
-        }
-    }
-}
-
 static void spapr_set_vsmt_mode(sPAPRMachineState *spapr, Error **errp)
 {
     Error *local_err = NULL;
@@ -2359,6 +2302,78 @@ out:
     error_propagate(errp, local_err);
 }
 
+static void spapr_init_cpus(sPAPRMachineState *spapr)
+{
+    MachineState *machine = MACHINE(spapr);
+    MachineClass *mc = MACHINE_GET_CLASS(machine);
+    sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
+    const char *type = spapr_get_cpu_core_type(machine->cpu_type);
+    const CPUArchIdList *possible_cpus;
+    int boot_cores_nr = smp_cpus / smp_threads;
+    int i;
+
+    possible_cpus = mc->possible_cpu_arch_ids(machine);
+    if (mc->has_hotpluggable_cpus) {
+        if (smp_cpus % smp_threads) {
+            error_report("smp_cpus (%u) must be multiple of threads (%u)",
+                         smp_cpus, smp_threads);
+            exit(1);
+        }
+        if (max_cpus % smp_threads) {
+            error_report("max_cpus (%u) must be multiple of threads (%u)",
+                         max_cpus, smp_threads);
+            exit(1);
+        }
+    } else {
+        if (max_cpus != smp_cpus) {
+            error_report("This machine version does not support CPU hotplug");
+            exit(1);
+        }
+        boot_cores_nr = possible_cpus->len;
+    }
+
+    /* VSMT must be set in order to be able to compute VCPU ids, ie to
+     * call xics_max_server_number() or spapr_vcpu_id().
+     */
+    spapr_set_vsmt_mode(spapr, &error_fatal);
+
+    if (smc->pre_2_10_has_unused_icps) {
+        int i;
+
+        for (i = 0; i < xics_max_server_number(spapr); i++) {
+            /* Dummy entries get deregistered when real ICPState objects
+             * are registered during CPU core hotplug.
+             */
+            pre_2_10_vmstate_register_dummy_icp(i);
+        }
+    }
+
+    for (i = 0; i < possible_cpus->len; i++) {
+        int core_id = i * smp_threads;
+
+        if (mc->has_hotpluggable_cpus) {
+            spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU,
+                                   spapr_vcpu_id(spapr, core_id));
+        }
+
+        if (i < boot_cores_nr) {
+            Object *core  = object_new(type);
+            int nr_threads = smp_threads;
+
+            /* Handle the partially filled core for older machine types */
+            if ((i + 1) * smp_threads >= smp_cpus) {
+                nr_threads = smp_cpus - i * smp_threads;
+            }
+
+            object_property_set_int(core, nr_threads, "nr-threads",
+                                    &error_fatal);
+            object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID,
+                                    &error_fatal);
+            object_property_set_bool(core, true, "realized", &error_fatal);
+        }
+    }
+}
+
 /* pSeries LPAR / sPAPR hardware init */
 static void spapr_machine_init(MachineState *machine)
 {
@@ -2486,8 +2501,6 @@ static void spapr_machine_init(MachineState *machine)
     }
 
     /* init CPUs */
-    spapr_set_vsmt_mode(spapr, &error_fatal);
-
     spapr_init_cpus(spapr);
 
     if (kvm_enabled()) {
@@ -3810,13 +3823,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj,
 
 int spapr_get_vcpu_id(PowerPCCPU *cpu)
 {
-    CPUState *cs = CPU(cpu);
-
-    if (kvm_enabled()) {
-        return kvm_arch_vcpu_id(cs);
-    } else {
-        return cs->cpu_index;
-    }
+    return cpu->vcpu_id;
 }
 
 void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp)
@@ -3983,6 +3990,23 @@ static void spapr_machine_2_12_class_options(MachineClass *mc)
 
 DEFINE_SPAPR_MACHINE(2_12, "2.12", true);
 
+static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine)
+{
+    spapr_machine_2_12_instance_options(machine);
+}
+
+static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc)
+{
+    sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
+
+    spapr_machine_2_12_class_options(mc);
+    smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND;
+    smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND;
+    smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD;
+}
+
+DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false);
+
 /*
  * pseries-2.11
  */
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
index 99a4b71d19..531e145114 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -32,6 +32,20 @@
 
 #include "hw/ppc/spapr.h"
 
+typedef struct sPAPRCapPossible {
+    int num;            /* size of vals array below */
+    const char *help;   /* help text for vals */
+    /*
+     * Note:
+     * - because of the way compatibility is determined vals MUST be ordered
+     *   such that later options are a superset of all preceding options.
+     * - the order of vals must be preserved, that is their index is important,
+     *   however vals may be added to the end of the list so long as the above
+     *   point is observed
+     */
+    const char *vals[];
+} sPAPRCapPossible;
+
 typedef struct sPAPRCapabilityInfo {
     const char *name;
     const char *description;
@@ -41,6 +55,8 @@ typedef struct sPAPRCapabilityInfo {
     ObjectPropertyAccessor *get;
     ObjectPropertyAccessor *set;
     const char *type;
+    /* Possible values if this is a custom string type */
+    sPAPRCapPossible *possible;
     /* Make sure the virtual hardware can support this capability */
     void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp);
 } sPAPRCapabilityInfo;
@@ -73,41 +89,34 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name,
     spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF;
 }
 
-static void spapr_cap_get_tristate(Object *obj, Visitor *v, const char *name,
-                                   void *opaque, Error **errp)
+
+static void  spapr_cap_get_string(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
 {
     sPAPRCapabilityInfo *cap = opaque;
     sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
     char *val = NULL;
     uint8_t value = spapr_get_cap(spapr, cap->index);
 
-    switch (value) {
-    case SPAPR_CAP_BROKEN:
-        val = g_strdup("broken");
-        break;
-    case SPAPR_CAP_WORKAROUND:
-        val = g_strdup("workaround");
-        break;
-    case SPAPR_CAP_FIXED:
-        val = g_strdup("fixed");
-        break;
-    default:
+    if (value >= cap->possible->num) {
         error_setg(errp, "Invalid value (%d) for cap-%s", value, cap->name);
         return;
     }
 
+    val = g_strdup(cap->possible->vals[value]);
+
     visit_type_str(v, name, &val, errp);
     g_free(val);
 }
 
-static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name,
-                                   void *opaque, Error **errp)
+static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name,
+                                 void *opaque, Error **errp)
 {
     sPAPRCapabilityInfo *cap = opaque;
     sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
-    char *val;
     Error *local_err = NULL;
-    uint8_t value;
+    uint8_t i;
+    char *val;
 
     visit_type_str(v, name, &val, &local_err);
     if (local_err) {
@@ -115,20 +124,20 @@ static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name,
         return;
     }
 
-    if (!strcasecmp(val, "broken")) {
-        value = SPAPR_CAP_BROKEN;
-    } else if (!strcasecmp(val, "workaround")) {
-        value = SPAPR_CAP_WORKAROUND;
-    } else if (!strcasecmp(val, "fixed")) {
-        value = SPAPR_CAP_FIXED;
-    } else {
-        error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val,
-                   cap->name);
+    if (!strcmp(val, "?")) {
+        error_setg(errp, "%s", cap->possible->help);
         goto out;
     }
+    for (i = 0; i < cap->possible->num; i++) {
+        if (!strcasecmp(val, cap->possible->vals[i])) {
+            spapr->cmd_line_caps[cap->index] = true;
+            spapr->eff.caps[cap->index] = i;
+            goto out;
+        }
+    }
 
-    spapr->cmd_line_caps[cap->index] = true;
-    spapr->eff.caps[cap->index] = value;
+    error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val,
+               cap->name);
 out:
     g_free(val);
 }
@@ -180,38 +189,77 @@ static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
     }
 }
 
+sPAPRCapPossible cap_cfpc_possible = {
+    .num = 3,
+    .vals = {"broken", "workaround", "fixed"},
+    .help = "broken - no protection, workaround - workaround available,"
+            " fixed - fixed in hardware",
+};
+
 static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val,
                                  Error **errp)
 {
+    uint8_t kvm_val =  kvmppc_get_cap_safe_cache();
+
     if (tcg_enabled() && val) {
         /* TODO - for now only allow broken for TCG */
-        error_setg(errp, "Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc");
-    } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_cache())) {
-        error_setg(errp, "Requested safe cache capability level not supported by kvm, try a different value for cap-cfpc");
+        error_setg(errp,
+"Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc");
+    } else if (kvm_enabled() && (val > kvm_val)) {
+        error_setg(errp,
+"Requested safe cache capability level not supported by kvm, try cap-cfpc=%s",
+                   cap_cfpc_possible.vals[kvm_val]);
     }
 }
 
+sPAPRCapPossible cap_sbbc_possible = {
+    .num = 3,
+    .vals = {"broken", "workaround", "fixed"},
+    .help = "broken - no protection, workaround - workaround available,"
+            " fixed - fixed in hardware",
+};
+
 static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val,
                                         Error **errp)
 {
+    uint8_t kvm_val =  kvmppc_get_cap_safe_bounds_check();
+
     if (tcg_enabled() && val) {
         /* TODO - for now only allow broken for TCG */
-        error_setg(errp, "Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc");
-    } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_bounds_check())) {
-        error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try a different value for cap-sbbc");
+        error_setg(errp,
+"Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc");
+    } else if (kvm_enabled() && (val > kvm_val)) {
+        error_setg(errp,
+"Requested safe bounds check capability level not supported by kvm, try cap-sbbc=%s",
+                   cap_sbbc_possible.vals[kvm_val]);
     }
 }
 
+sPAPRCapPossible cap_ibs_possible = {
+    .num = 4,
+    /* Note workaround only maintained for compatibility */
+    .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd"},
+    .help = "broken - no protection, fixed-ibs - indirect branch serialisation,"
+            " fixed-ccd - cache count disabled",
+};
+
 static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr,
                                            uint8_t val, Error **errp)
 {
+    uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch();
+
     if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */
-        error_setg(errp, "Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=fixed");
+        error_setg(errp,
+"Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=%s",
+                   cap_ibs_possible.vals[kvm_val]);
     } else if (tcg_enabled() && val) {
         /* TODO - for now only allow broken for TCG */
-        error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs");
-    } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) {
-        error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try a different value for cap-ibs");
+        error_setg(errp,
+"Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs");
+    } else if (kvm_enabled() && val && (val != kvm_val)) {
+        error_setg(errp,
+"Requested safe indirect branch capability level not supported by kvm, try cap-ibs=%s",
+                   cap_ibs_possible.vals[kvm_val]);
     }
 }
 
@@ -249,27 +297,31 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
         .name = "cfpc",
         .description = "Cache Flush on Privilege Change" VALUE_DESC_TRISTATE,
         .index = SPAPR_CAP_CFPC,
-        .get = spapr_cap_get_tristate,
-        .set = spapr_cap_set_tristate,
+        .get = spapr_cap_get_string,
+        .set = spapr_cap_set_string,
         .type = "string",
+        .possible = &cap_cfpc_possible,
         .apply = cap_safe_cache_apply,
     },
     [SPAPR_CAP_SBBC] = {
         .name = "sbbc",
         .description = "Speculation Barrier Bounds Checking" VALUE_DESC_TRISTATE,
         .index = SPAPR_CAP_SBBC,
-        .get = spapr_cap_get_tristate,
-        .set = spapr_cap_set_tristate,
+        .get = spapr_cap_get_string,
+        .set = spapr_cap_set_string,
         .type = "string",
+        .possible = &cap_sbbc_possible,
         .apply = cap_safe_bounds_check_apply,
     },
     [SPAPR_CAP_IBS] = {
         .name = "ibs",
-        .description = "Indirect Branch Serialisation (broken, fixed)",
+        .description =
+            "Indirect Branch Speculation (broken, fixed-ibs, fixed-ccd)",
         .index = SPAPR_CAP_IBS,
-        .get = spapr_cap_get_tristate,
-        .set = spapr_cap_set_tristate,
+        .get = spapr_cap_get_string,
+        .set = spapr_cap_set_string,
         .type = "string",
+        .possible = &cap_ibs_possible,
         .apply = cap_safe_indirect_branch_apply,
     },
 };
@@ -283,15 +335,26 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr,
 
     caps = smc->default_caps;
 
+    if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00,
+                          0, spapr->max_compat_pvr)) {
+        caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN;
+    }
+
     if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07,
                           0, spapr->max_compat_pvr)) {
         caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF;
     }
 
+    if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS,
+                          0, spapr->max_compat_pvr)) {
+        caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN;
+    }
+
     if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06,
                           0, spapr->max_compat_pvr)) {
         caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF;
         caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF;
+        caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
     }
 
     return caps;
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 1986560480..16bccdd5c0 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1705,7 +1705,10 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu,
     }
 
     switch (safe_indirect_branch) {
-    case SPAPR_CAP_FIXED:
+    case SPAPR_CAP_FIXED_CCD:
+        characteristics |= H_CPU_CHAR_CACHE_COUNT_DIS;
+        break;
+    case SPAPR_CAP_FIXED_IBS:
         characteristics |= H_CPU_CHAR_BCCTRL_SERIALISED;
         break;
     default: /* broken */
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index b7c3e64b5e..66ec7eda6e 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -99,3 +99,11 @@ mac99_uninorth_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"P
 # hw/ppc/ppc4xx_pci.c
 ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d"
 ppc4xx_pci_set_irq(int irq_num) "PCI irq %d"
+
+# hw/ppc/ppc440_pcix.c
+ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d"
+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, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64
diff --git a/include/hw/intc/heathrow_pic.h b/include/hw/intc/heathrow_pic.h
new file mode 100644
index 0000000000..bc3ffaab87
--- /dev/null
+++ b/include/hw/intc/heathrow_pic.h
@@ -0,0 +1,49 @@
+/*
+ * Heathrow PIC support (OldWorld PowerMac)
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HEATHROW_H
+#define HEATHROW_H
+
+#define TYPE_HEATHROW "heathrow"
+#define HEATHROW(obj) OBJECT_CHECK(HeathrowState, (obj), TYPE_HEATHROW)
+
+typedef struct HeathrowPICState {
+    uint32_t events;
+    uint32_t mask;
+    uint32_t levels;
+    uint32_t level_triggered;
+} HeathrowPICState;
+
+typedef struct HeathrowState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mem;
+    HeathrowPICState pics[2];
+    qemu_irq *irqs;
+} HeathrowState;
+
+#define HEATHROW_NUM_IRQS 64
+
+#endif /* HEATHROW_H */
diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h
new file mode 100644
index 0000000000..4528282b36
--- /dev/null
+++ b/include/hw/misc/macio/macio.h
@@ -0,0 +1,79 @@
+/*
+ * PowerMac MacIO device emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MACIO_H
+#define MACIO_H
+
+#include "hw/intc/heathrow_pic.h"
+#include "hw/misc/macio/cuda.h"
+#include "hw/ppc/mac_dbdma.h"
+#include "hw/ppc/openpic.h"
+
+#define TYPE_MACIO "macio"
+#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO)
+
+typedef struct MacIOState {
+    /*< private >*/
+    PCIDevice parent;
+    /*< public >*/
+
+    MemoryRegion bar;
+    CUDAState cuda;
+    DBDMAState dbdma;
+    ESCCState escc;
+    uint64_t frequency;
+} MacIOState;
+
+#define TYPE_OLDWORLD_MACIO "macio-oldworld"
+#define OLDWORLD_MACIO(obj) \
+    OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO)
+
+typedef struct OldWorldMacIOState {
+    /*< private >*/
+    MacIOState parent_obj;
+    /*< public >*/
+
+    HeathrowState *pic;
+    qemu_irq irqs[7];
+
+    MacIONVRAMState nvram;
+    MACIOIDEState ide[2];
+} OldWorldMacIOState;
+
+#define TYPE_NEWWORLD_MACIO "macio-newworld"
+#define NEWWORLD_MACIO(obj) \
+    OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO)
+
+typedef struct NewWorldMacIOState {
+    /*< private >*/
+    MacIOState parent_obj;
+    /*< public >*/
+
+    OpenPICState *pic;
+    qemu_irq irqs[7];
+    MACIOIDEState ide[2];
+} NewWorldMacIOState;
+
+#endif /* MACIO_H */
diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h
index e55ce546aa..5eb982197d 100644
--- a/include/hw/ppc/openpic.h
+++ b/include/hw/ppc/openpic.h
@@ -2,10 +2,13 @@
 #define OPENPIC_H
 
 #include "qemu-common.h"
+#include "hw/sysbus.h"
 #include "hw/qdev-core.h"
 #include "qom/cpu.h"
 
-#define TYPE_OPENPIC "openpic"
+#define MAX_CPU     32
+#define MAX_MSI     8
+#define VID         0x03 /* MPIC version ID */
 
 /* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
 enum {
@@ -28,7 +31,158 @@ enum {
 #define OPENPIC_MAX_IRQ     (OPENPIC_MAX_SRC + OPENPIC_MAX_IPI + \
                              OPENPIC_MAX_TMR)
 
-#define TYPE_KVM_OPENPIC "kvm-openpic"
-int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs);
+/* Raven */
+#define RAVEN_MAX_CPU      2
+#define RAVEN_MAX_EXT     48
+#define RAVEN_MAX_IRQ     64
+#define RAVEN_MAX_TMR      OPENPIC_MAX_TMR
+#define RAVEN_MAX_IPI      OPENPIC_MAX_IPI
+
+/* KeyLargo */
+#define KEYLARGO_MAX_CPU  4
+#define KEYLARGO_MAX_EXT  64
+#define KEYLARGO_MAX_IPI  4
+#define KEYLARGO_MAX_IRQ  (64 + KEYLARGO_MAX_IPI)
+#define KEYLARGO_MAX_TMR  0
+#define KEYLARGO_IPI_IRQ  (KEYLARGO_MAX_EXT) /* First IPI IRQ */
+/* Timers don't exist but this makes the code happy... */
+#define KEYLARGO_TMR_IRQ  (KEYLARGO_IPI_IRQ + KEYLARGO_MAX_IPI)
+
+/* Interrupt definitions */
+#define RAVEN_FE_IRQ     (RAVEN_MAX_EXT)     /* Internal functional IRQ */
+#define RAVEN_ERR_IRQ    (RAVEN_MAX_EXT + 1) /* Error IRQ */
+#define RAVEN_TMR_IRQ    (RAVEN_MAX_EXT + 2) /* First timer IRQ */
+#define RAVEN_IPI_IRQ    (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */
+/* First doorbell IRQ */
+#define RAVEN_DBL_IRQ    (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI))
+
+typedef struct FslMpicInfo {
+    int max_ext;
+} FslMpicInfo;
+
+typedef enum IRQType {
+    IRQ_TYPE_NORMAL = 0,
+    IRQ_TYPE_FSLINT,        /* FSL internal interrupt -- level only */
+    IRQ_TYPE_FSLSPECIAL,    /* FSL timer/IPI interrupt, edge, no polarity */
+} IRQType;
+
+/* Round up to the nearest 64 IRQs so that the queue length
+ * won't change when moving between 32 and 64 bit hosts.
+ */
+#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63)
+
+typedef struct IRQQueue {
+    unsigned long *queue;
+    int32_t queue_size; /* Only used for VMSTATE_BITMAP */
+    int next;
+    int priority;
+} IRQQueue;
+
+typedef struct IRQSource {
+    uint32_t ivpr;  /* IRQ vector/priority register */
+    uint32_t idr;   /* IRQ destination register */
+    uint32_t destmask; /* bitmap of CPU destinations */
+    int last_cpu;
+    int output;     /* IRQ level, e.g. OPENPIC_OUTPUT_INT */
+    int pending;    /* TRUE if IRQ is pending */
+    IRQType type;
+    bool level:1;   /* level-triggered */
+    bool nomask:1;  /* critical interrupts ignore mask on some FSL MPICs */
+} IRQSource;
+
+#define IVPR_MASK_SHIFT       31
+#define IVPR_MASK_MASK        (1U << IVPR_MASK_SHIFT)
+#define IVPR_ACTIVITY_SHIFT   30
+#define IVPR_ACTIVITY_MASK    (1U << IVPR_ACTIVITY_SHIFT)
+#define IVPR_MODE_SHIFT       29
+#define IVPR_MODE_MASK        (1U << IVPR_MODE_SHIFT)
+#define IVPR_POLARITY_SHIFT   23
+#define IVPR_POLARITY_MASK    (1U << IVPR_POLARITY_SHIFT)
+#define IVPR_SENSE_SHIFT      22
+#define IVPR_SENSE_MASK       (1U << IVPR_SENSE_SHIFT)
+
+#define IVPR_PRIORITY_MASK     (0xFU << 16)
+#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16))
+#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask)
+
+/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */
+#define IDR_EP      0x80000000  /* external pin */
+#define IDR_CI      0x40000000  /* critical interrupt */
+
+typedef struct OpenPICTimer {
+    uint32_t tccr;  /* Global timer current count register */
+    uint32_t tbcr;  /* Global timer base count register */
+    int                   n_IRQ;
+    bool                  qemu_timer_active; /* Is the qemu_timer is running? */
+    struct QEMUTimer     *qemu_timer;
+    struct OpenPICState  *opp;          /* Device timer is part of. */
+    /* The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last
+       current_count written or read, only defined if qemu_timer_active. */
+    uint64_t              origin_time;
+} OpenPICTimer;
+
+typedef struct OpenPICMSI {
+    uint32_t msir;   /* Shared Message Signaled Interrupt Register */
+} OpenPICMSI;
+
+typedef struct IRQDest {
+    int32_t ctpr; /* CPU current task priority */
+    IRQQueue raised;
+    IRQQueue servicing;
+    qemu_irq *irqs;
+
+    /* Count of IRQ sources asserting on non-INT outputs */
+    uint32_t outputs_active[OPENPIC_OUTPUT_NB];
+} IRQDest;
+
+#define TYPE_OPENPIC "openpic"
+#define OPENPIC(obj) OBJECT_CHECK(OpenPICState, (obj), TYPE_OPENPIC)
+
+typedef struct OpenPICState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion mem;
+
+    /* Behavior control */
+    FslMpicInfo *fsl;
+    uint32_t model;
+    uint32_t flags;
+    uint32_t nb_irqs;
+    uint32_t vid;
+    uint32_t vir; /* Vendor identification register */
+    uint32_t vector_mask;
+    uint32_t tfrr_reset;
+    uint32_t ivpr_reset;
+    uint32_t idr_reset;
+    uint32_t brr1;
+    uint32_t mpic_mode_mask;
+
+    /* Sub-regions */
+    MemoryRegion sub_io_mem[6];
+
+    /* Global registers */
+    uint32_t frr; /* Feature reporting register */
+    uint32_t gcr; /* Global configuration register  */
+    uint32_t pir; /* Processor initialization register */
+    uint32_t spve; /* Spurious vector register */
+    uint32_t tfrr; /* Timer frequency reporting register */
+    /* Source registers */
+    IRQSource src[OPENPIC_MAX_IRQ];
+    /* Local registers per output pin */
+    IRQDest dst[MAX_CPU];
+    uint32_t nb_cpus;
+    /* Timer registers */
+    OpenPICTimer timers[OPENPIC_MAX_TMR];
+    uint32_t max_tmr;
+
+    /* Shared MSI registers */
+    OpenPICMSI msi[MAX_MSI];
+    uint32_t max_irq;
+    uint32_t irq_ipi0;
+    uint32_t irq_tim0;
+    uint32_t irq_msi;
+} OpenPICState;
 
 #endif /* OPENPIC_H */
diff --git a/include/hw/ppc/openpic_kvm.h b/include/hw/ppc/openpic_kvm.h
new file mode 100644
index 0000000000..9ef4215257
--- /dev/null
+++ b/include/hw/ppc/openpic_kvm.h
@@ -0,0 +1,7 @@
+#ifndef OPENPIC_KVM_H
+#define OPENPIC_KVM_H
+
+#define TYPE_KVM_OPENPIC "kvm-openpic"
+int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs);
+
+#endif /* OPENPIC_KVM_H */
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 36942b378d..d60b7c6d7a 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -75,10 +75,12 @@ typedef enum {
 /* Bool Caps */
 #define SPAPR_CAP_OFF                   0x00
 #define SPAPR_CAP_ON                    0x01
-/* Broken | Workaround | Fixed Caps */
+/* Custom Caps */
 #define SPAPR_CAP_BROKEN                0x00
 #define SPAPR_CAP_WORKAROUND            0x01
 #define SPAPR_CAP_FIXED                 0x02
+#define SPAPR_CAP_FIXED_IBS             0x02
+#define SPAPR_CAP_FIXED_CCD             0x03
 
 typedef struct sPAPRCapabilities sPAPRCapabilities;
 struct sPAPRCapabilities {
@@ -313,6 +315,7 @@ struct sPAPRMachineState {
 #define H_CPU_CHAR_L1D_THREAD_PRIV              PPC_BIT(4)
 #define H_CPU_CHAR_HON_BRANCH_HINTS             PPC_BIT(5)
 #define H_CPU_CHAR_THR_RECONF_TRIG              PPC_BIT(6)
+#define H_CPU_CHAR_CACHE_COUNT_DIS              PPC_BIT(7)
 #define H_CPU_BEHAV_FAVOUR_SECURITY             PPC_BIT(0)
 #define H_CPU_BEHAV_L1D_FLUSH_PR                PPC_BIT(1)
 #define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR           PPC_BIT(2)
diff --git a/pc-bios/canyonlands.dtb b/pc-bios/canyonlands.dtb
new file mode 100644
index 0000000000..9dce3443ad
--- /dev/null
+++ b/pc-bios/canyonlands.dtb
Binary files differdiff --git a/pc-bios/canyonlands.dts b/pc-bios/canyonlands.dts
new file mode 100644
index 0000000000..0d6ac92d0f
--- /dev/null
+++ b/pc-bios/canyonlands.dts
@@ -0,0 +1,566 @@
+/*
+ * Device Tree Source for AMCC Canyonlands (460EX)
+ *
+ * Copyright 2008-2009 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without
+ * any warranty of any kind, whether express or implied.
+ */
+
+/dts-v1/;
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <1>;
+	model = "amcc,canyonlands";
+	compatible = "amcc,canyonlands";
+	dcr-parent = <&{/cpus/cpu@0}>;
+
+	aliases {
+		ethernet0 = &EMAC0;
+		ethernet1 = &EMAC1;
+		serial0 = &UART0;
+		serial1 = &UART1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu@0 {
+			device_type = "cpu";
+			model = "PowerPC,460EX";
+			reg = <0x00000000>;
+			clock-frequency = <0>; /* Filled in by U-Boot */
+			timebase-frequency = <0>; /* Filled in by U-Boot */
+			i-cache-line-size = <32>;
+			d-cache-line-size = <32>;
+			i-cache-size = <32768>;
+			d-cache-size = <32768>;
+			dcr-controller;
+			dcr-access-method = "native";
+			next-level-cache = <&L2C0>;
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x00000000 0x00000000>; /* Filled in by U-Boot */
+	};
+
+	UIC0: interrupt-controller0 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <0>;
+		dcr-reg = <0x0c0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+	};
+
+	UIC1: interrupt-controller1 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <1>;
+		dcr-reg = <0x0d0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0x1e 0x4 0x1f 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	UIC2: interrupt-controller2 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <2>;
+		dcr-reg = <0x0e0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0xa 0x4 0xb 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	UIC3: interrupt-controller3 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <3>;
+		dcr-reg = <0x0f0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0x10 0x4 0x11 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	SDR0: sdr {
+		compatible = "ibm,sdr-460ex";
+		dcr-reg = <0x00e 0x002>;
+	};
+
+	CPR0: cpr {
+		compatible = "ibm,cpr-460ex";
+		dcr-reg = <0x00c 0x002>;
+	};
+
+	CPM0: cpm {
+		compatible = "ibm,cpm";
+		dcr-access-method = "native";
+		dcr-reg = <0x160 0x003>;
+		unused-units = <0x00000100>;
+		idle-doze = <0x02000000>;
+		standby = <0xfeff791d>;
+	};
+
+	L2C0: l2c {
+		compatible = "ibm,l2-cache-460ex", "ibm,l2-cache";
+		dcr-reg = <0x020 0x008		/* Internal SRAM DCR's */
+			   0x030 0x008>;	/* L2 cache DCR's */
+		cache-line-size = <32>;		/* 32 bytes */
+		cache-size = <262144>;		/* L2, 256K */
+		interrupt-parent = <&UIC1>;
+		interrupts = <11 1>;
+	};
+
+	plb {
+		compatible = "ibm,plb-460ex", "ibm,plb4";
+		#address-cells = <2>;
+		#size-cells = <1>;
+		ranges;
+		clock-frequency = <0>; /* Filled in by U-Boot */
+
+		SDRAM0: sdram {
+			compatible = "ibm,sdram-460ex", "ibm,sdram-405gp";
+			dcr-reg = <0x010 0x002>;
+		};
+
+		CRYPTO: crypto@180000 {
+			compatible = "amcc,ppc460ex-crypto", "amcc,ppc4xx-crypto";
+			reg = <4 0x00180000 0x80400>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <0x1d 0x4>;
+		};
+
+		HWRNG: hwrng@110000 {
+			compatible = "amcc,ppc460ex-rng", "ppc4xx-rng";
+			reg = <4 0x00110000 0x50>;
+		};
+
+		MAL0: mcmal {
+			compatible = "ibm,mcmal-460ex", "ibm,mcmal2";
+			dcr-reg = <0x180 0x062>;
+			num-tx-chans = <2>;
+			num-rx-chans = <16>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-parent = <&UIC2>;
+			interrupts = <	/*TXEOB*/ 0x6 0x4
+					/*RXEOB*/ 0x7 0x4
+					/*SERR*/  0x3 0x4
+					/*TXDE*/  0x4 0x4
+					/*RXDE*/  0x5 0x4>;
+		};
+
+		USB0: ehci@bffd0400 {
+			compatible = "ibm,usb-ehci-460ex", "usb-ehci";
+			interrupt-parent = <&UIC2>;
+			interrupts = <0x1d 4>;
+			reg = <4 0xbffd0400 0x90 4 0xbffd0490 0x70>;
+		};
+
+		USB1: usb@bffd0000 {
+			compatible = "ohci-le";
+			reg = <4 0xbffd0000 0x60>;
+			interrupt-parent = <&UIC2>;
+			interrupts = <0x1e 4>;
+		};
+
+		USBOTG0: usbotg@bff80000 {
+			compatible = "amcc,dwc-otg";
+			reg = <0x4 0xbff80000 0x10000>;
+			interrupt-parent = <&USBOTG0>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x1 0x2>;
+			interrupt-map = </* USB-OTG */ 0x0 &UIC2 0x1c 0x4
+					 /* HIGH-POWER */ 0x1 &UIC1 0x1a 0x8
+					 /* DMA */ 0x2 &UIC0 0xc 0x4>;
+		};
+
+		AHBDMA: dma@bffd0800 {
+			compatible = "snps,dma-spear1340";
+			reg = <4 0xbffd0800 0x400>;
+			interrupt-parent = <&UIC3>;
+			interrupts = <0x5 0x4>;
+			#dma-cells = <3>;
+		};
+
+		SATA0: sata@bffd1000 {
+			compatible = "amcc,sata-460ex";
+			reg = <4 0xbffd1000 0x800>;
+			interrupt-parent = <&UIC3>;
+			interrupts = <0x0 0x4>;
+			dmas = <&AHBDMA 0 1 0>;
+			dma-names = "sata-dma";
+		};
+
+		POB0: opb {
+			compatible = "ibm,opb-460ex", "ibm,opb";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xb0000000 0x00000004 0xb0000000 0x50000000>;
+			clock-frequency = <0>; /* Filled in by U-Boot */
+
+			EBC0: ebc {
+				compatible = "ibm,ebc-460ex", "ibm,ebc";
+				dcr-reg = <0x012 0x002>;
+				#address-cells = <2>;
+				#size-cells = <1>;
+				clock-frequency = <0>; /* Filled in by U-Boot */
+				/* ranges property is supplied by U-Boot */
+				interrupts = <0x6 0x4>;
+				interrupt-parent = <&UIC1>;
+
+				nor_flash@0,0 {
+					compatible = "amd,s29gl512n", "cfi-flash";
+					bank-width = <2>;
+					reg = <0x00000000 0x00000000 0x04000000>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					partition@0 {
+						label = "kernel";
+						reg = <0x00000000 0x001e0000>;
+					};
+					partition@1e0000 {
+						label = "dtb";
+						reg = <0x001e0000 0x00020000>;
+					};
+					partition@200000 {
+						label = "ramdisk";
+						reg = <0x00200000 0x01400000>;
+					};
+					partition@1600000 {
+						label = "jffs2";
+						reg = <0x01600000 0x00400000>;
+					};
+					partition@1a00000 {
+						label = "user";
+						reg = <0x01a00000 0x02560000>;
+					};
+					partition@3f60000 {
+						label = "env";
+						reg = <0x03f60000 0x00040000>;
+					};
+					partition@3fa0000 {
+						label = "u-boot";
+						reg = <0x03fa0000 0x00060000>;
+					};
+				};
+
+				cpld@2,0 {
+					compatible = "amcc,ppc460ex-bcsr";
+					reg = <2 0x0 0x9>;
+				};
+
+				ndfc@3,0 {
+					compatible = "ibm,ndfc";
+					reg = <0x00000003 0x00000000 0x00002000>;
+					ccr = <0x00001000>;
+					bank-settings = <0x80002222>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+
+					nand {
+						#address-cells = <1>;
+						#size-cells = <1>;
+
+						partition@0 {
+							label = "u-boot";
+							reg = <0x00000000 0x00100000>;
+						};
+						partition@100000 {
+							label = "user";
+							reg = <0x00000000 0x03f00000>;
+						};
+					};
+				};
+			};
+
+			UART0: serial@ef600300 {
+				device_type = "serial";
+				compatible = "ns16550";
+				reg = <0xef600300 0x00000008>;
+				virtual-reg = <0xef600300>;
+				clock-frequency = <0>; /* Filled in by U-Boot */
+				current-speed = <0>; /* Filled in by U-Boot */
+				interrupt-parent = <&UIC1>;
+				interrupts = <0x1 0x4>;
+			};
+
+			UART1: serial@ef600400 {
+				device_type = "serial";
+				compatible = "ns16550";
+				reg = <0xef600400 0x00000008>;
+				virtual-reg = <0xef600400>;
+				clock-frequency = <0>; /* Filled in by U-Boot */
+				current-speed = <0>; /* Filled in by U-Boot */
+				interrupt-parent = <&UIC0>;
+				interrupts = <0x1 0x4>;
+			};
+
+			IIC0: i2c@ef600700 {
+				compatible = "ibm,iic-460ex", "ibm,iic";
+				reg = <0xef600700 0x00000014>;
+				interrupt-parent = <&UIC0>;
+				interrupts = <0x2 0x4>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+                                rtc@68 {
+                                        compatible = "st,m41t80";
+                                        reg = <0x68>;
+					interrupt-parent = <&UIC2>;
+					interrupts = <0x19 0x8>;
+                                };
+                                sttm@48 {
+                                        compatible = "ad,ad7414";
+                                        reg = <0x48>;
+					interrupt-parent = <&UIC1>;
+					interrupts = <0x14 0x8>;
+                                };
+			};
+
+			IIC1: i2c@ef600800 {
+				compatible = "ibm,iic-460ex", "ibm,iic";
+				reg = <0xef600800 0x00000014>;
+				interrupt-parent = <&UIC0>;
+				interrupts = <0x3 0x4>;
+			};
+
+			GPIO0: gpio@ef600b00 {
+				compatible = "ibm,ppc4xx-gpio";
+				reg = <0xef600b00 0x00000048>;
+				gpio-controller;
+			};
+
+			ZMII0: emac-zmii@ef600d00 {
+				compatible = "ibm,zmii-460ex", "ibm,zmii";
+				reg = <0xef600d00 0x0000000c>;
+			};
+
+			RGMII0: emac-rgmii@ef601500 {
+				compatible = "ibm,rgmii-460ex", "ibm,rgmii";
+				reg = <0xef601500 0x00000008>;
+				has-mdio;
+			};
+
+			TAH0: emac-tah@ef601350 {
+				compatible = "ibm,tah-460ex", "ibm,tah";
+				reg = <0xef601350 0x00000030>;
+			};
+
+			TAH1: emac-tah@ef601450 {
+				compatible = "ibm,tah-460ex", "ibm,tah";
+				reg = <0xef601450 0x00000030>;
+			};
+
+			EMAC0: ethernet@ef600e00 {
+				device_type = "network";
+				compatible = "ibm,emac-460ex", "ibm,emac4sync";
+				interrupt-parent = <&EMAC0>;
+				interrupts = <0x0 0x1>;
+				#interrupt-cells = <1>;
+				#address-cells = <0>;
+				#size-cells = <0>;
+				interrupt-map = </*Status*/ 0x0 &UIC2 0x10 0x4
+						 /*Wake*/   0x1 &UIC2 0x14 0x4>;
+				reg = <0xef600e00 0x000000c4>;
+				local-mac-address = [000000000000]; /* Filled in by U-Boot */
+				mal-device = <&MAL0>;
+				mal-tx-channel = <0>;
+				mal-rx-channel = <0>;
+				cell-index = <0>;
+				max-frame-size = <9000>;
+				rx-fifo-size = <4096>;
+				tx-fifo-size = <2048>;
+				rx-fifo-size-gige = <16384>;
+				phy-mode = "rgmii";
+				phy-map = <0x00000000>;
+				rgmii-device = <&RGMII0>;
+				rgmii-channel = <0>;
+				tah-device = <&TAH0>;
+				tah-channel = <0>;
+				has-inverted-stacr-oc;
+				has-new-stacr-staopc;
+			};
+
+			EMAC1: ethernet@ef600f00 {
+				device_type = "network";
+				compatible = "ibm,emac-460ex", "ibm,emac4sync";
+				interrupt-parent = <&EMAC1>;
+				interrupts = <0x0 0x1>;
+				#interrupt-cells = <1>;
+				#address-cells = <0>;
+				#size-cells = <0>;
+				interrupt-map = </*Status*/ 0x0 &UIC2 0x11 0x4
+						 /*Wake*/   0x1 &UIC2 0x15 0x4>;
+				reg = <0xef600f00 0x000000c4>;
+				local-mac-address = [000000000000]; /* Filled in by U-Boot */
+				mal-device = <&MAL0>;
+				mal-tx-channel = <1>;
+				mal-rx-channel = <8>;
+				cell-index = <1>;
+				max-frame-size = <9000>;
+				rx-fifo-size = <4096>;
+				tx-fifo-size = <2048>;
+				rx-fifo-size-gige = <16384>;
+				phy-mode = "rgmii";
+				phy-map = <0x00000000>;
+				rgmii-device = <&RGMII0>;
+				rgmii-channel = <1>;
+				tah-device = <&TAH1>;
+				tah-channel = <1>;
+				has-inverted-stacr-oc;
+				has-new-stacr-staopc;
+				mdio-device = <&EMAC0>;
+			};
+		};
+
+		PCIX0: pci@c0ec00000 {
+			device_type = "pci";
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb-pcix-460ex", "ibm,plb-pcix";
+			primary;
+			large-inbound-windows;
+			enable-msi-hole;
+			reg = <0x0000000c 0x0ec00000   0x00000008	/* Config space access */
+			       0x00000000 0x00000000 0x00000000		/* no IACK cycles */
+			       0x0000000c 0x0ed00000   0x00000004   /* Special cycles */
+			       0x0000000c 0x0ec80000 0x00000100	/* Internal registers */
+			       0x0000000c 0x0ec80100  0x000000fc>;	/* Internal messaging registers */
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <0x02000000 0x00000000 0x80000000 0x0000000d 0x80000000 0x00000000 0x80000000
+				  0x02000000 0x00000000 0x00000000 0x0000000c 0x0ee00000 0x00000000 0x00100000
+				  0x01000000 0x00000000 0x00000000 0x0000000c 0x08000000 0x00000000 0x00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+			/* This drives busses 0 to 0x3f */
+			bus-range = <0x0 0x3f>;
+
+			/* All PCI interrupts are routed to ext IRQ 2 -> UIC1-0 */
+			interrupt-map-mask = <0x0 0x0 0x0 0x0>;
+			interrupt-map = < 0x0 0x0 0x0 0x0 &UIC1 0x0 0x8 >;
+		};
+
+		PCIE0: pciex@d00000000 {
+			device_type = "pci";
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb-pciex-460ex", "ibm,plb-pciex";
+			primary;
+			port = <0x0>; /* port number */
+			reg = <0x0000000d 0x00000000 0x20000000	/* Config space access */
+			       0x0000000c 0x08010000 0x00001000>;	/* Registers */
+			dcr-reg = <0x100 0x020>;
+			sdr-base = <0x300>;
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
+				  0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000
+				  0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+			/* This drives busses 40 to 0x7f */
+			bus-range = <0x40 0x7f>;
+
+			/* Legacy interrupts (note the weird polarity, the bridge seems
+			 * to invert PCIe legacy interrupts).
+			 * We are de-swizzling here because the numbers are actually for
+			 * port of the root complex virtual P2P bridge. But I want
+			 * to avoid putting a node for it in the tree, so the numbers
+			 * below are basically de-swizzled numbers.
+			 * The real slot is on idsel 0, so the swizzling is 1:1
+			 */
+			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+			interrupt-map = <
+				0x0 0x0 0x0 0x1 &UIC3 0xc 0x4 /* swizzled int A */
+				0x0 0x0 0x0 0x2 &UIC3 0xd 0x4 /* swizzled int B */
+				0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */
+				0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>;
+		};
+
+		PCIE1: pciex@d20000000 {
+			device_type = "pci";
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb-pciex-460ex", "ibm,plb-pciex";
+			primary;
+			port = <0x1>; /* port number */
+			reg = <0x0000000d 0x20000000 0x20000000	/* Config space access */
+			       0x0000000c 0x08011000 0x00001000>;	/* Registers */
+			dcr-reg = <0x120 0x020>;
+			sdr-base = <0x340>;
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
+				  0x02000000 0x00000000 0x00000000 0x0000000f 0x00100000 0x00000000 0x00100000
+				  0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+			/* This drives busses 80 to 0xbf */
+			bus-range = <0x80 0xbf>;
+
+			/* Legacy interrupts (note the weird polarity, the bridge seems
+			 * to invert PCIe legacy interrupts).
+			 * We are de-swizzling here because the numbers are actually for
+			 * port of the root complex virtual P2P bridge. But I want
+			 * to avoid putting a node for it in the tree, so the numbers
+			 * below are basically de-swizzled numbers.
+			 * The real slot is on idsel 0, so the swizzling is 1:1
+			 */
+			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+			interrupt-map = <
+				0x0 0x0 0x0 0x1 &UIC3 0x10 0x4 /* swizzled int A */
+				0x0 0x0 0x0 0x2 &UIC3 0x11 0x4 /* swizzled int B */
+				0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */
+				0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>;
+		};
+
+		MSI: ppc4xx-msi@C10000000 {
+			compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+			reg = < 0xC 0x10000000 0x100>;
+			sdr-base = <0x36C>;
+			msi-data = <0x00000000>;
+			msi-mask = <0x44440000>;
+			interrupt-count = <3>;
+			interrupts = <0 1 2 3>;
+			interrupt-parent = <&UIC3>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0 &UIC3 0x18 1
+					1 &UIC3 0x19 1
+					2 &UIC3 0x1A 1
+					3 &UIC3 0x1B 1>;
+		};
+	};
+};
diff --git a/pc-bios/u-boot-sam460-20100605.bin b/pc-bios/u-boot-sam460-20100605.bin
new file mode 100755
index 0000000000..99408f8e95
--- /dev/null
+++ b/pc-bios/u-boot-sam460-20100605.bin
Binary files differdiff --git a/roms/Makefile b/roms/Makefile
index b5e5a69e91..02b69fbac8 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -65,6 +65,7 @@ default:
 	@echo "  slof           -- update slof.bin"
 	@echo "  skiboot        -- update skiboot.lid"
 	@echo "  u-boot.e500    -- update u-boot.e500"
+	@echo "  u-boot.sam460  -- update u-boot.sam460"
 
 bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k
 	cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin
@@ -147,6 +148,11 @@ u-boot.e500:
 	$(powerpc_cross_prefix)strip u-boot/build.e500/u-boot -o \
 		../pc-bios/u-boot.e500
 
+u-boot.sam460:
+	$(MAKE) -C u-boot-sam460ex Sam460ex_config
+	$(MAKE) -C u-boot-sam460ex CROSS_COMPILE=$(powerpc_cross_prefix)
+	cp u-boot-sam460ex/u-boot.bin ../pc-bios/u-boot-sam460-20100605.bin
+
 skiboot:
 	$(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix)
 	cp skiboot/skiboot.lid ../pc-bios/skiboot.lid
@@ -160,4 +166,5 @@ clean:
 	$(MAKE) -C ipxe/src veryclean
 	$(MAKE) -C SLOF clean
 	rm -rf u-boot/build.e500
+	$(MAKE) -C u-boot-sam460ex distclean
 	$(MAKE) -C skiboot clean
diff --git a/roms/u-boot-sam460ex b/roms/u-boot-sam460ex
new file mode 160000
+Subproject 119aa277f74a4a2d3f7ab6c9471292308eba14e
diff --git a/target/ppc/kvm-stub.c b/target/ppc/kvm-stub.c
index efeafca1df..b8aa97f2d4 100644
--- a/target/ppc/kvm-stub.c
+++ b/target/ppc/kvm-stub.c
@@ -12,7 +12,7 @@
 #include "qemu/osdep.h"
 #include "qemu-common.h"
 #include "cpu.h"
-#include "hw/ppc/openpic.h"
+#include "hw/ppc/openpic_kvm.h"
 
 int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs)
 {
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 9842b3bb12..79a436a384 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2494,8 +2494,10 @@ static void kvmppc_get_cpu_characteristics(KVMState *s)
         cap_ppc_safe_bounds_check = 1;
     }
     /* Parse and set cap_ppc_safe_indirect_branch */
-    if (c.character & H_CPU_CHAR_BCCTRL_SERIALISED) {
-        cap_ppc_safe_indirect_branch = 2;
+    if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) {
+        cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_CCD;
+    } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) {
+        cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_IBS;
     }
 }
 
diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c
index 17a87df654..391b94b97d 100644
--- a/target/ppc/translate_init.c
+++ b/target/ppc/translate_init.c
@@ -8692,6 +8692,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                     (1ull << MSR_DR) |
                     (1ull << MSR_PMM) |
                     (1ull << MSR_RI) |
+                    (1ull << MSR_TS0) |
+                    (1ull << MSR_TS1) |
                     (1ull << MSR_LE);
     pcc->mmu_model = POWERPC_MMU_2_07;
 #if defined(CONFIG_SOFTMMU)