Blame 0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch

c8dfc6
From 1ee48073d05a8a0dfe08ad2853be125a87f176de Mon Sep 17 00:00:00 2001
c8dfc6
From: Hans de Goede <hdegoede@redhat.com>
c8dfc6
Date: Wed, 29 Aug 2012 10:37:37 +0200
c8dfc6
Subject: [PATCH 306/366] ehci: Validate qh is not changed unexpectedly by the
c8dfc6
 guest
c8dfc6
c8dfc6
-combine the qh check with the check for devaddr changes
c8dfc6
-also ensure that p gets set to NULL when the queue gets cancelled on
c8dfc6
 devaddr change, which was not done properly before this patch
c8dfc6
c8dfc6
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
c8dfc6
---
c8dfc6
 hw/usb/hcd-ehci.c | 39 ++++++++++++++++++++++++++++-----------
c8dfc6
 1 file changed, 28 insertions(+), 11 deletions(-)
c8dfc6
c8dfc6
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
c8dfc6
index e7c36f4..35eb441 100644
c8dfc6
--- a/hw/usb/hcd-ehci.c
c8dfc6
+++ b/hw/usb/hcd-ehci.c
c8dfc6
@@ -780,6 +780,14 @@ static void ehci_cancel_queue(EHCIQueue *q)
c8dfc6
     } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
c8dfc6
 }
c8dfc6
 
c8dfc6
+static void ehci_reset_queue(EHCIQueue *q)
c8dfc6
+{
c8dfc6
+    trace_usb_ehci_queue_action(q, "reset");
c8dfc6
+    ehci_cancel_queue(q);
c8dfc6
+    q->dev = NULL;
c8dfc6
+    q->qtdaddr = 0;
c8dfc6
+}
c8dfc6
+
c8dfc6
 static void ehci_free_queue(EHCIQueue *q)
c8dfc6
 {
c8dfc6
     EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
c8dfc6
@@ -1755,8 +1763,9 @@ out:
c8dfc6
 static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
c8dfc6
 {
c8dfc6
     EHCIPacket *p;
c8dfc6
-    uint32_t entry, devaddr;
c8dfc6
+    uint32_t entry, devaddr, endp;
c8dfc6
     EHCIQueue *q;
c8dfc6
+    EHCIqh qh;
c8dfc6
 
c8dfc6
     entry = ehci_get_fetch_addr(ehci, async);
c8dfc6
     q = ehci_find_queue_by_qh(ehci, entry, async);
c8dfc6
@@ -1774,17 +1783,25 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
c8dfc6
     }
c8dfc6
 
c8dfc6
     get_dwords(ehci, NLPTR_GET(q->qhaddr),
c8dfc6
-               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
c8dfc6
-    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
c8dfc6
+               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
c8dfc6
+    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh;;
c8dfc6
+
c8dfc6
+    /*
c8dfc6
+     * The overlay area of the qh should never be changed by the guest,
c8dfc6
+     * except when idle, in which case the reset is a nop.
c8dfc6
+     */
c8dfc6
+    devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR);
c8dfc6
+    endp    = get_field(qh.epchar, QH_EPCHAR_EP);
c8dfc6
+    if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
c8dfc6
+        (endp    != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
c8dfc6
+        (memcmp(&qh.current_qtd, &q->qh.current_qtd,
c8dfc6
+                                 9 * sizeof(uint32_t)) != 0) ||
c8dfc6
+        (q->dev != NULL && q->dev->addr != devaddr)) {
c8dfc6
+        ehci_reset_queue(q);
c8dfc6
+        p = NULL;
c8dfc6
+    }
c8dfc6
+    q->qh = qh;
c8dfc6
 
c8dfc6
-    devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
c8dfc6
-    if (q->dev != NULL && q->dev->addr != devaddr) {
c8dfc6
-        if (!QTAILQ_EMPTY(&q->packets)) {
c8dfc6
-            /* should not happen (guest bug) */
c8dfc6
-            ehci_cancel_queue(q);
c8dfc6
-        }
c8dfc6
-        q->dev = NULL;
c8dfc6
-    }
c8dfc6
     if (q->dev == NULL) {
c8dfc6
         q->dev = ehci_find_device(q->ehci, devaddr);
c8dfc6
     }
c8dfc6
-- 
c8dfc6
1.7.12
c8dfc6