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