1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * cmd_gpt.c -- GPT (GUID Partition Table) handling command 4 * 5 * Copyright (C) 2015 6 * Lukasz Majewski <l.majewski@majess.pl> 7 * 8 * Copyright (C) 2012 Samsung Electronics 9 * author: Lukasz Majewski <l.majewski@samsung.com> 10 * author: Piotr Wilczek <p.wilczek@samsung.com> 11 */ 12 13#include <common.h> 14#include <blk.h> 15#include <env.h> 16#include <log.h> 17#include <malloc.h> 18#include <command.h> 19#include <part.h> 20#include <part_efi.h> 21#include <part.h> 22#include <exports.h> 23#include <uuid.h> 24#include <linux/ctype.h> 25#include <div64.h> 26#include <memalign.h> 27#include <linux/compat.h> 28#include <linux/err.h> 29#include <linux/sizes.h> 30#include <stdlib.h> 31 32static LIST_HEAD(disk_partitions); 33 34/** 35 * extract_env(): Expand env name from string format '&{env_name}' 36 * and return pointer to the env (if the env is set) 37 * 38 * @param str - pointer to string 39 * @param env - pointer to pointer to extracted env 40 * 41 * Return: - zero on successful expand and env is set 42 */ 43static int extract_env(const char *str, char **env) 44{ 45 int ret = -1; 46 char *e, *s; 47#ifdef CONFIG_RANDOM_UUID 48 char uuid_str[UUID_STR_LEN + 1]; 49#endif 50 51 if (!str || strlen(str) < 4) 52 return -1; 53 54 if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}'))) 55 return -1; 56 57 s = strdup(str); 58 if (s == NULL) 59 return -1; 60 61 memset(s + strlen(s) - 1, '\0', 1); 62 memmove(s, s + 2, strlen(s) - 1); 63 64 e = env_get(s); 65 if (e == NULL) { 66#ifdef CONFIG_RANDOM_UUID 67 debug("%s unset. ", str); 68 gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID); 69 env_set(s, uuid_str); 70 71 e = env_get(s); 72 if (e) { 73 debug("Set to random.\n"); 74 ret = 0; 75 } else { 76 debug("Can't get random UUID.\n"); 77 } 78#else 79 debug("%s unset.\n", str); 80#endif 81 } else { 82 debug("%s get from environment.\n", str); 83 ret = 0; 84 } 85 86 *env = e; 87 free(s); 88 89 return ret; 90} 91 92/** 93 * extract_val(): Extract value from a key=value pair list (comma separated). 94 * Only value for the given key is returend. 95 * Function allocates memory for the value, remember to free! 96 * 97 * @param str - pointer to string with key=values pairs 98 * @param key - pointer to the key to search for 99 * 100 * Return: - pointer to allocated string with the value 101 */ 102static char *extract_val(const char *str, const char *key) 103{ 104 char *v, *k; 105 char *s, *strcopy; 106 char *new = NULL; 107 108 strcopy = strdup(str); 109 if (strcopy == NULL) 110 return NULL; 111 112 s = strcopy; 113 while (s) { 114 v = strsep(&s, ","); 115 if (!v) 116 break; 117 k = strsep(&v, "="); 118 if (!k) 119 break; 120 if (strcmp(k, key) == 0) { 121 new = strdup(v); 122 break; 123 } 124 } 125 126 free(strcopy); 127 128 return new; 129} 130 131/** 132 * found_key(): Found key without value in parameter list (comma separated). 133 * 134 * @param str - pointer to string with key 135 * @param key - pointer to the key to search for 136 * 137 * Return: - true on found key 138 */ 139static bool found_key(const char *str, const char *key) 140{ 141 char *k; 142 char *s, *strcopy; 143 bool result = false; 144 145 strcopy = strdup(str); 146 if (!strcopy) 147 return NULL; 148 149 s = strcopy; 150 while (s) { 151 k = strsep(&s, ","); 152 if (!k) 153 break; 154 if (strcmp(k, key) == 0) { 155 result = true; 156 break; 157 } 158 } 159 160 free(strcopy); 161 162 return result; 163} 164 165/** 166 * calc_parts_list_len() - get size of partition table description 167 * 168 * @numparts: number of partitions 169 * Return: string size including terminating NUL 170 */ 171static int calc_parts_list_len(int numparts) 172{ 173 /* number of hexadecimal digits of the lbaint_t representation */ 174 const int lbaint_size = 2 * sizeof(lbaint_t); 175 int partlistlen; 176 177 /* media description including terminating NUL */ 178 partlistlen = strlen("uuid_disk=;") + UUID_STR_LEN + 1; 179 /* per-partition descriptions; numparts */ 180 partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN); 181 /* see part.h for definition of struct disk_partition */ 182 partlistlen += numparts * (strlen("start=0x,") + lbaint_size); 183 partlistlen += numparts * (strlen("size=0x,") + lbaint_size); 184 if (IS_ENABLED(CONFIG_PARTITION_UUIDS)) 185 partlistlen += numparts * (strlen("uuid=,") + UUID_STR_LEN); 186 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) 187 partlistlen += numparts * (strlen("type=;") + UUID_STR_LEN); 188 debug("Length of partitions_list is %d for %d partitions\n", 189 partlistlen, numparts); 190 return partlistlen; 191} 192 193#ifdef CONFIG_CMD_GPT_RENAME 194static void del_gpt_info(void) 195{ 196 struct list_head *pos = &disk_partitions; 197 struct disk_part *curr; 198 while (!list_empty(pos)) { 199 curr = list_entry(pos->next, struct disk_part, list); 200 list_del(pos->next); 201 free(curr); 202 } 203} 204 205static struct disk_part *allocate_disk_part(struct disk_partition *info, 206 int partnum) 207{ 208 struct disk_part *newpart; 209 newpart = calloc(1, sizeof(struct disk_part)); 210 if (!newpart) 211 return ERR_PTR(-ENOMEM); 212 213 newpart->gpt_part_info.start = info->start; 214 newpart->gpt_part_info.size = info->size; 215 newpart->gpt_part_info.blksz = info->blksz; 216 strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name, 217 PART_NAME_LEN); 218 newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0'; 219 strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type, 220 PART_TYPE_LEN); 221 newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0'; 222 newpart->gpt_part_info.bootable = info->bootable; 223 if (IS_ENABLED(CONFIG_PARTITION_UUIDS)) 224 disk_partition_set_uuid(&newpart->gpt_part_info, 225 disk_partition_uuid(info)); 226 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) 227 disk_partition_set_type_guid(&newpart->gpt_part_info, 228 disk_partition_type_guid(info)); 229 newpart->partnum = partnum; 230 231 return newpart; 232} 233 234static void prettyprint_part_size(char *sizestr, lbaint_t partsize, 235 lbaint_t blksize) 236{ 237 unsigned long long partbytes, partmegabytes; 238 239 partbytes = partsize * blksize; 240 partmegabytes = lldiv(partbytes, SZ_1M); 241 snprintf(sizestr, 16, "%lluMiB", partmegabytes); 242} 243 244static void print_gpt_info(void) 245{ 246 struct list_head *pos; 247 struct disk_part *curr; 248 char partstartstr[16]; 249 char partsizestr[16]; 250 251 list_for_each(pos, &disk_partitions) { 252 curr = list_entry(pos, struct disk_part, list); 253 prettyprint_part_size(partstartstr, curr->gpt_part_info.start, 254 curr->gpt_part_info.blksz); 255 prettyprint_part_size(partsizestr, curr->gpt_part_info.size, 256 curr->gpt_part_info.blksz); 257 258 printf("Partition %d:\n", curr->partnum); 259 printf("Start %s, size %s\n", partstartstr, partsizestr); 260 printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz, 261 curr->gpt_part_info.name); 262 printf("Type %s, bootable %d\n", curr->gpt_part_info.type, 263 curr->gpt_part_info.bootable & PART_BOOTABLE); 264 if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) 265 printf("UUID %s\n", 266 disk_partition_uuid(&curr->gpt_part_info)); 267 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) 268 printf("Type GUID %s\n", 269 disk_partition_type_guid(&curr->gpt_part_info)); 270 printf("\n"); 271 } 272} 273 274/* 275 * create the string that upstream 'gpt write' command will accept as an 276 * argument 277 * 278 * From doc/README.gpt, Format of partitions layout: 279 * "uuid_disk=...;name=u-boot,size=60MiB,uuid=...; 280 * name=kernel,size=60MiB,uuid=...;" 281 * The fields 'name' and 'size' are mandatory for every partition. 282 * The field 'start' is optional. The fields 'uuid' and 'uuid_disk' 283 * are optional if CONFIG_RANDOM_UUID is enabled. 284 */ 285static int create_gpt_partitions_list(int numparts, const char *guid, 286 char *partitions_list) 287{ 288 struct list_head *pos; 289 struct disk_part *curr; 290 char partstr[PART_NAME_LEN + 1]; 291 292 if (!partitions_list) 293 return -EINVAL; 294 295 strcpy(partitions_list, "uuid_disk="); 296 strncat(partitions_list, guid, UUID_STR_LEN + 1); 297 strcat(partitions_list, ";"); 298 299 list_for_each(pos, &disk_partitions) { 300 curr = list_entry(pos, struct disk_part, list); 301 strcat(partitions_list, "name="); 302 strncat(partitions_list, (const char *)curr->gpt_part_info.name, 303 PART_NAME_LEN + 1); 304 sprintf(partstr, ",start=0x%llx", 305 (unsigned long long)curr->gpt_part_info.start * 306 curr->gpt_part_info.blksz); 307 /* one extra byte for NULL */ 308 strncat(partitions_list, partstr, PART_NAME_LEN + 1); 309 sprintf(partstr, ",size=0x%llx", 310 (unsigned long long)curr->gpt_part_info.size * 311 curr->gpt_part_info.blksz); 312 strncat(partitions_list, partstr, PART_NAME_LEN + 1); 313 314 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) { 315 strcat(partitions_list, ",type="); 316 strncat(partitions_list, 317 disk_partition_type_guid(&curr->gpt_part_info), 318 UUID_STR_LEN + 1); 319 } 320 if (CONFIG_IS_ENABLED(PARTITION_UUIDS)) { 321 strcat(partitions_list, ",uuid="); 322 strncat(partitions_list, 323 disk_partition_uuid(&curr->gpt_part_info), 324 UUID_STR_LEN + 1); 325 } 326 if (curr->gpt_part_info.bootable & PART_BOOTABLE) 327 strcat(partitions_list, ",bootable"); 328 strcat(partitions_list, ";"); 329 } 330 return 0; 331} 332 333/* 334 * read partition info into disk_partitions list where 335 * it can be printed or modified 336 */ 337static int get_gpt_info(struct blk_desc *dev_desc) 338{ 339 /* start partition numbering at 1, as U-Boot does */ 340 int valid_parts = 0, p, ret; 341 struct disk_partition info; 342 struct disk_part *new_disk_part; 343 344 /* 345 * Always re-read partition info from device, in case 346 * it has changed 347 */ 348 INIT_LIST_HEAD(&disk_partitions); 349 350 for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { 351 ret = part_get_info(dev_desc, p, &info); 352 if (ret) 353 continue; 354 355 /* Add 1 here because counter is zero-based but p1 is 356 the first partition */ 357 new_disk_part = allocate_disk_part(&info, valid_parts+1); 358 if (IS_ERR(new_disk_part)) 359 goto out; 360 361 list_add_tail(&new_disk_part->list, &disk_partitions); 362 valid_parts++; 363 } 364 if (valid_parts == 0) { 365 printf("** No valid partitions found **\n"); 366 goto out; 367 } 368 return valid_parts; 369 out: 370 if (valid_parts >= 1) 371 del_gpt_info(); 372 return -ENODEV; 373} 374 375/* a wrapper to test get_gpt_info */ 376static int do_get_gpt_info(struct blk_desc *dev_desc, char * const namestr) 377{ 378 int numparts; 379 380 numparts = get_gpt_info(dev_desc); 381 382 if (numparts > 0) { 383 if (namestr) { 384 char disk_guid[UUID_STR_LEN + 1]; 385 char *partitions_list; 386 int partlistlen; 387 int ret = -1; 388 389 ret = get_disk_guid(dev_desc, disk_guid); 390 if (ret < 0) 391 return ret; 392 393 partlistlen = calc_parts_list_len(numparts); 394 partitions_list = malloc(partlistlen); 395 if (!partitions_list) { 396 del_gpt_info(); 397 return -ENOMEM; 398 } 399 memset(partitions_list, '\0', partlistlen); 400 401 ret = create_gpt_partitions_list(numparts, disk_guid, 402 partitions_list); 403 if (ret < 0) 404 printf("Error: Could not create partition list string!\n"); 405 else 406 env_set(namestr, partitions_list); 407 408 free(partitions_list); 409 } else { 410 print_gpt_info(); 411 } 412 del_gpt_info(); 413 return 0; 414 } 415 return numparts; 416} 417#endif 418 419/** 420 * set_gpt_info(): Fill partition information from string 421 * function allocates memory, remember to free! 422 * 423 * @param dev_desc - pointer block device descriptor 424 * @param str_part - pointer to string with partition information 425 * @param str_disk_guid - pointer to pointer to allocated string with disk guid 426 * @param partitions - pointer to pointer to allocated partitions array 427 * @param parts_count - number of partitions 428 * 429 * Return: - zero on success, otherwise error 430 * 431 */ 432static int set_gpt_info(struct blk_desc *dev_desc, 433 const char *str_part, 434 char **str_disk_guid, 435 struct disk_partition **partitions, 436 u8 *parts_count) 437{ 438 char *tok, *str, *s; 439 int i; 440 char *val, *p; 441 int p_count; 442 struct disk_partition *parts; 443 int errno = 0; 444 uint64_t size_ll, start_ll; 445 lbaint_t offset = 0; 446 int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS); 447 448 debug("%s: lba num: 0x%x %d\n", __func__, 449 (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); 450 451 if (str_part == NULL) 452 return -1; 453 454 str = strdup(str_part); 455 if (str == NULL) 456 return -ENOMEM; 457 458 /* extract disk guid */ 459 s = str; 460 val = extract_val(str, "uuid_disk"); 461 if (!val) { 462#ifdef CONFIG_RANDOM_UUID 463 *str_disk_guid = malloc(UUID_STR_LEN + 1); 464 if (*str_disk_guid == NULL) 465 return -ENOMEM; 466 gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD); 467#else 468 free(str); 469 return -2; 470#endif 471 } else { 472 val = strsep(&val, ";"); 473 if (extract_env(val, &p)) 474 p = val; 475 *str_disk_guid = strdup(p); 476 free(val); 477 /* Move s to first partition */ 478 strsep(&s, ";"); 479 } 480 if (s == NULL) { 481 printf("Error: is the partitions string NULL-terminated?\n"); 482 return -EINVAL; 483 } 484 if (strnlen(s, max_str_part) == 0) 485 return -3; 486 487 i = strnlen(s, max_str_part) - 1; 488 if (s[i] == ';') 489 s[i] = '\0'; 490 491 /* calculate expected number of partitions */ 492 p_count = 1; 493 p = s; 494 while (*p) { 495 if (*p++ == ';') 496 p_count++; 497 } 498 499 /* allocate memory for partitions */ 500 parts = calloc(sizeof(struct disk_partition), p_count); 501 if (parts == NULL) 502 return -ENOMEM; 503 504 /* retrieve partitions data from string */ 505 for (i = 0; i < p_count; i++) { 506 tok = strsep(&s, ";"); 507 508 if (tok == NULL) 509 break; 510 511 /* uuid */ 512 val = extract_val(tok, "uuid"); 513 if (!val) { 514 /* 'uuid' is optional if random uuid's are enabled */ 515#ifdef CONFIG_RANDOM_UUID 516 gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD); 517#else 518 errno = -4; 519 goto err; 520#endif 521 } else { 522 if (extract_env(val, &p)) 523 p = val; 524 if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) { 525 printf("Wrong uuid format for partition %d\n", i); 526 errno = -4; 527 goto err; 528 } 529 strncpy((char *)parts[i].uuid, p, max_str_part); 530 free(val); 531 } 532#ifdef CONFIG_PARTITION_TYPE_GUID 533 /* guid */ 534 val = extract_val(tok, "type"); 535 if (val) { 536 /* 'type' is optional */ 537 if (extract_env(val, &p)) 538 p = val; 539 if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) { 540 printf("Wrong type guid format for partition %d\n", 541 i); 542 errno = -4; 543 goto err; 544 } 545 strncpy((char *)parts[i].type_guid, p, max_str_part); 546 free(val); 547 } 548#endif 549 /* name */ 550 val = extract_val(tok, "name"); 551 if (!val) { /* name is mandatory */ 552 errno = -4; 553 goto err; 554 } 555 if (extract_env(val, &p)) 556 p = val; 557 if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) { 558 errno = -4; 559 goto err; 560 } 561 strncpy((char *)parts[i].name, p, max_str_part); 562 free(val); 563 564 /* size */ 565 val = extract_val(tok, "size"); 566 if (!val) { /* 'size' is mandatory */ 567 errno = -4; 568 goto err; 569 } 570 if (extract_env(val, &p)) 571 p = val; 572 if ((strcmp(p, "-") == 0)) { 573 /* Let part efi module to auto extend the size */ 574 parts[i].size = 0; 575 } else { 576 size_ll = ustrtoull(p, &p, 0); 577 parts[i].size = lldiv(size_ll, dev_desc->blksz); 578 } 579 580 free(val); 581 582 /* start address */ 583 val = extract_val(tok, "start"); 584 if (val) { /* start address is optional */ 585 if (extract_env(val, &p)) 586 p = val; 587 start_ll = ustrtoull(p, &p, 0); 588 parts[i].start = lldiv(start_ll, dev_desc->blksz); 589 free(val); 590 } 591 592 offset += parts[i].size + parts[i].start; 593 594 /* bootable */ 595 if (found_key(tok, "bootable")) 596 parts[i].bootable = PART_BOOTABLE; 597 } 598 599 *parts_count = p_count; 600 *partitions = parts; 601 free(str); 602 603 return 0; 604err: 605 free(str); 606 free(*str_disk_guid); 607 free(parts); 608 609 return errno; 610} 611 612static int gpt_repair(struct blk_desc *blk_dev_desc) 613{ 614 int ret = 0; 615 616 ret = gpt_repair_headers(blk_dev_desc); 617 618 return ret; 619} 620 621static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part) 622{ 623 int ret; 624 char *str_disk_guid; 625 u8 part_count = 0; 626 struct disk_partition *partitions = NULL; 627 628 /* fill partitions */ 629 ret = set_gpt_info(blk_dev_desc, str_part, 630 &str_disk_guid, &partitions, &part_count); 631 if (ret) { 632 if (ret == -1) 633 printf("No partition list provided\n"); 634 if (ret == -2) 635 printf("Missing disk guid\n"); 636 if ((ret == -3) || (ret == -4)) 637 printf("Partition list incomplete\n"); 638 return -1; 639 } 640 641 /* save partitions layout to disk */ 642 ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count); 643 free(str_disk_guid); 644 free(partitions); 645 646 return ret; 647} 648 649static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) 650{ 651 ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1, 652 blk_dev_desc->blksz); 653 struct disk_partition *partitions = NULL; 654 gpt_entry *gpt_pte = NULL; 655 char *str_disk_guid; 656 u8 part_count = 0; 657 int ret = 0; 658 659 /* fill partitions */ 660 ret = set_gpt_info(blk_dev_desc, str_part, 661 &str_disk_guid, &partitions, &part_count); 662 if (ret) { 663 if (ret == -1) { 664 printf("No partition list provided - only basic check\n"); 665 ret = gpt_verify_headers(blk_dev_desc, gpt_head, 666 &gpt_pte); 667 goto out; 668 } 669 if (ret == -2) 670 printf("Missing disk guid\n"); 671 if ((ret == -3) || (ret == -4)) 672 printf("Partition list incomplete\n"); 673 return -1; 674 } 675 676 /* Check partition layout with provided pattern */ 677 ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count, 678 gpt_head, &gpt_pte); 679 free(str_disk_guid); 680 free(partitions); 681 out: 682 free(gpt_pte); 683 return ret; 684} 685 686/** 687 * gpt_enumerate() - Enumerate partition names into environment variable. 688 * 689 * Enumerate partition names. Partition names are stored in gpt_partition_list 690 * environment variable. Each partition name is delimited by space. 691 * 692 * @desc: block device descriptor 693 * 694 * @Return: '0' on success and -ve error on failure 695 */ 696static int gpt_enumerate(struct blk_desc *desc) 697{ 698 struct part_driver *first_drv, *part_drv; 699 int str_len = 0, tmp_len; 700 char part_list[2048]; 701 int n_drvs; 702 char *ptr; 703 704 part_list[0] = 0; 705 n_drvs = part_driver_get_count(); 706 if (!n_drvs) { 707 printf("Failed to get partition driver count\n"); 708 return -ENOENT; 709 } 710 711 first_drv = part_driver_get_first(); 712 for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { 713 struct disk_partition pinfo; 714 int ret; 715 int i; 716 717 if (part_drv->test(desc)) 718 continue; 719 720 for (i = 1; i < part_drv->max_entries; i++) { 721 ret = part_drv->get_info(desc, i, &pinfo); 722 if (ret) 723 continue; 724 725 ptr = &part_list[str_len]; 726 tmp_len = strlen((const char *)pinfo.name); 727 str_len += tmp_len; 728 /* +1 for space */ 729 str_len++; 730 if (str_len > sizeof(part_list)) { 731 printf("Error insufficient memory\n"); 732 return -ENOMEM; 733 } 734 strcpy(ptr, (const char *)pinfo.name); 735 /* One byte for space(" ") delimiter */ 736 ptr[tmp_len] = ' '; 737 } 738 if (*part_list) 739 part_list[strlen(part_list) - 1] = 0; 740 break; 741 } 742 debug("setenv gpt_partition_list %s\n", part_list); 743 744 return env_set("gpt_partition_list", part_list); 745} 746 747/** 748 * gpt_setenv_part_variables() - setup partition environmental variables 749 * 750 * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr 751 * and gpt_partition_size, gpt_partition_bootable environment variables. 752 * 753 * @pinfo: pointer to disk partition 754 * @i: partition entry 755 * 756 * @Return: '0' on success and -ENOENT on failure 757 */ 758static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i) 759{ 760 int ret; 761 762 ret = env_set_hex("gpt_partition_addr", pinfo->start); 763 if (ret) 764 goto fail; 765 766 ret = env_set_hex("gpt_partition_size", pinfo->size); 767 if (ret) 768 goto fail; 769 770 ret = env_set_hex("gpt_partition_entry", i); 771 if (ret) 772 goto fail; 773 774 ret = env_set("gpt_partition_name", (const char *)pinfo->name); 775 if (ret) 776 goto fail; 777 778 ret = env_set_ulong("gpt_partition_bootable", !!(pinfo->bootable & PART_BOOTABLE)); 779 if (ret) 780 goto fail; 781 782 return 0; 783 784fail: 785 return -ENOENT; 786} 787 788/** 789 * gpt_setenv() - Dynamically setup environment variables. 790 * 791 * Dynamically setup environment variables for name, index, offset and size 792 * for partition in GPT table after running "gpt setenv" for a partition name. 793 * 794 * @desc: block device descriptor 795 * @name: partition name 796 * 797 * @Return: '0' on success and -ve err on failure 798 */ 799static int gpt_setenv(struct blk_desc *desc, const char *name) 800{ 801 struct part_driver *first_drv, *part_drv; 802 int n_drvs; 803 int ret = -1; 804 805 n_drvs = part_driver_get_count(); 806 if (!n_drvs) { 807 printf("Failed to get partition driver count\n"); 808 goto fail; 809 } 810 811 first_drv = part_driver_get_first(); 812 for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { 813 struct disk_partition pinfo; 814 int i; 815 816 for (i = 1; i < part_drv->max_entries; i++) { 817 ret = part_drv->get_info(desc, i, &pinfo); 818 if (ret) 819 continue; 820 821 if (!strcmp(name, (const char *)pinfo.name)) { 822 /* match found, setup environment variables */ 823 ret = gpt_setenv_part_variables(&pinfo, i); 824 if (ret) 825 goto fail; 826 827 return 0; 828 } 829 } 830 } 831 832fail: 833 return ret; 834} 835 836static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) 837{ 838 int ret; 839 char disk_guid[UUID_STR_LEN + 1]; 840 841 ret = get_disk_guid(dev_desc, disk_guid); 842 if (ret < 0) 843 return CMD_RET_FAILURE; 844 845 if (namestr) 846 env_set(namestr, disk_guid); 847 else 848 printf("%s\n", disk_guid); 849 850 return ret; 851} 852 853#ifdef CONFIG_CMD_GPT_RENAME 854static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, 855 char *name1, char *name2) 856{ 857 struct list_head *pos; 858 struct disk_part *curr; 859 struct disk_partition *new_partitions = NULL; 860 char disk_guid[UUID_STR_LEN + 1]; 861 char *partitions_list, *str_disk_guid = NULL; 862 u8 part_count = 0; 863 int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; 864 865 if (!subcomm || !name1 || !name2 || 866 (strcmp(subcomm, "swap") && strcmp(subcomm, "rename") && 867 strcmp(subcomm, "transpose"))) 868 return -EINVAL; 869 870 ret = get_disk_guid(dev_desc, disk_guid); 871 if (ret < 0) 872 return ret; 873 /* 874 * Allocates disk_partitions, requiring matching call to del_gpt_info() 875 * if successful. 876 */ 877 numparts = get_gpt_info(dev_desc); 878 if (numparts <= 0) 879 return numparts ? numparts : -ENODEV; 880 881 partlistlen = calc_parts_list_len(numparts); 882 partitions_list = malloc(partlistlen); 883 if (!partitions_list) { 884 del_gpt_info(); 885 return -ENOMEM; 886 } 887 memset(partitions_list, '\0', partlistlen); 888 889 ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); 890 if (ret < 0) { 891 free(partitions_list); 892 return ret; 893 } 894 /* 895 * Uncomment the following line to print a string that 'gpt write' 896 * or 'gpt verify' will accept as input. 897 */ 898 debug("OLD partitions_list is %s with %u chars\n", partitions_list, 899 (unsigned)strlen(partitions_list)); 900 901 /* set_gpt_info allocates new_partitions and str_disk_guid */ 902 ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, 903 &new_partitions, &part_count); 904 if (ret < 0) 905 goto out; 906 907 if (!strcmp(subcomm, "swap")) { 908 if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { 909 printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); 910 ret = -EINVAL; 911 goto out; 912 } 913 list_for_each(pos, &disk_partitions) { 914 curr = list_entry(pos, struct disk_part, list); 915 if (!strcmp((char *)curr->gpt_part_info.name, name1)) { 916 strcpy((char *)curr->gpt_part_info.name, name2); 917 ctr1++; 918 } else if (!strcmp((char *)curr->gpt_part_info.name, name2)) { 919 strcpy((char *)curr->gpt_part_info.name, name1); 920 ctr2++; 921 } 922 } 923 if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) { 924 printf("Cannot swap partition names except in pairs.\n"); 925 ret = -EINVAL; 926 goto out; 927 } 928 } else if (!strcmp(subcomm, "transpose")) { 929 int idx1, idx2; 930 struct disk_partition* first = NULL; 931 struct disk_partition* second= NULL; 932 struct disk_partition tmp_part; 933 934 idx1 = simple_strtoul(name1, NULL, 10); 935 idx2 = simple_strtoul(name2, NULL, 10); 936 if (idx1 == idx2) { 937 printf("Cannot swap partition with itself\n"); 938 ret = -EINVAL; 939 goto out; 940 } 941 942 list_for_each(pos, &disk_partitions) { 943 curr = list_entry(pos, struct disk_part, list); 944 if (curr->partnum == idx1) 945 first = &curr->gpt_part_info; 946 else if (curr->partnum == idx2) 947 second = &curr->gpt_part_info; 948 } 949 if (!first) { 950 printf("Illegal partition number %s\n", name1); 951 ret = -EINVAL; 952 goto out; 953 } 954 if (!second) { 955 printf("Illegal partition number %s\n", name2); 956 ret = -EINVAL; 957 goto out; 958 } 959 960 tmp_part = *first; 961 *first = *second; 962 *second = tmp_part; 963 } else { /* rename */ 964 if (strlen(name2) > PART_NAME_LEN) { 965 printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN); 966 ret = -EINVAL; 967 goto out; 968 } 969 partnum = (int)simple_strtol(name1, NULL, 10); 970 if ((partnum < 0) || (partnum > numparts)) { 971 printf("Illegal partition number %s\n", name1); 972 ret = -EINVAL; 973 goto out; 974 } 975 ret = part_get_info(dev_desc, partnum, new_partitions); 976 if (ret < 0) 977 goto out; 978 979 /* U-Boot partition numbering starts at 1 */ 980 list_for_each(pos, &disk_partitions) { 981 curr = list_entry(pos, struct disk_part, list); 982 if (i == partnum) { 983 strcpy((char *)curr->gpt_part_info.name, name2); 984 break; 985 } 986 i++; 987 } 988 } 989 990 ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list); 991 if (ret < 0) 992 goto out; 993 debug("NEW partitions_list is %s with %u chars\n", partitions_list, 994 (unsigned)strlen(partitions_list)); 995 996 ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, 997 &new_partitions, &part_count); 998 /* 999 * Even though valid pointers are here passed into set_gpt_info(), 1000 * it mallocs again, and there's no way to tell which failed. 1001 */ 1002 if (ret < 0) 1003 goto out; 1004 1005 debug("Writing new partition table\n"); 1006 ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); 1007 if (ret < 0) { 1008 printf("Writing new partition table failed\n"); 1009 goto out; 1010 } 1011 1012 debug("Reading back new partition table\n"); 1013 /* 1014 * Empty the existing disk_partitions list, as otherwise the memory in 1015 * the original list is unreachable. 1016 */ 1017 del_gpt_info(); 1018 numparts = get_gpt_info(dev_desc); 1019 if (numparts <= 0) { 1020 ret = numparts ? numparts : -ENODEV; 1021 goto out; 1022 } 1023 printf("new partition table with %d partitions is:\n", numparts); 1024 print_gpt_info(); 1025 out: 1026 del_gpt_info(); 1027#ifdef CONFIG_RANDOM_UUID 1028 free(str_disk_guid); 1029#endif 1030 free(new_partitions); 1031 free(partitions_list); 1032 return ret; 1033} 1034 1035/** 1036 * gpt_set_bootable() - Set bootable flags for partitions 1037 * 1038 * Sets the bootable flag for any partition names in the comma separated list of 1039 * partition names. Any partitions not in the list have their bootable flag 1040 * cleared 1041 * 1042 * @desc: block device descriptor 1043 * @name: Comma separated list of partition names 1044 * 1045 * @Return: '0' on success and -ve error on failure 1046 */ 1047static int gpt_set_bootable(struct blk_desc *blk_dev_desc, char *const part_list) 1048{ 1049 char *name; 1050 char disk_guid[UUID_STR_LEN + 1]; 1051 struct list_head *pos; 1052 struct disk_part *curr; 1053 struct disk_partition *partitions = NULL; 1054 int part_count = 0; 1055 int ret = get_disk_guid(blk_dev_desc, disk_guid); 1056 1057 if (ret < 0) 1058 return ret; 1059 1060 ret = get_gpt_info(blk_dev_desc); 1061 if (ret <= 0) 1062 goto out; 1063 1064 part_count = ret; 1065 partitions = malloc(sizeof(*partitions) * part_count); 1066 if (!partitions) { 1067 ret = -ENOMEM; 1068 goto out; 1069 } 1070 1071 /* Copy partitions and clear bootable flag */ 1072 part_count = 0; 1073 list_for_each(pos, &disk_partitions) { 1074 curr = list_entry(pos, struct disk_part, list); 1075 partitions[part_count] = curr->gpt_part_info; 1076 partitions[part_count].bootable &= ~PART_BOOTABLE; 1077 part_count++; 1078 } 1079 1080 name = strtok(part_list, ","); 1081 while (name) { 1082 bool found = false; 1083 1084 for (int i = 0; i < part_count; i++) { 1085 if (strcmp((char *)partitions[i].name, name) == 0) { 1086 partitions[i].bootable |= PART_BOOTABLE; 1087 found = true; 1088 } 1089 } 1090 1091 if (!found) { 1092 printf("Warning: No partition matching '%s' found\n", 1093 name); 1094 } 1095 1096 name = strtok(NULL, ","); 1097 } 1098 1099 ret = gpt_restore(blk_dev_desc, disk_guid, partitions, part_count); 1100 1101out: 1102 del_gpt_info(); 1103 1104 if (partitions) 1105 free(partitions); 1106 1107 return ret; 1108} 1109#endif 1110 1111/** 1112 * do_gpt(): Perform GPT operations 1113 * 1114 * @param cmdtp - command name 1115 * @param flag 1116 * @param argc 1117 * @param argv 1118 * 1119 * Return: zero on success; otherwise error 1120 */ 1121static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 1122{ 1123 int ret = CMD_RET_SUCCESS; 1124 int dev = 0; 1125 char *ep; 1126 struct blk_desc *blk_dev_desc = NULL; 1127 1128#ifndef CONFIG_CMD_GPT_RENAME 1129 if (argc < 4 || argc > 5) 1130#else 1131 if (argc < 4 || argc > 6) 1132#endif 1133 return CMD_RET_USAGE; 1134 1135 dev = (int)dectoul(argv[3], &ep); 1136 if (!ep || ep[0] != '\0') { 1137 printf("'%s' is not a number\n", argv[3]); 1138 return CMD_RET_USAGE; 1139 } 1140 blk_dev_desc = blk_get_dev(argv[2], dev); 1141 if (!blk_dev_desc) { 1142 printf("%s: %s dev %d NOT available\n", 1143 __func__, argv[2], dev); 1144 return CMD_RET_FAILURE; 1145 } 1146 1147 if (strcmp(argv[1], "repair") == 0) { 1148 printf("Repairing GPT: "); 1149 ret = gpt_repair(blk_dev_desc); 1150 } else if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { 1151 printf("Writing GPT: "); 1152 ret = gpt_default(blk_dev_desc, argv[4]); 1153 } else if ((strcmp(argv[1], "verify") == 0)) { 1154 ret = gpt_verify(blk_dev_desc, argv[4]); 1155 printf("Verify GPT: "); 1156 } else if ((strcmp(argv[1], "setenv") == 0)) { 1157 ret = gpt_setenv(blk_dev_desc, argv[4]); 1158 } else if ((strcmp(argv[1], "enumerate") == 0)) { 1159 ret = gpt_enumerate(blk_dev_desc); 1160 } else if (strcmp(argv[1], "guid") == 0) { 1161 ret = do_disk_guid(blk_dev_desc, argv[4]); 1162#ifdef CONFIG_CMD_GPT_RENAME 1163 } else if (strcmp(argv[1], "read") == 0) { 1164 ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL); 1165 } else if ((strcmp(argv[1], "swap") == 0) || 1166 (strcmp(argv[1], "rename") == 0) || 1167 (strcmp(argv[1], "transpose") == 0)) { 1168 ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); 1169 } else if ((strcmp(argv[1], "set-bootable") == 0)) { 1170 ret = gpt_set_bootable(blk_dev_desc, argv[4]); 1171#endif 1172 } else { 1173 return CMD_RET_USAGE; 1174 } 1175 1176 if (ret) { 1177 printf("error!\n"); 1178 return CMD_RET_FAILURE; 1179 } 1180 1181 printf("success!\n"); 1182 return CMD_RET_SUCCESS; 1183} 1184 1185U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, 1186 "GUID Partition Table", 1187 "<command> <interface> <dev> <partitions_list>\n" 1188 " - GUID partition table restoration and validity check\n" 1189 " Restore or verify GPT information on a device connected\n" 1190 " to interface\n" 1191 " Example usage:\n" 1192 " gpt repair mmc 0\n" 1193 " - repair the GPT on the device\n" 1194 " gpt write mmc 0 $partitions\n" 1195 " - write the GPT to device\n" 1196 " gpt verify mmc 0 $partitions\n" 1197 " - verify the GPT on device against $partitions\n" 1198 " gpt setenv mmc 0 $name\n" 1199 " - setup environment variables for partition $name:\n" 1200 " gpt_partition_addr, gpt_partition_size,\n" 1201 " gpt_partition_name, gpt_partition_entry,\n" 1202 " gpt_partition_bootable\n" 1203 " gpt enumerate mmc 0\n" 1204 " - store list of partitions to gpt_partition_list environment variable\n" 1205 " gpt guid <interface> <dev>\n" 1206 " - print disk GUID\n" 1207 " gpt guid <interface> <dev> <varname>\n" 1208 " - set environment variable to disk GUID\n" 1209 " Example usage:\n" 1210 " gpt guid mmc 0\n" 1211 " gpt guid mmc 0 varname\n" 1212#ifdef CONFIG_CMD_GPT_RENAME 1213 "gpt partition renaming commands:\n" 1214 " gpt read <interface> <dev> [<varname>]\n" 1215 " - read GPT into a data structure for manipulation\n" 1216 " - read GPT partitions into environment variable\n" 1217 " gpt swap <interface> <dev> <name1> <name2>\n" 1218 " - change all partitions named name1 to name2\n" 1219 " and vice-versa\n" 1220 " gpt transpose <interface> <dev> <part1> <part2>\n" 1221 " - Swap the order of the entries for part1 and part2 in the partition table\n" 1222 " gpt rename <interface> <dev> <part> <name>\n" 1223 " - rename the specified partition\n" 1224 " gpt set-bootable <interface> <dev> <list>\n" 1225 " - make partition names in list bootable\n" 1226 " Example usage:\n" 1227 " gpt swap mmc 0 foo bar\n" 1228 " gpt rename mmc 0 3 foo\n" 1229 " gpt set-bootable mmc 0 boot_a,boot_b\n" 1230 " gpt transpose mmc 0 1 2\n" 1231#endif 1232); 1233