Message ID | 1348628371-6828-1-git-send-email-david@gibson.dropbear.id.au |
---|---|
State | New |
Headers | show |
On 09/26/12 04:59, David Gibson wrote: > With the IOMMU infrastructure introduced before 1.2, we need to use > dma_memory_map() to obtain a qemu pointer to memory from an IO bus address. > However, dma_memory_map() alters the given length to reflect the length > over which the used DMA translation is valid - which could be either more > or less than the requested length. > > usb_packet_map() does not correctly handle these cases, simply failing if > dma_memory_map() alters the requested length. If dma_memory_map() > increased the length, we just need to use the requested length for the > qemu_iovec_add(). However, if it decreased the length, it means that a > single DMA translation is not valid for the whole sglist element, and so > we need to loop, splitting it up into multiple iovec entries for each > piece with a DMA translation (in practice >2 pieces is unlikely). > > This patch implements the correct behaviour Patch added to usb patch queue. thanks, Gerd
diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c index c0de30e..703e2d2 100644 --- a/hw/usb/libhw.c +++ b/hw/usb/libhw.c @@ -28,19 +28,25 @@ int usb_packet_map(USBPacket *p, QEMUSGList *sgl) { DMADirection dir = (p->pid == USB_TOKEN_IN) ? DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - dma_addr_t len; void *mem; int i; for (i = 0; i < sgl->nsg; i++) { - len = sgl->sg[i].len; - mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &len, dir); - if (!mem) { - goto err; - } - qemu_iovec_add(&p->iov, mem, len); - if (len != sgl->sg[i].len) { - goto err; + dma_addr_t base = sgl->sg[i].base; + dma_addr_t len = sgl->sg[i].len; + + while (len) { + dma_addr_t xlen = len; + mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &xlen, dir); + if (!mem) { + goto err; + } + if (xlen > len) { + xlen = len; + } + qemu_iovec_add(&p->iov, mem, xlen); + len -= xlen; + base += xlen; } } return 0;
With the IOMMU infrastructure introduced before 1.2, we need to use dma_memory_map() to obtain a qemu pointer to memory from an IO bus address. However, dma_memory_map() alters the given length to reflect the length over which the used DMA translation is valid - which could be either more or less than the requested length. usb_packet_map() does not correctly handle these cases, simply failing if dma_memory_map() alters the requested length. If dma_memory_map() increased the length, we just need to use the requested length for the qemu_iovec_add(). However, if it decreased the length, it means that a single DMA translation is not valid for the whole sglist element, and so we need to loop, splitting it up into multiple iovec entries for each piece with a DMA translation (in practice >2 pieces is unlikely). This patch implements the correct behaviour Signed-off-by: David Gibson <david@gibson.dropbear.id.au> --- hw/usb/libhw.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)