uboot/cmd/nvedit_efi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *  Integrate UEFI variables to u-boot env interface
   4 *
   5 *  Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
   6 */
   7
   8#include <charset.h>
   9#include <common.h>
  10#include <command.h>
  11#include <efi_loader.h>
  12#include <efi_variable.h>
  13#include <env.h>
  14#include <exports.h>
  15#include <hexdump.h>
  16#include <malloc.h>
  17#include <mapmem.h>
  18#include <rtc.h>
  19#include <uuid.h>
  20#include <linux/kernel.h>
  21
  22/*
  23 * From efi_variable.c,
  24 *
  25 * Mapping between UEFI variables and u-boot variables:
  26 *
  27 *   efi_$guid_$varname = {attributes}(type)value
  28 */
  29
  30static const struct {
  31        u32 mask;
  32        char *text;
  33} efi_var_attrs[] = {
  34        {EFI_VARIABLE_NON_VOLATILE, "NV"},
  35        {EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"},
  36        {EFI_VARIABLE_RUNTIME_ACCESS, "RT"},
  37        {EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"},
  38        {EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"},
  39        {EFI_VARIABLE_READ_ONLY, "RO"},
  40};
  41
  42/**
  43 * efi_dump_single_var() - show information about a UEFI variable
  44 *
  45 * @name:       Name of the variable
  46 * @guid:       Vendor GUID
  47 * @verbose:    if true, dump data
  48 *
  49 * Show information encoded in one UEFI variable
  50 */
  51static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
  52{
  53        u32 attributes;
  54        u8 *data;
  55        u64 time;
  56        struct rtc_time tm;
  57        efi_uintn_t size;
  58        int count, i;
  59        efi_status_t ret;
  60
  61        data = NULL;
  62        size = 0;
  63        ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time);
  64        if (ret == EFI_BUFFER_TOO_SMALL) {
  65                data = malloc(size);
  66                if (!data)
  67                        goto out;
  68
  69                ret = efi_get_variable_int(name, guid, &attributes, &size,
  70                                           data, &time);
  71        }
  72        if (ret == EFI_NOT_FOUND) {
  73                printf("Error: \"%ls\" not defined\n", name);
  74                goto out;
  75        }
  76        if (ret != EFI_SUCCESS)
  77                goto out;
  78
  79        rtc_to_tm(time, &tm);
  80        printf("%ls:\n    %pUl (%pUs)\n", name, guid, guid);
  81        if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
  82                printf("    %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year,
  83                       tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  84        printf("    ");
  85        for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
  86                if (attributes & efi_var_attrs[i].mask) {
  87                        if (count)
  88                                putc('|');
  89                        count++;
  90                        puts(efi_var_attrs[i].text);
  91                }
  92        printf(", DataSize = 0x%zx\n", size);
  93        if (verbose)
  94                print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
  95                               data, size, true);
  96
  97out:
  98        free(data);
  99}
 100
 101static bool match_name(int argc, char *const argv[], u16 *var_name16)
 102{
 103        char *buf, *p;
 104        size_t buflen;
 105        int i;
 106        bool result = false;
 107
 108        buflen = utf16_utf8_strlen(var_name16) + 1;
 109        buf = calloc(1, buflen);
 110        if (!buf)
 111                return result;
 112
 113        p = buf;
 114        utf16_utf8_strcpy(&p, var_name16);
 115
 116        for (i = 0; i < argc; argc--, argv++) {
 117                if (!strcmp(buf, argv[i])) {
 118                        result = true;
 119                        goto out;
 120                }
 121        }
 122
 123out:
 124        free(buf);
 125
 126        return result;
 127}
 128
 129/**
 130 * efi_dump_var_all() - show information about all the UEFI variables
 131 *
 132 * @argc:       Number of arguments (variables)
 133 * @argv:       Argument (variable name) array
 134 * @verbose:    if true, dump data
 135 * Return:      CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
 136 *
 137 * Show information encoded in all the UEFI variables
 138 */
 139static int efi_dump_var_all(int argc,  char *const argv[],
 140                            const efi_guid_t *guid_p, bool verbose)
 141{
 142        u16 *var_name16, *p;
 143        efi_uintn_t buf_size, size;
 144        efi_guid_t guid;
 145        efi_status_t ret;
 146        bool match = false;
 147
 148        buf_size = 128;
 149        var_name16 = malloc(buf_size);
 150        if (!var_name16)
 151                return CMD_RET_FAILURE;
 152
 153        var_name16[0] = 0;
 154        for (;;) {
 155                size = buf_size;
 156                ret = efi_get_next_variable_name_int(&size, var_name16,
 157                                                     &guid);
 158                if (ret == EFI_NOT_FOUND)
 159                        break;
 160                if (ret == EFI_BUFFER_TOO_SMALL) {
 161                        buf_size = size;
 162                        p = realloc(var_name16, buf_size);
 163                        if (!p) {
 164                                free(var_name16);
 165                                return CMD_RET_FAILURE;
 166                        }
 167                        var_name16 = p;
 168                        ret = efi_get_next_variable_name_int(&size, var_name16,
 169                                                             &guid);
 170                }
 171                if (ret != EFI_SUCCESS) {
 172                        free(var_name16);
 173                        return CMD_RET_FAILURE;
 174                }
 175
 176                if (guid_p && guidcmp(guid_p, &guid))
 177                        continue;
 178                if (!argc || match_name(argc, argv, var_name16)) {
 179                        match = true;
 180                        efi_dump_single_var(var_name16, &guid, verbose);
 181                }
 182        }
 183        free(var_name16);
 184
 185        if (!match && argc == 1)
 186                printf("Error: \"%s\" not defined\n", argv[0]);
 187
 188        return CMD_RET_SUCCESS;
 189}
 190
 191/**
 192 * do_env_print_efi() - show information about UEFI variables
 193 *
 194 * @cmdtp:      Command table
 195 * @flag:       Command flag
 196 * @argc:       Number of arguments
 197 * @argv:       Argument array
 198 * Return:      CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
 199 *
 200 * This function is for "env print -e" or "printenv -e" command:
 201 *   => env print -e [-n] [-guid <guid> | -all] [var [...]]
 202 * If one or more variable names are specified, show information
 203 * named UEFI variables, otherwise show all the UEFI variables.
 204 */
 205int do_env_print_efi(struct cmd_tbl *cmdtp, int flag, int argc,
 206                     char *const argv[])
 207{
 208        const efi_guid_t *guid_p = NULL;
 209        efi_guid_t guid;
 210        bool verbose = true;
 211        efi_status_t ret;
 212
 213        /* Initialize EFI drivers */
 214        ret = efi_init_obj_list();
 215        if (ret != EFI_SUCCESS) {
 216                printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
 217                       ret & ~EFI_ERROR_MASK);
 218                return CMD_RET_FAILURE;
 219        }
 220
 221        for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
 222                if (!strcmp(argv[0], "-guid")) {
 223                        if (argc == 1)
 224                                return CMD_RET_USAGE;
 225                        argc--;
 226                        argv++;
 227                        if (uuid_str_to_bin(argv[0], guid.b,
 228                                            UUID_STR_FORMAT_GUID))
 229                                return CMD_RET_USAGE;
 230                        guid_p = (const efi_guid_t *)guid.b;
 231                } else if (!strcmp(argv[0], "-n")) {
 232                        verbose = false;
 233                } else {
 234                        return CMD_RET_USAGE;
 235                }
 236        }
 237
 238        /* enumerate and show all UEFI variables */
 239        return efi_dump_var_all(argc, argv, guid_p, verbose);
 240}
 241
 242/**
 243 * append_value() - encode UEFI variable's value
 244 * @bufp:       Buffer of encoded UEFI variable's value
 245 * @sizep:      Size of buffer
 246 * @data:       data to be encoded into the value
 247 * Return:      0 on success, -1 otherwise
 248 *
 249 * Interpret a given data string and append it to buffer.
 250 * Buffer will be realloc'ed if necessary.
 251 *
 252 * Currently supported formats are:
 253 *   =0x0123...:                Hexadecimal number
 254 *   =H0123...:                 Hexadecimal-byte array
 255 *   ="...", =S"..." or <string>:
 256 *                              String
 257 */
 258static int append_value(char **bufp, size_t *sizep, char *data)
 259{
 260        char *tmp_buf = NULL, *new_buf = NULL, *value;
 261        unsigned long len = 0;
 262
 263        if (!strncmp(data, "=0x", 2)) { /* hexadecimal number */
 264                union {
 265                        u8 u8;
 266                        u16 u16;
 267                        u32 u32;
 268                        u64 u64;
 269                } tmp_data;
 270                unsigned long hex_value;
 271                void *hex_ptr;
 272
 273                data += 3;
 274                len = strlen(data);
 275                if ((len & 0x1)) /* not multiple of two */
 276                        return -1;
 277
 278                len /= 2;
 279                if (len > 8)
 280                        return -1;
 281                else if (len > 4)
 282                        len = 8;
 283                else if (len > 2)
 284                        len = 4;
 285
 286                /* convert hex hexadecimal number */
 287                if (strict_strtoul(data, 16, &hex_value) < 0)
 288                        return -1;
 289
 290                tmp_buf = malloc(len);
 291                if (!tmp_buf)
 292                        return -1;
 293
 294                if (len == 1) {
 295                        tmp_data.u8 = hex_value;
 296                        hex_ptr = &tmp_data.u8;
 297                } else if (len == 2) {
 298                        tmp_data.u16 = hex_value;
 299                        hex_ptr = &tmp_data.u16;
 300                } else if (len == 4) {
 301                        tmp_data.u32 = hex_value;
 302                        hex_ptr = &tmp_data.u32;
 303                } else {
 304                        tmp_data.u64 = hex_value;
 305                        hex_ptr = &tmp_data.u64;
 306                }
 307                memcpy(tmp_buf, hex_ptr, len);
 308                value = tmp_buf;
 309
 310        } else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */
 311                data += 2;
 312                len = strlen(data);
 313                if (len & 0x1) /* not multiple of two */
 314                        return -1;
 315
 316                len /= 2;
 317                tmp_buf = malloc(len);
 318                if (!tmp_buf)
 319                        return -1;
 320
 321                if (hex2bin((u8 *)tmp_buf, data, len) < 0) {
 322                        printf("Error: illegal hexadecimal string\n");
 323                        free(tmp_buf);
 324                        return -1;
 325                }
 326
 327                value = tmp_buf;
 328        } else { /* string */
 329                if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) {
 330                        if (data[1] == '"')
 331                                data += 2;
 332                        else
 333                                data += 3;
 334                        value = data;
 335                        len = strlen(data) - 1;
 336                        if (data[len] != '"')
 337                                return -1;
 338                } else {
 339                        value = data;
 340                        len = strlen(data);
 341                }
 342        }
 343
 344        new_buf = realloc(*bufp, *sizep + len);
 345        if (!new_buf)
 346                goto out;
 347
 348        memcpy(new_buf + *sizep, value, len);
 349        *bufp = new_buf;
 350        *sizep += len;
 351
 352out:
 353        free(tmp_buf);
 354
 355        return 0;
 356}
 357
 358/**
 359 * do_env_set_efi() - set UEFI variable
 360 *
 361 * @cmdtp:      Command table
 362 * @flag:       Command flag
 363 * @argc:       Number of arguments
 364 * @argv:       Argument array
 365 * Return:      CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
 366 *
 367 * This function is for "env set -e" or "setenv -e" command:
 368 *   => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v]
 369 *                 [-i address,size] var, or
 370 *                 var [value ...]
 371 * Encode values specified and set given UEFI variable.
 372 * If no value is specified, delete the variable.
 373 */
 374int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc,
 375                   char *const argv[])
 376{
 377        char *var_name, *value, *ep;
 378        ulong addr;
 379        efi_uintn_t size;
 380        efi_guid_t guid;
 381        u32 attributes;
 382        bool default_guid, verbose, value_on_memory;
 383        u16 *var_name16 = NULL, *p;
 384        size_t len;
 385        efi_status_t ret;
 386
 387        if (argc == 1)
 388                return CMD_RET_USAGE;
 389
 390        /* Initialize EFI drivers */
 391        ret = efi_init_obj_list();
 392        if (ret != EFI_SUCCESS) {
 393                printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
 394                       ret & ~EFI_ERROR_MASK);
 395                return CMD_RET_FAILURE;
 396        }
 397
 398        /*
 399         * attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
 400         *           EFI_VARIABLE_RUNTIME_ACCESS;
 401         */
 402        value = NULL;
 403        size = 0;
 404        attributes = 0;
 405        guid = efi_global_variable_guid;
 406        default_guid = true;
 407        verbose = false;
 408        value_on_memory = false;
 409        for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
 410                if (!strcmp(argv[0], "-guid")) {
 411                        if (argc == 1)
 412                                return CMD_RET_USAGE;
 413
 414                        argc--;
 415                        argv++;
 416                        if (uuid_str_to_bin(argv[0], guid.b,
 417                                            UUID_STR_FORMAT_GUID)) {
 418                                return CMD_RET_USAGE;
 419                        }
 420                        default_guid = false;
 421                } else if (!strcmp(argv[0], "-bs")) {
 422                        attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
 423                } else if (!strcmp(argv[0], "-rt")) {
 424                        attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
 425                } else if (!strcmp(argv[0], "-nv")) {
 426                        attributes |= EFI_VARIABLE_NON_VOLATILE;
 427                } else if (!strcmp(argv[0], "-at")) {
 428                        attributes |=
 429                          EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
 430                } else if (!strcmp(argv[0], "-a")) {
 431                        attributes |= EFI_VARIABLE_APPEND_WRITE;
 432                } else if (!strcmp(argv[0], "-i")) {
 433                        /* data comes from memory */
 434                        if (argc == 1)
 435                                return CMD_RET_USAGE;
 436
 437                        argc--;
 438                        argv++;
 439                        addr = hextoul(argv[0], &ep);
 440                        if (*ep != ':')
 441                                return CMD_RET_USAGE;
 442
 443                        /* 0 should be allowed for delete */
 444                        size = hextoul(++ep, NULL);
 445
 446                        value_on_memory = true;
 447                } else if (!strcmp(argv[0], "-v")) {
 448                        verbose = true;
 449                } else {
 450                        return CMD_RET_USAGE;
 451                }
 452        }
 453        if (!argc)
 454                return CMD_RET_USAGE;
 455
 456        var_name = argv[0];
 457        if (default_guid) {
 458                if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") ||
 459                    !strcmp(var_name, "dbt"))
 460                        guid = efi_guid_image_security_database;
 461                else
 462                        guid = efi_global_variable_guid;
 463        }
 464
 465        if (verbose) {
 466                printf("GUID: %pUl (%pUs)\n", &guid, &guid);
 467                printf("Attributes: 0x%x\n", attributes);
 468        }
 469
 470        /* for value */
 471        if (value_on_memory)
 472                value = map_sysmem(addr, 0);
 473        else if (argc > 1)
 474                for (argc--, argv++; argc > 0; argc--, argv++)
 475                        if (append_value(&value, &size, argv[0]) < 0) {
 476                                printf("## Failed to process an argument, %s\n",
 477                                       argv[0]);
 478                                ret = CMD_RET_FAILURE;
 479                                goto out;
 480                        }
 481
 482        if (size && verbose) {
 483                printf("Value:\n");
 484                print_hex_dump("    ", DUMP_PREFIX_OFFSET,
 485                               16, 1, value, size, true);
 486        }
 487
 488        len = utf8_utf16_strnlen(var_name, strlen(var_name));
 489        var_name16 = malloc((len + 1) * 2);
 490        if (!var_name16) {
 491                printf("## Out of memory\n");
 492                ret = CMD_RET_FAILURE;
 493                goto out;
 494        }
 495        p = var_name16;
 496        utf8_utf16_strncpy(&p, var_name, len + 1);
 497
 498        ret = efi_set_variable_int(var_name16, &guid, attributes, size, value,
 499                                   true);
 500        unmap_sysmem(value);
 501        if (ret == EFI_SUCCESS) {
 502                ret = CMD_RET_SUCCESS;
 503        } else {
 504                const char *msg;
 505
 506                switch (ret) {
 507                case EFI_NOT_FOUND:
 508                        msg = " (not found)";
 509                        break;
 510                case EFI_WRITE_PROTECTED:
 511                        msg = " (read only)";
 512                        break;
 513                case EFI_INVALID_PARAMETER:
 514                        msg = " (invalid parameter)";
 515                        break;
 516                case EFI_SECURITY_VIOLATION:
 517                        msg = " (validation failed)";
 518                        break;
 519                case EFI_OUT_OF_RESOURCES:
 520                        msg = " (out of memory)";
 521                        break;
 522                default:
 523                        msg = "";
 524                        break;
 525                }
 526                printf("## Failed to set EFI variable%s\n", msg);
 527                ret = CMD_RET_FAILURE;
 528        }
 529out:
 530        if (value_on_memory)
 531                unmap_sysmem(value);
 532        else
 533                free(value);
 534        free(var_name16);
 535
 536        return ret;
 537}
 538