summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/ide/macio.c241
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/xics.c (renamed from hw/ppc/xics.c)0
-rw-r--r--hw/misc/macio/mac_dbdma.c193
-rw-r--r--hw/misc/macio/macio.c126
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/mac.h3
-rw-r--r--hw/ppc/mac_newworld.c5
-rw-r--r--hw/ppc/mac_oldworld.c22
-rw-r--r--hw/ppc/spapr.c5
-rw-r--r--hw/ppc/spapr_hcall.c10
-rw-r--r--hw/ppc/spapr_pci.c4
12 files changed, 394 insertions, 218 deletions
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 479820239e..38ad92423d 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -30,6 +30,22 @@
 
 #include <hw/ide/internal.h>
 
+/* debug MACIO */
+// #define DEBUG_MACIO
+
+#ifdef DEBUG_MACIO
+static const int debug_macio = 1;
+#else
+static const int debug_macio = 0;
+#endif
+
+#define MACIO_DPRINTF(fmt, ...) do { \
+        if (debug_macio) { \
+            printf(fmt , ## __VA_ARGS__); \
+        } \
+    } while (0)
+
+
 /***********************************************************/
 /* MacIO based PowerPC IDE */
 
@@ -40,14 +56,26 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
     DBDMA_io *io = opaque;
     MACIOIDEState *m = io->opaque;
     IDEState *s = idebus_active_if(&m->bus);
+    int unaligned;
 
     if (ret < 0) {
         m->aiocb = NULL;
         qemu_sglist_destroy(&s->sg);
         ide_atapi_io_error(s, ret);
+        io->remainder_len = 0;
         goto done;
     }
 
+    if (!m->dma_active) {
+        MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
+                      s->nsector, io->len, s->status);
+        /* data not ready yet, wait for the channel to get restarted */
+        io->processing = false;
+        return;
+    }
+
+    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
+
     if (s->io_buffer_size > 0) {
         m->aiocb = NULL;
         qemu_sglist_destroy(&s->sg);
@@ -55,33 +83,94 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
         s->packet_transfer_size -= s->io_buffer_size;
 
         s->io_buffer_index += s->io_buffer_size;
-	s->lba += s->io_buffer_index >> 11;
+        s->lba += s->io_buffer_index >> 11;
         s->io_buffer_index &= 0x7ff;
     }
 
-    if (s->packet_transfer_size <= 0)
+    s->io_buffer_size = MIN(io->len, s->packet_transfer_size);
+
+    MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len,
+                  io->len, s->packet_transfer_size);
+    if (io->remainder_len && io->len) {
+        /* guest wants the rest of its previous transfer */
+        int remainder_len = MIN(io->remainder_len, io->len);
+
+        MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len);
+
+        cpu_physical_memory_write(io->addr, io->remainder + 0x200 -
+                                  remainder_len, remainder_len);
+
+        io->addr += remainder_len;
+        io->len -= remainder_len;
+        s->io_buffer_size = remainder_len;
+        io->remainder_len -= remainder_len;
+        /* treat remainder as individual transfer, start again */
+        qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
+                         &address_space_memory);
+        pmac_ide_atapi_transfer_cb(opaque, 0);
+        return;
+    }
+
+    if (!s->packet_transfer_size) {
+        MACIO_DPRINTF("end of transfer\n");
         ide_atapi_cmd_ok(s);
+        m->dma_active = false;
+    }
 
     if (io->len == 0) {
+        MACIO_DPRINTF("end of DMA\n");
         goto done;
     }
 
     /* launch next transfer */
 
-    s->io_buffer_size = io->len;
+    /* handle unaligned accesses first, get them over with and only do the
+       remaining bulk transfer using our async DMA helpers */
+    unaligned = io->len & 0x1ff;
+    if (unaligned) {
+        int sector_num = (s->lba << 2) + (s->io_buffer_index >> 9);
+        int nsector = io->len >> 9;
+
+        MACIO_DPRINTF("precopying unaligned %d bytes to %#lx\n",
+                      unaligned, io->addr + io->len - unaligned);
+
+        bdrv_read(s->bs, sector_num + nsector, io->remainder, 1);
+        cpu_physical_memory_write(io->addr + io->len - unaligned,
+                                  io->remainder, unaligned);
+
+        io->len -= unaligned;
+    }
+
+    MACIO_DPRINTF("io->len = %#x\n", io->len);
 
     qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                      &address_space_memory);
     qemu_sglist_add(&s->sg, io->addr, io->len);
-    io->addr += io->len;
+    io->addr += s->io_buffer_size;
+    io->remainder_len = MIN(s->packet_transfer_size - s->io_buffer_size,
+                            (0x200 - unaligned) & 0x1ff);
+    MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);
+
+    /* We would read no data from the block layer, thus not get a callback.
+       Just fake completion manually. */
+    if (!io->len) {
+        pmac_ide_atapi_transfer_cb(opaque, 0);
+        return;
+    }
+
     io->len = 0;
 
+    MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n",
+                  (s->lba << 2) + (s->io_buffer_index >> 9),
+                  s->packet_transfer_size, s->dma_cmd);
+
     m->aiocb = dma_bdrv_read(s->bs, &s->sg,
                              (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
                              pmac_ide_atapi_transfer_cb, io);
     return;
 
 done:
+    MACIO_DPRINTF("done DMA\n");
     bdrv_acct_done(s->bs, &s->acct);
     io->dma_end(opaque);
 }
@@ -91,17 +180,29 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
     DBDMA_io *io = opaque;
     MACIOIDEState *m = io->opaque;
     IDEState *s = idebus_active_if(&m->bus);
-    int n;
+    int n = 0;
     int64_t sector_num;
+    int unaligned;
 
     if (ret < 0) {
+        MACIO_DPRINTF("DMA error\n");
         m->aiocb = NULL;
         qemu_sglist_destroy(&s->sg);
-	ide_dma_error(s);
+        ide_dma_error(s);
+        io->remainder_len = 0;
         goto done;
     }
 
+    if (!m->dma_active) {
+        MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
+                      s->nsector, io->len, s->status);
+        /* data not ready yet, wait for the channel to get restarted */
+        io->processing = false;
+        return;
+    }
+
     sector_num = ide_get_sector(s);
+    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
     if (s->io_buffer_size > 0) {
         m->aiocb = NULL;
         qemu_sglist_destroy(&s->sg);
@@ -111,36 +212,105 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
         s->nsector -= n;
     }
 
-    /* end of transfer ? */
-    if (s->nsector == 0) {
+    MACIO_DPRINTF("remainder: %d io->len: %d nsector: %d sector_num: %ld\n",
+                  io->remainder_len, io->len, s->nsector, sector_num);
+    if (io->remainder_len && io->len) {
+        /* guest wants the rest of its previous transfer */
+        int remainder_len = MIN(io->remainder_len, io->len);
+        uint8_t *p = &io->remainder[0x200 - remainder_len];
+
+        MACIO_DPRINTF("copying remainder %d bytes at %#lx\n",
+                      remainder_len, io->addr);
+
+        switch (s->dma_cmd) {
+        case IDE_DMA_READ:
+            cpu_physical_memory_write(io->addr, p, remainder_len);
+            break;
+        case IDE_DMA_WRITE:
+            cpu_physical_memory_read(io->addr, p, remainder_len);
+            bdrv_write(s->bs, sector_num - 1, io->remainder, 1);
+            break;
+        case IDE_DMA_TRIM:
+            break;
+        }
+        io->addr += remainder_len;
+        io->len -= remainder_len;
+        io->remainder_len -= remainder_len;
+    }
+
+    if (s->nsector == 0 && !io->remainder_len) {
+        MACIO_DPRINTF("end of transfer\n");
         s->status = READY_STAT | SEEK_STAT;
         ide_set_irq(s->bus);
+        m->dma_active = false;
     }
 
-    /* end of DMA ? */
     if (io->len == 0) {
+        MACIO_DPRINTF("end of DMA\n");
         goto done;
     }
 
     /* launch next transfer */
 
     s->io_buffer_index = 0;
-    s->io_buffer_size = io->len;
+    s->io_buffer_size = MIN(io->len, s->nsector * 512);
+
+    /* handle unaligned accesses first, get them over with and only do the
+       remaining bulk transfer using our async DMA helpers */
+    unaligned = io->len & 0x1ff;
+    if (unaligned) {
+        int nsector = io->len >> 9;
+
+        MACIO_DPRINTF("precopying unaligned %d bytes to %#lx\n",
+                      unaligned, io->addr + io->len - unaligned);
+
+        switch (s->dma_cmd) {
+        case IDE_DMA_READ:
+            bdrv_read(s->bs, sector_num + nsector, io->remainder, 1);
+            cpu_physical_memory_write(io->addr + io->len - unaligned,
+                                      io->remainder, unaligned);
+            break;
+        case IDE_DMA_WRITE:
+            /* cache the contents in our io struct */
+            cpu_physical_memory_read(io->addr + io->len - unaligned,
+                                     io->remainder, unaligned);
+            break;
+        case IDE_DMA_TRIM:
+            break;
+        }
+
+        io->len -= unaligned;
+    }
+
+    MACIO_DPRINTF("io->len = %#x\n", io->len);
 
     qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
                      &address_space_memory);
     qemu_sglist_add(&s->sg, io->addr, io->len);
-    io->addr += io->len;
+    io->addr += io->len + unaligned;
+    io->remainder_len = (0x200 - unaligned) & 0x1ff;
+    MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);
+
+    /* We would read no data from the block layer, thus not get a callback.
+       Just fake completion manually. */
+    if (!io->len) {
+        pmac_ide_transfer_cb(opaque, 0);
+        return;
+    }
+
     io->len = 0;
 
+    MACIO_DPRINTF("sector_num=%" PRId64 " n=%d, nsector=%d, cmd_cmd=%d\n",
+                  sector_num, n, s->nsector, s->dma_cmd);
+
     switch (s->dma_cmd) {
     case IDE_DMA_READ:
         m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
-		                 pmac_ide_transfer_cb, io);
+                                 pmac_ide_transfer_cb, io);
         break;
     case IDE_DMA_WRITE:
         m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
-		                  pmac_ide_transfer_cb, io);
+                                  pmac_ide_transfer_cb, io);
         break;
     case IDE_DMA_TRIM:
         m->aiocb = dma_bdrv_io(s->bs, &s->sg, sector_num,
@@ -162,6 +332,8 @@ static void pmac_ide_transfer(DBDMA_io *io)
     MACIOIDEState *m = io->opaque;
     IDEState *s = idebus_active_if(&m->bus);
 
+    MACIO_DPRINTF("\n");
+
     s->io_buffer_size = 0;
     if (s->drive_kind == IDE_CD) {
         bdrv_acct_start(s->bs, &s->acct, io->len, BDRV_ACCT_READ);
@@ -322,11 +494,51 @@ static void macio_ide_reset(DeviceState *dev)
     ide_bus_reset(&d->bus);
 }
 
+static int ide_nop(IDEDMA *dma)
+{
+    return 0;
+}
+
+static int ide_nop_int(IDEDMA *dma, int x)
+{
+    return 0;
+}
+
+static void ide_nop_restart(void *opaque, int x, RunState y)
+{
+}
+
+static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
+                            BlockDriverCompletionFunc *cb)
+{
+    MACIOIDEState *m = container_of(dma, MACIOIDEState, dma);
+
+    MACIO_DPRINTF("\n");
+    m->dma_active = true;
+    DBDMA_kick(m->dbdma);
+}
+
+static const IDEDMAOps dbdma_ops = {
+    .start_dma      = ide_dbdma_start,
+    .start_transfer = ide_nop,
+    .prepare_buf    = ide_nop_int,
+    .rw_buf         = ide_nop_int,
+    .set_unit       = ide_nop_int,
+    .add_status     = ide_nop_int,
+    .set_inactive   = ide_nop,
+    .restart_cb     = ide_nop_restart,
+    .reset          = ide_nop,
+};
+
 static void macio_ide_realizefn(DeviceState *dev, Error **errp)
 {
     MACIOIDEState *s = MACIO_IDE(dev);
 
     ide_init2(&s->bus, s->irq);
+
+    /* Register DMA callbacks */
+    s->dma.ops = &dbdma_ops;
+    s->bus.dma = &s->dma;
 }
 
 static void macio_ide_initfn(Object *obj)
@@ -363,7 +575,7 @@ static void macio_ide_register_types(void)
     type_register_static(&macio_ide_type_info);
 }
 
-/* hd_table must contain 4 block drivers */
+/* hd_table must contain 2 block drivers */
 void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table)
 {
     int i;
@@ -377,6 +589,7 @@ void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table)
 
 void macio_ide_register_dma(MACIOIDEState *s, void *dbdma, int channel)
 {
+    s->dbdma = dbdma;
     DBDMA_register_channel(dbdma, channel, s->dma_irq,
                            pmac_ide_transfer, pmac_ide_flush, s);
 }
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 86f9d5b9df..2851eed25f 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -22,3 +22,4 @@ obj-$(CONFIG_IOAPIC) += ioapic.o
 obj-$(CONFIG_OMAP) += omap_intc.o
 obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 obj-$(CONFIG_SH4) += sh_intc.o
+obj-$(CONFIG_XICS) += xics.o
diff --git a/hw/ppc/xics.c b/hw/intc/xics.c
index 091912e2ca..091912e2ca 100644
--- a/hw/ppc/xics.c
+++ b/hw/intc/xics.c
diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
index a1d7862a66..f47a736182 100644
--- a/hw/misc/macio/mac_dbdma.c
+++ b/hw/misc/macio/mac_dbdma.c
@@ -54,122 +54,10 @@
 /*
  */
 
-/*
- * DBDMA control/status registers.  All little-endian.
- */
-
-#define DBDMA_CONTROL         0x00
-#define DBDMA_STATUS          0x01
-#define DBDMA_CMDPTR_HI       0x02
-#define DBDMA_CMDPTR_LO       0x03
-#define DBDMA_INTR_SEL        0x04
-#define DBDMA_BRANCH_SEL      0x05
-#define DBDMA_WAIT_SEL        0x06
-#define DBDMA_XFER_MODE       0x07
-#define DBDMA_DATA2PTR_HI     0x08
-#define DBDMA_DATA2PTR_LO     0x09
-#define DBDMA_RES1            0x0A
-#define DBDMA_ADDRESS_HI      0x0B
-#define DBDMA_BRANCH_ADDR_HI  0x0C
-#define DBDMA_RES2            0x0D
-#define DBDMA_RES3            0x0E
-#define DBDMA_RES4            0x0F
-
-#define DBDMA_REGS            16
-#define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
-
-#define DBDMA_CHANNEL_SHIFT   7
-#define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
-
-#define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
-
-/* Bits in control and status registers */
-
-#define RUN	0x8000
-#define PAUSE	0x4000
-#define FLUSH	0x2000
-#define WAKE	0x1000
-#define DEAD	0x0800
-#define ACTIVE	0x0400
-#define BT	0x0100
-#define DEVSTAT	0x00ff
-
-/*
- * DBDMA command structure.  These fields are all little-endian!
- */
-
-typedef struct dbdma_cmd {
-    uint16_t req_count;	  /* requested byte transfer count */
-    uint16_t command;	  /* command word (has bit-fields) */
-    uint32_t phy_addr;	  /* physical data address */
-    uint32_t cmd_dep;	  /* command-dependent field */
-    uint16_t res_count;	  /* residual count after completion */
-    uint16_t xfer_status; /* transfer status */
-} dbdma_cmd;
-
-/* DBDMA command values in command field */
-
-#define COMMAND_MASK    0xf000
-#define OUTPUT_MORE	0x0000	/* transfer memory data to stream */
-#define OUTPUT_LAST	0x1000	/* ditto followed by end marker */
-#define INPUT_MORE	0x2000	/* transfer stream data to memory */
-#define INPUT_LAST	0x3000	/* ditto, expect end marker */
-#define STORE_WORD	0x4000	/* write word (4 bytes) to device reg */
-#define LOAD_WORD	0x5000	/* read word (4 bytes) from device reg */
-#define DBDMA_NOP	0x6000	/* do nothing */
-#define DBDMA_STOP	0x7000	/* suspend processing */
-
-/* Key values in command field */
-
-#define KEY_MASK        0x0700
-#define KEY_STREAM0	0x0000	/* usual data stream */
-#define KEY_STREAM1	0x0100	/* control/status stream */
-#define KEY_STREAM2	0x0200	/* device-dependent stream */
-#define KEY_STREAM3	0x0300	/* device-dependent stream */
-#define KEY_STREAM4	0x0400	/* reserved */
-#define KEY_REGS	0x0500	/* device register space */
-#define KEY_SYSTEM	0x0600	/* system memory-mapped space */
-#define KEY_DEVICE	0x0700	/* device memory-mapped space */
-
-/* Interrupt control values in command field */
-
-#define INTR_MASK       0x0030
-#define INTR_NEVER	0x0000	/* don't interrupt */
-#define INTR_IFSET	0x0010	/* intr if condition bit is 1 */
-#define INTR_IFCLR	0x0020	/* intr if condition bit is 0 */
-#define INTR_ALWAYS	0x0030	/* always interrupt */
-
-/* Branch control values in command field */
-
-#define BR_MASK         0x000c
-#define BR_NEVER	0x0000	/* don't branch */
-#define BR_IFSET	0x0004	/* branch if condition bit is 1 */
-#define BR_IFCLR	0x0008	/* branch if condition bit is 0 */
-#define BR_ALWAYS	0x000c	/* always branch */
-
-/* Wait control values in command field */
-
-#define WAIT_MASK       0x0003
-#define WAIT_NEVER	0x0000	/* don't wait */
-#define WAIT_IFSET	0x0001	/* wait if condition bit is 1 */
-#define WAIT_IFCLR	0x0002	/* wait if condition bit is 0 */
-#define WAIT_ALWAYS	0x0003	/* always wait */
-
-typedef struct DBDMA_channel {
-    int channel;
-    uint32_t regs[DBDMA_REGS];
-    qemu_irq irq;
-    DBDMA_io io;
-    DBDMA_rw rw;
-    DBDMA_flush flush;
-    dbdma_cmd current;
-    int processing;
-} DBDMA_channel;
-
-typedef struct {
-    MemoryRegion mem;
-    DBDMA_channel channels[DBDMA_CHANNELS];
-} DBDMAState;
+static DBDMAState *dbdma_from_ch(DBDMA_channel *ch)
+{
+    return container_of(ch, DBDMAState, channels[ch->channel]);
+}
 
 #ifdef DEBUG_DBDMA
 static void dump_dbdma_cmd(dbdma_cmd *cmd)
@@ -224,7 +112,7 @@ static void conditional_interrupt(DBDMA_channel *ch)
     uint32_t status;
     int cond;
 
-    DBDMA_DPRINTF("conditional_interrupt\n");
+    DBDMA_DPRINTF("%s\n", __func__);
 
     intr = le16_to_cpu(current->command) & INTR_MASK;
 
@@ -233,6 +121,7 @@ static void conditional_interrupt(DBDMA_channel *ch)
         return;
     case INTR_ALWAYS: /* always interrupt */
         qemu_irq_raise(ch->irq);
+        DBDMA_DPRINTF("%s: raise\n", __func__);
         return;
     }
 
@@ -245,12 +134,16 @@ static void conditional_interrupt(DBDMA_channel *ch)
 
     switch(intr) {
     case INTR_IFSET:  /* intr if condition bit is 1 */
-        if (cond)
+        if (cond) {
             qemu_irq_raise(ch->irq);
+            DBDMA_DPRINTF("%s: raise\n", __func__);
+        }
         return;
     case INTR_IFCLR:  /* intr if condition bit is 0 */
-        if (!cond)
+        if (!cond) {
             qemu_irq_raise(ch->irq);
+            DBDMA_DPRINTF("%s: raise\n", __func__);
+        }
         return;
     }
 }
@@ -360,7 +253,6 @@ static void conditional_branch(DBDMA_channel *ch)
     }
 }
 
-static QEMUBH *dbdma_bh;
 static void channel_run(DBDMA_channel *ch);
 
 static void dbdma_end(DBDMA_io *io)
@@ -368,6 +260,8 @@ static void dbdma_end(DBDMA_io *io)
     DBDMA_channel *ch = io->channel;
     dbdma_cmd *current = &ch->current;
 
+    DBDMA_DPRINTF("%s\n", __func__);
+
     if (conditional_wait(ch))
         goto wait;
 
@@ -381,7 +275,9 @@ static void dbdma_end(DBDMA_io *io)
     conditional_branch(ch);
 
 wait:
-    ch->processing = 0;
+    /* Indicate that we're ready for a new DMA round */
+    ch->io.processing = false;
+
     if ((ch->regs[DBDMA_STATUS] & RUN) &&
         (ch->regs[DBDMA_STATUS] & ACTIVE))
         channel_run(ch);
@@ -407,7 +303,7 @@ static void start_output(DBDMA_channel *ch, int key, uint32_t addr,
     ch->io.is_last = is_last;
     ch->io.dma_end = dbdma_end;
     ch->io.is_dma_out = 1;
-    ch->processing = 1;
+    ch->io.processing = true;
     if (ch->rw) {
         ch->rw(&ch->io);
     }
@@ -422,6 +318,7 @@ static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
      * are not implemented in the mac-io chip
      */
 
+    DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
     if (!addr || key > KEY_STREAM3) {
         kill_channel(ch);
         return;
@@ -432,7 +329,7 @@ static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
     ch->io.is_last = is_last;
     ch->io.dma_end = dbdma_end;
     ch->io.is_dma_out = 0;
-    ch->processing = 1;
+    ch->io.processing = true;
     if (ch->rw) {
         ch->rw(&ch->io);
     }
@@ -474,7 +371,7 @@ static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
     next(ch);
 
 wait:
-    qemu_bh_schedule(dbdma_bh);
+    DBDMA_kick(dbdma_from_ch(ch));
 }
 
 static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
@@ -512,7 +409,7 @@ static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
     next(ch);
 
 wait:
-    qemu_bh_schedule(dbdma_bh);
+    DBDMA_kick(dbdma_from_ch(ch));
 }
 
 static void nop(DBDMA_channel *ch)
@@ -529,7 +426,7 @@ static void nop(DBDMA_channel *ch)
     conditional_branch(ch);
 
 wait:
-    qemu_bh_schedule(dbdma_bh);
+    DBDMA_kick(dbdma_from_ch(ch));
 }
 
 static void stop(DBDMA_channel *ch)
@@ -558,11 +455,11 @@ static void channel_run(DBDMA_channel *ch)
     switch (cmd) {
     case DBDMA_NOP:
         nop(ch);
-	return;
+        return;
 
     case DBDMA_STOP:
         stop(ch);
-	return;
+        return;
     }
 
     key = le16_to_cpu(current->command) & 0x0700;
@@ -578,19 +475,19 @@ static void channel_run(DBDMA_channel *ch)
     switch (cmd) {
     case OUTPUT_MORE:
         start_output(ch, key, phy_addr, req_count, 0);
-	return;
+        return;
 
     case OUTPUT_LAST:
         start_output(ch, key, phy_addr, req_count, 1);
-	return;
+        return;
 
     case INPUT_MORE:
         start_input(ch, key, phy_addr, req_count, 0);
-	return;
+        return;
 
     case INPUT_LAST:
         start_input(ch, key, phy_addr, req_count, 1);
-	return;
+        return;
     }
 
     if (key < KEY_REGS) {
@@ -615,11 +512,11 @@ static void channel_run(DBDMA_channel *ch)
     switch (cmd) {
     case LOAD_WORD:
         load_word(ch, key, phy_addr, req_count);
-	return;
+        return;
 
     case STORE_WORD:
         store_word(ch, key, phy_addr, req_count);
-	return;
+        return;
     }
 }
 
@@ -630,7 +527,7 @@ static void DBDMA_run(DBDMAState *s)
     for (channel = 0; channel < DBDMA_CHANNELS; channel++) {
         DBDMA_channel *ch = &s->channels[channel];
         uint32_t status = ch->regs[DBDMA_STATUS];
-        if (!ch->processing && (status & RUN) && (status & ACTIVE)) {
+        if (!ch->io.processing && (status & RUN) && (status & ACTIVE)) {
             channel_run(ch);
         }
     }
@@ -645,6 +542,11 @@ static void DBDMA_run_bh(void *opaque)
     DBDMA_run(s);
 }
 
+void DBDMA_kick(DBDMAState *dbdma)
+{
+    qemu_bh_schedule(dbdma->bh);
+}
+
 void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
                             DBDMA_rw rw, DBDMA_flush flush,
                             void *opaque)
@@ -698,10 +600,12 @@ dbdma_control_write(DBDMA_channel *ch)
 
     ch->regs[DBDMA_STATUS] = status;
 
-    if (status & ACTIVE)
-        qemu_bh_schedule(dbdma_bh);
-    if ((status & FLUSH) && ch->flush)
+    if (status & ACTIVE) {
+        DBDMA_kick(dbdma_from_ch(ch));
+    }
+    if ((status & FLUSH) && ch->flush) {
         ch->flush(&ch->io);
+    }
 }
 
 static void dbdma_write(void *opaque, hwaddr addr,
@@ -712,15 +616,16 @@ static void dbdma_write(void *opaque, hwaddr addr,
     DBDMA_channel *ch = &s->channels[channel];
     int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
 
-    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n",
+                  addr, value);
     DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
                   (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
 
-    /* cmdptr cannot be modified if channel is RUN or ACTIVE */
+    /* cmdptr cannot be modified if channel is ACTIVE */
 
-    if (reg == DBDMA_CMDPTR_LO &&
-        (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE)))
-	return;
+    if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & ACTIVE)) {
+        return;
+    }
 
     ch->regs[reg] = value;
 
@@ -853,7 +758,7 @@ void* DBDMA_init (MemoryRegion **dbdma_mem)
     vmstate_register(NULL, -1, &vmstate_dbdma, s);
     qemu_register_reset(dbdma_reset, s);
 
-    dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
+    s->bh = qemu_bh_new(DBDMA_run_bh, s);
 
     return s;
 }
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index 76a1cfba32..c0d0bf7287 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -52,10 +52,10 @@ typedef struct OldWorldMacIOState {
     MacIOState parent_obj;
     /*< public >*/
 
-    qemu_irq irqs[3];
+    qemu_irq irqs[5];
 
     MacIONVRAMState nvram;
-    MACIOIDEState ide;
+    MACIOIDEState ide[2];
 } OldWorldMacIOState;
 
 #define NEWWORLD_MACIO(obj) \
@@ -147,18 +147,32 @@ static int macio_common_initfn(PCIDevice *d)
     return 0;
 }
 
+static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
+                            qemu_irq irq1, int dmaid)
+{
+    SysBusDevice *sysbus_dev;
+
+    sysbus_dev = SYS_BUS_DEVICE(ide);
+    sysbus_connect_irq(sysbus_dev, 0, irq0);
+    sysbus_connect_irq(sysbus_dev, 1, irq1);
+    macio_ide_register_dma(ide, s->dbdma, dmaid);
+    return qdev_init(DEVICE(ide));
+}
+
 static int macio_oldworld_initfn(PCIDevice *d)
 {
     MacIOState *s = MACIO(d);
     OldWorldMacIOState *os = OLDWORLD_MACIO(d);
     SysBusDevice *sysbus_dev;
+    int i;
+    int cur_irq = 0;
     int ret = macio_common_initfn(d);
     if (ret < 0) {
         return ret;
     }
 
     sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
-    sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]);
+    sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
 
     ret = qdev_init(DEVICE(&os->nvram));
     if (ret < 0) {
@@ -174,23 +188,39 @@ static int macio_oldworld_initfn(PCIDevice *d)
         memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem);
     }
 
-    sysbus_dev = SYS_BUS_DEVICE(&os->ide);
-    sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]);
-    sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]);
-    macio_ide_register_dma(&os->ide, s->dbdma, 0x16);
-    ret = qdev_init(DEVICE(&os->ide));
-    if (ret < 0) {
-        return ret;
+    /* IDE buses */
+    for (i = 0; i < ARRAY_SIZE(os->ide); i++) {
+        qemu_irq irq0 = os->irqs[cur_irq++];
+        qemu_irq irq1 = os->irqs[cur_irq++];
+
+        ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4));
+        if (ret < 0) {
+            return ret;
+        }
     }
 
     return 0;
 }
 
+static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, int index)
+{
+    gchar *name;
+
+    object_initialize(ide, TYPE_MACIO_IDE);
+    qdev_set_parent_bus(DEVICE(ide), sysbus_get_default());
+    memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000),
+                                &ide->mem);
+    name = g_strdup_printf("ide[%i]", index);
+    object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL);
+    g_free(name);
+}
+
 static void macio_oldworld_init(Object *obj)
 {
     MacIOState *s = MACIO(obj);
     OldWorldMacIOState *os = OLDWORLD_MACIO(obj);
     DeviceState *dev;
+    int i;
 
     qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs));
 
@@ -199,48 +229,75 @@ static void macio_oldworld_init(Object *obj)
     qdev_prop_set_uint32(dev, "size", 0x2000);
     qdev_prop_set_uint32(dev, "it_shift", 4);
 
-    object_initialize(&os->ide, TYPE_MACIO_IDE);
-    qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default());
-    memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem);
-    object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL);
+    for (i = 0; i < 2; i++) {
+        macio_init_ide(s, &os->ide[i], i);
+    }
 }
 
+static void timer_write(void *opaque, hwaddr addr, uint64_t value,
+                       unsigned size)
+{
+}
+
+static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint32_t value = 0;
+
+    switch (addr) {
+    case 0x38:
+        value = qemu_get_clock_ns(vm_clock);
+        break;
+    case 0x3c:
+        value = qemu_get_clock_ns(vm_clock) >> 32;
+        break;
+    }
+
+    return value;
+}
+
+static const MemoryRegionOps timer_ops = {
+    .read = timer_read,
+    .write = timer_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
 static int macio_newworld_initfn(PCIDevice *d)
 {
     MacIOState *s = MACIO(d);
     NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
     SysBusDevice *sysbus_dev;
+    MemoryRegion *timer_memory = g_new(MemoryRegion, 1);
+    int i;
+    int cur_irq = 0;
     int ret = macio_common_initfn(d);
     if (ret < 0) {
         return ret;
     }
 
     sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]);
+    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(&ns->ide[0]);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]);
-    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]);
-    macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16);
-    ret = qdev_init(DEVICE(&ns->ide[0]));
-    if (ret < 0) {
-        return ret;
-    }
+    /* IDE buses */
+    for (i = 0; i < ARRAY_SIZE(ns->ide); i++) {
+        qemu_irq irq0 = ns->irqs[cur_irq++];
+        qemu_irq irq1 = ns->irqs[cur_irq++];
 
-    sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]);
-    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]);
-    macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a);
-    ret = qdev_init(DEVICE(&ns->ide[1]));
-    if (ret < 0) {
-        return ret;
+        ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4));
+        if (ret < 0) {
+            return ret;
+        }
     }
 
+    /* Timer */
+    memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer",
+                          0x1000);
+    memory_region_add_subregion(&s->bar, 0x15000, timer_memory);
+
     return 0;
 }
 
@@ -249,18 +306,11 @@ static void macio_newworld_init(Object *obj)
     MacIOState *s = MACIO(obj);
     NewWorldMacIOState *ns = NEWWORLD_MACIO(obj);
     int i;
-    gchar *name;
 
     qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs));
 
     for (i = 0; i < 2; i++) {
-        object_initialize(&ns->ide[i], TYPE_MACIO_IDE);
-        qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default());
-        memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000),
-                                    &ns->ide[i].mem);
-        name = g_strdup_printf("ide[%i]", i);
-        object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL);
-        g_free(name);
+        macio_init_ide(s, &ns->ide[i], i);
     }
 }
 
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index be00d1da3b..7a1cd5d89e 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -1,7 +1,7 @@
 # shared objects
 obj-y += ppc.o ppc_booke.o
 # IBM pSeries (sPAPR)
-obj-$(CONFIG_PSERIES) += spapr.o xics.o spapr_vio.o spapr_events.o
+obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o
 # PowerPC 4xx boards
diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h
index 54efaed627..1e578dd59d 100644
--- a/hw/ppc/mac.h
+++ b/hw/ppc/mac.h
@@ -131,6 +131,9 @@ typedef struct MACIOIDEState {
     MemoryRegion mem;
     IDEBus bus;
     BlockDriverAIOCB *aiocb;
+    IDEDMA dma;
+    void *dbdma;
+    bool dma_active;
 } MACIOIDEState;
 
 void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table);
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 77a8c2e28b..fe803480a7 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -71,6 +71,7 @@
 
 #define MAX_IDE_BUS 2
 #define CFG_ADDR 0xf0000510
+#define TBFREQ (100UL * 1000UL * 1000UL)
 
 /* debug UniNorth */
 //#define DEBUG_UNIN
@@ -191,7 +192,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args)
         env = &cpu->env;
 
         /* Set time-base frequency to 100 Mhz */
-        cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+        cpu_ppc_tb_init(env, TBFREQ);
         qemu_register_reset(ppc_core99_reset, cpu);
     }
 
@@ -460,7 +461,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args)
         fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
 #endif
     } else {
-        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ);
     }
     /* Mac OS X requires a "known good" clock-frequency value; pass it one. */
     fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, 266000000);
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index 4663ed2730..8b8c6b93a5 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -45,6 +45,7 @@
 
 #define MAX_IDE_BUS 2
 #define CFG_ADDR 0xf0000510
+#define TBFREQ 16600000UL
 
 static int fw_cfg_boot_set(void *opaque, const char *boot_device)
 {
@@ -114,7 +115,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args)
         env = &cpu->env;
 
         /* Set time-base frequency to 16.6 Mhz */
-        cpu_ppc_tb_init(env,  16600000UL);
+        cpu_ppc_tb_init(env,  TBFREQ);
         qemu_register_reset(ppc_heathrow_reset, cpu);
     }
 
@@ -267,20 +268,19 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args)
     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 */
-    qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */
+    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 */
     macio_init(macio, pic_mem, escc_bar);
 
-    /* First IDE channel is a MAC IDE on the MacIO bus */
     macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio),
-                                                        "ide"));
+                                                        "ide[0]"));
     macio_ide_init_drives(macio_ide, hd);
 
-    /* Second IDE channel is a CMD646 on the PCI bus */
-    hd[0] = hd[MAX_IDE_DEVS];
-    hd[1] = hd[MAX_IDE_DEVS + 1];
-    hd[3] = hd[2] = NULL;
-    pci_cmd646_ide_init(pci_bus, hd, 0);
+    macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio),
+                                                        "ide[1]"));
+    macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]);
 
     dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda"));
     adb_bus = qdev_get_child_bus(dev, "adb.0");
@@ -331,7 +331,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args)
         fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
 #endif
     } else {
-        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, TBFREQ);
     }
     /* Mac OS X requires a "known good" clock-frequency value; pass it one. */
     fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, 266000000);
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 5c31ad36bd..48ae09283d 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -940,7 +940,10 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
         }
     }
 
-    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME);
+    if (bios_name == NULL) {
+        bios_name = FW_FILE_NAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
     fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
     if (fw_size < 0) {
         hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index e6f321d538..ed32decebf 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -121,14 +121,14 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     return H_SUCCESS;
 }
 
-enum {
+typedef enum {
     REMOVE_SUCCESS = 0,
     REMOVE_NOT_FOUND = 1,
     REMOVE_PARM = 2,
     REMOVE_HW = 3,
-};
+} RemoveResult;
 
-static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
+static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
                                 target_ulong avpn,
                                 target_ulong flags,
                                 target_ulong *vp, target_ulong *rp)
@@ -165,7 +165,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
     target_ulong flags = args[0];
     target_ulong pte_index = args[1];
     target_ulong avpn = args[2];
-    int ret;
+    RemoveResult ret;
 
     ret = remove_hpte(env, pte_index, avpn, flags,
                       &args[0], &args[1]);
@@ -184,7 +184,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
         return H_HARDWARE;
     }
 
-    assert(0);
+    g_assert_not_reached();
 }
 
 #define H_BULK_REMOVE_TYPE             0xc000000000000000ULL
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 18f2e2f842..318bc9d6ef 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -451,7 +451,7 @@ static uint64_t spapr_io_read(void *opaque, hwaddr addr,
     case 4:
         return cpu_inl(addr);
     }
-    assert(0);
+    g_assert_not_reached();
 }
 
 static void spapr_io_write(void *opaque, hwaddr addr,
@@ -468,7 +468,7 @@ static void spapr_io_write(void *opaque, hwaddr addr,
         cpu_outl(addr, data);
         return;
     }
-    assert(0);
+    g_assert_not_reached();
 }
 
 static const MemoryRegionOps spapr_io_ops = {