xhci: Fix some DMA host endian bugs

The xhci device does correct endian switches on the results of some DMAs
but not all.  In particular, there are many DMAs of what are essentially
arrays of 32-bit integers which never get byteswapped.  This causes them
to be interpreted incorrectly on big-endian hosts, since (as per the xhci
spec) these arrays are always little-endian in guest memory.

This patch adds some helper functions to fix these bugs.  This may not be
all the endian bugs in the xhci code, but it's certainly some of them and
the Linux guest xhci driver certainly gets further with these fixes.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
David Gibson 2012-11-05 14:29:01 +11:00 committed by Gerd Hoffmann
parent ffd8a97fb3
commit 616b5d53ae
1 changed files with 54 additions and 27 deletions

View File

@ -634,6 +634,34 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
} }
} }
static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
uint32_t *buf, size_t len)
{
int i;
assert((len % sizeof(uint32_t)) == 0);
pci_dma_read(&xhci->pci_dev, addr, buf, len);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
buf[i] = le32_to_cpu(buf[i]);
}
}
static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
uint32_t *buf, size_t len)
{
int i;
uint32_t tmp[len / sizeof(uint32_t)];
assert((len % sizeof(uint32_t)) == 0);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
tmp[i] = cpu_to_le32(buf[i]);
}
pci_dma_write(&xhci->pci_dev, addr, tmp, len);
}
static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
{ {
int index; int index;
@ -1045,14 +1073,14 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
{ {
uint32_t ctx[5]; uint32_t ctx[5];
pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx)); xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
ctx[0] &= ~EP_STATE_MASK; ctx[0] &= ~EP_STATE_MASK;
ctx[0] |= state; ctx[0] |= state;
ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
ctx[3] = (epctx->ring.dequeue >> 16) >> 16; ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
epctx->pctx, state, ctx[3], ctx[2]); epctx->pctx, state, ctx[3], ctx[2]);
pci_dma_write(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx)); xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
epctx->state = state; epctx->state = state;
} }
@ -1881,14 +1909,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
assert(slotid >= 1 && slotid <= xhci->numslots); assert(slotid >= 1 && slotid <= xhci->numslots);
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx)); poctx = ldq_le_pci_dma(&xhci->pci_dev, dcbaap + 8*slotid);
ictx = xhci_mask64(pictx); ictx = xhci_mask64(pictx);
octx = xhci_mask64(le64_to_cpu(poctx)); octx = xhci_mask64(poctx);
DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) { if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -1896,8 +1924,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR; return CC_TRB_ERROR;
} }
pci_dma_read(&xhci->pci_dev, ictx+32, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, slot_ctx, sizeof(slot_ctx));
pci_dma_read(&xhci->pci_dev, ictx+64, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx));
DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
@ -1951,8 +1979,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
return res; return res;
} }
@ -1985,17 +2013,17 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
} }
} }
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) { if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -2003,8 +2031,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR; return CC_TRB_ERROR;
} }
pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) { if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]); fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
@ -2016,8 +2044,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
xhci_disable_ep(xhci, slotid, i); xhci_disable_ep(xhci, slotid, i);
} }
if (ictl_ctx[1] & (1<<i)) { if (ictl_ctx[1] & (1<<i)) {
pci_dma_read(&xhci->pci_dev, ictx+32+(32*i), ep_ctx, xhci_dma_read_u32s(xhci, ictx+32+(32*i), ep_ctx, sizeof(ep_ctx));
sizeof(ep_ctx));
DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
ep_ctx[3], ep_ctx[4]); ep_ctx[3], ep_ctx[4]);
@ -2029,7 +2056,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
ep_ctx[3], ep_ctx[4]); ep_ctx[3], ep_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx+(32*i), ep_ctx, sizeof(ep_ctx)); xhci_dma_write_u32s(xhci, octx+(32*i), ep_ctx, sizeof(ep_ctx));
} }
} }
@ -2041,7 +2068,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }
@ -2066,7 +2093,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) { if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -2075,12 +2102,12 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
} }
if (ictl_ctx[1] & 0x1) { if (ictl_ctx[1] & 0x1) {
pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]); islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[1] &= ~0xFFFF; /* max exit latency */ slot_ctx[1] &= ~0xFFFF; /* max exit latency */
slot_ctx[1] |= islot_ctx[1] & 0xFFFF; slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
@ -2090,17 +2117,17 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
} }
if (ictl_ctx[1] & 0x2) { if (ictl_ctx[1] & 0x2) {
pci_dma_read(&xhci->pci_dev, ictx+64, iep0_ctx, sizeof(iep0_ctx)); xhci_dma_read_u32s(xhci, ictx+64, iep0_ctx, sizeof(iep0_ctx));
DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
iep0_ctx[0], iep0_ctx[1], iep0_ctx[2], iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
iep0_ctx[3], iep0_ctx[4]); iep0_ctx[3], iep0_ctx[4]);
pci_dma_read(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_read_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/ ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000; ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
@ -2108,7 +2135,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
} }
return CC_SUCCESS; return CC_SUCCESS;
@ -2133,12 +2160,12 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
} }
} }
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT; slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }