diff --git a/cmd/gpt.c b/cmd/gpt.c index 3cc6436b28b..d7e96529a69 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -162,22 +162,31 @@ static bool found_key(const char *str, const char *key) return result; } +/** + * calc_parts_list_len() - get size of partition table description + * + * @numparts: number of partitions + * Return: string size including terminating NUL + */ static int calc_parts_list_len(int numparts) { - int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk="); - /* for the comma */ - partlistlen++; + /* number of hexadecimal digits of the lbaint_t representation */ + const int lbaint_size = 2 * sizeof(lbaint_t); + int partlistlen; - /* per-partition additions; numparts starts at 1, so this should be correct */ - partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1); + /* media description including terminating NUL */ + partlistlen = strlen("uuid_disk=;") + UUID_STR_LEN + 1; + /* per-partition descriptions; numparts */ + partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN); /* 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); + partlistlen += numparts * (strlen("start=0x,") + lbaint_size); + partlistlen += numparts * (strlen("size=0x,") + lbaint_size); + if (IS_ENABLED(CONFIG_PARTITION_UUIDS)) + partlistlen += numparts * (strlen("uuid=,") + UUID_STR_LEN); + if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) + partlistlen += numparts * (strlen("type=;") + UUID_STR_LEN); + debug("Length of partitions_list is %d for %d partitions\n", + partlistlen, numparts); return partlistlen; } @@ -211,10 +220,12 @@ static struct disk_part *allocate_disk_part(struct disk_partition *info, PART_TYPE_LEN); newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; newpart->gpt_part_info.bootable = info->bootable; - if (IS_ENABLED(CONFIG_PARTITION_UUIDS)) { - strlcpy(newpart->gpt_part_info.uuid, disk_partition_uuid(info), - UUID_STR_LEN + 1); - } + if (IS_ENABLED(CONFIG_PARTITION_UUIDS)) + disk_partition_set_uuid(&newpart->gpt_part_info, + disk_partition_uuid(info)); + if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) + disk_partition_set_type_guid(&newpart->gpt_part_info, + disk_partition_type_guid(info)); newpart->partnum = partnum; return newpart; @@ -250,9 +261,12 @@ static void print_gpt_info(void) curr->gpt_part_info.name); printf("Type %s, bootable %d\n", curr->gpt_part_info.type, curr->gpt_part_info.bootable & PART_BOOTABLE); -#ifdef CONFIG_PARTITION_UUIDS - printf("UUID %s\n", curr->gpt_part_info.uuid); -#endif + if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) + printf("UUID %s\n", + disk_partition_uuid(&curr->gpt_part_info)); + if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) + printf("Type GUID %s\n", + disk_partition_type_guid(&curr->gpt_part_info)); printf("\n"); } } @@ -297,9 +311,20 @@ static int create_gpt_partitions_list(int numparts, const char *guid, 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); + if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) { + strcat(partitions_list, ",type="); + strncat(partitions_list, + disk_partition_type_guid(&curr->gpt_part_info), + UUID_STR_LEN + 1); + } + if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) { + strcat(partitions_list, ",uuid="); + strncat(partitions_list, + disk_partition_uuid(&curr->gpt_part_info), + UUID_STR_LEN + 1); + } + if (curr->gpt_part_info.bootable & PART_BOOTABLE) + strcat(partitions_list, ",bootable"); strcat(partitions_list, ";"); } return 0; @@ -723,7 +748,7 @@ static int gpt_enumerate(struct blk_desc *desc) * gpt_setenv_part_variables() - setup partition environmental variables * * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr - * and gpt_partition_size environment variables. + * and gpt_partition_size, gpt_partition_bootable environment variables. * * @pinfo: pointer to disk partition * @i: partition entry @@ -750,6 +775,10 @@ static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i) if (ret) goto fail; + ret = env_set_ulong("gpt_partition_bootable", !!(pinfo->bootable & PART_BOOTABLE)); + if (ret) + goto fail; + return 0; fail: @@ -833,8 +862,9 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, 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")))) + if (!subcomm || !name1 || !name2 || + (strcmp(subcomm, "swap") && strcmp(subcomm, "rename") && + strcmp(subcomm, "transpose"))) return -EINVAL; ret = get_disk_guid(dev_desc, disk_guid); @@ -895,6 +925,41 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, ret = -EINVAL; goto out; } + } else if (!strcmp(subcomm, "transpose")) { + int idx1, idx2; + struct disk_partition* first = NULL; + struct disk_partition* second= NULL; + struct disk_partition tmp_part; + + idx1 = simple_strtoul(name1, NULL, 10); + idx2 = simple_strtoul(name2, NULL, 10); + if (idx1 == idx2) { + printf("Cannot swap partition with itself\n"); + ret = -EINVAL; + goto out; + } + + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + if (curr->partnum == idx1) + first = &curr->gpt_part_info; + else if (curr->partnum == idx2) + second = &curr->gpt_part_info; + } + if (!first) { + printf("Illegal partition number %s\n", name1); + ret = -EINVAL; + goto out; + } + if (!second) { + printf("Illegal partition number %s\n", name2); + ret = -EINVAL; + goto out; + } + + tmp_part = *first; + *first = *second; + *second = tmp_part; } else { /* rename */ if (strlen(name2) > PART_NAME_LEN) { printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); @@ -966,6 +1031,81 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, free(partitions_list); return ret; } + +/** + * gpt_set_bootable() - Set bootable flags for partitions + * + * Sets the bootable flag for any partition names in the comma separated list of + * partition names. Any partitions not in the list have their bootable flag + * cleared + * + * @desc: block device descriptor + * @name: Comma separated list of partition names + * + * @Return: '0' on success and -ve error on failure + */ +static int gpt_set_bootable(struct blk_desc *blk_dev_desc, char *const part_list) +{ + char *name; + char disk_guid[UUID_STR_LEN + 1]; + struct list_head *pos; + struct disk_part *curr; + struct disk_partition *partitions = NULL; + int part_count = 0; + int ret = get_disk_guid(blk_dev_desc, disk_guid); + + if (ret < 0) + return ret; + + ret = get_gpt_info(blk_dev_desc); + if (ret <= 0) + goto out; + + part_count = ret; + partitions = malloc(sizeof(*partitions) * part_count); + if (!partitions) { + ret = -ENOMEM; + goto out; + } + + /* Copy partitions and clear bootable flag */ + part_count = 0; + list_for_each(pos, &disk_partitions) { + curr = list_entry(pos, struct disk_part, list); + partitions[part_count] = curr->gpt_part_info; + partitions[part_count].bootable &= ~PART_BOOTABLE; + part_count++; + } + + name = strtok(part_list, ","); + while (name) { + bool found = false; + + for (int i = 0; i < part_count; i++) { + if (strcmp((char *)partitions[i].name, name) == 0) { + partitions[i].bootable |= PART_BOOTABLE; + found = true; + } + } + + if (!found) { + printf("Warning: No partition matching '%s' found\n", + name); + } + + name = strtok(NULL, ","); + } + + ret = gpt_restore(blk_dev_desc, disk_guid, partitions, part_count); + +out: + del_gpt_info(); + + if (partitions) + free(partitions); + + return ret; +} #endif /** @@ -1023,8 +1163,11 @@ static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } else if (strcmp(argv[1], "read") == 0) { ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL); } else if ((strcmp(argv[1], "swap") == 0) || - (strcmp(argv[1], "rename") == 0)) { + (strcmp(argv[1], "rename") == 0) || + (strcmp(argv[1], "transpose") == 0)) { ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); + } else if ((strcmp(argv[1], "set-bootable") == 0)) { + ret = gpt_set_bootable(blk_dev_desc, argv[4]); #endif } else { return CMD_RET_USAGE; @@ -1055,7 +1198,8 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " gpt setenv mmc 0 $name\n" " - setup environment variables for partition $name:\n" " gpt_partition_addr, gpt_partition_size,\n" - " gpt_partition_name, gpt_partition_entry\n" + " gpt_partition_name, gpt_partition_entry,\n" + " gpt_partition_bootable\n" " gpt enumerate mmc 0\n" " - store list of partitions to gpt_partition_list environment variable\n" " gpt guid \n" @@ -1073,10 +1217,16 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " gpt swap \n" " - change all partitions named name1 to name2\n" " and vice-versa\n" + " gpt transpose \n" + " - Swap the order of the entries for part1 and part2 in the partition table\n" " gpt rename \n" " - rename the specified partition\n" + " gpt set-bootable \n" + " - make partition names in list bootable\n" " Example usage:\n" " gpt swap mmc 0 foo bar\n" " gpt rename mmc 0 3 foo\n" + " gpt set-bootable mmc 0 boot_a,boot_b\n" + " gpt transpose mmc 0 1 2\n" #endif ); diff --git a/disk/part_efi.c b/disk/part_efi.c index 39382c5faee..b7aef3731b5 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -299,7 +299,7 @@ int part_get_info_efi(struct blk_desc *desc, int part, } if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) { uuid_bin_to_str(gpt_pte[part - 1].partition_type_guid.b, - (char *)disk_partition_type_uuid(info), + (char *)disk_partition_type_guid(info), UUID_STR_FORMAT_GUID); } diff --git a/doc/usage/cmd/gpt.rst b/doc/usage/cmd/gpt.rst index 6387c8116fe..f6115ecb0ee 100644 --- a/doc/usage/cmd/gpt.rst +++ b/doc/usage/cmd/gpt.rst @@ -13,8 +13,10 @@ Synopsis gpt read [] gpt rename gpt repair + gpt set-bootable gpt setenv gpt swap + gpt transpose gpt verify [] gpt write @@ -90,6 +92,13 @@ gpt repair Repairs the GPT partition tables if it they become corrupted. +gpt set-bootable +~~~~~~~~~~~~~~~~ + +Sets the bootable flag for all partitions in the table. If the partition name +is in 'partition list' (separated by ','), the bootable flag is set, otherwise +it is cleared. CONFIG_CMD_GPT_RENAME=y is required. + gpt setenv ~~~~~~~~~~ @@ -108,6 +117,9 @@ gpt_partition_name gpt_partition_entry the partition number in the table, e.g. 1, 2, 3, etc. +gpt_partition_bootable + 1 if the partition is marked as bootable, 0 if not + gpt swap ~~~~~~~~ @@ -115,6 +127,13 @@ Changes the names of all partitions that are named 'name1' to be 'name2', and all partitions named 'name2' to be 'name1'. CONFIG_CMD_GPT_RENAME=y is required. +gpt transpose +~~~~~~~~~~~~~ + +Swaps the order of two partition table entries with indexes 'part1' and 'part2' +in the partition table, but otherwise leaves the actual partition data +untouched. + gpt verify ~~~~~~~~~~ @@ -167,6 +186,8 @@ Get the information about the partition named 'rootfs':: rootfs => echo ${gpt_partition_entry} 2 + => echo ${gpt_partition_bootable} + 0 Get the list of partition names on the disk:: @@ -182,3 +203,24 @@ Get the GUID for a disk:: => gpt guid mmc gpt_disk_uuid => echo ${gpt_disk_uuid} bec9fc2a-86c1-483d-8a0e-0109732277d7 + +Set the bootable flag for the 'boot' partition and clear it for all others:: + + => gpt set-bootable mmc 0 boot + +Swap the order of the 'boot' and 'rootfs' partition table entries:: + => gpt setenv mmc 0 rootfs + => echo ${gpt_partition_entry} + 2 + => gpt setenv mmc 0 boot + => echo ${gpt_partition_entry} + 1 + + => gpt transpose mmc 0 1 2 + + => gpt setenv mmc 0 rootfs + => echo ${gpt_partition_entry} + 1 + => gpt setenv mmc 0 boot + => echo ${gpt_partition_entry} + 2 diff --git a/include/part.h b/include/part.h index f321479a5e9..db34bc6bb7d 100644 --- a/include/part.h +++ b/include/part.h @@ -108,18 +108,38 @@ static inline void disk_partition_clr_uuid(struct disk_partition *info) } /* Accessors for struct disk_partition field ->type_guid */ -extern char *__invalid_use_of_disk_partition_type_uuid; +extern char *__invalid_use_of_disk_partition_type_guid; +/** + * disk_partition_type_guid() - get partition type GUID + * + * By using this function to get the partition type GUID we can use + * 'if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID))' instead of + * '#ifdef CONFIG_PARTITION_TYPE_GUID'. + * + * @info: partition information + * Return: partition type GUID + */ static inline const -char *disk_partition_type_uuid(const struct disk_partition *info) +char *disk_partition_type_guid(const struct disk_partition *info) { #ifdef CONFIG_PARTITION_TYPE_GUID return info->type_guid; #else - return __invalid_use_of_disk_partition_type_uuid; + return __invalid_use_of_disk_partition_type_guid; #endif } +/** + * disk_partition_set_type_guid() - set partition type GUID + * + * By using this function to set the partition type GUID we can use + * 'if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID))' instead of + * '#ifdef CONFIG_PARTITION_TYPE_GUID'. + * + * @info: partition information + * @val: partition type GUID as string + */ static inline void disk_partition_set_type_guid(struct disk_partition *info, const char *val) { diff --git a/test/py/tests/test_gpt.py b/test/py/tests/test_gpt.py index 73bfbf77a27..6e135b663e8 100644 --- a/test/py/tests/test_gpt.py +++ b/test/py/tests/test_gpt.py @@ -16,6 +16,35 @@ the test. # Mark all tests here as slow pytestmark = pytest.mark.slow +def parse_gpt_parts(disk_str): + """Parser a partition string into a list of partitions. + + Args: + disk_str: The disk description string, as returned by `gpt read` + + Returns: + A list of parsed partitions. Each partition is a dictionary with the + string value from each specified key in the partition description, or a + key with with the value True for a boolean flag + """ + parts = [] + for part_str in disk_str.split(';'): + part = {} + for option in part_str.split(","): + if not option: + continue + + if "=" in option: + key, value = option.split("=") + part[key] = value + else: + part[option] = True + + if part: + parts.append(part) + + return parts + class GptTestDiskImage(object): """Disk Image used by the GPT tests.""" @@ -49,10 +78,13 @@ class GptTestDiskImage(object): u_boot_utils.run_and_log(u_boot_console, cmd) # part1 offset 1MB size 1MB cmd = ('sgdisk', '--new=1:2048:4095', '--change-name=1:part1', + '--partition-guid=1:33194895-67f6-4561-8457-6fdeed4f50a3', + '-A 1:set:2', persistent) # part2 offset 2MB size 1.5MB u_boot_utils.run_and_log(u_boot_console, cmd) cmd = ('sgdisk', '--new=2:4096:7167', '--change-name=2:part2', + '--partition-guid=2:cc9c6e4a-6551-4cb5-87be-3210f96c86fb', persistent) u_boot_utils.run_and_log(u_boot_console, cmd) cmd = ('sgdisk', '--load-backup=' + persistent) @@ -61,18 +93,14 @@ class GptTestDiskImage(object): cmd = ('cp', persistent, self.path) u_boot_utils.run_and_log(u_boot_console, cmd) -gtdi = None @pytest.fixture(scope='function') def state_disk_image(u_boot_console): """pytest fixture to provide a GptTestDiskImage object to tests. This is function-scoped because it uses u_boot_console, which is also - function-scoped. However, we don't need to actually do any function-scope - work, so this simply returns the same object over and over each time.""" + function-scoped. A new disk is returned each time to prevent tests from + interfering with each other.""" - global gtdi - if not gtdi: - gtdi = GptTestDiskImage(u_boot_console) - return gtdi + return GptTestDiskImage(u_boot_console) @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_gpt') @@ -91,6 +119,41 @@ def test_gpt_read(state_disk_image, u_boot_console): assert '0x00000800 0x00000fff "part1"' in output assert '0x00001000 0x00001bff "part2"' in output +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('partition_type_guid') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_read_var(state_disk_image, u_boot_console): + """Test the gpt read command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt read host 0 gpt_parts') + assert 'success!' in output + + output = u_boot_console.run_command('echo ${gpt_parts}') + parts = parse_gpt_parts(output.rstrip()) + + assert parts == [ + { + "uuid_disk": "375a56f7-d6c9-4e81-b5f0-09d41ca89efe", + }, + { + "name": "part1", + "start": "0x100000", + "size": "0x100000", + "type": "0fc63daf-8483-4772-8e79-3d69d8477de4", + "uuid": "33194895-67f6-4561-8457-6fdeed4f50a3", + "bootable": True, + }, + { + "name": "part2", + "start": "0x200000", + "size": "0x180000", + "type": "0fc63daf-8483-4772-8e79-3d69d8477de4", + "uuid": "cc9c6e4a-6551-4cb5-87be-3210f96c86fb", + }, + ] + @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_gpt') @pytest.mark.requiredtool('sgdisk') @@ -121,6 +184,38 @@ def test_gpt_guid(state_disk_image, u_boot_console): output = u_boot_console.run_command('gpt guid host 0') assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_setenv(state_disk_image, u_boot_console): + """Test the gpt setenv command.""" + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt setenv host 0 part1') + assert 'success!' in output + output = u_boot_console.run_command('echo ${gpt_partition_addr}') + assert output.rstrip() == '800' + output = u_boot_console.run_command('echo ${gpt_partition_size}') + assert output.rstrip() == '800' + output = u_boot_console.run_command('echo ${gpt_partition_name}') + assert output.rstrip() == 'part1' + output = u_boot_console.run_command('echo ${gpt_partition_entry}') + assert output.rstrip() == '1' + output = u_boot_console.run_command('echo ${gpt_partition_bootable}') + assert output.rstrip() == '1' + + output = u_boot_console.run_command('gpt setenv host 0 part2') + assert 'success!' in output + output = u_boot_console.run_command('echo ${gpt_partition_addr}') + assert output.rstrip() == '1000' + output = u_boot_console.run_command('echo ${gpt_partition_size}') + assert output.rstrip() == 'c00' + output = u_boot_console.run_command('echo ${gpt_partition_name}') + assert output.rstrip() == 'part2' + output = u_boot_console.run_command('echo ${gpt_partition_entry}') + assert output.rstrip() == '2' + output = u_boot_console.run_command('echo ${gpt_partition_bootable}') + assert output.rstrip() == '0' + @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_gpt') @pytest.mark.requiredtool('sgdisk') @@ -186,12 +281,34 @@ def test_gpt_swap_partitions(state_disk_image, u_boot_console): u_boot_console.run_command('host bind 0 ' + state_disk_image.path) output = u_boot_console.run_command('part list host 0') - assert '0x00000800 0x00000fff "first"' in output - assert '0x00001000 0x00001bff "second"' in output - u_boot_console.run_command('gpt swap host 0 first second') + assert '0x00000800 0x00000fff "part1"' in output + assert '0x00001000 0x00001bff "part2"' in output + u_boot_console.run_command('gpt swap host 0 part1 part2') output = u_boot_console.run_command('part list host 0') - assert '0x00000800 0x00000fff "second"' in output - assert '0x00001000 0x00001bff "first"' in output + assert '0x00000800 0x00000fff "part2"' in output + assert '0x00001000 0x00001bff "part1"' in output + +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_gpt_rename') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_set_bootable(state_disk_image, u_boot_console): + """Test the gpt set-bootable command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + parts = ('part2', 'part1') + for bootable in parts: + output = u_boot_console.run_command(f'gpt set-bootable host 0 {bootable}') + assert 'success!' in output + + for p in parts: + output = u_boot_console.run_command(f'gpt setenv host 0 {p}') + assert 'success!' in output + output = u_boot_console.run_command('echo ${gpt_partition_bootable}') + if p == bootable: + assert output.rstrip() == '1' + else: + assert output.rstrip() == '0' @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_gpt') @@ -212,3 +329,22 @@ def test_gpt_write(state_disk_image, u_boot_console): assert '0x00001000 0x00001bff "second"' in output output = u_boot_console.run_command('gpt guid host 0') assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output + +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_gpt_rename') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_transpose(state_disk_image, u_boot_console): + """Test the gpt transpose command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('part list host 0') + assert '1\t0x00000800\t0x00000fff\t"part1"' in output + assert '2\t0x00001000\t0x00001bff\t"part2"' in output + + output = u_boot_console.run_command('gpt transpose host 0 1 2') + assert 'success!' in output + + output = u_boot_console.run_command('part list host 0') + assert '2\t0x00000800\t0x00000fff\t"part1"' in output + assert '1\t0x00001000\t0x00001bff\t"part2"' in output