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