libpayload: usb: Fix several minor USB stack bugs

This patch fixes the following minor bugs in the USB stack:

1. Ensure that all dynamically allocated device structures are cleaned
on detachment, and that the device address is correctly released again.
2. Make sure MSC and HID drivers notice missing endpoints and actually
detach the device in that case (to prevent it from being used).
3. Make sure XHCI-specific set_address() cleans up all data structures
on failure.
4. Fix broken Slot ID range check that prevented XHCI devices from being
correctly cleaned up.

BUG=chrome-os-partner:22139
TEST=Manual

Change-Id: I7b2b9c8cd6c5e93cb19abcf01425bcd85d2e1f22
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/170665
Reviewed-by: Stefan Reinauer <reinauer@google.com>
Commit-Queue: Ronald Minnich <rminnich@chromium.org>
Tested-by: Ronald Minnich <rminnich@chromium.org>
diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c
index 0ec6f5c..8576cf8 100644
--- a/payloads/libpayload/drivers/usb/usb.c
+++ b/payloads/libpayload/drivers/usb/usb.c
@@ -575,10 +575,14 @@
 	   been called yet by the usb class driver */
 	if (controller->devices[devno]) {
 		controller->devices[devno]->destroy (controller->devices[devno]);
-		free(controller->devices[devno]);
-		controller->devices[devno] = NULL;
 		if (controller->destroy_device)
 			controller->destroy_device(controller, devno);
+		if (controller->devices[devno]->configuration)
+			free(controller->devices[devno]->configuration);
+		if (controller->devices[devno]->descriptor)
+			free(controller->devices[devno]->descriptor);
+		free(controller->devices[devno]);
+		controller->devices[devno] = NULL;
 	}
 }
 
diff --git a/payloads/libpayload/drivers/usb/usbhid.c b/payloads/libpayload/drivers/usb/usbhid.c
index 0fb4f83..9226f78 100644
--- a/payloads/libpayload/drivers/usb/usbhid.c
+++ b/payloads/libpayload/drivers/usb/usbhid.c
@@ -467,15 +467,18 @@
 			dev->destroy = usb_hid_destroy;
 			dev->poll = usb_hid_poll;
 			int i;
-			for (i = 0; i <= dev->num_endp; i++) {
-				if (dev->endpoints[i].endpoint == 0)
-					continue;
+			for (i = 1; i < dev->num_endp; i++) {
 				if (dev->endpoints[i].type != INTERRUPT)
 					continue;
 				if (dev->endpoints[i].direction != IN)
 					continue;
 				break;
 			}
+			if (i >= dev->num_endp) {
+				usb_debug ("Could not find HID endpoint\n");
+				usb_detach_device (dev->controller, dev->address);
+				return;
+			}
 			usb_debug ("  found endpoint %x for interrupt-in\n", i);
 			/* 20 buffers of 8 bytes, for every 10 msecs */
 			HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10);
diff --git a/payloads/libpayload/drivers/usb/usbmsc.c b/payloads/libpayload/drivers/usb/usbmsc.c
index 037ead3..178f982 100644
--- a/payloads/libpayload/drivers/usb/usbmsc.c
+++ b/payloads/libpayload/drivers/usb/usbmsc.c
@@ -598,6 +598,7 @@
 
 	if (interface->bInterfaceProtocol != 0x50) {
 		usb_debug ("  Protocol not supported.\n");
+		usb_detach_device (dev->controller, dev->address);
 		return;
 	}
 
@@ -606,6 +607,7 @@
 		(interface->bInterfaceSubClass != 6)) {	// SCSI
 		/* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */
 		usb_debug ("  Interface SubClass not supported.\n");
+		usb_detach_device (dev->controller, dev->address);
 		return;
 	}
 
@@ -632,11 +634,13 @@
 	}
 
 	if (MSC_INST (dev)->bulk_in == 0) {
-		usb_debug("couldn't find bulk-in endpoint");
+		usb_debug("couldn't find bulk-in endpoint.\n");
+		usb_detach_device (dev->controller, dev->address);
 		return;
 	}
 	if (MSC_INST (dev)->bulk_out == 0) {
-		usb_debug("couldn't find bulk-out endpoint");
+		usb_debug("couldn't find bulk-out endpoint.\n");
+		usb_detach_device (dev->controller, dev->address);
 		return;
 	}
 	usb_debug ("  using endpoint %x as in, %x as out\n",
diff --git a/payloads/libpayload/drivers/usb/xhci_devconf.c b/payloads/libpayload/drivers/usb/xhci_devconf.c
index c7f3ddd..712b916 100644
--- a/payloads/libpayload/drivers/usb/xhci_devconf.c
+++ b/payloads/libpayload/drivers/usb/xhci_devconf.c
@@ -150,7 +150,7 @@
 	di = &xhci->dev[slot_id];
 	void *dma_buffer = dma_memalign(64, NUM_EPS * ctxsize);
 	if (!dma_buffer)
-		goto _free_return;
+		goto _disable_return;
 	memset(dma_buffer, 0, NUM_EPS * ctxsize);
 	for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
 		di->ctx.ep[i] = dma_buffer;
@@ -219,6 +219,7 @@
 _disable_return:
 	xhci_cmd_disable_slot(xhci, slot_id);
 	xhci->dcbaa[slot_id] = 0;
+	usb_detach_device(controller, slot_id);
 _free_return:
 	if (tr)
 		free((void *)tr->ring);
@@ -395,7 +396,7 @@
 {
 	xhci_t *const xhci = XHCI_INST(controller);
 
-	if (slot_id <= 0 || xhci->max_slots_en > slot_id)
+	if (slot_id <= 0 || slot_id > xhci->max_slots_en)
 		return;
 
 	int i;