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