1300081Simp/*
2300081Simp * Copyright (c) 2015 Netflix, Inc. All Rights Reserved.
3300081Simp *
4300081Simp * Redistribution and use in source and binary forms, with or without
5300081Simp * modification, are permitted provided that the following conditions
6300081Simp * are met:
7300081Simp * 1. Redistributions of source code must retain the above copyright
8300081Simp *    notice, this list of conditions and the following disclaimer.
9300081Simp * 2. Redistributions in binary form must reproduce the above copyright
10300081Simp *    notice, this list of conditions and the following disclaimer in the
11300081Simp *    documentation and/or other materials provided with the distribution.
12300081Simp *
13300081Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14300081Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15300081Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16300081Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17300081Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18300081Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19300081Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20300081Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21300081Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22300081Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23300081Simp * SUCH DAMAGE.
24300081Simp */
25300081Simp
26300081Simp#include <sys/cdefs.h>
27300081Simp__FBSDID("$FreeBSD: stable/11/stand/efi/libefi/env.c 346482 2019-04-21 04:26:02Z kevans $");
28300081Simp
29329011Skevans#include <stand.h>
30329011Skevans#include <string.h>
31300081Simp#include <efi.h>
32346482Skevans#include <efichar.h>
33300081Simp#include <efilib.h>
34346482Skevans#include <efigpt.h>	/* Partition GUIDS */
35346482Skevans#include <Guid/MemoryTypeInformation.h>
36346482Skevans#include <Guid/MtcVendor.h>
37346482Skevans#include <Guid/ZeroGuid.h>
38346482Skevans#include <Protocol/EdidActive.h>
39346482Skevans#include <Protocol/EdidDiscovered.h>
40329011Skevans#include <uuid.h>
41329099Skevans#include <stdbool.h>
42346482Skevans#include <sys/param.h>
43329011Skevans#include "bootstrap.h"
44300081Simp
45346482Skevans/*
46346482Skevans * About ENABLE_UPDATES
47346482Skevans *
48346482Skevans * The UEFI variables are identified only by GUID and name, there is no
49346482Skevans * way to (auto)detect the type for the value, so we need to process the
50346482Skevans * variables case by case, as we do learn about them.
51346482Skevans *
52346482Skevans * While showing the variable name and the value is safe, we must not store
53346482Skevans * random values nor allow removing (random) variables.
54346482Skevans *
55346482Skevans * Since we do have stub code to set/unset the variables, I do want to keep
56346482Skevans * it to make the future development a bit easier, but the updates are disabled
57346482Skevans * by default till:
58346482Skevans *	a) the validation and data translation to values is properly implemented
59346482Skevans *	b) We have established which variables we do allow to be updated.
60346482Skevans * Therefore the set/unset code is included only for developers aid.
61346482Skevans */
62346482Skevans
63346482Skevansstatic struct efi_uuid_mapping {
64346482Skevans	const char *efi_guid_name;
65346482Skevans	EFI_GUID efi_guid;
66346482Skevans} efi_uuid_mapping[] = {
67346482Skevans	{ .efi_guid_name = "global", .efi_guid = EFI_GLOBAL_VARIABLE },
68346482Skevans	{ .efi_guid_name = "freebsd", .efi_guid = FREEBSD_BOOT_VAR_GUID },
69346482Skevans	/* EFI Systab entry names. */
70346482Skevans	{ .efi_guid_name = "MPS Table", .efi_guid = MPS_TABLE_GUID },
71346482Skevans	{ .efi_guid_name = "ACPI Table", .efi_guid = ACPI_TABLE_GUID },
72346482Skevans	{ .efi_guid_name = "ACPI 2.0 Table", .efi_guid = ACPI_20_TABLE_GUID },
73346482Skevans	{ .efi_guid_name = "SMBIOS Table", .efi_guid = SMBIOS_TABLE_GUID },
74346482Skevans	{ .efi_guid_name = "SMBIOS3 Table", .efi_guid = SMBIOS3_TABLE_GUID },
75346482Skevans	{ .efi_guid_name = "DXE Table", .efi_guid = DXE_SERVICES_TABLE_GUID },
76346482Skevans	{ .efi_guid_name = "HOB List Table", .efi_guid = HOB_LIST_TABLE_GUID },
77346482Skevans	{ .efi_guid_name = EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
78346482Skevans	    .efi_guid = EFI_MEMORY_TYPE_INFORMATION_GUID },
79346482Skevans	{ .efi_guid_name = "Debug Image Info Table",
80346482Skevans	    .efi_guid = DEBUG_IMAGE_INFO_TABLE_GUID },
81346482Skevans	{ .efi_guid_name = "FDT Table", .efi_guid = FDT_TABLE_GUID },
82346482Skevans	/*
83346482Skevans	 * Protocol names for debug purposes.
84346482Skevans	 * Can be removed along with lsefi command.
85346482Skevans	 */
86346482Skevans	{ .efi_guid_name = "device path", .efi_guid = DEVICE_PATH_PROTOCOL },
87346482Skevans	{ .efi_guid_name = "block io", .efi_guid = BLOCK_IO_PROTOCOL },
88346482Skevans	{ .efi_guid_name = "disk io", .efi_guid = DISK_IO_PROTOCOL },
89346482Skevans	{ .efi_guid_name = "disk info", .efi_guid =
90346482Skevans	    EFI_DISK_INFO_PROTOCOL_GUID },
91346482Skevans	{ .efi_guid_name = "simple fs",
92346482Skevans	    .efi_guid = SIMPLE_FILE_SYSTEM_PROTOCOL },
93346482Skevans	{ .efi_guid_name = "load file", .efi_guid = LOAD_FILE_PROTOCOL },
94346482Skevans	{ .efi_guid_name = "device io", .efi_guid = DEVICE_IO_PROTOCOL },
95346482Skevans	{ .efi_guid_name = "unicode collation",
96346482Skevans	    .efi_guid = UNICODE_COLLATION_PROTOCOL },
97346482Skevans	{ .efi_guid_name = "unicode collation2",
98346482Skevans	    .efi_guid = EFI_UNICODE_COLLATION2_PROTOCOL_GUID },
99346482Skevans	{ .efi_guid_name = "simple network",
100346482Skevans	    .efi_guid = EFI_SIMPLE_NETWORK_PROTOCOL },
101346482Skevans	{ .efi_guid_name = "simple text output",
102346482Skevans	    .efi_guid = SIMPLE_TEXT_OUTPUT_PROTOCOL },
103346482Skevans	{ .efi_guid_name = "simple text input",
104346482Skevans	    .efi_guid = SIMPLE_TEXT_INPUT_PROTOCOL },
105346482Skevans	{ .efi_guid_name = "simple text ex input",
106346482Skevans	    .efi_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID },
107346482Skevans	{ .efi_guid_name = "console control",
108346482Skevans	    .efi_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID },
109346482Skevans	{ .efi_guid_name = "stdin", .efi_guid = EFI_CONSOLE_IN_DEVICE_GUID },
110346482Skevans	{ .efi_guid_name = "stdout", .efi_guid = EFI_CONSOLE_OUT_DEVICE_GUID },
111346482Skevans	{ .efi_guid_name = "stderr",
112346482Skevans	    .efi_guid = EFI_STANDARD_ERROR_DEVICE_GUID },
113346482Skevans	{ .efi_guid_name = "GOP",
114346482Skevans	    .efi_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID },
115346482Skevans	{ .efi_guid_name = "UGA draw", .efi_guid = EFI_UGA_DRAW_PROTOCOL_GUID },
116346482Skevans	{ .efi_guid_name = "PXE base code",
117346482Skevans	    .efi_guid = EFI_PXE_BASE_CODE_PROTOCOL },
118346482Skevans	{ .efi_guid_name = "PXE base code callback",
119346482Skevans	    .efi_guid = EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL },
120346482Skevans	{ .efi_guid_name = "serial io", .efi_guid = SERIAL_IO_PROTOCOL },
121346482Skevans	{ .efi_guid_name = "loaded image", .efi_guid = LOADED_IMAGE_PROTOCOL },
122346482Skevans	{ .efi_guid_name = "loaded image device path",
123346482Skevans	    .efi_guid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID },
124346482Skevans	{ .efi_guid_name = "ISA io", .efi_guid = EFI_ISA_IO_PROTOCOL_GUID },
125346482Skevans	{ .efi_guid_name = "IDE controller init",
126346482Skevans	    .efi_guid = EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID },
127346482Skevans	{ .efi_guid_name = "ISA ACPI", .efi_guid = EFI_ISA_ACPI_PROTOCOL_GUID },
128346482Skevans	{ .efi_guid_name = "PCI", .efi_guid = EFI_PCI_IO_PROTOCOL_GUID },
129346482Skevans	{ .efi_guid_name = "PCI root", .efi_guid = EFI_PCI_ROOT_IO_GUID },
130346482Skevans	{ .efi_guid_name = "PCI enumeration",
131346482Skevans	    .efi_guid = EFI_PCI_ENUMERATION_COMPLETE_GUID },
132346482Skevans        { .efi_guid_name = "Driver diagnostics",
133346482Skevans	    .efi_guid = EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID },
134346482Skevans        { .efi_guid_name = "Driver diagnostics2",
135346482Skevans	    .efi_guid = EFI_DRIVER_DIAGNOSTICS2_PROTOCOL_GUID },
136346482Skevans        { .efi_guid_name = "simple pointer",
137346482Skevans	    .efi_guid = EFI_SIMPLE_POINTER_PROTOCOL_GUID },
138346482Skevans        { .efi_guid_name = "absolute pointer",
139346482Skevans	    .efi_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID },
140346482Skevans        { .efi_guid_name = "VLAN config",
141346482Skevans	    .efi_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID },
142346482Skevans        { .efi_guid_name = "ARP service binding",
143346482Skevans	    .efi_guid = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID },
144346482Skevans        { .efi_guid_name = "ARP", .efi_guid = EFI_ARP_PROTOCOL_GUID },
145346482Skevans        { .efi_guid_name = "IPv4 service binding",
146346482Skevans	    .efi_guid = EFI_IP4_SERVICE_BINDING_PROTOCOL },
147346482Skevans        { .efi_guid_name = "IPv4", .efi_guid = EFI_IP4_PROTOCOL },
148346482Skevans        { .efi_guid_name = "IPv4 config",
149346482Skevans	    .efi_guid = EFI_IP4_CONFIG_PROTOCOL_GUID },
150346482Skevans        { .efi_guid_name = "IPv6 service binding",
151346482Skevans	    .efi_guid = EFI_IP6_SERVICE_BINDING_PROTOCOL },
152346482Skevans        { .efi_guid_name = "IPv6", .efi_guid = EFI_IP6_PROTOCOL },
153346482Skevans        { .efi_guid_name = "IPv6 config",
154346482Skevans	    .efi_guid = EFI_IP6_CONFIG_PROTOCOL_GUID },
155346482Skevans        { .efi_guid_name = "UDPv4", .efi_guid = EFI_UDP4_PROTOCOL },
156346482Skevans        { .efi_guid_name = "UDPv4 service binding",
157346482Skevans	    .efi_guid = EFI_UDP4_SERVICE_BINDING_PROTOCOL },
158346482Skevans        { .efi_guid_name = "UDPv6", .efi_guid = EFI_UDP6_PROTOCOL },
159346482Skevans        { .efi_guid_name = "UDPv6 service binding",
160346482Skevans	    .efi_guid = EFI_UDP6_SERVICE_BINDING_PROTOCOL },
161346482Skevans        { .efi_guid_name = "TCPv4", .efi_guid = EFI_TCP4_PROTOCOL },
162346482Skevans        { .efi_guid_name = "TCPv4 service binding",
163346482Skevans	    .efi_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL },
164346482Skevans        { .efi_guid_name = "TCPv6", .efi_guid = EFI_TCP6_PROTOCOL },
165346482Skevans        { .efi_guid_name = "TCPv6 service binding",
166346482Skevans	    .efi_guid = EFI_TCP6_SERVICE_BINDING_PROTOCOL },
167346482Skevans        { .efi_guid_name = "EFI System partition",
168346482Skevans	    .efi_guid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID },
169346482Skevans        { .efi_guid_name = "MBR legacy",
170346482Skevans	    .efi_guid = EFI_PART_TYPE_LEGACY_MBR_GUID },
171346482Skevans        { .efi_guid_name = "device tree", .efi_guid = EFI_DEVICE_TREE_GUID },
172346482Skevans        { .efi_guid_name = "USB io", .efi_guid = EFI_USB_IO_PROTOCOL_GUID },
173346482Skevans        { .efi_guid_name = "USB2 HC", .efi_guid = EFI_USB2_HC_PROTOCOL_GUID },
174346482Skevans        { .efi_guid_name = "component name",
175346482Skevans	    .efi_guid = EFI_COMPONENT_NAME_PROTOCOL_GUID },
176346482Skevans        { .efi_guid_name = "component name2",
177346482Skevans	    .efi_guid = EFI_COMPONENT_NAME2_PROTOCOL_GUID },
178346482Skevans        { .efi_guid_name = "driver binding",
179346482Skevans	    .efi_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID },
180346482Skevans        { .efi_guid_name = "driver configuration",
181346482Skevans	    .efi_guid = EFI_DRIVER_CONFIGURATION_PROTOCOL_GUID },
182346482Skevans        { .efi_guid_name = "driver configuration2",
183346482Skevans	    .efi_guid = EFI_DRIVER_CONFIGURATION2_PROTOCOL_GUID },
184346482Skevans        { .efi_guid_name = "decompress",
185346482Skevans	    .efi_guid = EFI_DECOMPRESS_PROTOCOL_GUID },
186346482Skevans        { .efi_guid_name = "ebc interpreter",
187346482Skevans	    .efi_guid = EFI_EBC_INTERPRETER_PROTOCOL_GUID },
188346482Skevans        { .efi_guid_name = "network interface identifier",
189346482Skevans	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL },
190346482Skevans        { .efi_guid_name = "network interface identifier_31",
191346482Skevans	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31 },
192346482Skevans        { .efi_guid_name = "managed network service binding",
193346482Skevans	    .efi_guid = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID },
194346482Skevans        { .efi_guid_name = "managed network",
195346482Skevans	    .efi_guid = EFI_MANAGED_NETWORK_PROTOCOL_GUID },
196346482Skevans        { .efi_guid_name = "form browser",
197346482Skevans	    .efi_guid = EFI_FORM_BROWSER2_PROTOCOL_GUID },
198346482Skevans        { .efi_guid_name = "HII config routing",
199346482Skevans	    .efi_guid = EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID },
200346482Skevans        { .efi_guid_name = "HII database",
201346482Skevans	    .efi_guid = EFI_HII_DATABASE_PROTOCOL_GUID },
202346482Skevans        { .efi_guid_name = "HII string",
203346482Skevans	    .efi_guid = EFI_HII_STRING_PROTOCOL_GUID },
204346482Skevans        { .efi_guid_name = "HII image",
205346482Skevans	    .efi_guid = EFI_HII_IMAGE_PROTOCOL_GUID },
206346482Skevans        { .efi_guid_name = "HII font", .efi_guid = EFI_HII_FONT_PROTOCOL_GUID },
207346482Skevans        { .efi_guid_name = "HII config",
208346482Skevans	    .efi_guid = EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID },
209346482Skevans        { .efi_guid_name = "MTFTP4 service binding",
210346482Skevans	    .efi_guid = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID },
211346482Skevans        { .efi_guid_name = "MTFTP4", .efi_guid = EFI_MTFTP4_PROTOCOL_GUID },
212346482Skevans        { .efi_guid_name = "MTFTP6 service binding",
213346482Skevans	    .efi_guid = EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID },
214346482Skevans        { .efi_guid_name = "MTFTP6", .efi_guid = EFI_MTFTP6_PROTOCOL_GUID },
215346482Skevans        { .efi_guid_name = "DHCP4 service binding",
216346482Skevans	    .efi_guid = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID },
217346482Skevans        { .efi_guid_name = "DHCP4", .efi_guid = EFI_DHCP4_PROTOCOL_GUID },
218346482Skevans        { .efi_guid_name = "DHCP6 service binding",
219346482Skevans	    .efi_guid = EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID },
220346482Skevans        { .efi_guid_name = "DHCP6", .efi_guid = EFI_DHCP6_PROTOCOL_GUID },
221346482Skevans        { .efi_guid_name = "SCSI io", .efi_guid = EFI_SCSI_IO_PROTOCOL_GUID },
222346482Skevans        { .efi_guid_name = "SCSI pass thru",
223346482Skevans	    .efi_guid = EFI_SCSI_PASS_THRU_PROTOCOL_GUID },
224346482Skevans        { .efi_guid_name = "SCSI pass thru ext",
225346482Skevans	    .efi_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID },
226346482Skevans        { .efi_guid_name = "Capsule arch",
227346482Skevans	    .efi_guid = EFI_CAPSULE_ARCH_PROTOCOL_GUID },
228346482Skevans        { .efi_guid_name = "monotonic counter arch",
229346482Skevans	    .efi_guid = EFI_MONOTONIC_COUNTER_ARCH_PROTOCOL_GUID },
230346482Skevans        { .efi_guid_name = "realtime clock arch",
231346482Skevans	    .efi_guid = EFI_REALTIME_CLOCK_ARCH_PROTOCOL_GUID },
232346482Skevans        { .efi_guid_name = "variable arch",
233346482Skevans	    .efi_guid = EFI_VARIABLE_ARCH_PROTOCOL_GUID },
234346482Skevans        { .efi_guid_name = "variable write arch",
235346482Skevans	    .efi_guid = EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID },
236346482Skevans        { .efi_guid_name = "watchdog timer arch",
237346482Skevans	    .efi_guid = EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID },
238346482Skevans        { .efi_guid_name = "ACPI support",
239346482Skevans	    .efi_guid = EFI_ACPI_SUPPORT_PROTOCOL_GUID },
240346482Skevans        { .efi_guid_name = "BDS arch", .efi_guid = EFI_BDS_ARCH_PROTOCOL_GUID },
241346482Skevans        { .efi_guid_name = "metronome arch",
242346482Skevans	    .efi_guid = EFI_METRONOME_ARCH_PROTOCOL_GUID },
243346482Skevans        { .efi_guid_name = "timer arch",
244346482Skevans	    .efi_guid = EFI_TIMER_ARCH_PROTOCOL_GUID },
245346482Skevans        { .efi_guid_name = "DPC", .efi_guid = EFI_DPC_PROTOCOL_GUID },
246346482Skevans        { .efi_guid_name = "print2", .efi_guid = EFI_PRINT2_PROTOCOL_GUID },
247346482Skevans        { .efi_guid_name = "device path to text",
248346482Skevans	    .efi_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID },
249346482Skevans        { .efi_guid_name = "reset arch",
250346482Skevans	    .efi_guid = EFI_RESET_ARCH_PROTOCOL_GUID },
251346482Skevans        { .efi_guid_name = "CPU arch", .efi_guid = EFI_CPU_ARCH_PROTOCOL_GUID },
252346482Skevans        { .efi_guid_name = "CPU IO2", .efi_guid = EFI_CPU_IO2_PROTOCOL_GUID },
253346482Skevans        { .efi_guid_name = "Legacy 8259",
254346482Skevans	    .efi_guid = EFI_LEGACY_8259_PROTOCOL_GUID },
255346482Skevans        { .efi_guid_name = "Security arch",
256346482Skevans	    .efi_guid = EFI_SECURITY_ARCH_PROTOCOL_GUID },
257346482Skevans        { .efi_guid_name = "Security2 arch",
258346482Skevans	    .efi_guid = EFI_SECURITY2_ARCH_PROTOCOL_GUID },
259346482Skevans        { .efi_guid_name = "Runtime arch",
260346482Skevans	    .efi_guid = EFI_RUNTIME_ARCH_PROTOCOL_GUID },
261346482Skevans        { .efi_guid_name = "status code runtime",
262346482Skevans	    .efi_guid = EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID },
263346482Skevans        { .efi_guid_name = "data hub", .efi_guid = EFI_DATA_HUB_PROTOCOL_GUID },
264346482Skevans        { .efi_guid_name = "PCD", .efi_guid = PCD_PROTOCOL_GUID },
265346482Skevans        { .efi_guid_name = "EFI PCD", .efi_guid = EFI_PCD_PROTOCOL_GUID },
266346482Skevans        { .efi_guid_name = "firmware volume block",
267346482Skevans	    .efi_guid = EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID },
268346482Skevans        { .efi_guid_name = "firmware volume2",
269346482Skevans	    .efi_guid = EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID },
270346482Skevans        { .efi_guid_name = "firmware volume dispatch",
271346482Skevans	    .efi_guid = EFI_FIRMWARE_VOLUME_DISPATCH_PROTOCOL_GUID },
272346482Skevans        { .efi_guid_name = "lzma compress", .efi_guid = LZMA_COMPRESS_GUID },
273346482Skevans        { .efi_guid_name = "MP services",
274346482Skevans	    .efi_guid = EFI_MP_SERVICES_PROTOCOL_GUID },
275346482Skevans        { .efi_guid_name = MTC_VARIABLE_NAME, .efi_guid = MTC_VENDOR_GUID },
276346482Skevans        { .efi_guid_name = "RTC", .efi_guid = { 0x378D7B65, 0x8DA9, 0x4773,
277346482Skevans	    { 0xB6, 0xE4, 0xA4, 0x78, 0x26, 0xA8, 0x33, 0xE1} } },
278346482Skevans        { .efi_guid_name = "Active EDID",
279346482Skevans	    .efi_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID },
280346482Skevans        { .efi_guid_name = "Discovered EDID",
281346482Skevans	    .efi_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID }
282346482Skevans};
283346482Skevans
284346482Skevansbool
285346482Skevansefi_guid_to_str(const EFI_GUID *guid, char **sp)
286346482Skevans{
287346482Skevans	uint32_t status;
288346482Skevans
289346482Skevans	uuid_to_string((const uuid_t *)guid, sp, &status);
290346482Skevans	return (status == uuid_s_ok ? true : false);
291346482Skevans}
292346482Skevans
293346482Skevansbool
294346482Skevansefi_str_to_guid(const char *s, EFI_GUID *guid)
295346482Skevans{
296346482Skevans	uint32_t status;
297346482Skevans
298346482Skevans	uuid_from_string(s, (uuid_t *)guid, &status);
299346482Skevans	return (status == uuid_s_ok ? true : false);
300346482Skevans}
301346482Skevans
302346482Skevansbool
303346482Skevansefi_name_to_guid(const char *name, EFI_GUID *guid)
304346482Skevans{
305346482Skevans	uint32_t i;
306346482Skevans
307346482Skevans	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
308346482Skevans		if (strcasecmp(name, efi_uuid_mapping[i].efi_guid_name) == 0) {
309346482Skevans			*guid = efi_uuid_mapping[i].efi_guid;
310346482Skevans			return (true);
311346482Skevans		}
312346482Skevans	}
313346482Skevans	return (efi_str_to_guid(name, guid));
314346482Skevans}
315346482Skevans
316346482Skevansbool
317346482Skevansefi_guid_to_name(EFI_GUID *guid, char **name)
318346482Skevans{
319346482Skevans	uint32_t i;
320346482Skevans	int rv;
321346482Skevans
322346482Skevans	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
323346482Skevans		rv = uuid_equal((uuid_t *)guid,
324346482Skevans		    (uuid_t *)&efi_uuid_mapping[i].efi_guid, NULL);
325346482Skevans		if (rv != 0) {
326346482Skevans			*name = strdup(efi_uuid_mapping[i].efi_guid_name);
327346482Skevans			if (*name == NULL)
328346482Skevans				return (false);
329346482Skevans			return (true);
330346482Skevans		}
331346482Skevans	}
332346482Skevans	return (efi_guid_to_str(guid, name));
333346482Skevans}
334346482Skevans
335329099Skevansvoid
336329099Skevansefi_init_environment(void)
337329099Skevans{
338329099Skevans	char var[128];
339329099Skevans
340329099Skevans	snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
341329099Skevans	    ST->Hdr.Revision & 0xffff);
342329099Skevans	env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
343329099Skevans}
344329099Skevans
345329099SkevansCOMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
346329099Skevans
347329099Skevansstatic int
348346482Skevansefi_print_other_value(uint8_t *data, UINTN datasz)
349346482Skevans{
350346482Skevans	UINTN i;
351346482Skevans	bool is_ascii = true;
352346482Skevans
353346482Skevans	printf(" = ");
354346482Skevans	for (i = 0; i < datasz - 1; i++) {
355346482Skevans		/*
356346482Skevans		 * Quick hack to see if this ascii-ish string is printable
357346482Skevans		 * range plus tab, cr and lf.
358346482Skevans		 */
359346482Skevans		if ((data[i] < 32 || data[i] > 126)
360346482Skevans		    && data[i] != 9 && data[i] != 10 && data[i] != 13) {
361346482Skevans			is_ascii = false;
362346482Skevans			break;
363346482Skevans		}
364346482Skevans	}
365346482Skevans	if (data[datasz - 1] != '\0')
366346482Skevans		is_ascii = false;
367346482Skevans	if (is_ascii == true) {
368346482Skevans		printf("%s", data);
369346482Skevans		if (pager_output("\n"))
370346482Skevans			return (CMD_WARN);
371346482Skevans	} else {
372346482Skevans		if (pager_output("\n"))
373346482Skevans			return (CMD_WARN);
374346482Skevans		/*
375346482Skevans		 * Dump hex bytes grouped by 4.
376346482Skevans		 */
377346482Skevans		for (i = 0; i < datasz; i++) {
378346482Skevans			printf("%02x ", data[i]);
379346482Skevans			if ((i + 1) % 4 == 0)
380346482Skevans				printf(" ");
381346482Skevans			if ((i + 1) % 20 == 0) {
382346482Skevans				if (pager_output("\n"))
383346482Skevans					return (CMD_WARN);
384346482Skevans			}
385346482Skevans		}
386346482Skevans		if (pager_output("\n"))
387346482Skevans			return (CMD_WARN);
388346482Skevans	}
389346482Skevans
390346482Skevans	return (CMD_OK);
391346482Skevans}
392346482Skevans
393346482Skevans/* This appears to be some sort of UEFI shell alias table. */
394346482Skevansstatic int
395346482Skevansefi_print_shell_str(const CHAR16 *varnamearg __unused, uint8_t *data,
396346482Skevans    UINTN datasz __unused)
397346482Skevans{
398346482Skevans	printf(" = %S", (CHAR16 *)data);
399346482Skevans	if (pager_output("\n"))
400346482Skevans		return (CMD_WARN);
401346482Skevans	return (CMD_OK);
402346482Skevans}
403346482Skevans
404346482Skevansconst char *
405346482Skevansefi_memory_type(EFI_MEMORY_TYPE type)
406346482Skevans{
407346482Skevans	const char *types[] = {
408346482Skevans	    "Reserved",
409346482Skevans	    "LoaderCode",
410346482Skevans	    "LoaderData",
411346482Skevans	    "BootServicesCode",
412346482Skevans	    "BootServicesData",
413346482Skevans	    "RuntimeServicesCode",
414346482Skevans	    "RuntimeServicesData",
415346482Skevans	    "ConventionalMemory",
416346482Skevans	    "UnusableMemory",
417346482Skevans	    "ACPIReclaimMemory",
418346482Skevans	    "ACPIMemoryNVS",
419346482Skevans	    "MemoryMappedIO",
420346482Skevans	    "MemoryMappedIOPortSpace",
421346482Skevans	    "PalCode",
422346482Skevans	    "PersistentMemory"
423346482Skevans	};
424346482Skevans
425346482Skevans	switch (type) {
426346482Skevans	case EfiReservedMemoryType:
427346482Skevans	case EfiLoaderCode:
428346482Skevans	case EfiLoaderData:
429346482Skevans	case EfiBootServicesCode:
430346482Skevans	case EfiBootServicesData:
431346482Skevans	case EfiRuntimeServicesCode:
432346482Skevans	case EfiRuntimeServicesData:
433346482Skevans	case EfiConventionalMemory:
434346482Skevans	case EfiUnusableMemory:
435346482Skevans	case EfiACPIReclaimMemory:
436346482Skevans	case EfiACPIMemoryNVS:
437346482Skevans	case EfiMemoryMappedIO:
438346482Skevans	case EfiMemoryMappedIOPortSpace:
439346482Skevans	case EfiPalCode:
440346482Skevans	case EfiPersistentMemory:
441346482Skevans		return (types[type]);
442346482Skevans	default:
443346482Skevans		return ("Unknown");
444346482Skevans	}
445346482Skevans}
446346482Skevans
447346482Skevans/* Print memory type table. */
448346482Skevansstatic int
449346482Skevansefi_print_mem_type(const CHAR16 *varnamearg __unused, uint8_t *data,
450346482Skevans    UINTN datasz)
451346482Skevans{
452346482Skevans	int i, n;
453346482Skevans	EFI_MEMORY_TYPE_INFORMATION *ti;
454346482Skevans
455346482Skevans	ti = (EFI_MEMORY_TYPE_INFORMATION *)data;
456346482Skevans	if (pager_output(" = \n"))
457346482Skevans		return (CMD_WARN);
458346482Skevans
459346482Skevans	n = datasz / sizeof (EFI_MEMORY_TYPE_INFORMATION);
460346482Skevans	for (i = 0; i < n && ti[i].NumberOfPages != 0; i++) {
461346482Skevans		printf("\t%23s pages: %u", efi_memory_type(ti[i].Type),
462346482Skevans		    ti[i].NumberOfPages);
463346482Skevans		if (pager_output("\n"))
464346482Skevans			return (CMD_WARN);
465346482Skevans	}
466346482Skevans
467346482Skevans	return (CMD_OK);
468346482Skevans}
469346482Skevans
470346482Skevans/*
471346482Skevans * Print FreeBSD variables.
472346482Skevans * We have LoaderPath and LoaderDev as CHAR16 strings.
473346482Skevans */
474346482Skevansstatic int
475346482Skevansefi_print_freebsd(const CHAR16 *varnamearg, uint8_t *data,
476346482Skevans    UINTN datasz __unused)
477346482Skevans{
478346482Skevans	int rv = -1;
479346482Skevans	char *var = NULL;
480346482Skevans
481346482Skevans	if (ucs2_to_utf8(varnamearg, &var) != 0)
482346482Skevans		return (CMD_ERROR);
483346482Skevans
484346482Skevans	if (strcmp("LoaderPath", var) == 0 ||
485346482Skevans	    strcmp("LoaderDev", var) == 0) {
486346482Skevans		printf(" = ");
487346482Skevans		printf("%S", (CHAR16 *)data);
488346482Skevans
489346482Skevans		if (pager_output("\n"))
490346482Skevans			rv = CMD_WARN;
491346482Skevans		else
492346482Skevans			rv = CMD_OK;
493346482Skevans	}
494346482Skevans
495346482Skevans	free(var);
496346482Skevans	return (rv);
497346482Skevans}
498346482Skevans
499346482Skevans/* Print global variables. */
500346482Skevansstatic int
501346482Skevansefi_print_global(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz)
502346482Skevans{
503346482Skevans	int rv = -1;
504346482Skevans	char *var = NULL;
505346482Skevans
506346482Skevans	if (ucs2_to_utf8(varnamearg, &var) != 0)
507346482Skevans		return (CMD_ERROR);
508346482Skevans
509346482Skevans	if (strcmp("AuditMode", var) == 0) {
510346482Skevans		printf(" = ");
511346482Skevans		printf("0x%x", *data);	/* 8-bit int */
512346482Skevans		goto done;
513346482Skevans	}
514346482Skevans
515346482Skevans	if (strcmp("BootOptionSupport", var) == 0) {
516346482Skevans		printf(" = ");
517346482Skevans		printf("0x%x", *((uint32_t *)data));	/* UINT32 */
518346482Skevans		goto done;
519346482Skevans	}
520346482Skevans
521346482Skevans	if (strcmp("BootCurrent", var) == 0 ||
522346482Skevans	    strcmp("BootNext", var) == 0 ||
523346482Skevans	    strcmp("Timeout", var) == 0) {
524346482Skevans		printf(" = ");
525346482Skevans		printf("%u", *((uint16_t *)data));	/* UINT16 */
526346482Skevans		goto done;
527346482Skevans	}
528346482Skevans
529346482Skevans	if (strcmp("BootOrder", var) == 0 ||
530346482Skevans	    strcmp("DriverOrder", var) == 0) {
531346482Skevans		UINTN i;
532346482Skevans		UINT16 *u16 = (UINT16 *)data;
533346482Skevans
534346482Skevans		printf(" =");
535346482Skevans		for (i = 0; i < datasz / sizeof (UINT16); i++)
536346482Skevans			printf(" %u", u16[i]);
537346482Skevans		goto done;
538346482Skevans	}
539346482Skevans	if (strncmp("Boot", var, 4) == 0 ||
540346482Skevans	    strncmp("Driver", var, 5) == 0 ||
541346482Skevans	    strncmp("SysPrep", var, 7) == 0 ||
542346482Skevans	    strncmp("OsRecovery", var, 10) == 0) {
543346482Skevans		UINT16 filepathlistlen;
544346482Skevans		CHAR16 *text;
545346482Skevans		int desclen;
546346482Skevans		EFI_DEVICE_PATH *dp;
547346482Skevans
548346482Skevans		data += sizeof(UINT32);
549346482Skevans		filepathlistlen = *(uint16_t *)data;
550346482Skevans		data += sizeof (UINT16);
551346482Skevans		text = (CHAR16 *)data;
552346482Skevans
553346482Skevans		for (desclen = 0; text[desclen] != 0; desclen++)
554346482Skevans			;
555346482Skevans		if (desclen != 0) {
556346482Skevans			/* Add terminating zero and we have CHAR16. */
557346482Skevans			desclen = (desclen + 1) * 2;
558346482Skevans		}
559346482Skevans
560346482Skevans		printf(" = ");
561346482Skevans		printf("%S", text);
562346482Skevans		if (filepathlistlen != 0) {
563346482Skevans			/* Output pathname from new line. */
564346482Skevans			if (pager_output("\n")) {
565346482Skevans				rv = CMD_WARN;
566346482Skevans				goto done;
567346482Skevans			}
568346482Skevans			dp = malloc(filepathlistlen);
569346482Skevans			if (dp == NULL)
570346482Skevans				goto done;
571346482Skevans
572346482Skevans			memcpy(dp, data + desclen, filepathlistlen);
573346482Skevans			text = efi_devpath_name(dp);
574346482Skevans			if (text != NULL) {
575346482Skevans				printf("\t%S", text);
576346482Skevans				efi_free_devpath_name(text);
577346482Skevans			}
578346482Skevans			free(dp);
579346482Skevans		}
580346482Skevans		goto done;
581346482Skevans	}
582346482Skevans
583346482Skevans	if (strcmp("ConIn", var) == 0 ||
584346482Skevans	    strcmp("ConInDev", var) == 0 ||
585346482Skevans	    strcmp("ConOut", var) == 0 ||
586346482Skevans	    strcmp("ConOutDev", var) == 0 ||
587346482Skevans	    strcmp("ErrOut", var) == 0 ||
588346482Skevans	    strcmp("ErrOutDev", var) == 0) {
589346482Skevans		CHAR16 *text;
590346482Skevans
591346482Skevans		printf(" = ");
592346482Skevans		text = efi_devpath_name((EFI_DEVICE_PATH *)data);
593346482Skevans		if (text != NULL) {
594346482Skevans			printf("%S", text);
595346482Skevans			efi_free_devpath_name(text);
596346482Skevans		}
597346482Skevans		goto done;
598346482Skevans	}
599346482Skevans
600346482Skevans	if (strcmp("PlatformLang", var) == 0 ||
601346482Skevans	    strcmp("PlatformLangCodes", var) == 0 ||
602346482Skevans	    strcmp("LangCodes", var) == 0 ||
603346482Skevans	    strcmp("Lang", var) == 0) {
604346482Skevans		printf(" = ");
605346482Skevans		printf("%s", data);	/* ASCII string */
606346482Skevans		goto done;
607346482Skevans	}
608346482Skevans
609346482Skevans	/*
610346482Skevans	 * Feature bitmap from firmware to OS.
611346482Skevans	 * Older UEFI provides UINT32, newer UINT64.
612346482Skevans	 */
613346482Skevans	if (strcmp("OsIndicationsSupported", var) == 0) {
614346482Skevans		printf(" = ");
615346482Skevans		if (datasz == 4)
616346482Skevans			printf("0x%x", *((uint32_t *)data));
617346482Skevans		else
618346482Skevans			printf("0x%jx", *((uint64_t *)data));
619346482Skevans		goto done;
620346482Skevans	}
621346482Skevans
622346482Skevans	/* Fallback for anything else. */
623346482Skevans	rv = efi_print_other_value(data, datasz);
624346482Skevansdone:
625346482Skevans	if (rv == -1) {
626346482Skevans		if (pager_output("\n"))
627346482Skevans			rv = CMD_WARN;
628346482Skevans		else
629346482Skevans			rv = CMD_OK;
630346482Skevans	}
631346482Skevans	free(var);
632346482Skevans	return (rv);
633346482Skevans}
634346482Skevans
635346482Skevansstatic void
636346482Skevansefi_print_var_attr(UINT32 attr)
637346482Skevans{
638346482Skevans	bool comma = false;
639346482Skevans
640346482Skevans	if (attr & EFI_VARIABLE_NON_VOLATILE) {
641346482Skevans		printf("NV");
642346482Skevans		comma = true;
643346482Skevans	}
644346482Skevans	if (attr & EFI_VARIABLE_BOOTSERVICE_ACCESS) {
645346482Skevans		if (comma == true)
646346482Skevans			printf(",");
647346482Skevans		printf("BS");
648346482Skevans		comma = true;
649346482Skevans	}
650346482Skevans	if (attr & EFI_VARIABLE_RUNTIME_ACCESS) {
651346482Skevans		if (comma == true)
652346482Skevans			printf(",");
653346482Skevans		printf("RS");
654346482Skevans		comma = true;
655346482Skevans	}
656346482Skevans	if (attr & EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
657346482Skevans		if (comma == true)
658346482Skevans			printf(",");
659346482Skevans		printf("HR");
660346482Skevans		comma = true;
661346482Skevans	}
662346482Skevans	if (attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
663346482Skevans		if (comma == true)
664346482Skevans			printf(",");
665346482Skevans		printf("AT");
666346482Skevans		comma = true;
667346482Skevans	}
668346482Skevans}
669346482Skevans
670346482Skevansstatic int
671329099Skevansefi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
672329099Skevans{
673346482Skevans	UINTN		datasz;
674329099Skevans	EFI_STATUS	status;
675329099Skevans	UINT32		attr;
676329099Skevans	char		*str;
677346482Skevans	uint8_t		*data;
678346482Skevans	int		rv = CMD_OK;
679329099Skevans
680346482Skevans	str = NULL;
681329099Skevans	datasz = 0;
682346482Skevans	status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, NULL);
683329099Skevans	if (status != EFI_BUFFER_TOO_SMALL) {
684329099Skevans		printf("Can't get the variable: error %#lx\n",
685329099Skevans		    EFI_ERROR_CODE(status));
686329099Skevans		return (CMD_ERROR);
687329099Skevans	}
688329099Skevans	data = malloc(datasz);
689346482Skevans	if (data == NULL) {
690346482Skevans		printf("Out of memory\n");
691346482Skevans		return (CMD_ERROR);
692346482Skevans	}
693346482Skevans
694346482Skevans	status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, data);
695329099Skevans	if (status != EFI_SUCCESS) {
696329099Skevans		printf("Can't get the variable: error %#lx\n",
697329099Skevans		    EFI_ERROR_CODE(status));
698346482Skevans		free(data);
699329099Skevans		return (CMD_ERROR);
700329099Skevans	}
701346482Skevans
702346482Skevans	if (efi_guid_to_name(matchguid, &str) == false) {
703346482Skevans		rv = CMD_ERROR;
704346482Skevans		goto done;
705329099Skevans	}
706346482Skevans	printf("%s ", str);
707346482Skevans	efi_print_var_attr(attr);
708346482Skevans	printf(" %S", varnamearg);
709346482Skevans
710346482Skevans	if (lflag == 0) {
711346482Skevans		if (strcmp(str, "global") == 0)
712346482Skevans			rv = efi_print_global(varnamearg, data, datasz);
713346482Skevans		else if (strcmp(str, "freebsd") == 0)
714346482Skevans			rv = efi_print_freebsd(varnamearg, data, datasz);
715346482Skevans		else if (strcmp(str,
716346482Skevans		    EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME) == 0)
717346482Skevans			rv = efi_print_mem_type(varnamearg, data, datasz);
718346482Skevans		else if (strcmp(str,
719346482Skevans		    "47c7b227-c42a-11d2-8e57-00a0c969723b") == 0)
720346482Skevans			rv = efi_print_shell_str(varnamearg, data, datasz);
721346482Skevans		else if (strcmp(str, MTC_VARIABLE_NAME) == 0) {
722346482Skevans			printf(" = ");
723346482Skevans			printf("%u", *((uint32_t *)data));	/* UINT32 */
724346482Skevans			rv = CMD_OK;
725346482Skevans			if (pager_output("\n"))
726346482Skevans				rv = CMD_WARN;
727346482Skevans		} else
728346482Skevans			rv = efi_print_other_value(data, datasz);
729346482Skevans	} else if (pager_output("\n"))
730346482Skevans		rv =  CMD_WARN;
731346482Skevans
732346482Skevansdone:
733346482Skevans	free(str);
734329099Skevans	free(data);
735346482Skevans	return (rv);
736329099Skevans}
737329099Skevans
738329099Skevansstatic int
739329099Skevanscommand_efi_show(int argc, char *argv[])
740329099Skevans{
741329099Skevans	/*
742329099Skevans	 * efi-show [-a]
743329099Skevans	 *	print all the env
744346472Skevans	 * efi-show -g UUID
745329099Skevans	 *	print all the env vars tagged with UUID
746329099Skevans	 * efi-show -v var
747329099Skevans	 *	search all the env vars and print the ones matching var
748346472Skevans	 * efi-show -g UUID -v var
749346472Skevans	 * efi-show UUID var
750329099Skevans	 *	print all the env vars that match UUID and var
751329099Skevans	 */
752329099Skevans	/* NB: We assume EFI_GUID is the same as uuid_t */
753329099Skevans	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
754329099Skevans	int		ch, rv;
755329099Skevans	unsigned	i;
756329099Skevans	EFI_STATUS	status;
757346482Skevans	EFI_GUID	varguid = ZERO_GUID;
758346482Skevans	EFI_GUID	matchguid = ZERO_GUID;
759329099Skevans	CHAR16		*varname;
760329099Skevans	CHAR16		*newnm;
761329099Skevans	CHAR16		varnamearg[128];
762329099Skevans	UINTN		varalloc;
763329099Skevans	UINTN		varsz;
764329099Skevans
765346482Skevans	optind = 1;
766346482Skevans	optreset = 1;
767346482Skevans	opterr = 1;
768346482Skevans
769329099Skevans	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
770329099Skevans		switch (ch) {
771329099Skevans		case 'a':
772329099Skevans			aflag = 1;
773329099Skevans			break;
774329099Skevans		case 'g':
775329099Skevans			gflag = 1;
776346482Skevans			if (efi_name_to_guid(optarg, &matchguid) == false) {
777346482Skevans				printf("uuid %s could not be parsed\n", optarg);
778329099Skevans				return (CMD_ERROR);
779329099Skevans			}
780329099Skevans			break;
781329099Skevans		case 'l':
782329099Skevans			lflag = 1;
783329099Skevans			break;
784329099Skevans		case 'v':
785329099Skevans			vflag = 1;
786329099Skevans			if (strlen(optarg) >= nitems(varnamearg)) {
787346482Skevans				printf("Variable %s is longer than %zu "
788346482Skevans				    "characters\n", optarg, nitems(varnamearg));
789329099Skevans				return (CMD_ERROR);
790329099Skevans			}
791346482Skevans			cpy8to16(optarg, varnamearg, nitems(varnamearg));
792329099Skevans			break;
793329099Skevans		default:
794329099Skevans			return (CMD_ERROR);
795329099Skevans		}
796329099Skevans	}
797329099Skevans
798346482Skevans	if (argc == 1)		/* default is -a */
799346482Skevans		aflag = 1;
800346482Skevans
801329099Skevans	if (aflag && (gflag || vflag)) {
802346482Skevans		printf("-a isn't compatible with -g or -v\n");
803329099Skevans		return (CMD_ERROR);
804329099Skevans	}
805329099Skevans
806329099Skevans	if (aflag && optind < argc) {
807329099Skevans		printf("-a doesn't take any args\n");
808329099Skevans		return (CMD_ERROR);
809329099Skevans	}
810329099Skevans
811329099Skevans	argc -= optind;
812329099Skevans	argv += optind;
813329099Skevans
814329099Skevans	pager_open();
815329099Skevans	if (vflag && gflag) {
816329099Skevans		rv = efi_print_var(varnamearg, &matchguid, lflag);
817346482Skevans		if (rv == CMD_WARN)
818346482Skevans			rv = CMD_OK;
819329099Skevans		pager_close();
820329099Skevans		return (rv);
821329099Skevans	}
822329099Skevans
823329099Skevans	if (argc == 2) {
824329099Skevans		optarg = argv[0];
825329099Skevans		if (strlen(optarg) >= nitems(varnamearg)) {
826346482Skevans			printf("Variable %s is longer than %zu characters\n",
827329099Skevans			    optarg, nitems(varnamearg));
828329099Skevans			pager_close();
829329099Skevans			return (CMD_ERROR);
830329099Skevans		}
831329099Skevans		for (i = 0; i < strlen(optarg); i++)
832329099Skevans			varnamearg[i] = optarg[i];
833329099Skevans		varnamearg[i] = 0;
834329099Skevans		optarg = argv[1];
835346482Skevans		if (efi_name_to_guid(optarg, &matchguid) == false) {
836346482Skevans			printf("uuid %s could not be parsed\n", optarg);
837329099Skevans			pager_close();
838329099Skevans			return (CMD_ERROR);
839329099Skevans		}
840329099Skevans		rv = efi_print_var(varnamearg, &matchguid, lflag);
841346482Skevans		if (rv == CMD_WARN)
842346482Skevans			rv = CMD_OK;
843329099Skevans		pager_close();
844329099Skevans		return (rv);
845329099Skevans	}
846329099Skevans
847329099Skevans	if (argc > 0) {
848346482Skevans		printf("Too many args: %d\n", argc);
849329099Skevans		pager_close();
850329099Skevans		return (CMD_ERROR);
851329099Skevans	}
852329099Skevans
853329099Skevans	/*
854329099Skevans	 * Initiate the search -- note the standard takes pain
855329099Skevans	 * to specify the initial call must be a poiner to a NULL
856329099Skevans	 * character.
857329099Skevans	 */
858329099Skevans	varalloc = 1024;
859329099Skevans	varname = malloc(varalloc);
860329099Skevans	if (varname == NULL) {
861329099Skevans		printf("Can't allocate memory to get variables\n");
862329099Skevans		pager_close();
863329099Skevans		return (CMD_ERROR);
864329099Skevans	}
865329099Skevans	varname[0] = 0;
866329099Skevans	while (1) {
867329099Skevans		varsz = varalloc;
868329099Skevans		status = RS->GetNextVariableName(&varsz, varname, &varguid);
869329099Skevans		if (status == EFI_BUFFER_TOO_SMALL) {
870329099Skevans			varalloc = varsz;
871329099Skevans			newnm = realloc(varname, varalloc);
872329099Skevans			if (newnm == NULL) {
873346482Skevans				printf("Can't allocate memory to get "
874346482Skevans				    "variables\n");
875346482Skevans				rv = CMD_ERROR;
876346482Skevans				break;
877329099Skevans			}
878329099Skevans			varname = newnm;
879329099Skevans			continue; /* Try again with bigger buffer */
880329099Skevans		}
881346482Skevans		if (status == EFI_NOT_FOUND) {
882346482Skevans			rv = CMD_OK;
883329099Skevans			break;
884346482Skevans		}
885346482Skevans		if (status != EFI_SUCCESS) {
886346482Skevans			rv = CMD_ERROR;
887346482Skevans			break;
888346482Skevans		}
889346482Skevans
890329099Skevans		if (aflag) {
891346482Skevans			rv = efi_print_var(varname, &varguid, lflag);
892346482Skevans			if (rv != CMD_OK) {
893346482Skevans				if (rv == CMD_WARN)
894346482Skevans					rv = CMD_OK;
895329099Skevans				break;
896346482Skevans			}
897329099Skevans			continue;
898329099Skevans		}
899329099Skevans		if (vflag) {
900329099Skevans			if (wcscmp(varnamearg, varname) == 0) {
901346482Skevans				rv = efi_print_var(varname, &varguid, lflag);
902346482Skevans				if (rv != CMD_OK) {
903346482Skevans					if (rv == CMD_WARN)
904346482Skevans						rv = CMD_OK;
905329099Skevans					break;
906346482Skevans				}
907329099Skevans				continue;
908329099Skevans			}
909329099Skevans		}
910329099Skevans		if (gflag) {
911346482Skevans			rv = uuid_equal((uuid_t *)&varguid,
912346482Skevans			    (uuid_t *)&matchguid, NULL);
913346482Skevans			if (rv != 0) {
914346482Skevans				rv = efi_print_var(varname, &varguid, lflag);
915346482Skevans				if (rv != CMD_OK) {
916346482Skevans					if (rv == CMD_WARN)
917346482Skevans						rv = CMD_OK;
918329099Skevans					break;
919346482Skevans				}
920329099Skevans				continue;
921329099Skevans			}
922329099Skevans		}
923329099Skevans	}
924329099Skevans	free(varname);
925329099Skevans	pager_close();
926329099Skevans
927346482Skevans	return (rv);
928329099Skevans}
929329099Skevans
930329099SkevansCOMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
931329099Skevans
932329099Skevansstatic int
933329099Skevanscommand_efi_set(int argc, char *argv[])
934329099Skevans{
935329099Skevans	char *uuid, *var, *val;
936329099Skevans	CHAR16 wvar[128];
937329099Skevans	EFI_GUID guid;
938346482Skevans#if defined(ENABLE_UPDATES)
939329099Skevans	EFI_STATUS err;
940346482Skevans#endif
941329099Skevans
942329099Skevans	if (argc != 4) {
943329099Skevans		printf("efi-set uuid var new-value\n");
944329099Skevans		return (CMD_ERROR);
945329099Skevans	}
946329099Skevans	uuid = argv[1];
947329099Skevans	var = argv[2];
948329099Skevans	val = argv[3];
949346482Skevans	if (efi_name_to_guid(uuid, &guid) == false) {
950346482Skevans		printf("Invalid uuid %s\n", uuid);
951329099Skevans		return (CMD_ERROR);
952329099Skevans	}
953346482Skevans	cpy8to16(var, wvar, nitems(wvar));
954346482Skevans#if defined(ENABLE_UPDATES)
955346482Skevans	err = RS->SetVariable(wvar, &guid, EFI_VARIABLE_NON_VOLATILE |
956346482Skevans	    EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
957329099Skevans	    strlen(val) + 1, val);
958329099Skevans	if (EFI_ERROR(err)) {
959346482Skevans		printf("Failed to set variable: error %lu\n",
960346482Skevans		    EFI_ERROR_CODE(err));
961329099Skevans		return (CMD_ERROR);
962329099Skevans	}
963346482Skevans#else
964346482Skevans	printf("would set %s %s = %s\n", uuid, var, val);
965346482Skevans#endif
966329099Skevans	return (CMD_OK);
967329099Skevans}
968329099Skevans
969329099SkevansCOMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
970329099Skevans
971329099Skevansstatic int
972329099Skevanscommand_efi_unset(int argc, char *argv[])
973329099Skevans{
974329099Skevans	char *uuid, *var;
975329099Skevans	CHAR16 wvar[128];
976329099Skevans	EFI_GUID guid;
977346482Skevans#if defined(ENABLE_UPDATES)
978329099Skevans	EFI_STATUS err;
979346482Skevans#endif
980329099Skevans
981329099Skevans	if (argc != 3) {
982329099Skevans		printf("efi-unset uuid var\n");
983329099Skevans		return (CMD_ERROR);
984329099Skevans	}
985329099Skevans	uuid = argv[1];
986329099Skevans	var = argv[2];
987346482Skevans	if (efi_name_to_guid(uuid, &guid) == false) {
988329099Skevans		printf("Invalid uuid %s\n", uuid);
989329099Skevans		return (CMD_ERROR);
990329099Skevans	}
991346482Skevans	cpy8to16(var, wvar, nitems(wvar));
992346482Skevans#if defined(ENABLE_UPDATES)
993329099Skevans	err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
994329099Skevans	if (EFI_ERROR(err)) {
995346482Skevans		printf("Failed to unset variable: error %lu\n",
996346482Skevans		    EFI_ERROR_CODE(err));
997329099Skevans		return (CMD_ERROR);
998329099Skevans	}
999346482Skevans#else
1000346482Skevans	printf("would unset %s %s \n", uuid, var);
1001346482Skevans#endif
1002329099Skevans	return (CMD_OK);
1003329099Skevans}
1004