uboot/cmd/gpt.c
<<
>>
Prefs
   1/*
   2 * cmd_gpt.c -- GPT (GUID Partition Table) handling command
   3 *
   4 * Copyright (C) 2015
   5 * Lukasz Majewski <l.majewski@majess.pl>
   6 *
   7 * Copyright (C) 2012 Samsung Electronics
   8 * author: Lukasz Majewski <l.majewski@samsung.com>
   9 * author: Piotr Wilczek <p.wilczek@samsung.com>
  10 *
  11 * SPDX-License-Identifier:     GPL-2.0+
  12 */
  13
  14#include <common.h>
  15#include <malloc.h>
  16#include <command.h>
  17#include <part_efi.h>
  18#include <exports.h>
  19#include <linux/ctype.h>
  20#include <div64.h>
  21#include <memalign.h>
  22
  23#ifndef CONFIG_PARTITION_UUIDS
  24#error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_GPT to be enabled
  25#endif
  26
  27/**
  28 * extract_env(): Expand env name from string format '&{env_name}'
  29 *                and return pointer to the env (if the env is set)
  30 *
  31 * @param str - pointer to string
  32 * @param env - pointer to pointer to extracted env
  33 *
  34 * @return - zero on successful expand and env is set
  35 */
  36static int extract_env(const char *str, char **env)
  37{
  38        int ret = -1;
  39        char *e, *s;
  40#ifdef CONFIG_RANDOM_UUID
  41        char uuid_str[UUID_STR_LEN + 1];
  42#endif
  43
  44        if (!str || strlen(str) < 4)
  45                return -1;
  46
  47        if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
  48                return -1;
  49
  50        s = strdup(str);
  51        if (s == NULL)
  52                return -1;
  53
  54        memset(s + strlen(s) - 1, '\0', 1);
  55        memmove(s, s + 2, strlen(s) - 1);
  56
  57        e = getenv(s);
  58        if (e == NULL) {
  59#ifdef CONFIG_RANDOM_UUID
  60                debug("%s unset. ", str);
  61                gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_STD);
  62                setenv(s, uuid_str);
  63
  64                e = getenv(s);
  65                if (e) {
  66                        debug("Set to random.\n");
  67                        ret = 0;
  68                } else {
  69                        debug("Can't get random UUID.\n");
  70                }
  71#else
  72                debug("%s unset.\n", str);
  73#endif
  74        } else {
  75                debug("%s get from environment.\n", str);
  76                ret = 0;
  77        }
  78
  79        *env = e;
  80        free(s);
  81
  82        return ret;
  83}
  84
  85/**
  86 * extract_val(): Extract value from a key=value pair list (comma separated).
  87 *                Only value for the given key is returend.
  88 *                Function allocates memory for the value, remember to free!
  89 *
  90 * @param str - pointer to string with key=values pairs
  91 * @param key - pointer to the key to search for
  92 *
  93 * @return - pointer to allocated string with the value
  94 */
  95static char *extract_val(const char *str, const char *key)
  96{
  97        char *v, *k;
  98        char *s, *strcopy;
  99        char *new = NULL;
 100
 101        strcopy = strdup(str);
 102        if (strcopy == NULL)
 103                return NULL;
 104
 105        s = strcopy;
 106        while (s) {
 107                v = strsep(&s, ",");
 108                if (!v)
 109                        break;
 110                k = strsep(&v, "=");
 111                if (!k)
 112                        break;
 113                if  (strcmp(k, key) == 0) {
 114                        new = strdup(v);
 115                        break;
 116                }
 117        }
 118
 119        free(strcopy);
 120
 121        return new;
 122}
 123
 124/**
 125 * found_key(): Found key without value in parameter list (comma separated).
 126 *
 127 * @param str - pointer to string with key
 128 * @param key - pointer to the key to search for
 129 *
 130 * @return - true on found key
 131 */
 132static bool found_key(const char *str, const char *key)
 133{
 134        char *k;
 135        char *s, *strcopy;
 136        bool result = false;
 137
 138        strcopy = strdup(str);
 139        if (!strcopy)
 140                return NULL;
 141
 142        s = strcopy;
 143        while (s) {
 144                k = strsep(&s, ",");
 145                if (!k)
 146                        break;
 147                if  (strcmp(k, key) == 0) {
 148                        result = true;
 149                        break;
 150                }
 151        }
 152
 153        free(strcopy);
 154
 155        return result;
 156}
 157
 158/**
 159 * set_gpt_info(): Fill partition information from string
 160 *              function allocates memory, remember to free!
 161 *
 162 * @param dev_desc - pointer block device descriptor
 163 * @param str_part - pointer to string with partition information
 164 * @param str_disk_guid - pointer to pointer to allocated string with disk guid
 165 * @param partitions - pointer to pointer to allocated partitions array
 166 * @param parts_count - number of partitions
 167 *
 168 * @return - zero on success, otherwise error
 169 *
 170 */
 171static int set_gpt_info(struct blk_desc *dev_desc,
 172                        const char *str_part,
 173                        char **str_disk_guid,
 174                        disk_partition_t **partitions,
 175                        u8 *parts_count)
 176{
 177        char *tok, *str, *s;
 178        int i;
 179        char *val, *p;
 180        int p_count;
 181        disk_partition_t *parts;
 182        int errno = 0;
 183        uint64_t size_ll, start_ll;
 184        lbaint_t offset = 0;
 185
 186        debug("%s:  lba num: 0x%x %d\n", __func__,
 187              (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
 188
 189        if (str_part == NULL)
 190                return -1;
 191
 192        str = strdup(str_part);
 193
 194        /* extract disk guid */
 195        s = str;
 196        val = extract_val(str, "uuid_disk");
 197        if (!val) {
 198#ifdef CONFIG_RANDOM_UUID
 199                *str_disk_guid = malloc(UUID_STR_LEN + 1);
 200                gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
 201#else
 202                free(str);
 203                return -2;
 204#endif
 205        } else {
 206                val = strsep(&val, ";");
 207                if (extract_env(val, &p))
 208                        p = val;
 209                *str_disk_guid = strdup(p);
 210                free(val);
 211                /* Move s to first partition */
 212                strsep(&s, ";");
 213        }
 214        if (strlen(s) == 0)
 215                return -3;
 216
 217        i = strlen(s) - 1;
 218        if (s[i] == ';')
 219                s[i] = '\0';
 220
 221        /* calculate expected number of partitions */
 222        p_count = 1;
 223        p = s;
 224        while (*p) {
 225                if (*p++ == ';')
 226                        p_count++;
 227        }
 228
 229        /* allocate memory for partitions */
 230        parts = calloc(sizeof(disk_partition_t), p_count);
 231
 232        /* retrieve partitions data from string */
 233        for (i = 0; i < p_count; i++) {
 234                tok = strsep(&s, ";");
 235
 236                if (tok == NULL)
 237                        break;
 238
 239                /* uuid */
 240                val = extract_val(tok, "uuid");
 241                if (!val) {
 242                        /* 'uuid' is optional if random uuid's are enabled */
 243#ifdef CONFIG_RANDOM_UUID
 244                        gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
 245#else
 246                        errno = -4;
 247                        goto err;
 248#endif
 249                } else {
 250                        if (extract_env(val, &p))
 251                                p = val;
 252                        if (strlen(p) >= sizeof(parts[i].uuid)) {
 253                                printf("Wrong uuid format for partition %d\n", i);
 254                                errno = -4;
 255                                goto err;
 256                        }
 257                        strcpy((char *)parts[i].uuid, p);
 258                        free(val);
 259                }
 260#ifdef CONFIG_PARTITION_TYPE_GUID
 261                /* guid */
 262                val = extract_val(tok, "type");
 263                if (val) {
 264                        /* 'type' is optional */
 265                        if (extract_env(val, &p))
 266                                p = val;
 267                        if (strlen(p) >= sizeof(parts[i].type_guid)) {
 268                                printf("Wrong type guid format for partition %d\n",
 269                                       i);
 270                                errno = -4;
 271                                goto err;
 272                        }
 273                        strcpy((char *)parts[i].type_guid, p);
 274                        free(val);
 275                }
 276#endif
 277                /* name */
 278                val = extract_val(tok, "name");
 279                if (!val) { /* name is mandatory */
 280                        errno = -4;
 281                        goto err;
 282                }
 283                if (extract_env(val, &p))
 284                        p = val;
 285                if (strlen(p) >= sizeof(parts[i].name)) {
 286                        errno = -4;
 287                        goto err;
 288                }
 289                strcpy((char *)parts[i].name, p);
 290                free(val);
 291
 292                /* size */
 293                val = extract_val(tok, "size");
 294                if (!val) { /* 'size' is mandatory */
 295                        errno = -4;
 296                        goto err;
 297                }
 298                if (extract_env(val, &p))
 299                        p = val;
 300                if ((strcmp(p, "-") == 0)) {
 301                        /* Let part efi module to auto extend the size */
 302                        parts[i].size = 0;
 303                } else {
 304                        size_ll = ustrtoull(p, &p, 0);
 305                        parts[i].size = lldiv(size_ll, dev_desc->blksz);
 306                }
 307
 308                free(val);
 309
 310                /* start address */
 311                val = extract_val(tok, "start");
 312                if (val) { /* start address is optional */
 313                        if (extract_env(val, &p))
 314                                p = val;
 315                        start_ll = ustrtoull(p, &p, 0);
 316                        parts[i].start = lldiv(start_ll, dev_desc->blksz);
 317                        free(val);
 318                }
 319
 320                offset += parts[i].size + parts[i].start;
 321
 322                /* bootable */
 323                if (found_key(tok, "bootable"))
 324                        parts[i].bootable = 1;
 325        }
 326
 327        *parts_count = p_count;
 328        *partitions = parts;
 329        free(str);
 330
 331        return 0;
 332err:
 333        free(str);
 334        free(*str_disk_guid);
 335        free(parts);
 336
 337        return errno;
 338}
 339
 340static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
 341{
 342        int ret;
 343        char *str_disk_guid;
 344        u8 part_count = 0;
 345        disk_partition_t *partitions = NULL;
 346
 347        /* fill partitions */
 348        ret = set_gpt_info(blk_dev_desc, str_part,
 349                        &str_disk_guid, &partitions, &part_count);
 350        if (ret) {
 351                if (ret == -1)
 352                        printf("No partition list provided\n");
 353                if (ret == -2)
 354                        printf("Missing disk guid\n");
 355                if ((ret == -3) || (ret == -4))
 356                        printf("Partition list incomplete\n");
 357                return -1;
 358        }
 359
 360        /* save partitions layout to disk */
 361        ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
 362        free(str_disk_guid);
 363        free(partitions);
 364
 365        return ret;
 366}
 367
 368static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
 369{
 370        ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
 371                                     blk_dev_desc->blksz);
 372        disk_partition_t *partitions = NULL;
 373        gpt_entry *gpt_pte = NULL;
 374        char *str_disk_guid;
 375        u8 part_count = 0;
 376        int ret = 0;
 377
 378        /* fill partitions */
 379        ret = set_gpt_info(blk_dev_desc, str_part,
 380                        &str_disk_guid, &partitions, &part_count);
 381        if (ret) {
 382                if (ret == -1) {
 383                        printf("No partition list provided - only basic check\n");
 384                        ret = gpt_verify_headers(blk_dev_desc, gpt_head,
 385                                                 &gpt_pte);
 386                        goto out;
 387                }
 388                if (ret == -2)
 389                        printf("Missing disk guid\n");
 390                if ((ret == -3) || (ret == -4))
 391                        printf("Partition list incomplete\n");
 392                return -1;
 393        }
 394
 395        /* Check partition layout with provided pattern */
 396        ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
 397                                    gpt_head, &gpt_pte);
 398        free(str_disk_guid);
 399        free(partitions);
 400 out:
 401        free(gpt_pte);
 402        return ret;
 403}
 404
 405/**
 406 * do_gpt(): Perform GPT operations
 407 *
 408 * @param cmdtp - command name
 409 * @param flag
 410 * @param argc
 411 * @param argv
 412 *
 413 * @return zero on success; otherwise error
 414 */
 415static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 416{
 417        int ret = CMD_RET_SUCCESS;
 418        int dev = 0;
 419        char *ep;
 420        struct blk_desc *blk_dev_desc = NULL;
 421
 422        if (argc < 4 || argc > 5)
 423                return CMD_RET_USAGE;
 424
 425        dev = (int)simple_strtoul(argv[3], &ep, 10);
 426        if (!ep || ep[0] != '\0') {
 427                printf("'%s' is not a number\n", argv[3]);
 428                return CMD_RET_USAGE;
 429        }
 430        blk_dev_desc = blk_get_dev(argv[2], dev);
 431        if (!blk_dev_desc) {
 432                printf("%s: %s dev %d NOT available\n",
 433                       __func__, argv[2], dev);
 434                return CMD_RET_FAILURE;
 435        }
 436
 437        if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
 438                printf("Writing GPT: ");
 439                ret = gpt_default(blk_dev_desc, argv[4]);
 440        } else if ((strcmp(argv[1], "verify") == 0)) {
 441                ret = gpt_verify(blk_dev_desc, argv[4]);
 442                printf("Verify GPT: ");
 443        } else {
 444                return CMD_RET_USAGE;
 445        }
 446
 447        if (ret) {
 448                printf("error!\n");
 449                return CMD_RET_FAILURE;
 450        }
 451
 452        printf("success!\n");
 453        return CMD_RET_SUCCESS;
 454}
 455
 456U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
 457        "GUID Partition Table",
 458        "<command> <interface> <dev> <partitions_list>\n"
 459        " - GUID partition table restoration and validity check\n"
 460        " Restore or verify GPT information on a device connected\n"
 461        " to interface\n"
 462        " Example usage:\n"
 463        " gpt write mmc 0 $partitions\n"
 464        " gpt verify mmc 0 $partitions\n"
 465);
 466