libpayload: ehci: Prevent some race conditions

Prevent race conditions, when an interrupt-queue underrun occurred and
the controller is currently working on our queue head or a transfer is
still in progress.

Change-Id: Ia14f80a08071306ee5d1349780be081bfacb206a
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: http://review.coreboot.org/1902
Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Tested-by: build bot (Jenkins)
diff --git a/payloads/libpayload/drivers/usb/ehci.c b/payloads/libpayload/drivers/usb/ehci.c
index 4ff137e..6a1cfb5 100644
--- a/payloads/libpayload/drivers/usb/ehci.c
+++ b/payloads/libpayload/drivers/usb/ehci.c
@@ -626,8 +626,14 @@
 		intrq->head = intrq->head->next;
 	}
 	/* reset queue if we fully processed it after underrun */
-	else if (intrq->qh.td.next_qtd & QTD_TERMINATE) {
+	else if ((intrq->qh.td.next_qtd & QTD_TERMINATE) &&
+			/* to prevent race conditions:
+			   not our head and not active */
+			(intrq->qh.current_td_ptr !=
+			 virt_to_phys(&intrq->head->td)) &&
+			!(intrq->qh.td.token & QTD_ACTIVE)) {
 		usb_debug("resetting underrun ehci interrupt queue.\n");
+		intrq->qh.current_td_ptr = 0;
 		memset((void *)&intrq->qh.td, 0, sizeof(intrq->qh.td));
 		intrq->qh.td.next_qtd = virt_to_phys(&intrq->head->td);
 	}