1 /*- 2 * Copyright (c) 2008-2010 Rui Paulo 3 * Copyright (c) 2006 Marcel Moolenaar 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/boot/efi/loader/main.c 295408 2016-02-08 19:34:17Z imp $ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/reboot.h> 32 #include <sys/boot.h> 33 #include <stand.h> 34 #include <string.h> 35 #include <setjmp.h> 36 37 #include <efi.h> 38 #include <efilib.h> 39 40 #include <uuid.h> 41 42 #include <bootstrap.h> 43 #include <smbios.h> 44 45 #include "loader_efi.h" 46 47 extern char bootprog_name[]; 48 extern char bootprog_rev[]; 49 extern char bootprog_date[]; 50 extern char bootprog_maker[]; 51 52 struct arch_switch archsw; /* MI/MD interface boundary */ 53 54 EFI_GUID acpi = ACPI_TABLE_GUID; 55 EFI_GUID acpi20 = EFI_ACPI_TABLE_GUID; 56 EFI_GUID devid = DEVICE_PATH_PROTOCOL; 57 EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; 58 EFI_GUID mps = EFI_MPS_TABLE_GUID; 59 EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; 60 EFI_GUID smbios = SMBIOS_TABLE_GUID; 61 EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; 62 EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; 63 EFI_GUID hoblist = HOB_LIST_GUID; 64 EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; 65 EFI_GUID debugimg = EFI_DEBUG_IMAGE_INFO_TABLE_GUID; 66 EFI_GUID fdtdtb = FDT_TABLE_GUID; 67 EFI_GUID inputid = SIMPLE_INPUT_PROTOCOL; 68 EFI_GUID esrt = EFI_SYSTEM_RESOURCE_TABLE_GUID; 69 EFI_GUID lzmadecomp = LZMA_CUSTOM_DECOMPRESS_GUID; 70 EFI_GUID amiromlayout = AMI_ROM_LAYOUT_GUID; 71 72 /* 73 * Need this because EFI uses UTF-16 unicode string constants, but we 74 * use UTF-8. We can't use printf due to the possibility of \0 and we 75 * don't support support wide characters either. 76 */ 77 static void 78 print_str16(const CHAR16 *str) 79 { 80 int i; 81 82 for (i = 0; str[i]; i++) 83 printf("%c", (char)str[i]); 84 } 85 86 /* 87 * cpy8to16 copies a traditional C string into a CHAR16 string and 88 * 0 terminates it. len is the size of *dst in bytes. 89 */ 90 static void 91 cpy8to16(const char *src, CHAR16 *dst, size_t len) 92 { 93 len <<= 1; /* Assume CHAR16 is 2 bytes */ 94 while (len > 0 && *src) { 95 *dst++ = *src++; 96 len--; 97 } 98 *dst++ = (CHAR16)0; 99 } 100 101 static void 102 cp16to8(const CHAR16 *src, char *dst, size_t len) 103 { 104 size_t i; 105 106 for (i = 0; i < len && src[i]; i++) 107 dst[i] = (char)src[i]; 108 } 109 110 static int 111 has_keyboard(void) 112 { 113 EFI_STATUS status; 114 EFI_DEVICE_PATH *path; 115 EFI_HANDLE *hin, *hin_end, *walker; 116 UINTN sz; 117 int retval = 0; 118 119 /* 120 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and 121 * do the typical dance to get the right sized buffer. 122 */ 123 sz = 0; 124 hin = NULL; 125 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); 126 if (status == EFI_BUFFER_TOO_SMALL) { 127 hin = (EFI_HANDLE *)malloc(sz); 128 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 129 hin); 130 if (EFI_ERROR(status)) 131 free(hin); 132 } 133 if (EFI_ERROR(status)) 134 return retval; 135 136 /* 137 * Look at each of the handles. If it supports the device path protocol, 138 * use it to get the device path for this handle. Then see if that 139 * device path matches either the USB device path for keyboards or the 140 * legacy device path for keyboards. 141 */ 142 hin_end = &hin[sz / sizeof(*hin)]; 143 for (walker = hin; walker < hin_end; walker++) { 144 status = OpenProtocolByHandle(*walker, &devid, (VOID **)&path); 145 if (EFI_ERROR(status)) 146 continue; 147 148 while (!IsDevicePathEnd(path)) { 149 /* 150 * Check for the ACPI keyboard node. All PNP3xx nodes 151 * are keyboards of different flavors. Note: It is 152 * unclear of there's always a keyboard node when 153 * there's a keyboard controller, or if there's only one 154 * when a keyboard is detected at boot. 155 */ 156 if (DevicePathType(path) == ACPI_DEVICE_PATH && 157 (DevicePathSubType(path) == ACPI_DP || 158 DevicePathSubType(path) == ACPI_EXTENDED_DP)) { 159 ACPI_HID_DEVICE_PATH *acpi; 160 161 acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; 162 if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && 163 (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { 164 retval = 1; 165 goto out; 166 } 167 /* 168 * Check for USB keyboard node, if present. Unlike a 169 * PS/2 keyboard, these definitely only appear when 170 * connected to the system. 171 */ 172 } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && 173 DevicePathSubType(path) == MSG_USB_CLASS_DP) { 174 USB_CLASS_DEVICE_PATH *usb; 175 176 usb = (USB_CLASS_DEVICE_PATH *)(void *)path; 177 if (usb->DeviceClass == 3 && /* HID */ 178 usb->DeviceSubClass == 1 && /* Boot devices */ 179 usb->DeviceProtocol == 1) { /* Boot keyboards */ 180 retval = 1; 181 goto out; 182 } 183 } 184 path = NextDevicePathNode(path); 185 } 186 } 187 out: 188 free(hin); 189 return retval; 190 } 191 192 EFI_STATUS 193 main(int argc, CHAR16 *argv[]) 194 { 195 char var[128]; 196 EFI_LOADED_IMAGE *img; 197 EFI_GUID *guid; 198 int i, j, vargood, unit, howto; 199 struct devsw *dev; 200 uint64_t pool_guid; 201 UINTN k; 202 int has_kbd; 203 204 archsw.arch_autoload = efi_autoload; 205 archsw.arch_getdev = efi_getdev; 206 archsw.arch_copyin = efi_copyin; 207 archsw.arch_copyout = efi_copyout; 208 archsw.arch_readin = efi_readin; 209 210 has_kbd = has_keyboard(); 211 212 /* 213 * XXX Chicken-and-egg problem; we want to have console output 214 * early, but some console attributes may depend on reading from 215 * eg. the boot device, which we can't do yet. We can use 216 * printf() etc. once this is done. 217 */ 218 cons_probe(); 219 220 /* 221 * Parse the args to set the console settings, etc 222 * boot1.efi passes these in, if it can read /boot.config or /boot/config 223 * or iPXE may be setup to pass these in. 224 * 225 * Loop through the args, and for each one that contains an '=' that is 226 * not the first character, add it to the environment. This allows 227 * loader and kernel env vars to be passed on the command line. Convert 228 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. 229 */ 230 howto = 0; 231 for (i = 1; i < argc; i++) { 232 if (argv[i][0] == '-') { 233 for (j = 1; argv[i][j] != 0; j++) { 234 int ch; 235 236 ch = argv[i][j]; 237 switch (ch) { 238 case 'a': 239 howto |= RB_ASKNAME; 240 break; 241 case 'd': 242 howto |= RB_KDB; 243 break; 244 case 'h': 245 howto |= RB_SERIAL; 246 break; 247 case 'm': 248 howto |= RB_MUTE; 249 break; 250 case 'p': 251 howto |= RB_PAUSE; 252 break; 253 case 'P': 254 if (!has_kbd) { 255 howto &= ~(RB_MUTE|RB_VIDEO); 256 howto |= RB_SERIAL; 257 } 258 break; 259 case 'r': 260 howto |= RB_DFLTROOT; 261 break; 262 case 's': 263 howto |= RB_SINGLE; 264 break; 265 case 'S': 266 if (argv[i][j + 1] == 0) { 267 if (i + 1 == argc) { 268 setenv("comconsole_speed", "115200", 1); 269 } else { 270 cp16to8(&argv[i + 1][0], var, 271 sizeof(var)); 272 setenv("comconsole_speedspeed", var, 1); 273 } 274 i++; 275 break; 276 } else { 277 cp16to8(&argv[i][j + 1], var, 278 sizeof(var)); 279 setenv("comconsole_speed", var, 1); 280 break; 281 } 282 case 'v': 283 howto |= RB_VERBOSE; 284 break; 285 } 286 } 287 } else { 288 vargood = 0; 289 for (j = 0; argv[i][j] != 0; j++) { 290 if (j == sizeof(var)) { 291 vargood = 0; 292 break; 293 } 294 if (j > 0 && argv[i][j] == '=') 295 vargood = 1; 296 var[j] = (char)argv[i][j]; 297 } 298 if (vargood) { 299 var[j] = 0; 300 putenv(var); 301 } 302 } 303 } 304 for (i = 0; howto_names[i].ev != NULL; i++) 305 if (howto & howto_names[i].mask) 306 setenv(howto_names[i].ev, "YES", 1); 307 if (howto & RB_SERIAL) { 308 setenv("console", "comconsole" , 1); 309 } 310 311 if (efi_copy_init()) { 312 printf("failed to allocate staging area\n"); 313 return (EFI_BUFFER_TOO_SMALL); 314 } 315 316 /* 317 * March through the device switch probing for things. 318 */ 319 for (i = 0; devsw[i] != NULL; i++) 320 if (devsw[i]->dv_init != NULL) 321 (devsw[i]->dv_init)(); 322 323 /* Get our loaded image protocol interface structure. */ 324 OpenProtocolByHandle(IH, &imgid, (VOID**)&img); 325 326 printf("Command line arguments:"); 327 for (i = 0; i < argc; i++) { 328 printf(" "); 329 print_str16(argv[i]); 330 } 331 printf("\n"); 332 333 printf("Image base: 0x%lx\n", (u_long)img->ImageBase); 334 printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, 335 ST->Hdr.Revision & 0xffff); 336 printf("EFI Firmware: "); 337 /* printf doesn't understand EFI Unicode */ 338 ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); 339 printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, 340 ST->FirmwareRevision & 0xffff); 341 342 printf("\n"); 343 printf("%s, Revision %s\n", bootprog_name, bootprog_rev); 344 printf("(%s, %s)\n", bootprog_maker, bootprog_date); 345 346 /* 347 * Disable the watchdog timer. By default the boot manager sets 348 * the timer to 5 minutes before invoking a boot option. If we 349 * want to return to the boot manager, we have to disable the 350 * watchdog timer and since we're an interactive program, we don't 351 * want to wait until the user types "quit". The timer may have 352 * fired by then. We don't care if this fails. It does not prevent 353 * normal functioning in any way... 354 */ 355 BS->SetWatchdogTimer(0, 0, 0, NULL); 356 357 if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0) 358 return (EFI_NOT_FOUND); 359 360 switch (dev->dv_type) { 361 default: { 362 struct efi_devdesc currdev; 363 364 currdev.d_dev = dev; 365 currdev.d_kind.efidisk.unit = unit; 366 currdev.d_kind.efidisk.data = NULL; 367 currdev.d_type = currdev.d_dev->dv_type; 368 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), 369 efi_setcurrdev, env_nounset); 370 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, 371 env_nounset); 372 break; 373 } 374 } 375 376 /* enable EHCI */ 377 setenv("ehci_load", "YES", 1); 378 379 /* enable XHCI */ 380 setenv("xhci_load", "YES", 1); 381 382 /* Check if ACPI is available */ 383 if (efi_get_table(&acpi20) != NULL || 384 efi_get_table(&acpi) != NULL) { 385 setenv("acpi_load", "YES", 1); 386 } 387 388 setenv("LINES", "24", 1); /* optional */ 389 390 for (k = 0; k < ST->NumberOfTableEntries; k++) { 391 guid = &ST->ConfigurationTable[k].VendorGuid; 392 if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { 393 smbios_detect(ST->ConfigurationTable[k].VendorTable); 394 break; 395 } 396 } 397 398 interact(); /* doesn't return */ 399 400 return (EFI_SUCCESS); /* keep compiler happy */ 401 } 402 403 /* XXX move to lib stand ? */ 404 static int 405 wcscmp(CHAR16 *a, CHAR16 *b) 406 { 407 408 while (*a && *b && *a == *b) { 409 a++; 410 b++; 411 } 412 return *a - *b; 413 } 414 415 416 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); 417 418 static int 419 command_reboot(int argc, char *argv[]) 420 { 421 int i; 422 423 for (i = 0; devsw[i] != NULL; ++i) 424 if (devsw[i]->dv_cleanup != NULL) 425 (devsw[i]->dv_cleanup)(); 426 427 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, 428 (CHAR16 *)"Reboot from the loader"); 429 430 /* NOTREACHED */ 431 return (CMD_ERROR); 432 } 433 434 COMMAND_SET(quit, "quit", "exit the loader", command_quit); 435 436 static int 437 command_quit(int argc, char *argv[]) 438 { 439 exit(0); 440 return (CMD_OK); 441 } 442 443 COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); 444 445 static int 446 command_memmap(int argc, char *argv[]) 447 { 448 UINTN sz; 449 EFI_MEMORY_DESCRIPTOR *map, *p; 450 UINTN key, dsz; 451 UINT32 dver; 452 EFI_STATUS status; 453 int i, ndesc; 454 char line[80]; 455 static char *types[] = { 456 "Reserved", 457 "LoaderCode", 458 "LoaderData", 459 "BootServicesCode", 460 "BootServicesData", 461 "RuntimeServicesCode", 462 "RuntimeServicesData", 463 "ConventionalMemory", 464 "UnusableMemory", 465 "ACPIReclaimMemory", 466 "ACPIMemoryNVS", 467 "MemoryMappedIO", 468 "MemoryMappedIOPortSpace", 469 "PalCode" 470 }; 471 472 sz = 0; 473 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); 474 if (status != EFI_BUFFER_TOO_SMALL) { 475 printf("Can't determine memory map size\n"); 476 return (CMD_ERROR); 477 } 478 map = malloc(sz); 479 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); 480 if (EFI_ERROR(status)) { 481 printf("Can't read memory map\n"); 482 return (CMD_ERROR); 483 } 484 485 ndesc = sz / dsz; 486 snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", 487 "Type", "Physical", "Virtual", "#Pages", "Attr"); 488 pager_open(); 489 if (pager_output(line)) { 490 pager_close(); 491 return (CMD_OK); 492 } 493 494 for (i = 0, p = map; i < ndesc; 495 i++, p = NextMemoryDescriptor(p, dsz)) { 496 printf("%23s %012jx %012jx %08jx ", types[p->Type], 497 (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, 498 (uintmax_t)p->NumberOfPages); 499 if (p->Attribute & EFI_MEMORY_UC) 500 printf("UC "); 501 if (p->Attribute & EFI_MEMORY_WC) 502 printf("WC "); 503 if (p->Attribute & EFI_MEMORY_WT) 504 printf("WT "); 505 if (p->Attribute & EFI_MEMORY_WB) 506 printf("WB "); 507 if (p->Attribute & EFI_MEMORY_UCE) 508 printf("UCE "); 509 if (p->Attribute & EFI_MEMORY_WP) 510 printf("WP "); 511 if (p->Attribute & EFI_MEMORY_RP) 512 printf("RP "); 513 if (p->Attribute & EFI_MEMORY_XP) 514 printf("XP "); 515 if (pager_output("\n")) 516 break; 517 } 518 519 pager_close(); 520 return (CMD_OK); 521 } 522 523 COMMAND_SET(configuration, "configuration", "print configuration tables", 524 command_configuration); 525 526 static const char * 527 guid_to_string(EFI_GUID *guid) 528 { 529 static char buf[40]; 530 531 sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 532 guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], 533 guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], 534 guid->Data4[5], guid->Data4[6], guid->Data4[7]); 535 return (buf); 536 } 537 538 static int 539 command_configuration(int argc, char *argv[]) 540 { 541 char line[80]; 542 UINTN i; 543 544 snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n", 545 (unsigned long)ST->NumberOfTableEntries); 546 pager_open(); 547 if (pager_output(line)) { 548 pager_close(); 549 return (CMD_OK); 550 } 551 552 for (i = 0; i < ST->NumberOfTableEntries; i++) { 553 EFI_GUID *guid; 554 555 printf(" "); 556 guid = &ST->ConfigurationTable[i].VendorGuid; 557 if (!memcmp(guid, &mps, sizeof(EFI_GUID))) 558 printf("MPS Table"); 559 else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) 560 printf("ACPI Table"); 561 else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) 562 printf("ACPI 2.0 Table"); 563 else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) 564 printf("SMBIOS Table"); 565 else if (!memcmp(guid, &smbios3, sizeof(EFI_GUID))) 566 printf("SMBIOS3 Table"); 567 else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) 568 printf("DXE Table"); 569 else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) 570 printf("HOB List Table"); 571 else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) 572 printf("Memory Type Information Table"); 573 else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) 574 printf("Debug Image Info Table"); 575 else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) 576 printf("FDT Table"); 577 else if (!memcmp(guid, &esrt, sizeof(EFI_GUID))) 578 printf("System Resource Table"); 579 else if (!memcmp(guid, &lzmadecomp, sizeof(EFI_GUID))) 580 printf("LZMA-compressed Filesystem"); 581 else if (!memcmp(guid, &amiromlayout, sizeof(EFI_GUID))) 582 printf("AMI ROM Layout"); 583 else 584 printf("Unknown Table (%s)", guid_to_string(guid)); 585 snprintf(line, sizeof(line), " at %p\n", 586 ST->ConfigurationTable[i].VendorTable); 587 if (pager_output(line)) 588 break; 589 } 590 591 pager_close(); 592 return (CMD_OK); 593 } 594 595 596 COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); 597 598 static int 599 command_mode(int argc, char *argv[]) 600 { 601 UINTN cols, rows; 602 unsigned int mode; 603 int i; 604 char *cp; 605 char rowenv[8]; 606 EFI_STATUS status; 607 SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 608 extern void HO(void); 609 610 conout = ST->ConOut; 611 612 if (argc > 1) { 613 mode = strtol(argv[1], &cp, 0); 614 if (cp[0] != '\0') { 615 printf("Invalid mode\n"); 616 return (CMD_ERROR); 617 } 618 status = conout->QueryMode(conout, mode, &cols, &rows); 619 if (EFI_ERROR(status)) { 620 printf("invalid mode %d\n", mode); 621 return (CMD_ERROR); 622 } 623 status = conout->SetMode(conout, mode); 624 if (EFI_ERROR(status)) { 625 printf("couldn't set mode %d\n", mode); 626 return (CMD_ERROR); 627 } 628 sprintf(rowenv, "%u", (unsigned)rows); 629 setenv("LINES", rowenv, 1); 630 HO(); /* set cursor */ 631 return (CMD_OK); 632 } 633 634 printf("Current mode: %d\n", conout->Mode->Mode); 635 for (i = 0; i <= conout->Mode->MaxMode; i++) { 636 status = conout->QueryMode(conout, i, &cols, &rows); 637 if (EFI_ERROR(status)) 638 continue; 639 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, 640 (unsigned)rows); 641 } 642 643 if (i != 0) 644 printf("Select a mode with the command \"mode <number>\"\n"); 645 646 return (CMD_OK); 647 } 648 649 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); 650 651 static int 652 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) 653 { 654 UINTN datasz, i; 655 EFI_STATUS status; 656 UINT32 attr; 657 CHAR16 *data; 658 char *str; 659 uint32_t uuid_status; 660 int is_ascii; 661 662 datasz = 0; 663 status = RS->GetVariable(varnamearg, matchguid, &attr, 664 &datasz, NULL); 665 if (status != EFI_BUFFER_TOO_SMALL) { 666 printf("Can't get the variable: error %#llx\n", status); 667 return (CMD_ERROR); 668 } 669 data = malloc(datasz); 670 status = RS->GetVariable(varnamearg, matchguid, &attr, 671 &datasz, data); 672 if (status != EFI_SUCCESS) { 673 printf("Can't get the variable: error %#llx\n", status); 674 return (CMD_ERROR); 675 } 676 uuid_to_string((uuid_t *)matchguid, &str, &uuid_status); 677 if (lflag) { 678 printf("%s 0x%x %S", str, attr, varnamearg); 679 } else { 680 printf("%s 0x%x %S=", str, attr, varnamearg); 681 is_ascii = 1; 682 free(str); 683 str = (char *)data; 684 for (i = 0; i < datasz - 1; i++) { 685 /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */ 686 if ((str[i] < 32 || str[i] > 126) && str[i] != 9 && str[i] != 10 && str[i] != 13) { 687 is_ascii = 0; 688 break; 689 } 690 } 691 if (str[datasz - 1] != '\0') 692 is_ascii = 0; 693 if (is_ascii) 694 printf("%s", str); 695 else { 696 for (i = 0; i < datasz / 2; i++) { 697 if (isalnum(data[i]) || isspace(data[i])) 698 printf("%c", data[i]); 699 else 700 printf("\\x%02x", data[i]); 701 } 702 } 703 } 704 free(data); 705 pager_output("\n"); 706 return (CMD_OK); 707 } 708 709 static int 710 command_efi_show(int argc, char *argv[]) 711 { 712 /* 713 * efi-show [-a] 714 * print all the env 715 * efi-show -u UUID 716 * print all the env vars tagged with UUID 717 * efi-show -v var 718 * search all the env vars and print the ones matching var 719 * eif-show -u UUID -v var 720 * eif-show UUID var 721 * print all the env vars that match UUID and var 722 */ 723 /* NB: We assume EFI_GUID is the same as uuid_t */ 724 int aflag = 0, gflag = 0, lflag = 0, vflag = 0; 725 int ch, rv; 726 unsigned i; 727 EFI_STATUS status; 728 EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 729 EFI_GUID matchguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 730 uint32_t uuid_status; 731 CHAR16 *varname; 732 CHAR16 *newnm; 733 CHAR16 varnamearg[128]; 734 UINTN varalloc; 735 UINTN varsz; 736 737 while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { 738 switch (ch) { 739 case 'a': 740 aflag = 1; 741 break; 742 case 'g': 743 gflag = 1; 744 uuid_from_string(optarg, (uuid_t *)&matchguid, 745 &uuid_status); 746 if (uuid_status != uuid_s_ok) { 747 printf("uid %s could not be parsed\n", optarg); 748 return (CMD_ERROR); 749 } 750 break; 751 case 'l': 752 lflag = 1; 753 break; 754 case 'v': 755 vflag = 1; 756 if (strlen(optarg) >= nitems(varnamearg)) { 757 printf("Variable %s is longer than %zu characters\n", 758 optarg, nitems(varnamearg)); 759 return (CMD_ERROR); 760 } 761 for (i = 0; i < strlen(optarg); i++) 762 varnamearg[i] = optarg[i]; 763 varnamearg[i] = 0; 764 break; 765 default: 766 printf("Invalid argument %c\n", ch); 767 return (CMD_ERROR); 768 } 769 } 770 771 if (aflag && (gflag || vflag)) { 772 printf("-a isn't compatible with -v or -u\n"); 773 return (CMD_ERROR); 774 } 775 776 if (aflag && optind < argc) { 777 printf("-a doesn't take any args"); 778 return (CMD_ERROR); 779 } 780 781 if (optind == argc) 782 aflag = 1; 783 784 argc -= optind; 785 argv += optind; 786 787 pager_open(); 788 if (vflag && gflag) { 789 rv = efi_print_var(varnamearg, &matchguid, lflag); 790 pager_close(); 791 return (rv); 792 } 793 794 if (argc == 2) { 795 optarg = argv[0]; 796 if (strlen(optarg) >= nitems(varnamearg)) { 797 printf("Variable %s is longer than %zu characters\n", 798 optarg, nitems(varnamearg)); 799 pager_close(); 800 return (CMD_ERROR); 801 } 802 for (i = 0; i < strlen(optarg); i++) 803 varnamearg[i] = optarg[i]; 804 varnamearg[i] = 0; 805 optarg = argv[1]; 806 uuid_from_string(optarg, (uuid_t *)&matchguid, 807 &uuid_status); 808 if (uuid_status != uuid_s_ok) { 809 printf("uid %s could not be parsed\n", optarg); 810 pager_close(); 811 return (CMD_ERROR); 812 } 813 rv = efi_print_var(varnamearg, &matchguid, lflag); 814 pager_close(); 815 return (rv); 816 } 817 818 if (argc > 0) { 819 printf("Too many args %d\n", argc); 820 pager_close(); 821 return (CMD_ERROR); 822 } 823 824 /* 825 * Initiate the search -- note the standard takes pain 826 * to specify the initial call must be a poiner to a NULL 827 * character. 828 */ 829 varalloc = 1024; 830 varname = malloc(varalloc); 831 if (varname == NULL) { 832 printf("Can't allocate memory to get variables\n"); 833 pager_close(); 834 return (CMD_ERROR); 835 } 836 varname[0] = 0; 837 while (1) { 838 varsz = varalloc; 839 status = RS->GetNextVariableName(&varsz, varname, &varguid); 840 if (status == EFI_BUFFER_TOO_SMALL) { 841 varalloc = varsz; 842 newnm = malloc(varalloc); 843 if (newnm == NULL) { 844 printf("Can't allocate memory to get variables\n"); 845 free(varname); 846 pager_close(); 847 return (CMD_ERROR); 848 } 849 memcpy(newnm, varname, varsz); 850 free(varname); 851 varname = newnm; 852 continue; /* Try again with bigger buffer */ 853 } 854 if (status != EFI_SUCCESS) 855 break; 856 if (aflag) { 857 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 858 break; 859 continue; 860 } 861 if (vflag) { 862 if (wcscmp(varnamearg, varname) == 0) { 863 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 864 break; 865 continue; 866 } 867 } 868 if (gflag) { 869 if (memcmp(&varguid, &matchguid, sizeof(varguid)) == 0) { 870 if (efi_print_var(varname, &varguid, lflag) != CMD_OK) 871 break; 872 continue; 873 } 874 } 875 } 876 free(varname); 877 pager_close(); 878 879 return (CMD_OK); 880 } 881 882 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); 883 884 static int 885 command_efi_set(int argc, char *argv[]) 886 { 887 char *uuid, *var, *val; 888 CHAR16 wvar[128]; 889 EFI_GUID guid; 890 uint32_t status; 891 EFI_STATUS err; 892 893 if (argc != 4) { 894 printf("efi-set uuid var new-value\n"); 895 return (CMD_ERROR); 896 } 897 uuid = argv[1]; 898 var = argv[2]; 899 val = argv[3]; 900 uuid_from_string(uuid, (uuid_t *)&guid, &status); 901 if (status != uuid_s_ok) { 902 printf("Invalid uuid %s %d\n", uuid, status); 903 return (CMD_ERROR); 904 } 905 cpy8to16(var, wvar, sizeof(wvar)); 906 err = RS->SetVariable(wvar, &guid, 907 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, 908 strlen(val) + 1, val); 909 if (EFI_ERROR(err)) { 910 printf("Failed to set variable: error %llu\n", err); 911 return (CMD_ERROR); 912 } 913 return (CMD_OK); 914 } 915 916 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); 917 918 static int 919 command_efi_unset(int argc, char *argv[]) 920 { 921 char *uuid, *var; 922 CHAR16 wvar[128]; 923 EFI_GUID guid; 924 uint32_t status; 925 EFI_STATUS err; 926 927 if (argc != 3) { 928 printf("efi-unset uuid var\n"); 929 return (CMD_ERROR); 930 } 931 uuid = argv[1]; 932 var = argv[2]; 933 uuid_from_string(uuid, (uuid_t *)&guid, &status); 934 if (status != uuid_s_ok) { 935 printf("Invalid uuid %s\n", uuid); 936 return (CMD_ERROR); 937 } 938 cpy8to16(var, wvar, sizeof(wvar)); 939 err = RS->SetVariable(wvar, &guid, 0, 0, NULL); 940 if (EFI_ERROR(err)) { 941 printf("Failed to unset variable: error %llu\n", err); 942 return (CMD_ERROR); 943 } 944 return (CMD_OK); 945 } 946