summary refs log tree commit diff stats
path: root/hw/ide/macio.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ide/macio.c')
-rw-r--r--hw/ide/macio.c241
1 files changed, 227 insertions, 14 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);
 }