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