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