Browse Source

[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>
tags/v1.20.1
Michael Brown 5 years ago
parent
commit
4c8721331d
1 changed files with 6 additions and 2 deletions
  1. 6
    2
      src/drivers/usb/usbio.c

+ 6
- 2
src/drivers/usb/usbio.c View File

351
 	}
351
 	}
352
 
352
 
353
 	/* Construct transfer */
353
 	/* Construct transfer */
354
-	assert ( iob_len ( iobuf ) >= sizeof ( *msg ) );
355
-	msg = iobuf->data;
354
+	msg = iob_push ( iobuf, sizeof ( *msg ) );
356
 	iob_pull ( iobuf, sizeof ( *msg ) );
355
 	iob_pull ( iobuf, sizeof ( *msg ) );
357
 	request = le16_to_cpu ( msg->setup.request );
356
 	request = le16_to_cpu ( msg->setup.request );
358
 	len = iob_len ( iobuf );
357
 	len = iob_len ( iobuf );
995
  */
994
  */
996
 static int usbio_endpoint_message ( struct usb_endpoint *ep,
995
 static int usbio_endpoint_message ( struct usb_endpoint *ep,
997
 				    struct io_buffer *iobuf ) {
996
 				    struct io_buffer *iobuf ) {
997
+	struct usb_setup_packet *setup;
998
+
999
+	/* Adjust I/O buffer to start of data payload */
1000
+	assert ( iob_len ( iobuf ) >= sizeof ( *setup ) );
1001
+	iob_pull ( iobuf, sizeof ( *setup ) );
998
 
1002
 
999
 	/* Enqueue transfer */
1003
 	/* Enqueue transfer */
1000
 	return usbio_endpoint_enqueue ( ep, iobuf, USBIO_MESSAGE );
1004
 	return usbio_endpoint_enqueue ( ep, iobuf, USBIO_MESSAGE );

Loading…
Cancel
Save