summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2019-07-22 11:06:40 -0700
committerPatrick Georgi <pgeorgi@google.com>2019-08-22 10:36:48 +0000
commit182fea717e413bf3c0920920da13a98aa8fe890c (patch)
tree99e7e206ac8d080aeaa48b4a191016ddadc5688c
parentc788ae328eb3708923623ff15a8aa27f686c84f8 (diff)
libpayload: usbmsc: Skip zero-length packets at end of data
Some broken USB mass storage devices send another zero-length packet at the end of the data part of a transfer if the amount of data was evenly divisible by the packet size (which is pretty much always the case for block reads). This packet will get interpreted as the CSW and screw up the MSC state machine. This patch works around this issue by retrying the CSW transfer when it was received as exactly 0 bytes. This is the same mitigation the Linux kernel uses and harmless for correctly behaving devices. Also tighten validation of the CSW a little, making sure we verify the length before we read any fields and checking the signature in addition to the tag. Change-Id: I24f183f27b2c4f0142ba6c4b35b490c5798d0d21 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/34485 Reviewed-by: Patrick Georgi <pgeorgi@google.com> Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
-rw-r--r--payloads/libpayload/drivers/usb/usbmsc.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/payloads/libpayload/drivers/usb/usbmsc.c b/payloads/libpayload/drivers/usb/usbmsc.c
index 2c7cbe5ef2..d793e34ba7 100644
--- a/payloads/libpayload/drivers/usb/usbmsc.c
+++ b/payloads/libpayload/drivers/usb/usbmsc.c
@@ -218,14 +218,23 @@ wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
static int
get_csw (endpoint_t *ep, csw_t *csw)
{
- if (ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
+ hci_t *ctrlr = ep->dev->controller;
+ int ret = ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
+
+ /* Some broken sticks send a zero-length packet at the end of their data
+ transfer which would show up here. Skip it to get the actual CSW. */
+ if (ret == 0)
+ ret = ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
+
+ if (ret < 0) {
clear_stall (ep);
- if (ep->dev->controller->bulk
- (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
+ if (ctrlr->bulk (ep, sizeof (csw_t), (u8 *) csw, 1) < 0) {
return reset_transport (ep->dev);
}
}
- if (csw->dCSWTag != tag) {
+ if (ret != sizeof(csw_t) || csw->dCSWTag != tag ||
+ csw->dCSWSignature != csw_signature) {
+ usb_debug ("MSC: received malformed CSW\n");
return reset_transport (ep->dev);
}
return MSC_COMMAND_OK;