2 * Copyright (c) 2008-2010 Rui Paulo
3 * Copyright (c) 2006 Marcel Moolenaar
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD: head/sys/boot/efi/loader/main.c 295408 2016-02-08 19:34:17Z imp $");
31 #include <sys/param.h>
32 #include <sys/reboot.h>
43 #include <bootstrap.h>
46 #include "loader_efi.h"
48 extern char bootprog_name
[];
49 extern char bootprog_rev
[];
50 extern char bootprog_date
[];
51 extern char bootprog_maker
[];
53 struct arch_switch archsw
; /* MI/MD interface boundary */
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
;
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_TEXT_INPUT_PROTOCOL
;
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.
75 print_str16(const CHAR16
*str
)
79 for (i
= 0; str
[i
]; i
++)
80 printf("%c", (char)str
[i
]);
84 * cpy8to16 copies a traditional C string into a CHAR16 string and
85 * 0 terminates it. len is the size of *dst in bytes.
88 cpy8to16(const char *src
, CHAR16
*dst
, size_t len
)
90 len
<<= 1; /* Assume CHAR16 is 2 bytes */
91 while (len
> 0 && *src
) {
99 cp16to8(const CHAR16
*src
, char *dst
, size_t len
)
103 for (i
= 0; i
< len
&& src
[i
]; i
++)
104 dst
[i
] = (char)src
[i
];
111 EFI_DEVICE_PATH
*path
;
112 EFI_HANDLE
*hin
, *hin_end
, *walker
;
117 * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and
118 * do the typical dance to get the right sized buffer.
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
,
127 if (EFI_ERROR(status
))
130 if (EFI_ERROR(status
))
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.
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
))
145 while (!IsDevicePathEnd(path
)) {
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.
153 if (DevicePathType(path
) == ACPI_DEVICE_PATH
&&
154 (DevicePathSubType(path
) == ACPI_DP
||
155 DevicePathSubType(path
) == ACPI_EXTENDED_DP
)) {
156 ACPI_HID_DEVICE_PATH
*acpi
;
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
) {
165 * Check for USB keyboard node, if present. Unlike a
166 * PS/2 keyboard, these definitely only appear when
167 * connected to the system.
169 } else if (DevicePathType(path
) == MESSAGING_DEVICE_PATH
&&
170 DevicePathSubType(path
) == MSG_USB_CLASS_DP
) {
171 USB_CLASS_DEVICE_PATH
*usb
;
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 */
181 path
= NextDevicePathNode(path
);
190 main(int argc
, CHAR16
*argv
[])
193 EFI_LOADED_IMAGE
*img
;
195 int i
, j
, vargood
, unit
, howto
;
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
;
207 has_kbd
= has_keyboard();
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.
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.
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.
228 for (i
= 1; i
< argc
; i
++) {
229 if (argv
[i
][0] == '-') {
230 for (j
= 1; argv
[i
][j
] != 0; j
++) {
252 howto
&= ~(RB_MUTE
|RB_VIDEO
);
257 howto
|= RB_DFLTROOT
;
263 if (argv
[i
][j
+ 1] == 0) {
265 setenv("comconsole_speed", "115200", 1);
267 cp16to8(&argv
[i
+ 1][0], var
,
269 setenv("comconsole_speedspeed", var
, 1);
274 cp16to8(&argv
[i
][j
+ 1], var
,
276 setenv("comconsole_speed", var
, 1);
286 for (j
= 0; argv
[i
][j
] != 0; j
++) {
287 if (j
== sizeof(var
)) {
291 if (j
> 0 && argv
[i
][j
] == '=')
293 var
[j
] = (char)argv
[i
][j
];
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);
308 if (efi_copy_init()) {
309 printf("failed to allocate staging area\n");
310 return (EFI_BUFFER_TOO_SMALL
);
314 * March through the device switch probing for things.
316 for (i
= 0; devsw
[i
] != NULL
; i
++)
317 if (devsw
[i
]->dv_init
!= NULL
)
318 (devsw
[i
]->dv_init
)();
320 /* Get our loaded image protocol interface structure. */
321 BS
->HandleProtocol(IH
, &imgid
, (VOID
**)&img
);
323 printf("Command line arguments:");
324 for (i
= 0; i
< argc
; i
++) {
326 print_str16(argv
[i
]);
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);
340 printf("%s, Revision %s\n", bootprog_name
, bootprog_rev
);
341 printf("(%s, %s)\n", bootprog_maker
, bootprog_date
);
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...
352 BS
->SetWatchdogTimer(0, 0, 0, NULL
);
354 if (efi_handle_lookup(img
->DeviceHandle
, &dev
, &unit
, &pool_guid
) != 0)
355 return (EFI_NOT_FOUND
);
357 switch (dev
->dv_type
) {
359 struct efi_devdesc currdev
;
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
,
374 setenv("ehci_load", "YES", 1);
377 setenv("xhci_load", "YES", 1);
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);
385 setenv("LINES", "24", 1); /* optional */
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
);
395 interact(); /* doesn't return */
397 return (EFI_SUCCESS
); /* keep compiler happy */
400 /* XXX move to lib stand ? */
402 wcscmp(CHAR16
*a
, CHAR16
*b
)
405 while (*a
&& *b
&& *a
== *b
) {
413 COMMAND_SET(reboot
, "reboot", "reboot the system", command_reboot
);
416 command_reboot(int argc
, char *argv
[])
420 for (i
= 0; devsw
[i
] != NULL
; ++i
)
421 if (devsw
[i
]->dv_cleanup
!= NULL
)
422 (devsw
[i
]->dv_cleanup
)();
424 RS
->ResetSystem(EfiResetCold
, EFI_SUCCESS
, 23,
425 (CHAR16
*)"Reboot from the loader");
431 COMMAND_SET(quit
, "quit", "exit the loader", command_quit
);
434 command_quit(int argc
, char *argv
[])
440 COMMAND_SET(memmap
, "memmap", "print memory map", command_memmap
);
443 command_memmap(int argc
, char *argv
[])
446 EFI_MEMORY_DESCRIPTOR
*map
, *p
;
451 static char *types
[] = {
457 "RuntimeServicesCode",
458 "RuntimeServicesData",
459 "ConventionalMemory",
464 "MemoryMappedIOPortSpace",
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");
475 status
= BS
->GetMemoryMap(&sz
, map
, &key
, &dsz
, &dver
);
476 if (EFI_ERROR(status
)) {
477 printf("Can't read memory map\n");
482 printf("%23s %12s %12s %8s %4s\n",
483 "Type", "Physical", "Virtual", "#Pages", "Attr");
485 for (i
= 0, p
= map
; i
< ndesc
;
486 i
++, p
= NextMemoryDescriptor(p
, dsz
)) {
487 printf("%23s %012jx %012jx %08jx ", types
[p
->Type
],
488 (uintmax_t)p
->PhysicalStart
, (uintmax_t)p
->VirtualStart
,
489 (uintmax_t)p
->NumberOfPages
);
490 if (p
->Attribute
& EFI_MEMORY_UC
)
492 if (p
->Attribute
& EFI_MEMORY_WC
)
494 if (p
->Attribute
& EFI_MEMORY_WT
)
496 if (p
->Attribute
& EFI_MEMORY_WB
)
498 if (p
->Attribute
& EFI_MEMORY_UCE
)
500 if (p
->Attribute
& EFI_MEMORY_WP
)
502 if (p
->Attribute
& EFI_MEMORY_RP
)
504 if (p
->Attribute
& EFI_MEMORY_XP
)
512 COMMAND_SET(configuration
, "configuration", "print configuration tables",
513 command_configuration
);
516 guid_to_string(EFI_GUID
*guid
)
520 sprintf(buf
, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
521 guid
->Data1
, guid
->Data2
, guid
->Data3
, guid
->Data4
[0],
522 guid
->Data4
[1], guid
->Data4
[2], guid
->Data4
[3], guid
->Data4
[4],
523 guid
->Data4
[5], guid
->Data4
[6], guid
->Data4
[7]);
528 command_configuration(int argc
, char *argv
[])
532 printf("NumberOfTableEntries=%lu\n",
533 (unsigned long)ST
->NumberOfTableEntries
);
534 for (i
= 0; i
< ST
->NumberOfTableEntries
; i
++) {
538 guid
= &ST
->ConfigurationTable
[i
].VendorGuid
;
539 if (!memcmp(guid
, &mps
, sizeof(EFI_GUID
)))
541 else if (!memcmp(guid
, &acpi
, sizeof(EFI_GUID
)))
542 printf("ACPI Table");
543 else if (!memcmp(guid
, &acpi20
, sizeof(EFI_GUID
)))
544 printf("ACPI 2.0 Table");
545 else if (!memcmp(guid
, &smbios
, sizeof(EFI_GUID
)))
546 printf("SMBIOS Table");
547 else if (!memcmp(guid
, &dxe
, sizeof(EFI_GUID
)))
549 else if (!memcmp(guid
, &hoblist
, sizeof(EFI_GUID
)))
550 printf("HOB List Table");
551 else if (!memcmp(guid
, &memtype
, sizeof(EFI_GUID
)))
552 printf("Memory Type Information Table");
553 else if (!memcmp(guid
, &debugimg
, sizeof(EFI_GUID
)))
554 printf("Debug Image Info Table");
555 else if (!memcmp(guid
, &fdtdtb
, sizeof(EFI_GUID
)))
558 printf("Unknown Table (%s)", guid_to_string(guid
));
559 printf(" at %p\n", ST
->ConfigurationTable
[i
].VendorTable
);
566 COMMAND_SET(mode
, "mode", "change or display EFI text modes", command_mode
);
569 command_mode(int argc
, char *argv
[])
577 SIMPLE_TEXT_OUTPUT_INTERFACE
*conout
;
578 extern void HO(void);
583 mode
= strtol(argv
[1], &cp
, 0);
585 printf("Invalid mode\n");
588 status
= conout
->QueryMode(conout
, mode
, &cols
, &rows
);
589 if (EFI_ERROR(status
)) {
590 printf("invalid mode %d\n", mode
);
593 status
= conout
->SetMode(conout
, mode
);
594 if (EFI_ERROR(status
)) {
595 printf("couldn't set mode %d\n", mode
);
598 sprintf(rowenv
, "%u", (unsigned)rows
);
599 setenv("LINES", rowenv
, 1);
600 HO(); /* set cursor */
604 printf("Current mode: %d\n", conout
->Mode
->Mode
);
605 for (i
= 0; i
<= conout
->Mode
->MaxMode
; i
++) {
606 status
= conout
->QueryMode(conout
, i
, &cols
, &rows
);
607 if (EFI_ERROR(status
))
609 printf("Mode %d: %u columns, %u rows\n", i
, (unsigned)cols
,
614 printf("Select a mode with the command \"mode <number>\"\n");
619 COMMAND_SET(efishow
, "efi-show", "print some or all EFI variables", command_efi_show
);
622 efi_print_var(CHAR16
*varnamearg
, EFI_GUID
*matchguid
, int lflag
)
629 uint32_t uuid_status
;
633 status
= RS
->GetVariable(varnamearg
, matchguid
, &attr
,
635 if (status
!= EFI_BUFFER_TOO_SMALL
) {
636 printf("Can't get the variable: error %#lx\n", status
);
639 data
= malloc(datasz
);
640 status
= RS
->GetVariable(varnamearg
, matchguid
, &attr
,
642 if (status
!= EFI_SUCCESS
) {
643 printf("Can't get the variable: error %#lx\n", status
);
646 uuid_to_string((uuid_t
*)matchguid
, &str
, &uuid_status
);
648 printf("%s 0x%x %S", str
, attr
, varnamearg
);
650 printf("%s 0x%x %S=", str
, attr
, varnamearg
);
654 for (i
= 0; i
< datasz
- 1; i
++) {
655 /* Quick hack to see if this ascii-ish string printable range plus tab, cr and lf */
656 if ((str
[i
] < 32 || str
[i
] > 126) && str
[i
] != 9 && str
[i
] != 10 && str
[i
] != 13) {
661 if (str
[datasz
- 1] != '\0')
666 for (i
= 0; i
< datasz
/ 2; i
++) {
667 if (isalnum(data
[i
]) || isspace(data
[i
]))
668 printf("%c", data
[i
]);
670 printf("\\x%02x", data
[i
]);
680 command_efi_show(int argc
, char *argv
[])
686 * print all the env vars tagged with UUID
688 * search all the env vars and print the ones matching var
689 * eif-show -u UUID -v var
691 * print all the env vars that match UUID and var
693 /* NB: We assume EFI_GUID is the same as uuid_t */
694 int aflag
= 0, gflag
= 0, lflag
= 0, vflag
= 0;
698 EFI_GUID varguid
= { 0,0,0,{0,0,0,0,0,0,0,0} };
699 EFI_GUID matchguid
= { 0,0,0,{0,0,0,0,0,0,0,0} };
700 uint32_t uuid_status
;
703 CHAR16 varnamearg
[128];
707 while ((ch
= getopt(argc
, argv
, "ag:lv:")) != -1) {
714 uuid_from_string(optarg
, (uuid_t
*)&matchguid
,
716 if (uuid_status
!= uuid_s_ok
) {
717 printf("uid %s could not be parsed\n", optarg
);
726 if (strlen(optarg
) >= nitems(varnamearg
)) {
727 printf("Variable %s is longer than %zd characters\n",
728 optarg
, nitems(varnamearg
));
731 for (i
= 0; i
< strlen(optarg
); i
++)
732 varnamearg
[i
] = optarg
[i
];
736 printf("Invalid argument %c\n", ch
);
741 if (aflag
&& (gflag
|| vflag
)) {
742 printf("-a isn't compatible with -v or -u\n");
746 if (aflag
&& optind
< argc
) {
747 printf("-a doesn't take any args");
758 if (vflag
&& gflag
) {
759 rv
= efi_print_var(varnamearg
, &matchguid
, lflag
);
766 if (strlen(optarg
) >= nitems(varnamearg
)) {
767 printf("Variable %s is longer than %zd characters\n",
768 optarg
, nitems(varnamearg
));
772 for (i
= 0; i
< strlen(optarg
); i
++)
773 varnamearg
[i
] = optarg
[i
];
776 uuid_from_string(optarg
, (uuid_t
*)&matchguid
,
778 if (uuid_status
!= uuid_s_ok
) {
779 printf("uid %s could not be parsed\n", optarg
);
783 rv
= efi_print_var(varnamearg
, &matchguid
, lflag
);
789 printf("Too many args %d\n", argc
);
795 * Initiate the search -- note the standard takes pain
796 * to specify the initial call must be a poiner to a NULL
800 varname
= malloc(varalloc
);
801 if (varname
== NULL
) {
802 printf("Can't allocate memory to get variables\n");
809 status
= RS
->GetNextVariableName(&varsz
, varname
, &varguid
);
810 if (status
== EFI_BUFFER_TOO_SMALL
) {
812 newnm
= malloc(varalloc
);
814 printf("Can't allocate memory to get variables\n");
819 memcpy(newnm
, varname
, varsz
);
822 continue; /* Try again with bigger buffer */
824 if (status
!= EFI_SUCCESS
)
827 if (efi_print_var(varname
, &varguid
, lflag
) != CMD_OK
)
832 if (wcscmp(varnamearg
, varname
) == 0) {
833 if (efi_print_var(varname
, &varguid
, lflag
) != CMD_OK
)
839 if (memcmp(&varguid
, &matchguid
, sizeof(varguid
)) == 0) {
840 if (efi_print_var(varname
, &varguid
, lflag
) != CMD_OK
)
852 COMMAND_SET(efiset
, "efi-set", "set EFI variables", command_efi_set
);
855 command_efi_set(int argc
, char *argv
[])
857 char *uuid
, *var
, *val
;
864 printf("efi-set uuid var new-value\n");
870 uuid_from_string(uuid
, (uuid_t
*)&guid
, &status
);
871 if (status
!= uuid_s_ok
) {
872 printf("Invalid uuid %s %d\n", uuid
, status
);
875 cpy8to16(var
, wvar
, sizeof(wvar
));
876 err
= RS
->SetVariable(wvar
, &guid
,
877 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
878 strlen(val
) + 1, val
);
879 if (EFI_ERROR(err
)) {
880 printf("Failed to set variable: error %lu\n", EFI_ERROR_CODE(err
));
886 COMMAND_SET(efiunset
, "efi-unset", "delete / unset EFI variables", command_efi_unset
);
889 command_efi_unset(int argc
, char *argv
[])
898 printf("efi-unset uuid var\n");
903 uuid_from_string(uuid
, (uuid_t
*)&guid
, &status
);
904 if (status
!= uuid_s_ok
) {
905 printf("Invalid uuid %s\n", uuid
);
908 cpy8to16(var
, wvar
, sizeof(wvar
));
909 err
= RS
->SetVariable(wvar
, &guid
, 0, 0, NULL
);
910 if (EFI_ERROR(err
)) {
911 printf("Failed to unset variable: error %lu\n", EFI_ERROR_CODE(err
));