aboutsummaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-10-24 18:31:16 +0200
committerGerd Hoffmann <kraxel@redhat.com>2012-10-25 09:08:11 +0200
commit8c75a899f85dda9f8305d25f0cc453a79115fd90 (patch)
treea8674092985cb195170f98fede4e532b6be2cd59 /hw/usb
parent66a08cbe6ad1aebec8eecf58b3ba042e19dd1649 (diff)
uhci: Detect guest td re-use
A td can be reused by the guest in a different queue, before we notice the original queue has been unlinked. So search for tds by addr only, detect guest td reuse, and cancel the original queue, this is necessary to keep our packet ids unique. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/hcd-uhci.c33
1 files changed, 16 insertions, 17 deletions
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 0984bee4f3..c4f2f98f17 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -319,28 +319,18 @@ static void uhci_async_cancel_all(UHCIState *s)
}
}
-static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr,
- UHCI_TD *td)
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr)
{
- uint32_t token = uhci_queue_token(td);
UHCIQueue *queue;
UHCIAsync *async;
QTAILQ_FOREACH(queue, &s->queues, next) {
- if (queue->token == token) {
- break;
- }
- }
- if (queue == NULL) {
- return NULL;
- }
-
- QTAILQ_FOREACH(async, &queue->asyncs, next) {
- if (async->td_addr == td_addr) {
- return async;
+ QTAILQ_FOREACH(async, &queue->asyncs, next) {
+ if (async->td_addr == td_addr) {
+ return async;
+ }
}
}
-
return NULL;
}
@@ -805,11 +795,21 @@ out:
static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
{
- UHCIAsync *async;
int len = 0, max_len;
bool spd;
bool queuing = (q != NULL);
uint8_t pid = td->token & 0xff;
+ UHCIAsync *async = uhci_async_find_td(s, td_addr);
+
+ if (async) {
+ if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) {
+ assert(q == NULL || q == async->queue);
+ q = async->queue;
+ } else {
+ uhci_queue_free(async->queue, "guest re-used pending td");
+ async = NULL;
+ }
+ }
if (q == NULL) {
q = uhci_queue_find(s, td);
@@ -831,7 +831,6 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
return TD_RESULT_NEXT_QH;
}
- async = uhci_async_find_td(s, td_addr, td);
if (async) {
/* Already submitted */
async->queue->valid = 32;