aboutsummaryrefslogtreecommitdiff
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);
}