Hi,
I found some USB problem:

Some part of GRUB wants read "long" data block from USB mass storage
device when GRUB opens USB disk, e.g. when "ls" command is entered first
time after EHCI/UHCI/OHCI module is loaded.
"Long" means data block of 0x8000 bytes length - it is not too much
nowadays :-)

But:
Some USB devices have too low limit of bulk data packet size, e.g. 32
bytes or less - so, in such case the USB driver needs to use lot of
Transfer Descriptors (TDs) to transfer 32Kbytes of data.
Unfortunately, number of TDs is limited in GRUB driver(s) - and there is
not enough TDs in this situation.
The result is internal error, no data transfer is initiated by driver
and error code is returned to calling function from usbms.c.

It is vary bad situation: USB device receives CBW command to transfer
0x8000 bytes - but transfer of data is never started because driver run
out of TDs...
Some devices could be automatically recovered from such situation and
works normally later when GRUB tries read disk by smaller parts.
But some devices remains confused even if they are reset by USBMS
specific reset command.

So, I wrote simple experimental patch which splits "long" transfer into
smaller parts - it looks to solve this issue.
Patch solves only read transfer - AFAIK GRUB loader is not designed to
write some data into disk, so there probably never be transfer of "long"
data block in direction into USB mass storage device.
Maximal size 2Kbyte of USB data transfer data block (defined in
usbtrans.h) looks to be more or less optimal value.

It is probably not critical patch because this situation happens mainly
if USB device is full speed device - i.e. this problem is related mainly
to very old USB flash disks or some (older) special mass storage devices
like GPS devices, cameras, card readers etc. - which will be probably
never used as boot devices... (but - who knows...)  :-) 

BR,
Ales
--- ./grub/include/grub/usbtrans.h	2012-11-11 21:01:57.000000000 +0100
+++ ./grub_patched/include/grub/usbtrans.h	2012-11-30 21:35:11.000000000 +0100
@@ -19,6 +19,8 @@
 #ifndef	GRUB_USBTRANS_H
 #define	GRUB_USBTRANS_H	1
 
+#define MAX_USB_TRANSFER_LEN 0x0800
+
 typedef enum
   {
     GRUB_USB_TRANSFER_TYPE_IN,
--- ./grub/grub-core/bus/usb/usbtrans.c	2012-11-11 21:01:57.000000000 +0100
+++ ./grub_patched/grub-core/bus/usb/usbtrans.c	2012-11-30 22:42:04.000000000 +0100
@@ -351,11 +351,23 @@ grub_usb_err_t
 grub_usb_bulk_read (grub_usb_device_t dev,
 		    int endpoint, grub_size_t size, char *data)
 {
-  grub_size_t actual;
+  grub_size_t actual, transferred;
   grub_usb_err_t err;
-  err = grub_usb_bulk_readwrite (dev, endpoint, size, data,
-				 GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual);
-  if (!err && actual != size)
+  grub_size_t current_size, position;
+
+  for (position = 0, transferred = 0;
+       position < size; position += MAX_USB_TRANSFER_LEN)
+    {
+      current_size = size - position;
+      if (current_size >= MAX_USB_TRANSFER_LEN)
+	current_size = MAX_USB_TRANSFER_LEN;
+      err = grub_usb_bulk_readwrite (dev, endpoint, current_size,
+              &data[position], GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual);
+      transferred += actual;
+      if (err || (current_size != actual) ) break;
+    }
+
+  if (!err && transferred != size)
     err = GRUB_USB_ERR_DATA;
   return err;
 }

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to