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