uboot/cmd/gpt.c
<<
>>
Prefs
   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
 165static int calc_parts_list_len(int numparts)
 166{
 167        int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
 168        /* for the comma */
 169        partlistlen++;
 170
 171        /* per-partition additions; numparts starts at 1, so this should be correct */
 172        partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
 173        /* see part.h for definition of struct disk_partition */
 174        partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1);
 175        partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1);
 176        partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
 177        /* for the terminating null */
 178        partlistlen++;
 179        debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
 180              numparts);
 181        return partlistlen;
 182}
 183
 184#ifdef CONFIG_CMD_GPT_RENAME
 185static void del_gpt_info(void)
 186{
 187        struct list_head *pos = &disk_partitions;
 188        struct disk_part *curr;
 189        while (!list_empty(pos)) {
 190                curr = list_entry(pos->next, struct disk_part, list);
 191                list_del(pos->next);
 192                free(curr);
 193        }
 194}
 195
 196static struct disk_part *allocate_disk_part(struct disk_partition *info,
 197                                            int partnum)
 198{
 199        struct disk_part *newpart;
 200        newpart = calloc(1, sizeof(struct disk_part));
 201        if (!newpart)
 202                return ERR_PTR(-ENOMEM);
 203
 204        newpart->gpt_part_info.start = info->start;
 205        newpart->gpt_part_info.size = info->size;
 206        newpart->gpt_part_info.blksz = info->blksz;
 207        strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
 208                PART_NAME_LEN);
 209        newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
 210        strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
 211                PART_TYPE_LEN);
 212        newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
 213        newpart->gpt_part_info.bootable = info->bootable;
 214#ifdef CONFIG_PARTITION_UUIDS
 215        strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
 216                UUID_STR_LEN);
 217        /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
 218        newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
 219#endif
 220        newpart->partnum = partnum;
 221
 222        return newpart;
 223}
 224
 225static void prettyprint_part_size(char *sizestr, lbaint_t partsize,
 226                                  lbaint_t blksize)
 227{
 228        unsigned long long partbytes, partmegabytes;
 229
 230        partbytes = partsize * blksize;
 231        partmegabytes = lldiv(partbytes, SZ_1M);
 232        snprintf(sizestr, 16, "%lluMiB", partmegabytes);
 233}
 234
 235static void print_gpt_info(void)
 236{
 237        struct list_head *pos;
 238        struct disk_part *curr;
 239        char partstartstr[16];
 240        char partsizestr[16];
 241
 242        list_for_each(pos, &disk_partitions) {
 243                curr = list_entry(pos, struct disk_part, list);
 244                prettyprint_part_size(partstartstr, curr->gpt_part_info.start,
 245                                      curr->gpt_part_info.blksz);
 246                prettyprint_part_size(partsizestr, curr->gpt_part_info.size,
 247                                      curr->gpt_part_info.blksz);
 248
 249                printf("Partition %d:\n", curr->partnum);
 250                printf("Start %s, size %s\n", partstartstr, partsizestr);
 251                printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
 252                       curr->gpt_part_info.name);
 253                printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
 254                       curr->gpt_part_info.bootable & PART_BOOTABLE);
 255#ifdef CONFIG_PARTITION_UUIDS
 256                printf("UUID %s\n", curr->gpt_part_info.uuid);
 257#endif
 258                printf("\n");
 259        }
 260}
 261
 262/*
 263 * create the string that upstream 'gpt write' command will accept as an
 264 * argument
 265 *
 266 * From doc/README.gpt, Format of partitions layout:
 267 *    "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
 268 *      name=kernel,size=60MiB,uuid=...;"
 269 * The fields 'name' and 'size' are mandatory for every partition.
 270 * The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
 271 * are optional if CONFIG_RANDOM_UUID is enabled.
 272 */
 273static int create_gpt_partitions_list(int numparts, const char *guid,
 274                                      char *partitions_list)
 275{
 276        struct list_head *pos;
 277        struct disk_part *curr;
 278        char partstr[PART_NAME_LEN + 1];
 279
 280        if (!partitions_list)
 281                return -EINVAL;
 282
 283        strcpy(partitions_list, "uuid_disk=");
 284        strncat(partitions_list, guid, UUID_STR_LEN + 1);
 285        strcat(partitions_list, ";");
 286
 287        list_for_each(pos, &disk_partitions) {
 288                curr = list_entry(pos, struct disk_part, list);
 289                strcat(partitions_list, "name=");
 290                strncat(partitions_list, (const char *)curr->gpt_part_info.name,
 291                        PART_NAME_LEN + 1);
 292                sprintf(partstr, ",start=0x%llx",
 293                        (unsigned long long)curr->gpt_part_info.start *
 294                                            curr->gpt_part_info.blksz);
 295                /* one extra byte for NULL */
 296                strncat(partitions_list, partstr, PART_NAME_LEN + 1);
 297                sprintf(partstr, ",size=0x%llx",
 298                        (unsigned long long)curr->gpt_part_info.size *
 299                                            curr->gpt_part_info.blksz);
 300                strncat(partitions_list, partstr, PART_NAME_LEN + 1);
 301
 302                strcat(partitions_list, ",uuid=");
 303                strncat(partitions_list, curr->gpt_part_info.uuid,
 304                        UUID_STR_LEN + 1);
 305                strcat(partitions_list, ";");
 306        }
 307        return 0;
 308}
 309
 310/*
 311 * read partition info into disk_partitions list where
 312 * it can be printed or modified
 313 */
 314static int get_gpt_info(struct blk_desc *dev_desc)
 315{
 316        /* start partition numbering at 1, as U-Boot does */
 317        int valid_parts = 0, p, ret;
 318        struct disk_partition info;
 319        struct disk_part *new_disk_part;
 320
 321        /*
 322         * Always re-read partition info from device, in case
 323         * it has changed
 324         */
 325        INIT_LIST_HEAD(&disk_partitions);
 326
 327        for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
 328                ret = part_get_info(dev_desc, p, &info);
 329                if (ret)
 330                        continue;
 331
 332                /* Add 1 here because counter is zero-based but p1 is
 333                   the first partition */
 334                new_disk_part = allocate_disk_part(&info, valid_parts+1);
 335                if (IS_ERR(new_disk_part))
 336                        goto out;
 337
 338                list_add_tail(&new_disk_part->list, &disk_partitions);
 339                valid_parts++;
 340        }
 341        if (valid_parts == 0) {
 342                printf("** No valid partitions found **\n");
 343                goto out;
 344        }
 345        return valid_parts;
 346 out:
 347        if (valid_parts >= 1)
 348                del_gpt_info();
 349        return -ENODEV;
 350}
 351
 352/* a wrapper to test get_gpt_info */
 353static int do_get_gpt_info(struct blk_desc *dev_desc, char * const namestr)
 354{
 355        int numparts;
 356
 357        numparts = get_gpt_info(dev_desc);
 358
 359        if (numparts > 0) {
 360                if (namestr) {
 361                        char disk_guid[UUID_STR_LEN + 1];
 362                        char *partitions_list;
 363                        int partlistlen;
 364                        int ret = -1;
 365
 366                        ret = get_disk_guid(dev_desc, disk_guid);
 367                        if (ret < 0)
 368                                return ret;
 369
 370                        partlistlen = calc_parts_list_len(numparts);
 371                        partitions_list = malloc(partlistlen);
 372                        if (!partitions_list) {
 373                                del_gpt_info();
 374                                return -ENOMEM;
 375                        }
 376                        memset(partitions_list, '\0', partlistlen);
 377
 378                        ret = create_gpt_partitions_list(numparts, disk_guid,
 379                                                         partitions_list);
 380                        if (ret < 0)
 381                                printf("Error: Could not create partition list string!\n");
 382                        else
 383                                env_set(namestr, partitions_list);
 384
 385                        free(partitions_list);
 386                } else {
 387                        print_gpt_info();
 388                }
 389                del_gpt_info();
 390                return 0;
 391        }
 392        return numparts;
 393}
 394#endif
 395
 396/**
 397 * set_gpt_info(): Fill partition information from string
 398 *              function allocates memory, remember to free!
 399 *
 400 * @param dev_desc - pointer block device descriptor
 401 * @param str_part - pointer to string with partition information
 402 * @param str_disk_guid - pointer to pointer to allocated string with disk guid
 403 * @param partitions - pointer to pointer to allocated partitions array
 404 * @param parts_count - number of partitions
 405 *
 406 * @return - zero on success, otherwise error
 407 *
 408 */
 409static int set_gpt_info(struct blk_desc *dev_desc,
 410                        const char *str_part,
 411                        char **str_disk_guid,
 412                        struct disk_partition **partitions,
 413                        u8 *parts_count)
 414{
 415        char *tok, *str, *s;
 416        int i;
 417        char *val, *p;
 418        int p_count;
 419        struct disk_partition *parts;
 420        int errno = 0;
 421        uint64_t size_ll, start_ll;
 422        lbaint_t offset = 0;
 423        int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
 424
 425        debug("%s:  lba num: 0x%x %d\n", __func__,
 426              (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
 427
 428        if (str_part == NULL)
 429                return -1;
 430
 431        str = strdup(str_part);
 432        if (str == NULL)
 433                return -ENOMEM;
 434
 435        /* extract disk guid */
 436        s = str;
 437        val = extract_val(str, "uuid_disk");
 438        if (!val) {
 439#ifdef CONFIG_RANDOM_UUID
 440                *str_disk_guid = malloc(UUID_STR_LEN + 1);
 441                if (*str_disk_guid == NULL)
 442                        return -ENOMEM;
 443                gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
 444#else
 445                free(str);
 446                return -2;
 447#endif
 448        } else {
 449                val = strsep(&val, ";");
 450                if (extract_env(val, &p))
 451                        p = val;
 452                *str_disk_guid = strdup(p);
 453                free(val);
 454                /* Move s to first partition */
 455                strsep(&s, ";");
 456        }
 457        if (s == NULL) {
 458                printf("Error: is the partitions string NULL-terminated?\n");
 459                return -EINVAL;
 460        }
 461        if (strnlen(s, max_str_part) == 0)
 462                return -3;
 463
 464        i = strnlen(s, max_str_part) - 1;
 465        if (s[i] == ';')
 466                s[i] = '\0';
 467
 468        /* calculate expected number of partitions */
 469        p_count = 1;
 470        p = s;
 471        while (*p) {
 472                if (*p++ == ';')
 473                        p_count++;
 474        }
 475
 476        /* allocate memory for partitions */
 477        parts = calloc(sizeof(struct disk_partition), p_count);
 478        if (parts == NULL)
 479                return -ENOMEM;
 480
 481        /* retrieve partitions data from string */
 482        for (i = 0; i < p_count; i++) {
 483                tok = strsep(&s, ";");
 484
 485                if (tok == NULL)
 486                        break;
 487
 488                /* uuid */
 489                val = extract_val(tok, "uuid");
 490                if (!val) {
 491                        /* 'uuid' is optional if random uuid's are enabled */
 492#ifdef CONFIG_RANDOM_UUID
 493                        gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
 494#else
 495                        errno = -4;
 496                        goto err;
 497#endif
 498                } else {
 499                        if (extract_env(val, &p))
 500                                p = val;
 501                        if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) {
 502                                printf("Wrong uuid format for partition %d\n", i);
 503                                errno = -4;
 504                                goto err;
 505                        }
 506                        strncpy((char *)parts[i].uuid, p, max_str_part);
 507                        free(val);
 508                }
 509#ifdef CONFIG_PARTITION_TYPE_GUID
 510                /* guid */
 511                val = extract_val(tok, "type");
 512                if (val) {
 513                        /* 'type' is optional */
 514                        if (extract_env(val, &p))
 515                                p = val;
 516                        if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) {
 517                                printf("Wrong type guid format for partition %d\n",
 518                                       i);
 519                                errno = -4;
 520                                goto err;
 521                        }
 522                        strncpy((char *)parts[i].type_guid, p, max_str_part);
 523                        free(val);
 524                }
 525#endif
 526                /* name */
 527                val = extract_val(tok, "name");
 528                if (!val) { /* name is mandatory */
 529                        errno = -4;
 530                        goto err;
 531                }
 532                if (extract_env(val, &p))
 533                        p = val;
 534                if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) {
 535                        errno = -4;
 536                        goto err;
 537                }
 538                strncpy((char *)parts[i].name, p, max_str_part);
 539                free(val);
 540
 541                /* size */
 542                val = extract_val(tok, "size");
 543                if (!val) { /* 'size' is mandatory */
 544                        errno = -4;
 545                        goto err;
 546                }
 547                if (extract_env(val, &p))
 548                        p = val;
 549                if ((strcmp(p, "-") == 0)) {
 550                        /* Let part efi module to auto extend the size */
 551                        parts[i].size = 0;
 552                } else {
 553                        size_ll = ustrtoull(p, &p, 0);
 554                        parts[i].size = lldiv(size_ll, dev_desc->blksz);
 555                }
 556
 557                free(val);
 558
 559                /* start address */
 560                val = extract_val(tok, "start");
 561                if (val) { /* start address is optional */
 562                        if (extract_env(val, &p))
 563                                p = val;
 564                        start_ll = ustrtoull(p, &p, 0);
 565                        parts[i].start = lldiv(start_ll, dev_desc->blksz);
 566                        free(val);
 567                }
 568
 569                offset += parts[i].size + parts[i].start;
 570
 571                /* bootable */
 572                if (found_key(tok, "bootable"))
 573                        parts[i].bootable = PART_BOOTABLE;
 574        }
 575
 576        *parts_count = p_count;
 577        *partitions = parts;
 578        free(str);
 579
 580        return 0;
 581err:
 582        free(str);
 583        free(*str_disk_guid);
 584        free(parts);
 585
 586        return errno;
 587}
 588
 589static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
 590{
 591        int ret;
 592        char *str_disk_guid;
 593        u8 part_count = 0;
 594        struct disk_partition *partitions = NULL;
 595
 596        /* fill partitions */
 597        ret = set_gpt_info(blk_dev_desc, str_part,
 598                        &str_disk_guid, &partitions, &part_count);
 599        if (ret) {
 600                if (ret == -1)
 601                        printf("No partition list provided\n");
 602                if (ret == -2)
 603                        printf("Missing disk guid\n");
 604                if ((ret == -3) || (ret == -4))
 605                        printf("Partition list incomplete\n");
 606                return -1;
 607        }
 608
 609        /* save partitions layout to disk */
 610        ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
 611        free(str_disk_guid);
 612        free(partitions);
 613
 614        return ret;
 615}
 616
 617static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
 618{
 619        ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
 620                                     blk_dev_desc->blksz);
 621        struct disk_partition *partitions = NULL;
 622        gpt_entry *gpt_pte = NULL;
 623        char *str_disk_guid;
 624        u8 part_count = 0;
 625        int ret = 0;
 626
 627        /* fill partitions */
 628        ret = set_gpt_info(blk_dev_desc, str_part,
 629                        &str_disk_guid, &partitions, &part_count);
 630        if (ret) {
 631                if (ret == -1) {
 632                        printf("No partition list provided - only basic check\n");
 633                        ret = gpt_verify_headers(blk_dev_desc, gpt_head,
 634                                                 &gpt_pte);
 635                        goto out;
 636                }
 637                if (ret == -2)
 638                        printf("Missing disk guid\n");
 639                if ((ret == -3) || (ret == -4))
 640                        printf("Partition list incomplete\n");
 641                return -1;
 642        }
 643
 644        /* Check partition layout with provided pattern */
 645        ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
 646                                    gpt_head, &gpt_pte);
 647        free(str_disk_guid);
 648        free(partitions);
 649 out:
 650        free(gpt_pte);
 651        return ret;
 652}
 653
 654/**
 655 * gpt_enumerate() - Enumerate partition names into environment variable.
 656 *
 657 * Enumerate partition names. Partition names are stored in gpt_partition_list
 658 * environment variable. Each partition name is delimited by space.
 659 *
 660 * @desc: block device descriptor
 661 *
 662 * @Return: '0' on success and -ve error on failure
 663 */
 664static int gpt_enumerate(struct blk_desc *desc)
 665{
 666        struct part_driver *first_drv, *part_drv;
 667        int str_len = 0, tmp_len;
 668        char part_list[2048];
 669        int n_drvs;
 670        char *ptr;
 671
 672        part_list[0] = 0;
 673        n_drvs = part_driver_get_count();
 674        if (!n_drvs) {
 675                printf("Failed to get partition driver count\n");
 676                return -ENOENT;
 677        }
 678
 679        first_drv = part_driver_get_first();
 680        for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) {
 681                struct disk_partition pinfo;
 682                int ret;
 683                int i;
 684
 685                for (i = 1; i < part_drv->max_entries; i++) {
 686                        ret = part_drv->get_info(desc, i, &pinfo);
 687                        if (ret) {
 688                                /* no more entries in table */
 689                                break;
 690                        }
 691
 692                        ptr = &part_list[str_len];
 693                        tmp_len = strlen((const char *)pinfo.name);
 694                        str_len += tmp_len;
 695                        /* +1 for space */
 696                        str_len++;
 697                        if (str_len > sizeof(part_list)) {
 698                                printf("Error insufficient memory\n");
 699                                return -ENOMEM;
 700                        }
 701                        strcpy(ptr, (const char *)pinfo.name);
 702                        /* One byte for space(" ") delimiter */
 703                        ptr[tmp_len] = ' ';
 704                }
 705        }
 706        if (*part_list)
 707                part_list[strlen(part_list) - 1] = 0;
 708        debug("setenv gpt_partition_list %s\n", part_list);
 709
 710        return env_set("gpt_partition_list", part_list);
 711}
 712
 713/**
 714 * gpt_setenv_part_variables() - setup partition environmental variables
 715 *
 716 * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr
 717 * and gpt_partition_size environment variables.
 718 *
 719 * @pinfo: pointer to disk partition
 720 * @i: partition entry
 721 *
 722 * @Return: '0' on success and -ENOENT on failure
 723 */
 724static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i)
 725{
 726        int ret;
 727
 728        ret = env_set_hex("gpt_partition_addr", pinfo->start);
 729        if (ret)
 730                goto fail;
 731
 732        ret = env_set_hex("gpt_partition_size", pinfo->size);
 733        if (ret)
 734                goto fail;
 735
 736        ret = env_set_ulong("gpt_partition_entry", i);
 737        if (ret)
 738                goto fail;
 739
 740        ret = env_set("gpt_partition_name", (const char *)pinfo->name);
 741        if (ret)
 742                goto fail;
 743
 744        return 0;
 745
 746fail:
 747        return -ENOENT;
 748}
 749
 750/**
 751 * gpt_setenv() - Dynamically setup environment variables.
 752 *
 753 * Dynamically setup environment variables for name, index, offset and size
 754 * for partition in GPT table after running "gpt setenv" for a partition name.
 755 *
 756 * @desc: block device descriptor
 757 * @name: partition name
 758 *
 759 * @Return: '0' on success and -ve err on failure
 760 */
 761static int gpt_setenv(struct blk_desc *desc, const char *name)
 762{
 763        struct part_driver *first_drv, *part_drv;
 764        int n_drvs;
 765        int ret = -1;
 766
 767        n_drvs = part_driver_get_count();
 768        if (!n_drvs) {
 769                printf("Failed to get partition driver count\n");
 770                goto fail;
 771        }
 772
 773        first_drv = part_driver_get_first();
 774        for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) {
 775                struct disk_partition pinfo;
 776                int i;
 777
 778                for (i = 1; i < part_drv->max_entries; i++) {
 779                        ret = part_drv->get_info(desc, i, &pinfo);
 780                        if (ret) {
 781                                /* no more entries in table */
 782                                break;
 783                        }
 784
 785                        if (!strcmp(name, (const char *)pinfo.name)) {
 786                                /* match found, setup environment variables */
 787                                ret = gpt_setenv_part_variables(&pinfo, i);
 788                                if (ret)
 789                                        goto fail;
 790
 791                                return 0;
 792                        }
 793                }
 794        }
 795
 796fail:
 797        return ret;
 798}
 799
 800static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
 801{
 802        int ret;
 803        char disk_guid[UUID_STR_LEN + 1];
 804
 805        ret = get_disk_guid(dev_desc, disk_guid);
 806        if (ret < 0)
 807                return CMD_RET_FAILURE;
 808
 809        if (namestr)
 810                env_set(namestr, disk_guid);
 811        else
 812                printf("%s\n", disk_guid);
 813
 814        return ret;
 815}
 816
 817#ifdef CONFIG_CMD_GPT_RENAME
 818static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
 819                               char *name1, char *name2)
 820{
 821        struct list_head *pos;
 822        struct disk_part *curr;
 823        struct disk_partition *new_partitions = NULL;
 824        char disk_guid[UUID_STR_LEN + 1];
 825        char *partitions_list, *str_disk_guid = NULL;
 826        u8 part_count = 0;
 827        int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
 828
 829        if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) ||
 830            (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename"))))
 831                return -EINVAL;
 832
 833        ret = get_disk_guid(dev_desc, disk_guid);
 834        if (ret < 0)
 835                return ret;
 836        /*
 837         * Allocates disk_partitions, requiring matching call to del_gpt_info()
 838         * if successful.
 839         */
 840        numparts = get_gpt_info(dev_desc);
 841        if (numparts <=  0)
 842                return numparts ? numparts : -ENODEV;
 843
 844        partlistlen = calc_parts_list_len(numparts);
 845        partitions_list = malloc(partlistlen);
 846        if (!partitions_list) {
 847                del_gpt_info();
 848                return -ENOMEM;
 849        }
 850        memset(partitions_list, '\0', partlistlen);
 851
 852        ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
 853        if (ret < 0) {
 854                free(partitions_list);
 855                return ret;
 856        }
 857        /*
 858         * Uncomment the following line to print a string that 'gpt write'
 859         * or 'gpt verify' will accept as input.
 860         */
 861        debug("OLD partitions_list is %s with %u chars\n", partitions_list,
 862              (unsigned)strlen(partitions_list));
 863
 864        /* set_gpt_info allocates new_partitions and str_disk_guid */
 865        ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
 866                           &new_partitions, &part_count);
 867        if (ret < 0)
 868                goto out;
 869
 870        if (!strcmp(subcomm, "swap")) {
 871                if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
 872                        printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
 873                        ret = -EINVAL;
 874                        goto out;
 875                }
 876                list_for_each(pos, &disk_partitions) {
 877                        curr = list_entry(pos, struct disk_part, list);
 878                        if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
 879                                strcpy((char *)curr->gpt_part_info.name, name2);
 880                                ctr1++;
 881                        } else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
 882                                strcpy((char *)curr->gpt_part_info.name, name1);
 883                                ctr2++;
 884                        }
 885                }
 886                if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) {
 887                        printf("Cannot swap partition names except in pairs.\n");
 888                        ret = -EINVAL;
 889                        goto out;
 890                }
 891        } else { /* rename */
 892                if (strlen(name2) > PART_NAME_LEN) {
 893                        printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
 894                        ret = -EINVAL;
 895                        goto out;
 896                }
 897                partnum = (int)simple_strtol(name1, NULL, 10);
 898                if ((partnum < 0) || (partnum > numparts)) {
 899                        printf("Illegal partition number %s\n", name1);
 900                        ret = -EINVAL;
 901                        goto out;
 902                }
 903                ret = part_get_info(dev_desc, partnum, new_partitions);
 904                if (ret < 0)
 905                        goto out;
 906
 907                /* U-Boot partition numbering starts at 1 */
 908                list_for_each(pos, &disk_partitions) {
 909                        curr = list_entry(pos, struct disk_part, list);
 910                        if (i == partnum) {
 911                                strcpy((char *)curr->gpt_part_info.name, name2);
 912                                break;
 913                        }
 914                        i++;
 915                }
 916        }
 917
 918        ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
 919        if (ret < 0)
 920                goto out;
 921        debug("NEW partitions_list is %s with %u chars\n", partitions_list,
 922              (unsigned)strlen(partitions_list));
 923
 924        ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
 925                           &new_partitions, &part_count);
 926        /*
 927         * Even though valid pointers are here passed into set_gpt_info(),
 928         * it mallocs again, and there's no way to tell which failed.
 929         */
 930        if (ret < 0)
 931                goto out;
 932
 933        debug("Writing new partition table\n");
 934        ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
 935        if (ret < 0) {
 936                printf("Writing new partition table failed\n");
 937                goto out;
 938        }
 939
 940        debug("Reading back new partition table\n");
 941        /*
 942         * Empty the existing disk_partitions list, as otherwise the memory in
 943         * the original list is unreachable.
 944         */
 945        del_gpt_info();
 946        numparts = get_gpt_info(dev_desc);
 947        if (numparts <=  0) {
 948                ret = numparts ? numparts : -ENODEV;
 949                goto out;
 950        }
 951        printf("new partition table with %d partitions is:\n", numparts);
 952        print_gpt_info();
 953 out:
 954        del_gpt_info();
 955#ifdef CONFIG_RANDOM_UUID
 956        free(str_disk_guid);
 957#endif
 958        free(new_partitions);
 959        free(partitions_list);
 960        return ret;
 961}
 962#endif
 963
 964/**
 965 * do_gpt(): Perform GPT operations
 966 *
 967 * @param cmdtp - command name
 968 * @param flag
 969 * @param argc
 970 * @param argv
 971 *
 972 * @return zero on success; otherwise error
 973 */
 974static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 975{
 976        int ret = CMD_RET_SUCCESS;
 977        int dev = 0;
 978        char *ep;
 979        struct blk_desc *blk_dev_desc = NULL;
 980
 981#ifndef CONFIG_CMD_GPT_RENAME
 982        if (argc < 4 || argc > 5)
 983#else
 984        if (argc < 4 || argc > 6)
 985#endif
 986                return CMD_RET_USAGE;
 987
 988        dev = (int)dectoul(argv[3], &ep);
 989        if (!ep || ep[0] != '\0') {
 990                printf("'%s' is not a number\n", argv[3]);
 991                return CMD_RET_USAGE;
 992        }
 993        blk_dev_desc = blk_get_dev(argv[2], dev);
 994        if (!blk_dev_desc) {
 995                printf("%s: %s dev %d NOT available\n",
 996                       __func__, argv[2], dev);
 997                return CMD_RET_FAILURE;
 998        }
 999
1000        if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
1001                printf("Writing GPT: ");
1002                ret = gpt_default(blk_dev_desc, argv[4]);
1003        } else if ((strcmp(argv[1], "verify") == 0)) {
1004                ret = gpt_verify(blk_dev_desc, argv[4]);
1005                printf("Verify GPT: ");
1006        } else if ((strcmp(argv[1], "setenv") == 0)) {
1007                ret = gpt_setenv(blk_dev_desc, argv[4]);
1008        } else if ((strcmp(argv[1], "enumerate") == 0)) {
1009                ret = gpt_enumerate(blk_dev_desc);
1010        } else if (strcmp(argv[1], "guid") == 0) {
1011                ret = do_disk_guid(blk_dev_desc, argv[4]);
1012#ifdef CONFIG_CMD_GPT_RENAME
1013        } else if (strcmp(argv[1], "read") == 0) {
1014                ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL);
1015        } else if ((strcmp(argv[1], "swap") == 0) ||
1016                   (strcmp(argv[1], "rename") == 0)) {
1017                ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
1018#endif
1019        } else {
1020                return CMD_RET_USAGE;
1021        }
1022
1023        if (ret) {
1024                printf("error!\n");
1025                return CMD_RET_FAILURE;
1026        }
1027
1028        printf("success!\n");
1029        return CMD_RET_SUCCESS;
1030}
1031
1032U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
1033        "GUID Partition Table",
1034        "<command> <interface> <dev> <partitions_list>\n"
1035        " - GUID partition table restoration and validity check\n"
1036        " Restore or verify GPT information on a device connected\n"
1037        " to interface\n"
1038        " Example usage:\n"
1039        " gpt write mmc 0 $partitions\n"
1040        "    - write the GPT to device\n"
1041        " gpt verify mmc 0 $partitions\n"
1042        "    - verify the GPT on device against $partitions\n"
1043        " gpt setenv mmc 0 $name\n"
1044        "    - setup environment variables for partition $name:\n"
1045        "      gpt_partition_addr, gpt_partition_size,\n"
1046        "      gpt_partition_name, gpt_partition_entry\n"
1047        " gpt enumerate mmc 0\n"
1048        "    - store list of partitions to gpt_partition_list environment variable\n"
1049        " read <interface> <dev>\n"
1050        "    - read GPT into a data structure for manipulation\n"
1051        " gpt guid <interface> <dev>\n"
1052        "    - print disk GUID\n"
1053        " gpt guid <interface> <dev> <varname>\n"
1054        "    - set environment variable to disk GUID\n"
1055        " Example usage:\n"
1056        " gpt guid mmc 0\n"
1057        " gpt guid mmc 0 varname\n"
1058#ifdef CONFIG_CMD_GPT_RENAME
1059        "gpt partition renaming commands:\n"
1060        " gpt read <interface> <dev> [<varname>]\n"
1061        "    - read GPT into a data structure for manipulation\n"
1062        "    - read GPT partitions into environment variable\n"
1063        " gpt swap <interface> <dev> <name1> <name2>\n"
1064        "    - change all partitions named name1 to name2\n"
1065        "      and vice-versa\n"
1066        " gpt rename <interface> <dev> <part> <name>\n"
1067        "    - rename the specified partition\n"
1068        " Example usage:\n"
1069        " gpt swap mmc 0 foo bar\n"
1070        " gpt rename mmc 0 3 foo\n"
1071#endif
1072);
1073