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

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