diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index 87b716c8f0..6a9939ca89 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -536,6 +536,7 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps, trb->ptr_low = virt_to_phys(cur_start); TRB_SET(TL, trb, cur_length); TRB_SET(TDS, trb, packets); + TRB_SET(CH, trb, 1); /* Check for first, data stage TRB */ if (!trb_count && ep == 1) { @@ -545,17 +546,19 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps, TRB_SET(TT, trb, TRB_NORMAL); } - /* Check for last TRB */ - if (!length) - TRB_SET(IOC, trb, 1); - else - TRB_SET(CH, trb, 1); - xhci_enqueue_trb(tr); cur_start += cur_length; ++trb_count; } + + trb = tr->cur; + xhci_clear_trb(trb, tr->pcs); + trb->ptr_low = virt_to_phys(trb); /* for easier debugging only */ + TRB_SET(TT, trb, TRB_EVENT_DATA); + TRB_SET(IOC, trb, 1); + + xhci_enqueue_trb(tr); } static int @@ -569,7 +572,7 @@ xhci_control(usbdev_t *const dev, const direction_t dir, transfer_ring_t *const tr = xhci->dev[dev->address].transfer_rings[1]; const size_t off = (size_t)data & 0xffff; - if ((off + dalen) > ((TRANSFER_RING_SIZE - 3) << 16)) { + if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) { xhci_debug("Unsupported transfer size\n"); return -1; } @@ -624,11 +627,11 @@ xhci_control(usbdev_t *const dev, const direction_t dir, xhci->dbreg[dev->address] = 1; /* Wait for transfer events */ - int i, residue = 0; + int i, transferred = 0; const int n_stages = 2 + !!dalen; for (i = 0; i < n_stages; ++i) { const int ret = xhci_wait_for_transfer(xhci, dev->address, 1); - residue += ret; + transferred += ret; if (ret < 0) { if (ret == TIMEOUT) { xhci_debug("Stopping ID %d EP 1\n", @@ -650,8 +653,8 @@ xhci_control(usbdev_t *const dev, const direction_t dir, } if (dir == IN && data != src) - memcpy(src, data, dalen - residue); - return dalen - residue; + memcpy(src, data, transferred); + return transferred; } /* finalize == 1: if data is of packet aligned size, add a zero length packet */ @@ -670,7 +673,7 @@ xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; const size_t off = (size_t)data & 0xffff; - if ((off + size) > ((TRANSFER_RING_SIZE - 1) << 16)) { + if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) { xhci_debug("Unsupported transfer size\n"); return -1; } @@ -718,8 +721,8 @@ xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, } if (ep->direction == IN && data != src) - memcpy(src, data, size - ret); - return size - ret; + memcpy(src, data, ret); + return ret; } static trb_t * diff --git a/payloads/libpayload/drivers/usb/xhci_events.c b/payloads/libpayload/drivers/usb/xhci_events.c index b947c7d2a1..dacb5d8618 100644 --- a/payloads/libpayload/drivers/usb/xhci_events.c +++ b/payloads/libpayload/drivers/usb/xhci_events.c @@ -306,7 +306,7 @@ xhci_wait_for_command_done(xhci_t *const xhci, return cc; } -/* returns amount of bytes not transferred on success, negative CC on error */ +/* returns amount of bytes transferred on success, negative CC on error */ int xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id) { diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h index 9981f072cb..fbffdb3ecd 100644 --- a/payloads/libpayload/drivers/usb/xhci_private.h +++ b/payloads/libpayload/drivers/usb/xhci_private.h @@ -64,7 +64,7 @@ enum { TRB_NORMAL = 1, TRB_SETUP_STAGE = 2, TRB_DATA_STAGE = 3, TRB_STATUS_STAGE = 4, - TRB_LINK = 6, + TRB_LINK = 6, TRB_EVENT_DATA = 7, TRB_CMD_ENABLE_SLOT = 9, TRB_CMD_DISABLE_SLOT = 10, TRB_CMD_ADDRESS_DEV = 11, TRB_CMD_CONFIGURE_EP = 12, TRB_CMD_EVAL_CTX = 13, TRB_CMD_RESET_EP = 14, TRB_CMD_STOP_EP = 15, TRB_CMD_SET_TR_DQ = 16, TRB_CMD_NOOP = 23, @@ -149,6 +149,7 @@ typedef struct { u8 adv; } event_ring_t; +/* Never raise this above 256 to prevent transfer event length overflow! */ #define TRANSFER_RING_SIZE 32 typedef struct { trb_t *ring;