Patchwork [U-Boot] Fix block device accesses beyond 2TiB

login
register
mail settings
Submitter Sascha Silbe
Date June 14, 2013, 11:07 a.m.
Message ID <1371208045-4428-1-git-send-email-t-uboot@infra-silbe.de>
Download mbox | patch
Permalink /patch/251357/
State Accepted
Delegated to: Tom Rini
Headers show

Comments

Sascha Silbe - June 14, 2013, 11:07 a.m.
With CONFIG_SYS_64BIT_LBA, lbaint_t gets defined as a 64-bit type,
which is required to represent block numbers for storage devices that
exceed 2TiB (the block size usually is 512B), e.g. recent hard drives.

For some obscure reason, the current U-Boot code uses lbaint_t for the
number of blocks to read (a rather optimistic estimation of how RAM
sizes will evolve), but not for the starting address. Trying to access
blocks beyond the 2TiB boundary will simply wrap around and read a
block within the 0..2TiB range.

We now use lbaint_t for block start addresses, too. This required
changes to all block drivers as the signature of block_read(),
block_write() and block_erase() in block_dev_desc_t changed.

Signed-off-by: Sascha Silbe <t-uboot@infra-silbe.de>
---
Functionality tested on CuBox Pro with a Western Digital WD30EFRX hard
disk (3TB). Build tested for all arm boards and sandbox.

Testing on other boards and architectures would be appreciated.

Fixes for typos, style errors etc. are explicitly out of scope for
this patch, even those checkpatch complains about because they appear
on or near lines touched by the patch. They are unrelated to the issue
at hand and can be fixed up later. Mixing in unrelated changes would
just make harder to revert any problematic change.

 common/cmd_ide.c     | 14 +++++++-------
 common/usb_storage.c |  8 ++++----
 drivers/mmc/mmc.c    | 17 +++++++++--------
 include/ide.h        |  5 +++--
 include/part.h       |  6 +++---
 5 files changed, 26 insertions(+), 24 deletions(-)
Marek Vasut - June 17, 2013, 8:26 p.m.
Hello Sascha,

> With CONFIG_SYS_64BIT_LBA, lbaint_t gets defined as a 64-bit type,
> which is required to represent block numbers for storage devices that
> exceed 2TiB (the block size usually is 512B), e.g. recent hard drives.
> 
> For some obscure reason, the current U-Boot code uses lbaint_t for the
> number of blocks to read (a rather optimistic estimation of how RAM
> sizes will evolve), but not for the starting address. Trying to access
> blocks beyond the 2TiB boundary will simply wrap around and read a
> block within the 0..2TiB range.
> 
> We now use lbaint_t for block start addresses, too. This required
> changes to all block drivers as the signature of block_read(),
> block_write() and block_erase() in block_dev_desc_t changed.
> 
> Signed-off-by: Sascha Silbe <t-uboot@infra-silbe.de>
> ---
> Functionality tested on CuBox Pro with a Western Digital WD30EFRX hard
> disk (3TB). Build tested for all arm boards and sandbox.
> 
> Testing on other boards and architectures would be appreciated.
> 
> Fixes for typos, style errors etc. are explicitly out of scope for
> this patch, even those checkpatch complains about because they appear
> on or near lines touched by the patch. They are unrelated to the issue
> at hand and can be fixed up later. Mixing in unrelated changes would
> just make harder to revert any problematic change.

Quick review looks OK.

Best regards,
Marek Vasut
Albert ARIBAUD - June 22, 2013, 10:07 a.m.
On Mon, 17 Jun 2013 22:26:00 +0200, Marek Vasut <marex@denx.de> wrote:

> Hello Sascha,
> 
> > With CONFIG_SYS_64BIT_LBA, lbaint_t gets defined as a 64-bit type,
> > which is required to represent block numbers for storage devices that
> > exceed 2TiB (the block size usually is 512B), e.g. recent hard drives.
> > 
> > For some obscure reason, the current U-Boot code uses lbaint_t for the
> > number of blocks to read (a rather optimistic estimation of how RAM
> > sizes will evolve), but not for the starting address. Trying to access
> > blocks beyond the 2TiB boundary will simply wrap around and read a
> > block within the 0..2TiB range.
> > 
> > We now use lbaint_t for block start addresses, too. This required
> > changes to all block drivers as the signature of block_read(),
> > block_write() and block_erase() in block_dev_desc_t changed.
> > 
> > Signed-off-by: Sascha Silbe <t-uboot@infra-silbe.de>
> > ---
> > Functionality tested on CuBox Pro with a Western Digital WD30EFRX hard
> > disk (3TB). Build tested for all arm boards and sandbox.
> > 
> > Testing on other boards and architectures would be appreciated.
> > 
> > Fixes for typos, style errors etc. are explicitly out of scope for
> > this patch, even those checkpatch complains about because they appear
> > on or near lines touched by the patch. They are unrelated to the issue
> > at hand and can be fixed up later. Mixing in unrelated changes would
> > just make harder to revert any problematic change.
> 
> Quick review looks OK.
> 
> Best regards,
> Marek Vasut

Anyone could test Sascha's patch? Especially Frédédic, can you test it
and see how this works with your enabling 64-bit LBA on LaCie kirkwood
products?

Amicalement,
Albert ARIBAUD - June 22, 2013, 3:31 p.m.
Hi Frédéric,

On Sat, 22 Jun 2013 14:29:26 +0200, Frédéric Leroy <fredo@starox.org>
wrote:

> Hello Albert, Sascha, Marek
> 
> Le 22/06/2013 12:07, Albert ARIBAUD a écrit :
> > > Quick review looks OK.
> > > 
> > > Best regards,
> > > Marek Vasut
> >
> > Anyone could test Sascha's patch? Especially Frédédic, can you test it
> > and see how this works with your enabling 64-bit LBA on LaCie kirkwood
> > products?
> >
> > Amicalement,
> 
> I applied Sascha patch with 64-bit LBA enabled.
> I verifyed it by using the "ide read" command.
> Logs are attached for u-boot tests, with and without the patch.
> I added the output of dd of the same block on a linux machine in order
> to verify the content.
> There is still issues to access file on a ext fs beyond the 2.1 limit
> (see end of logs).
> 
> However, the patch issues no warning at compile time and fixes the ide
> block layer.

OK -- anyone has any idea why Sasha's patch fixes reading from "far"
blocks but does not fix ex2ls? Frankly, I'd prefer it if the patch
fixed it all. :)

> Best regards,

Amicalement,
Frederic Leroy - June 24, 2013, 9:46 a.m.
Le 22/06/2013 17:31, Albert ARIBAUD a écrit :
> Hi Frédéric,
>
> > However, the patch issues no warning at compile time and fixes the ide
> > block layer.
>
> OK -- anyone has any idea why Sasha's patch fixes reading from "far"
> blocks but does not fix ex2ls? Frankly, I'd prefer it if the patch
> fixed it all. :)

I did a quick look.
Ext2 code use "unsigned long" for the partition offset and "int" for
sector computation.
I will try to fix it, and try to test the other fs ( ext4, xfs, fat).
I would see one patch for the ide code, and one or more patch for the fs
code.
IMHO, Sascha patch can be applied as.

Regards,
Sascha Silbe - June 24, 2013, 9:13 p.m.
Frédéric Leroy <fredo@starox.org> writes:

> Le 22/06/2013 17:31, Albert ARIBAUD a écrit :
>> > However, the patch issues no warning at compile time and fixes the ide
>> > block layer.
>>
>> OK -- anyone has any idea why Sasha's patch fixes reading from "far"
>> blocks but does not fix ex2ls? Frankly, I'd prefer it if the patch
>> fixed it all. :)

Thanks to Frédéric for testing and to Marek for the quick review.


[...]
> Ext2 code use "unsigned long" for the partition offset and "int" for
> sector computation.
> I will try to fix it, and try to test the other fs ( ext4, xfs, fat).
> I would see one patch for the ide code, and one or more patch for the fs
> code.
> IMHO, Sascha patch can be applied as.

While the IDE layer fix isn't enough for most use cases (storage devices
in the TiB range are usually used with file systems rather than raw
partitions), I agree that the file system level fixes should happen in
separate patches.

Tom, do you consider this a bug fix worth landing in v2013.07 or an
intrusive change (possibly even a feature as it never worked before)
that would go into v2013.10 instead?

If it's considered a bug fix, it would probably be a good idea to land
my patch ASAP rather than waiting for the FS level fixes. In the
"feature" case we can wait and include both IDE and FS fixes in a single
patch set once someone gets around to do the FS fixes.

Sascha
Tom Rini - June 26, 2013, 8:25 p.m.
On Fri, Jun 14, 2013 at 01:07:25PM +0200, Sascha Silbe wrote:

> With CONFIG_SYS_64BIT_LBA, lbaint_t gets defined as a 64-bit type,
> which is required to represent block numbers for storage devices that
> exceed 2TiB (the block size usually is 512B), e.g. recent hard drives.
> 
> For some obscure reason, the current U-Boot code uses lbaint_t for the
> number of blocks to read (a rather optimistic estimation of how RAM
> sizes will evolve), but not for the starting address. Trying to access
> blocks beyond the 2TiB boundary will simply wrap around and read a
> block within the 0..2TiB range.
> 
> We now use lbaint_t for block start addresses, too. This required
> changes to all block drivers as the signature of block_read(),
> block_write() and block_erase() in block_dev_desc_t changed.
> 
> Signed-off-by: Sascha Silbe <t-uboot@infra-silbe.de>

Applied to u-boot/master, thanks!

Patch

diff --git a/common/cmd_ide.c b/common/cmd_ide.c
index 78b4aa7..59e95df 100644
--- a/common/cmd_ide.c
+++ b/common/cmd_ide.c
@@ -830,7 +830,7 @@  static void ide_ident(block_dev_desc_t *dev_desc)
 
 /* ------------------------------------------------------------------------- */
 
-ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer)
+ulong ide_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer)
 {
 	ulong n = 0;
 	unsigned char c;
@@ -844,7 +844,7 @@  ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer)
 		lba48 = 1;
 	}
 #endif
-	debug("ide_read dev %d start %lX, blocks " LBAF " buffer at %lX\n",
+	debug("ide_read dev %d start " LBAF ", blocks " LBAF " buffer at %lX\n",
 	      device, blknr, blkcnt, (ulong) buffer);
 
 	ide_led(DEVICE_LED(device), 1);	/* LED on       */
@@ -934,8 +934,8 @@  ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer)
 
 		if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) !=
 		    ATA_STAT_DRQ) {
-			printf("Error (no IRQ) dev %d blk %ld: status %#02x\n",
-				device, blknr, c);
+			printf("Error (no IRQ) dev %d blk " LBAF ": status "
+			       "%#02x\n", device, blknr, c);
 			break;
 		}
 
@@ -954,7 +954,7 @@  IDE_READ_E:
 /* ------------------------------------------------------------------------- */
 
 
-ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer)
+ulong ide_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer)
 {
 	ulong n = 0;
 	unsigned char c;
@@ -1022,8 +1022,8 @@  ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer)
 
 		if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) !=
 		    ATA_STAT_DRQ) {
-			printf("Error (no IRQ) dev %d blk %ld: status %#02x\n",
-				device, blknr, c);
+			printf("Error (no IRQ) dev %d blk " LBAF ": status "
+				"%#02x\n", device, blknr, c);
 			goto WR_OUT;
 		}
 
diff --git a/common/usb_storage.c b/common/usb_storage.c
index 457970f..4599d03 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -170,9 +170,9 @@  int usb_stor_get_info(struct usb_device *dev, struct us_data *us,
 		      block_dev_desc_t *dev_desc);
 int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,
 		      struct us_data *ss);
-unsigned long usb_stor_read(int device, unsigned long blknr,
+unsigned long usb_stor_read(int device, lbaint_t blknr,
 			    lbaint_t blkcnt, void *buffer);
-unsigned long usb_stor_write(int device, unsigned long blknr,
+unsigned long usb_stor_write(int device, lbaint_t blknr,
 			     lbaint_t blkcnt, const void *buffer);
 struct usb_device * usb_get_dev_index(int index);
 void uhci_show_temp_int_td(void);
@@ -1054,7 +1054,7 @@  static void usb_bin_fixup(struct usb_device_descriptor descriptor,
 }
 #endif /* CONFIG_USB_BIN_FIXUP */
 
-unsigned long usb_stor_read(int device, unsigned long blknr,
+unsigned long usb_stor_read(int device, lbaint_t blknr,
 			    lbaint_t blkcnt, void *buffer)
 {
 	lbaint_t start, blks;
@@ -1127,7 +1127,7 @@  retry_it:
 	return blkcnt;
 }
 
-unsigned long usb_stor_write(int device, unsigned long blknr,
+unsigned long usb_stor_write(int device, lbaint_t blknr,
 				lbaint_t blkcnt, const void *buffer)
 {
 	lbaint_t start, blks;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 0a2f535..3b01434 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -254,7 +254,7 @@  err_out:
 }
 
 static unsigned long
-mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
+mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt)
 {
 	int err = 0;
 	struct mmc *mmc = find_mmc_device(dev_num);
@@ -266,7 +266,8 @@  mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
 
 	if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
 		printf("\n\nCaution! Your devices Erase group is 0x%x\n"
-			"The erase range would be change to 0x%lx~0x%lx\n\n",
+		       "The erase range would be change to "
+		       "0x" LBAF "~0x" LBAF "\n\n",
 		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
 		       ((start + blkcnt + mmc->erase_grp_size)
 		       & ~(mmc->erase_grp_size - 1)) - 1);
@@ -289,14 +290,14 @@  mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
 }
 
 static ulong
-mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
+mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*src)
 {
 	struct mmc_cmd cmd;
 	struct mmc_data data;
 	int timeout = 1000;
 
 	if ((start + blkcnt) > mmc->block_dev.lba) {
-		printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
+		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
 			start + blkcnt, mmc->block_dev.lba);
 		return 0;
 	}
@@ -344,7 +345,7 @@  mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
 }
 
 static ulong
-mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
+mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void*src)
 {
 	lbaint_t cur, blocks_todo = blkcnt;
 
@@ -367,7 +368,7 @@  mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
 	return blkcnt;
 }
 
-static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start,
+static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
 			   lbaint_t blkcnt)
 {
 	struct mmc_cmd cmd;
@@ -406,7 +407,7 @@  static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start,
 	return blkcnt;
 }
 
-static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
+static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst)
 {
 	lbaint_t cur, blocks_todo = blkcnt;
 
@@ -418,7 +419,7 @@  static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
 		return 0;
 
 	if ((start + blkcnt) > mmc->block_dev.lba) {
-		printf("MMC: block number 0x%lx exceeds max(0x%lx)\n",
+		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
 			start + blkcnt, mmc->block_dev.lba);
 		return 0;
 	}
diff --git a/include/ide.h b/include/ide.h
index afea85c..f691a74 100644
--- a/include/ide.h
+++ b/include/ide.h
@@ -54,8 +54,9 @@  typedef ulong lbaint_t;
  */
 
 void ide_init(void);
-ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer);
-ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer);
+ulong ide_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer);
+ulong ide_write(int device, lbaint_t blknr, lbaint_t blkcnt,
+		const void *buffer);
 
 #ifdef CONFIG_IDE_PREINIT
 int ide_preinit(void);
diff --git a/include/part.h b/include/part.h
index f7c7cc5..35c1c5b 100644
--- a/include/part.h
+++ b/include/part.h
@@ -43,15 +43,15 @@  typedef struct block_dev_desc {
 	char		product[20+1];	/* IDE Serial no, SCSI product */
 	char		revision[8+1];	/* firmware revision */
 	unsigned long	(*block_read)(int dev,
-				      unsigned long start,
+				      lbaint_t start,
 				      lbaint_t blkcnt,
 				      void *buffer);
 	unsigned long	(*block_write)(int dev,
-				       unsigned long start,
+				       lbaint_t start,
 				       lbaint_t blkcnt,
 				       const void *buffer);
 	unsigned long   (*block_erase)(int dev,
-				       unsigned long start,
+				       lbaint_t start,
 				       lbaint_t blkcnt);
 	void		*priv;		/* driver private struct pointer */
 }block_dev_desc_t;