/* Very pessimistic, let's hope it's enough for all cases */
#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS)
-/* Do not deliver ER Full events. NEC's driver does some things not bound
- * to the specs when it gets them */
-#define ER_FULL_HACK
#define TRB_LINK_LIMIT 4
+#define COMMAND_LIMIT 256
+#define TRANSFER_LIMIT 256
#define LEN_CAP 0x40
#define LEN_OPER (0x400 + 0x10 * MAXPORTS)
ER_DEVICE_NOTIFICATION,
ER_MFINDEX_WRAP,
/* vendor specific bits */
- CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48,
CR_VENDOR_NEC_FIRMWARE_REVISION = 49,
CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50,
} TRBType;
dma_addr_t pctx;
unsigned int max_psize;
uint32_t state;
+ uint32_t kick_active;
/* streams */
unsigned int max_pstreams;
uint32_t erdp_low;
uint32_t erdp_high;
- bool msix_used, er_pcs, er_full;
+ bool msix_used, er_pcs;
dma_addr_t er_start;
uint32_t er_size;
unsigned int er_ep_idx;
+ /* kept for live migration compat only */
+ bool er_full_unused;
XHCIEvent ev_buffer[EV_QUEUE];
unsigned int ev_buffer_put;
unsigned int ev_buffer_get;
XHCIInterrupter intr[MAXINTRS];
XHCIRing cmd_ring;
+
+ bool nec_quirks;
};
-#define TYPE_XHCI "nec-usb-xhci"
+#define TYPE_XHCI "base-xhci"
+#define TYPE_NEC_XHCI "nec-usb-xhci"
+#define TYPE_QEMU_XHCI "qemu-xhci"
#define XHCI(obj) \
OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI)
[ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER",
[ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION",
[ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP",
- [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE",
[CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION",
[CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
};
return xhci->flags & (1 << bit);
}
+static void xhci_set_flag(XHCIState *xhci, enum xhci_flags bit)
+{
+ xhci->flags |= (1 << bit);
+}
+
static uint64_t xhci_mfindex_get(XHCIState *xhci)
{
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
static void xhci_intr_raise(XHCIState *xhci, int v)
{
PCIDevice *pci_dev = PCI_DEVICE(xhci);
+ bool pending = (xhci->intr[v].erdp_low & ERDP_EHB);
xhci->intr[v].erdp_low |= ERDP_EHB;
xhci->intr[v].iman |= IMAN_IP;
xhci->usbsts |= USBSTS_EINT;
+ if (pending) {
+ return;
+ }
if (!(xhci->intr[v].iman & IMAN_IE)) {
return;
}
static inline int xhci_running(XHCIState *xhci)
{
- return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full;
+ return !(xhci->usbsts & USBSTS_HCH);
}
static void xhci_die(XHCIState *xhci)
}
}
-static void xhci_events_update(XHCIState *xhci, int v)
-{
- XHCIInterrupter *intr = &xhci->intr[v];
- dma_addr_t erdp;
- unsigned int dp_idx;
- bool do_irq = 0;
-
- if (xhci->usbsts & USBSTS_HCH) {
- return;
- }
-
- erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
- if (erdp < intr->er_start ||
- erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
- DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
- DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
- v, intr->er_start, intr->er_size);
- xhci_die(xhci);
- return;
- }
- dp_idx = (erdp - intr->er_start) / TRB_SIZE;
- assert(dp_idx < intr->er_size);
-
- /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
- * deadlocks when the ER is full. Hack it by holding off events until
- * the driver decides to free at least half of the ring */
- if (intr->er_full) {
- int er_free = dp_idx - intr->er_ep_idx;
- if (er_free <= 0) {
- er_free += intr->er_size;
- }
- if (er_free < (intr->er_size/2)) {
- DPRINTF("xhci_events_update(): event ring still "
- "more than half full (hack)\n");
- return;
- }
- }
-
- while (intr->ev_buffer_put != intr->ev_buffer_get) {
- assert(intr->er_full);
- if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) {
- DPRINTF("xhci_events_update(): event ring full again\n");
-#ifndef ER_FULL_HACK
- XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
- xhci_write_event(xhci, &full, v);
-#endif
- do_irq = 1;
- break;
- }
- XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get];
- xhci_write_event(xhci, event, v);
- intr->ev_buffer_get++;
- do_irq = 1;
- if (intr->ev_buffer_get == EV_QUEUE) {
- intr->ev_buffer_get = 0;
- }
- }
-
- if (do_irq) {
- xhci_intr_raise(xhci, v);
- }
-
- if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) {
- DPRINTF("xhci_events_update(): event ring no longer full\n");
- intr->er_full = 0;
- }
-}
-
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
{
XHCIInterrupter *intr;
}
intr = &xhci->intr[v];
- if (intr->er_full) {
- DPRINTF("xhci_event(): ER full, queueing\n");
- if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
- DPRINTF("xhci: event queue full, dropping event!\n");
- return;
- }
- intr->ev_buffer[intr->ev_buffer_put++] = *event;
- if (intr->ev_buffer_put == EV_QUEUE) {
- intr->ev_buffer_put = 0;
- }
- return;
- }
-
erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
if (erdp < intr->er_start ||
erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
dp_idx = (erdp - intr->er_start) / TRB_SIZE;
assert(dp_idx < intr->er_size);
- if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) {
- DPRINTF("xhci_event(): ER full, queueing\n");
-#ifndef ER_FULL_HACK
+ if ((intr->er_ep_idx + 2) % intr->er_size == dp_idx) {
+ DPRINTF("xhci: ER %d full, send ring full error\n", v);
XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
- xhci_write_event(xhci, &full);
-#endif
- intr->er_full = 1;
- if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
- DPRINTF("xhci: event queue full, dropping event!\n");
- return;
- }
- intr->ev_buffer[intr->ev_buffer_put++] = *event;
- if (intr->ev_buffer_put == EV_QUEUE) {
- intr->ev_buffer_put = 0;
- }
+ xhci_write_event(xhci, &full, v);
+ } else if ((intr->er_ep_idx + 1) % intr->er_size == dp_idx) {
+ DPRINTF("xhci: ER %d full, drop event\n", v);
} else {
xhci_write_event(xhci, event, v);
}
return type;
} else {
if (++link_cnt > TRB_LINK_LIMIT) {
+ trace_usb_xhci_enforced_limit("trb-link");
return 0;
}
ring->dequeue = xhci_mask64(trb->parameter);
intr->er_ep_idx = 0;
intr->er_pcs = 1;
- intr->er_full = 0;
DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n",
v, intr->er_start, intr->er_size);
return 0;
}
-static int xhci_complete_packet(XHCITransfer *xfer)
+static int xhci_try_complete_packet(XHCITransfer *xfer)
{
if (xfer->packet.status == USB_RET_ASYNC) {
trace_usb_xhci_xfer_async(xfer);
xfer->packet.parameter = trb_setup->parameter;
usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
-
- xhci_complete_packet(xfer);
- if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_epctx(xfer->epctx, 0);
- }
+ xhci_try_complete_packet(xfer);
return 0;
}
return -1;
}
usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
-
- xhci_complete_packet(xfer);
- if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_epctx(xfer->epctx, xfer->streamid);
- }
+ xhci_try_complete_packet(xfer);
return 0;
}
return;
}
+ if (epctx->kick_active) {
+ return;
+ }
xhci_kick_epctx(epctx, streamid);
}
static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
{
XHCIState *xhci = epctx->xhci;
- XHCIStreamContext *stctx;
+ XHCIStreamContext *stctx = NULL;
XHCITransfer *xfer;
XHCIRing *ring;
USBEndpoint *ep = NULL;
uint64_t mfindex;
+ unsigned int count = 0;
int length;
int i;
trace_usb_xhci_ep_kick(epctx->slotid, epctx->epid, streamid);
+ assert(!epctx->kick_active);
/* If the device has been detached, but the guest has not noticed this
yet the 2 above checks will succeed, but we must NOT continue */
}
usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
assert(xfer->packet.status != USB_RET_NAK);
- xhci_complete_packet(xfer);
+ xhci_try_complete_packet(xfer);
} else {
/* retry nak'ed transfer */
if (xhci_setup_packet(xfer) < 0) {
if (xfer->packet.status == USB_RET_NAK) {
return;
}
- xhci_complete_packet(xfer);
+ xhci_try_complete_packet(xfer);
}
assert(!xfer->running_retry);
- xhci_ep_free_xfer(epctx->retry);
+ if (xfer->complete) {
+ xhci_ep_free_xfer(epctx->retry);
+ }
epctx->retry = NULL;
}
}
assert(ring->dequeue != 0);
+ epctx->kick_active++;
while (1) {
length = xhci_ring_chain_length(xhci, ring);
if (length <= 0) {
epctx->retry = xfer;
break;
}
+ if (count++ > TRANSFER_LIMIT) {
+ trace_usb_xhci_enforced_limit("transfers");
+ break;
+ }
}
+ /* update ring dequeue ptr */
+ xhci_set_ep_state(xhci, epctx, stctx, epctx->state);
+ epctx->kick_active--;
ep = xhci_epid_to_usbep(epctx);
if (ep) {
return ~val;
}
-static void xhci_via_challenge(XHCIState *xhci, uint64_t addr)
-{
- PCIDevice *pci_dev = PCI_DEVICE(xhci);
- uint32_t buf[8];
- uint32_t obuf[8];
- dma_addr_t paddr = xhci_mask64(addr);
-
- pci_dma_read(pci_dev, paddr, &buf, 32);
-
- memcpy(obuf, buf, sizeof(obuf));
-
- if ((buf[0] & 0xff) == 2) {
- obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3];
- obuf[0] |= (buf[2] * buf[3]) & 0xff;
- obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3];
- obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3];
- obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3];
- obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3];
- obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3];
- obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956;
- obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
- }
-
- pci_dma_write(pci_dev, paddr, &obuf, 32);
-}
-
static void xhci_process_commands(XHCIState *xhci)
{
XHCITRB trb;
TRBType type;
XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
dma_addr_t addr;
- unsigned int i, slotid = 0;
+ unsigned int i, slotid = 0, count = 0;
DPRINTF("xhci_process_commands()\n");
if (!xhci_running(xhci)) {
case CR_GET_PORT_BANDWIDTH:
event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
break;
- case CR_VENDOR_VIA_CHALLENGE_RESPONSE:
- xhci_via_challenge(xhci, trb.parameter);
- break;
case CR_VENDOR_NEC_FIRMWARE_REVISION:
- event.type = 48; /* NEC reply */
- event.length = 0x3025;
+ if (xhci->nec_quirks) {
+ event.type = 48; /* NEC reply */
+ event.length = 0x3025;
+ } else {
+ event.ccode = CC_TRB_ERROR;
+ }
break;
case CR_VENDOR_NEC_CHALLENGE_RESPONSE:
- {
- uint32_t chi = trb.parameter >> 32;
- uint32_t clo = trb.parameter;
- uint32_t val = xhci_nec_challenge(chi, clo);
- event.length = val & 0xFFFF;
- event.epid = val >> 16;
- slotid = val >> 24;
- event.type = 48; /* NEC reply */
- }
- break;
+ if (xhci->nec_quirks) {
+ uint32_t chi = trb.parameter >> 32;
+ uint32_t clo = trb.parameter;
+ uint32_t val = xhci_nec_challenge(chi, clo);
+ event.length = val & 0xFFFF;
+ event.epid = val >> 16;
+ slotid = val >> 24;
+ event.type = 48; /* NEC reply */
+ } else {
+ event.ccode = CC_TRB_ERROR;
+ }
+ break;
default:
trace_usb_xhci_unimplemented("command", type);
event.ccode = CC_TRB_ERROR;
}
event.slotid = slotid;
xhci_event(xhci, &event, 0);
+
+ if (count++ > COMMAND_LIMIT) {
+ trace_usb_xhci_enforced_limit("commands");
+ return;
+ }
}
}
xhci->intr[i].er_ep_idx = 0;
xhci->intr[i].er_pcs = 1;
- xhci->intr[i].er_full = 0;
xhci->intr[i].ev_buffer_put = 0;
xhci->intr[i].ev_buffer_get = 0;
}
intr->erstsz = val & 0xffff;
break;
case 0x10: /* ERSTBA low */
- /* XXX NEC driver bug: it doesn't align this to 64 bytes
- intr->erstba_low = val & 0xffffffc0; */
- intr->erstba_low = val & 0xfffffff0;
+ if (xhci->nec_quirks) {
+ /* NEC driver bug: it doesn't align this to 64 bytes */
+ intr->erstba_low = val & 0xfffffff0;
+ } else {
+ intr->erstba_low = val & 0xffffffc0;
+ }
break;
case 0x14: /* ERSTBA high */
intr->erstba_high = val;
intr->erdp_low &= ~ERDP_EHB;
}
intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
+ if (val & ERDP_EHB) {
+ dma_addr_t erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
+ unsigned int dp_idx = (erdp - intr->er_start) / TRB_SIZE;
+ if (erdp >= intr->er_start &&
+ erdp < (intr->er_start + TRB_SIZE * intr->er_size) &&
+ dp_idx != intr->er_ep_idx) {
+ xhci_intr_raise(xhci, v);
+ }
+ }
break;
case 0x1c: /* ERDP high */
intr->erdp_high = val;
- xhci_events_update(xhci, v);
break;
default:
trace_usb_xhci_unimplemented("oper write", reg);
xhci_ep_nuke_one_xfer(xfer, 0);
return;
}
- xhci_complete_packet(xfer);
+ xhci_try_complete_packet(xfer);
xhci_kick_epctx(xfer->epctx, xfer->streamid);
if (xfer->complete) {
xhci_ep_free_xfer(xfer);
dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
dev->config[0x60] = 0x30; /* release number */
- usb_xhci_init(xhci);
-
- if (xhci->msi != ON_OFF_AUTO_OFF) {
- ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
- /* Any error other than -ENOTSUP(board's MSI support is broken)
- * is a programming error */
- assert(!ret || ret == -ENOTSUP);
- if (ret && xhci->msi == ON_OFF_AUTO_ON) {
- /* Can't satisfy user's explicit msi=on request, fail */
- error_append_hint(&err, "You have to use msi=auto (default) or "
- "msi=off with this machine type.\n");
- error_propagate(errp, err);
- return;
- }
- assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
- /* With msi=auto, we fall back to MSI off silently */
- error_free(err);
+ if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
+ xhci->nec_quirks = true;
}
-
if (xhci->numintrs > MAXINTRS) {
xhci->numintrs = MAXINTRS;
}
xhci->max_pstreams_mask = 0;
}
+ if (xhci->msi != ON_OFF_AUTO_OFF) {
+ ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
+ /* Any error other than -ENOTSUP(board's MSI support is broken)
+ * is a programming error */
+ assert(!ret || ret == -ENOTSUP);
+ if (ret && xhci->msi == ON_OFF_AUTO_ON) {
+ /* Can't satisfy user's explicit msi=on request, fail */
+ error_append_hint(&err, "You have to use msi=auto (default) or "
+ "msi=off with this machine type.\n");
+ error_propagate(errp, err);
+ return;
+ }
+ assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
+ /* With msi=auto, we fall back to MSI off silently */
+ error_free(err);
+ }
+
+ usb_xhci_init(xhci);
xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci);
memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS);
}
if (xhci->msix != ON_OFF_AUTO_OFF) {
- /* TODO check for errors */
+ /* TODO check for errors, and should fail when msix=on */
msix_init(dev, xhci->numintrs,
&xhci->mem, 0, OFF_MSIX_TABLE,
&xhci->mem, 0, OFF_MSIX_PBA,
- 0x90);
+ 0x90, NULL);
}
}
static bool xhci_er_full(void *opaque, int version_id)
{
- struct XHCIInterrupter *intr = opaque;
- return intr->er_full;
+ return false;
}
static const VMStateDescription vmstate_xhci_intr = {
VMSTATE_UINT32(er_ep_idx, XHCIInterrupter),
/* event queue (used if ring is full) */
- VMSTATE_BOOL(er_full, XHCIInterrupter),
+ VMSTATE_BOOL(er_full_unused, XHCIInterrupter),
VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full),
VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full),
VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE,
}
};
-static Property xhci_properties[] = {
+static Property nec_xhci_properties[] = {
DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO),
DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO),
DEFINE_PROP_BIT("superspeed-ports-first",
XHCIState, flags, XHCI_FLAG_SS_FIRST, true),
DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags,
XHCI_FLAG_FORCE_PCIE_ENDCAP, false),
- DEFINE_PROP_BIT("streams", XHCIState, flags,
- XHCI_FLAG_ENABLE_STREAMS, true),
DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS),
DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property xhci_properties[] = {
+ DEFINE_PROP_BIT("streams", XHCIState, flags,
+ XHCI_FLAG_ENABLE_STREAMS, true),
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
DEFINE_PROP_END_OF_LIST(),
set_bit(DEVICE_CATEGORY_USB, dc->categories);
k->realize = usb_xhci_realize;
k->exit = usb_xhci_exit;
- k->vendor_id = PCI_VENDOR_ID_NEC;
- k->device_id = PCI_DEVICE_ID_NEC_UPD720200;
k->class_id = PCI_CLASS_SERIAL_USB;
- k->revision = 0x03;
k->is_express = 1;
}
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XHCIState),
.class_init = xhci_class_init,
+ .abstract = true,
+};
+
+static void nec_xhci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = nec_xhci_properties;
+ k->vendor_id = PCI_VENDOR_ID_NEC;
+ k->device_id = PCI_DEVICE_ID_NEC_UPD720200;
+ k->revision = 0x03;
+}
+
+static const TypeInfo nec_xhci_info = {
+ .name = TYPE_NEC_XHCI,
+ .parent = TYPE_XHCI,
+ .class_init = nec_xhci_class_init,
+};
+
+static void qemu_xhci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_XHCI;
+ k->revision = 0x01;
+}
+
+static void qemu_xhci_instance_init(Object *obj)
+{
+ XHCIState *xhci = XHCI(obj);
+
+ xhci->msi = ON_OFF_AUTO_OFF;
+ xhci->msix = ON_OFF_AUTO_AUTO;
+ xhci->numintrs = MAXINTRS;
+ xhci->numslots = MAXSLOTS;
+ xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST);
+}
+
+static const TypeInfo qemu_xhci_info = {
+ .name = TYPE_QEMU_XHCI,
+ .parent = TYPE_XHCI,
+ .class_init = qemu_xhci_class_init,
+ .instance_init = qemu_xhci_instance_init,
};
static void xhci_register_types(void)
{
type_register_static(&xhci_info);
+ type_register_static(&nec_xhci_info);
+ type_register_static(&qemu_xhci_info);
}
type_init(xhci_register_types)