From patchwork Thu Jul 11 17:01:23 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 258604 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id F31EF2C035E for ; Fri, 12 Jul 2013 04:40:46 +1000 (EST) Received: from localhost ([::1]:59038 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UxKKH-0006Ui-Ng for incoming@patchwork.ozlabs.org; Thu, 11 Jul 2013 13:06:41 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42823) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UxKFN-0001D6-4u for qemu-devel@nongnu.org; Thu, 11 Jul 2013 13:01:44 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UxKFE-0001Pn-Oo for qemu-devel@nongnu.org; Thu, 11 Jul 2013 13:01:36 -0400 Received: from cantor2.suse.de ([195.135.220.15]:54968 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UxKFE-0001Oe-9a; Thu, 11 Jul 2013 13:01:28 -0400 Received: from relay1.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 8E399A5443; Thu, 11 Jul 2013 19:01:26 +0200 (CEST) From: Alexander Graf To: qemu-devel@nongnu.org Date: Thu, 11 Jul 2013 19:01:23 +0200 Message-Id: <1373562085-29728-21-git-send-email-agraf@suse.de> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1373562085-29728-1-git-send-email-agraf@suse.de> References: <1373562085-29728-1-git-send-email-agraf@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x X-Received-From: 195.135.220.15 Cc: Blue Swirl , qemu-ppc@nongnu.org, Aurelien Jarno Subject: [Qemu-devel] [PATCH 20/22] PPC: dbdma: Support unaligned DMA access X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The DBDMA engine really just reads bytes from a producing device (IDE in our case) and shoves these bytes into memory. It doesn't care whether any alignment takes place or not. Our code today however assumes that block accesses always happen on sector (512 byte) boundaries. This is a fair assumption for most cases. However, Mac OS X really likes to do unaligned, incomplete accesses that it finishes with the next DMA request. So we need to read / write the unaligned bits independent of the actual asynchronous request, because that one can only handle 512-byte-aligned data. We also need to cache these unaligned sectors until the next DMA request, at which point the data might be successfully flushed from the pipe. Signed-off-by: Alexander Graf --- hw/ide/macio.c | 131 ++++++++++++++++++++++++++++++++++++++++++--- include/hw/ppc/mac_dbdma.h | 3 ++ 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 2b1e51d..18bc1d1 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -56,11 +56,13 @@ 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; } @@ -85,7 +87,31 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) s->io_buffer_index &= 0x7ff; } - if (s->packet_transfer_size <= 0) { + s->io_buffer_size = 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); + + 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; @@ -98,14 +124,40 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) /* launch next transfer */ - MACIO_DPRINTF("io->len = %#x\n", 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; - s->io_buffer_size = io->len; + 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", @@ -128,14 +180,16 @@ 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); + io->remainder_len = 0; goto done; } @@ -158,7 +212,33 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->nsector -= n; } - 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); @@ -175,12 +255,49 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) s->io_buffer_index = 0; 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 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", diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 4d7318d..90efd27 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -39,6 +39,9 @@ struct DBDMA_io { DBDMA_end dma_end; /* DMA is in progress, don't start another one */ bool processing; + /* unaligned last sector of a request */ + uint8_t remainder[0x200]; + int remainder_len; }; /*