[U-Boot] [PATCH 0/3] add support for GPT partition name manipulation

From: Alison Chaiken alison@she-devel.com
One way for userspace and the bootloader to exchange information about dynamic image selection is via the storage device partition table, as described at
https://source.android.com/devices/tech/ota/ab_updates
The scheme described there relies on setting partitions' "boot" flag. When no partition on a device is bootable since the kernel and U-Boot are stored elsewhere, the name field in the GPT partition table offers another logical place to store information. These patches allow users to easily modify GPT partition names via bootscripts that can select different images based on a boot-failure counter, or when userspace installs a software update.
These patches have been tested on a TI DRA7xx-based SOM with U-Boot 2015.07. The storage device is an eMMC.
Alison Chaiken (3): GPT: add accessor function for disk GUID GPT: read partition table from device into a data structure rename GPT partitions to detect boot failure
cmd/gpt.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- disk/part_efi.c | 31 ++++++ include/part.h | 24 ++++ 3 files changed, 392 insertions(+), 2 deletions(-)

From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 29 ++++++++++++++++++++++++++++- disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ include/part.h | 15 +++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..814cb11 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return 1; + + if (namestr != NULL) + setenv(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return 0; +} + /** * do_gpt(): Perform GPT operations * @@ -412,7 +429,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = CMD_RET_SUCCESS; int dev = 0; - char *ep; + char *ep, *varname = NULL; struct blk_desc *blk_dev_desc = NULL;
if (argc < 4 || argc > 5) @@ -436,6 +453,9 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if (strcmp(argv[1], "guid") == 0) { + if (argc == 5) strcpy(varname, argv[4]); + return do_disk_guid(blk_dev_desc, varname); } else { return CMD_RET_USAGE; } @@ -458,4 +478,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " guid <interface> <dev>\n" + " - print disk GUID\n" + " guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 1b7ba27..de35c9b 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) * Public Functions (include/part.h) */
+/* + * UUID is displayed as 32 hexadecimal digits, in 5 groups, + * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters + */ +int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); + gpt_entry *gpt_pte = NULL; + unsigned char *guid_bin; + + /* This function validates AND fills in the GPT header and PTE */ + if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + return -1; + } else { + printf("%s: *** Using Backup GPT ***\n", + __func__); + } + } + + guid_bin = (unsigned char *)(gpt_head->disk_guid.b); + uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID); + + return 0; +} + void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/include/part.h b/include/part.h index 83bce05..69143bb 100644 --- a/include/part.h +++ b/include/part.h @@ -367,6 +367,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte); + + +/** + * get_disk_guid() - Function to read the GUID string from a device's GPT + * + * This function reads the GUID string from a block device whose descriptor + * is provided. + * + * @param dev_desc - block device descriptor + * @param guid - pre-allocated string in which to return the GUID + * + * @return - '0' on success, otherwise error + */ +int get_disk_guid(struct blk_desc *dev_desc, char *guid); + #endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

On Sat, May 20, 2017 at 07:27:53PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Overall looks good, a few style things:
- if (namestr != NULL)
setenv(namestr, disk_guid);
You can just if (namestr) here
[snip]
- } else if (strcmp(argv[1], "guid") == 0) {
if (argc == 5) strcpy(varname, argv[4]);
Go ahead and do this on multiple lines please. Thanks!

From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 9 +++++ 2 files changed, 122 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 814cb11..128c895 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,112 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = (struct disk_part *)malloc(sizeof(*newpart)); + + if (!newpart) + return ERR_PTR(-ENOMEM); + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + /* + * 33 rather than 32, as each string must be null-terminated; + * other extract_val() above will fail. + */ + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, 33); + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, 33); + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN + 1); +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as u-boot does */ + int valid_parts = 1, p, ret = 0; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part) && (valid_parts >= 2)) + return -1; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + del_gpt_info(); + return -1; + } + return --valid_parts; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + } + return ret; +} + + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -456,6 +565,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if (strcmp(argv[1], "guid") == 0) { if (argc == 5) strcpy(varname, argv[4]); return do_disk_guid(blk_dev_desc, varname); + } else if (strcmp(argv[1], "read") == 0) { + return do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -478,6 +589,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index 69143bb..87a9818 100644 --- a/include/part.h +++ b/include/part.h @@ -9,6 +9,7 @@
#include <blk.h> #include <ide.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -46,6 +47,8 @@ struct block_drvr { #define DEV_TYPE_CDROM 0x05 /* CD-ROM */ #define DEV_TYPE_OPDISK 0x07 /* optical disk */
+#define MAX_SEARCH_PARTITIONS 16 + typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ lbaint_t size; /* number of blocks in partition */ @@ -64,6 +67,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

On Sat, May 20, 2017 at 07:27:54PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
[snip]
- /*
* 33 rather than 32, as each string must be null-terminated;
* other extract_val() above will fail.
*/
- strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, 33);
- strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, 33);
This isn't right. We have name[32]/type[32] so we're overflowing. I'm not quite sure where 32 came from and if we should be defining a name for it as well. But to make sure it is null-terminated we should be here memset'ing after malloc.
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
UUID_STR_LEN + 1);
+#endif
Then this should just be copying UUID_STR_LEN over.
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -1;
We should return something from errno here rather than -1. Thanks!

From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it renames it failed’ and renames the ‘backup’ partition as ‘candidate’.
Logic: -- Partitions can go to ‘failed’ only from ‘candidate’ and only via u-boot. Partitions can go to ‘backup’ only from ‘primary’ and vice-versa, only via Linux. Partitions go to ‘candidate’ from ‘primary’ or ‘backup’ only via u-boot. Only system update software will rename 'failed' partitions.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 6 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 128c895..f2c32ae 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -21,6 +21,9 @@ #include <memalign.h> #include <linux/compat.h>
+/* ONEMIB is 2**20 */ +#define ONEMIB 1024*1024 + static LIST_HEAD(disk_partitions);
/** @@ -114,7 +117,6 @@ static char *extract_val(const char *str, const char *key) break; } } - free(strcopy);
return new; @@ -158,6 +160,7 @@ static void del_gpt_info(void) { struct list_head *pos = &disk_partitions; struct disk_part *curr; + while (!list_empty(pos)) { curr = list_entry(pos->next, struct disk_part, list); list_del(pos->next); @@ -165,6 +168,11 @@ static void del_gpt_info(void) } }
+/* + * The number '33' comes from the '32' in the definition of disk_partition_t + * in include/part.h. That file has '37' rather than UUID_STR_LEN + 1, from + * include/uuid.h + */ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) { struct disk_part *newpart; @@ -191,16 +199,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, ONEMIB); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -212,6 +237,89 @@ static void print_gpt_info(void) } }
+static int calc_parts_list_len(int numparts) +{ + /* + * prefatory string: + * doc/README.GPT, suggests that + * int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk="); + * is correct, but extract_val() expects "uuid_disk" first. + */ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + 33); + /* 17 because partstr below is 16 chars */ + partlistlen += numparts * (strlen("start=MiB,") + 17); + partlistlen += numparts * (strlen("size=MiB,") + 17); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen ++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[16]; + + if (!partitions_list) + return -1; + + /* + * README.gpt specifies starting with "partitions=" like so: + * strcpy(partitions_list, "partitions=uuid_disk="); + * but that breaks extract_val, which doesn't skip over 'partitions='. + */ + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + /* + * name is 32 chars long, per definition of disk_partition_t in part.h, + * plus one extra byte for NULL + */ + strncat(partitions_list, (const char *)curr->gpt_part_info.name, 33); + + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, 17); + + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, 17); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -223,8 +331,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -241,7 +352,6 @@ static int get_gpt_info(struct blk_desc *dev_desc) if (!valid_parts) { printf("** No valid partitions found **\n"); del_gpt_info(); - return -1; } return --valid_parts; } @@ -295,10 +405,13 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; val = extract_val(str, "uuid_disk"); + if (!val) { #ifdef CONFIG_RANDOM_UUID *str_disk_guid = malloc(UUID_STR_LEN + 1); @@ -312,6 +425,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (extract_env(val, &p)) p = val; *str_disk_guid = strdup(p); + free(val); /* Move s to first partition */ strsep(&s, ";"); @@ -524,6 +638,83 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return 0; }
+static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("Current partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel")) + strcpy((char *)curr->gpt_part_info.name, "candidate_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel")) + strcpy((char *)curr->gpt_part_info.name, "backup_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "backup_rootfs"); + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} + /** * do_gpt(): Perform GPT operations * @@ -567,6 +758,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, varname); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); + } else if (strcmp(argv[1], "flip") == 0) { + return do_flip_gpt_parts(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -591,6 +784,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " gpt verify mmc 0 $partitions\n" " read <interface> <dev>\n" " - read GPT into a data structure for manipulation\n" + " flip <interface> <dev>\n" + " - exchange 'primary' and 'backup' partition names in GPT" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n"

On Sat, May 20, 2017 at 07:27:55PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
[snip]
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 6 deletions(-)
There's some whitespace-only chunks, please drop. Also:
[snip]
+/* ONEMIB is 2**20 */ +#define ONEMIB 1024*1024
SZ_1MB from <linux/sizes.h>
+/*
- The number '33' comes from the '32' in the definition of disk_partition_t
- in include/part.h. That file has '37' rather than UUID_STR_LEN + 1, from
- include/uuid.h
- */
Lets update include/part.h in a new patch earlier in the series, possibly as part of addressing the problems I mention with the previous patch. Thanks!

alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it renames it failed’ and renames the ‘backup’ partition as ‘candidate’.
Logic: -- Partitions can go to ‘failed’ only from ‘candidate’ and only via u-boot. Partitions can go to ‘backup’ only from ‘primary’ and vice-versa, only via Linux. Partitions go to ‘candidate’ from ‘primary’ or ‘backup’ only via u-boot. Only system update software will rename 'failed' partitions.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 6 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 128c895..f2c32ae 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -21,6 +21,9 @@ #include <memalign.h> #include <linux/compat.h>
+/* ONEMIB is 2**20 */ +#define ONEMIB 1024*1024
'()' are missing around the expression! But anyway, there is no need to reinvent the wheel, since SZ_1M exists in include/linux/sizes.h.
static LIST_HEAD(disk_partitions);
/** @@ -114,7 +117,6 @@ static char *extract_val(const char *str, const char *key) break; } }
free(strcopy);
return new;
@@ -158,6 +160,7 @@ static void del_gpt_info(void) { struct list_head *pos = &disk_partitions; struct disk_part *curr;
- while (!list_empty(pos)) { curr = list_entry(pos->next, struct disk_part, list); list_del(pos->next);
@@ -165,6 +168,11 @@ static void del_gpt_info(void) } }
+/*
- The number '33' comes from the '32' in the definition of disk_partition_t
- in include/part.h. That file has '37' rather than UUID_STR_LEN + 1, from
- include/uuid.h
- */
static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) { struct disk_part *newpart; @@ -191,16 +199,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize,
unsigned long blksize)
+{
- unsigned long long partbytes;
- unsigned long partmegabytes;
- partbytes = partsize * blksize;
- partmegabytes = lldiv(partbytes, ONEMIB);
- snprintf(sizestr, 16, "%luMiB", partmegabytes);
+}
static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list);
prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type,printf("Start %s, size %s\n", partstartstr, partsizestr);
@@ -212,6 +237,89 @@ static void print_gpt_info(void) } }
+static int calc_parts_list_len(int numparts) +{
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
*/
- int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
- /* for the comma */
- partlistlen++;
- /* per-partition additions; numparts starts at 1, so this should be correct */
- partlistlen += numparts * (strlen("name=,") + 33);
- /* 17 because partstr below is 16 chars */
- partlistlen += numparts * (strlen("start=MiB,") + 17);
- partlistlen += numparts * (strlen("size=MiB,") + 17);
- partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
- /* for the terminating null */
- partlistlen ++;
partlistlen++;
- debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
numparts);
- return partlistlen;
+}
+/*
- create the string that upstream 'gpt write' command will accept as an
- argument
- From doc/README.gpt, Format of partitions layout:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
- are optional if CONFIG_RANDOM_UUID is enabled.
- */
+static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{
- struct list_head *pos;
- struct disk_part *curr;
- char partstr[16];
- if (!partitions_list)
return -1;
- /*
* README.gpt specifies starting with "partitions=" like so:
* strcpy(partitions_list, "partitions=uuid_disk=");
* but that breaks extract_val, which doesn't skip over 'partitions='.
*/
- strcpy(partitions_list, "uuid_disk=");
- strncat(partitions_list, guid, UUID_STR_LEN + 1);
- strcat(partitions_list, ";");
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
strcat(partitions_list, "name=");
/*
* name is 32 chars long, per definition of disk_partition_t in part.h,
* plus one extra byte for NULL
*/
strncat(partitions_list, (const char *)curr->gpt_part_info.name, 33);
unnecessary cast. The same holds for all the other (const char *) casts in this patch.
strcat(partitions_list, ",start=");
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
strncat(partitions_list, partstr, 17);
strcat(partitions_list, ",size=");
/* lbaint_t is unsigned long, per include/ide.h */
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
/* one extra byte for NULL */
strncat(partitions_list, partstr, 17);
strcat(partitions_list, ",uuid=");
strncat(partitions_list, (const char *)curr->gpt_part_info.uuid,
UUID_STR_LEN + 1);
strcat(partitions_list, ";");
- }
- return 0;
+}
/*
- read partition info into disk_partitions list where
- it can be printed or modified
@@ -223,8 +331,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
/*
* Always re-read partition info from device, in case
* it has changed
*/
INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info);
@@ -241,7 +352,6 @@ static int get_gpt_info(struct blk_desc *dev_desc) if (!valid_parts) { printf("** No valid partitions found **\n"); del_gpt_info();
} return --valid_parts;return -1;
} @@ -295,10 +405,13 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part);
if (str == NULL)
return -ENOMEM;
/* extract disk guid */ s = str; val = extract_val(str, "uuid_disk");
if (!val) {
#ifdef CONFIG_RANDOM_UUID *str_disk_guid = malloc(UUID_STR_LEN + 1); @@ -312,6 +425,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (extract_env(val, &p)) p = val; *str_disk_guid = strdup(p);
- free(val); /* Move s to first partition */ strsep(&s, ";");
@@ -524,6 +638,83 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return 0; }
+static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{
- struct list_head *pos;
- struct disk_part *curr;
- disk_partition_t *new_partitions = NULL;
- char disk_guid[UUID_STR_LEN + 1];
- char *partitions_list, *str_disk_guid;
- u8 part_count = 0;
- int partlistlen, ret, numparts = 0;
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return ret;
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
- printf("Current partition table with %d partitions is:\n", numparts);
- print_gpt_info();
- partlistlen = calc_parts_list_len(numparts);
- partitions_list = (char *)malloc(partlistlen);
Useless cast.
Lothar Waßmann

On Sat, May 20, 2017 at 07:27:52PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
One way for userspace and the bootloader to exchange information about dynamic image selection is via the storage device partition table, as described at
https://source.android.com/devices/tech/ota/ab_updates
The scheme described there relies on setting partitions' "boot" flag. When no partition on a device is bootable since the kernel and U-Boot are stored elsewhere, the name field in the GPT partition table offers another logical place to store information. These patches allow users to easily modify GPT partition names via bootscripts that can select different images based on a boot-failure counter, or when userspace installs a software update.
These patches have been tested on a TI DRA7xx-based SOM with U-Boot 2015.07. The storage device is an eMMC.
Alison Chaiken (3): GPT: add accessor function for disk GUID GPT: read partition table from device into a data structure rename GPT partitions to detect boot failure
cmd/gpt.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- disk/part_efi.c | 31 ++++++ include/part.h | 24 ++++ 3 files changed, 392 insertions(+), 2 deletions(-)
Interesting. Adding Lukasz for comments as well. I was thinking perhaps the final patch in the series might want to be guarded with some Kconfig option as it's rather specific to this update mechanism and probably causes a noticeable size increase due to the strings. Thanks!

From: Alison Chaiken alison@she-devel.com
One way for userspace and the bootloader to exchange information about dynamic image selection is via the storage device partition table, as described at
https://source.android.com/devices/tech/ota/ab_updates
The scheme described there relies on setting partitions' "boot" flag. When no partition on a device is bootable since the kernel and U-Boot are stored elsewhere, the name field in the GPT partition table offers another logical place to store information. These patches allow users to easily modify GPT partition names via bootscripts that can select different images based on a boot-failure counter, or when userspace installs a software update.
These patches have been tested on a TI DRA7xx-based SOM with U-Boot 2015.07. The storage device is an eMMC.
Significant changes since v1: -- Put the gpt_flip() function and auxiliary ones inside a new CONFIG_CMD_GPT_FLIP option that depends on CMD_GPT. -- Replace intentional overwriting of name and type string arrays with memset() instead. -- Move part.h changes earlier in the patchset. -- Add a few lines to README.gpt about the new gpt subcommands.
Added a few simple patches to do the following: -- Replace remaining occurrences of '37' with UUID_STR_LEN+1; -- Introduce new macros to get rid of the similar '32'; -- fix a smaller error in doc/README.gpt.
There are also some fixups of whitespace and formatting errors (plus usual inevitable addition of new ones).
To do in future: -- Add support for preserving the type flag for partitions. The u-boot version on which this patchset is based did not have this feature, and it's easy to add, but I need to figure how to test it first.
Alison Chaiken (6): EFI: replace number with UUID_STR_LEN macro disk_partition: introduce macros for description string lengths GPT: add accessor function for disk GUID GPT: read partition table from device into a data structure rename GPT partitions to detect boot failure GPT: fix error in partitions string doc
cmd/Kconfig | 7 ++ cmd/gpt.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- disk/part_efi.c | 33 +++++- doc/README.gpt | 24 ++++- include/part.h | 35 +++++- 5 files changed, 410 insertions(+), 11 deletions(-)

From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- disk/part_efi.c | 2 +- include/part.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/disk/part_efi.c b/disk/part_efi.c index 1b7ba27..20d33ef 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -183,7 +183,7 @@ void part_print_efi(struct blk_desc *dev_desc) ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); gpt_entry *gpt_pte = NULL; int i = 0; - char uuid[37]; + char uuid[UUID_STR_LEN + 1]; unsigned char *uuid_bin;
/* This function validates AND fills in the GPT header and PTE */ diff --git a/include/part.h b/include/part.h index 83bce05..6ace09f 100644 --- a/include/part.h +++ b/include/part.h @@ -9,6 +9,7 @@
#include <blk.h> #include <ide.h> +#include <uuid.h>
struct block_drvr { char *name; @@ -54,10 +55,10 @@ typedef struct disk_partition { uchar type[32]; /* string type description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS) - char uuid[37]; /* filesystem UUID as string, if exists */ + char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */ #endif #ifdef CONFIG_PARTITION_TYPE_GUID - char type_guid[37]; /* type GUID as string, if exists */ + char type_guid[UUID_STR_LEN + 1]; /* type GUID as string, if exists */ #endif #ifdef CONFIG_DOS_PARTITION uchar sys_ind; /* partition type */

On Mon, May 29, 2017 at 09:49:28AM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Reviewed-by: Tom Rini trini@konsulko.com

Hi Alison,
From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Acked-by: Lukasz Majewski lukma@denx.de
disk/part_efi.c | 2 +- include/part.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/disk/part_efi.c b/disk/part_efi.c index 1b7ba27..20d33ef 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -183,7 +183,7 @@ void part_print_efi(struct blk_desc *dev_desc) ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); gpt_entry *gpt_pte = NULL; int i = 0;
- char uuid[37];
char uuid[UUID_STR_LEN + 1]; unsigned char *uuid_bin;
/* This function validates AND fills in the GPT header and
PTE */ diff --git a/include/part.h b/include/part.h index 83bce05..6ace09f 100644 --- a/include/part.h +++ b/include/part.h @@ -9,6 +9,7 @@
#include <blk.h> #include <ide.h> +#include <uuid.h>
struct block_drvr { char *name; @@ -54,10 +55,10 @@ typedef struct disk_partition { uchar type[32]; /* string type description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
- char uuid[37]; /* filesystem UUID as string,
if exists */
- char uuid[UUID_STR_LEN + 1]; /* filesystem
UUID as string, if exists */ #endif #ifdef CONFIG_PARTITION_TYPE_GUID
- char type_guid[37]; /* type GUID as string, if
exists */
- char type_guid[UUID_STR_LEN + 1]; /* type GUID
as string, if exists */ #endif #ifdef CONFIG_DOS_PARTITION uchar sys_ind; /* partition type */
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- include/part.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/part.h b/include/part.h index 6ace09f..87b1111 100644 --- a/include/part.h +++ b/include/part.h @@ -47,12 +47,15 @@ struct block_drvr { #define DEV_TYPE_CDROM 0x05 /* CD-ROM */ #define DEV_TYPE_OPDISK 0x07 /* optical disk */
+#define PART_NAME_LEN 32 +#define PART_TYPE_LEN 32 + typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ lbaint_t size; /* number of blocks in partition */ ulong blksz; /* block size in bytes */ - uchar name[32]; /* partition name */ - uchar type[32]; /* string type description */ + uchar name[PART_NAME_LEN]; /* partition name */ + uchar type[PART_TYPE_LEN]; /* string type description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS) char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */

Hi Alison,
From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com
include/part.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/part.h b/include/part.h index 6ace09f..87b1111 100644 --- a/include/part.h +++ b/include/part.h @@ -47,12 +47,15 @@ struct block_drvr { #define DEV_TYPE_CDROM 0x05 /* CD-ROM */ #define DEV_TYPE_OPDISK 0x07 /* optical disk */
+#define PART_NAME_LEN 32 +#define PART_TYPE_LEN 32
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ lbaint_t size; /* number of blocks in partition */ ulong blksz; /* block size in bytes */
- uchar name[32]; /* partition
name */
- uchar type[32]; /* string type
description */
- uchar name[PART_NAME_LEN]; /* partition
name */
- uchar type[PART_TYPE_LEN]; /* string type
description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS) char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */
Acked-by: Lukasz Majewski lukma@denx.de
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

On Mon, May 29, 2017 at 09:49:29AM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Reviewed-by: Tom Rini trini@konsulko.com

From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 30 +++++++++++++++++++++++++++++- disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..3b7d929 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return 1; + + if (namestr) + setenv(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return 0; +} + /** * do_gpt(): Perform GPT operations * @@ -412,7 +429,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = CMD_RET_SUCCESS; int dev = 0; - char *ep; + char *ep, *varname = NULL; struct blk_desc *blk_dev_desc = NULL;
if (argc < 4 || argc > 5) @@ -436,6 +453,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if (strcmp(argv[1], "guid") == 0) { + if (argc == 5) + strcpy(varname, argv[4]); + return do_disk_guid(blk_dev_desc, varname); } else { return CMD_RET_USAGE; } @@ -458,4 +479,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " guid <interface> <dev>\n" + " - print disk GUID\n" + " guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..ff9f408 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) * Public Functions (include/part.h) */
+/* + * UUID is displayed as 32 hexadecimal digits, in 5 groups, + * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters + */ +int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); + gpt_entry *gpt_pte = NULL; + unsigned char *guid_bin; + + /* This function validates AND fills in the GPT header and PTE */ + if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + if (is_gpt_valid(dev_desc, (dev_desc->lba - 1), + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + return -1; + } else { + printf("%s: *** Using Backup GPT ***\n", + __func__); + } + } + + guid_bin = (unsigned char *)(gpt_head->disk_guid.b); + uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID); + + return 0; +} + void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..c0779a4 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to - the generated UUID. + the generated UUID. The 'gpt guid' command reads the current value of the + uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT partition bootable (set attribute flags "Legacy BIOS bootable"). diff --git a/include/part.h b/include/part.h index 87b1111..16c4a46 100644 --- a/include/part.h +++ b/include/part.h @@ -371,6 +371,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte); + + +/** + * get_disk_guid() - Function to read the GUID string from a device's GPT + * + * This function reads the GUID string from a block device whose descriptor + * is provided. + * + * @param dev_desc - block device descriptor + * @param guid - pre-allocated string in which to return the GUID + * + * @return - '0' on success, otherwise error + */ +int get_disk_guid(struct blk_desc *dev_desc, char *guid); + #endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 30 +++++++++++++++++++++++++++++- disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..3b7d929 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{
- int ret;
- char disk_guid[UUID_STR_LEN + 1];
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return 1;
Since you are calling this function from a CMD handler and passing its return value as the CMD handlers exit code this should be: return CMD_RET_FAILURE;
- if (namestr)
setenv(namestr, disk_guid);
- else
printf("%s\n", disk_guid);
- return 0;
... and this: return CMD_RET_SUCCESS;
/**
- do_gpt(): Perform GPT operations
@@ -412,7 +429,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = CMD_RET_SUCCESS; int dev = 0;
- char *ep;
char *ep, *varname = NULL; struct blk_desc *blk_dev_desc = NULL;
if (argc < 4 || argc > 5)
@@ -436,6 +453,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: ");
- } else if (strcmp(argv[1], "guid") == 0) {
if (argc == 5)
strcpy(varname, argv[4]);
You didn't allocate any memory for varname and copy the argument string to a NULL pointer! Maybe use strdup() and free varname after use?
} else { return CMD_RET_USAGE; }return do_disk_guid(blk_dev_desc, varname);
@@ -458,4 +479,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " guid <interface> <dev>\n"
- " - print disk GUID\n"
- " guid <interface> <dev> <varname>\n"
- " - set environment variable to disk GUID\n"
- " Example usage:\n"
- " gpt guid mmc 0\n"
- " gpt guid mmc 0 varname\n"
); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..ff9f408 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h)
- Public Functions (include/part.h)
*/
+/*
- UUID is displayed as 32 hexadecimal digits, in 5 groups,
- separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters
- */
+int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{
- ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz);
- gpt_entry *gpt_pte = NULL;
- unsigned char *guid_bin;
- /* This function validates AND fills in the GPT header and PTE */
- if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
gpt_head, &gpt_pte) != 1) {
printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
if (is_gpt_valid(dev_desc, (dev_desc->lba - 1),
useless '()'
gpt_head, &gpt_pte) != 1) {
printf("%s: *** ERROR: Invalid Backup GPT ***\n",
__func__);
return -1;
return -EINVAL;?
} else {
printf("%s: *** Using Backup GPT ***\n",
__func__);
}
- }
- guid_bin = (unsigned char *)(gpt_head->disk_guid.b);
disk_guid.b is of type u8 which is the same as unsigned char. No need for a type cast here (and the () around gpt_head->... are unnecessary).
- uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID);
- return 0;
+}
void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..c0779a4 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to
- the generated UUID.
the generated UUID. The 'gpt guid' command reads the current value of the
uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT partition bootable (set attribute flags "Legacy BIOS bootable").
diff --git a/include/part.h b/include/part.h index 87b1111..16c4a46 100644 --- a/include/part.h +++ b/include/part.h @@ -371,6 +371,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte);
+/**
- get_disk_guid() - Function to read the GUID string from a device's GPT
- This function reads the GUID string from a block device whose descriptor
- is provided.
- @param dev_desc - block device descriptor
- @param guid - pre-allocated string in which to return the GUID
- @return - '0' on success, otherwise error
- */
+int get_disk_guid(struct blk_desc *dev_desc, char *guid);
#endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

From: Alison Chaiken alison@peloton-tech.com
One way for userspace and the bootloader to exchange information about dynamic image selection is via the storage device partition table, as described at
https://source.android.com/devices/tech/ota/ab_updates
The scheme described there relies on setting partitions' "boot" flag. When no partition on a device is bootable since the kernel and U-Boot are stored elsewhere, the name field in the GPT partition table offers another logical place to store information. These patches allow users to easily modify GPT partition names via bootscripts that can select different images based on a boot-failure counter, or when userspace installs a software update.
These patches have been (re)tested on a TI DRA7xx-based SOM with U-Boot 2015.07. The storage device is an eMMC.
Significant changes since v2: -- Got rid of the need to allocate memory for the GUID string in do_gpt(); -- Fixed the problems with string NULL termination in allocate_disk_part(); -- Removed duplicate definition of MAX_SEARCH_PARTITIONS from disk/part.c and increased the value in include/part.h to 64; -- Improved the commit message for "rename GPT partitions to detect boot failure" to better describe the version of the patch I submitted; -- Fixed numerous small problems with function return values.
Significant changes since v1: -- Put the gpt_flip() function and auxiliary ones inside a new CONFIG_CMD_GPT_FLIP option that depends on CMD_GPT. -- Replace intentional overwriting of name and type string arrays with memset() instead. -- Move part.h changes earlier in the patchset. -- Add a few lines to README.gpt about the new gpt subcommands.
Added a few simple patches to do the following: -- Replace remaining occurrences of '37' with UUID_STR_LEN+1; -- Introduce new macros to get rid of the similar '32'; -- fix a smaller error in doc/README.gpt.
There are also some fixups of whitespace and formatting errors (plus usual inevitable addition of new ones).
To do in future: -- Add support for preserving the type flag for partitions. The u-boot version on which this patchset is based did not have this feature, and it's easy to add, but I need to figure how to test it first.
-- Add tests for the new gpt commands to the sandbox.
Alison Chaiken (5): GPT: add accessor function for disk GUID partitions: increase MAX_SEARCH_PARTITIONS and move to part.h GPT: read partition table from device into a data structure rename GPT partitions to detect boot failure GPT: fix error in partitions string doc
cmd/Kconfig | 7 ++ cmd/gpt.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ disk/part.c | 1 - disk/part_efi.c | 31 ++++++ doc/README.gpt | 24 ++++- include/part.h | 23 ++++ 6 files changed, 398 insertions(+), 6 deletions(-)

From: Alison Chaiken alison@peloton-tech.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 26 ++++++++++++++++++++++++++ disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..4d00a35 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return CMD_RET_FAILURE; + + if (namestr) + setenv(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return ret; +} + /** * do_gpt(): Perform GPT operations * @@ -436,6 +453,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if (strcmp(argv[1], "guid") == 0) { + return do_disk_guid(blk_dev_desc, argv[4]); } else { return CMD_RET_USAGE; } @@ -458,4 +477,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " guid <interface> <dev>\n" + " - print disk GUID\n" + " guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..71c3cb3 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) * Public Functions (include/part.h) */
+/* + * UUID is displayed as 32 hexadecimal digits, in 5 groups, + * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters + */ +int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); + gpt_entry *gpt_pte = NULL; + unsigned char *guid_bin; + + /* This function validates AND fills in the GPT header and PTE */ + if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + if (is_gpt_valid(dev_desc, dev_desc->lba - 1, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + return -EINVAL; + } else { + printf("%s: *** Using Backup GPT ***\n", + __func__); + } + } + + guid_bin = gpt_head->disk_guid.b; + uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID); + + return 0; +} + void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..c0779a4 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to - the generated UUID. + the generated UUID. The 'gpt guid' command reads the current value of the + uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT partition bootable (set attribute flags "Legacy BIOS bootable"). diff --git a/include/part.h b/include/part.h index 87b1111..16c4a46 100644 --- a/include/part.h +++ b/include/part.h @@ -371,6 +371,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte); + + +/** + * get_disk_guid() - Function to read the GUID string from a device's GPT + * + * This function reads the GUID string from a block device whose descriptor + * is provided. + * + * @param dev_desc - block device descriptor + * @param guid - pre-allocated string in which to return the GUID + * + * @return - '0' on success, otherwise error + */ +int get_disk_guid(struct blk_desc *dev_desc, char *guid); + #endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

Hi,
On Fri, 2 Jun 2017 19:22:30 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 26 ++++++++++++++++++++++++++ disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..4d00a35 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{
- int ret;
- char disk_guid[UUID_STR_LEN + 1];
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return CMD_RET_FAILURE;
- if (namestr)
setenv(namestr, disk_guid);
- else
printf("%s\n", disk_guid);
- return ret;
For consistency this should be: return CMD_RET_SUCCESS; but see below.
/**
- do_gpt(): Perform GPT operations
@@ -436,6 +453,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: ");
- } else if (strcmp(argv[1], "guid") == 0) {
} else { return CMD_RET_USAGE; }return do_disk_guid(blk_dev_desc, argv[4]);
IMO you should use the existing error handling infrastructure lateron in this file and just assign the return value of the subcommand handlers to 'ret' and let the existing code sort out the return code of this function (just like the gpt_verify() call above does it).
Lothar Waßmann

From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Changes since v4: -- Made do_disk_guid() return 0 upon success so that now, in the sandbox, u-boot prints a "success!" exit message.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 26 ++++++++++++++++++++++++++ disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..65fb80b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return CMD_RET_FAILURE; + + if (namestr) + setenv(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return ret; +} + /** * do_gpt(): Perform GPT operations * @@ -436,6 +453,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if (strcmp(argv[1], "guid") == 0) { + ret = do_disk_guid(blk_dev_desc, argv[4]); } else { return CMD_RET_USAGE; } @@ -458,4 +477,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " guid <interface> <dev>\n" + " - print disk GUID\n" + " guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..71c3cb3 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) * Public Functions (include/part.h) */
+/* + * UUID is displayed as 32 hexadecimal digits, in 5 groups, + * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters + */ +int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); + gpt_entry *gpt_pte = NULL; + unsigned char *guid_bin; + + /* This function validates AND fills in the GPT header and PTE */ + if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + if (is_gpt_valid(dev_desc, dev_desc->lba - 1, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + return -EINVAL; + } else { + printf("%s: *** Using Backup GPT ***\n", + __func__); + } + } + + guid_bin = gpt_head->disk_guid.b; + uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID); + + return 0; +} + void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..c0779a4 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to - the generated UUID. + the generated UUID. The 'gpt guid' command reads the current value of the + uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT partition bootable (set attribute flags "Legacy BIOS bootable"). diff --git a/include/part.h b/include/part.h index 87b1111..16c4a46 100644 --- a/include/part.h +++ b/include/part.h @@ -371,6 +371,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte); + + +/** + * get_disk_guid() - Function to read the GUID string from a device's GPT + * + * This function reads the GUID string from a block device whose descriptor + * is provided. + * + * @param dev_desc - block device descriptor + * @param guid - pre-allocated string in which to return the GUID + * + * @return - '0' on success, otherwise error + */ +int get_disk_guid(struct blk_desc *dev_desc, char *guid); + #endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

On Fri, Jun 09, 2017 at 10:27:40PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Changes since v4: -- Made do_disk_guid() return 0 upon success so that now, in the sandbox, u-boot prints a "success!" exit message.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Reviewed-by: Tom Rini trini@konsulko.com

From: Alison Chaiken alison@peloton-tech.com
Move MAX_SEARCH_PARTITIONS to part.h so that functions in cmd directory can find it. At the same time, increase the value to 64 since some operating systems use many, and the resources consumed by a larger value are minimal.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- disk/part.c | 1 - include/part.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/disk/part.c b/disk/part.c index 491b02d..e640a55 100644 --- a/disk/part.c +++ b/disk/part.c @@ -388,7 +388,6 @@ cleanup:
#define PART_UNSPECIFIED -2 #define PART_AUTO -1 -#define MAX_SEARCH_PARTITIONS 16 int blk_get_device_part_str(const char *ifname, const char *dev_part_str, struct blk_desc **dev_desc, disk_partition_t *info, int allow_whole_dev) diff --git a/include/part.h b/include/part.h index 16c4a46..c41aa6a 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 64
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */

Hi Alison,
From: Alison Chaiken alison@peloton-tech.com
Move MAX_SEARCH_PARTITIONS to part.h so that functions in cmd directory can find it. At the same time, increase the value to 64 since some operating systems use many, and the resources consumed by a larger value are minimal.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
disk/part.c | 1 - include/part.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/disk/part.c b/disk/part.c index 491b02d..e640a55 100644 --- a/disk/part.c +++ b/disk/part.c @@ -388,7 +388,6 @@ cleanup:
#define PART_UNSPECIFIED -2 #define PART_AUTO -1 -#define MAX_SEARCH_PARTITIONS 16 int blk_get_device_part_str(const char *ifname, const char *dev_part_str, struct blk_desc **dev_desc, disk_partition_t *info, int allow_whole_dev) diff --git a/include/part.h b/include/part.h index 16c4a46..c41aa6a 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 64
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */
Reviewed-by: Lukasz Majewski lukma@denx.de
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 119 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 4d00a35..5c2651f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = (struct disk_part *)malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 1, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part) && valid_parts >= 2) + return -ENODEV; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + del_gpt_info(); + return -ENODEV; + } + return --valid_parts; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + } + return ret; +} + + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +563,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { return do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + return do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +587,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

Hi,
On Fri, 2 Jun 2017 19:22:32 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 119 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 4d00a35..5c2651f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h>
+static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
curr = list_entry(pos->next, struct disk_part, list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = (struct disk_part *)malloc(sizeof(*newpart));
- if (!newpart)
return ERR_PTR(-ENOMEM);
- memset(newpart, '\0', sizeof(newpart));
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, PART_NAME_LEN);
- newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
- strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, PART_TYPE_LEN);
- newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
UUID_STR_LEN);
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as U-Boot does */
- int valid_parts = 1, p, ret;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info, valid_parts);
if (IS_ERR(new_disk_part) && valid_parts >= 2)
return -ENODEV;
del_gpt_info()? This calls for a common error exit which does the cleanup.
list_add_tail(&new_disk_part->list, &disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -ENODEV;
- }
- return --valid_parts;
+}
Lothar Waßmann

From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Changes since v4: -- Made do_get_gpt_info() return 0 upon success so that now, in the sandbox, u-boot prints a "success!" exit message. -- Created a single exit point for get_gpt_info(), with the result that after an error, del_gpt_info() always removes the list.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 127 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b..669031f8 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,119 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = (struct disk_part *)malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 1, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part)) + goto out; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + goto out; + } + return --valid_parts; + out: + if (valid_parts >= 2) + del_gpt_info(); + return -ENODEV; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + return 0; + } + return ret; +} + + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +571,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { ret = do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + ret = do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +595,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

On Fri, Jun 09, 2017 at 10:30:39PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Changes since v4: -- Made do_get_gpt_info() return 0 upon success so that now, in the sandbox, u-boot prints a "success!" exit message. -- Created a single exit point for get_gpt_info(), with the result that after an error, del_gpt_info() always removes the list.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Reviewed-by: Tom Rini trini@konsulko.com

Hi,
On Fri, 2 Jun 2017 19:22:32 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 119 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 4d00a35..5c2651f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h>
+static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
curr = list_entry(pos->next, struct disk_part, list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = (struct disk_part *)malloc(sizeof(*newpart));
- if (!newpart)
return ERR_PTR(-ENOMEM);
- memset(newpart, '\0', sizeof(newpart));
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, PART_NAME_LEN);
- newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
- strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, PART_TYPE_LEN);
- newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
UUID_STR_LEN);
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as U-Boot does */
- int valid_parts = 1, p, ret;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info, valid_parts);
if (IS_ERR(new_disk_part) && valid_parts >= 2)
return -ENODEV;
list_add_tail(&new_disk_part->list, &disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -ENODEV;
- }
- return --valid_parts;
+}
+/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{
- int ret;
- ret = get_gpt_info(dev_desc);
- if (ret > 0) {
print_gpt_info();
del_gpt_info();
- }
- return ret;
+}
/**
- set_gpt_info(): Fill partition information from string
function allocates memory, remember to free!
@@ -455,6 +563,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { return do_disk_guid(blk_dev_desc, argv[4]);
- } else if (strcmp(argv[1], "read") == 0) {
return do_get_gpt_info(blk_dev_desc);
See my comment to your patch 1/5
} else { return CMD_RET_USAGE; }
@@ -477,6 +587,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " read <interface> <dev>\n"
- " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n"
diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part {
- int partnum;
- disk_partition_t gpt_part_info;
- struct list_head list;
+};
/* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it tries to boot the ‘backup’ partition.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 7 ++ cmd/gpt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 6f75b86..8b925e5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -593,6 +593,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP + bool "GPT flip-partitions command" + depends on CMD_GPT + help + Enables the 'gpt' command to write modified GPT partition + tables via the 'gpt flip' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 5c2651f..f6968de 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -190,16 +191,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -211,6 +229,85 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{ + /* + * prefatory string: + * doc/README.GPT, suggests that + * int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk="); + * is correct, but extract_val() expects "uuid_disk" first. + */ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* 17 because partstr in create_gpt_partitions_list() is 16 chars */ + partlistlen += numparts * (strlen("start=MiB,") + 17); + partlistlen += numparts * (strlen("size=MiB,") + 17); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + /* + * README.gpt specifies starting with "partitions=" like so: + * strcpy(partitions_list, "partitions=uuid_disk="); + * but that breaks extract_val, which doesn't skip over 'partitions='. + */ + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -222,8 +319,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -294,6 +394,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -523,6 +625,86 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("Current partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel")) + strcpy((char *)curr->gpt_part_info.name, "candidate_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel")) + strcpy((char *)curr->gpt_part_info.name, "backup_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "backup_rootfs"); + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -565,6 +747,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP + } else if (strcmp(argv[1], "flip") == 0) { + return do_flip_gpt_parts(blk_dev_desc); +#endif } else { return CMD_RET_USAGE; } @@ -596,4 +782,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_FLIP + "gpt partition-flip command\n" + "gpt flip <interface> <dev>\n" + " - exchange device's 'primary' and 'backup' partition names\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index c0779a4..e29b188 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,19 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read' and 'gpt flip' commands facilitate programmatic renaming of +partitions from bootscripts by generating and modifying the partitions +layout string. The code in gpt_flip() illustrates the case of +swapping 'primary' and 'backup' partitions via: + +U-BOOT> gpt flip mmc 0 + +Choose different partition names by modifying these strings in gpt.c.
Partition type GUID: ====================

Hi,
On Fri, 2 Jun 2017 19:22:33 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it tries to boot the ‘backup’ partition.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/Kconfig | 7 ++ cmd/gpt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 6f75b86..8b925e5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -593,6 +593,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP
- bool "GPT flip-partitions command"
- depends on CMD_GPT
- help
Enables the 'gpt' command to write modified GPT partition
tables via the 'gpt flip' command.
config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 5c2651f..f6968de 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -190,16 +191,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize,
unsigned long blksize)
+{
- unsigned long long partbytes;
- unsigned long partmegabytes;
- partbytes = partsize * blksize;
- partmegabytes = lldiv(partbytes, SZ_1M);
- snprintf(sizestr, 16, "%luMiB", partmegabytes);
+}
static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list);
prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type,printf("Start %s, size %s\n", partstartstr, partsizestr);
@@ -211,6 +229,85 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
*/
- int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
- /* for the comma */
- partlistlen++;
- /* per-partition additions; numparts starts at 1, so this should be correct */
- partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
- /* 17 because partstr in create_gpt_partitions_list() is 16 chars */
NO! The size of partstr in create_gpt_partitions_list() is: | char partstr[PART_NAME_LEN + 1]; which happens to be defined as: --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 ^^ #define PART_TYPE_LEN 32
- partlistlen += numparts * (strlen("start=MiB,") + 17);
- partlistlen += numparts * (strlen("size=MiB,") + 17);
Never use magic numbers in code, but use appropriate #define's!
@@ -523,6 +625,86 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{
- struct list_head *pos;
- struct disk_part *curr;
- disk_partition_t *new_partitions = NULL;
- char disk_guid[UUID_STR_LEN + 1];
- char *partitions_list, *str_disk_guid;
- u8 part_count = 0;
- int partlistlen, ret, numparts = 0;
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return ret;
AGAIN: You are passing the return value of this function back to the caller of the CMD handler do_gpt() which expects a return value of CMD_RET_FAILURE or CMD_RET_SUCCESS, not an arbitrary negative value.
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
- printf("Current partition table with %d partitions is:\n", numparts);
- print_gpt_info();
- partlistlen = calc_parts_list_len(numparts);
- partitions_list = (char *)malloc(partlistlen);
- memset(partitions_list, '\0', partlistlen);
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid,
partitions_list);
- if (ret < 0)
return ret;
- debug("OLD partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel"))
strcpy((char *)curr->gpt_part_info.name, "candidate_kernel");
if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel"))
strcpy((char *)curr->gpt_part_info.name, "backup_kernel");
if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs"))
strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs");
if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs"))
strcpy((char *)curr->gpt_part_info.name, "backup_rootfs");
- }
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list);
- if (ret < 0)
return ret;
- debug("NEW partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
- debug("Writing new partition table\n");
- ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
- if (ret < 0) {
printf("Writing new partition table failed\n");
return ret;
- }
- debug("Reading back new partition table\n");
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
- printf("new partition table with %d partitions is:\n", numparts);
- print_gpt_info();
- del_gpt_info();
- free(partitions_list);
- free(str_disk_guid);
- free(new_partitions);
- return ret;
+} +#endif
/**
- do_gpt(): Perform GPT operations
@@ -565,6 +747,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP
- } else if (strcmp(argv[1], "flip") == 0) {
return do_flip_gpt_parts(blk_dev_desc);
+#endif
See my comment to your patch 1/5.
Lothar Waßmann

From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it tries to boot the ‘backup’ partition.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v4: -- Changed the erroneous '17' values in calc_parts_list_len() to match the definition of struct disk_partition in part.h. -- Modified do_gpt() so that the 'gpt flip' command prints "success!" or "error!" on exit.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 7 +++ cmd/gpt.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 206 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 270cff6..880ca97 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -593,6 +593,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP + bool "GPT flip-partitions command" + depends on CMD_GPT + help + Enables the 'gpt' command to write modified GPT partition + tables via the 'gpt flip' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 669031f8..ad7c7ec 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +195,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +233,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +312,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -302,6 +391,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -531,6 +622,88 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("Current partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel")) + strcpy((char *)curr->gpt_part_info.name, "candidate_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel")) + strcpy((char *)curr->gpt_part_info.name, "backup_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "backup_rootfs"); + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -573,6 +746,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP + } else if (strcmp(argv[1], "flip") == 0) { + ret = do_flip_gpt_parts(blk_dev_desc); +#endif } else { return CMD_RET_USAGE; } @@ -604,4 +781,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_FLIP + "gpt partition-flip command\n" + "gpt flip <interface> <dev>\n" + " - exchange device's 'primary' and 'backup' partition names\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index c0779a4..e29b188 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,19 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read' and 'gpt flip' commands facilitate programmatic renaming of +partitions from bootscripts by generating and modifying the partitions +layout string. The code in gpt_flip() illustrates the case of +swapping 'primary' and 'backup' partitions via: + +U-BOOT> gpt flip mmc 0 + +Choose different partition names by modifying these strings in gpt.c.
Partition type GUID: ====================

Dear Alison,
In message 1497072917-16614-1-git-send-email-alison@peloton-tech.com you wrote:
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it tries to boot the ‘backup’ partition.
I think you are talking about two parts here, and I strongly feel these should be kept separate.
The first part is support for renaming GPT partitions. The idea is clean, but should it not rather be kept general and let the user provide the new name as argument, i. e. implement a generic "gpt rename" command instead of a hard-wired "gpt flip"?
The second part is the flipping of the partition names. This is in my opinion policy, and should also not be hardwaired into this command, but left to the user. Maybe I do nmot want to fall back to the ‘backup’ partition after a single boot attempt, but instead try other things first, like booting another kernel or another device tree blob?
I feel this should be not so much hardwired, but leave more freedom for the user's creativity.
What do you think?
Best regards,
Wolfgang Denk

On Fri, Jun 9, 2017 at 11:51 PM, Wolfgang Denk wd@denx.de wrote:
I think you are talking about two parts here, and I strongly feel these should be kept separate.
The first part is support for renaming GPT partitions. The idea is clean, but should it not rather be kept general and let the user provide the new name as argument, i. e. implement a generic "gpt rename" command instead of a hard-wired "gpt flip"?
The second part is the flipping of the partition names. This is in my opinion policy, and should also not be hardwaired into this command, but left to the user. Maybe I do nmot want to fall back to the ‘backup’ partition after a single boot attempt, but instead try other things first, like booting another kernel or another device tree blob?
I feel this should be not so much hardwired, but leave more freedom for the user's creativity.
Lukasz has already made similar suggestions. In the following message is a new version which attempts to implement them.
Thanks to Lothar and Lukasz for all their help.
Best wishes, Alison Chaiken Peloton Technology

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode prints a warning if no matching partition names are found. If only one matching name of a provided pair is found, it renames the matching partitions to the new name.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v5: -- The former 'gpt flip' is now 'gpt swap', which takes the two arbitrary partition names it exchanges as command-line input. -- A new command, 'gpt rename', now allows renaming of single, specified partitions. -- Updated README.gpt to reflect the changes.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 8 ++ cmd/gpt.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 261 insertions(+), 6 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 412bf24..7b262de 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 669031f8..aa3245a 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,9 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> + +extern int atoi(const char *nptr);
static LIST_HEAD(disk_partitions);
@@ -194,16 +197,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +235,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +314,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -302,6 +393,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -531,6 +624,126 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, changed = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + changed++; + } + else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + changed++; + } + + } + if (changed == 0) { + printf("No matching partition names were found.\n"); + return ret; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + + partnum = atoi(name1); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -548,7 +761,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
- if (argc < 4 || argc > 5) + if (argc < 4 || argc > 6) return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -573,6 +786,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -604,4 +822,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

On Sat, Jun 10, 2017 at 04:33:37PM -0700, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode prints a warning if no matching partition names are found. If only one matching name of a provided pair is found, it renames the matching partitions to the new name.
So in the case where 'gpt swap partA partB' is used and partB doesn't exist, it's the same as 'gpt rename partA partB' ? That seems like it might surprise users and I think if we changed:
- if (!strcmp(subcomm, "swap")) {
if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
return -EINVAL;
}
list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
strcpy((char *)curr->gpt_part_info.name, name2);
changed++;
}
else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
strcpy((char *)curr->gpt_part_info.name, name1);
changed++;
}
}
if (changed == 0) {
printf("No matching partition names were found.\n");
return ret;
}
We could also test for if changed == 1 and return an error. I assume that a GPT with the same name used more than one time is already invalid, but we could be defensive here and test for != 2 instead. Thanks!

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 8 ++ cmd/gpt.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 258 insertions(+), 6 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 412bf24..7b262de 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 669031f8..b0c9623 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,9 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> + +extern int atoi(const char *nptr);
static LIST_HEAD(disk_partitions);
@@ -194,16 +197,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +235,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +314,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -302,6 +393,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -531,6 +624,123 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } + else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + return -EINVAL; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + partnum = atoi(name1); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -548,7 +758,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
- if (argc < 4 || argc > 5) + if (argc < 4 || argc > 6) return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -573,6 +783,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -604,4 +819,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

Dear Alison,
In message 1497137617-772-1-git-send-email-alison@peloton-tech.com you wrote:
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
Thanks.
One question: can multiple GPT partitions have the same name?
The 'swap' mode prints a warning if no matching partition names are found. If only one matching name of a provided pair is found, it renames the matching partitions to the new name.
I see a problem here...
- if (!strcmp(subcomm, "swap")) {
if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
return -EINVAL;
}
list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
strcpy((char *)curr->gpt_part_info.name, name2);
changed++;
}
else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
strcpy((char *)curr->gpt_part_info.name, name1);
changed++;
}
}
if (changed == 0) {
printf("No matching partition names were found.\n");
return ret;
}
You will never know if there really was a pair of names that was swapped. Just a single rename of name1->name2 _or_ of name2->name1 will make the user think everything was fine.
[Note: I'm in the process of relocating and will be offline for the next 2..3. days. Don't expect more comments from mee soon. Sorry...]
Best regards,
Wolfgang Denk

On Mon, Jun 12, 2017 at 12:45 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message 1497137617-772-1-git-send-email-alison@peloton-tech.com you wrote:
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
Thanks.
One question: can multiple GPT partitions have the same name?
The idea behind the 'swap' mode is that a storage device can have two sets of partitions, one set all named 'primary' and one set all named 'backup'. The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being interrupted.
The 'swap' mode prints a warning if no matching partition names are found. If only one matching name of a provided pair is found, it renames the matching partitions to the new name.
I see a problem here...
if (!strcmp(subcomm, "swap")) {
if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) >
PART_NAME_LEN)) {
printf("Names longer than %d characters are
truncated.\n", PART_NAME_LEN);
return -EINVAL;
}
list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name,
name1)) {
strcpy((char *)curr->gpt_part_info.name,
name2);
changed++;
}
else if (!strcmp((char *)curr->gpt_part_info.name,
name2)) {
strcpy((char *)curr->gpt_part_info.name,
name1);
changed++;
}
}
if (changed == 0) {
printf("No matching partition names were
found.\n");
return ret;
}
You will never know if there really was a pair of names that was swapped. Just a single rename of name1->name2 _or_ of name2->name1 will make the user think everything was fine.
Point taken. The last version I posted has two counters instead of just changed in order to address this problem.
[Note: I'm in the process of relocating and will be offline for the next 2..3. days. Don't expect more comments from mee soon. Sorry...] Best regards, Wolfgang Denk
One additional note: the last version I posted worked fine for the sandbox, but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone have a better idea to solve this problem?
Thanks, Alison Chaiken Peloton Technology

On Mon, Jun 12, 2017 at 07:24:17AM -0700, Alison Chaiken wrote:
On Mon, Jun 12, 2017 at 12:45 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message 1497137617-772-1-git-send-email-alison@peloton-tech.com you wrote:
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
Thanks.
One question: can multiple GPT partitions have the same name?
The idea behind the 'swap' mode is that a storage device can have two sets of partitions, one set all named 'primary' and one set all named 'backup'. The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being interrupted.
I'm a little confused here now then. Can you provide an example set of usage, in the cover letter for v8 of the series (see below) that makes it a bit clearer? Thanks!
The 'swap' mode prints a warning if no matching partition names are found. If only one matching name of a provided pair is found, it renames the matching partitions to the new name.
I see a problem here...
if (!strcmp(subcomm, "swap")) {
if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) >
PART_NAME_LEN)) {
printf("Names longer than %d characters are
truncated.\n", PART_NAME_LEN);
return -EINVAL;
}
list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name,
name1)) {
strcpy((char *)curr->gpt_part_info.name,
name2);
changed++;
}
else if (!strcmp((char *)curr->gpt_part_info.name,
name2)) {
strcpy((char *)curr->gpt_part_info.name,
name1);
changed++;
}
}
if (changed == 0) {
printf("No matching partition names were
found.\n");
return ret;
}
You will never know if there really was a pair of names that was swapped. Just a single rename of name1->name2 _or_ of name2->name1 will make the user think everything was fine.
Point taken. The last version I posted has two counters instead of just changed in order to address this problem.
[Note: I'm in the process of relocating and will be offline for the next 2..3. days. Don't expect more comments from mee soon. Sorry...] Best regards, Wolfgang Denk
One additional note: the last version I posted worked fine for the sandbox, but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone have a better idea to solve this problem?
Looking at the man page for atoi: The atoi() function converts the initial portion of the string pointed to by nptr to int. The behavior is the same as
strtol(nptr, NULL, 10);
So we should just re-work to use simple_strtol here. And since you brought it up on IRC, I suppose at this point a 'clean' posting of all 5 patches marked as 'v8' might make it easier to see what's what and in what order. Thanks again!

Dear Tom,
In message 20170612145646.GY10782@bill-the-cat you wrote:
Looking at the man page for atoi: The atoi() function converts the initial portion of the string pointed to by nptr to int. The behavior is the same as
strtol(nptr, NULL, 10);
So we should just re-work to use simple_strtol here. And since you brought it up on IRC, I suppose at this point a 'clean' posting of all 5 patches marked as 'v8' might make it easier to see what's what and in what order. Thanks again!
Hm... the claim that the behaviour is the _same_ is not strictly correct.
atoi() returns "int", while strtol() returns "long int"...
Anyway, you are right that atoi() is not being used anywhere in active U-Boot code.
Best regards,
Wolfgang Denk

From: Alison Chaiken alison@peloton-tech.com
Here is a resubmission of the 9-part patch series, the first 8 of which have already been marked 'applied', 'reviewed', or 'ack'ed' by Lukasz Majewski, Simon Glass or Tom Rini. I'm reposting them all as a set at Tom Rini's suggestion. Those earlier patches are listed here:
GPT: read partition table from device into a data structure Reviewed-by: Tom Rini, Jun 11 in 20170611133854.GS10782@bill-the-cat
GPT: add accessor function for disk GUID Reviewed-by: Tom Rini, Jun 11 in 20170611133849.GR10782@bill-the-cat
partitions: increase MAX_SEARCH_PARTITIONS and move to part.h Reviewed-by: Lukasz Majewski, Jun 3 in 20170603135259.6ba67e86@jawa
cmd gpt: test in sandbox Applied to u-boot-dm by Simon Glass, Jun 15 in CAPnjgZ0G6QADVFLC0mExQN3HXhLXbgE4neBEtmWAg0YZu-=aNg@mail.gmail.com
sandbox: README: fix partition command invocation Reviewed-by: Simon Glass, Jun 15, applied to u-boot-dm in CAPnjgZ1DTzPMJuDXuoQQjgrGrmTq73QqJ3qiegCS6416b1OQfQ@mail.gmail.com
GPT: fix error in partitions string doc Ack'ed by Lukasz Majewski, May 31, in 0170531132155.33a558e7@jawa
disk_partition: introduce macros for description string lengths Reviewed-by: Tom Rini, May 31 in 20170531135056.GZ10782@bill-the-cat
EFI: replace number with UUID_STR_LEN macro Acked-by: Lukasz Majewski, May 31 in 20170531093702.736b567f@jawa
Changes since last version to the last patch in the series:
-- Removed several casts in create_gpt_partitions_list() and do_rename_gpt_parts() as suggested by Lothar Waßmann. The cast of gpt_part_info.name to char* is needed to silence a compiler warning.
-- Substituted simple_strtol() for atoi() in do_gpt_rename_parts() as suggested by Tom Rini and Wolfgang Denk.
-- Fixed bug in do_rename_gpt_parts() where 0 was returned as a valid number of partitions with which to continue.
-- Added a comment to do_rename_gpt_parts() noting that the debug() string it optionally prints is useful as input to the pre-existing 'gpt write' and 'gpt verify' commands.
-- Changed some -1 return values to -ENODEV and -EINVAL as suggested by Tom Rini.
Changes to penultimate patch: -- Removed a cast in allocate_disk_part() as suggested by Lothar.
Remaining tasks planned for future patches:
-- Create tests for GPT functionality as suggested by Lukasz.
-- Improve support for CONFIG_RANDOM_UUID.
-- Preserve partition type info when rewriting GPT.
Alison Chaiken (9): EFI: replace number with UUID_STR_LEN macro disk_partition: introduce macros for description string lengths GPT: fix error in partitions string doc sandbox: README: fix partition command invocation cmd gpt: test in sandbox partitions: increase MAX_SEARCH_PARTITIONS and move to part.h GPT: add accessor function for disk GUID GPT: read partition table from device into a data structure GPT: provide commands to selectively rename partitions
board/sandbox/README.sandbox | 2 +- cmd/Kconfig | 8 + cmd/gpt.c | 376 ++++++++++++++++++++++++++++++++++++++++++- disk/part.c | 1 - disk/part_efi.c | 33 +++- doc/README.gpt | 39 ++++- include/part.h | 35 +++- 7 files changed, 481 insertions(+), 13 deletions(-)

From: Alison Chaiken alison@peloton-tech.com
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- disk/part_efi.c | 2 +- include/part.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/disk/part_efi.c b/disk/part_efi.c index 1b7ba27..20d33ef 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -183,7 +183,7 @@ void part_print_efi(struct blk_desc *dev_desc) ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); gpt_entry *gpt_pte = NULL; int i = 0; - char uuid[37]; + char uuid[UUID_STR_LEN + 1]; unsigned char *uuid_bin;
/* This function validates AND fills in the GPT header and PTE */ diff --git a/include/part.h b/include/part.h index 83bce05..6ace09f 100644 --- a/include/part.h +++ b/include/part.h @@ -9,6 +9,7 @@
#include <blk.h> #include <ide.h> +#include <uuid.h>
struct block_drvr { char *name; @@ -54,10 +55,10 @@ typedef struct disk_partition { uchar type[32]; /* string type description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS) - char uuid[37]; /* filesystem UUID as string, if exists */ + char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */ #endif #ifdef CONFIG_PARTITION_TYPE_GUID - char type_guid[37]; /* type GUID as string, if exists */ + char type_guid[UUID_STR_LEN + 1]; /* type GUID as string, if exists */ #endif #ifdef CONFIG_DOS_PARTITION uchar sys_ind; /* partition type */

On Sun, Jun 25, 2017 at 04:43:17PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- include/part.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/part.h b/include/part.h index 6ace09f..87b1111 100644 --- a/include/part.h +++ b/include/part.h @@ -47,12 +47,15 @@ struct block_drvr { #define DEV_TYPE_CDROM 0x05 /* CD-ROM */ #define DEV_TYPE_OPDISK 0x07 /* optical disk */
+#define PART_NAME_LEN 32 +#define PART_TYPE_LEN 32 + typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ lbaint_t size; /* number of blocks in partition */ ulong blksz; /* block size in bytes */ - uchar name[32]; /* partition name */ - uchar type[32]; /* string type description */ + uchar name[PART_NAME_LEN]; /* partition name */ + uchar type[PART_TYPE_LEN]; /* string type description */ int bootable; /* Active/Bootable flag is set */ #if CONFIG_IS_ENABLED(PARTITION_UUIDS) char uuid[UUID_STR_LEN + 1]; /* filesystem UUID as string, if exists */

On Sun, Jun 25, 2017 at 04:43:18PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt accordingly.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- doc/README.gpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..589295b 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to: 1. Define partition layout in the environment. Format of partitions layout: - "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...;" or - "partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name}, + "uuid_disk=${uuid_gpt_disk};name=${uboot_name}, size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition. @@ -219,7 +219,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;"
@@ -241,7 +241,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.

On Sun, Jun 25, 2017 at 04:43:19PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt accordingly.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
The instructions for creating a disk image that are presently in README.sandbox fail because sfdisk doesn't know about GPT.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- board/sandbox/README.sandbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox index 02d8ab3..9dc2eb0 100644 --- a/board/sandbox/README.sandbox +++ b/board/sandbox/README.sandbox @@ -333,7 +333,7 @@ the contents of the root directory on the second partion of the image A disk image can be created using the following commands:
$> truncate -s 1200M ./disk.raw -$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sfdisk ./disk.raw +$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sgdisk ./disk.raw $> lodev=`sudo losetup -P -f --show ./disk.raw` $> sudo mkfs.vfat -n EFI -v ${lodev}p1 $> sudo mkfs.ext4 -L ROOT -v ${lodev}p2

On Sun, Jun 25, 2017 at 04:43:20PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
The instructions for creating a disk image that are presently in README.sandbox fail because sfdisk doesn't know about GPT.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
Make minor changes to README.gpt and sandbox_defconfig to support testing of the gpt command's functionality in the sandbox.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- doc/README.gpt | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/doc/README.gpt b/doc/README.gpt index 589295b..0a3286f 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,16 @@ U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+The GPT functionality may be tested with the 'sandbox' board by +creating a disk image as described under 'Block Device Emulation' in +board/sandbox/README.sandbox: + +=>host bind 0 ./disk.raw +=> gpt read host 0 +[ . . . ] +=> gpt flip host 0 +[ . . . ] + Partition type GUID: ====================

On Sun, Jun 25, 2017 at 04:43:21PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Make minor changes to README.gpt and sandbox_defconfig to support testing of the gpt command's functionality in the sandbox.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
Move MAX_SEARCH_PARTITIONS to part.h so that functions in cmd directory can find it. At the same time, increase the value to 64 since some operating systems use many, and the resources consumed by a larger value are minimal.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- disk/part.c | 1 - include/part.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/disk/part.c b/disk/part.c index 491b02d..e640a55 100644 --- a/disk/part.c +++ b/disk/part.c @@ -388,7 +388,6 @@ cleanup:
#define PART_UNSPECIFIED -2 #define PART_AUTO -1 -#define MAX_SEARCH_PARTITIONS 16 int blk_get_device_part_str(const char *ifname, const char *dev_part_str, struct blk_desc **dev_desc, disk_partition_t *info, int allow_whole_dev) diff --git a/include/part.h b/include/part.h index 87b1111..22da604 100644 --- a/include/part.h +++ b/include/part.h @@ -49,6 +49,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 64
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */

On Sun, Jun 25, 2017 at 04:43:22PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Move MAX_SEARCH_PARTITIONS to part.h so that functions in cmd directory can find it. At the same time, increase the value to 64 since some operating systems use many, and the resources consumed by a larger value are minimal.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 26 ++++++++++++++++++++++++++ disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..65fb80b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{ + int ret; + char disk_guid[UUID_STR_LEN + 1]; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return CMD_RET_FAILURE; + + if (namestr) + setenv(namestr, disk_guid); + else + printf("%s\n", disk_guid); + + return ret; +} + /** * do_gpt(): Perform GPT operations * @@ -436,6 +453,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if (strcmp(argv[1], "guid") == 0) { + ret = do_disk_guid(blk_dev_desc, argv[4]); } else { return CMD_RET_USAGE; } @@ -458,4 +477,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " guid <interface> <dev>\n" + " - print disk GUID\n" + " guid <interface> <dev> <varname>\n" + " - set environment variable to disk GUID\n" + " Example usage:\n" + " gpt guid mmc 0\n" + " gpt guid mmc 0 varname\n" ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..71c3cb3 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h) * Public Functions (include/part.h) */
+/* + * UUID is displayed as 32 hexadecimal digits, in 5 groups, + * separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters + */ +int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{ + ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); + gpt_entry *gpt_pte = NULL; + unsigned char *guid_bin; + + /* This function validates AND fills in the GPT header and PTE */ + if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid GPT ***\n", __func__); + if (is_gpt_valid(dev_desc, dev_desc->lba - 1, + gpt_head, &gpt_pte) != 1) { + printf("%s: *** ERROR: Invalid Backup GPT ***\n", + __func__); + return -EINVAL; + } else { + printf("%s: *** Using Backup GPT ***\n", + __func__); + } + } + + guid_bin = gpt_head->disk_guid.b; + uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID); + + return 0; +} + void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 0a3286f..6c5ab78 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to - the generated UUID. + the generated UUID. The 'gpt guid' command reads the current value of the + uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT partition bootable (set attribute flags "Legacy BIOS bootable"). diff --git a/include/part.h b/include/part.h index 22da604..c41aa6a 100644 --- a/include/part.h +++ b/include/part.h @@ -372,6 +372,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte); + + +/** + * get_disk_guid() - Function to read the GUID string from a device's GPT + * + * This function reads the GUID string from a block device whose descriptor + * is provided. + * + * @param dev_desc - block device descriptor + * @param guid - pre-allocated string in which to return the GUID + * + * @return - '0' on success, otherwise error + */ +int get_disk_guid(struct blk_desc *dev_desc, char *guid); + #endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)

On Sun, Jun 25, 2017 at 04:43:23PM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Changes since v6: none.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Change since v6: -- Removed a cast in allocate_disk_part() as suggested by Lothar.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 126 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b..9934ef4 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,118 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 1, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part)) + goto out; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + goto out; + } + return --valid_parts; + out: + if (valid_parts >= 2) + del_gpt_info(); + return -ENODEV; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + return 0; + } + return ret; +} + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +570,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { ret = do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + ret = do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +594,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

Hi,
On Sun, 25 Jun 2017 16:43:24 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Change since v6: -- Removed a cast in allocate_disk_part() as suggested by Lothar.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 126 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b..9934ef4 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h>
+static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,118 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
curr = list_entry(pos->next, struct disk_part, list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = malloc(sizeof(*newpart));
- if (!newpart)
return ERR_PTR(-ENOMEM);
- memset(newpart, '\0', sizeof(newpart));
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
PART_NAME_LEN);
- newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
- strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
PART_TYPE_LEN);
- newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
UUID_STR_LEN);
- /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
- newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as U-Boot does */
- int valid_parts = 1, p, ret;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info, valid_parts);
if (IS_ERR(new_disk_part))
goto out;
list_add_tail(&new_disk_part->list, &disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
You initialize valid_parts to '1' and only ever increment it, so it can never be '0' here.
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com ---
Changes since v7: The failure-handling logic of get_gpt_info() now is triggered properly when valid_part=1.
cmd/gpt.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 126 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b..dd5f78b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,118 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 1, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part)) + goto out; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (valid_parts == 1) { + printf("** No valid partitions found **\n"); + goto out; + } + return --valid_parts; + out: + if (valid_parts >= 2) + del_gpt_info(); + return -ENODEV; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + return 0; + } + return ret; +} + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +570,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { ret = do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + ret = do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +594,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

Hi,
On Sat, 1 Jul 2017 15:42:57 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Changes since v7: The failure-handling logic of get_gpt_info() now is triggered properly when valid_part=1.
cmd/gpt.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 126 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b..dd5f78b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c
[...]
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as U-Boot does */
- int valid_parts = 1, p, ret;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info, valid_parts);
if (IS_ERR(new_disk_part))
goto out;
list_add_tail(&new_disk_part->list, &disk_partitions);
valid_parts++;
- }
- if (valid_parts == 1) {
printf("** No valid partitions found **\n");
goto out;
- }
- return --valid_parts;
I personally find it somewhat strange to initialize a counter to 1 and then return its value reduced by one, rather than initializing it to 0 and returning the exact counter value.
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 128 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 65fb80b3df..8457b80757 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c
Changes since v7: -- Made counter in get_gpt_info() start at 0 rather than 1. -- Checkpatch cleanup.
@@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,120 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 0, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + /* Add 1 here because counter is zero-based but p1 is + the first partition */ + new_disk_part = allocate_disk_part(&info, valid_parts+1); + if (IS_ERR(new_disk_part)) + goto out; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (valid_parts == 0) { + printf("** No valid partitions found **\n"); + goto out; + } + return valid_parts; + out: + if (valid_parts >= 1) + del_gpt_info(); + return -ENODEV; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + return 0; + } + return ret; +} + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +572,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { ret = do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + ret = do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +596,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6adc2..0cd803a933 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

On Tue, Jul 04, 2017 at 11:18:50AM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
After changing this to only be adding significant code when CMD_GPT_RENAME is enabled, applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v7:
-- Removed several casts in create_gpt_partitions_list() and do_rename_gpt_parts() as suggested by Lothar Waßmann. The cast of gpt_part_info.name to char* is needed to silence a compiler warning.
-- Substituted simple_strtol() for atoi() in do_gpt_rename_parts() as suggested by Tom Rini and Wolfgang Denk.
-- Fixed bug in do_rename_gpt_parts() where 0 was returned as a valid number of partitions with which to continue.
-- Added a comment to do_rename_gpt_parts() noting that the debug() string it optionally prints is useful as input to the pre-existing 'gpt write' and 'gpt verify' commands.
-- Changed some -1 return values to -ENODEV and -EINVAL as suggested by Tom Rini.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 8 ++ cmd/gpt.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 261 insertions(+), 6 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 07b0e3b..ae61b96 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 9934ef4..8c0441f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,8 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> +#include <stdlib.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +196,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +234,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -EINVAL; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +313,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -301,6 +391,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -530,6 +622,127 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts: -ENODEV; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + /* + * Uncomment the following line to print a string that 'gpt write' + * or 'gpt verify' will accept as input. + */ + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } + else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + return -EINVAL; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + partnum = (int)simple_strtol(name1, NULL, 10); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -547,7 +760,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
- if (argc < 4 || argc > 5) + if (argc < 4 || argc > 6) return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -572,6 +785,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -603,4 +821,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

Hi
On Mon, Jun 26, 2017 at 7:43 AM, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v7:
-- Removed several casts in create_gpt_partitions_list() and do_rename_gpt_parts() as suggested by Lothar Waßmann. The cast of gpt_part_info.name to char* is needed to silence a compiler warning.
-- Substituted simple_strtol() for atoi() in do_gpt_rename_parts() as suggested by Tom Rini and Wolfgang Denk.
-- Fixed bug in do_rename_gpt_parts() where 0 was returned as a valid number of partitions with which to continue.
-- Added a comment to do_rename_gpt_parts() noting that the debug() string it optionally prints is useful as input to the pre-existing 'gpt write' and 'gpt verify' commands.
-- Changed some -1 return values to -ENODEV and -EINVAL as suggested by Tom Rini.
Can you please remove the changelog from the commit message, instead put it in the commit-notes section below ---?
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/Kconfig | 8 ++ cmd/gpt.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 261 insertions(+), 6 deletions(-)
[snip]
Regards, Bin

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 8 ++ cmd/gpt.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 261 insertions(+), 6 deletions(-)
Changes since v7:
-- Removed several casts in create_gpt_partitions_list() and do_rename_gpt_parts() as suggested by Lothar Waßmann. The cast of gpt_part_info.name to char* is needed to silence a compiler warning.
-- Substituted simple_strtol() for atoi() in do_gpt_rename_parts() as suggested by Tom Rini and Wolfgang Denk.
-- Fixed bug in do_rename_gpt_parts() where 0 was returned as a valid number of partitions with which to continue.
-- Added a comment to do_rename_gpt_parts() noting that the debug() string it optionally prints is useful as input to the pre-existing 'gpt write' and 'gpt verify' commands.
-- Changed some -1 return values to -ENODEV and -EINVAL as suggested by Tom Rini.
diff --git a/cmd/Kconfig b/cmd/Kconfig index 07b0e3b..ae61b96 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 9934ef4..8c0441f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,8 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> +#include <stdlib.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +196,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +234,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -EINVAL; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +313,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -301,6 +391,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -530,6 +622,127 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts: -ENODEV; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + /* + * Uncomment the following line to print a string that 'gpt write' + * or 'gpt verify' will accept as input. + */ + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } + else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + return -EINVAL; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + partnum = (int)simple_strtol(name1, NULL, 10); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -547,7 +760,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
- if (argc < 4 || argc > 5) + if (argc < 4 || argc > 6) return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -572,6 +785,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -603,4 +821,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

Hi,
On Sun, 25 Jun 2017 16:43:25 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v7:
-- Removed several casts in create_gpt_partitions_list() and do_rename_gpt_parts() as suggested by Lothar Waßmann. The cast of gpt_part_info.name to char* is needed to silence a compiler warning.
-- Substituted simple_strtol() for atoi() in do_gpt_rename_parts() as suggested by Tom Rini and Wolfgang Denk.
-- Fixed bug in do_rename_gpt_parts() where 0 was returned as a valid number of partitions with which to continue.
-- Added a comment to do_rename_gpt_parts() noting that the debug() string it optionally prints is useful as input to the pre-existing 'gpt write' and 'gpt verify' commands.
-- Changed some -1 return values to -ENODEV and -EINVAL as suggested by Tom Rini.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/Kconfig | 8 ++ cmd/gpt.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 20 ++++- 3 files changed, 261 insertions(+), 6 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 07b0e3b..ae61b96 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME
- bool "GPT partition renaming commands"
- depends on CMD_GPT
- help
Enables the 'gpt' command to interchange names on two GPT
partitions via the 'gpt swap' command or to rename single
partitions via the 'rename' command.
config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 9934ef4..8c0441f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,8 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> +#include <stdlib.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +196,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize,
unsigned long blksize)
+{
- unsigned long long partbytes;
- unsigned long partmegabytes;
- partbytes = partsize * blksize;
- partmegabytes = lldiv(partbytes, SZ_1M);
- snprintf(sizestr, 16, "%luMiB", partmegabytes);
+}
static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list);
prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type,printf("Start %s, size %s\n", partstartstr, partsizestr);
@@ -215,6 +234,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{
- int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
- /* for the comma */
- partlistlen++;
- /* per-partition additions; numparts starts at 1, so this should be correct */
- partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
- /* see part.h for definition of struct disk_partition */
- partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1);
- partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1);
- partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
- /* for the terminating null */
- partlistlen++;
- debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
numparts);
- return partlistlen;
+}
+/*
- create the string that upstream 'gpt write' command will accept as an
- argument
- From doc/README.gpt, Format of partitions layout:
- "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
- are optional if CONFIG_RANDOM_UUID is enabled.
- */
+static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{
- struct list_head *pos;
- struct disk_part *curr;
- char partstr[PART_NAME_LEN + 1];
- if (!partitions_list)
return -EINVAL;
- strcpy(partitions_list, "uuid_disk=");
- strncat(partitions_list, guid, UUID_STR_LEN + 1);
- strcat(partitions_list, ";");
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
strcat(partitions_list, "name=");
strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1);
strcat(partitions_list, ",start=");
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
/* one extra byte for NULL */
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",size=");
/* lbaint_t is unsigned long, per include/ide.h */
AFAICT lbaint_t is declared in include/blk.h and may be uint64_t or ulong depending on CONFIG_SYS_64BIT_LBA.
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",uuid=");
strncat(partitions_list, curr->gpt_part_info.uuid,
UUID_STR_LEN + 1);
strcat(partitions_list, ";");
- }
- return 0;
+} +#endif
/*
- read partition info into disk_partitions list where
- it can be printed or modified
@@ -226,8 +313,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
/*
* Always re-read partition info from device, in case
* it has changed
*/
INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info);
@@ -301,6 +391,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part);
if (str == NULL)
return -ENOMEM;
/* extract disk guid */ s = str;
@@ -530,6 +622,127 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
char *name1, char *name2)
+{
- struct list_head *pos;
- struct disk_part *curr;
- disk_partition_t *new_partitions = NULL;
- char disk_guid[UUID_STR_LEN + 1];
- char *partitions_list, *str_disk_guid;
- u8 part_count = 0;
- int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
- if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) ||
(strcmp(subcomm, "swap") && (strcmp(subcomm, "rename"))))
return -EINVAL;
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return ret;
- numparts = get_gpt_info(dev_desc);
- if (numparts <= 0)
return numparts ? numparts: -ENODEV;
- partlistlen = calc_parts_list_len(numparts);
- partitions_list = malloc(partlistlen);
- memset(partitions_list, '\0', partlistlen);
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid,
unnecessary type cast. A function that takes a 'const' argument may well be passed a non-'const' value.
partitions_list);
- if (ret < 0)
return ret;
- /*
* Uncomment the following line to print a string that 'gpt write'
* or 'gpt verify' will accept as input.
*/
- debug("OLD partitions_list is %s with %u chars\n", partitions_list,
(unsigned)strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
dto.
[...]
list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (i == partnum) {
strcpy((char *)curr->gpt_part_info.name, name2);
break;
}
i++;
}
- }
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list);
dto.
- if (ret < 0)
return ret;
- debug("NEW partitions_list is %s with %u chars\n", partitions_list,
(unsigned)strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
dto.
@@ -547,7 +760,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
- if (argc < 4 || argc > 5)
- if (argc < 4 || argc > 6)
Other subcommands still accept only 4 parameters, so this check should possibly be done after checking the subcommand name, or at least made conditional on CONFIG_CMD_GPT_RENAME which enables the new subcommands.
return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -572,6 +785,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME
- } else if ((strcmp(argv[1], "swap") == 0) ||
(strcmp(argv[1], "rename") == 0)) {
ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
+#endif } else { return CMD_RET_USAGE; } @@ -603,4 +821,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME
- "gpt partition renaming commands:\n"
- "gpt swap <interface> <dev> <name1> <name2>\n"
- " - change all partitions named name1 to name2\n"
- " and vice-versa\n"
- "gpt rename <interface> <dev> <part> <name>\n"
- " - rename the specified partition\n"
- " Example usage:\n"
- " gpt swap mmc 0 foo bar\n"
- " gpt rename mmc 0 3 foo\n"
+#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +====================================
+GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names:
+U-BOOT> gpt swap mmc 0 primary backup
+Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary':
+U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com ---
Changes since v7: This version removes casts to unsigned long where the underlying data type is lbaint_t. I tested this version both with and without CONFIG_SYS_64BIT_LBA, and the output is fine in either case. This version also removes unneeded casts to const and restores the gpt command's error reporting to its earlier state if CONFIG_GPT_RENAME is unset.
In addition, there is an additional check that a malloc() has succeeded.
cmd/Kconfig | 8 ++ cmd/gpt.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- doc/README.gpt | 20 ++++- 3 files changed, 263 insertions(+), 5 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 07b0e3b..ae61b96 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index dd5f78b..73bf273 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,8 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> +#include <stdlib.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +196,32 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, lbaint_t partsize, + lbaint_t blksize) +{ + unsigned long long partbytes, partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%lluMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, curr->gpt_part_info.start, + curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, curr->gpt_part_info.size, + curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +233,73 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -EINVAL; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + prettyprint_part_size(partstr, curr->gpt_part_info.size, + curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +311,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -301,6 +389,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -530,6 +620,128 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts: -ENODEV; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + if (partitions_list == NULL) + return -ENOMEM; + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) + return ret; + /* + * Uncomment the following line to print a string that 'gpt write' + * or 'gpt verify' will accept as input. + */ + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } + else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + return -EINVAL; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + partnum = (int)simple_strtol(name1, NULL, 10); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -547,7 +759,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
+#ifndef CONFIG_CMD_GPT_RENAME if (argc < 4 || argc > 5) +#else + if (argc < 4 || argc > 6) +#endif return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -572,6 +788,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -603,4 +824,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab78..d3db8bc 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

Dear Alison,
In message CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail.gmail.com you wrote:
The idea behind the 'swap' mode is that a storage device can have two sets of partitions, one set all named 'primary' and one set all named 'backup'. The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the sandbox, but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone have a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk

On Sun, Jun 18, 2017 at 4:03 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message <CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail. gmail.com> you wrote:
The idea behind the 'swap' mode is that a storage device can have two
sets
of partitions, one set all named 'primary' and one set all named
'backup'.
The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being
interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the
sandbox,
but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone
have
a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk
I'm about to submit a new version of the patches that adopts Wolfgang's and Tom's suggestions about replacing atoi().
Regarding the atomicity of 'gpt swap, the point is that 'gpt swap' first modifies the names in an in-memory data structure, and then uses the existing 'gpt write' functionality to change the actual partition table stored on the device. Thus, interruption of the new command is low-risk, as interruption of the modification of the new data structure has no persistent effect, and the risk associated with 'gpt write' is the same as always.
By the way, in the course of testing an earlier version of this patch series, I noticed that 'gpt write' and 'gpt verify' segv if presented with a non-null-terminated partitions string. It's the strlen function in lib that actually generates an error. I haven't yet quite figured out what the best solution to the problem is: should strlen() itself be modified, or is it enough to test in gpt.c? The right solution is not to present the commands with poorly formed strings, but it's easy to do so.
Mit freundlichen Grüße, Alison

On Sun, Jun 25, 2017 at 02:54:56PM -0700, Alison Chaiken wrote:
On Sun, Jun 18, 2017 at 4:03 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message <CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail. gmail.com> you wrote:
The idea behind the 'swap' mode is that a storage device can have two
sets
of partitions, one set all named 'primary' and one set all named
'backup'.
The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being
interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the
sandbox,
but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone
have
a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk
I'm about to submit a new version of the patches that adopts Wolfgang's and Tom's suggestions about replacing atoi().
Regarding the atomicity of 'gpt swap, the point is that 'gpt swap' first modifies the names in an in-memory data structure, and then uses the existing 'gpt write' functionality to change the actual partition table stored on the device. Thus, interruption of the new command is low-risk, as interruption of the modification of the new data structure has no persistent effect, and the risk associated with 'gpt write' is the same as always.
By the way, in the course of testing an earlier version of this patch series, I noticed that 'gpt write' and 'gpt verify' segv if presented with a non-null-terminated partitions string. It's the strlen function in lib that actually generates an error. I haven't yet quite figured out what the best solution to the problem is: should strlen() itself be modified, or is it enough to test in gpt.c? The right solution is not to present the commands with poorly formed strings, but it's easy to do so.
It is likely this particular bug is one of many in the class of using strlen when we cannot really be certain of well formatted input / buffer, sadly. So a fix to gpt.c and if you're so inclined, a git grep around for other likely cases.

Hi,
On Sun, 25 Jun 2017 14:54:56 -0700 Alison Chaiken wrote:
On Sun, Jun 18, 2017 at 4:03 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message <CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail. gmail.com> you wrote:
The idea behind the 'swap' mode is that a storage device can have two
sets
of partitions, one set all named 'primary' and one set all named
'backup'.
The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being
interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the
sandbox,
but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone
have
a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk
I'm about to submit a new version of the patches that adopts Wolfgang's and Tom's suggestions about replacing atoi().
Regarding the atomicity of 'gpt swap, the point is that 'gpt swap' first modifies the names in an in-memory data structure, and then uses the existing 'gpt write' functionality to change the actual partition table stored on the device. Thus, interruption of the new command is low-risk, as interruption of the modification of the new data structure has no persistent effect, and the risk associated with 'gpt write' is the same as always.
By the way, in the course of testing an earlier version of this patch series, I noticed that 'gpt write' and 'gpt verify' segv if presented with a non-null-terminated partitions string. It's the strlen function in lib that actually generates an error. I haven't yet quite figured out what the best solution to the problem is: should strlen() itself be modified, or is it enough to test in gpt.c? The right solution is not to present the commands with poorly formed strings, but it's easy to do so.
You can use strnlen() if you know the maximum allowed length of the string.
NB: A quick glance at set_gpt_info() revealed this potential crash cause: | str = strdup(str_part); | | /* extract disk guid */ | s = str; | val = extract_val(str, "uuid_disk"); strdup() may fail (especially if the input string is not zero terminated) and return a NULL pointer which then will happily be used by extract_val().
Lothar Waßmann

Hi,
On Tue, 27 Jun 2017 09:05:14 +0200 Lothar Waßmann wrote:
Hi,
On Sun, 25 Jun 2017 14:54:56 -0700 Alison Chaiken wrote:
On Sun, Jun 18, 2017 at 4:03 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message <CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail. gmail.com> you wrote:
The idea behind the 'swap' mode is that a storage device can have two
sets
of partitions, one set all named 'primary' and one set all named
'backup'.
The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being
interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the
sandbox,
but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone
have
a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk
I'm about to submit a new version of the patches that adopts Wolfgang's and Tom's suggestions about replacing atoi().
Regarding the atomicity of 'gpt swap, the point is that 'gpt swap' first modifies the names in an in-memory data structure, and then uses the existing 'gpt write' functionality to change the actual partition table stored on the device. Thus, interruption of the new command is low-risk, as interruption of the modification of the new data structure has no persistent effect, and the risk associated with 'gpt write' is the same as always.
By the way, in the course of testing an earlier version of this patch series, I noticed that 'gpt write' and 'gpt verify' segv if presented with a non-null-terminated partitions string. It's the strlen function in lib that actually generates an error. I haven't yet quite figured out what the best solution to the problem is: should strlen() itself be modified, or is it enough to test in gpt.c? The right solution is not to present the commands with poorly formed strings, but it's easy to do so.
You can use strnlen() if you know the maximum allowed length of the string.
NB: A quick glance at set_gpt_info() revealed this potential crash cause: | str = strdup(str_part); | | /* extract disk guid */ | s = str; | val = extract_val(str, "uuid_disk"); strdup() may fail (especially if the input string is not zero terminated) and return a NULL pointer which then will happily be used by extract_val().
There are some more highlights in this code: | *str_disk_guid = malloc(UUID_STR_LEN + 1); | gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
malloc() can fail too.
| *str_disk_guid = strdup(p); | free(val); | /* Move s to first partition */ | strsep(&s, ";");
if strdup() fails, *str_disk_guid will be a NULL pointer, but the function will return success eventually and the NULL pointer will be passed on to subsequent functions without further checks.
|static bool found_key(const char *str, const char *key) |{ | char *k; | char *s, *strcopy; | bool result = false; | | strcopy = strdup(str); | if (!strcopy) | return NULL;
The function has a bool return type, but returns a pointer type here. This accidentally works as expected.
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
Strings read from devices may sometimes fail to be NULL-terminated. The functions in lib/string.c are subject to failure in this case. Protect against observed failures in set_gpt_info() by switching to length-checking variants with a length limit of the maximum possible partition table length. At the same time, add a few checks for NULL string pointers.
Here is an example as observed in sandbox under GDB:
=> gpt verify host 0 $partitions Program received signal SIGSEGV, Segmentation fault. 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 267 for (sc = s; *sc != '\0'; ++sc) (gdb) bt #0 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 #1 0x00000000004140b2 in set_gpt_info (str_part=<optimized out>, str_disk_guid=str_disk_guid@entry=0x7fffffffdbe8, partitions=partitions@entry=0x7fffffffdbd8, parts_count=parts_count@entry=0x7fffffffdbcf "", dev_desc=<optimized out>) at cmd/gpt.c:415 #2 0x00000000004145b9 in gpt_verify (str_part=<optimized out>, blk_dev_desc=0x7fffef09a9d0) at cmd/gpt.c:580 #3 do_gpt (cmdtp=<optimized out>, flag=<optimized out>, argc=<optimized out>, argv=0x7fffef09a8f0) at cmd/gpt.c:783 #4 0x00000000004295b0 in cmd_call (argv=0x7fffef09a8f0, argc=0x5, flag=<optimized out>, cmdtp=0x714e20 <_u_boot_list_2_cmd_2_gpt>) at common/command.c:500 #5 cmd_process (flag=<optimized out>, argc=0x5, argv=0x7fffef09a8f0, repeatable=repeatable@entry=0x726c04 <flag_repeat>, ticks=ticks@entry=0x0) at common/command.c:539
Suggested-by: Lothar Waßmann LW@karo-electronics.de Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 73bf273..8bd7bdf 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -233,7 +233,7 @@ static void print_gpt_info(void) } }
-#ifdef CONFIG_CMD_GPT_RENAME + static int calc_parts_list_len(int numparts) { int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); @@ -253,6 +253,7 @@ static int calc_parts_list_len(int numparts) return partlistlen; }
+#ifdef CONFIG_CMD_GPT_RENAME /* * create the string that upstream 'gpt write' command will accept as an * argument @@ -381,6 +382,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, int errno = 0; uint64_t size_ll, start_ll; lbaint_t offset = 0; + int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
debug("%s: lba num: 0x%x %d\n", __func__, (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); @@ -398,6 +400,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (!val) { #ifdef CONFIG_RANDOM_UUID *str_disk_guid = malloc(UUID_STR_LEN + 1); + if (str_disk_guid == NULL) + return -ENOMEM; gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD); #else free(str); @@ -412,10 +416,14 @@ static int set_gpt_info(struct blk_desc *dev_desc, /* Move s to first partition */ strsep(&s, ";"); } - if (strlen(s) == 0) + if (s == NULL) { + printf("Error: is the partitions string NULL-terminated?\n"); + return -EINVAL; + } + if (strnlen(s, max_str_part) == 0) return -3;
- i = strlen(s) - 1; + i = strnlen(s, max_str_part) - 1; if (s[i] == ';') s[i] = '\0';
@@ -429,6 +437,8 @@ static int set_gpt_info(struct blk_desc *dev_desc,
/* allocate memory for partitions */ parts = calloc(sizeof(disk_partition_t), p_count); + if (parts == NULL) + return -ENOMEM;
/* retrieve partitions data from string */ for (i = 0; i < p_count; i++) { @@ -450,12 +460,12 @@ static int set_gpt_info(struct blk_desc *dev_desc, } else { if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].uuid)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) { printf("Wrong uuid format for partition %d\n", i); errno = -4; goto err; } - strcpy((char *)parts[i].uuid, p); + strncpy((char *)parts[i].uuid, p, max_str_part); free(val); } #ifdef CONFIG_PARTITION_TYPE_GUID @@ -465,13 +475,13 @@ static int set_gpt_info(struct blk_desc *dev_desc, /* 'type' is optional */ if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].type_guid)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) { printf("Wrong type guid format for partition %d\n", i); errno = -4; goto err; } - strcpy((char *)parts[i].type_guid, p); + strncpy((char *)parts[i].type_guid, p, max_str_part); free(val); } #endif @@ -483,11 +493,11 @@ static int set_gpt_info(struct blk_desc *dev_desc, } if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].name)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) { errno = -4; goto err; } - strcpy((char *)parts[i].name, p); + strncpy((char *)parts[i].name, p, max_str_part); free(val);
/* size */

Hi,
On Sat, 1 Jul 2017 15:44:44 -0700 alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
Strings read from devices may sometimes fail to be NULL-terminated. The functions in lib/string.c are subject to failure in this case. Protect against observed failures in set_gpt_info() by switching to length-checking variants with a length limit of the maximum possible partition table length. At the same time, add a few checks for NULL string pointers.
Here is an example as observed in sandbox under GDB:
=> gpt verify host 0 $partitions Program received signal SIGSEGV, Segmentation fault. 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 267 for (sc = s; *sc != '\0'; ++sc) (gdb) bt #0 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 #1 0x00000000004140b2 in set_gpt_info (str_part=<optimized out>, str_disk_guid=str_disk_guid@entry=0x7fffffffdbe8, partitions=partitions@entry=0x7fffffffdbd8, parts_count=parts_count@entry=0x7fffffffdbcf "", dev_desc=<optimized out>) at cmd/gpt.c:415 #2 0x00000000004145b9 in gpt_verify (str_part=<optimized out>, blk_dev_desc=0x7fffef09a9d0) at cmd/gpt.c:580 #3 do_gpt (cmdtp=<optimized out>, flag=<optimized out>, argc=<optimized out>, argv=0x7fffef09a8f0) at cmd/gpt.c:783 #4 0x00000000004295b0 in cmd_call (argv=0x7fffef09a8f0, argc=0x5, flag=<optimized out>, cmdtp=0x714e20 <_u_boot_list_2_cmd_2_gpt>) at common/command.c:500 #5 cmd_process (flag=<optimized out>, argc=0x5, argv=0x7fffef09a8f0, repeatable=repeatable@entry=0x726c04 <flag_repeat>, ticks=ticks@entry=0x0) at common/command.c:539
Suggested-by: Lothar Waßmann LW@karo-electronics.de Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 73bf273..8bd7bdf 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -233,7 +233,7 @@ static void print_gpt_info(void) } }
-#ifdef CONFIG_CMD_GPT_RENAME
static int calc_parts_list_len(int numparts) { int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); @@ -253,6 +253,7 @@ static int calc_parts_list_len(int numparts) return partlistlen; }
+#ifdef CONFIG_CMD_GPT_RENAME /*
- create the string that upstream 'gpt write' command will accept as an
- argument
@@ -381,6 +382,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, int errno = 0; uint64_t size_ll, start_ll; lbaint_t offset = 0;
int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
indentation should use tabs not spaces (scripts/checkpatch.pl would tell you).
debug("%s: lba num: 0x%x %d\n", __func__, (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); @@ -398,6 +400,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (!val) { #ifdef CONFIG_RANDOM_UUID *str_disk_guid = malloc(UUID_STR_LEN + 1);
if (str_disk_guid == NULL)
gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);return -ENOMEM;
#else free(str); @@ -412,10 +416,14 @@ static int set_gpt_info(struct blk_desc *dev_desc, /* Move s to first partition */ strsep(&s, ";"); }
- if (strlen(s) == 0)
- if (s == NULL) {
printf("Error: is the partitions string NULL-terminated?\n");
return -EINVAL;
dto.
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
Strings read from devices may sometimes fail to be NULL-terminated. The functions in lib/string.c are subject to failure in this case. Protect against observed failures in set_gpt_info() by switching to length-checking variants with a length limit of the maximum possible partition table length. At the same time, add a few checks for NULL string pointers.
Here is an example as observed in sandbox under GDB:
=> gpt verify host 0 $partitions Program received signal SIGSEGV, Segmentation fault. 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 267 for (sc = s; *sc != '\0'; ++sc) (gdb) bt #0 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 #1 0x00000000004140b2 in set_gpt_info (str_part=<optimized out>, str_disk_guid=str_disk_guid@entry=0x7fffffffdbe8, partitions=partitions@entry=0x7fffffffdbd8, parts_count=parts_count@entry=0x7fffffffdbcf "", dev_desc=<optimized out>) at cmd/gpt.c:415 #2 0x00000000004145b9 in gpt_verify (str_part=<optimized out>, blk_dev_desc=0x7fffef09a9d0) at cmd/gpt.c:580 #3 do_gpt (cmdtp=<optimized out>, flag=<optimized out>, argc=<optimized out>, argv=0x7fffef09a8f0) at cmd/gpt.c:783 #4 0x00000000004295b0 in cmd_call (argv=0x7fffef09a8f0, argc=0x5, flag=<optimized out>, cmdtp=0x714e20 <_u_boot_list_2_cmd_2_gpt>) at common/command.c:500 #5 cmd_process (flag=<optimized out>, argc=0x5, argv=0x7fffef09a8f0, repeatable=repeatable@entry=0x726c04 <flag_repeat>, ticks=ticks@entry=0x0) at common/command.c:539
Suggested-by: Lothar Waßmann LW@karo-electronics.de Signed-off-by: Alison Chaiken alison@peloton-tech.com ---
Changes since v1: -- Checkpatch cleanups.
cmd/gpt.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index d703385a24..f8f35c23e0 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -233,7 +233,7 @@ static void print_gpt_info(void) } }
-#ifdef CONFIG_CMD_GPT_RENAME + static int calc_parts_list_len(int numparts) { int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); @@ -253,6 +253,7 @@ static int calc_parts_list_len(int numparts) return partlistlen; }
+#ifdef CONFIG_CMD_GPT_RENAME /* * create the string that upstream 'gpt write' command will accept as an * argument @@ -385,6 +386,7 @@ static int set_gpt_info(struct blk_desc *dev_desc, int errno = 0; uint64_t size_ll, start_ll; lbaint_t offset = 0; + int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
debug("%s: lba num: 0x%x %d\n", __func__, (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); @@ -402,6 +404,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, if (!val) { #ifdef CONFIG_RANDOM_UUID *str_disk_guid = malloc(UUID_STR_LEN + 1); + if (str_disk_guid == NULL) + return -ENOMEM; gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD); #else free(str); @@ -416,10 +420,14 @@ static int set_gpt_info(struct blk_desc *dev_desc, /* Move s to first partition */ strsep(&s, ";"); } - if (strlen(s) == 0) + if (s == NULL) { + printf("Error: is the partitions string NULL-terminated?\n"); + return -EINVAL; + } + if (strnlen(s, max_str_part) == 0) return -3;
- i = strlen(s) - 1; + i = strnlen(s, max_str_part) - 1; if (s[i] == ';') s[i] = '\0';
@@ -433,6 +441,8 @@ static int set_gpt_info(struct blk_desc *dev_desc,
/* allocate memory for partitions */ parts = calloc(sizeof(disk_partition_t), p_count); + if (parts == NULL) + return -ENOMEM;
/* retrieve partitions data from string */ for (i = 0; i < p_count; i++) { @@ -454,12 +464,12 @@ static int set_gpt_info(struct blk_desc *dev_desc, } else { if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].uuid)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) { printf("Wrong uuid format for partition %d\n", i); errno = -4; goto err; } - strcpy((char *)parts[i].uuid, p); + strncpy((char *)parts[i].uuid, p, max_str_part); free(val); } #ifdef CONFIG_PARTITION_TYPE_GUID @@ -469,13 +479,13 @@ static int set_gpt_info(struct blk_desc *dev_desc, /* 'type' is optional */ if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].type_guid)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) { printf("Wrong type guid format for partition %d\n", i); errno = -4; goto err; } - strcpy((char *)parts[i].type_guid, p); + strncpy((char *)parts[i].type_guid, p, max_str_part); free(val); } #endif @@ -487,11 +497,11 @@ static int set_gpt_info(struct blk_desc *dev_desc, } if (extract_env(val, &p)) p = val; - if (strlen(p) >= sizeof(parts[i].name)) { + if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) { errno = -4; goto err; } - strcpy((char *)parts[i].name, p); + strncpy((char *)parts[i].name, p, max_str_part); free(val);
/* size */

On Tue, Jul 04, 2017 at 11:19:46AM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
Strings read from devices may sometimes fail to be NULL-terminated. The functions in lib/string.c are subject to failure in this case. Protect against observed failures in set_gpt_info() by switching to length-checking variants with a length limit of the maximum possible partition table length. At the same time, add a few checks for NULL string pointers.
Here is an example as observed in sandbox under GDB:
=> gpt verify host 0 $partitions Program received signal SIGSEGV, Segmentation fault. 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 267 for (sc = s; *sc != '\0'; ++sc) (gdb) bt #0 0x0000000000477747 in strlen (s=0x0) at lib/string.c:267 #1 0x00000000004140b2 in set_gpt_info (str_part=<optimized out>, str_disk_guid=str_disk_guid@entry=0x7fffffffdbe8, partitions=partitions@entry=0x7fffffffdbd8, parts_count=parts_count@entry=0x7fffffffdbcf "", dev_desc=<optimized out>) at cmd/gpt.c:415 #2 0x00000000004145b9 in gpt_verify (str_part=<optimized out>, blk_dev_desc=0x7fffef09a9d0) at cmd/gpt.c:580 #3 do_gpt (cmdtp=<optimized out>, flag=<optimized out>, argc=<optimized out>, argv=0x7fffef09a8f0) at cmd/gpt.c:783 #4 0x00000000004295b0 in cmd_call (argv=0x7fffef09a8f0, argc=0x5, flag=<optimized out>, cmdtp=0x714e20 <_u_boot_list_2_cmd_2_gpt>) at common/command.c:500 #5 cmd_process (flag=<optimized out>, argc=0x5, argv=0x7fffef09a8f0, repeatable=repeatable@entry=0x726c04 <flag_repeat>, ticks=ticks@entry=0x0) at common/command.c:539
Suggested-by: Lothar Waßmann LW@karo-electronics.de Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

On Tue, Jun 27, 2017 at 12:05 AM, Lothar Waßmann LW@karo-electronics.de wrote:
Hi,
On Sun, 25 Jun 2017 14:54:56 -0700 Alison Chaiken wrote:
On Sun, Jun 18, 2017 at 4:03 AM, Wolfgang Denk wd@denx.de wrote:
Dear Alison,
In message <CAOuSAjdHerD7iWSwv5HQmx07nALRHschnH5=XToNEZDqA9JsvQ@mail. gmail.com> you wrote:
The idea behind the 'swap' mode is that a storage device can have two
sets
of partitions, one set all named 'primary' and one set all named
'backup'.
The software updater in userspace can then simply rename the partitions with sgdisk in order to pick the new image. The swap mode changes the whole set of labels at once, so there's little chance of being
interrupted.
It's still a sequential, non-atomic operation, and "little chance" is exactly the places where Murphy likes to hit you.
One additional note: the last version I posted worked fine for the
sandbox,
but wouldn't link for an ARM target with the Linaro toolchain, as the linker couldn't find atoi(). I guess the libc for the x86 compiler includes it. To test on ARM, I copied in simple_atoi() from lib/vsprintf.c, but assuredly that is an ugly solution. Does anyone
have
a better idea to solve this problem?
U-Boot should always be self-contained and not link regular library code from the tool chain.
Best regards,
Wolfgang Denk
I'm about to submit a new version of the patches that adopts Wolfgang's and Tom's suggestions about replacing atoi().
Regarding the atomicity of 'gpt swap, the point is that 'gpt swap' first modifies the names in an in-memory data structure, and then uses the existing 'gpt write' functionality to change the actual partition table stored on the device. Thus, interruption of the new command is low-risk, as interruption of the modification of the new data structure has no persistent effect, and the risk associated with 'gpt write' is the same as always.
By the way, in the course of testing an earlier version of this patch series, I noticed that 'gpt write' and 'gpt verify' segv if presented with a non-null-terminated partitions string. It's the strlen function in lib that actually generates an error. I haven't yet quite figured out what the best solution to the problem is: should strlen() itself be modified, or is it enough to test in gpt.c? The right solution is not to present the commands with poorly formed strings, but it's easy to do so.
You can use strnlen() if you know the maximum allowed length of the string.
Thanks for that tip: I didn't know that strnlen() existed. That indeed seems like a good protection against at least the problems identified here.
NB: A quick glance at set_gpt_info() revealed this potential crash cause: | str = strdup(str_part); | | /* extract disk guid */ | s = str; | val = extract_val(str, "uuid_disk"); strdup() may fail (especially if the input string is not zero terminated) and return a NULL pointer which then will happily be used by extract_val().
Lothar Waßmann
The underlying cause of the problem is that u-boot's implementations of strlen() and the CLI handle strings differently. The former terminates strings only on NULLs, while the latter terminates strings *either* on NULLs or on semicolons. Reading the partition table back from a device's GPT results in a string with a NULL only at the very end, but with one semicolon per partition. Running 'gpt verify mmc 0 $partitions" or 'gpt write mmc 0 $partitions' therefore causes a crash if the user has previously typed 'setenv partitions 1;2;3;4;5;' rather than 'setenv partitions "1;2;3;4;5;"'. In the former case, the partitions string ends up being '1' without NULL-termination, resulting in the crash.
U-boot environment parameters often have lots of semi-colon-separated lists. I'm not sure what other code paths lead to strlen() or strdup(), as Lothar alludes to above, but it seems likely to me that there are other ways to reproduce this bug. Looking at lib/string.c, there are other functions like strcpy() that also check "!= '\0'".
--- Alison

Hi,
The underlying cause of the problem is that u-boot's implementations of strlen() and the CLI handle strings differently. The former
"u-boot's implementation" is conformant with the standard and well documented libc implementation that exists since the dawn of C programming.
terminates strings only on NULLs, while the latter terminates strings *either* on NULLs or on semicolons. Reading the partition table back from a device's GPT results in a string with a NULL only at the very end, but with one semicolon per partition. Running 'gpt verify mmc 0 $partitions" or 'gpt write mmc 0 $partitions' therefore causes a crash if the user has previously typed 'setenv partitions 1;2;3;4;5;' rather than 'setenv partitions "1;2;3;4;5;"'. In the former case, the partitions string ends up being '1' without NULL-termination,
That's nonsense. _Every_ string in the environment is NULL terminated. In the former case of your example the characters after the first semicolon are interpreted as commands and lead to an error message Unknown command '2' - try 'help' ... because the ';' is treated as a command terminator by the parser unless quoted.
Lothar Waßmann

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com ---
Changes since v7: -- Checkpatch cleanups.
cmd/Kconfig | 8 ++ cmd/gpt.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- doc/README.gpt | 20 ++++- 3 files changed, 264 insertions(+), 5 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 6758db16f3..8d6fcb576d 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -595,6 +595,14 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_RENAME + bool "GPT partition renaming commands" + depends on CMD_GPT + help + Enables the 'gpt' command to interchange names on two GPT + partitions via the 'gpt swap' command or to rename single + partitions via the 'rename' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index 8457b80757..d703385a24 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,8 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h> +#include <stdlib.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +196,32 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, lbaint_t partsize, + lbaint_t blksize) +{ + unsigned long long partbytes, partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%lluMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, curr->gpt_part_info.start, + curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, curr->gpt_part_info.size, + curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +233,75 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_RENAME +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* see part.h for definition of struct disk_partition */ + partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, + char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -EINVAL; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, + PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + prettyprint_part_size(partstr, curr->gpt_part_info.size, + curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +313,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -303,6 +393,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -532,6 +624,127 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_RENAME +static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + + if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) || + (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename")))) + return -EINVAL; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + if (partitions_list == NULL) + return -ENOMEM; + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) + return ret; + /* + * Uncomment the following line to print a string that 'gpt write' + * or 'gpt verify' will accept as input. + */ + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, name1)) { + strcpy((char *)curr->gpt_part_info.name, name2); + ctr1++; + } else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { + strcpy((char *)curr->gpt_part_info.name, name1); + ctr2++; + } + } + if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { + printf("Cannot swap partition names except in pairs.\n"); + return -EINVAL; + } + } else { /* rename */ + if (strlen(name2) > PART_NAME_LEN) { + printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); + return -EINVAL; + } + partnum = (int)simple_strtol(name1, NULL, 10); + if ((partnum < 0) || (partnum > numparts)) { + printf("Illegal partition number %s\n", name1); + return -EINVAL; + } + ret = part_get_info(dev_desc, partnum, new_partitions); + if (ret < 0) + return ret; + + /* U-Boot partition numbering starts at 1 */ + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (i == partnum) { + strcpy((char *)curr->gpt_part_info.name, name2); + break; + } + i++; + } + } + + ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts <= 0) + return numparts ? numparts : -ENODEV; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -549,7 +762,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char *ep; struct blk_desc *blk_dev_desc = NULL;
+#ifndef CONFIG_CMD_GPT_RENAME if (argc < 4 || argc > 5) +#else + if (argc < 4 || argc > 6) +#endif return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); @@ -574,6 +791,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_RENAME + } else if ((strcmp(argv[1], "swap") == 0) || + (strcmp(argv[1], "rename") == 0)) { + ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); +#endif } else { return CMD_RET_USAGE; } @@ -605,4 +827,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_RENAME + "gpt partition renaming commands:\n" + "gpt swap <interface> <dev> <name1> <name2>\n" + " - change all partitions named name1 to name2\n" + " and vice-versa\n" + "gpt rename <interface> <dev> <part> <name>\n" + " - rename the specified partition\n" + " Example usage:\n" + " gpt swap mmc 0 foo bar\n" + " gpt rename mmc 0 3 foo\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index 6c5ab7858a..d3db8bce1c 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,24 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read', 'gpt rename' and 'gpt swap' commands facilitate +programmatic renaming of partitions from bootscripts by generating and +modifying the partitions layout string. Here is an illustration of +employing 'swap' to exchange 'primary' and 'backup' partition names: + +U-BOOT> gpt swap mmc 0 primary backup + +Afterwards, all partitions previously named 'primary' will be named +'backup', and vice-versa. Alternatively, single partitions may be +renamed. In this example, mmc0's first partition will be renamed +'primary': + +U-BOOT> gpt rename mmc 0 1 primary
The GPT functionality may be tested with the 'sandbox' board by creating a disk image as described under 'Block Device Emulation' in @@ -218,7 +236,7 @@ board/sandbox/README.sandbox: =>host bind 0 ./disk.raw => gpt read host 0 [ . . . ] -=> gpt flip host 0 +=> gpt swap host 0 name othername [ . . . ]
Partition type GUID:

On Tue, Jul 04, 2017 at 11:19:18AM -0700, Alison Chaiken wrote:
From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via new 'gpt swap' and 'gpt rename' commands.
The 'swap' mode returns an error if no matching partition names are found, or if the number of partitions with one name does not equal the number with the second name. The 'rename' variant always succeeds as long as a partition with the provided number exists.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
Applied to u-boot/master, thanks!

From: Alison Chaiken alison@peloton-tech.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt and file comments accordingly.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 13 +------------ doc/README.gpt | 8 ++++---- 2 files changed, 5 insertions(+), 16 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index f6968de..e2cb2a1 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -232,12 +232,6 @@ static void print_gpt_info(void) #ifdef CONFIG_CMD_GPT_FLIP static int calc_parts_list_len(int numparts) { - /* - * prefatory string: - * doc/README.GPT, suggests that - * int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk="); - * is correct, but extract_val() expects "uuid_disk" first. - */ int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); /* for the comma */ partlistlen++; @@ -260,7 +254,7 @@ static int calc_parts_list_len(int numparts) * argument * * From doc/README.gpt, Format of partitions layout: - * "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; * name=kernel,size=60MiB,uuid=...;" * The fields 'name' and 'size' are mandatory for every partition. * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' @@ -275,11 +269,6 @@ static int create_gpt_partitions_list(int numparts, const char *guid, char *part if (!partitions_list) return -1;
- /* - * README.gpt specifies starting with "partitions=" like so: - * strcpy(partitions_list, "partitions=uuid_disk="); - * but that breaks extract_val, which doesn't skip over 'partitions='. - */ strcpy(partitions_list, "uuid_disk="); strncat(partitions_list, guid, UUID_STR_LEN + 1); strcat(partitions_list, ";"); diff --git a/doc/README.gpt b/doc/README.gpt index e29b188..754e490 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to: 1. Define partition layout in the environment. Format of partitions layout: - "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...;" or - "partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name}, + "uuid_disk=${uuid_gpt_disk};name=${uboot_name}, size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition. @@ -233,7 +233,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;"
@@ -255,7 +255,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.

On Fri, 2 Jun 2017 19:22:29 -0700 alison@peloton-tech.com wrote:
Significant changes since v2: -- Got rid of the need to allocate memory for the GUID string in do_gpt(); -- Fixed the problems with string NULL termination in allocate_disk_part(); -- Removed duplicate definition of MAX_SEARCH_PARTITIONS from disk/part.c and increased the value in include/part.h to 64; -- Improved the commit message for "rename GPT partitions to detect boot failure" to better describe the version of the patch I submitted; -- Fixed numerous small problems with function return values.
Just informative - (no need to resend patches) - for next submissions - please add changelog to each relevant patch.
In this way one can quickly refer to the code.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi Alison,
From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Acked-by: Lukasz Majewski lukma@denx.de
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 30 +++++++++++++++++++++++++++++- disk/part_efi.c | 31 +++++++++++++++++++++++++++++++ doc/README.gpt | 3 ++- include/part.h | 15 +++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3e98821..3b7d929 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -398,6 +398,23 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; }
+static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) +{
- int ret;
- char disk_guid[UUID_STR_LEN + 1];
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return 1;
- if (namestr)
setenv(namestr, disk_guid);
- else
printf("%s\n", disk_guid);
- return 0;
+}
/**
- do_gpt(): Perform GPT operations
@@ -412,7 +429,7 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = CMD_RET_SUCCESS; int dev = 0;
- char *ep;
char *ep, *varname = NULL; struct blk_desc *blk_dev_desc = NULL;
if (argc < 4 || argc > 5)
@@ -436,6 +453,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: ");
- } else if (strcmp(argv[1], "guid") == 0) {
if (argc == 5)
strcpy(varname, argv[4]);
} else { return CMD_RET_USAGE; }return do_disk_guid(blk_dev_desc, varname);
@@ -458,4 +479,11 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " guid <interface> <dev>\n"
- " - print disk GUID\n"
- " guid <interface> <dev> <varname>\n"
- " - set environment variable to disk GUID\n"
- " Example usage:\n"
- " gpt guid mmc 0\n"
- " gpt guid mmc 0 varname\n"
); diff --git a/disk/part_efi.c b/disk/part_efi.c index 20d33ef..ff9f408 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -178,6 +178,37 @@ static void prepare_backup_gpt_header(gpt_header *gpt_h)
- Public Functions (include/part.h)
*/
+/*
- UUID is displayed as 32 hexadecimal digits, in 5 groups,
- separated by hyphens, in the form 8-4-4-4-12 for a total of 36
characters
- */
+int get_disk_guid(struct blk_desc * dev_desc, char *guid) +{
- ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
dev_desc->blksz);
- gpt_entry *gpt_pte = NULL;
- unsigned char *guid_bin;
- /* This function validates AND fills in the GPT header and
PTE */
- if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
gpt_head, &gpt_pte) != 1) {
printf("%s: *** ERROR: Invalid GPT ***\n", __func__);
if (is_gpt_valid(dev_desc, (dev_desc->lba - 1),
gpt_head, &gpt_pte) != 1) {
printf("%s: *** ERROR: Invalid Backup GPT
***\n",
__func__);
return -1;
} else {
printf("%s: *** Using Backup GPT
***\n",
__func__);
}
- }
- guid_bin = (unsigned char *)(gpt_head->disk_guid.b);
- uuid_bin_to_str(guid_bin, guid, UUID_STR_FORMAT_GUID);
- return 0;
+}
void part_print_efi(struct blk_desc *dev_desc) { ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, dev_desc->blksz); diff --git a/doc/README.gpt b/doc/README.gpt index 3fcd835..c0779a4 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -171,7 +171,8 @@ To restore GUID partition table one needs to: The fields 'uuid' and 'uuid_disk' are optional if CONFIG_RANDOM_UUID is enabled. A random uuid will be used if omitted or they point to an empty/ non-existent environment variable. The environment variable will be set to
- the generated UUID.
- the generated UUID. The 'gpt guid' command reads the current
value of the
uuid_disk from the GPT.
The field 'bootable' is optional, it is used to mark the GPT
partition bootable (set attribute flags "Legacy BIOS bootable"). diff --git a/include/part.h b/include/part.h index 87b1111..16c4a46 100644 --- a/include/part.h +++ b/include/part.h @@ -371,6 +371,21 @@ int gpt_verify_headers(struct blk_desc *dev_desc, gpt_header *gpt_head, int gpt_verify_partitions(struct blk_desc *dev_desc, disk_partition_t *partitions, int parts, gpt_header *gpt_head, gpt_entry **gpt_pte);
+/**
- get_disk_guid() - Function to read the GUID string from a
device's GPT
- This function reads the GUID string from a block device whose
descriptor
- is provided.
- @param dev_desc - block device descriptor
- @param guid - pre-allocated string in which to return the GUID
- @return - '0' on success, otherwise error
- */
+int get_disk_guid(struct blk_desc *dev_desc, char *guid);
#endif
#if CONFIG_IS_ENABLED(DOS_PARTITION)
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi,
On Wed, 31 May 2017 09:44:43 +0200 Lukasz Majewski wrote:
Hi Alison,
From: Alison Chaiken alison@she-devel.com
In order to read the GPT, modify the partition name strings, and then write out a new GPT, the disk GUID is needed. While there is an existing accessor for the partition UUIDs, there is none yet for the disk GUID.
Acked-by: Lukasz Majewski lukma@denx.de
Did you read my comments on this patch in: 20170530084651.545d9954@ipc1.ka-ro e.g.: |> + if (argc == 5) |> + strcpy(varname, argv[4]); |> |You didn't allocate any memory for varname and copy the argument string |to a NULL pointer! |Maybe use strdup() and free varname after use?
Lothar Waßmann

From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 8 +++++ 2 files changed, 120 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3b7d929..c61d2b1 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = (struct disk_part *)malloc(sizeof(*newpart)); + memset(newpart, '\0', sizeof(newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, PART_NAME_LEN); + memset(newpart->gpt_part_info.name + 31, '\0', 1); + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, PART_TYPE_LEN); + memset(newpart->gpt_part_info.type + 31, '\0', 1); + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as u-boot does */ + int valid_parts = 1, p, ret = 0; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part) && (valid_parts >= 2)) + return -1; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + del_gpt_info(); + return -ENODEV; + } + return --valid_parts; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + } + return ret; +} + + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -457,6 +565,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc == 5) strcpy(varname, argv[4]); return do_disk_guid(blk_dev_desc, varname); + } else if (strcmp(argv[1], "read") == 0) { + return do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -479,6 +589,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index 16c4a46..7d7052a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -49,6 +50,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 16
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ @@ -68,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 8 +++++ 2 files changed, 120 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3b7d929..c61d2b1 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h>
+static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
empty line between declarations and code?
curr = list_entry(pos->next, struct disk_part, list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = (struct disk_part *)malloc(sizeof(*newpart));
useless type cast. malloc returns a void pointer which can be assigned to any typed pointer without a cast.
- memset(newpart, '\0', sizeof(newpart));
- if (!newpart)
This NULL check should be done before the memset()!
return ERR_PTR(-ENOMEM);
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, PART_NAME_LEN);
- memset(newpart->gpt_part_info.name + 31, '\0', 1);
newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; 1. No need for memset here. 2. You are copying PART_NAME_LEN bytes, but set the byte at pos 31 to '\0'. => This code will blow up if PART_NAME_LEN is changed to a different value!
- strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, PART_TYPE_LEN);
- memset(newpart->gpt_part_info.type + 31, '\0', 1);
dto.
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
UUID_STR_LEN);
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as u-boot does */
s/u-boot/U-Boot/
- int valid_parts = 1, p, ret = 0;
No need to initialize ret here (no need to declare it here either).
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info, valid_parts);
if (IS_ERR(new_disk_part) && (valid_parts >= 2))
No need for () around the '>=' expression.
return -1;
You return -ENODEV lateron, so you should return a valid errno value here. -1 (-EPERM) is most probably not the error code you want to use here.
list_add_tail(&new_disk_part->list, &disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -ENODEV;
- }
- return --valid_parts;
+}
+/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{
- int ret;
- ret = get_gpt_info(dev_desc);
- if (ret > 0) {
print_gpt_info();
del_gpt_info();
- }
- return ret;
Since the return value of this function is passed on as the exit code of the calling CMD handler it should be one of CMD_RET_SUCCESS or CMD_RET_FAILURE: return ret ? CMD_RET_SUCCESS : CMD_RET_FAILURE; But see my comment below!
/**
- set_gpt_info(): Fill partition information from string
function allocates memory, remember to free!
@@ -457,6 +565,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc == 5) strcpy(varname, argv[4]); return do_disk_guid(blk_dev_desc, varname);
- } else if (strcmp(argv[1], "read") == 0) {
return do_get_gpt_info(blk_dev_desc);
If you have a careful look at the succeeding code in this function you would see, that it does not blindly pass on the return value of the functions called for each subcommand but does: | if (ret) { | printf("error!\n"); | return CMD_RET_FAILURE; | } | | printf("success!\n"); | return CMD_RET_SUCCESS;
So you should just assign the return value of do_disk_guid() and do_get_gpt_info() to the variable 'ret' and let the original code handle it.
} else { return CMD_RET_USAGE; } @@ -479,6 +589,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " read <interface> <dev>\n"
- " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n"
diff --git a/include/part.h b/include/part.h index 16c4a46..7d7052a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -49,6 +50,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 16
Why the hard limit to 16 partitions? A standard Android system will jump right in your face with this limit.
Lothar Waßmann

On 2017-05-30 00:37, Lothar Waßmann wrote: [ SNIP]
+#define MAX_SEARCH_PARTITIONS 16
Why the hard limit to 16 partitions? A standard Android system will jump right in your face with this limit.
Lothar, I'm working on making the other changes you suggested. Meanwhile, the answer to this question is that I copied the macro from a header file in 2015.07, which is the version for which I developed the patches. I saw no equivalent value in this latest release of Das U-Boot. Do you have a better suggestion? Is there a similar macro elsewhere that I failed to discover?
Thanks, Alison
--- Alison Chaiken alison@she-devel.com, 650-279-5600 http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis

"Chaiken, Alison" alison@she-devel.com wrote:
On 2017-05-30 00:37, Lothar Waßmann wrote: [ SNIP]
+#define MAX_SEARCH_PARTITIONS 16
Why the hard limit to 16 partitions? A standard Android system will jump right in your face with this limit.
Lothar, I'm working on making the other changes you suggested. Meanwhile, the answer to this question is that I copied the macro from a header file in 2015.07, which is the version for which I developed the patches. I saw no equivalent value in this latest release of Das U-Boot. Do you have a better suggestion? Is there a similar macro elsewhere that I failed to discover?
I'm just concerned, that the limit may be too low for a certain class of systems and see no reason why it shouldn't be set higher. There is no memory allocation involved that would consume more memory with a higher limit.
I'v seen, the macro is defined in disk/part.c. Maybe for consistency this should be defined in some header file and used in the original place and your patch.
Lothar Waßmann

Hi Alison,
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Acked-by: Lukasz Majewski lukma@denx.de
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 8 +++++ 2 files changed, 120 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3b7d929..c61d2b1 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h>
+static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
curr = list_entry(pos->next, struct disk_part, list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = (struct disk_part *)malloc(sizeof(*newpart));
- memset(newpart, '\0', sizeof(newpart));
- if (!newpart)
return ERR_PTR(-ENOMEM);
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char
*)info->name, PART_NAME_LEN);
- memset(newpart->gpt_part_info.name + 31, '\0', 1);
- strncpy((char *)newpart->gpt_part_info.type, (const char
*)info->type, PART_TYPE_LEN);
- memset(newpart->gpt_part_info.type + 31, '\0', 1);
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char
*)info->uuid,
UUID_STR_LEN);
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n",
(unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n",
curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n",
curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as u-boot does */
- int valid_parts = 1, p, ret = 0;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info,
valid_parts);
if (IS_ERR(new_disk_part) && (valid_parts >= 2))
return -1;
list_add_tail(&new_disk_part->list,
&disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -ENODEV;
- }
- return --valid_parts;
+}
+/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{
- int ret;
- ret = get_gpt_info(dev_desc);
- if (ret > 0) {
print_gpt_info();
del_gpt_info();
- }
- return ret;
+}
/**
- set_gpt_info(): Fill partition information from string
function allocates memory, remember to free!
@@ -457,6 +565,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc == 5) strcpy(varname, argv[4]); return do_disk_guid(blk_dev_desc, varname);
- } else if (strcmp(argv[1], "read") == 0) {
} else { return CMD_RET_USAGE; }return do_get_gpt_info(blk_dev_desc);
@@ -479,6 +589,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " read <interface> <dev>\n"
- " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n"
diff --git a/include/part.h b/include/part.h index 16c4a46..7d7052a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -49,6 +50,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 16
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ @@ -68,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part {
- int partnum;
- disk_partition_t gpt_part_info;
- struct list_head list;
+};
/* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi,
On Wed, 31 May 2017 09:48:45 +0200 Lukasz Majewski wrote:
Hi Alison,
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Acked-by: Lukasz Majewski lukma@denx.de
You definitely should read other peoples comments on patches before blindly Acking them!
Lothar Waßmann

On Wed, 31 May 2017 10:48:59 +0200 Lothar Waßmann LW@KARO-electronics.de wrote:
Hi,
On Wed, 31 May 2017 09:48:45 +0200 Lukasz Majewski wrote:
Hi Alison,
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Acked-by: Lukasz Majewski lukma@denx.de
You definitely should read other peoples comments on patches before blindly Acking them!
Your reply to the original thread (v1):
From: Lothar Waßmann LW@KARO-electronics.de To: alison@peloton-tech.com Cc: u-boot@lists.denx.de, alison@she-devel.com, trini@konsulko.com
I was not added to TO/CC.
And unfortunately (from the lack of time) - I don't have time to promptly reply/see all the responses to the ML.
Alison sent v2 on Mon (to which I was added to CC).
I've assumed that this was fixed for v2. Wasn't it?
Lothar Waßmann
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Lukasz Majewski lukma@denx.de wrote:
On Wed, 31 May 2017 10:48:59 +0200 Lothar Waßmann LW@KARO-electronics.de wrote:
Hi,
On Wed, 31 May 2017 09:48:45 +0200 Lukasz Majewski wrote:
Hi Alison,
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Acked-by: Lukasz Majewski lukma@denx.de
You definitely should read other peoples comments on patches before blindly Acking them!
Your reply to the original thread (v1):
From: Lothar Waßmann LW@KARO-electronics.de To: alison@peloton-tech.com Cc: u-boot@lists.denx.de, alison@she-devel.com, trini@konsulko.com
I was not added to TO/CC.
And unfortunately (from the lack of time) - I don't have time to promptly reply/see all the responses to the ML.
Alison sent v2 on Mon (to which I was added to CC).
I've assumed that this was fixed for v2. Wasn't it?
I replied to his v2 patch.
Lothar Waßmann

Hi Alison,
Hi Alison,
From: Alison Chaiken alison@she-devel.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Acked-by: Lukasz Majewski lukma@denx.de
Sorry, but I was to fast.
Please fix problems pointed out by Lothar.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 8 +++++ 2 files changed, 120 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 3b7d929..c61d2b1 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> +Lothar Waßmann LW@KARO-electronics.de +static LIST_HEAD(disk_partitions);
/**
- extract_env(): Expand env name from string format '&{env_name}'
@@ -151,6 +154,111 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{
- struct list_head *pos = &disk_partitions;
- struct disk_part *curr;
- while (!list_empty(pos)) {
curr = list_entry(pos->next, struct disk_part,
list);
list_del(pos->next);
free(curr);
- }
+}
+static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{
- struct disk_part *newpart;
- newpart = (struct disk_part *)malloc(sizeof(*newpart));
- memset(newpart, '\0', sizeof(newpart));
- if (!newpart)
return ERR_PTR(-ENOMEM);
- newpart->gpt_part_info.start = info->start;
- newpart->gpt_part_info.size = info->size;
- newpart->gpt_part_info.blksz = info->blksz;
- strncpy((char *)newpart->gpt_part_info.name, (const char
*)info->name, PART_NAME_LEN);
- memset(newpart->gpt_part_info.name + 31, '\0', 1);
- strncpy((char *)newpart->gpt_part_info.type, (const char
*)info->type, PART_TYPE_LEN);
- memset(newpart->gpt_part_info.type + 31, '\0', 1);
- newpart->gpt_part_info.bootable = info->bootable;
+#ifdef CONFIG_PARTITION_UUIDS
- strncpy(newpart->gpt_part_info.uuid, (const char
*)info->uuid,
UUID_STR_LEN);
+#endif
- newpart->partnum = partnum;
- return newpart;
+}
+static void print_gpt_info(void) +{
- struct list_head *pos;
- struct disk_part *curr;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n",
(unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n",
curr->gpt_part_info.blksz,
curr->gpt_part_info.name);
printf("Type %s, bootable %d\n",
curr->gpt_part_info.type,
curr->gpt_part_info.bootable);
+#ifdef CONFIG_PARTITION_UUIDS
printf("UUID %s\n", curr->gpt_part_info.uuid);
+#endif
printf("\n");
- }
+}
+/*
- read partition info into disk_partitions list where
- it can be printed or modified
- */
+static int get_gpt_info(struct blk_desc *dev_desc) +{
- /* start partition numbering at 1, as u-boot does */
- int valid_parts = 1, p, ret = 0;
- disk_partition_t info;
- struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
- for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
ret = part_get_info(dev_desc, p, &info);
if (ret)
continue;
new_disk_part = allocate_disk_part(&info,
valid_parts);
if (IS_ERR(new_disk_part) && (valid_parts >= 2))
return -1;
list_add_tail(&new_disk_part->list,
&disk_partitions);
valid_parts++;
- }
- if (!valid_parts) {
printf("** No valid partitions found **\n");
del_gpt_info();
return -ENODEV;
- }
- return --valid_parts;
+}
+/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{
- int ret;
- ret = get_gpt_info(dev_desc);
- if (ret > 0) {
print_gpt_info();
del_gpt_info();
- }
- return ret;
+}
/**
- set_gpt_info(): Fill partition information from string
function allocates memory, remember to free!
@@ -457,6 +565,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc == 5) strcpy(varname, argv[4]); return do_disk_guid(blk_dev_desc, varname);
- } else if (strcmp(argv[1], "read") == 0) {
} else { return CMD_RET_USAGE; }return do_get_gpt_info(blk_dev_desc);
@@ -479,6 +589,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n"
- " read <interface> <dev>\n"
- " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n"
diff --git a/include/part.h b/include/part.h index 16c4a46..7d7052a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -49,6 +50,7 @@ struct block_drvr {
#define PART_NAME_LEN 32 #define PART_TYPE_LEN 32 +#define MAX_SEARCH_PARTITIONS 16
typedef struct disk_partition { lbaint_t start; /* # of first block in partition */ @@ -68,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part {
- int partnum;
- disk_partition_t gpt_part_info;
- struct list_head list;
+};
/* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command which is enabled via a CONFIG_CMD_GPT_FLIP option.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it renames it failed’ and renames the ‘backup’ partition as ‘candidate’.
Logic: -- Partitions can go to ‘failed’ only from ‘candidate’ and only via u-boot. Partitions can go to ‘backup’ only from ‘primary’ and vice-versa, only via Linux. Partitions go to ‘candidate’ from ‘primary’ or ‘backup’ only via u-boot. Only system update software will rename 'failed' partitions.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 7 ++ cmd/gpt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 5ee52f6..a8f7716 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -575,6 +575,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP + bool "GPT flip-partitions command" + depends on CMD_GPT + help + Enables the 'gpt' command to write modified GPT partition + tables via the 'gpt flip' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index c61d2b1..6a0b70f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -190,16 +191,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -211,6 +229,85 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{ + /* + * prefatory string: + * doc/README.GPT, suggests that + * int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk="); + * is correct, but extract_val() expects "uuid_disk" first. + */ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* 17 because partstr in create_gpt_partitions_list() is 16 chars */ + partlistlen += numparts * (strlen("start=MiB,") + 17); + partlistlen += numparts * (strlen("size=MiB,") + 17); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + /* + * README.gpt specifies starting with "partitions=" like so: + * strcpy(partitions_list, "partitions=uuid_disk="); + * but that breaks extract_val, which doesn't skip over 'partitions='. + */ + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -222,8 +319,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -294,6 +394,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -523,6 +625,86 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return 0; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("Current partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel")) + strcpy((char *)curr->gpt_part_info.name, "candidate_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel")) + strcpy((char *)curr->gpt_part_info.name, "backup_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "backup_rootfs"); + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -567,6 +749,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, varname); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP + } else if (strcmp(argv[1], "flip") == 0) { + return do_flip_gpt_parts(blk_dev_desc); +#endif } else { return CMD_RET_USAGE; } @@ -598,4 +784,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_FLIP + "gpt partition-flip command\n" + "gpt flip <interface> <dev>\n" + " - exchange device's 'primary' and 'backup' partition names\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index c0779a4..e29b188 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,19 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read' and 'gpt flip' commands facilitate programmatic renaming of +partitions from bootscripts by generating and modifying the partitions +layout string. The code in gpt_flip() illustrates the case of +swapping 'primary' and 'backup' partitions via: + +U-BOOT> gpt flip mmc 0 + +Choose different partition names by modifying these strings in gpt.c.
Partition type GUID: ====================

alison@peloton-tech.com wrote:
From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command which is enabled via a CONFIG_CMD_GPT_FLIP option.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it renames it failed’ and renames the ‘backup’ partition as ‘candidate’.
Logic: -- Partitions can go to ‘failed’ only from ‘candidate’ and only via u-boot. Partitions can go to ‘backup’ only from ‘primary’ and vice-versa, only via Linux. Partitions go to ‘candidate’ from ‘primary’ or ‘backup’ only via u-boot. Only system update software will rename 'failed' partitions.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/Kconfig | 7 ++ cmd/gpt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 5ee52f6..a8f7716 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -575,6 +575,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP
- bool "GPT flip-partitions command"
- depends on CMD_GPT
- help
Enables the 'gpt' command to write modified GPT partition
tables via the 'gpt flip' command.
config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index c61d2b1..6a0b70f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -190,16 +191,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize,
unsigned long blksize)
+{
- unsigned long long partbytes;
- unsigned long partmegabytes;
- partbytes = partsize * blksize;
- partmegabytes = lldiv(partbytes, SZ_1M);
- snprintf(sizestr, 16, "%luMiB", partmegabytes);
+}
static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list);
prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type,printf("Start %s, size %s\n", partstartstr, partsizestr);
@@ -211,6 +229,85 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
*/
- int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
- /* for the comma */
- partlistlen++;
- /* per-partition additions; numparts starts at 1, so this should be correct */
- partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
- /* 17 because partstr in create_gpt_partitions_list() is 16 chars */
- partlistlen += numparts * (strlen("start=MiB,") + 17);
- partlistlen += numparts * (strlen("size=MiB,") + 17);
- partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
- /* for the terminating null */
- partlistlen++;
- debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
numparts);
- return partlistlen;
+}
+/*
- create the string that upstream 'gpt write' command will accept as an
- argument
- From doc/README.gpt, Format of partitions layout:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
- are optional if CONFIG_RANDOM_UUID is enabled.
- */
+static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{
- struct list_head *pos;
- struct disk_part *curr;
- char partstr[PART_NAME_LEN + 1];
- if (!partitions_list)
return -1;
- /*
* README.gpt specifies starting with "partitions=" like so:
* strcpy(partitions_list, "partitions=uuid_disk=");
* but that breaks extract_val, which doesn't skip over 'partitions='.
*/
- strcpy(partitions_list, "uuid_disk=");
- strncat(partitions_list, guid, UUID_STR_LEN + 1);
- strcat(partitions_list, ";");
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
strcat(partitions_list, "name=");
strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1);
strcat(partitions_list, ",start=");
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start,
(unsigned long) curr->gpt_part_info.blksz);
/* one extra byte for NULL */
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",size=");
/* lbaint_t is unsigned long, per include/ide.h */
prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size,
(unsigned long) curr->gpt_part_info.blksz);
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",uuid=");
strncat(partitions_list, (const char *)curr->gpt_part_info.uuid,
UUID_STR_LEN + 1);
strcat(partitions_list, ";");
- }
- return 0;
+} +#endif
/*
- read partition info into disk_partitions list where
- it can be printed or modified
@@ -222,8 +319,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
/*
* Always re-read partition info from device, in case
* it has changed
*/
INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info);
@@ -294,6 +394,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part);
if (str == NULL)
return -ENOMEM;
/* extract disk guid */ s = str;
@@ -523,6 +625,86 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return 0; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{
- struct list_head *pos;
- struct disk_part *curr;
- disk_partition_t *new_partitions = NULL;
- char disk_guid[UUID_STR_LEN + 1];
- char *partitions_list, *str_disk_guid;
- u8 part_count = 0;
- int partlistlen, ret, numparts = 0;
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return ret;
This function should return either CMD_RET_FAILURE or CMD_RET_SUCCESS, since the return value is passed on as the exit code of the calling CMD handler function!
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
see above.
- printf("Current partition table with %d partitions is:\n", numparts);
- print_gpt_info();
- partlistlen = calc_parts_list_len(numparts);
- partitions_list = (char *)malloc(partlistlen);
- memset(partitions_list, '\0', partlistlen);
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid,
partitions_list);
- if (ret < 0)
return ret;
see above.
- debug("OLD partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
see above.
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel"))
strcpy((char *)curr->gpt_part_info.name, "candidate_kernel");
if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel"))
strcpy((char *)curr->gpt_part_info.name, "backup_kernel");
if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs"))
strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs");
if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs"))
strcpy((char *)curr->gpt_part_info.name, "backup_rootfs");
- }
- ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list);
- if (ret < 0)
return ret;
see above.
- debug("NEW partitions_list is %s with %d chars\n", partitions_list, strlen(partitions_list));
- ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
see above.
- debug("Writing new partition table\n");
- ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
- if (ret < 0) {
printf("Writing new partition table failed\n");
return ret;
see above.
- }
- debug("Reading back new partition table\n");
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
see above.
- printf("new partition table with %d partitions is:\n", numparts);
- print_gpt_info();
- del_gpt_info();
- free(partitions_list);
- free(str_disk_guid);
- free(new_partitions);
- return ret;
see above.
+} +#endif
/**
- do_gpt(): Perform GPT operations
@@ -567,6 +749,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, varname); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP
- } else if (strcmp(argv[1], "flip") == 0) {
return do_flip_gpt_parts(blk_dev_desc);
Either make do_flip_gpt_part() return CMD_RET_... as indicated above, or check its return value here and convert it to CMD_RET_SUCCESS or CMD_RET_FAILURE.
Also see my comment to your patch 4/6 concerning this code location.
Lothar Waßmann

Hi Alison,
From: Alison Chaiken alison@she-devel.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command which is enabled via a CONFIG_CMD_GPT_FLIP option.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it renames it failed’ and renames the ‘backup’ partition as ‘candidate’.
Logic: -- Partitions can go to ‘failed’ only from ‘candidate’ and only via u-boot. Partitions can go to ‘backup’ only from ‘primary’ and vice-versa, only via Linux. Partitions go to ‘candidate’ from ‘primary’ or ‘backup’ only via u-boot. Only system update software will rename 'failed' partitions.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/Kconfig | 7 ++ cmd/gpt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 5ee52f6..a8f7716 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -575,6 +575,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP
- bool "GPT flip-partitions command"
- depends on CMD_GPT
- help
Enables the 'gpt' command to write modified GPT partition
tables via the 'gpt flip' command.
config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index c61d2b1..6a0b70f 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -190,16 +191,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize,
unsigned long blksize)
+{
- unsigned long long partbytes;
- unsigned long partmegabytes;
- partbytes = partsize * blksize;
- partmegabytes = lldiv(partbytes, SZ_1M);
- snprintf(sizestr, 16, "%luMiB", partmegabytes);
+}
static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list);
prettyprint_part_size(partstartstr, (unsigned
long)curr->gpt_part_info.start,
(unsigned long)
curr->gpt_part_info.blksz);
prettyprint_part_size(partsizestr, (unsigned
long)curr->gpt_part_info.size,
(unsigned long)
curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n",
(unsigned)curr->gpt_part_info.start,
(unsigned)curr->gpt_part_info.size);
printf("Start %s, size %s\n", partstartstr,
partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -211,6 +229,85 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 +
strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
*/
- int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
- /* for the comma */
- partlistlen++;
- /* per-partition additions; numparts starts at 1, so this
should be correct */
- partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN
- 1);
- /* 17 because partstr in create_gpt_partitions_list() is 16
chars */
- partlistlen += numparts * (strlen("start=MiB,") + 17);
- partlistlen += numparts * (strlen("size=MiB,") + 17);
- partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN +
1);
- /* for the terminating null */
- partlistlen++;
- debug("Length of partitions_list is %d for %d partitions\n",
partlistlen,
numparts);
- return partlistlen;
+}
+/*
- create the string that upstream 'gpt write' command will accept
as an
- argument
- From doc/README.gpt, Format of partitions layout:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
- are optional if CONFIG_RANDOM_UUID is enabled.
- */
+static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{
- struct list_head *pos;
- struct disk_part *curr;
- char partstr[PART_NAME_LEN + 1];
- if (!partitions_list)
return -1;
- /*
* README.gpt specifies starting with "partitions=" like so:
* strcpy(partitions_list, "partitions=uuid_disk=");
* but that breaks extract_val, which doesn't skip over
'partitions='.
*/
- strcpy(partitions_list, "uuid_disk=");
- strncat(partitions_list, guid, UUID_STR_LEN + 1);
- strcat(partitions_list, ";");
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
strcat(partitions_list, "name=");
strncat(partitions_list, (const char
*)curr->gpt_part_info.name, PART_NAME_LEN + 1);
strcat(partitions_list, ",start=");
prettyprint_part_size(partstr, (unsigned
long)curr->gpt_part_info.start,
(unsigned long)
curr->gpt_part_info.blksz);
/* one extra byte for NULL */
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",size=");
/* lbaint_t is unsigned long, per include/ide.h */
prettyprint_part_size(partstr, (unsigned
long)curr->gpt_part_info.size,
(unsigned long)
curr->gpt_part_info.blksz);
strncat(partitions_list, partstr, PART_NAME_LEN + 1);
strcat(partitions_list, ",uuid=");
strncat(partitions_list, (const char
*)curr->gpt_part_info.uuid,
UUID_STR_LEN + 1);
strcat(partitions_list, ";");
- }
- return 0;
+} +#endif
/*
- read partition info into disk_partitions list where
- it can be printed or modified
@@ -222,8 +319,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL)
INIT_LIST_HEAD(&disk_partitions);
/*
* Always re-read partition info from device, in case
* it has changed
*/
INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info);
@@ -294,6 +394,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part);
if (str == NULL)
return -ENOMEM;
/* extract disk guid */ s = str;
@@ -523,6 +625,86 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return 0; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{
- struct list_head *pos;
- struct disk_part *curr;
- disk_partition_t *new_partitions = NULL;
- char disk_guid[UUID_STR_LEN + 1];
- char *partitions_list, *str_disk_guid;
- u8 part_count = 0;
- int partlistlen, ret, numparts = 0;
- ret = get_disk_guid(dev_desc, disk_guid);
- if (ret < 0)
return ret;
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
- printf("Current partition table with %d partitions is:\n",
numparts);
- print_gpt_info();
- partlistlen = calc_parts_list_len(numparts);
- partitions_list = (char *)malloc(partlistlen);
- memset(partitions_list, '\0', partlistlen);
- ret = create_gpt_partitions_list(numparts, (const char *)
disk_guid,
partitions_list);
- if (ret < 0)
return ret;
- debug("OLD partitions_list is %s with %d chars\n",
partitions_list, strlen(partitions_list)); +
- ret = set_gpt_info(dev_desc, (const char *)partitions_list,
&str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
- list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list);
if (!strcmp((char *)curr->gpt_part_info.name,
"backup_kernel"))
strcpy((char *)curr->gpt_part_info.name,
"candidate_kernel");
if (!strcmp((char *)curr->gpt_part_info.name,
"primary_kernel"))
strcpy((char *)curr->gpt_part_info.name,
"backup_kernel");
if (!strcmp((char *)curr->gpt_part_info.name,
"backup_rootfs"))
strcpy((char *)curr->gpt_part_info.name,
"candidate_rootfs");
if (!strcmp((char *)curr->gpt_part_info.name,
"primary_rootfs"))
strcpy((char *)curr->gpt_part_info.name,
"backup_rootfs");
- }
- ret = create_gpt_partitions_list(numparts, (const char *)
disk_guid, partitions_list);
- if (ret < 0)
return ret;
- debug("NEW partitions_list is %s with %d chars\n",
partitions_list, strlen(partitions_list)); +
- ret = set_gpt_info(dev_desc, (const char *)partitions_list,
&str_disk_guid,
&new_partitions, &part_count);
- if (ret < 0)
return ret;
- debug("Writing new partition table\n");
- ret = gpt_restore(dev_desc, disk_guid, new_partitions,
numparts);
- if (ret < 0) {
printf("Writing new partition table failed\n");
return ret;
- }
- debug("Reading back new partition table\n");
- numparts = get_gpt_info(dev_desc);
- if (numparts < 0)
return numparts;
- printf("new partition table with %d partitions is:\n",
numparts);
- print_gpt_info();
- del_gpt_info();
- free(partitions_list);
- free(str_disk_guid);
- free(new_partitions);
- return ret;
+} +#endif
/**
- do_gpt(): Perform GPT operations
@@ -567,6 +749,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, varname); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP
- } else if (strcmp(argv[1], "flip") == 0) {
return do_flip_gpt_parts(blk_dev_desc);
+#endif } else { return CMD_RET_USAGE; } @@ -598,4 +784,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_FLIP
- "gpt partition-flip command\n"
- "gpt flip <interface> <dev>\n"
- " - exchange device's 'primary' and 'backup' partition
names\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index c0779a4..e29b188 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,19 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +====================================
+GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read' and 'gpt flip' commands facilitate programmatic renaming of +partitions from bootscripts by generating and modifying the partitions +layout string. The code in gpt_flip() illustrates the case of +swapping 'primary' and 'backup' partitions via:
+U-BOOT> gpt flip mmc 0
Maybe it would be better to have
gpt flip mmc 0 <optional parameter 'name'>
(By default we have "primary" and "backup")
In that way we could set other names to GPT partitions without the need to modify the code.
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
Then you can 'mount' some gpt test image ('host' command) and use it with: gpt <command> host X .....
Despite above comments - you did a great job :-)
Reviewed-by: Lukasz Majewski lukma@denx.de
+Choose different partition names by modifying these strings in gpt.c.
Partition type GUID:
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

On 2017-05-31 01:12, Lukasz Majewski wrote:
[ SNIP ]
Maybe it would be better to have
gpt flip mmc 0 <optional parameter 'name'>
(By default we have "primary" and "backup")
In that way we could set other names to GPT partitions without the need to modify the code.
Another possibility is to support
gpt flip mmc 0 <name 1> <name 2>
where two names are required, with defaults 'primary' and 'backup'. Perhaps I should rework patch 5 that way? I considered this form for the original submission, but wasn't sure what other projects might find useful.
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
I will look into adding another patch to do that. I'm sure such tests are a good idea for our project.
Despite above comments - you did a great job :-)
Thanks. I'm sorry to have omitted you, Lukasz, from the original submission, but I consulted an old version of MAINTAINERS from my original 2015.07.
-- Alison
--- Alison Chaiken alison@she-devel.com, 650-279-5600 http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis

Hi Alison,
On 2017-05-31 01:12, Lukasz Majewski wrote:
[ SNIP ]
Maybe it would be better to have
gpt flip mmc 0 <optional parameter 'name'>
(By default we have "primary" and "backup")
In that way we could set other names to GPT partitions without the need to modify the code.
Another possibility is to support
gpt flip mmc 0 <name 1> <name 2>
where two names are required, with defaults 'primary' and 'backup'. Perhaps I should rework patch 5 that way?
Please correct my understanding if I'm wrong.
You parse GPT partitions to a list and there are only two names possible:
"primary" and "backup".
When we call gpt filp we rename those names ("primary" -> "backup" and the other way).
Ahhh..... so your patch would allow to rename "primary" to <name1> and "backup" to <name2> for all names.
My idea was rather to have a gpt call to set name of a GPT partition....
I considered this form for the original submission, but wasn't sure what other projects might find useful.
I can image that somebody would like to change on the fly single GPT partition name (when having/care about 2 partitions for his/her project specific data).
But, I don't know how much effort it would take to implement it (and if others would see it beneficial).
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
I will look into adding another patch to do that. I'm sure such tests are a good idea for our project.
It is relatively easy to use sandbox to test some high level stuff (like file systems, gpt, etc). One don't need HW for it.
Despite above comments - you did a great job :-)
Thanks. I'm sorry to have omitted you, Lukasz, from the original submission, but I consulted an old version of MAINTAINERS from my original 2015.07.
It's Ok, don't worry. Things like that do happen....
-- Alison
Alison Chaiken alison@she-devel.com, 650-279-5600 http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Lukasz Majewski wrote:
Maybe it would be better to have
gpt flip mmc 0 <optional parameter 'name'>
(By default we have "primary" and "backup")
In that way we could set other names to GPT partitions without the need to modify the code.
I answered:
Another possibility is to support
gpt flip mmc 0 <name 1> <name 2>
where two names are required, with defaults 'primary' and 'backup'. Perhaps I should rework patch 5 that way?
Lukasz responded:
Please correct my understanding if I'm wrong.
You parse GPT partitions to a list and there are only two names possible:
"primary" and "backup".
When we call gpt filp we rename those names ("primary" -> "backup" and the other way).
Ahhh..... so your patch would allow to rename "primary" to <name1> and "backup" to <name2> for all names.
My idea was rather to have a gpt call to set name of a GPT partition....
I can image that somebody would like to change on the fly single GPT partition name (when having/care about 2 partitions for his/her project specific data).
But, I don't know how much effort it would take to implement it (and if others would see it beneficial).
I would propose then two options:
gpt rename mmc 0 <name 1> <name 2>
which renames all the 1's to 2's, and
gpt flip mmc 0 <name 1> <name 2>
which swaps the two name strings for all partitions where they're found. These two operations together with 'gpt write' then cover all the common use cases I can imagine.
I'm open to any other suggestions, of course. I just posted what I already had to get started.
-- Alison
--- Alison Chaiken alison@she-devel.com, 650-279-5600 http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis

Hi Alison,
Lukasz Majewski wrote:
Maybe it would be better to have
gpt flip mmc 0 <optional parameter 'name'>
(By default we have "primary" and "backup")
In that way we could set other names to GPT partitions without the need to modify the code.
I answered:
Another possibility is to support
gpt flip mmc 0 <name 1> <name 2>
where two names are required, with defaults 'primary' and 'backup'. Perhaps I should rework patch 5 that way?
Lukasz responded:
Please correct my understanding if I'm wrong.
You parse GPT partitions to a list and there are only two names possible:
"primary" and "backup".
When we call gpt filp we rename those names ("primary" -> "backup" and the other way).
Ahhh..... so your patch would allow to rename "primary" to <name1> and "backup" to <name2> for all names.
My idea was rather to have a gpt call to set name of a GPT partition....
I can image that somebody would like to change on the fly single GPT partition name (when having/care about 2 partitions for his/her project specific data).
But, I don't know how much effort it would take to implement it (and if others would see it beneficial).
I would propose then two options:
gpt rename mmc 0 <name 1> <name 2>
which renames all the 1's to 2's, and
gpt flip mmc 0 <name 1> <name 2>
which swaps the two name strings for all partitions where they're found. These two operations together with 'gpt write' then cover all the common use cases I can imagine.
I think that this is enough. Let's wait for other's opinions (if any).
I'm open to any other suggestions, of course. I just posted what I already had to get started.
-- Alison
Alison Chaiken alison@she-devel.com, 650-279-5600 http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

From: Alison Chaiken alison@peloton-tech.com
One way for userspace and the bootloader to exchange information about dynamic image selection is via the storage device partition table, as described at
https://source.android.com/devices/tech/ota/ab_updates
The scheme described there relies on setting partitions' "boot" flag. When no partition on a device is bootable since the kernel and U-Boot are stored elsewhere, the name field in the GPT partition table offers another logical place to store information. These patches allow users to easily modify GPT partition names via bootscripts that can select different images based on a boot-failure counter, or when userspace installs a software update.
These patches have been tested in the u-boot sandbox and (re)tested on a TI DRA7xx-based SOM with U-Boot 2015.07.
Significant changes since v3: -- Testing in sandbox showed error in NULL termination of UUID string in cmd/gpt.c. That problem is fixed in 0001-GPT-read-partition-table-from-device-into-a-data-str.patch. -- Patch 3 "GPT: fix error in partitions string doc" now only treats the topic in the commit title, as the part dealing with the proposed 'flip' feature is moved to that patch. -- Two new commits come from testing in the sandbox (of which I was previously unaware). One fixes an error in README.sandbox, and one adds a few comments about sandbox testing to README.gpt.
Significant changes since v2: -- Got rid of the need to allocate memory for the GUID string in do_gpt(); -- Fixed the problems with string NULL termination in allocate_disk_part(); -- Removed duplicate definition of MAX_SEARCH_PARTITIONS from disk/part.c and increased the value in include/part.h to 64; -- Improved the commit message for "rename GPT partitions to detect boot failure" to better describe the version of the patch I submitted; -- Fixed numerous small problems with function return values.
Significant changes since v1: -- Put the gpt_flip() function and auxiliary ones inside a new CONFIG_CMD_GPT_FLIP option that depends on CMD_GPT. -- Replace intentional overwriting of name and type string arrays with memset() instead. -- Move part.h changes earlier in the patchset. -- Add a few lines to README.gpt about the new gpt subcommands.
Added a few simple patches to do the following: -- Replace remaining occurrences of '37' with UUID_STR_LEN+1; -- Introduce new macros to get rid of the similar '32'; -- fix a smaller error in doc/README.gpt.
There are also some fixups of whitespace and formatting errors (plus usual inevitable addition of new ones).
To do in future: -- Add support for preserving the type flag for partitions. The u-boot version on which this patchset is based did not have this feature, and it's easy to add, but I need to figure how to test it first.
-- Add tests for the new gpt commands to the sandbox.
Alison Chaiken (5): GPT: read partition table from device into a data structure rename GPT partitions to detect boot failure GPT: fix error in partitions string doc sandbox: README: fix partition command invocation cmd gpt: test in sandbox
board/sandbox/README.sandbox | 2 +- cmd/Kconfig | 7 + cmd/gpt.c | 298 +++++++++++++++++++++++++++++++++++++++++++ doc/README.gpt | 31 ++++- include/part.h | 7 + 5 files changed, 340 insertions(+), 5 deletions(-)

From: Alison Chaiken alison@peloton-tech.com
Make the partition table available for modification by reading it from the user-specified device into a linked list. Provide an accessor function for command-line testing.
Changes since v3: Fixed problem with NULL termination of the newly created uuid[] member of the partitions list in allocate_disk_part. This version has been tested in the u-boot sandbox.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 7 ++++ 2 files changed, 123 insertions(+)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 4d00a35..e898f35 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -19,6 +19,9 @@ #include <linux/ctype.h> #include <div64.h> #include <memalign.h> +#include <linux/compat.h> + +static LIST_HEAD(disk_partitions);
/** * extract_env(): Expand env name from string format '&{env_name}' @@ -151,6 +154,115 @@ static bool found_key(const char *str, const char *key) return result; }
+static void del_gpt_info(void) +{ + struct list_head *pos = &disk_partitions; + struct disk_part *curr; + while (!list_empty(pos)) { + curr = list_entry(pos->next, struct disk_part, list); + list_del(pos->next); + free(curr); + } +} + +static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) +{ + struct disk_part *newpart; + newpart = (struct disk_part *)malloc(sizeof(*newpart)); + if (!newpart) + return ERR_PTR(-ENOMEM); + memset(newpart, '\0', sizeof(newpart)); + + newpart->gpt_part_info.start = info->start; + newpart->gpt_part_info.size = info->size; + newpart->gpt_part_info.blksz = info->blksz; + strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, + PART_NAME_LEN); + newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; + strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, + PART_TYPE_LEN); + newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; + newpart->gpt_part_info.bootable = info->bootable; +#ifdef CONFIG_PARTITION_UUIDS + strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid, + UUID_STR_LEN); + /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */ + newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0'; +#endif + newpart->partnum = partnum; + + return newpart; +} + +static void print_gpt_info(void) +{ + struct list_head *pos; + struct disk_part *curr; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + printf("Partition %d:\n", curr->partnum); + printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, + (unsigned)curr->gpt_part_info.size); + printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, + curr->gpt_part_info.name); + printf("Type %s, bootable %d\n", curr->gpt_part_info.type, + curr->gpt_part_info.bootable); +#ifdef CONFIG_PARTITION_UUIDS + printf("UUID %s\n", curr->gpt_part_info.uuid); +#endif + printf("\n"); + } +} + +/* + * read partition info into disk_partitions list where + * it can be printed or modified + */ +static int get_gpt_info(struct blk_desc *dev_desc) +{ + /* start partition numbering at 1, as U-Boot does */ + int valid_parts = 1, p, ret; + disk_partition_t info; + struct disk_part *new_disk_part; + + if (disk_partitions.next == NULL) + INIT_LIST_HEAD(&disk_partitions); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + ret = part_get_info(dev_desc, p, &info); + if (ret) + continue; + + new_disk_part = allocate_disk_part(&info, valid_parts); + if (IS_ERR(new_disk_part) && valid_parts >= 2) + return -ENODEV; + + list_add_tail(&new_disk_part->list, &disk_partitions); + valid_parts++; + } + if (!valid_parts) { + printf("** No valid partitions found **\n"); + del_gpt_info(); + return -ENODEV; + } + return --valid_parts; +} + +/* a wrapper to test get_gpt_info */ +static int do_get_gpt_info(struct blk_desc *dev_desc) +{ + int ret; + + ret = get_gpt_info(dev_desc); + if (ret > 0) { + print_gpt_info(); + del_gpt_info(); + } + return ret; +} + + /** * set_gpt_info(): Fill partition information from string * function allocates memory, remember to free! @@ -455,6 +567,8 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("Verify GPT: "); } else if (strcmp(argv[1], "guid") == 0) { return do_disk_guid(blk_dev_desc, argv[4]); + } else if (strcmp(argv[1], "read") == 0) { + return do_get_gpt_info(blk_dev_desc); } else { return CMD_RET_USAGE; } @@ -477,6 +591,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt write mmc 0 $partitions\n" " gpt verify mmc 0 $partitions\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " guid <interface> <dev>\n" " - print disk GUID\n" " guid <interface> <dev> <varname>\n" diff --git a/include/part.h b/include/part.h index c41aa6a..0cd803a 100644 --- a/include/part.h +++ b/include/part.h @@ -10,6 +10,7 @@ #include <blk.h> #include <ide.h> #include <uuid.h> +#include <linux/list.h>
struct block_drvr { char *name; @@ -69,6 +70,12 @@ typedef struct disk_partition { #endif } disk_partition_t;
+struct disk_part { + int partnum; + disk_partition_t gpt_part_info; + struct list_head list; +}; + /* Misc _get_dev functions */ #ifdef CONFIG_PARTITIONS /**

From: Alison Chaiken alison@peloton-tech.com
This patch provides support in u-boot for renaming GPT partitions. The renaming is accomplished via a new 'gpt flip' command.
The concept for the bootloader state machine is the following:
-- u-boot renames ‘primary’ partitions as ‘candidate’ and tries to boot them. -- Linux, at boot, will rename ‘candidate’ partitions as ‘primary’. -- If u-boot sees a ‘candidate’ partition after a boot attempt, it tries to boot the ‘backup’ partition.
Rewriting the partition table has the side-effect that all partitions end up with "msftdata" flag set. The reason is that partition type PARTITION_BASIC_DATA_GUID is hard-coded in the gpt_fill_pte() function. This does not appear to cause any harm.
Changes since v3: -- Squashed changes to comments that had been made in a different patch into this one. -- Fixed lines over 80 chars. -- Tested in u-boot sandbox.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/Kconfig | 7 +++ cmd/gpt.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- doc/README.gpt | 13 ++++ 3 files changed, 206 insertions(+), 4 deletions(-)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 6f75b86..8b925e5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -593,6 +593,13 @@ config CMD_GPT Enable the 'gpt' command to ready and write GPT style partition tables.
+config CMD_GPT_FLIP + bool "GPT flip-partitions command" + depends on CMD_GPT + help + Enables the 'gpt' command to write modified GPT partition + tables via the 'gpt flip' command. + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/gpt.c b/cmd/gpt.c index e898f35..f806492 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -20,6 +20,7 @@ #include <div64.h> #include <memalign.h> #include <linux/compat.h> +#include <linux/sizes.h>
static LIST_HEAD(disk_partitions);
@@ -194,16 +195,33 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum) return newpart; }
+static void prettyprint_part_size(char *sizestr, unsigned long partsize, + unsigned long blksize) +{ + unsigned long long partbytes; + unsigned long partmegabytes; + + partbytes = partsize * blksize; + partmegabytes = lldiv(partbytes, SZ_1M); + snprintf(sizestr, 16, "%luMiB", partmegabytes); +} + static void print_gpt_info(void) { struct list_head *pos; struct disk_part *curr; + char partstartstr[16]; + char partsizestr[16];
list_for_each(pos, &disk_partitions) { curr = list_entry(pos, struct disk_part, list); + prettyprint_part_size(partstartstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + prettyprint_part_size(partsizestr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + printf("Partition %d:\n", curr->partnum); - printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, - (unsigned)curr->gpt_part_info.size); + printf("Start %s, size %s\n", partstartstr, partsizestr); printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, @@ -215,6 +233,74 @@ static void print_gpt_info(void) } }
+#ifdef CONFIG_CMD_GPT_FLIP +static int calc_parts_list_len(int numparts) +{ + int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); + /* for the comma */ + partlistlen++; + + /* per-partition additions; numparts starts at 1, so this should be correct */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* 17 because partstr in create_gpt_partitions_list() is 16 chars */ + partlistlen += numparts * (strlen("start=MiB,") + 17); + partlistlen += numparts * (strlen("size=MiB,") + 17); + partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1); + /* for the terminating null */ + partlistlen++; + debug("Length of partitions_list is %d for %d partitions\n", partlistlen, + numparts); + return partlistlen; +} + +/* + * create the string that upstream 'gpt write' command will accept as an + * argument + * + * From doc/README.gpt, Format of partitions layout: + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * name=kernel,size=60MiB,uuid=...;" + * The fields 'name' and 'size' are mandatory for every partition. + * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' + * are optional if CONFIG_RANDOM_UUID is enabled. + */ +static int create_gpt_partitions_list(int numparts, const char *guid, char *partitions_list) +{ + struct list_head *pos; + struct disk_part *curr; + char partstr[PART_NAME_LEN + 1]; + + if (!partitions_list) + return -1; + + strcpy(partitions_list, "uuid_disk="); + strncat(partitions_list, guid, UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + strcat(partitions_list, "name="); + strncat(partitions_list, (const char *)curr->gpt_part_info.name, PART_NAME_LEN + 1); + strcat(partitions_list, ",start="); + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.start, + (unsigned long) curr->gpt_part_info.blksz); + /* one extra byte for NULL */ + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + strcat(partitions_list, ",size="); + /* lbaint_t is unsigned long, per include/ide.h */ + prettyprint_part_size(partstr, (unsigned long)curr->gpt_part_info.size, + (unsigned long) curr->gpt_part_info.blksz); + strncat(partitions_list, partstr, PART_NAME_LEN + 1); + + strcat(partitions_list, ",uuid="); + strncat(partitions_list, (const char *)curr->gpt_part_info.uuid, + UUID_STR_LEN + 1); + strcat(partitions_list, ";"); + } + return 0; +} +#endif + /* * read partition info into disk_partitions list where * it can be printed or modified @@ -226,8 +312,11 @@ static int get_gpt_info(struct blk_desc *dev_desc) disk_partition_t info; struct disk_part *new_disk_part;
- if (disk_partitions.next == NULL) - INIT_LIST_HEAD(&disk_partitions); + /* + * Always re-read partition info from device, in case + * it has changed + */ + INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { ret = part_get_info(dev_desc, p, &info); @@ -298,6 +387,8 @@ static int set_gpt_info(struct blk_desc *dev_desc, return -1;
str = strdup(str_part); + if (str == NULL) + return -ENOMEM;
/* extract disk guid */ s = str; @@ -527,6 +618,88 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) return ret; }
+#ifdef CONFIG_CMD_GPT_FLIP +static int do_flip_gpt_parts(struct blk_desc *dev_desc) +{ + struct list_head *pos; + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list, *str_disk_guid; + u8 part_count = 0; + int partlistlen, ret, numparts = 0; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("Current partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + partlistlen = calc_parts_list_len(numparts); + partitions_list = (char *)malloc(partlistlen); + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, + partitions_list); + if (ret < 0) + return ret; + debug("OLD partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_kernel")) + strcpy((char *)curr->gpt_part_info.name, "candidate_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_kernel")) + strcpy((char *)curr->gpt_part_info.name, "backup_kernel"); + if (!strcmp((char *)curr->gpt_part_info.name, "backup_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "candidate_rootfs"); + if (!strcmp((char *)curr->gpt_part_info.name, "primary_rootfs")) + strcpy((char *)curr->gpt_part_info.name, "backup_rootfs"); + } + + ret = create_gpt_partitions_list(numparts, (const char *) disk_guid, partitions_list); + if (ret < 0) + return ret; + debug("NEW partitions_list is %s with %u chars\n", partitions_list, + (unsigned)strlen(partitions_list)); + + ret = set_gpt_info(dev_desc, (const char *)partitions_list, &str_disk_guid, + &new_partitions, &part_count); + if (ret < 0) + return ret; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); + if (ret < 0) { + printf("Writing new partition table failed\n"); + return ret; + } + + debug("Reading back new partition table\n"); + numparts = get_gpt_info(dev_desc); + if (numparts < 0) + return numparts; + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); + + del_gpt_info(); + free(partitions_list); + free(str_disk_guid); + free(new_partitions); + return ret; +} +#endif + /** * do_gpt(): Perform GPT operations * @@ -569,6 +742,10 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_disk_guid(blk_dev_desc, argv[4]); } else if (strcmp(argv[1], "read") == 0) { return do_get_gpt_info(blk_dev_desc); +#ifdef CONFIG_CMD_GPT_FLIP + } else if (strcmp(argv[1], "flip") == 0) { + return do_flip_gpt_parts(blk_dev_desc); +#endif } else { return CMD_RET_USAGE; } @@ -600,4 +777,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " Example usage:\n" " gpt guid mmc 0\n" " gpt guid mmc 0 varname\n" +#ifdef CONFIG_CMD_GPT_FLIP + "gpt partition-flip command\n" + "gpt flip <interface> <dev>\n" + " - exchange device's 'primary' and 'backup' partition names\n" +#endif ); diff --git a/doc/README.gpt b/doc/README.gpt index c0779a4..e29b188 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -210,6 +210,19 @@ Following line can be used to assess if GPT verification has succeed: U-BOOT> gpt verify mmc 0 $partitions U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi
+Renaming GPT partitions from U-Boot: +==================================== + +GPT partition names are a mechanism via which userspace and U-Boot can +communicate about software updates and boot failure. The 'gpt guid', +'gpt read' and 'gpt flip' commands facilitate programmatic renaming of +partitions from bootscripts by generating and modifying the partitions +layout string. The code in gpt_flip() illustrates the case of +swapping 'primary' and 'backup' partitions via: + +U-BOOT> gpt flip mmc 0 + +Choose different partition names by modifying these strings in gpt.c.
Partition type GUID: ====================

From: Alison Chaiken alison@peloton-tech.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt accordingly.
Changes since v3: -- The part of this patch dealing with the partition-flipping behavior was squashed into the patch that introduces that feature.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- doc/README.gpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/README.gpt b/doc/README.gpt index e29b188..754e490 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to: 1. Define partition layout in the environment. Format of partitions layout: - "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...;" or - "partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name}, + "uuid_disk=${uuid_gpt_disk};name=${uboot_name}, size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition. @@ -233,7 +233,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;"
@@ -255,7 +255,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.

From: Alison Chaiken alison@peloton-tech.com
The instructions for creating a disk image that are presently in README.sandbox fail because sfdisk doesn't know about GPT.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- board/sandbox/README.sandbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox index 02d8ab3..9dc2eb0 100644 --- a/board/sandbox/README.sandbox +++ b/board/sandbox/README.sandbox @@ -333,7 +333,7 @@ the contents of the root directory on the second partion of the image A disk image can be created using the following commands:
$> truncate -s 1200M ./disk.raw -$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sfdisk ./disk.raw +$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sgdisk ./disk.raw $> lodev=`sudo losetup -P -f --show ./disk.raw` $> sudo mkfs.vfat -n EFI -v ${lodev}p1 $> sudo mkfs.ext4 -L ROOT -v ${lodev}p2

On 4 June 2017 at 16:11, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
The instructions for creating a disk image that are presently in README.sandbox fail because sfdisk doesn't know about GPT.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
board/sandbox/README.sandbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/board/sandbox/README.sandbox b/board/sandbox/README.sandbox index 02d8ab3..9dc2eb0 100644 --- a/board/sandbox/README.sandbox +++ b/board/sandbox/README.sandbox @@ -333,7 +333,7 @@ the contents of the root directory on the second partion of the image A disk image can be created using the following commands:
$> truncate -s 1200M ./disk.raw -$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sfdisk ./disk.raw +$> echo -e "label: gpt\n,64M,U\n,,L" | /usr/sbin/sgdisk ./disk.raw $> lodev=`sudo losetup -P -f --show ./disk.raw` $> sudo mkfs.vfat -n EFI -v ${lodev}p1 $> sudo mkfs.ext4 -L ROOT -v ${lodev}p2
Reviewed-by: Simon Glass sjg@chromium.org Tested on Ubuntu 14.04 (with tools in /sbin): Tested-by: Simon Glass sjg@chromium.org
-- 2.1.4
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On 4 June 2017 at 16:11, alison@peloton-tech.com wrote:
From: Alison Chaiken alison@peloton-tech.com
The instructions for creating a disk image that are presently in README.sandbox fail because sfdisk doesn't know about GPT.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
board/sandbox/README.sandbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

From: Alison Chaiken alison@peloton-tech.com
Make minor changes to README.gpt and sandbox_defconfig to support testing of the gpt command's functionality in the sandbox.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- doc/README.gpt | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/doc/README.gpt b/doc/README.gpt index 754e490..7b3936b 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -224,6 +224,16 @@ U-BOOT> gpt flip mmc 0
Choose different partition names by modifying these strings in gpt.c.
+The GPT functionality may be tested with the 'sandbox' board by +creating a disk image as described under 'Block Device Emulation' in +board/sandbox/README.sandbox: + +=>host bind 0 ./disk.raw +=> gpt read host 0 +[ . . . ] +=> gpt flip host 0 +[ . . . ] + Partition type GUID: ====================

From: Alison Chaiken alison@peloton-tech.com
Make minor changes to README.gpt and sandbox_defconfig to support testing of the gpt command's functionality in the sandbox.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- doc/README.gpt | 10 ++++++++++ 1 file changed, 10 insertions(+)
Applied to u-boot-dm, thanks!

On 2017-05-31 01:12, Lukasz Majewski wrote:
[ . . . ]
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
Then you can 'mount' some gpt test image ('host' command) and use it with: gpt <command> host X .....
The GPT functionality really cannot be tested without a block device. We could build the disk.raw described in board/sandbox/README.sandbox when (CONFIG_UNIT_TEST && CONFIG_CMD_GPT_RENAME) in imitation of the file helloworld.efi. There would be no reason for the size to be as large as 1200M, of course. 256KB appears to be the smallest usable size for 4 partitions (although trying to partition a 16 KB raw object with gdisk produces entertaining results).
To create this object for testing, we could use a subdirectory like lib/efi_loader, but maybe doing so in test/ makes more sense? If the build system did create the object, then we could modify board/sandbox/README.sandbox to so indicate. Then the test should go in test/py/tests presumably.
Thanks for feedback, Alison
--- Alison Chaiken alison@she-devel.com http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis

On 08/28/2017 01:02 AM, Chaiken, Alison wrote:
On 2017-05-31 01:12, Lukasz Majewski wrote:
[ . . . ]
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
Then you can 'mount' some gpt test image ('host' command) and use it with: gpt <command> host X .....
The GPT functionality really cannot be tested without a block device. We could build the disk.raw described in board/sandbox/README.sandbox when (CONFIG_UNIT_TEST && CONFIG_CMD_GPT_RENAME) in imitation of the file helloworld.efi. There would be no reason for the size to be as large as 1200M, of course. 256KB appears to be the smallest usable size for 4 partitions (although trying to partition a 16 KB raw object with gdisk produces entertaining results).
To create this object for testing, we could use a subdirectory like lib/efi_loader, but maybe doing so in test/ makes more sense? If the build system did create the object, then we could modify board/sandbox/README.sandbox to so indicate. Then the test should go in test/py/tests presumably.
If you have encountered some issues with adding this test to sandbox (and those are difficult to solve), then it would be also correct to add this test to test/py and run it on a real HW.
Please also keep in mind to provide some kind of verbose in-code description or proper documentation entry.
Thanks for feedback, Alison
Alison Chaiken alison@she-devel.com http://{ she-devel.com, exerciseforthereader.org } "We are giving up our privacy, one convenience at a time." -- Evangelos Simoudis

On Mon, Aug 28, 2017 at 09:54:38AM +0200, Łukasz Majewski wrote:
On 08/28/2017 01:02 AM, Chaiken, Alison wrote:
On 2017-05-31 01:12, Lukasz Majewski wrote:
[ . . . ]
And another request -> Could you consider adding tests for those new gpt commands to the 'sandbox' (sandbox_defconfig) ?
Then you can 'mount' some gpt test image ('host' command) and use it with: gpt <command> host X .....
The GPT functionality really cannot be tested without a block device. We could build the disk.raw described in board/sandbox/README.sandbox when (CONFIG_UNIT_TEST && CONFIG_CMD_GPT_RENAME) in imitation of the file helloworld.efi. There would be no reason for the size to be as large as 1200M, of course. 256KB appears to be the smallest usable size for 4 partitions (although trying to partition a 16 KB raw object with gdisk produces entertaining results).
To create this object for testing, we could use a subdirectory like lib/efi_loader, but maybe doing so in test/ makes more sense? If the build system did create the object, then we could modify board/sandbox/README.sandbox to so indicate. Then the test should go in test/py/tests presumably.
If you have encountered some issues with adding this test to sandbox (and those are difficult to solve), then it would be also correct to add this test to test/py and run it on a real HW.
Right. Everyone can run test.py on sandbox _and_ QEMU, and there are then some people with labs setup that also run test.py on real hardware. Between QEMU and real hardware we should be able to add and run tests for this. Thanks!

From: Alison Chaiken alison@she-devel.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt and file comments accordingly.
Signed-off-by: Alison Chaiken alison@peloton-tech.com --- cmd/gpt.c | 13 +------------ doc/README.gpt | 8 ++++---- 2 files changed, 5 insertions(+), 16 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 6a0b70f..487314b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -232,12 +232,6 @@ static void print_gpt_info(void) #ifdef CONFIG_CMD_GPT_FLIP static int calc_parts_list_len(int numparts) { - /* - * prefatory string: - * doc/README.GPT, suggests that - * int partlistlen = UUID_STR_LEN + 1 + strlen("partitions=uuid_disk="); - * is correct, but extract_val() expects "uuid_disk" first. - */ int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); /* for the comma */ partlistlen++; @@ -260,7 +254,7 @@ static int calc_parts_list_len(int numparts) * argument * * From doc/README.gpt, Format of partitions layout: - * "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; * name=kernel,size=60MiB,uuid=...;" * The fields 'name' and 'size' are mandatory for every partition. * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' @@ -275,11 +269,6 @@ static int create_gpt_partitions_list(int numparts, const char *guid, char *part if (!partitions_list) return -1;
- /* - * README.gpt specifies starting with "partitions=" like so: - * strcpy(partitions_list, "partitions=uuid_disk="); - * but that breaks extract_val, which doesn't skip over 'partitions='. - */ strcpy(partitions_list, "uuid_disk="); strncat(partitions_list, guid, UUID_STR_LEN + 1); strcat(partitions_list, ";"); diff --git a/doc/README.gpt b/doc/README.gpt index e29b188..754e490 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to: 1. Define partition layout in the environment. Format of partitions layout: - "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...;" or - "partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name}, + "uuid_disk=${uuid_gpt_disk};name=${uboot_name}, size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition. @@ -233,7 +233,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;"
@@ -255,7 +255,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.

Hi Alison,
From: Alison Chaiken alison@she-devel.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt and file comments accordingly.
Acked-by: Lukasz Majewski lukma@denx.de
Thank you for updating the README.gpt entry.
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 13 +------------ doc/README.gpt | 8 ++++---- 2 files changed, 5 insertions(+), 16 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 6a0b70f..487314b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -232,12 +232,6 @@ static void print_gpt_info(void) #ifdef CONFIG_CMD_GPT_FLIP static int calc_parts_list_len(int numparts) {
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 +
strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); /* for the comma */ partlistlen++;*/
@@ -260,7 +254,7 @@ static int calc_parts_list_len(int numparts)
- argument
- From doc/README.gpt, Format of partitions layout:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
@@ -275,11 +269,6 @@ static int create_gpt_partitions_list(int numparts, const char *guid, char *part if (!partitions_list) return -1;
- /*
* README.gpt specifies starting with "partitions=" like so:
* strcpy(partitions_list, "partitions=uuid_disk=");
* but that breaks extract_val, which doesn't skip over
'partitions='.
strcpy(partitions_list, "uuid_disk="); strncat(partitions_list, guid, UUID_STR_LEN + 1); strcat(partitions_list, ";");*/
diff --git a/doc/README.gpt b/doc/README.gpt index e29b188..754e490 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to:
- Define partition layout in the environment. Format of partitions layout:
"partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
name=kernel,size=60MiB,uuid=...;" or"uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
"partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name},
"uuid_disk=${uuid_gpt_disk};name=${uboot_name},
size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition.
@@ -233,7 +233,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
"partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;""uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
@@ -255,7 +255,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de

Hi Alison,
Hi Alison,
From: Alison Chaiken alison@she-devel.com
The existing partitions-list parsing in cmd/gpt.c passes a value from gpt_default() to set_gpt_info() that README.gpt suggests should begin with 'partitions='. Partition-list strings should in fact begin with 'uuid_disk', as otherwise the call from set_gpt_info() to extract_val() to find 'uuid_disk' will fail. Change README.gpt and file comments accordingly.
Acked-by: Lukasz Majewski lukma@denx.de
Thank you for updating the README.gpt entry.
Please also use either patman or get_maintainer before sending the patchset.
For exmaple: ./scripts/get_maintainer.pl 0001-wip.patch
In that way all relevant people would be informed :-)
Signed-off-by: Alison Chaiken alison@peloton-tech.com
cmd/gpt.c | 13 +------------ doc/README.gpt | 8 ++++---- 2 files changed, 5 insertions(+), 16 deletions(-)
diff --git a/cmd/gpt.c b/cmd/gpt.c index 6a0b70f..487314b 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -232,12 +232,6 @@ static void print_gpt_info(void) #ifdef CONFIG_CMD_GPT_FLIP static int calc_parts_list_len(int numparts) {
- /*
* prefatory string:
* doc/README.GPT, suggests that
* int partlistlen = UUID_STR_LEN + 1 +
strlen("partitions=uuid_disk=");
* is correct, but extract_val() expects "uuid_disk" first.
int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); /* for the comma */ partlistlen++;*/
@@ -260,7 +254,7 @@ static int calc_parts_list_len(int numparts)
- argument
- From doc/README.gpt, Format of partitions layout:
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- name=kernel,size=60MiB,uuid=...;"
- The fields 'name' and 'size' are mandatory for every partition.
- The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
@@ -275,11 +269,6 @@ static int create_gpt_partitions_list(int numparts, const char *guid, char *part if (!partitions_list) return -1;
- /*
* README.gpt specifies starting with "partitions=" like
so:
* strcpy(partitions_list, "partitions=uuid_disk=");
* but that breaks extract_val, which doesn't skip over
'partitions='.
strcpy(partitions_list, "uuid_disk="); strncat(partitions_list, guid, UUID_STR_LEN + 1); strcat(partitions_list, ";");*/
diff --git a/doc/README.gpt b/doc/README.gpt index e29b188..754e490 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -156,10 +156,10 @@ Creating GPT partitions in U-Boot: To restore GUID partition table one needs to:
- Define partition layout in the environment. Format of partitions layout:
"partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
name=kernel,size=60MiB,uuid=...;" or"uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
"partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name},
"uuid_disk=${uuid_gpt_disk};name=${uboot_name},
size=${uboot_size},uuid=${uboot_uuid};"
The fields 'name' and 'size' are mandatory for every partition.
@@ -233,7 +233,7 @@ PARTITION_BASIC_DATA_GUID (EBD0A0A2-B9E5-4433-87C0-68B6B72699C7). If you define 'CONFIG_PARTITION_TYPE_GUID', a optionnal parameter 'type' can specify a other partition type guid:
"partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
name=kernel,size=60MiB,uuid=..., type=0FC63DAF-8483-4772-8E79-3D69D8477DE4;""uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
@@ -255,7 +255,7 @@ Some strings can be also used at the place of known GUID : "lvm" = PARTITION_LINUX_LVM_GUID (E6D6D379-F507-44C2-A23C-238F2A3DF928)
- "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
- "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; name=kernel,size=60MiB,uuid=...,type=linux;"
They are also used to display the type of partition in "part list" command.
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Best regards,
Lukasz Majewski
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
participants (11)
-
Alison Chaiken
-
alison@peloton-tech.com
-
Bin Meng
-
Chaiken, Alison
-
Lothar Waßmann
-
Lukasz Majewski
-
Simon Glass
-
sjg@google.com
-
Tom Rini
-
Wolfgang Denk
-
Łukasz Majewski