main.c revision 332748
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: stable/11/stand/efi/loader/main.c 332748 2018-04-19 01:15:19Z kevans $");
30
31#include <sys/disk.h>
32#include <sys/param.h>
33#include <sys/reboot.h>
34#include <sys/boot.h>
35#include <stdint.h>
36#include <stand.h>
37#include <string.h>
38#include <setjmp.h>
39#include <disk.h>
40
41#include <efi.h>
42#include <efilib.h>
43
44#include <uuid.h>
45
46#include <bootstrap.h>
47#include <smbios.h>
48
49#ifdef EFI_ZFS_BOOT
50#include <libzfs.h>
51
52#include "efizfs.h"
53#endif
54
55#include "loader_efi.h"
56
57extern char bootprog_info[];
58
59struct arch_switch archsw;	/* MI/MD interface boundary */
60
61EFI_GUID acpi = ACPI_TABLE_GUID;
62EFI_GUID acpi20 = ACPI_20_TABLE_GUID;
63EFI_GUID devid = DEVICE_PATH_PROTOCOL;
64EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
65EFI_GUID mps = MPS_TABLE_GUID;
66EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL;
67EFI_GUID smbios = SMBIOS_TABLE_GUID;
68EFI_GUID smbios3 = SMBIOS3_TABLE_GUID;
69EFI_GUID dxe = DXE_SERVICES_TABLE_GUID;
70EFI_GUID hoblist = HOB_LIST_TABLE_GUID;
71EFI_GUID lzmadecomp = LZMA_DECOMPRESSION_GUID;
72EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID;
73EFI_GUID esrt = ESRT_TABLE_GUID;
74EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID;
75EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID;
76EFI_GUID fdtdtb = FDT_TABLE_GUID;
77EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
78
79static EFI_LOADED_IMAGE *img;
80
81/*
82 * Number of seconds to wait for a keystroke before exiting with failure
83 * in the event no currdev is found. -2 means always break, -1 means
84 * never break, 0 means poll once and then reboot, > 0 means wait for
85 * that many seconds. "fail_timeout" can be set in the environment as
86 * well.
87 */
88static int fail_timeout = 5;
89
90#ifdef	EFI_ZFS_BOOT
91bool
92efi_zfs_is_preferred(EFI_HANDLE *h)
93{
94        return (h == img->DeviceHandle);
95}
96#endif
97
98static int
99has_keyboard(void)
100{
101	EFI_STATUS status;
102	EFI_DEVICE_PATH *path;
103	EFI_HANDLE *hin, *hin_end, *walker;
104	UINTN sz;
105	int retval = 0;
106
107	/*
108	 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
109	 * do the typical dance to get the right sized buffer.
110	 */
111	sz = 0;
112	hin = NULL;
113	status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0);
114	if (status == EFI_BUFFER_TOO_SMALL) {
115		hin = (EFI_HANDLE *)malloc(sz);
116		status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz,
117		    hin);
118		if (EFI_ERROR(status))
119			free(hin);
120	}
121	if (EFI_ERROR(status))
122		return retval;
123
124	/*
125	 * Look at each of the handles. If it supports the device path protocol,
126	 * use it to get the device path for this handle. Then see if that
127	 * device path matches either the USB device path for keyboards or the
128	 * legacy device path for keyboards.
129	 */
130	hin_end = &hin[sz / sizeof(*hin)];
131	for (walker = hin; walker < hin_end; walker++) {
132		status = BS->HandleProtocol(*walker, &devid, (VOID **)&path);
133		if (EFI_ERROR(status))
134			continue;
135
136		while (!IsDevicePathEnd(path)) {
137			/*
138			 * Check for the ACPI keyboard node. All PNP3xx nodes
139			 * are keyboards of different flavors. Note: It is
140			 * unclear of there's always a keyboard node when
141			 * there's a keyboard controller, or if there's only one
142			 * when a keyboard is detected at boot.
143			 */
144			if (DevicePathType(path) == ACPI_DEVICE_PATH &&
145			    (DevicePathSubType(path) == ACPI_DP ||
146				DevicePathSubType(path) == ACPI_EXTENDED_DP)) {
147				ACPI_HID_DEVICE_PATH  *acpi;
148
149				acpi = (ACPI_HID_DEVICE_PATH *)(void *)path;
150				if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 &&
151				    (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) {
152					retval = 1;
153					goto out;
154				}
155			/*
156			 * Check for USB keyboard node, if present. Unlike a
157			 * PS/2 keyboard, these definitely only appear when
158			 * connected to the system.
159			 */
160			} else if (DevicePathType(path) == MESSAGING_DEVICE_PATH &&
161			    DevicePathSubType(path) == MSG_USB_CLASS_DP) {
162				USB_CLASS_DEVICE_PATH *usb;
163
164				usb = (USB_CLASS_DEVICE_PATH *)(void *)path;
165				if (usb->DeviceClass == 3 && /* HID */
166				    usb->DeviceSubClass == 1 && /* Boot devices */
167				    usb->DeviceProtocol == 1) { /* Boot keyboards */
168					retval = 1;
169					goto out;
170				}
171			}
172			path = NextDevicePathNode(path);
173		}
174	}
175out:
176	free(hin);
177	return retval;
178}
179
180static void
181set_currdev_devdesc(struct devdesc *currdev)
182{
183	const char *devname;
184
185	devname = efi_fmtdev(currdev);
186
187	printf("Setting currdev to %s\n", devname);
188
189	env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset);
190	env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
191}
192
193static void
194set_currdev_devsw(struct devsw *dev, int unit)
195{
196	struct devdesc currdev;
197
198	currdev.d_dev = dev;
199	currdev.d_unit = unit;
200
201	set_currdev_devdesc(&currdev);
202}
203
204static void
205set_currdev_pdinfo(pdinfo_t *dp)
206{
207
208	/*
209	 * Disks are special: they have partitions. if the parent
210	 * pointer is non-null, we're a partition not a full disk
211	 * and we need to adjust currdev appropriately.
212	 */
213	if (dp->pd_devsw->dv_type == DEVT_DISK) {
214		struct disk_devdesc currdev;
215
216		currdev.dd.d_dev = dp->pd_devsw;
217		if (dp->pd_parent == NULL) {
218			currdev.dd.d_unit = dp->pd_unit;
219			currdev.d_slice = -1;
220			currdev.d_partition = -1;
221		} else {
222			currdev.dd.d_unit = dp->pd_parent->pd_unit;
223			currdev.d_slice = dp->pd_unit;
224			currdev.d_partition = 255;	/* Assumes GPT */
225		}
226		set_currdev_devdesc((struct devdesc *)&currdev);
227	} else {
228		set_currdev_devsw(dp->pd_devsw, dp->pd_unit);
229	}
230}
231
232static bool
233sanity_check_currdev(void)
234{
235	struct stat st;
236
237	return (stat("/boot/defaults/loader.conf", &st) == 0);
238}
239
240#ifdef EFI_ZFS_BOOT
241static bool
242probe_zfs_currdev(uint64_t guid)
243{
244	char *devname;
245	struct zfs_devdesc currdev;
246
247	currdev.dd.d_dev = &zfs_dev;
248	currdev.dd.d_unit = 0;
249	currdev.pool_guid = guid;
250	currdev.root_guid = 0;
251	set_currdev_devdesc((struct devdesc *)&currdev);
252	devname = efi_fmtdev(&currdev);
253	init_zfs_bootenv(devname);
254
255	return (sanity_check_currdev());
256}
257#endif
258
259static bool
260try_as_currdev(pdinfo_t *hd, pdinfo_t *pp)
261{
262	uint64_t guid;
263
264#ifdef EFI_ZFS_BOOT
265	/*
266	 * If there's a zpool on this device, try it as a ZFS
267	 * filesystem, which has somewhat different setup than all
268	 * other types of fs due to imperfect loader integration.
269	 * This all stems from ZFS being both a device (zpool) and
270	 * a filesystem, plus the boot env feature.
271	 */
272	if (efizfs_get_guid_by_handle(pp->pd_handle, &guid))
273		return (probe_zfs_currdev(guid));
274#endif
275	/*
276	 * All other filesystems just need the pdinfo
277	 * initialized in the standard way.
278	 */
279	set_currdev_pdinfo(pp);
280	return (sanity_check_currdev());
281}
282
283static int
284find_currdev(EFI_LOADED_IMAGE *img)
285{
286	pdinfo_t *dp, *pp;
287	EFI_DEVICE_PATH *devpath, *copy;
288	EFI_HANDLE h;
289	CHAR16 *text;
290	struct devsw *dev;
291	int unit;
292	uint64_t extra;
293
294#ifdef EFI_ZFS_BOOT
295	/*
296	 * Did efi_zfs_probe() detect the boot pool? If so, use the zpool
297	 * it found, if it's sane. ZFS is the only thing that looks for
298	 * disks and pools to boot. This may change in the future, however,
299	 * if we allow specifying which pool to boot from via UEFI variables
300	 * rather than the bootenv stuff that FreeBSD uses today.
301	 */
302	if (pool_guid != 0) {
303		printf("Trying ZFS pool\n");
304		if (probe_zfs_currdev(pool_guid))
305			return (0);
306	}
307#endif /* EFI_ZFS_BOOT */
308
309	/*
310	 * Try to find the block device by its handle based on the
311	 * image we're booting. If we can't find a sane partition,
312	 * search all the other partitions of the disk. We do not
313	 * search other disks because it's a violation of the UEFI
314	 * boot protocol to do so. We fail and let UEFI go on to
315	 * the next candidate.
316	 */
317	dp = efiblk_get_pdinfo_by_handle(img->DeviceHandle);
318	if (dp != NULL) {
319		text = efi_devpath_name(dp->pd_devpath);
320		if (text != NULL) {
321			printf("Trying ESP: %S\n", text);
322			efi_free_devpath_name(text);
323		}
324		set_currdev_pdinfo(dp);
325		if (sanity_check_currdev())
326			return (0);
327		if (dp->pd_parent != NULL) {
328			dp = dp->pd_parent;
329			STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
330				text = efi_devpath_name(pp->pd_devpath);
331				if (text != NULL) {
332					printf("And now the part: %S\n", text);
333					efi_free_devpath_name(text);
334				}
335				/*
336				 * Roll up the ZFS special case
337				 * for those partitions that have
338				 * zpools on them
339				 */
340				if (try_as_currdev(dp, pp))
341					return (0);
342			}
343		}
344	} else {
345		printf("Can't find device by handle\n");
346	}
347
348	/*
349	 * Try the device handle from our loaded image first.  If that
350	 * fails, use the device path from the loaded image and see if
351	 * any of the nodes in that path match one of the enumerated
352	 * handles. Currently, this handle list is only for netboot.
353	 */
354	if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
355		set_currdev_devsw(dev, unit);
356		if (sanity_check_currdev())
357			return (0);
358	}
359
360	copy = NULL;
361	devpath = efi_lookup_image_devpath(IH);
362	while (devpath != NULL) {
363		h = efi_devpath_handle(devpath);
364		if (h == NULL)
365			break;
366
367		free(copy);
368		copy = NULL;
369
370		if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
371			set_currdev_devsw(dev, unit);
372			if (sanity_check_currdev())
373				return (0);
374		}
375
376		devpath = efi_lookup_devpath(h);
377		if (devpath != NULL) {
378			copy = efi_devpath_trim(devpath);
379			devpath = copy;
380		}
381	}
382	free(copy);
383
384	return (ENOENT);
385}
386
387static bool
388interactive_interrupt(const char *msg)
389{
390	time_t now, then, last;
391
392	last = 0;
393	now = then = getsecs();
394	printf("%s\n", msg);
395	if (fail_timeout == -2)		/* Always break to OK */
396		return (true);
397	if (fail_timeout == -1)		/* Never break to OK */
398		return (false);
399	do {
400		if (last != now) {
401			printf("press any key to interrupt reboot in %d seconds\r",
402			    fail_timeout - (int)(now - then));
403			last = now;
404		}
405
406		/* XXX no pause or timeout wait for char */
407		if (ischar())
408			return (true);
409		now = getsecs();
410	} while (now - then < fail_timeout);
411	return (false);
412}
413
414EFI_STATUS
415main(int argc, CHAR16 *argv[])
416{
417	char var[128];
418	EFI_GUID *guid;
419	int i, j, vargood, howto;
420	UINTN k;
421	int has_kbd;
422	char *s;
423	EFI_DEVICE_PATH *imgpath;
424	CHAR16 *text;
425	EFI_STATUS status;
426	UINT16 boot_current;
427	size_t sz;
428	UINT16 boot_order[100];
429#if !defined(__arm__)
430	char buf[40];
431#endif
432
433	archsw.arch_autoload = efi_autoload;
434	archsw.arch_getdev = efi_getdev;
435	archsw.arch_copyin = efi_copyin;
436	archsw.arch_copyout = efi_copyout;
437	archsw.arch_readin = efi_readin;
438#ifdef EFI_ZFS_BOOT
439	/* Note this needs to be set before ZFS init. */
440	archsw.arch_zfs_probe = efi_zfs_probe;
441#endif
442
443        /* Get our loaded image protocol interface structure. */
444	BS->HandleProtocol(IH, &imgid, (VOID**)&img);
445
446	/* Init the time source */
447	efi_time_init();
448
449	has_kbd = has_keyboard();
450
451	/*
452	 * XXX Chicken-and-egg problem; we want to have console output
453	 * early, but some console attributes may depend on reading from
454	 * eg. the boot device, which we can't do yet.  We can use
455	 * printf() etc. once this is done.
456	 */
457	cons_probe();
458
459	/*
460	 * Initialise the block cache. Set the upper limit.
461	 */
462	bcache_init(32768, 512);
463
464	/*
465	 * Parse the args to set the console settings, etc
466	 * boot1.efi passes these in, if it can read /boot.config or /boot/config
467	 * or iPXE may be setup to pass these in. Or the optional argument in the
468	 * boot environment was used to pass these arguments in (in which case
469	 * neither /boot.config nor /boot/config are consulted).
470	 *
471	 * Loop through the args, and for each one that contains an '=' that is
472	 * not the first character, add it to the environment.  This allows
473	 * loader and kernel env vars to be passed on the command line.  Convert
474	 * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this
475	 * method is flawed for non-ASCII characters).
476	 */
477	howto = 0;
478	for (i = 1; i < argc; i++) {
479		if (argv[i][0] == '-') {
480			for (j = 1; argv[i][j] != 0; j++) {
481				int ch;
482
483				ch = argv[i][j];
484				switch (ch) {
485				case 'a':
486					howto |= RB_ASKNAME;
487					break;
488				case 'd':
489					howto |= RB_KDB;
490					break;
491				case 'D':
492					howto |= RB_MULTIPLE;
493					break;
494				case 'h':
495					howto |= RB_SERIAL;
496					break;
497				case 'm':
498					howto |= RB_MUTE;
499					break;
500				case 'p':
501					howto |= RB_PAUSE;
502					break;
503				case 'P':
504					if (!has_kbd)
505						howto |= RB_SERIAL | RB_MULTIPLE;
506					break;
507				case 'r':
508					howto |= RB_DFLTROOT;
509					break;
510				case 's':
511					howto |= RB_SINGLE;
512					break;
513				case 'S':
514					if (argv[i][j + 1] == 0) {
515						if (i + 1 == argc) {
516							setenv("comconsole_speed", "115200", 1);
517						} else {
518							cpy16to8(&argv[i + 1][0], var,
519							    sizeof(var));
520							setenv("comconsole_speed", var, 1);
521						}
522						i++;
523						break;
524					} else {
525						cpy16to8(&argv[i][j + 1], var,
526						    sizeof(var));
527						setenv("comconsole_speed", var, 1);
528						break;
529					}
530				case 'v':
531					howto |= RB_VERBOSE;
532					break;
533				}
534			}
535		} else {
536			vargood = 0;
537			for (j = 0; argv[i][j] != 0; j++) {
538				if (j == sizeof(var)) {
539					vargood = 0;
540					break;
541				}
542				if (j > 0 && argv[i][j] == '=')
543					vargood = 1;
544				var[j] = (char)argv[i][j];
545			}
546			if (vargood) {
547				var[j] = 0;
548				putenv(var);
549			}
550		}
551	}
552	for (i = 0; howto_names[i].ev != NULL; i++)
553		if (howto & howto_names[i].mask)
554			setenv(howto_names[i].ev, "YES", 1);
555
556	/*
557	 * XXX we need fallback to this stuff after looking at the ConIn, ConOut and ConErr variables
558	 */
559	if (howto & RB_MULTIPLE) {
560		if (howto & RB_SERIAL)
561			setenv("console", "comconsole efi" , 1);
562		else
563			setenv("console", "efi comconsole" , 1);
564	} else if (howto & RB_SERIAL) {
565		setenv("console", "comconsole" , 1);
566	} else
567		setenv("console", "efi", 1);
568
569	if (efi_copy_init()) {
570		printf("failed to allocate staging area\n");
571		return (EFI_BUFFER_TOO_SMALL);
572	}
573
574	if ((s = getenv("fail_timeout")) != NULL)
575		fail_timeout = strtol(s, NULL, 10);
576
577	/*
578	 * Scan the BLOCK IO MEDIA handles then
579	 * march through the device switch probing for things.
580	 */
581	if ((i = efipart_inithandles()) == 0) {
582		for (i = 0; devsw[i] != NULL; i++)
583			if (devsw[i]->dv_init != NULL)
584				(devsw[i]->dv_init)();
585	} else
586		printf("efipart_inithandles failed %d, expect failures", i);
587
588	printf("Command line arguments:");
589	for (i = 0; i < argc; i++)
590		printf(" %S", argv[i]);
591	printf("\n");
592
593	printf("Image base: 0x%lx\n", (u_long)img->ImageBase);
594	printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16,
595	    ST->Hdr.Revision & 0xffff);
596	printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor,
597	    ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
598
599	printf("\n%s", bootprog_info);
600
601	/* Determine the devpath of our image so we can prefer it. */
602	text = efi_devpath_name(img->FilePath);
603	if (text != NULL) {
604		printf("   Load Path: %S\n", text);
605		efi_setenv_freebsd_wcs("LoaderPath", text);
606		efi_free_devpath_name(text);
607	}
608
609	status = BS->HandleProtocol(img->DeviceHandle, &devid, (void **)&imgpath);
610	if (status == EFI_SUCCESS) {
611		text = efi_devpath_name(imgpath);
612		if (text != NULL) {
613			printf("   Load Device: %S\n", text);
614			efi_setenv_freebsd_wcs("LoaderDev", text);
615			efi_free_devpath_name(text);
616		}
617	}
618
619	boot_current = 0;
620	sz = sizeof(boot_current);
621	efi_global_getenv("BootCurrent", &boot_current, &sz);
622	printf("   BootCurrent: %04x\n", boot_current);
623
624	sz = sizeof(boot_order);
625	efi_global_getenv("BootOrder", &boot_order, &sz);
626	printf("   BootOrder:");
627	for (i = 0; i < sz / sizeof(boot_order[0]); i++)
628		printf(" %04x%s", boot_order[i],
629		    boot_order[i] == boot_current ? "[*]" : "");
630	printf("\n");
631
632	/*
633	 * Disable the watchdog timer. By default the boot manager sets
634	 * the timer to 5 minutes before invoking a boot option. If we
635	 * want to return to the boot manager, we have to disable the
636	 * watchdog timer and since we're an interactive program, we don't
637	 * want to wait until the user types "quit". The timer may have
638	 * fired by then. We don't care if this fails. It does not prevent
639	 * normal functioning in any way...
640	 */
641	BS->SetWatchdogTimer(0, 0, 0, NULL);
642
643	/*
644	 * Try and find a good currdev based on the image that was booted.
645	 * It might be desirable here to have a short pause to allow falling
646	 * through to the boot loader instead of returning instantly to follow
647	 * the boot protocol and also allow an escape hatch for users wishing
648	 * to try something different.
649	 */
650	if (find_currdev(img) != 0)
651		if (!interactive_interrupt("Failed to find bootable partition"))
652			return (EFI_NOT_FOUND);
653
654	efi_init_environment();
655	setenv("LINES", "24", 1);	/* optional */
656
657	for (k = 0; k < ST->NumberOfTableEntries; k++) {
658		guid = &ST->ConfigurationTable[k].VendorGuid;
659#if !defined(__arm__)
660		if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) {
661			snprintf(buf, sizeof(buf), "%p",
662			    ST->ConfigurationTable[k].VendorTable);
663			setenv("hint.smbios.0.mem", buf, 1);
664			smbios_detect(ST->ConfigurationTable[k].VendorTable);
665			break;
666		}
667#endif
668	}
669
670	interact();			/* doesn't return */
671
672	return (EFI_SUCCESS);		/* keep compiler happy */
673}
674
675COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
676
677static int
678command_reboot(int argc, char *argv[])
679{
680	int i;
681
682	for (i = 0; devsw[i] != NULL; ++i)
683		if (devsw[i]->dv_cleanup != NULL)
684			(devsw[i]->dv_cleanup)();
685
686	RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
687
688	/* NOTREACHED */
689	return (CMD_ERROR);
690}
691
692COMMAND_SET(quit, "quit", "exit the loader", command_quit);
693
694static int
695command_quit(int argc, char *argv[])
696{
697	exit(0);
698	return (CMD_OK);
699}
700
701COMMAND_SET(memmap, "memmap", "print memory map", command_memmap);
702
703static int
704command_memmap(int argc, char *argv[])
705{
706	UINTN sz;
707	EFI_MEMORY_DESCRIPTOR *map, *p;
708	UINTN key, dsz;
709	UINT32 dver;
710	EFI_STATUS status;
711	int i, ndesc;
712	char line[80];
713	static char *types[] = {
714	    "Reserved",
715	    "LoaderCode",
716	    "LoaderData",
717	    "BootServicesCode",
718	    "BootServicesData",
719	    "RuntimeServicesCode",
720	    "RuntimeServicesData",
721	    "ConventionalMemory",
722	    "UnusableMemory",
723	    "ACPIReclaimMemory",
724	    "ACPIMemoryNVS",
725	    "MemoryMappedIO",
726	    "MemoryMappedIOPortSpace",
727	    "PalCode"
728	};
729
730	sz = 0;
731	status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver);
732	if (status != EFI_BUFFER_TOO_SMALL) {
733		printf("Can't determine memory map size\n");
734		return (CMD_ERROR);
735	}
736	map = malloc(sz);
737	status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver);
738	if (EFI_ERROR(status)) {
739		printf("Can't read memory map\n");
740		return (CMD_ERROR);
741	}
742
743	ndesc = sz / dsz;
744	snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n",
745	    "Type", "Physical", "Virtual", "#Pages", "Attr");
746	pager_open();
747	if (pager_output(line)) {
748		pager_close();
749		return (CMD_OK);
750	}
751
752	for (i = 0, p = map; i < ndesc;
753	     i++, p = NextMemoryDescriptor(p, dsz)) {
754		printf("%23s %012jx %012jx %08jx ", types[p->Type],
755		    (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart,
756		    (uintmax_t)p->NumberOfPages);
757		if (p->Attribute & EFI_MEMORY_UC)
758			printf("UC ");
759		if (p->Attribute & EFI_MEMORY_WC)
760			printf("WC ");
761		if (p->Attribute & EFI_MEMORY_WT)
762			printf("WT ");
763		if (p->Attribute & EFI_MEMORY_WB)
764			printf("WB ");
765		if (p->Attribute & EFI_MEMORY_UCE)
766			printf("UCE ");
767		if (p->Attribute & EFI_MEMORY_WP)
768			printf("WP ");
769		if (p->Attribute & EFI_MEMORY_RP)
770			printf("RP ");
771		if (p->Attribute & EFI_MEMORY_XP)
772			printf("XP ");
773		if (pager_output("\n"))
774			break;
775	}
776
777	pager_close();
778	return (CMD_OK);
779}
780
781COMMAND_SET(configuration, "configuration", "print configuration tables",
782    command_configuration);
783
784static const char *
785guid_to_string(EFI_GUID *guid)
786{
787	static char buf[40];
788
789	sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
790	    guid->Data1, guid->Data2, guid->Data3, guid->Data4[0],
791	    guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4],
792	    guid->Data4[5], guid->Data4[6], guid->Data4[7]);
793	return (buf);
794}
795
796static int
797command_configuration(int argc, char *argv[])
798{
799	char line[80];
800	UINTN i;
801
802	snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n",
803		(unsigned long)ST->NumberOfTableEntries);
804	pager_open();
805	if (pager_output(line)) {
806		pager_close();
807		return (CMD_OK);
808	}
809
810	for (i = 0; i < ST->NumberOfTableEntries; i++) {
811		EFI_GUID *guid;
812
813		printf("  ");
814		guid = &ST->ConfigurationTable[i].VendorGuid;
815		if (!memcmp(guid, &mps, sizeof(EFI_GUID)))
816			printf("MPS Table");
817		else if (!memcmp(guid, &acpi, sizeof(EFI_GUID)))
818			printf("ACPI Table");
819		else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID)))
820			printf("ACPI 2.0 Table");
821		else if (!memcmp(guid, &smbios, sizeof(EFI_GUID)))
822			printf("SMBIOS Table %p",
823			    ST->ConfigurationTable[i].VendorTable);
824		else if (!memcmp(guid, &smbios3, sizeof(EFI_GUID)))
825			printf("SMBIOS3 Table");
826		else if (!memcmp(guid, &dxe, sizeof(EFI_GUID)))
827			printf("DXE Table");
828		else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID)))
829			printf("HOB List Table");
830		else if (!memcmp(guid, &lzmadecomp, sizeof(EFI_GUID)))
831			printf("LZMA Compression");
832		else if (!memcmp(guid, &mpcore, sizeof(EFI_GUID)))
833			printf("ARM MpCore Information Table");
834		else if (!memcmp(guid, &esrt, sizeof(EFI_GUID)))
835			printf("ESRT Table");
836		else if (!memcmp(guid, &memtype, sizeof(EFI_GUID)))
837			printf("Memory Type Information Table");
838		else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID)))
839			printf("Debug Image Info Table");
840		else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID)))
841			printf("FDT Table");
842		else
843			printf("Unknown Table (%s)", guid_to_string(guid));
844		snprintf(line, sizeof(line), " at %p\n",
845		    ST->ConfigurationTable[i].VendorTable);
846		if (pager_output(line))
847			break;
848	}
849
850	pager_close();
851	return (CMD_OK);
852}
853
854
855COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode);
856
857static int
858command_mode(int argc, char *argv[])
859{
860	UINTN cols, rows;
861	unsigned int mode;
862	int i;
863	char *cp;
864	char rowenv[8];
865	EFI_STATUS status;
866	SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
867	extern void HO(void);
868
869	conout = ST->ConOut;
870
871	if (argc > 1) {
872		mode = strtol(argv[1], &cp, 0);
873		if (cp[0] != '\0') {
874			printf("Invalid mode\n");
875			return (CMD_ERROR);
876		}
877		status = conout->QueryMode(conout, mode, &cols, &rows);
878		if (EFI_ERROR(status)) {
879			printf("invalid mode %d\n", mode);
880			return (CMD_ERROR);
881		}
882		status = conout->SetMode(conout, mode);
883		if (EFI_ERROR(status)) {
884			printf("couldn't set mode %d\n", mode);
885			return (CMD_ERROR);
886		}
887		sprintf(rowenv, "%u", (unsigned)rows);
888		setenv("LINES", rowenv, 1);
889		HO();		/* set cursor */
890		return (CMD_OK);
891	}
892
893	printf("Current mode: %d\n", conout->Mode->Mode);
894	for (i = 0; i <= conout->Mode->MaxMode; i++) {
895		status = conout->QueryMode(conout, i, &cols, &rows);
896		if (EFI_ERROR(status))
897			continue;
898		printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols,
899		    (unsigned)rows);
900	}
901
902	if (i != 0)
903		printf("Select a mode with the command \"mode <number>\"\n");
904
905	return (CMD_OK);
906}
907
908#ifdef LOADER_FDT_SUPPORT
909extern int command_fdt_internal(int argc, char *argv[]);
910
911/*
912 * Since proper fdt command handling function is defined in fdt_loader_cmd.c,
913 * and declaring it as extern is in contradiction with COMMAND_SET() macro
914 * (which uses static pointer), we're defining wrapper function, which
915 * calls the proper fdt handling routine.
916 */
917static int
918command_fdt(int argc, char *argv[])
919{
920
921	return (command_fdt_internal(argc, argv));
922}
923
924COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
925#endif
926
927/*
928 * Chain load another efi loader.
929 */
930static int
931command_chain(int argc, char *argv[])
932{
933	EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
934	EFI_HANDLE loaderhandle;
935	EFI_LOADED_IMAGE *loaded_image;
936	EFI_STATUS status;
937	struct stat st;
938	struct devdesc *dev;
939	char *name, *path;
940	void *buf;
941	int fd;
942
943	if (argc < 2) {
944		command_errmsg = "wrong number of arguments";
945		return (CMD_ERROR);
946	}
947
948	name = argv[1];
949
950	if ((fd = open(name, O_RDONLY)) < 0) {
951		command_errmsg = "no such file";
952		return (CMD_ERROR);
953	}
954
955	if (fstat(fd, &st) < -1) {
956		command_errmsg = "stat failed";
957		close(fd);
958		return (CMD_ERROR);
959	}
960
961	status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
962	if (status != EFI_SUCCESS) {
963		command_errmsg = "failed to allocate buffer";
964		close(fd);
965		return (CMD_ERROR);
966	}
967	if (read(fd, buf, st.st_size) != st.st_size) {
968		command_errmsg = "error while reading the file";
969		(void)BS->FreePool(buf);
970		close(fd);
971		return (CMD_ERROR);
972	}
973	close(fd);
974	status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
975	(void)BS->FreePool(buf);
976	if (status != EFI_SUCCESS) {
977		command_errmsg = "LoadImage failed";
978		return (CMD_ERROR);
979	}
980	status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
981	    (void **)&loaded_image);
982
983	if (argc > 2) {
984		int i, len = 0;
985		CHAR16 *argp;
986
987		for (i = 2; i < argc; i++)
988			len += strlen(argv[i]) + 1;
989
990		len *= sizeof (*argp);
991		loaded_image->LoadOptions = argp = malloc (len);
992		loaded_image->LoadOptionsSize = len;
993		for (i = 2; i < argc; i++) {
994			char *ptr = argv[i];
995			while (*ptr)
996				*(argp++) = *(ptr++);
997			*(argp++) = ' ';
998		}
999		*(--argv) = 0;
1000	}
1001
1002	if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) {
1003#ifdef EFI_ZFS_BOOT
1004		struct zfs_devdesc *z_dev;
1005#endif
1006		struct disk_devdesc *d_dev;
1007		pdinfo_t *hd, *pd;
1008
1009		switch (dev->d_dev->dv_type) {
1010#ifdef EFI_ZFS_BOOT
1011		case DEVT_ZFS:
1012			z_dev = (struct zfs_devdesc *)dev;
1013			loaded_image->DeviceHandle =
1014			    efizfs_get_handle_by_guid(z_dev->pool_guid);
1015			break;
1016#endif
1017		case DEVT_NET:
1018			loaded_image->DeviceHandle =
1019			    efi_find_handle(dev->d_dev, dev->d_unit);
1020			break;
1021		default:
1022			hd = efiblk_get_pdinfo(dev);
1023			if (STAILQ_EMPTY(&hd->pd_part)) {
1024				loaded_image->DeviceHandle = hd->pd_handle;
1025				break;
1026			}
1027			d_dev = (struct disk_devdesc *)dev;
1028			STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
1029				/*
1030				 * d_partition should be 255
1031				 */
1032				if (pd->pd_unit == (uint32_t)d_dev->d_slice) {
1033					loaded_image->DeviceHandle =
1034					    pd->pd_handle;
1035					break;
1036				}
1037			}
1038			break;
1039		}
1040	}
1041
1042	dev_cleanup();
1043	status = BS->StartImage(loaderhandle, NULL, NULL);
1044	if (status != EFI_SUCCESS) {
1045		command_errmsg = "StartImage failed";
1046		free(loaded_image->LoadOptions);
1047		loaded_image->LoadOptions = NULL;
1048		status = BS->UnloadImage(loaded_image);
1049		return (CMD_ERROR);
1050	}
1051
1052	return (CMD_ERROR);	/* not reached */
1053}
1054
1055COMMAND_SET(chain, "chain", "chain load file", command_chain);
1056