155de06f24
Elements in qemu SGLists can cross IOMMU page boundaries. So, in commit
39c138c842
"usb: Fix usb_packet_map() in the
presence of IOMMUs", I changed usb_packet_map() to split up each SGList
element on IOMMU page boundaries and each resulting piece of qemu's memory
space separately to the iovec the usb code uses internally.
That was correct in concept, but the patch has a bug. The 'base' variable
correctly steps through the dma address of each piece, but then we call
the dma_memory_map() function on the base address of the whole SGList
element every time.
This patch fixes at least one problem using XHCI on the pseries guest
machine. It didn't affect OHCI because that doesn't use usb_packet_map().
In theory it also affects EHCI, but we haven't observed that in practice.
I think the transfers were small enough on EHCI that they never crossed an
IOMMU page boundary in practice.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
71 lines
2.3 KiB
C
71 lines
2.3 KiB
C
/*
|
|
* QEMU USB emulation, libhw bits.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "qemu-common.h"
|
|
#include "cpu-common.h"
|
|
#include "hw/usb.h"
|
|
#include "dma.h"
|
|
|
|
int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
|
|
{
|
|
DMADirection dir = (p->pid == USB_TOKEN_IN) ?
|
|
DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE;
|
|
void *mem;
|
|
int i;
|
|
|
|
for (i = 0; i < sgl->nsg; i++) {
|
|
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, 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;
|
|
|
|
err:
|
|
usb_packet_unmap(p, sgl);
|
|
return -1;
|
|
}
|
|
|
|
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl)
|
|
{
|
|
DMADirection dir = (p->pid == USB_TOKEN_IN) ?
|
|
DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE;
|
|
int i;
|
|
|
|
for (i = 0; i < p->iov.niov; i++) {
|
|
dma_memory_unmap(sgl->dma, p->iov.iov[i].iov_base,
|
|
p->iov.iov[i].iov_len, dir,
|
|
p->iov.iov[i].iov_len);
|
|
}
|
|
}
|