Unleashed v1.4
[unleashed.git] / usr / src / boot / sys / boot / efi / loader / main.c
blob0f5ff2101874e1b5adfa8af85e99ec12f5ac7a31
1 /*
2 * Copyright (c) 2008-2010 Rui Paulo
3 * Copyright (c) 2006 Marcel Moolenaar
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
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.
28 #include <sys/cdefs.h>
30 #include <sys/disk.h>
31 #include <sys/param.h>
32 #include <sys/reboot.h>
33 #include <sys/boot.h>
34 #include <sys/consplat.h>
35 #include <stand.h>
36 #include <inttypes.h>
37 #include <string.h>
38 #include <setjmp.h>
39 #include <disk.h>
41 #include <efi.h>
42 #include <efilib.h>
43 #include <efigpt.h>
45 #include <uuid.h>
47 #include <bootstrap.h>
48 #include <gfx_fb.h>
49 #include <smbios.h>
51 #include <libzfs.h>
52 #include <efizfs.h>
54 #include "loader_efi.h"
56 struct arch_switch archsw; /* MI/MD interface boundary */
58 EFI_GUID devid = DEVICE_PATH_PROTOCOL;
59 EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
60 EFI_GUID smbios = SMBIOS_TABLE_GUID;
61 EFI_GUID smbios3 = SMBIOS3_TABLE_GUID;
62 EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
64 extern void acpi_detect(void);
65 extern void efi_getsmap(void);
67 static EFI_LOADED_IMAGE *img;
70 * Number of seconds to wait for a keystroke before exiting with failure
71 * in the event no currdev is found. -2 means always break, -1 means
72 * never break, 0 means poll once and then reboot, > 0 means wait for
73 * that many seconds. "fail_timeout" can be set in the environment as
74 * well.
76 static int fail_timeout = 5;
78 bool
79 efi_zfs_is_preferred(EFI_HANDLE *h)
81 EFI_DEVICE_PATH *devpath, *dp, *node;
82 HARDDRIVE_DEVICE_PATH *hd;
83 bool ret;
84 extern UINT64 start_sector; /* from multiboot.S */
86 /* This check is true for chainloader case. */
87 if (h == img->DeviceHandle)
88 return (true);
91 * Make sure the image was loaded from the hard disk.
93 devpath = efi_lookup_devpath(img->DeviceHandle);
94 if (devpath == NULL)
95 return (false);
96 node = efi_devpath_last_node(devpath);
97 if (node == NULL)
98 return (false);
99 if (DevicePathType(node) != MEDIA_DEVICE_PATH &&
100 (DevicePathSubType(node) != MEDIA_FILEPATH_DP ||
101 DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)) {
102 return (false);
106 * XXX We ignore the MEDIA_FILEPATH_DP here for now as it is
107 * used on arm and we do not support arm.
109 ret = false;
110 dp = efi_devpath_trim(devpath);
111 devpath = NULL;
112 if (dp == NULL)
113 goto done;
115 devpath = efi_lookup_devpath(h);
116 if (devpath == NULL)
117 goto done;
118 hd = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(devpath);
119 if (hd == NULL) {
120 devpath = NULL;
121 goto done;
123 devpath = efi_devpath_trim(devpath);
124 if (devpath == NULL)
125 goto done;
127 if (!efi_devpath_match(dp, devpath))
128 goto done;
130 /* It is the same disk, do we have partition start? */
131 if (start_sector == 0)
132 ret = true;
133 else if (start_sector == hd->PartitionStart)
134 ret = true;
136 done:
137 free(dp);
138 free(devpath);
139 return (ret);
142 static bool
143 has_keyboard(void)
145 EFI_STATUS status;
146 EFI_DEVICE_PATH *path;
147 EFI_HANDLE *hin, *hin_end, *walker;
148 UINTN sz;
149 bool retval = false;
152 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
153 * do the typical dance to get the right sized buffer.
155 sz = 0;
156 hin = NULL;
157 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
158 if (status == EFI_BUFFER_TOO_SMALL) {
159 hin = (EFI_HANDLE *)malloc(sz);
160 status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
161 hin);
162 if (EFI_ERROR(status))
163 free(hin);
165 if (EFI_ERROR(status))
166 return (retval);
169 * Look at each of the handles. If it supports the device path protocol,
170 * use it to get the device path for this handle. Then see if that
171 * device path matches either the USB device path for keyboards or the
172 * legacy device path for keyboards.
174 hin_end = &hin[sz / sizeof(*hin)];
175 for (walker = hin; walker < hin_end; walker++) {
176 status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
177 if (EFI_ERROR(status))
178 continue;
180 while (!IsDevicePathEnd(path)) {
182 * Check for the ACPI keyboard node. All PNP3xx nodes
183 * are keyboards of different flavors. Note: It is
184 * unclear of there's always a keyboard node when
185 * there's a keyboard controller, or if there's only one
186 * when a keyboard is detected at boot.
188 if (DevicePathType(path) == ACPI_DEVICE_PATH &&
189 (DevicePathSubType(path) == ACPI_DP ||
190 DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
191 ACPI_HID_DEVICE_PATH *acpi;
193 acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
194 if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
195 (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
196 retval = true;
197 goto out;
200 * Check for USB keyboard node, if present. Unlike a
201 * PS/2 keyboard, these definitely only appear when
202 * connected to the system.
204 } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
205 DevicePathSubType(path) == MSG_USB_CLASS_DP) {
206 USB_CLASS_DEVICE_PATH *usb;
208 usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
209 if (usb->DeviceClass == 3 && /* HID */
210 usb->DeviceSubClass == 1 && /* Boot devices */
211 usb->DeviceProtocol == 1) { /* Boot keyboards */
212 retval = true;
213 goto out;
216 path = NextDevicePathNode(path);
219 out:
220 free(hin);
221 return (retval);
224 static void
225 set_currdev_devdesc(struct devdesc *currdev)
227 char *devname;
229 devname = efi_fmtdev(currdev);
231 printf("Setting currdev to %s\n", devname);
233 env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
234 env_nounset);
235 env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
238 static void
239 set_currdev_devsw(struct devsw *dev, int unit)
241 struct devdesc currdev;
243 currdev.d_dev = dev;
244 currdev.d_unit = unit;
246 set_currdev_devdesc(&currdev);
249 static void
250 set_currdev_pdinfo(pdinfo_t *dp)
254 * Disks are special: they have partitions. if the parent
255 * pointer is non-null, we're a partition not a full disk
256 * and we need to adjust currdev appropriately.
258 if (dp->pd_devsw->dv_type == DEVT_DISK) {
259 struct disk_devdesc currdev;
261 currdev.dd.d_dev = dp->pd_devsw;
262 if (dp->pd_parent == NULL) {
263 currdev.dd.d_unit = dp->pd_unit;
264 currdev.d_slice = -1;
265 currdev.d_partition = -1;
266 } else {
267 currdev.dd.d_unit = dp->pd_parent->pd_unit;
268 currdev.d_slice = dp->pd_unit;
269 currdev.d_partition = 255; /* Assumes GPT */
271 set_currdev_devdesc((struct devdesc *)&currdev);
272 } else {
273 set_currdev_devsw(dp->pd_devsw, dp->pd_unit);
277 static bool
278 sanity_check_currdev(void)
280 struct stat st;
282 return (stat("/boot/defaults/loader.conf", &st) == 0);
285 static bool
286 probe_zfs_currdev(uint64_t guid)
288 struct zfs_devdesc currdev;
290 currdev.dd.d_dev = &zfs_dev;
291 currdev.dd.d_unit = 0;
292 currdev.pool_guid = guid;
293 currdev.root_guid = 0;
294 set_currdev_devdesc((struct devdesc *)&currdev);
296 return (sanity_check_currdev());
299 static bool
300 try_as_currdev(pdinfo_t *pp)
302 uint64_t guid;
305 * If there's a zpool on this device, try it as a ZFS
306 * filesystem, which has somewhat different setup than all
307 * other types of fs due to imperfect loader integration.
308 * This all stems from ZFS being both a device (zpool) and
309 * a filesystem, plus the boot env feature.
311 if (efizfs_get_guid_by_handle(pp->pd_handle, &guid))
312 return (probe_zfs_currdev(guid));
315 * All other filesystems just need the pdinfo
316 * initialized in the standard way.
318 set_currdev_pdinfo(pp);
319 return (sanity_check_currdev());
322 static bool
323 find_currdev(EFI_LOADED_IMAGE *img)
325 pdinfo_t *dp, *pp;
326 EFI_DEVICE_PATH *devpath, *copy;
327 EFI_HANDLE h;
328 CHAR16 *text;
329 struct devsw *dev;
330 int unit;
331 uint64_t extra;
334 * Did efi_zfs_probe() detect the boot pool? If so, use the zpool
335 * it found, if it's sane. ZFS is the only thing that looks for
336 * disks and pools to boot.
338 if (pool_guid != 0) {
339 printf("Trying ZFS pool\n");
340 if (probe_zfs_currdev(pool_guid))
341 return (true);
345 * Try to find the block device by its handle based on the
346 * image we're booting. If we can't find a sane partition,
347 * search all the other partitions of the disk. We do not
348 * search other disks because it's a violation of the UEFI
349 * boot protocol to do so. We fail and let UEFI go on to
350 * the next candidate.
352 dp = efiblk_get_pdinfo_by_handle(img->DeviceHandle);
353 if (dp != NULL) {
354 text = efi_devpath_name(dp->pd_devpath);
355 if (text != NULL) {
356 printf("Trying ESP: %S\n", text);
357 efi_free_devpath_name(text);
359 set_currdev_pdinfo(dp);
360 if (sanity_check_currdev())
361 return (true);
362 if (dp->pd_parent != NULL) {
363 dp = dp->pd_parent;
364 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
365 text = efi_devpath_name(pp->pd_devpath);
366 if (text != NULL) {
367 printf("And now the part: %S\n", text);
368 efi_free_devpath_name(text);
371 * Roll up the ZFS special case
372 * for those partitions that have
373 * zpools on them
375 if (try_as_currdev(pp))
376 return (true);
379 } else {
380 printf("Can't find device by handle\n");
384 * Try the device handle from our loaded image first. If that
385 * fails, use the device path from the loaded image and see if
386 * any of the nodes in that path match one of the enumerated
387 * handles. Currently, this handle list is only for netboot.
389 if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
390 set_currdev_devsw(dev, unit);
391 if (sanity_check_currdev())
392 return (true);
395 copy = NULL;
396 devpath = efi_lookup_image_devpath(IH);
397 while (devpath != NULL) {
398 h = efi_devpath_handle(devpath);
399 if (h == NULL)
400 break;
402 free(copy);
403 copy = NULL;
405 if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
406 set_currdev_devsw(dev, unit);
407 if (sanity_check_currdev())
408 return (true);
411 devpath = efi_lookup_devpath(h);
412 if (devpath != NULL) {
413 copy = efi_devpath_trim(devpath);
414 devpath = copy;
417 free(copy);
419 return (false);
422 static bool
423 interactive_interrupt(const char *msg)
425 time_t now, then, last;
427 last = 0;
428 now = then = getsecs();
429 printf("%s\n", msg);
430 if (fail_timeout == -2) /* Always break to OK */
431 return (true);
432 if (fail_timeout == -1) /* Never break to OK */
433 return (false);
434 do {
435 if (last != now) {
436 printf("press any key to interrupt reboot in %d seconds\r",
437 fail_timeout - (int)(now - then));
438 last = now;
441 /* XXX no pause or timeout wait for char */
442 if (ischar())
443 return (true);
444 now = getsecs();
445 } while (now - then < fail_timeout);
446 return (false);
449 EFI_STATUS
450 main(int argc, CHAR16 *argv[])
452 char var[128];
453 int i, j, howto;
454 bool vargood;
455 void *ptr;
456 bool has_kbd;
457 char *s;
458 EFI_DEVICE_PATH *imgpath;
459 CHAR16 *text;
460 EFI_STATUS status;
461 UINT16 boot_current;
462 size_t sz;
463 UINT16 boot_order[100];
465 archsw.arch_autoload = efi_autoload;
466 archsw.arch_getdev = efi_getdev;
467 archsw.arch_copyin = efi_copyin;
468 archsw.arch_copyout = efi_copyout;
469 archsw.arch_readin = efi_readin;
470 archsw.arch_loadaddr = efi_loadaddr;
471 archsw.arch_free_loadaddr = efi_free_loadaddr;
472 /* Note this needs to be set before ZFS init. */
473 archsw.arch_zfs_probe = efi_zfs_probe;
475 /* Get our loaded image protocol interface structure. */
476 BS->HandleProtocol(IH, &imgid, (VOID**)&img);
478 /* Init the time source */
479 efi_time_init();
481 has_kbd = has_keyboard();
484 * XXX Chicken-and-egg problem; we want to have console output
485 * early, but some console attributes may depend on reading from
486 * eg. the boot device, which we can't do yet. We can use
487 * printf() etc. once this is done.
489 cons_probe();
490 efi_getsmap();
493 * Initialise the block cache. Set the upper limit.
495 bcache_init(32768, 512);
498 * Parse the args to set the console settings, etc
499 * boot1.efi passes these in, if it can read /boot.config or /boot/config
500 * or iPXE may be setup to pass these in. Or the optional argument in the
501 * boot environment was used to pass these arguments in (in which case
502 * neither /boot.config nor /boot/config are consulted).
504 * Loop through the args, and for each one that contains an '=' that is
505 * not the first character, add it to the environment. This allows
506 * loader and kernel env vars to be passed on the command line. Convert
507 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this
508 * method is flawed for non-ASCII characters).
510 howto = 0;
511 for (i = 1; i < argc; i++) {
512 if (argv[i][0] == '-') {
513 for (j = 1; argv[i][j] != 0; j++) {
514 int ch;
516 ch = argv[i][j];
517 switch (ch) {
518 case 'a':
519 howto |= RB_ASKNAME;
520 break;
521 case 'd':
522 howto |= RB_KDB;
523 break;
524 case 'D':
525 howto |= RB_MULTIPLE;
526 break;
527 case 'h':
528 howto |= RB_SERIAL;
529 break;
530 case 'm':
531 howto |= RB_MUTE;
532 break;
533 case 'p':
534 howto |= RB_PAUSE;
535 break;
536 case 'P':
537 if (!has_kbd)
538 howto |= RB_SERIAL | RB_MULTIPLE;
539 break;
540 case 'r':
541 howto |= RB_DFLTROOT;
542 break;
543 case 's':
544 howto |= RB_SINGLE;
545 break;
546 case 'S':
547 if (argv[i][j + 1] == 0) {
548 if (i + 1 == argc) {
549 strncpy(var, "115200",
550 sizeof(var));
551 } else {
552 CHAR16 *ptr;
553 ptr = &argv[i + 1][0];
554 cpy16to8(ptr, var,
555 sizeof(var));
557 i++;
558 } else {
559 cpy16to8(&argv[i][j + 1], var,
560 sizeof(var));
562 strncat(var, ",8,n,1,-", sizeof(var));
563 setenv("ttya-mode", var, 1);
564 break;
565 case 'v':
566 howto |= RB_VERBOSE;
567 break;
570 } else {
571 vargood = false;
572 for (j = 0; argv[i][j] != 0; j++) {
573 if (j == sizeof(var)) {
574 vargood = false;
575 break;
577 if (j > 0 && argv[i][j] == '=')
578 vargood = true;
579 var[j] = (char)argv[i][j];
581 if (vargood) {
582 var[j] = 0;
583 putenv(var);
587 for (i = 0; howto_names[i].ev != NULL; i++)
588 if (howto & howto_names[i].mask)
589 setenv(howto_names[i].ev, "YES", 1);
592 * XXX we need fallback to this stuff after looking at the ConIn,
593 * ConOut and ConErr variables.
595 if (howto & RB_MULTIPLE) {
596 if (howto & RB_SERIAL)
597 setenv("console", "ttya text" , 1);
598 else
599 setenv("console", "text ttya" , 1);
600 } else if (howto & RB_SERIAL) {
601 setenv("console", "ttya" , 1);
602 } else
603 setenv("console", "text" , 1);
605 if ((s = getenv("fail_timeout")) != NULL)
606 fail_timeout = strtol(s, NULL, 10);
609 * Scan the BLOCK IO MEDIA handles then
610 * march through the device switch probing for things.
612 if ((i = efipart_inithandles()) == 0) {
613 for (i = 0; devsw[i] != NULL; i++)
614 if (devsw[i]->dv_init != NULL)
615 (devsw[i]->dv_init)();
616 } else
617 printf("efipart_inithandles failed %d, expect failures", i);
619 printf("Command line arguments:");
620 for (i = 0; i < argc; i++) {
621 printf(" %S", argv[i]);
623 printf("\n");
625 printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
626 printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
627 ST->Hdr.Revision & 0xffff);
628 printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
629 ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
631 printf("\n%s", bootprog_info);
633 /* Determine the devpath of our image so we can prefer it. */
634 text = efi_devpath_name(img->FilePath);
635 if (text != NULL) {
636 printf(" Load Path: %S\n", text);
637 efi_setenv_illumos_wcs("LoaderPath", text);
638 efi_free_devpath_name(text);
641 status = BS->HandleProtocol(img->DeviceHandle, &devid,
642 (void **)&imgpath);
643 if (status == EFI_SUCCESS) {
644 text = efi_devpath_name(imgpath);
645 if (text != NULL) {
646 printf(" Load Device: %S\n", text);
647 efi_setenv_illumos_wcs("LoaderDev", text);
648 efi_free_devpath_name(text);
652 boot_current = 0;
653 sz = sizeof(boot_current);
654 efi_global_getenv("BootCurrent", &boot_current, &sz);
655 printf(" BootCurrent: %04x\n", boot_current);
657 sz = sizeof(boot_order);
658 efi_global_getenv("BootOrder", &boot_order, &sz);
659 printf(" BootOrder:");
660 for (i = 0; i < sz / sizeof(boot_order[0]); i++)
661 printf(" %04x%s", boot_order[i],
662 boot_order[i] == boot_current ? "[*]" : "");
663 printf("\n");
666 * Disable the watchdog timer. By default the boot manager sets
667 * the timer to 5 minutes before invoking a boot option. If we
668 * want to return to the boot manager, we have to disable the
669 * watchdog timer and since we're an interactive program, we don't
670 * want to wait until the user types "quit". The timer may have
671 * fired by then. We don't care if this fails. It does not prevent
672 * normal functioning in any way...
674 BS->SetWatchdogTimer(0, 0, 0, NULL);
677 * Try and find a good currdev based on the image that was booted.
678 * It might be desirable here to have a short pause to allow falling
679 * through to the boot loader instead of returning instantly to follow
680 * the boot protocol and also allow an escape hatch for users wishing
681 * to try something different.
683 if (!find_currdev(img))
684 if (!interactive_interrupt("Failed to find bootable partition"))
685 return (EFI_NOT_FOUND);
687 autoload_font(); /* Set up the font list for console. */
688 efi_init_environment();
689 setenv("ISADIR", "amd64", 1); /* we only build 64bit */
690 bi_isadir(); /* set ISADIR */
691 acpi_detect();
693 if ((ptr = efi_get_table(&smbios3)) == NULL)
694 ptr = efi_get_table(&smbios);
695 smbios_detect(ptr);
697 interact(NULL); /* doesn't return */
699 return (EFI_SUCCESS); /* keep compiler happy */
702 COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
704 static int
705 command_reboot(int argc __unused, char *argv[] __unused)
707 int i;
709 for (i = 0; devsw[i] != NULL; ++i)
710 if (devsw[i]->dv_cleanup != NULL)
711 (devsw[i]->dv_cleanup)();
713 RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
715 /* NOTREACHED */
716 return (CMD_ERROR);
719 COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff);
721 static int
722 command_poweroff(int argc __unused, char *argv[] __unused)
724 int i;
726 for (i = 0; devsw[i] != NULL; ++i)
727 if (devsw[i]->dv_cleanup != NULL)
728 (devsw[i]->dv_cleanup)();
730 RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
732 /* NOTREACHED */
733 return (CMD_ERROR);
736 COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
738 static int
739 command_memmap(int argc __unused, char *argv[] __unused)
741 UINTN sz;
742 EFI_MEMORY_DESCRIPTOR *map, *p;
743 UINTN key, dsz;
744 UINT32 dver;
745 EFI_STATUS status;
746 int i, ndesc;
747 int rv = 0;
748 char line[80];
750 sz = 0;
751 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
752 if (status != EFI_BUFFER_TOO_SMALL) {
753 printf("Can't determine memory map size\n");
754 return (CMD_ERROR);
756 map = malloc(sz);
757 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
758 if (EFI_ERROR(status)) {
759 printf("Can't read memory map\n");
760 return (CMD_ERROR);
763 ndesc = sz / dsz;
764 snprintf(line, 80, "%23s %12s %12s %8s %4s\n",
765 "Type", "Physical", "Virtual", "#Pages", "Attr");
766 pager_open();
767 rv = pager_output(line);
768 if (rv) {
769 pager_close();
770 return (CMD_OK);
773 for (i = 0, p = map; i < ndesc;
774 i++, p = NextMemoryDescriptor(p, dsz)) {
775 snprintf(line, 80, "%23s %012jx %012jx %08jx ",
776 efi_memory_type(p->Type), p->PhysicalStart,
777 p->VirtualStart, p->NumberOfPages);
778 rv = pager_output(line);
779 if (rv)
780 break;
782 if (p->Attribute & EFI_MEMORY_UC)
783 printf("UC ");
784 if (p->Attribute & EFI_MEMORY_WC)
785 printf("WC ");
786 if (p->Attribute & EFI_MEMORY_WT)
787 printf("WT ");
788 if (p->Attribute & EFI_MEMORY_WB)
789 printf("WB ");
790 if (p->Attribute & EFI_MEMORY_UCE)
791 printf("UCE ");
792 if (p->Attribute & EFI_MEMORY_WP)
793 printf("WP ");
794 if (p->Attribute & EFI_MEMORY_RP)
795 printf("RP ");
796 if (p->Attribute & EFI_MEMORY_XP)
797 printf("XP ");
798 if (p->Attribute & EFI_MEMORY_NV)
799 printf("NV ");
800 if (p->Attribute & EFI_MEMORY_MORE_RELIABLE)
801 printf("MR ");
802 if (p->Attribute & EFI_MEMORY_RO)
803 printf("RO ");
804 rv = pager_output("\n");
805 if (rv)
806 break;
809 pager_close();
810 return (CMD_OK);
813 COMMAND_SET(configuration, "configuration", "print configuration tables",
814 command_configuration);
816 static int
817 command_configuration(int argc __unused, char *argv[] __unused)
819 UINTN i;
820 char *name;
822 printf("NumberOfTableEntries=%lu\n",
823 (unsigned long)ST->NumberOfTableEntries);
824 for (i = 0; i < ST->NumberOfTableEntries; i++) {
825 EFI_GUID *guid;
827 printf(" ");
828 guid = &ST->ConfigurationTable[i].VendorGuid;
830 if (efi_guid_to_name(guid, &name) == true) {
831 printf(name);
832 free(name);
833 } else {
834 printf("Error while translating UUID to name");
836 printf(" at %p\n", ST->ConfigurationTable[i].VendorTable);
839 return (CMD_OK);
843 COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
845 static int
846 command_mode(int argc, char *argv[])
848 UINTN cols, rows;
849 unsigned int mode;
850 int i;
851 char *cp;
852 EFI_STATUS status;
853 SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
854 EFI_CONSOLE_CONTROL_SCREEN_MODE sm;
856 if (plat_stdout_is_framebuffer())
857 sm = EfiConsoleControlScreenGraphics;
858 else
859 sm = EfiConsoleControlScreenText;
861 conout = ST->ConOut;
863 if (argc > 1) {
864 mode = strtol(argv[1], &cp, 0);
865 if (cp[0] != '\0') {
866 printf("Invalid mode\n");
867 return (CMD_ERROR);
869 status = conout->QueryMode(conout, mode, &cols, &rows);
870 if (EFI_ERROR(status)) {
871 printf("invalid mode %d\n", mode);
872 return (CMD_ERROR);
874 status = conout->SetMode(conout, mode);
875 if (EFI_ERROR(status)) {
876 printf("couldn't set mode %d\n", mode);
877 return (CMD_ERROR);
879 plat_cons_update_mode(sm);
880 return (CMD_OK);
883 printf("Current mode: %d\n", conout->Mode->Mode);
884 for (i = 0; i <= conout->Mode->MaxMode; i++) {
885 status = conout->QueryMode(conout, i, &cols, &rows);
886 if (EFI_ERROR(status))
887 continue;
888 printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
889 (unsigned)rows);
892 if (i != 0)
893 printf("Select a mode with the command \"mode <number>\"\n");
895 return (CMD_OK);
898 COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi);
900 static int
901 command_lsefi(int argc __unused, char *argv[] __unused)
903 char *name;
904 EFI_HANDLE *buffer = NULL;
905 EFI_HANDLE handle;
906 UINTN bufsz = 0, i, j;
907 EFI_STATUS status;
908 int ret;
910 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
911 if (status != EFI_BUFFER_TOO_SMALL) {
912 snprintf(command_errbuf, sizeof (command_errbuf),
913 "unexpected error: %lld", (long long)status);
914 return (CMD_ERROR);
916 if ((buffer = malloc(bufsz)) == NULL) {
917 sprintf(command_errbuf, "out of memory");
918 return (CMD_ERROR);
921 status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer);
922 if (EFI_ERROR(status)) {
923 free(buffer);
924 snprintf(command_errbuf, sizeof (command_errbuf),
925 "LocateHandle() error: %lld", (long long)status);
926 return (CMD_ERROR);
929 pager_open();
930 for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) {
931 UINTN nproto = 0;
932 EFI_GUID **protocols = NULL;
934 handle = buffer[i];
935 printf("Handle %p", handle);
936 if (pager_output("\n"))
937 break;
938 /* device path */
940 status = BS->ProtocolsPerHandle(handle, &protocols, &nproto);
941 if (EFI_ERROR(status)) {
942 snprintf(command_errbuf, sizeof (command_errbuf),
943 "ProtocolsPerHandle() error: %lld",
944 (long long)status);
945 continue;
948 for (j = 0; j < nproto; j++) {
949 if (efi_guid_to_name(protocols[j], &name) == true) {
950 printf(" %s", name);
951 free(name);
952 } else {
953 printf("Error while translating UUID to name");
955 if ((ret = pager_output("\n")) != 0)
956 break;
958 BS->FreePool(protocols);
959 if (ret != 0)
960 break;
962 pager_close();
963 free(buffer);
964 return (CMD_OK);
967 COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
968 command_lszfs);
970 static int
971 command_lszfs(int argc, char *argv[])
973 int err;
975 if (argc != 2) {
976 command_errmsg = "wrong number of arguments";
977 return (CMD_ERROR);
980 err = zfs_list(argv[1]);
981 if (err != 0) {
982 command_errmsg = strerror(err);
983 return (CMD_ERROR);
985 return (CMD_OK);
988 #ifdef __FreeBSD__
989 COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
990 command_reloadbe);
992 static int
993 command_reloadbe(int argc, char *argv[])
995 int err;
996 char *root;
998 if (argc > 2) {
999 command_errmsg = "wrong number of arguments";
1000 return (CMD_ERROR);
1003 if (argc == 2) {
1004 err = zfs_bootenv(argv[1]);
1005 } else {
1006 root = getenv("zfs_be_root");
1007 if (root == NULL) {
1008 return (CMD_OK);
1010 err = zfs_bootenv(root);
1013 if (err != 0) {
1014 command_errmsg = strerror(err);
1015 return (CMD_ERROR);
1018 return (CMD_OK);
1020 #endif /* __FreeBSD__ */
1022 #ifdef LOADER_FDT_SUPPORT
1023 extern int command_fdt_internal(int argc, char *argv[]);
1026 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
1027 * and declaring it as extern is in contradiction with COMMAND_SET() macro
1028 * (which uses static pointer), we're defining wrapper function, which
1029 * calls the proper fdt handling routine.
1031 static int
1032 command_fdt(int argc, char *argv[])
1034 return (command_fdt_internal(argc, argv));
1037 COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
1038 #endif
1041 * Chain load another efi loader.
1043 static int
1044 command_chain(int argc, char *argv[])
1046 EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
1047 EFI_HANDLE loaderhandle;
1048 EFI_LOADED_IMAGE *loaded_image;
1049 EFI_STATUS status;
1050 struct stat st;
1051 struct devdesc *dev;
1052 char *name, *path;
1053 void *buf;
1054 int fd;
1056 if (argc < 2) {
1057 command_errmsg = "wrong number of arguments";
1058 return (CMD_ERROR);
1061 name = argv[1];
1063 if ((fd = open(name, O_RDONLY)) < 0) {
1064 command_errmsg = "no such file";
1065 return (CMD_ERROR);
1068 if (fstat(fd, &st) < -1) {
1069 command_errmsg = "stat failed";
1070 close(fd);
1071 return (CMD_ERROR);
1074 status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
1075 if (status != EFI_SUCCESS) {
1076 command_errmsg = "failed to allocate buffer";
1077 close(fd);
1078 return (CMD_ERROR);
1080 if (read(fd, buf, st.st_size) != st.st_size) {
1081 command_errmsg = "error while reading the file";
1082 (void)BS->FreePool(buf);
1083 close(fd);
1084 return (CMD_ERROR);
1086 close(fd);
1087 status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
1088 (void)BS->FreePool(buf);
1089 if (status != EFI_SUCCESS) {
1090 command_errmsg = "LoadImage failed";
1091 return (CMD_ERROR);
1093 status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
1094 (void **)&loaded_image);
1096 if (argc > 2) {
1097 int i, len = 0;
1098 CHAR16 *argp;
1100 for (i = 2; i < argc; i++)
1101 len += strlen(argv[i]) + 1;
1103 len *= sizeof (*argp);
1104 loaded_image->LoadOptions = argp = malloc (len);
1105 if (loaded_image->LoadOptions == NULL) {
1106 (void) BS->UnloadImage(loaded_image);
1107 return (CMD_ERROR);
1109 loaded_image->LoadOptionsSize = len;
1110 for (i = 2; i < argc; i++) {
1111 char *ptr = argv[i];
1112 while (*ptr)
1113 *(argp++) = *(ptr++);
1114 *(argp++) = ' ';
1116 *(--argv) = 0;
1119 if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
1120 struct zfs_devdesc *z_dev;
1121 struct disk_devdesc *d_dev;
1122 pdinfo_t *hd, *pd;
1124 switch (dev->d_dev->dv_type) {
1125 case DEVT_ZFS:
1126 z_dev = (struct zfs_devdesc *)dev;
1127 loaded_image->DeviceHandle =
1128 efizfs_get_handle_by_guid(z_dev->pool_guid);
1129 break;
1130 case DEVT_NET:
1131 loaded_image->DeviceHandle =
1132 efi_find_handle(dev->d_dev, dev->d_unit);
1133 break;
1134 default:
1135 hd = efiblk_get_pdinfo(dev);
1136 if (STAILQ_EMPTY(&hd->pd_part)) {
1137 loaded_image->DeviceHandle = hd->pd_handle;
1138 break;
1140 d_dev = (struct disk_devdesc *)dev;
1141 STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
1143 * d_partition should be 255
1145 if (pd->pd_unit == d_dev->d_slice) {
1146 loaded_image->DeviceHandle =
1147 pd->pd_handle;
1148 break;
1151 break;
1155 dev_cleanup();
1156 status = BS->StartImage(loaderhandle, NULL, NULL);
1157 if (status != EFI_SUCCESS) {
1158 command_errmsg = "StartImage failed";
1159 free(loaded_image->LoadOptions);
1160 loaded_image->LoadOptions = NULL;
1161 status = BS->UnloadImage(loaded_image);
1162 return (CMD_ERROR);
1165 return (CMD_ERROR); /* not reached */
1168 COMMAND_SET(chain, "chain", "chain load file", command_chain);