xhci: add qemu-xhci device, some followup cleanups.
ccid: better sanity checking. ehci: fix memory leak ohci: bugfixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJYq+jbAAoJEEy22O7T6HE4gukP/jqP9SxKcZyEIFalmnOzR+Wq 5N20ZRuAJXAFEWG+FLOz+TC9tYCJ3IEpDwkEEtlFXdZgc8SF+YlOhWLRvTHSQYfy LZbYgoQnRsE+kpYVaADJ+lGYGRM/RjPPLkt54FF6SDX9jtNbv7nSyIvTXHkNrjZd cnGxCy4yu4S1kXhCURhkHiK8SjloexrwwtkfLfOVApFIpXcfb2enWIN79pbb6a1I 8GdYaTedQtr+//FasDagFWpHgpCiknA0A4C+C0E7tGtBkGa1IIoq0RB/82ZApoNB r8aM7SwhjsrRLGniIQNxXK93I5TX2SjHYeeIt37yvI89h5NAk6nzW6Dk9UbAWITq YOm9xOD27d8PtsBuEa9cVB2NJv5rflGyl+dsP0VbwKmhzMS/NU44cCLkcpDXsaIp R09vIuCCBoAyae9IgrFty6wJrpv2HmuLUCJelU3lOBSCGihSBCjTlLLnzSEkXV6o 9b+sfPTC5/d1U2sivCW7T0LxqIgyFew76NGdSO5Y86CqB+sYEQv3kwR6zcV/3WlE DonQLAPMtDdFwAoR/Aww7dhsNSMERYDz76xg6P7+FgbwG59iGzcqNkpsMyAAOvSS wVl1RkAm2Ln3Vo4re9fQJtWF/MLeDnZe7I1SQJdXF5NcRWCEPLd8tK+WFeg9HcPD 99jIUosbJ2prKtcKkNhI =5lEp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/pull-usb-20170221-1' into staging xhci: add qemu-xhci device, some followup cleanups. ccid: better sanity checking. ehci: fix memory leak ohci: bugfixes. # gpg: Signature made Tue 21 Feb 2017 07:14:35 GMT # gpg: using RSA key 0x4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/pull-usb-20170221-1: usb-ccid: add check message size checks usb-ccid: move header size check usb-ccid: better bulk_out error handling xhci: drop via vendor command handling xhci: fix nec vendor quirk handling xhci: add qemu xhci controller xhci: drop ER_FULL_HACK workaround xhci: apply limits to loops usb: ohci: limit the number of link eds usb: ohci: fix error return code in servicing iso td usb: ehci: fix memory leak in ehci Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b856256179
@ -61,6 +61,7 @@ PCI devices (other than virtio):
|
||||
1b36:0009 PCI Expander Bridge (-device pxb)
|
||||
1b36:000a PCI-PCI bridge (multiseat)
|
||||
1b36:000b PCIe Expander Bridge (-device pxb-pcie)
|
||||
1b36:000d PCI xhci usb host adapter
|
||||
|
||||
All these devices are documented in docs/specs.
|
||||
|
||||
|
@ -1001,80 +1001,92 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
||||
CCID_Header *ccid_header;
|
||||
|
||||
if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
goto err;
|
||||
}
|
||||
ccid_header = (CCID_Header *)s->bulk_out_data;
|
||||
usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
|
||||
s->bulk_out_pos += p->iov.size;
|
||||
if (p->iov.size == CCID_MAX_PACKET_SIZE) {
|
||||
if (s->bulk_out_pos < 10) {
|
||||
DPRINTF(s, 1, "%s: header incomplete\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ccid_header = (CCID_Header *)s->bulk_out_data;
|
||||
if ((s->bulk_out_pos - 10 < ccid_header->dwLength) &&
|
||||
(p->iov.size == CCID_MAX_PACKET_SIZE)) {
|
||||
DPRINTF(s, D_VERBOSE,
|
||||
"usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
|
||||
p->iov.size, ccid_header->dwLength);
|
||||
"usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
|
||||
s->bulk_out_pos - 10, ccid_header->dwLength);
|
||||
return;
|
||||
}
|
||||
if (s->bulk_out_pos < 10) {
|
||||
if (s->bulk_out_pos - 10 != ccid_header->dwLength) {
|
||||
DPRINTF(s, 1,
|
||||
"%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",
|
||||
__func__);
|
||||
} else {
|
||||
DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,
|
||||
ccid_header->bMessageType,
|
||||
ccid_message_type_to_str(ccid_header->bMessageType));
|
||||
switch (ccid_header->bMessageType) {
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
|
||||
DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,
|
||||
"usb-ccid: bulk_in: message size mismatch (got %d, expected %d)\n",
|
||||
s->bulk_out_pos - 10, ccid_header->dwLength);
|
||||
goto err;
|
||||
}
|
||||
|
||||
DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,
|
||||
ccid_header->bMessageType,
|
||||
ccid_message_type_to_str(ccid_header->bMessageType));
|
||||
switch (ccid_header->bMessageType) {
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
|
||||
DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,
|
||||
((CCID_IccPowerOn *)(ccid_header))->bPowerSelect);
|
||||
s->powered = true;
|
||||
if (!ccid_card_inserted(s)) {
|
||||
ccid_report_error_failed(s, ERROR_ICC_MUTE);
|
||||
}
|
||||
/* atr is written regardless of error. */
|
||||
ccid_write_data_block_atr(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
|
||||
ccid_reset_error_status(s);
|
||||
s->powered = false;
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
|
||||
ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_set_parameters(s, ccid_header);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_reset_parameters(s);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical:
|
||||
ccid_report_error_failed(s, 0);
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, 1,
|
||||
s->powered = true;
|
||||
if (!ccid_card_inserted(s)) {
|
||||
ccid_report_error_failed(s, ERROR_ICC_MUTE);
|
||||
}
|
||||
/* atr is written regardless of error. */
|
||||
ccid_write_data_block_atr(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
|
||||
ccid_reset_error_status(s);
|
||||
s->powered = false;
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
|
||||
ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_set_parameters(s, ccid_header);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_reset_parameters(s);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
|
||||
ccid_reset_error_status(s);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical:
|
||||
ccid_report_error_failed(s, 0);
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, 1,
|
||||
"handle_data: ERROR: unhandled message type %Xh\n",
|
||||
ccid_header->bMessageType);
|
||||
/*
|
||||
* The caller is expecting the device to respond, tell it we
|
||||
* don't support the operation.
|
||||
*/
|
||||
ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The caller is expecting the device to respond, tell it we
|
||||
* don't support the operation.
|
||||
*/
|
||||
ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
}
|
||||
s->bulk_out_pos = 0;
|
||||
return;
|
||||
|
||||
err:
|
||||
p->status = USB_RET_STALL;
|
||||
s->bulk_out_pos = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
|
||||
|
@ -89,6 +89,14 @@ static void usb_ehci_pci_init(Object *obj)
|
||||
usb_ehci_init(s, DEVICE(obj));
|
||||
}
|
||||
|
||||
static void usb_ehci_pci_finalize(Object *obj)
|
||||
{
|
||||
EHCIPCIState *i = PCI_EHCI(obj);
|
||||
EHCIState *s = &i->ehci;
|
||||
|
||||
usb_ehci_finalize(s);
|
||||
}
|
||||
|
||||
static void usb_ehci_pci_exit(PCIDevice *dev)
|
||||
{
|
||||
EHCIPCIState *i = PCI_EHCI(dev);
|
||||
@ -159,6 +167,7 @@ static const TypeInfo ehci_pci_type_info = {
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(EHCIPCIState),
|
||||
.instance_init = usb_ehci_pci_init,
|
||||
.instance_finalize = usb_ehci_pci_finalize,
|
||||
.abstract = true,
|
||||
.class_init = ehci_class_init,
|
||||
};
|
||||
|
@ -2545,6 +2545,11 @@ void usb_ehci_init(EHCIState *s, DeviceState *dev)
|
||||
&s->mem_ports);
|
||||
}
|
||||
|
||||
void usb_ehci_finalize(EHCIState *s)
|
||||
{
|
||||
usb_packet_cleanup(&s->ipacket);
|
||||
}
|
||||
|
||||
/*
|
||||
* vim: expandtab ts=4
|
||||
*/
|
||||
|
@ -323,6 +323,7 @@ struct EHCIState {
|
||||
extern const VMStateDescription vmstate_ehci;
|
||||
|
||||
void usb_ehci_init(EHCIState *s, DeviceState *dev);
|
||||
void usb_ehci_finalize(EHCIState *s);
|
||||
void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp);
|
||||
void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp);
|
||||
void ehci_reset(void *opaque);
|
||||
|
@ -42,6 +42,8 @@
|
||||
|
||||
#define OHCI_MAX_PORTS 15
|
||||
|
||||
#define ED_LINK_LIMIT 4
|
||||
|
||||
static int64_t usb_frame_time;
|
||||
static int64_t usb_bit_time;
|
||||
|
||||
@ -725,7 +727,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
if (ohci_read_iso_td(ohci, addr, &iso_td)) {
|
||||
trace_usb_ohci_iso_td_read_failed(addr);
|
||||
ohci_die(ohci);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
starting_frame = OHCI_BM(iso_td.flags, TD_SF);
|
||||
@ -1184,7 +1186,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
|
||||
uint32_t next_ed;
|
||||
uint32_t cur;
|
||||
int active;
|
||||
|
||||
uint32_t link_cnt = 0;
|
||||
active = 0;
|
||||
|
||||
if (head == 0)
|
||||
@ -1199,6 +1201,11 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
|
||||
|
||||
next_ed = ed.next & OHCI_DPTR_MASK;
|
||||
|
||||
if (++link_cnt > ED_LINK_LIMIT) {
|
||||
ohci_die(ohci);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
|
||||
uint32_t addr;
|
||||
/* Cancel pending packets for ED that have been paused. */
|
||||
|
@ -49,11 +49,10 @@
|
||||
|
||||
/* 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)
|
||||
@ -199,7 +198,6 @@ typedef enum TRBType {
|
||||
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;
|
||||
@ -431,12 +429,14 @@ typedef struct XHCIInterrupter {
|
||||
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;
|
||||
@ -486,9 +486,13 @@ struct XHCIState {
|
||||
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)
|
||||
@ -549,7 +553,6 @@ static const char *TRBType_names[] = {
|
||||
[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",
|
||||
};
|
||||
@ -826,7 +829,7 @@ static void xhci_intr_raise(XHCIState *xhci, int v)
|
||||
|
||||
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)
|
||||
@ -865,74 +868,6 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -945,19 +880,6 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
}
|
||||
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)) {
|
||||
@ -971,21 +893,12 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
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);
|
||||
}
|
||||
@ -1032,6 +945,7 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
|
||||
return type;
|
||||
} else {
|
||||
if (++link_cnt > TRB_LINK_LIMIT) {
|
||||
trace_usb_xhci_enforced_limit("trb-link");
|
||||
return 0;
|
||||
}
|
||||
ring->dequeue = xhci_mask64(trb->parameter);
|
||||
@ -1124,7 +1038,6 @@ static void xhci_er_reset(XHCIState *xhci, int v)
|
||||
|
||||
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);
|
||||
@ -2150,6 +2063,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
|
||||
XHCIRing *ring;
|
||||
USBEndpoint *ep = NULL;
|
||||
uint64_t mfindex;
|
||||
unsigned int count = 0;
|
||||
int length;
|
||||
int i;
|
||||
|
||||
@ -2262,6 +2176,10 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
|
||||
epctx->retry = xfer;
|
||||
break;
|
||||
}
|
||||
if (count++ > TRANSFER_LIMIT) {
|
||||
trace_usb_xhci_enforced_limit("transfers");
|
||||
break;
|
||||
}
|
||||
}
|
||||
epctx->kick_active--;
|
||||
|
||||
@ -2702,39 +2620,13 @@ static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo)
|
||||
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)) {
|
||||
@ -2823,24 +2715,27 @@ static void xhci_process_commands(XHCIState *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;
|
||||
@ -2848,6 +2743,11 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
}
|
||||
event.slotid = slotid;
|
||||
xhci_event(xhci, &event, 0);
|
||||
|
||||
if (count++ > COMMAND_LIMIT) {
|
||||
trace_usb_xhci_enforced_limit("commands");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2978,7 +2878,6 @@ static void xhci_reset(DeviceState *dev)
|
||||
|
||||
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;
|
||||
}
|
||||
@ -3343,9 +3242,12 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||
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;
|
||||
@ -3368,7 +3270,6 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||
break;
|
||||
case 0x1c: /* ERDP high */
|
||||
intr->erdp_high = val;
|
||||
xhci_events_update(xhci, v);
|
||||
break;
|
||||
default:
|
||||
trace_usb_xhci_unimplemented("oper write", reg);
|
||||
@ -3641,6 +3542,9 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
|
||||
dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
|
||||
dev->config[0x60] = 0x30; /* release number */
|
||||
|
||||
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
|
||||
xhci->nec_quirks = true;
|
||||
}
|
||||
if (xhci->numintrs > MAXINTRS) {
|
||||
xhci->numintrs = MAXINTRS;
|
||||
}
|
||||
@ -3866,8 +3770,7 @@ static const VMStateDescription vmstate_xhci_event = {
|
||||
|
||||
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 = {
|
||||
@ -3891,7 +3794,7 @@ 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,
|
||||
@ -3963,10 +3866,7 @@ static void xhci_class_init(ObjectClass *klass, void *data)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3975,11 +3875,44 @@ static const TypeInfo xhci_info = {
|
||||
.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);
|
||||
|
||||
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 const TypeInfo qemu_xhci_info = {
|
||||
.name = TYPE_QEMU_XHCI,
|
||||
.parent = TYPE_XHCI,
|
||||
.class_init = qemu_xhci_class_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)
|
||||
|
@ -174,6 +174,7 @@ usb_xhci_xfer_retry(void *xfer) "%p"
|
||||
usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
|
||||
usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
|
||||
usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)"
|
||||
usb_xhci_enforced_limit(const char *item) "%s"
|
||||
|
||||
# hw/usb/desc.c
|
||||
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
||||
|
@ -97,6 +97,7 @@
|
||||
#define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a
|
||||
#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b
|
||||
#define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c
|
||||
#define PCI_DEVICE_ID_REDHAT_XHCI 0x000d
|
||||
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
|
||||
|
||||
#define FMT_PCIBUS PRIx64
|
||||
|
Loading…
x
Reference in New Issue
Block a user