Commit 4827ac1e authored by Mark Cave-Ayland's avatar Mark Cave-Ayland Committed by John Snow
Browse files

macio: move unaligned DMA read code into separate pmac_dma_read() function



This considerably helps simplify the complexity of the macio read routines and
by switching macio CDROM accesses to use the new code, fixes the issue with
the CDROM device being detected intermittently by Darwin/OS X.

[Maintainer edit: printf format codes adjusted for 32/64bit. --js]

Signed-off-by: default avatarMark Cave-Ayland <mark.cave-ayland@ailande.co.uk>
Acked-by: default avatarJohn Snow <jsnow@redhat.com>
Message-id: 1425939893-14404-2-git-send-email-mark.cave-ayland@ilande.co.uk
Signed-off-by: default avatarJohn Snow <jsnow@redhat.com>
parent 5560b85a
Loading
Loading
Loading
Loading
+147 −106
Original line number Diff line number Diff line
@@ -51,128 +51,166 @@ static const int debug_macio = 0;

#define MACIO_PAGE_SIZE 4096

static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
static void pmac_dma_read(BlockBackend *blk,
                          int64_t sector_num, int nb_sectors,
                          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;
    int nsector, remainder;

    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 (io->remainder_len > 0) {
        /* Return remainder of request */
        int transfer = MIN(io->remainder_len, io->len);

        MACIO_DPRINTF("--- DMA read pop     - bounce addr: %p addr: %"
                      HWADDR_PRIx " remainder_len: %x\n",
                      &io->remainder + (0x200 - transfer), io->addr,
                      io->remainder_len);

        cpu_physical_memory_write(io->addr,
                                  &io->remainder + (0x200 - transfer),
                                  transfer);

        io->remainder_len -= transfer;
        io->len -= transfer;
        io->addr += transfer;

        s->io_buffer_index += transfer;
        s->io_buffer_size -= transfer;

        if (io->remainder_len != 0) {
            /* Still waiting for remainder */
            return;
        }

    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;
        if (io->len == 0) {
            MACIO_DPRINTF("--- finished all read processing; go and finish\n");
            cb(opaque, 0);
            return;
        }
    }

    MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
    if (s->drive_kind == IDE_CD) {
        sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9);
    } else {
        sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9);
    }

    if (s->io_buffer_size > 0) {
        m->aiocb = NULL;
        qemu_sglist_destroy(&s->sg);
    nsector = ((io->len + 0x1ff) >> 9);
    remainder = (nsector << 9) - io->len;

    MACIO_DPRINTF("--- DMA read transfer - addr: %" HWADDR_PRIx " len: %x\n",
                  io->addr, io->len);

        s->packet_transfer_size -= s->io_buffer_size;
    dma_addr = io->addr;
    dma_len = io->len;
    mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
                         DMA_DIRECTION_FROM_DEVICE);

        s->io_buffer_index += s->io_buffer_size;
        s->lba += s->io_buffer_index >> 11;
        s->io_buffer_index &= 0x7ff;
    if (!remainder) {
        MACIO_DPRINTF("--- DMA read aligned - addr: %" HWADDR_PRIx
                      " len: %x\n", io->addr, io->len);
        qemu_iovec_add(&io->iov, mem, io->len);
    } else {
        MACIO_DPRINTF("--- DMA read unaligned - addr: %" HWADDR_PRIx
                      " len: %x\n", io->addr, io->len);
        qemu_iovec_add(&io->iov, mem, io->len);

        MACIO_DPRINTF("--- DMA read push    - bounce addr: %p "
                      "remainder_len: %x\n",
                      &io->remainder + 0x200 - remainder, remainder);
        qemu_iovec_add(&io->iov, &io->remainder + 0x200 - remainder,
                       remainder);

        io->remainder_len = remainder;
    }

    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",
                  sector_num, nsector);

        cpu_physical_memory_write(io->addr, io->remainder + 0x200 -
                                  remainder_len, remainder_len);
    m->aiocb = blk_aio_readv(blk, sector_num, &io->iov, nsector, 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);
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 sector_num;
    int nsector, remainder;

    MACIO_DPRINTF("\ns is %p\n", s);
    MACIO_DPRINTF("io_buffer_index: %x\n", s->io_buffer_index);
    MACIO_DPRINTF("io_buffer_size: %x   packet_transfer_size: %x\n",
                  s->io_buffer_size, s->packet_transfer_size);
    MACIO_DPRINTF("lba: %x\n", s->lba);
    MACIO_DPRINTF("io_addr: %" HWADDR_PRIx "  io_len: %x\n", io->addr,
                  io->len);

    if (ret < 0) {
        MACIO_DPRINTF("THERE WAS AN 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;
    }

    if (!s->packet_transfer_size) {
        MACIO_DPRINTF("end of transfer\n");
    if (s->io_buffer_size <= 0) {
        ide_atapi_cmd_ok(s);
        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 */

    /* 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 %#" HWADDR_PRIx "\n",
                      unaligned, io->addr + io->len - unaligned);

        blk_read(s->blk, 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 += 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;
    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;
    }

    io->len = 0;
    /* Calculate number of sectors */
    sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9);
    nsector = (io->len + 0x1ff) >> 9;
    remainder = io->len & 0x1ff;

    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);
    MACIO_DPRINTF("nsector: %d   remainder: %x\n", nsector, remainder);
    MACIO_DPRINTF("sector: %"PRIx64"   %zx\n", sector_num, io->iov.size / 512);

    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);
    pmac_dma_read(s->blk, sector_num, nsector, pmac_ide_atapi_transfer_cb, io);
    return;

done:
    MACIO_DPRINTF("done DMA\n");
    MACIO_DPRINTF("done DMA\n\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)
@@ -364,33 +402,14 @@ 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;
    } else {
        s->io_buffer_size = 0;
    }

    switch (s->dma_cmd) {
@@ -562,6 +581,28 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
                            BlockCompletionFunc *cb)
{
    MACIOIDEState *m = container_of(dma, MACIOIDEState, dma);
    DBDMAState *dbdma = m->dbdma;
    DBDMA_io *io;
    int i;

    if (s->drive_kind == IDE_CD) {
        s->io_buffer_index = 0;
        s->io_buffer_size = s->packet_transfer_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");

        for (i = 0; i < DBDMA_CHANNELS; i++) {
            io = &dbdma->channels[i].io;

            if (io->opaque == m) {
                io->remainder_len = 0;
            }
        }
    }

    MACIO_DPRINTF("\n");
    m->dma_active = true;