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