summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2019-09-15 11:25:46 +0200
committerMichael Brown2019-09-15 11:25:46 +0200
commit4c8721331d8d658f37acf08b3596c14c0599ea52 (patch)
tree0d358a050e1b0ad5b949cccadb0777b38e24b393
parent[golan] Fix address-of-pointer bug for multicast attach/detach (diff)
downloadipxe-4c8721331d8d658f37acf08b3596c14c0599ea52.tar.gz
ipxe-4c8721331d8d658f37acf08b3596c14c0599ea52.tar.xz
ipxe-4c8721331d8d658f37acf08b3596c14c0599ea52.zip
[efi] Report failed control transfers as expected by the USB core
The USB core reuses the I/O buffer space occupied by the USB setup packet to hold the completion status for message transfers, assuming that the message() method will always strip the setup packet before returning. This assumption is correct for all of the hardware controller drivers (XHCI, EHCI, and UHCI), since these drivers are able to enqueue the transfer as a separate action from waiting for the transfer to complete. The EFI_USB_IO_PROTOCOL does not allow us to separate actions in this way: there is only a single blocking method that both enqueues and waits for completion. Our usbio driver therefore currently defers stripping the setup packet until the control endpoint is polled. This causes a bug if a message transfer is enqueued but never polled and is subsequently cancelled, since the cancellation will be reported with the I/O buffer still containing the setup packet. This breaks the assumption that the setup packet has been stripped, and triggers an assertion failure in usb_control_complete(). Fix by always stripping the setup packet in usbio_endpoint_message(), and adjusting usbio_control_poll() to match. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/drivers/usb/usbio.c8
1 files changed, 6 insertions, 2 deletions
diff --git a/src/drivers/usb/usbio.c b/src/drivers/usb/usbio.c
index 153f3942..e91416fd 100644
--- a/src/drivers/usb/usbio.c
+++ b/src/drivers/usb/usbio.c
@@ -351,8 +351,7 @@ static void usbio_control_poll ( struct usbio_endpoint *endpoint ) {
}
/* Construct transfer */
- assert ( iob_len ( iobuf ) >= sizeof ( *msg ) );
- msg = iobuf->data;
+ msg = iob_push ( iobuf, sizeof ( *msg ) );
iob_pull ( iobuf, sizeof ( *msg ) );
request = le16_to_cpu ( msg->setup.request );
len = iob_len ( iobuf );
@@ -995,6 +994,11 @@ static int usbio_endpoint_enqueue ( struct usb_endpoint *ep,
*/
static int usbio_endpoint_message ( struct usb_endpoint *ep,
struct io_buffer *iobuf ) {
+ struct usb_setup_packet *setup;
+
+ /* Adjust I/O buffer to start of data payload */
+ assert ( iob_len ( iobuf ) >= sizeof ( *setup ) );
+ iob_pull ( iobuf, sizeof ( *setup ) );
/* Enqueue transfer */
return usbio_endpoint_enqueue ( ep, iobuf, USBIO_MESSAGE );