diff mbox

[U-Boot,3/9] disk/part: introduce get_device_and_partition

Message ID 1345757510-6756-4-git-send-email-robherring2@gmail.com
State Changes Requested
Delegated to: Tom Rini
Headers show

Commit Message

Rob Herring Aug. 23, 2012, 9:31 p.m. UTC
From: Rob Herring <rob.herring@calxeda.com>

All block device related commands (scsiboot, fatload, ext2ls, etc.) have
simliar duplicated device and partition parsing and selection code. This
adds a common function to replace various implementations.

The new function has some enhancements over current versions. If no device
or partition is specified on the command line, the bootdevice env variable
will be used (scsiboot does this). If the partition is not specified and
the device has partitions, then the first bootable partition will be used.
If a bootable partition is not found, the first valid partition is used.
The ret value is not needed since part will be zero when no partition is
found.

Signed-off-by: Rob Herring <rob.herring@calxeda.com>
---
 disk/part.c    |   90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 include/part.h |   13 ++++++--
 2 files changed, 99 insertions(+), 4 deletions(-)

Comments

Stephen Warren Aug. 23, 2012, 10:36 p.m. UTC | #1
On 08/23/2012 03:31 PM, Rob Herring wrote:
> From: Rob Herring <rob.herring@calxeda.com>
> 
> All block device related commands (scsiboot, fatload, ext2ls, etc.) have
> simliar duplicated device and partition parsing and selection code. This
> adds a common function to replace various implementations.
> 
> The new function has some enhancements over current versions. If no device
> or partition is specified on the command line, the bootdevice env variable
> will be used (scsiboot does this). If the partition is not specified and
> the device has partitions, then the first bootable partition will be used.
> If a bootable partition is not found, the first valid partition is used.
> The ret value is not needed since part will be zero when no partition is
> found.

Two thoughts on this patch:

First, if I write "mmc 0" right now, command will always attempt to
access precisely partion 1, whereas after this patch, they will search
for the first bootable, or valid, partition. This is a change in
behavior. It's a pretty reasonable change, but I wonder if it might
cause problems somewhere.

Instead, perhaps this new feature should be explicitly requested,
supporting the following device/partition specifications:

# existing:
dev 0:0        # whole device
dev 0:n        # n >= 1: explicit partition
dev 0          # partition 1
# new:
dev 0:valid    # first valid partition
dev 0:bootable # first bootable partition
dev 0:default  # first bootable partition if there is one,
               # else first valid

That would allow scripts to be very explicit about whether they wanted
this new functionality.

Second, if I run a slew of ext2load commands:

ext2load mmc 0:bootable ${scriptaddr} boot.scr
source ${scriptaddr}
# script does:
ext2load mmc 0:bootable ${kernel_addr} zImage
ext2load mmc 0:bootable ${initrd_addr} initrd.bin
ext2load mmc 0:bootable ${fdt_addr} foo.dtb

Then there are two disadvantages:

1) I believe the partition table is read and decoded and search for
every one of those ext2load commands. Slightly inefficient.

2) There's no permanent record of the partition number, so this couldn't
be e.g. used to construct a kernel command-line etc.

Instead, I wonder if get_device_and_partition() should just support the
existing 3 device specification options, and we introduce a new command
to determine which partition to boot from, e.g.:

# writes result to "bootpart" variable
# or get-default or get-first-valid
part get-first-bootable mmc 0 bootpart

ext2load mmc 0:${bootpart} ${scriptaddr} boot.scr
source ${scriptaddr}
# script does:
ext2load mmc 0:${bootpart} ${kernel_addr} zImage
ext2load mmc 0:${bootpart} ${initrd_addr} initrd.bin
ext2load mmc 0:${bootpart} ${fdt_addr} foo.dtb

That solves those issues. Does anyone have any comment on the two
approaches?

(although perhaps e.g. ext2load always re-reads the partition table
anyway, so perhaps that advantage is moot?)

Aside from that, this series looks conceptually reasonable at a quick
glance. I'd be happy to provide an equivalent to patch 2 for GPT/EFI
partition tables.
Rob Herring Aug. 24, 2012, 1:57 a.m. UTC | #2
On 08/23/2012 05:36 PM, Stephen Warren wrote:
> On 08/23/2012 03:31 PM, Rob Herring wrote:
>> From: Rob Herring <rob.herring@calxeda.com>
>>
>> All block device related commands (scsiboot, fatload, ext2ls, etc.) have
>> simliar duplicated device and partition parsing and selection code. This
>> adds a common function to replace various implementations.
>>
>> The new function has some enhancements over current versions. If no device
>> or partition is specified on the command line, the bootdevice env variable
>> will be used (scsiboot does this). If the partition is not specified and
>> the device has partitions, then the first bootable partition will be used.
>> If a bootable partition is not found, the first valid partition is used.
>> The ret value is not needed since part will be zero when no partition is
>> found.
> 
> Two thoughts on this patch:
> 
> First, if I write "mmc 0" right now, command will always attempt to
> access precisely partion 1, whereas after this patch, they will search
> for the first bootable, or valid, partition. This is a change in
> behavior. It's a pretty reasonable change, but I wonder if it might
> cause problems somewhere.
> 
> Instead, perhaps this new feature should be explicitly requested,
> supporting the following device/partition specifications:
> 
> # existing:
> dev 0:0        # whole device
> dev 0:n        # n >= 1: explicit partition
> dev 0          # partition 1
> # new:
> dev 0:valid    # first valid partition
> dev 0:bootable # first bootable partition
> dev 0:default  # first bootable partition if there is one,
>                # else first valid

I'm not sure we need to distinguish valid vs. bootable. Returning the
first valid partition was really just to maintain somewhat backwards
compatible behavior.

Perhaps just "0:-" would be sufficient.

> 
> That would allow scripts to be very explicit about whether they wanted
> this new functionality.
> 
> Second, if I run a slew of ext2load commands:
> 
> ext2load mmc 0:bootable ${scriptaddr} boot.scr
> source ${scriptaddr}
> # script does:
> ext2load mmc 0:bootable ${kernel_addr} zImage
> ext2load mmc 0:bootable ${initrd_addr} initrd.bin
> ext2load mmc 0:bootable ${fdt_addr} foo.dtb
> 
> Then there are two disadvantages:
> 
> 1) I believe the partition table is read and decoded and search for
> every one of those ext2load commands. Slightly inefficient.

It was already multiple times per command with the command function
calling get_partition_info and then the filesystem code calling it again
internally as well. Now it is only 1 time at least. I would think the
1st partition being bootable is the common case.

> 2) There's no permanent record of the partition number, so this couldn't
> be e.g. used to construct a kernel command-line etc.

You mean to setup rootfs? I don't think we want u-boot to do that. Or
what would be the use?

> Instead, I wonder if get_device_and_partition() should just support the
> existing 3 device specification options, and we introduce a new command
> to determine which partition to boot from, e.g.:
> 
> # writes result to "bootpart" variable
> # or get-default or get-first-valid
> part get-first-bootable mmc 0 bootpart
> 
> ext2load mmc 0:${bootpart} ${scriptaddr} boot.scr
> source ${scriptaddr}
> # script does:
> ext2load mmc 0:${bootpart} ${kernel_addr} zImage
> ext2load mmc 0:${bootpart} ${initrd_addr} initrd.bin
> ext2load mmc 0:${bootpart} ${fdt_addr} foo.dtb
> 
> That solves those issues. Does anyone have any comment on the two
> approaches?

I'm really open to either way.

Another option would be for the first command run to set bootpart and
then re-use that value on subsequent commands.

Rob

> 
> (although perhaps e.g. ext2load always re-reads the partition table
> anyway, so perhaps that advantage is moot?)
> 
> Aside from that, this series looks conceptually reasonable at a quick
> glance. I'd be happy to provide an equivalent to patch 2 for GPT/EFI
> partition tables.
>
Stephen Warren Aug. 24, 2012, 2:51 a.m. UTC | #3
On 08/23/2012 07:57 PM, Rob Herring wrote:
> On 08/23/2012 05:36 PM, Stephen Warren wrote:
>> On 08/23/2012 03:31 PM, Rob Herring wrote:
>>> From: Rob Herring <rob.herring@calxeda.com>
>>>
>>> All block device related commands (scsiboot, fatload, ext2ls, etc.) have
>>> simliar duplicated device and partition parsing and selection code. This
>>> adds a common function to replace various implementations.
>>>
>>> The new function has some enhancements over current versions. If no device
>>> or partition is specified on the command line, the bootdevice env variable
>>> will be used (scsiboot does this). If the partition is not specified and
>>> the device has partitions, then the first bootable partition will be used.
>>> If a bootable partition is not found, the first valid partition is used.
>>> The ret value is not needed since part will be zero when no partition is
>>> found.
>>
>> Two thoughts on this patch:
>>
>> First, if I write "mmc 0" right now, command will always attempt to
>> access precisely partion 1, whereas after this patch, they will search
>> for the first bootable, or valid, partition. This is a change in
>> behavior. It's a pretty reasonable change, but I wonder if it might
>> cause problems somewhere.
>>
>> Instead, perhaps this new feature should be explicitly requested,
>> supporting the following device/partition specifications:
>>
>> # existing:
>> dev 0:0        # whole device
>> dev 0:n        # n >= 1: explicit partition
>> dev 0          # partition 1
>> # new:
>> dev 0:valid    # first valid partition
>> dev 0:bootable # first bootable partition
>> dev 0:default  # first bootable partition if there is one,
>>                # else first valid
> 
> I'm not sure we need to distinguish valid vs. bootable. Returning the
> first valid partition was really just to maintain somewhat backwards
> compatible behavior.
> 
> Perhaps just "0:-" would be sufficient.

I guess that syntax would be fine if we don't need to distinguish all
the cases. "-" isn't that descriptive though, and I've only seen it mean
"nothing" in U-Boot commands. So, bike-shedding a bit, it doesn't seem
exactly correct. Perhaps just "auto"?

>> That would allow scripts to be very explicit about whether they wanted
>> this new functionality.
>>
>> Second, if I run a slew of ext2load commands:
>>
>> ext2load mmc 0:bootable ${scriptaddr} boot.scr
>> source ${scriptaddr}
>> # script does:
>> ext2load mmc 0:bootable ${kernel_addr} zImage
>> ext2load mmc 0:bootable ${initrd_addr} initrd.bin
>> ext2load mmc 0:bootable ${fdt_addr} foo.dtb
>>
>> Then there are two disadvantages:
>>
>> 1) I believe the partition table is read and decoded and search for
>> every one of those ext2load commands. Slightly inefficient.
> 
> It was already multiple times per command with the command function
> calling get_partition_info and then the filesystem code calling it again
> internally as well. Now it is only 1 time at least. I would think the
> 1st partition being bootable is the common case.
> 
>> 2) There's no permanent record of the partition number, so this couldn't
>> be e.g. used to construct a kernel command-line etc.
> 
> You mean to setup rootfs? I don't think we want u-boot to do that. Or
> what would be the use?

I can imagine a boot.scr that does:

setenv bootargs root=/dev/mmcblk0p${bootpart}

But then, you may as well use the partition UUID feature instead of
that, so that boot.scr doesn't need to know the kernel's device name.

>> Instead, I wonder if get_device_and_partition() should just support the
>> existing 3 device specification options, and we introduce a new command
>> to determine which partition to boot from, e.g.:
>>
>> # writes result to "bootpart" variable
>> # or get-default or get-first-valid
>> part get-first-bootable mmc 0 bootpart
>>
>> ext2load mmc 0:${bootpart} ${scriptaddr} boot.scr
>> source ${scriptaddr}
>> # script does:
>> ext2load mmc 0:${bootpart} ${kernel_addr} zImage
>> ext2load mmc 0:${bootpart} ${initrd_addr} initrd.bin
>> ext2load mmc 0:${bootpart} ${fdt_addr} foo.dtb
>>
>> That solves those issues. Does anyone have any comment on the two
>> approaches?
> 
> I'm really open to either way.
> 
> Another option would be for the first command run to set bootpart and
> then re-use that value on subsequent commands.

That could work too, although commands using environment variables seems
a little implicit/hidden.
Tom Rini Sept. 5, 2012, 11:53 p.m. UTC | #4
On Thu, Aug 23, 2012 at 08:51:50PM -0600, Stephen Warren wrote:
> On 08/23/2012 07:57 PM, Rob Herring wrote:
> > On 08/23/2012 05:36 PM, Stephen Warren wrote:
> >> On 08/23/2012 03:31 PM, Rob Herring wrote:
> >>> From: Rob Herring <rob.herring@calxeda.com>
> >>>
> >>> All block device related commands (scsiboot, fatload, ext2ls, etc.) have
> >>> simliar duplicated device and partition parsing and selection code. This
> >>> adds a common function to replace various implementations.
> >>>
> >>> The new function has some enhancements over current versions. If no device
> >>> or partition is specified on the command line, the bootdevice env variable
> >>> will be used (scsiboot does this). If the partition is not specified and
> >>> the device has partitions, then the first bootable partition will be used.
> >>> If a bootable partition is not found, the first valid partition is used.
> >>> The ret value is not needed since part will be zero when no partition is
> >>> found.
> >>
> >> Two thoughts on this patch:
> >>
> >> First, if I write "mmc 0" right now, command will always attempt to
> >> access precisely partion 1, whereas after this patch, they will search
> >> for the first bootable, or valid, partition. This is a change in
> >> behavior. It's a pretty reasonable change, but I wonder if it might
> >> cause problems somewhere.
> >>
> >> Instead, perhaps this new feature should be explicitly requested,
> >> supporting the following device/partition specifications:
> >>
> >> # existing:
> >> dev 0:0        # whole device
> >> dev 0:n        # n >= 1: explicit partition
> >> dev 0          # partition 1
> >> # new:
> >> dev 0:valid    # first valid partition
> >> dev 0:bootable # first bootable partition
> >> dev 0:default  # first bootable partition if there is one,
> >>                # else first valid
> > 
> > I'm not sure we need to distinguish valid vs. bootable. Returning the
> > first valid partition was really just to maintain somewhat backwards
> > compatible behavior.
> > 
> > Perhaps just "0:-" would be sufficient.
> 
> I guess that syntax would be fine if we don't need to distinguish all
> the cases. "-" isn't that descriptive though, and I've only seen it mean
> "nothing" in U-Boot commands. So, bike-shedding a bit, it doesn't seem
> exactly correct. Perhaps just "auto"?

"auto" sounds like a good idea to me as well.

[snip]
> >> Instead, I wonder if get_device_and_partition() should just support the
> >> existing 3 device specification options, and we introduce a new command
> >> to determine which partition to boot from, e.g.:
> >>
> >> # writes result to "bootpart" variable
> >> # or get-default or get-first-valid
> >> part get-first-bootable mmc 0 bootpart
> >>
> >> ext2load mmc 0:${bootpart} ${scriptaddr} boot.scr
> >> source ${scriptaddr}
> >> # script does:
> >> ext2load mmc 0:${bootpart} ${kernel_addr} zImage
> >> ext2load mmc 0:${bootpart} ${initrd_addr} initrd.bin
> >> ext2load mmc 0:${bootpart} ${fdt_addr} foo.dtb
> >>
> >> That solves those issues. Does anyone have any comment on the two
> >> approaches?
> > 
> > I'm really open to either way.
> > 
> > Another option would be for the first command run to set bootpart and
> > then re-use that value on subsequent commands.
> 
> That could work too, although commands using environment variables seems
> a little implicit/hidden.

Agreed.  We should be very careful when changing behavior.  Adding a new
command so that folks can use this as they see fit sounds like the best
idea.  I shudder to think what the partition table on some SD cards I
have around looks like.
diff mbox

Patch

diff --git a/disk/part.c b/disk/part.c
index 76f3939..1284e1a 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -70,7 +70,7 @@  static const struct block_drvr block_drvr[] = {
 
 DECLARE_GLOBAL_DATA_PTR;
 
-block_dev_desc_t *get_dev(char* ifname, int dev)
+block_dev_desc_t *get_dev(const char *ifname, int dev)
 {
 	const struct block_drvr *drvr = block_drvr;
 	block_dev_desc_t* (*reloc_get_dev)(int dev);
@@ -97,7 +97,7 @@  block_dev_desc_t *get_dev(char* ifname, int dev)
 	return NULL;
 }
 #else
-block_dev_desc_t *get_dev(char* ifname, int dev)
+block_dev_desc_t *get_dev(const char *ifname, int dev)
 {
 	return NULL;
 }
@@ -288,6 +288,7 @@  void init_part (block_dev_desc_t * dev_desc)
 	    return;
 	}
 #endif
+	dev_desc->part_type = PART_TYPE_UNKNOWN;
 }
 
 
@@ -433,3 +434,88 @@  void print_part (block_dev_desc_t * dev_desc)
 #endif
 
 #endif
+
+int get_device_and_partition(const char *ifname, const char *dev_str,
+			     block_dev_desc_t **dev_desc,
+			     disk_partition_t *info)
+{
+	int ret;
+	char *ep;
+	int dev;
+	block_dev_desc_t *desc;
+	int p;
+	int part = 0;
+	char *part_str;
+	disk_partition_t *best_part = NULL;
+
+	if (dev_str)
+		dev = simple_strtoul(dev_str, &ep, 16);
+
+	if (!dev_str || (dev_str == ep)) {
+		dev_str = getenv("bootdevice");
+		if (dev_str)
+			dev = simple_strtoul(dev_str, &ep, 16);
+		if (!dev_str || (dev_str == ep))
+			goto err;
+	}
+
+	desc = get_dev(ifname, dev);
+	if (!desc || (desc->type == DEV_TYPE_UNKNOWN))
+		goto err;
+
+	if (desc->part_type == PART_TYPE_UNKNOWN) {
+		/* disk doesn't use partition table */
+		if (!desc->lba) {
+			printf("**Bad disk size - %s %d:0 **\n", ifname, dev);
+			return -1;
+		}
+		info->start = 0;
+		info->size = desc->lba;
+		info->blksz = desc->blksz;
+
+		*dev_desc = desc;
+		return 0;
+	}
+
+	part_str = strchr(dev_str, ':');
+	if (part_str) {
+		part = (int)simple_strtoul(++part_str, NULL, 16);
+		ret = get_partition_info(desc, part, info);
+	} else {
+		/* find the first bootable partition. If none are bootable,
+		 * fall back to the first valid partition */
+		for (p = 1; p < 16; p++) {
+			ret = get_partition_info(desc, p, info);
+			if (ret)
+				continue;
+
+			if (!best_part || info->bootable) {
+				best_part = info;
+				part = p;
+			}
+
+			if (info->bootable)
+				break;
+		}
+		info = best_part;
+		if (part)
+			ret = 0;
+	}
+	if (ret) {
+		printf("** Invalid partition %d, use `dev[:part]' **\n", part);
+		return -1;
+	}
+	if (strncmp((char *)info->type, BOOT_PART_TYPE, sizeof(info->type)) != 0) {
+		printf("** Invalid partition type \"%.32s\""
+			" (expect \"" BOOT_PART_TYPE "\")\n",
+			info->type);
+		return -1;
+	}
+
+	*dev_desc = desc;
+	return part;
+
+ err:
+	puts("** Invalid boot device, use `dev[:part]' **\n");
+	return -1;
+}
diff --git a/include/part.h b/include/part.h
index 447f69d..a6d06f3 100644
--- a/include/part.h
+++ b/include/part.h
@@ -98,7 +98,7 @@  typedef struct disk_partition {
 
 /* Misc _get_dev functions */
 #ifdef CONFIG_PARTITIONS
-block_dev_desc_t* get_dev(char* ifname, int dev);
+block_dev_desc_t *get_dev(const char *ifname, int dev);
 block_dev_desc_t* ide_get_dev(int dev);
 block_dev_desc_t* sata_get_dev(int dev);
 block_dev_desc_t* scsi_get_dev(int dev);
@@ -112,8 +112,12 @@  int get_partition_info (block_dev_desc_t * dev_desc, int part, disk_partition_t
 void print_part (block_dev_desc_t *dev_desc);
 void  init_part (block_dev_desc_t *dev_desc);
 void dev_print(block_dev_desc_t *dev_desc);
+int get_device_and_partition(const char *ifname, const char *dev_str,
+			     block_dev_desc_t **dev_desc,
+			     disk_partition_t *info);
 #else
-static inline block_dev_desc_t* get_dev(char* ifname, int dev) { return NULL; }
+static inline block_dev_desc_t *get_dev(const char *ifname, int dev)
+{ return NULL; }
 static inline block_dev_desc_t* ide_get_dev(int dev) { return NULL; }
 static inline block_dev_desc_t* sata_get_dev(int dev) { return NULL; }
 static inline block_dev_desc_t* scsi_get_dev(int dev) { return NULL; }
@@ -127,6 +131,11 @@  static inline int get_partition_info (block_dev_desc_t * dev_desc, int part,
 static inline void print_part (block_dev_desc_t *dev_desc) {}
 static inline void  init_part (block_dev_desc_t *dev_desc) {}
 static inline void dev_print(block_dev_desc_t *dev_desc) {}
+static inline int get_device_and_partition(const char *ifname,
+					   const char *dev_str,
+					   block_dev_desc_t **dev_desc,
+					   disk_partition_t *info)
+{ *dev_desc = NULL; return -1; }
 #endif
 
 #ifdef CONFIG_MAC_PARTITION