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