diff options
Diffstat (limited to 'hw/ide/macio.c')
-rw-r--r-- | hw/ide/macio.c | 476 |
1 files changed, 228 insertions, 248 deletions
diff --git a/hw/ide/macio.c b/hw/ide/macio.c index f6074f2024..66ac2baa94 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -51,128 +51,245 @@ static const int debug_macio = 0; #define MACIO_PAGE_SIZE 4096 -static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) +/* + * Unaligned DMA read/write access functions required for OS X/Darwin which + * don't perform DMA transactions on sector boundaries. These functions are + * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be + * easy to remove if the unaligned block APIs are ever exposed. + */ + +static void pmac_dma_read(BlockBackend *blk, + int64_t offset, unsigned int bytes, + void (*cb)(void *opaque, int ret), void *opaque) { DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - int unaligned; + dma_addr_t dma_addr, dma_len; + void *mem; + int64_t sector_num; + int nsector; + uint64_t align = BDRV_SECTOR_SIZE; + size_t head_bytes, tail_bytes; - if (ret < 0) { - m->aiocb = NULL; - qemu_sglist_destroy(&s->sg); - ide_atapi_io_error(s, ret); - io->remainder_len = 0; - goto done; - } + qemu_iovec_destroy(&io->iov); + qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - 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 = (offset >> 9); + nsector = (io->len >> 9); + + MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): " + "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, + sector_num, nsector); + + dma_addr = io->addr; + dma_len = io->len; + mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, + DMA_DIRECTION_FROM_DEVICE); + + if (offset & (align - 1)) { + head_bytes = offset & (align - 1); + + MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", " + "discarding %zu bytes\n", sector_num, head_bytes); + + qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); + + bytes += offset & (align - 1); + offset = offset & ~(align - 1); } - MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size); + qemu_iovec_add(&io->iov, mem, io->len); - if (s->io_buffer_size > 0) { - m->aiocb = NULL; - qemu_sglist_destroy(&s->sg); + if ((offset + bytes) & (align - 1)) { + tail_bytes = (offset + bytes) & (align - 1); - s->packet_transfer_size -= s->io_buffer_size; + MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", " + "discarding bytes %zu\n", sector_num, tail_bytes); - s->io_buffer_index += s->io_buffer_size; - s->lba += s->io_buffer_index >> 11; - s->io_buffer_index &= 0x7ff; + qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes); + bytes = ROUND_UP(bytes, align); } - s->io_buffer_size = MIN(io->len, s->packet_transfer_size); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; - 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); + io->len = 0; - MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len); + MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " + "nsector: %x\n", (offset >> 9), (bytes >> 9)); - cpu_physical_memory_write(io->addr, io->remainder + 0x200 - - remainder_len, remainder_len); + m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9), + cb, io); +} - 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; +static void pmac_dma_write(BlockBackend *blk, + int64_t offset, int bytes, + void (*cb)(void *opaque, int ret), void *opaque) +{ + DBDMA_io *io = opaque; + MACIOIDEState *m = io->opaque; + IDEState *s = idebus_active_if(&m->bus); + dma_addr_t dma_addr, dma_len; + void *mem; + int64_t sector_num; + int nsector; + uint64_t align = BDRV_SECTOR_SIZE; + size_t head_bytes, tail_bytes; + bool unaligned_head = false, unaligned_tail = false; + + qemu_iovec_destroy(&io->iov); + qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); + + sector_num = (offset >> 9); + nsector = (io->len >> 9); + + MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): " + "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, + sector_num, nsector); + + dma_addr = io->addr; + dma_len = io->len; + mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, + DMA_DIRECTION_TO_DEVICE); + + if (offset & (align - 1)) { + head_bytes = offset & (align - 1); + sector_num = ((offset & ~(align - 1)) >> 9); + + MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %" + PRId64 "\n", sector_num); + + blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); + + qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); + qemu_iovec_add(&io->iov, mem, io->len); + + bytes += offset & (align - 1); + offset = offset & ~(align - 1); + + unaligned_head = true; } - if (!s->packet_transfer_size) { - MACIO_DPRINTF("end of transfer\n"); - ide_atapi_cmd_ok(s); - m->dma_active = false; + if ((offset + bytes) & (align - 1)) { + tail_bytes = (offset + bytes) & (align - 1); + sector_num = (((offset + bytes) & ~(align - 1)) >> 9); + + MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %" + PRId64 "\n", sector_num); + + blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); + + if (!unaligned_head) { + qemu_iovec_add(&io->iov, mem, io->len); + } + + qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, + align - tail_bytes); + + bytes = ROUND_UP(bytes, align); + + unaligned_tail = true; } - if (io->len == 0) { - MACIO_DPRINTF("end of DMA\n"); - goto done; + if (!unaligned_head && !unaligned_tail) { + qemu_iovec_add(&io->iov, mem, io->len); } - /* launch next transfer */ + s->io_buffer_size -= io->len; + s->io_buffer_index += 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; + io->len = 0; - MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n", - unaligned, io->addr + io->len - unaligned); + MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " + "nsector: %x\n", (offset >> 9), (bytes >> 9)); - blk_read(s->blk, sector_num + nsector, io->remainder, 1); - cpu_physical_memory_write(io->addr + io->len - unaligned, - io->remainder, unaligned); + m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9), + cb, io); +} - io->len -= unaligned; - } +static void pmac_dma_trim(BlockBackend *blk, + int64_t offset, int bytes, + void (*cb)(void *opaque, int ret), void *opaque) +{ + DBDMA_io *io = opaque; + MACIOIDEState *m = io->opaque; + IDEState *s = idebus_active_if(&m->bus); + dma_addr_t dma_addr, dma_len; + void *mem; - MACIO_DPRINTF("io->len = %#x\n", io->len); + qemu_iovec_destroy(&io->iov); + qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - 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 += 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); + dma_addr = io->addr; + dma_len = io->len; + mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, + DMA_DIRECTION_TO_DEVICE); + + qemu_iovec_add(&io->iov, mem, io->len); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; + io->len = 0; + + m->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, (bytes >> 9), + cb, io); +} - /* 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); +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); + int64_t offset; + + MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n"); + + if (ret < 0) { + MACIO_DPRINTF("DMA error: %d\n", ret); + ide_atapi_io_error(s, ret); + 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; } - io->len = 0; + if (s->io_buffer_size <= 0) { + MACIO_DPRINTF("End of IDE transfer\n"); + ide_atapi_cmd_ok(s); + m->dma_active = false; + goto done; + } - 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); + if (io->len == 0) { + MACIO_DPRINTF("End of DMA transfer\n"); + goto done; + } - m->aiocb = dma_blk_read(s->blk, &s->sg, - (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9), - pmac_ide_atapi_transfer_cb, io); + if (s->lba == -1) { + /* Non-block ATAPI transfer - just copy to RAM */ + s->io_buffer_size = MIN(s->io_buffer_size, io->len); + cpu_physical_memory_write(io->addr, s->io_buffer, s->io_buffer_size); + ide_atapi_cmd_ok(s); + m->dma_active = false; + goto done; + } + + /* Calculate current offset */ + offset = (int64_t)(s->lba << 11) + s->io_buffer_index; + + pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io); return; done: - MACIO_DPRINTF("done DMA\n"); block_acct_done(blk_get_stats(s->blk), &s->acct); io->dma_end(opaque); + + return; } static void pmac_ide_transfer_cb(void *opaque, int ret) @@ -180,24 +297,17 @@ 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 = 0; - int64_t sector_num; - int unaligned; + int64_t offset; + + MACIO_DPRINTF("pmac_ide_transfer_cb\n"); if (ret < 0) { - MACIO_DPRINTF("DMA error\n"); + MACIO_DPRINTF("DMA error: %d\n", ret); m->aiocb = NULL; - qemu_sglist_destroy(&s->sg); ide_dma_error(s); - io->remainder_len = 0; goto done; } - if (--io->requests) { - /* More requests still in flight */ - return; - } - if (!m->dma_active) { MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n", s->nsector, io->len, s->status); @@ -206,155 +316,41 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) 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); - n = (s->io_buffer_size + 0x1ff) >> 9; - sector_num += n; - ide_set_sector(s, sector_num); - s->nsector -= n; - } - - if (io->finish_remain_read) { - /* Finish a stale read from the last iteration */ - io->finish_remain_read = false; - cpu_physical_memory_write(io->finish_addr, io->remainder, - io->finish_len); - } - - MACIO_DPRINTF("remainder: %d io->len: %d nsector: %d " - "sector_num: %" PRId64 "\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 %#" HWADDR_PRIx "\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); - break; - case IDE_DMA_TRIM: - break; - } - io->addr += remainder_len; - io->len -= remainder_len; - io->remainder_len -= remainder_len; - - if (s->dma_cmd == IDE_DMA_WRITE && !io->remainder_len) { - io->requests++; - qemu_iovec_reset(&io->iov); - qemu_iovec_add(&io->iov, io->remainder, 0x200); - - m->aiocb = blk_aio_writev(s->blk, sector_num - 1, &io->iov, 1, - pmac_ide_transfer_cb, io); - } - } - - if (s->nsector == 0 && !io->remainder_len) { - MACIO_DPRINTF("end of transfer\n"); + if (s->io_buffer_size <= 0) { + MACIO_DPRINTF("End of IDE transfer\n"); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); m->dma_active = false; + goto done; } if (io->len == 0) { - MACIO_DPRINTF("end of DMA\n"); + MACIO_DPRINTF("End of DMA transfer\n"); goto done; } - /* launch next transfer */ - - s->io_buffer_index = 0; - 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 %#" HWADDR_PRIx "\n", - unaligned, io->addr + io->len - unaligned); - - switch (s->dma_cmd) { - case IDE_DMA_READ: - io->requests++; - io->finish_addr = io->addr + io->len - unaligned; - io->finish_len = unaligned; - io->finish_remain_read = true; - qemu_iovec_reset(&io->iov); - qemu_iovec_add(&io->iov, io->remainder, 0x200); - - m->aiocb = blk_aio_readv(s->blk, sector_num + nsector, &io->iov, 1, - pmac_ide_transfer_cb, io); - break; - case IDE_DMA_WRITE: - /* cache the contents in our io struct */ - cpu_physical_memory_read(io->addr + io->len - unaligned, - io->remainder + io->remainder_len, - unaligned); - break; - case IDE_DMA_TRIM: - break; - } - } - - 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 + unaligned; - io->remainder_len = (0x200 - unaligned) & 0x1ff; - MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len); - - /* Only subsector reads happening */ - if (!io->len) { - if (!io->requests) { - io->requests++; - pmac_ide_transfer_cb(opaque, ret); - } - 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); + /* Calculate number of sectors */ + offset = (ide_get_sector(s) << 9) + s->io_buffer_index; switch (s->dma_cmd) { case IDE_DMA_READ: - m->aiocb = dma_blk_read(s->blk, &s->sg, sector_num, - pmac_ide_transfer_cb, io); + pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; case IDE_DMA_WRITE: - m->aiocb = dma_blk_write(s->blk, &s->sg, sector_num, - pmac_ide_transfer_cb, io); + pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - m->aiocb = dma_blk_io(s->blk, &s->sg, sector_num, - ide_issue_trim, pmac_ide_transfer_cb, io, - DMA_DIRECTION_TO_DEVICE); + pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io); break; } - io->requests++; return; done: if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { block_acct_done(blk_get_stats(s->blk), &s->acct); } - io->dma_end(io); + io->dma_end(opaque); } static void pmac_ide_transfer(DBDMA_io *io) @@ -364,31 +360,10 @@ static void pmac_ide_transfer(DBDMA_io *io) MACIO_DPRINTF("\n"); - s->io_buffer_size = 0; if (s->drive_kind == IDE_CD) { - - /* Handle non-block ATAPI DMA transfers */ - if (s->lba == -1) { - s->io_buffer_size = MIN(io->len, s->packet_transfer_size); - block_acct_start(blk_get_stats(s->blk), &s->acct, s->io_buffer_size, - BLOCK_ACCT_READ); - MACIO_DPRINTF("non-block ATAPI DMA transfer size: %d\n", - s->io_buffer_size); - - /* Copy ATAPI buffer directly to RAM and finish */ - cpu_physical_memory_write(io->addr, s->io_buffer, - s->io_buffer_size); - ide_atapi_cmd_ok(s); - m->dma_active = false; - - MACIO_DPRINTF("end of non-block ATAPI DMA transfer\n"); - block_acct_done(blk_get_stats(s->blk), &s->acct); - io->dma_end(io); - return; - } - block_acct_start(blk_get_stats(s->blk), &s->acct, io->len, BLOCK_ACCT_READ); + pmac_ide_atapi_transfer_cb(io, 0); return; } @@ -406,7 +381,6 @@ static void pmac_ide_transfer(DBDMA_io *io) break; } - io->requests++; pmac_ide_transfer_cb(io, 0); } @@ -553,21 +527,29 @@ static int ide_nop_int(IDEDMA *dma, int x) return 0; } -static int32_t ide_nop_int32(IDEDMA *dma, int x) +static int32_t ide_nop_int32(IDEDMA *dma, int32_t l) { return 0; } -static void ide_nop_restart(void *opaque, int x, RunState y) -{ -} - static void ide_dbdma_start(IDEDMA *dma, IDEState *s, BlockCompletionFunc *cb) { MACIOIDEState *m = container_of(dma, MACIOIDEState, dma); - MACIO_DPRINTF("\n"); + s->io_buffer_index = 0; + if (s->drive_kind == IDE_CD) { + s->io_buffer_size = s->packet_transfer_size; + } else { + s->io_buffer_size = s->nsector * BDRV_SECTOR_SIZE; + } + + MACIO_DPRINTF("\n\n------------ IDE transfer\n"); + MACIO_DPRINTF("buffer_size: %x buffer_index: %x\n", + s->io_buffer_size, s->io_buffer_index); + MACIO_DPRINTF("lba: %x size: %x\n", s->lba, s->io_buffer_size); + MACIO_DPRINTF("-------------------------\n"); + m->dma_active = true; DBDMA_kick(m->dbdma); } @@ -576,8 +558,6 @@ static const IDEDMAOps dbdma_ops = { .start_dma = ide_dbdma_start, .prepare_buf = ide_nop_int32, .rw_buf = ide_nop_int, - .set_unit = ide_nop_int, - .restart_cb = ide_nop_restart, }; static void macio_ide_realizefn(DeviceState *dev, Error **errp) |