GPT: provide commands to selectively rename partitions

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>
This commit is contained in:
Alison Chaiken 2017-07-04 11:19:18 -07:00 committed by Tom Rini
parent 09a49930e4
commit 203f9b48ad
3 changed files with 260 additions and 5 deletions

View file

@ -595,6 +595,14 @@ config CMD_GPT
Enable the 'gpt' command to ready and write GPT style partition Enable the 'gpt' command to ready and write GPT style partition
tables. 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 config CMD_ARMFLASH
#depends on FLASH_CFI_DRIVER #depends on FLASH_CFI_DRIVER
bool "armflash" bool "armflash"

235
cmd/gpt.c
View file

@ -20,6 +20,8 @@
#include <div64.h> #include <div64.h>
#include <memalign.h> #include <memalign.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/sizes.h>
#include <stdlib.h>
static LIST_HEAD(disk_partitions); static LIST_HEAD(disk_partitions);
@ -195,16 +197,32 @@ static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
return newpart; 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) static void print_gpt_info(void)
{ {
struct list_head *pos; struct list_head *pos;
struct disk_part *curr; struct disk_part *curr;
char partstartstr[16];
char partsizestr[16];
list_for_each(pos, &disk_partitions) { list_for_each(pos, &disk_partitions) {
curr = list_entry(pos, struct disk_part, list); 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("Partition %d:\n", curr->partnum);
printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start, printf("Start %s, size %s\n", partstartstr, partsizestr);
(unsigned)curr->gpt_part_info.size);
printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
curr->gpt_part_info.name); curr->gpt_part_info.name);
printf("Type %s, bootable %d\n", curr->gpt_part_info.type, printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
@ -216,6 +234,73 @@ static void print_gpt_info(void)
} }
} }
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;
}
/* /*
* read partition info into disk_partitions list where * read partition info into disk_partitions list where
* it can be printed or modified * it can be printed or modified
@ -227,7 +312,10 @@ static int get_gpt_info(struct blk_desc *dev_desc)
disk_partition_t info; disk_partition_t info;
struct disk_part *new_disk_part; struct disk_part *new_disk_part;
if (disk_partitions.next == NULL) /*
* Always re-read partition info from device, in case
* it has changed
*/
INIT_LIST_HEAD(&disk_partitions); INIT_LIST_HEAD(&disk_partitions);
for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
@ -305,6 +393,8 @@ static int set_gpt_info(struct blk_desc *dev_desc,
return -1; return -1;
str = strdup(str_part); str = strdup(str_part);
if (str == NULL)
return -ENOMEM;
/* extract disk guid */ /* extract disk guid */
s = str; s = str;
@ -534,6 +624,127 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
return ret; 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 * do_gpt(): Perform GPT operations
* *
@ -551,7 +762,11 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
char *ep; char *ep;
struct blk_desc *blk_dev_desc = NULL; struct blk_desc *blk_dev_desc = NULL;
#ifndef CONFIG_CMD_GPT_RENAME
if (argc < 4 || argc > 5) if (argc < 4 || argc > 5)
#else
if (argc < 4 || argc > 6)
#endif
return CMD_RET_USAGE; return CMD_RET_USAGE;
dev = (int)simple_strtoul(argv[3], &ep, 10); dev = (int)simple_strtoul(argv[3], &ep, 10);
@ -577,6 +792,9 @@ static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
#ifdef CONFIG_CMD_GPT_RENAME #ifdef CONFIG_CMD_GPT_RENAME
} else if (strcmp(argv[1], "read") == 0) { } else if (strcmp(argv[1], "read") == 0) {
ret = do_get_gpt_info(blk_dev_desc); ret = do_get_gpt_info(blk_dev_desc);
} 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 #endif
} else { } else {
return CMD_RET_USAGE; return CMD_RET_USAGE;
@ -609,4 +827,15 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
" Example usage:\n" " Example usage:\n"
" gpt guid mmc 0\n" " gpt guid mmc 0\n"
" gpt guid mmc 0 varname\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
); );

View file

@ -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> gpt verify mmc 0 $partitions
U-BOOT> if test $? = 0; then echo "GPT OK"; else echo "GPT ERR"; fi 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 The GPT functionality may be tested with the 'sandbox' board by
creating a disk image as described under 'Block Device Emulation' in creating a disk image as described under 'Block Device Emulation' in
@ -228,7 +246,7 @@ board/sandbox/README.sandbox:
=>host bind 0 ./disk.raw =>host bind 0 ./disk.raw
=> gpt read host 0 => gpt read host 0
[ . . . ] [ . . . ]
=> gpt flip host 0 => gpt swap host 0 name othername
[ . . . ] [ . . . ]
Partition type GUID: Partition type GUID: