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$"); 30 31#include <sys/param.h> 32#include <sys/reboot.h> 33#include <sys/boot.h> 34#include <stand.h> 35#include <string.h> 36#include <setjmp.h> 37 38#include <efi.h> 39#include <efilib.h> 40 41#include <bootstrap.h> 42#include <smbios.h> 43 44#ifdef EFI_ZFS_BOOT 45#include <libzfs.h> 46#endif 47 48#include "loader_efi.h" 49 50extern char bootprog_name[]; 51extern char bootprog_rev[]; 52extern char bootprog_date[]; 53extern char bootprog_maker[]; 54 55struct arch_switch archsw; /* MI/MD interface boundary */ 56 57EFI_GUID acpi = ACPI_TABLE_GUID; 58EFI_GUID acpi20 = ACPI_20_TABLE_GUID; 59EFI_GUID devid = DEVICE_PATH_PROTOCOL; 60EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; 61EFI_GUID mps = MPS_TABLE_GUID; 62EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; 63EFI_GUID smbios = SMBIOS_TABLE_GUID; 64EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; 65EFI_GUID hoblist = HOB_LIST_TABLE_GUID; 66EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; 67EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; 68EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; 69 70#ifdef EFI_ZFS_BOOT 71static void efi_zfs_probe(void); 72#endif 73 74/* 75 * Need this because EFI uses UTF-16 unicode string constants, but we 76 * use UTF-8. We can't use printf due to the possiblity of \0 and we 77 * don't support support wide characters either. 78 */ 79static void 80print_str16(const CHAR16 *str) 81{ 82 int i; 83 84 for (i = 0; str[i]; i++) 85 printf("%c", (char)str[i]); 86} 87 88static void 89cp16to8(const CHAR16 *src, char *dst, size_t len) 90{ 91 size_t i; 92 93 for (i = 0; i < len && src[i]; i++) 94 dst[i] = (char)src[i]; 95} 96 97static int 98has_keyboard(void) 99{ 100 EFI_STATUS status; 101 EFI_DEVICE_PATH *path; 102 EFI_HANDLE *hin, *hin_end, *walker; 103 UINTN sz; 104 int retval = 0; 105 106 /* 107 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and 108 * do the typical dance to get the right sized buffer. 109 */ 110 sz = 0; 111 hin = NULL; 112 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); 113 if (status == EFI_BUFFER_TOO_SMALL) { 114 hin = (EFI_HANDLE *)malloc(sz); 115 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 116 hin); 117 if (EFI_ERROR(status)) 118 free(hin); 119 } 120 if (EFI_ERROR(status)) 121 return retval; 122 123 /* 124 * Look at each of the handles. If it supports the device path protocol, 125 * use it to get the device path for this handle. Then see if that 126 * device path matches either the USB device path for keyboards or the 127 * legacy device path for keyboards. 128 */ 129 hin_end = &hin[sz / sizeof(*hin)]; 130 for (walker = hin; walker < hin_end; walker++) { 131 status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); 132 if (EFI_ERROR(status)) 133 continue; 134 135 while (!IsDevicePathEnd(path)) { 136 /* 137 * Check for the ACPI keyboard node. All PNP3xx nodes 138 * are keyboards of different flavors. Note: It is 139 * unclear of there's always a keyboard node when 140 * there's a keyboard controller, or if there's only one 141 * when a keyboard is detected at boot. 142 */ 143 if (DevicePathType(path) == ACPI_DEVICE_PATH && 144 (DevicePathSubType(path) == ACPI_DP || 145 DevicePathSubType(path) == ACPI_EXTENDED_DP)) { 146 ACPI_HID_DEVICE_PATH *acpi; 147 148 acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; 149 if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && 150 (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { 151 retval = 1; 152 goto out; 153 } 154 /* 155 * Check for USB keyboard node, if present. Unlike a 156 * PS/2 keyboard, these definitely only appear when 157 * connected to the system. 158 */ 159 } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && 160 DevicePathSubType(path) == MSG_USB_CLASS_DP) { 161 USB_CLASS_DEVICE_PATH *usb; 162 163 usb = (USB_CLASS_DEVICE_PATH *)(void *)path; 164 if (usb->DeviceClass == 3 && /* HID */ 165 usb->DeviceSubClass == 1 && /* Boot devices */ 166 usb->DeviceProtocol == 1) { /* Boot keyboards */ 167 retval = 1; 168 goto out; 169 } 170 } 171 path = NextDevicePathNode(path); 172 } 173 } 174out: 175 free(hin); 176 return retval; 177} 178 179EFI_STATUS 180main(int argc, CHAR16 *argv[]) 181{ 182 char var[128]; 183 EFI_LOADED_IMAGE *img; 184 EFI_GUID *guid; 185 int i, j, vargood, unit, howto; 186 struct devsw *dev; 187 uint64_t pool_guid; 188 UINTN k; 189 int has_kbd; 190 191 archsw.arch_autoload = efi_autoload; 192 archsw.arch_getdev = efi_getdev; 193 archsw.arch_copyin = efi_copyin; 194 archsw.arch_copyout = efi_copyout; 195 archsw.arch_readin = efi_readin; 196#ifdef EFI_ZFS_BOOT 197 /* Note this needs to be set before ZFS init. */ 198 archsw.arch_zfs_probe = efi_zfs_probe; 199#endif 200 201 has_kbd = has_keyboard(); 202 203 /* 204 * XXX Chicken-and-egg problem; we want to have console output 205 * early, but some console attributes may depend on reading from 206 * eg. the boot device, which we can't do yet. We can use 207 * printf() etc. once this is done. 208 */ 209 cons_probe(); 210 211 /* 212 * Parse the args to set the console settings, etc 213 * boot1.efi passes these in, if it can read /boot.config or /boot/config 214 * or iPXE may be setup to pass these in. 215 * 216 * Loop through the args, and for each one that contains an '=' that is 217 * not the first character, add it to the environment. This allows 218 * loader and kernel env vars to be passed on the command line. Convert 219 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. 220 */ 221 howto = 0; 222 for (i = 1; i < argc; i++) { 223 if (argv[i][0] == '-') { 224 for (j = 1; argv[i][j] != 0; j++) { 225 int ch; 226 227 ch = argv[i][j]; 228 switch (ch) { 229 case 'a': 230 howto |= RB_ASKNAME; 231 break; 232 case 'd': 233 howto |= RB_KDB; 234 break; 235 case 'D': 236 howto |= RB_MULTIPLE; 237 break; 238 case 'h': 239 howto |= RB_SERIAL; 240 break; 241 case 'm': 242 howto |= RB_MUTE; 243 break; 244 case 'p': 245 howto |= RB_PAUSE; 246 break; 247 case 'P': 248 if (!has_kbd) 249 howto |= RB_SERIAL | RB_MULTIPLE; 250 break; 251 case 'r': 252 howto |= RB_DFLTROOT; 253 break; 254 case 's': 255 howto |= RB_SINGLE; 256 break; 257 case 'S': 258 if (argv[i][j + 1] == 0) { 259 if (i + 1 == argc) { 260 setenv("comconsole_speed", "115200", 1); 261 } else { 262 cp16to8(&argv[i + 1][0], var, 263 sizeof(var)); 264 setenv("comconsole_speedspeed", var, 1); 265 } 266 i++; 267 break; 268 } else { 269 cp16to8(&argv[i][j + 1], var, 270 sizeof(var)); 271 setenv("comconsole_speed", var, 1); 272 break; 273 } 274 case 'v': 275 howto |= RB_VERBOSE; 276 break; 277 } 278 } 279 } else { 280 vargood = 0; 281 for (j = 0; argv[i][j] != 0; j++) { 282 if (j == sizeof(var)) { 283 vargood = 0; 284 break; 285 } 286 if (j > 0 && argv[i][j] == '=') 287 vargood = 1; 288 var[j] = (char)argv[i][j]; 289 } 290 if (vargood) { 291 var[j] = 0; 292 putenv(var); 293 } 294 } 295 } 296 for (i = 0; howto_names[i].ev != NULL; i++) 297 if (howto & howto_names[i].mask) 298 setenv(howto_names[i].ev, "YES", 1); 299 if (howto & RB_MULTIPLE) { 300 if (howto & RB_SERIAL) 301 setenv("console", "comconsole efi" , 1); 302 else 303 setenv("console", "efi comconsole" , 1); 304 } else if (howto & RB_SERIAL) { 305 setenv("console", "comconsole" , 1); 306 } 307 308 if (efi_copy_init()) { 309 printf("failed to allocate staging area\n"); 310 return (EFI_BUFFER_TOO_SMALL); 311 } 312 313 /* 314 * March through the device switch probing for things. 315 */ 316 for (i = 0; devsw[i] != NULL; i++) 317 if (devsw[i]->dv_init != NULL) 318 (devsw[i]->dv_init)(); 319 320 /* Get our loaded image protocol interface structure. */ 321 BS->HandleProtocol(IH, &imgid, (VOID**)&img); 322 323 printf("Command line arguments:"); 324 for (i = 0; i < argc; i++) { 325 printf(" "); 326 print_str16(argv[i]); 327 } 328 printf("\n"); 329 330 printf("Image base: 0x%lx\n", (u_long)img->ImageBase); 331 printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, 332 ST->Hdr.Revision & 0xffff); 333 printf("EFI Firmware: "); 334 /* printf doesn't understand EFI Unicode */ 335 ST->ConOut->OutputString(ST->ConOut, ST->FirmwareVendor); 336 printf(" (rev %d.%02d)\n", ST->FirmwareRevision >> 16, 337 ST->FirmwareRevision & 0xffff); 338 339 printf("\n"); 340 printf("%s, Revision %s\n", bootprog_name, bootprog_rev); 341 printf("(%s, %s)\n", bootprog_maker, bootprog_date); 342 343 /* 344 * Disable the watchdog timer. By default the boot manager sets 345 * the timer to 5 minutes before invoking a boot option. If we 346 * want to return to the boot manager, we have to disable the 347 * watchdog timer and since we're an interactive program, we don't 348 * want to wait until the user types "quit". The timer may have 349 * fired by then. We don't care if this fails. It does not prevent 350 * normal functioning in any way... 351 */ 352 BS->SetWatchdogTimer(0, 0, 0, NULL); 353 354 if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0) 355 return (EFI_NOT_FOUND); 356 357 switch (dev->dv_type) { 358#ifdef EFI_ZFS_BOOT 359 case DEVT_ZFS: { 360 struct zfs_devdesc currdev; 361 362 currdev.d_dev = dev; 363 currdev.d_unit = unit; 364 currdev.d_type = currdev.d_dev->dv_type; 365 currdev.d_opendata = NULL; 366 currdev.pool_guid = pool_guid; 367 currdev.root_guid = 0; 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 init_zfs_bootenv(zfs_fmtdev(&currdev)); 373 break; 374 } 375#endif 376 default: { 377 struct devdesc currdev; 378 379 currdev.d_dev = dev; 380 currdev.d_unit = unit; 381 currdev.d_opendata = NULL; 382 currdev.d_type = currdev.d_dev->dv_type; 383 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), 384 efi_setcurrdev, env_nounset); 385 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, 386 env_nounset); 387 break; 388 } 389 } 390 391 setenv("LINES", "24", 1); /* optional */ 392 393 for (k = 0; k < ST->NumberOfTableEntries; k++) { 394 guid = &ST->ConfigurationTable[k].VendorGuid; 395 if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { 396 smbios_detect(ST->ConfigurationTable[k].VendorTable); 397 break; 398 } 399 } 400 401 interact(); /* doesn't return */ 402 403 return (EFI_SUCCESS); /* keep compiler happy */ 404} 405 406COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); 407 408static int 409command_reboot(int argc, char *argv[]) 410{ 411 int i; 412 413 for (i = 0; devsw[i] != NULL; ++i) 414 if (devsw[i]->dv_cleanup != NULL) 415 (devsw[i]->dv_cleanup)(); 416 417 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, 418 (CHAR16 *)"Reboot from the loader"); 419 420 /* NOTREACHED */ 421 return (CMD_ERROR); 422} 423 424COMMAND_SET(quit, "quit", "exit the loader", command_quit); 425 426static int 427command_quit(int argc, char *argv[]) 428{ 429 exit(0); 430 return (CMD_OK); 431} 432 433COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); 434 435static int 436command_memmap(int argc, char *argv[]) 437{ 438 UINTN sz; 439 EFI_MEMORY_DESCRIPTOR *map, *p; 440 UINTN key, dsz; 441 UINT32 dver; 442 EFI_STATUS status; 443 int i, ndesc; 444 static char *types[] = { 445 "Reserved", 446 "LoaderCode", 447 "LoaderData", 448 "BootServicesCode", 449 "BootServicesData", 450 "RuntimeServicesCode", 451 "RuntimeServicesData", 452 "ConventionalMemory", 453 "UnusableMemory", 454 "ACPIReclaimMemory", 455 "ACPIMemoryNVS", 456 "MemoryMappedIO", 457 "MemoryMappedIOPortSpace", 458 "PalCode" 459 }; 460 461 sz = 0; 462 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); 463 if (status != EFI_BUFFER_TOO_SMALL) { 464 printf("Can't determine memory map size\n"); 465 return (CMD_ERROR); 466 } 467 map = malloc(sz); 468 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); 469 if (EFI_ERROR(status)) { 470 printf("Can't read memory map\n"); 471 return (CMD_ERROR); 472 } 473 474 ndesc = sz / dsz; 475 printf("%23s %12s %12s %8s %4s\n", 476 "Type", "Physical", "Virtual", "#Pages", "Attr"); 477 478 for (i = 0, p = map; i < ndesc; 479 i++, p = NextMemoryDescriptor(p, dsz)) { 480 printf("%23s %012jx %012jx %08jx ", types[p->Type], 481 (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, 482 (uintmax_t)p->NumberOfPages); 483 if (p->Attribute & EFI_MEMORY_UC) 484 printf("UC "); 485 if (p->Attribute & EFI_MEMORY_WC) 486 printf("WC "); 487 if (p->Attribute & EFI_MEMORY_WT) 488 printf("WT "); 489 if (p->Attribute & EFI_MEMORY_WB) 490 printf("WB "); 491 if (p->Attribute & EFI_MEMORY_UCE) 492 printf("UCE "); 493 if (p->Attribute & EFI_MEMORY_WP) 494 printf("WP "); 495 if (p->Attribute & EFI_MEMORY_RP) 496 printf("RP "); 497 if (p->Attribute & EFI_MEMORY_XP) 498 printf("XP "); 499 printf("\n"); 500 } 501 502 return (CMD_OK); 503} 504 505COMMAND_SET(configuration, "configuration", "print configuration tables", 506 command_configuration); 507 508static const char * 509guid_to_string(EFI_GUID *guid) 510{ 511 static char buf[40]; 512 513 sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 514 guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], 515 guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], 516 guid->Data4[5], guid->Data4[6], guid->Data4[7]); 517 return (buf); 518} 519 520static int 521command_configuration(int argc, char *argv[]) 522{ 523 UINTN i; 524 525 printf("NumberOfTableEntries=%lu\n", 526 (unsigned long)ST->NumberOfTableEntries); 527 for (i = 0; i < ST->NumberOfTableEntries; i++) { 528 EFI_GUID *guid; 529 530 printf(" "); 531 guid = &ST->ConfigurationTable[i].VendorGuid; 532 if (!memcmp(guid, &mps, sizeof(EFI_GUID))) 533 printf("MPS Table"); 534 else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) 535 printf("ACPI Table"); 536 else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) 537 printf("ACPI 2.0 Table"); 538 else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) 539 printf("SMBIOS Table"); 540 else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) 541 printf("DXE Table"); 542 else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) 543 printf("HOB List Table"); 544 else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) 545 printf("Memory Type Information Table"); 546 else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) 547 printf("Debug Image Info Table"); 548 else 549 printf("Unknown Table (%s)", guid_to_string(guid)); 550 printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); 551 } 552 553 return (CMD_OK); 554} 555 556 557COMMAND_SET(mode, "mode", "change or display text modes", command_mode); 558 559static int 560command_mode(int argc, char *argv[]) 561{ 562 UINTN cols, rows; 563 unsigned int mode; 564 int i; 565 char *cp; 566 char rowenv[8]; 567 EFI_STATUS status; 568 SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 569 extern void HO(void); 570 571 conout = ST->ConOut; 572 573 if (argc > 1) { 574 mode = strtol(argv[1], &cp, 0); 575 if (cp[0] != '\0') { 576 printf("Invalid mode\n"); 577 return (CMD_ERROR); 578 } 579 status = conout->QueryMode(conout, mode, &cols, &rows); 580 if (EFI_ERROR(status)) { 581 printf("invalid mode %d\n", mode); 582 return (CMD_ERROR); 583 } 584 status = conout->SetMode(conout, mode); 585 if (EFI_ERROR(status)) { 586 printf("couldn't set mode %d\n", mode); 587 return (CMD_ERROR); 588 } 589 sprintf(rowenv, "%u", (unsigned)rows); 590 setenv("LINES", rowenv, 1); 591 HO(); /* set cursor */ 592 return (CMD_OK); 593 } 594 595 printf("Current mode: %d\n", conout->Mode->Mode); 596 for (i = 0; i <= conout->Mode->MaxMode; i++) { 597 status = conout->QueryMode(conout, i, &cols, &rows); 598 if (EFI_ERROR(status)) 599 continue; 600 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, 601 (unsigned)rows); 602 } 603 604 if (i != 0) 605 printf("Choose the mode with \"col <mode number>\"\n"); 606 607 return (CMD_OK); 608} 609 610 611COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); 612 613static int 614command_nvram(int argc, char *argv[]) 615{ 616 CHAR16 var[128]; 617 CHAR16 *data; 618 EFI_STATUS status; 619 EFI_GUID varguid = { 0,0,0,{0,0,0,0,0,0,0,0} }; 620 UINTN varsz, datasz, i; 621 SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 622 623 conout = ST->ConOut; 624 625 /* Initiate the search */ 626 status = RS->GetNextVariableName(&varsz, NULL, NULL); 627 628 for (; status != EFI_NOT_FOUND; ) { 629 status = RS->GetNextVariableName(&varsz, var, &varguid); 630 //if (EFI_ERROR(status)) 631 //break; 632 633 conout->OutputString(conout, var); 634 printf("="); 635 datasz = 0; 636 status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL); 637 /* XXX: check status */ 638 data = malloc(datasz); 639 status = RS->GetVariable(var, &varguid, NULL, &datasz, data); 640 if (EFI_ERROR(status)) 641 printf("<error retrieving variable>"); 642 else { 643 for (i = 0; i < datasz; i++) { 644 if (isalnum(data[i]) || isspace(data[i])) 645 printf("%c", data[i]); 646 else 647 printf("\\x%02x", data[i]); 648 } 649 } 650 /* XXX */ 651 pager_output("\n"); 652 free(data); 653 } 654 655 return (CMD_OK); 656} 657 658#ifdef EFI_ZFS_BOOT 659COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", 660 command_lszfs); 661 662static int 663command_lszfs(int argc, char *argv[]) 664{ 665 int err; 666 667 if (argc != 2) { 668 command_errmsg = "wrong number of arguments"; 669 return (CMD_ERROR); 670 } 671 672 err = zfs_list(argv[1]); 673 if (err != 0) { 674 command_errmsg = strerror(err); 675 return (CMD_ERROR); 676 } 677 return (CMD_OK); 678} 679 680COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", 681 command_reloadbe); 682 683static int 684command_reloadbe(int argc, char *argv[]) 685{ 686 int err; 687 char *root; 688 689 if (argc > 2) { 690 command_errmsg = "wrong number of arguments"; 691 return (CMD_ERROR); 692 } 693 694 if (argc == 2) { 695 err = zfs_bootenv(argv[1]); 696 } else { 697 root = getenv("zfs_be_root"); 698 if (root == NULL) { 699 return (CMD_OK); 700 } 701 err = zfs_bootenv(root); 702 } 703 704 if (err != 0) { 705 command_errmsg = strerror(err); 706 return (CMD_ERROR); 707 } 708 709 return (CMD_OK); 710} 711#endif 712 713#ifdef EFI_ZFS_BOOT 714static void 715efi_zfs_probe(void) 716{ 717 EFI_HANDLE h; 718 u_int unit; 719 int i; 720 char dname[SPECNAMELEN + 1]; 721 uint64_t guid; 722 723 unit = 0; 724 h = efi_find_handle(&efipart_dev, 0); 725 for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { 726 snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i); 727 if (zfs_probe_dev(dname, &guid) == 0) 728 (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid); 729 } 730} 731#endif 732