xref: /freebsd/stand/efi/libefi/env.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*
2  * Copyright (c) 2015 Netflix, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include <stand.h>
28 #include <string.h>
29 #include <efi.h>
30 #include <efichar.h>
31 #include <efilib.h>
32 #include <efigpt.h>	/* Partition GUIDS */
33 #include <Guid/MemoryTypeInformation.h>
34 #include <Guid/MtcVendor.h>
35 #include <Guid/ZeroGuid.h>
36 #include <Protocol/EdidActive.h>
37 #include <Protocol/EdidDiscovered.h>
38 #include <uuid.h>
39 #include <stdbool.h>
40 #include <sys/param.h>
41 #include "bootstrap.h"
42 
43 /*
44  * About ENABLE_UPDATES
45  *
46  * The UEFI variables are identified only by GUID and name, there is no
47  * way to (auto)detect the type for the value, so we need to process the
48  * variables case by case, as we do learn about them.
49  *
50  * While showing the variable name and the value is safe, we must not store
51  * random values nor allow removing (random) variables.
52  *
53  * Since we do have stub code to set/unset the variables, I do want to keep
54  * it to make the future development a bit easier, but the updates are disabled
55  * by default till:
56  *	a) the validation and data translation to values is properly implemented
57  *	b) We have established which variables we do allow to be updated.
58  * Therefore the set/unset code is included only for developers aid.
59  */
60 
61 static struct efi_uuid_mapping {
62 	const char *efi_guid_name;
63 	EFI_GUID efi_guid;
64 } efi_uuid_mapping[] = {
65 	{ .efi_guid_name = "global", .efi_guid = EFI_GLOBAL_VARIABLE },
66 	{ .efi_guid_name = "freebsd", .efi_guid = FREEBSD_BOOT_VAR_GUID },
67 	/* EFI Systab entry names. */
68 	{ .efi_guid_name = "MPS Table", .efi_guid = MPS_TABLE_GUID },
69 	{ .efi_guid_name = "ACPI Table", .efi_guid = ACPI_TABLE_GUID },
70 	{ .efi_guid_name = "ACPI 2.0 Table", .efi_guid = ACPI_20_TABLE_GUID },
71 	{ .efi_guid_name = "SMBIOS Table", .efi_guid = SMBIOS_TABLE_GUID },
72 	{ .efi_guid_name = "SMBIOS3 Table", .efi_guid = SMBIOS3_TABLE_GUID },
73 	{ .efi_guid_name = "DXE Table", .efi_guid = DXE_SERVICES_TABLE_GUID },
74 	{ .efi_guid_name = "HOB List Table", .efi_guid = HOB_LIST_TABLE_GUID },
75 	{ .efi_guid_name = EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
76 	    .efi_guid = EFI_MEMORY_TYPE_INFORMATION_GUID },
77 	{ .efi_guid_name = "Debug Image Info Table",
78 	    .efi_guid = DEBUG_IMAGE_INFO_TABLE_GUID },
79 	{ .efi_guid_name = "FDT Table", .efi_guid = FDT_TABLE_GUID },
80 	/*
81 	 * Protocol names for debug purposes.
82 	 * Can be removed along with lsefi command.
83 	 */
84 	{ .efi_guid_name = "device path", .efi_guid = DEVICE_PATH_PROTOCOL },
85 	{ .efi_guid_name = "block io", .efi_guid = BLOCK_IO_PROTOCOL },
86 	{ .efi_guid_name = "disk io", .efi_guid = DISK_IO_PROTOCOL },
87 	{ .efi_guid_name = "disk info", .efi_guid =
88 	    EFI_DISK_INFO_PROTOCOL_GUID },
89 	{ .efi_guid_name = "simple fs",
90 	    .efi_guid = SIMPLE_FILE_SYSTEM_PROTOCOL },
91 	{ .efi_guid_name = "load file", .efi_guid = LOAD_FILE_PROTOCOL },
92 	{ .efi_guid_name = "device io", .efi_guid = DEVICE_IO_PROTOCOL },
93 	{ .efi_guid_name = "unicode collation",
94 	    .efi_guid = UNICODE_COLLATION_PROTOCOL },
95 	{ .efi_guid_name = "unicode collation2",
96 	    .efi_guid = EFI_UNICODE_COLLATION2_PROTOCOL_GUID },
97 	{ .efi_guid_name = "simple network",
98 	    .efi_guid = EFI_SIMPLE_NETWORK_PROTOCOL },
99 	{ .efi_guid_name = "simple text output",
100 	    .efi_guid = SIMPLE_TEXT_OUTPUT_PROTOCOL },
101 	{ .efi_guid_name = "simple text input",
102 	    .efi_guid = SIMPLE_TEXT_INPUT_PROTOCOL },
103 	{ .efi_guid_name = "simple text ex input",
104 	    .efi_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID },
105 	{ .efi_guid_name = "console control",
106 	    .efi_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID },
107 	{ .efi_guid_name = "stdin", .efi_guid = EFI_CONSOLE_IN_DEVICE_GUID },
108 	{ .efi_guid_name = "stdout", .efi_guid = EFI_CONSOLE_OUT_DEVICE_GUID },
109 	{ .efi_guid_name = "stderr",
110 	    .efi_guid = EFI_STANDARD_ERROR_DEVICE_GUID },
111 	{ .efi_guid_name = "GOP",
112 	    .efi_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID },
113 	{ .efi_guid_name = "UGA draw", .efi_guid = EFI_UGA_DRAW_PROTOCOL_GUID },
114 	{ .efi_guid_name = "PXE base code",
115 	    .efi_guid = EFI_PXE_BASE_CODE_PROTOCOL },
116 	{ .efi_guid_name = "PXE base code callback",
117 	    .efi_guid = EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL },
118 	{ .efi_guid_name = "serial io", .efi_guid = SERIAL_IO_PROTOCOL },
119 	{ .efi_guid_name = "loaded image", .efi_guid = LOADED_IMAGE_PROTOCOL },
120 	{ .efi_guid_name = "loaded image device path",
121 	    .efi_guid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID },
122 	{ .efi_guid_name = "ISA io", .efi_guid = EFI_ISA_IO_PROTOCOL_GUID },
123 	{ .efi_guid_name = "IDE controller init",
124 	    .efi_guid = EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID },
125 	{ .efi_guid_name = "ISA ACPI", .efi_guid = EFI_ISA_ACPI_PROTOCOL_GUID },
126 	{ .efi_guid_name = "PCI", .efi_guid = EFI_PCI_IO_PROTOCOL_GUID },
127 	{ .efi_guid_name = "PCI root", .efi_guid = EFI_PCI_ROOT_IO_GUID },
128 	{ .efi_guid_name = "PCI enumeration",
129 	    .efi_guid = EFI_PCI_ENUMERATION_COMPLETE_GUID },
130         { .efi_guid_name = "Driver diagnostics",
131 	    .efi_guid = EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID },
132         { .efi_guid_name = "Driver diagnostics2",
133 	    .efi_guid = EFI_DRIVER_DIAGNOSTICS2_PROTOCOL_GUID },
134         { .efi_guid_name = "simple pointer",
135 	    .efi_guid = EFI_SIMPLE_POINTER_PROTOCOL_GUID },
136         { .efi_guid_name = "absolute pointer",
137 	    .efi_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID },
138         { .efi_guid_name = "VLAN config",
139 	    .efi_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID },
140         { .efi_guid_name = "ARP service binding",
141 	    .efi_guid = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID },
142         { .efi_guid_name = "ARP", .efi_guid = EFI_ARP_PROTOCOL_GUID },
143         { .efi_guid_name = "IPv4 service binding",
144 	    .efi_guid = EFI_IP4_SERVICE_BINDING_PROTOCOL },
145         { .efi_guid_name = "IPv4", .efi_guid = EFI_IP4_PROTOCOL },
146         { .efi_guid_name = "IPv4 config",
147 	    .efi_guid = EFI_IP4_CONFIG_PROTOCOL_GUID },
148         { .efi_guid_name = "IPv6 service binding",
149 	    .efi_guid = EFI_IP6_SERVICE_BINDING_PROTOCOL },
150         { .efi_guid_name = "IPv6", .efi_guid = EFI_IP6_PROTOCOL },
151         { .efi_guid_name = "IPv6 config",
152 	    .efi_guid = EFI_IP6_CONFIG_PROTOCOL_GUID },
153         { .efi_guid_name = "UDPv4", .efi_guid = EFI_UDP4_PROTOCOL },
154         { .efi_guid_name = "UDPv4 service binding",
155 	    .efi_guid = EFI_UDP4_SERVICE_BINDING_PROTOCOL },
156         { .efi_guid_name = "UDPv6", .efi_guid = EFI_UDP6_PROTOCOL },
157         { .efi_guid_name = "UDPv6 service binding",
158 	    .efi_guid = EFI_UDP6_SERVICE_BINDING_PROTOCOL },
159         { .efi_guid_name = "TCPv4", .efi_guid = EFI_TCP4_PROTOCOL },
160         { .efi_guid_name = "TCPv4 service binding",
161 	    .efi_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL },
162         { .efi_guid_name = "TCPv6", .efi_guid = EFI_TCP6_PROTOCOL },
163         { .efi_guid_name = "TCPv6 service binding",
164 	    .efi_guid = EFI_TCP6_SERVICE_BINDING_PROTOCOL },
165         { .efi_guid_name = "EFI System partition",
166 	    .efi_guid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID },
167         { .efi_guid_name = "MBR legacy",
168 	    .efi_guid = EFI_PART_TYPE_LEGACY_MBR_GUID },
169         { .efi_guid_name = "device tree", .efi_guid = EFI_DEVICE_TREE_GUID },
170         { .efi_guid_name = "USB io", .efi_guid = EFI_USB_IO_PROTOCOL_GUID },
171         { .efi_guid_name = "USB2 HC", .efi_guid = EFI_USB2_HC_PROTOCOL_GUID },
172         { .efi_guid_name = "component name",
173 	    .efi_guid = EFI_COMPONENT_NAME_PROTOCOL_GUID },
174         { .efi_guid_name = "component name2",
175 	    .efi_guid = EFI_COMPONENT_NAME2_PROTOCOL_GUID },
176         { .efi_guid_name = "driver binding",
177 	    .efi_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID },
178         { .efi_guid_name = "driver configuration",
179 	    .efi_guid = EFI_DRIVER_CONFIGURATION_PROTOCOL_GUID },
180         { .efi_guid_name = "driver configuration2",
181 	    .efi_guid = EFI_DRIVER_CONFIGURATION2_PROTOCOL_GUID },
182         { .efi_guid_name = "decompress",
183 	    .efi_guid = EFI_DECOMPRESS_PROTOCOL_GUID },
184         { .efi_guid_name = "ebc interpreter",
185 	    .efi_guid = EFI_EBC_INTERPRETER_PROTOCOL_GUID },
186         { .efi_guid_name = "network interface identifier",
187 	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL },
188         { .efi_guid_name = "network interface identifier_31",
189 	    .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31 },
190         { .efi_guid_name = "managed network service binding",
191 	    .efi_guid = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID },
192         { .efi_guid_name = "managed network",
193 	    .efi_guid = EFI_MANAGED_NETWORK_PROTOCOL_GUID },
194         { .efi_guid_name = "form browser",
195 	    .efi_guid = EFI_FORM_BROWSER2_PROTOCOL_GUID },
196         { .efi_guid_name = "HII config routing",
197 	    .efi_guid = EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID },
198         { .efi_guid_name = "HII database",
199 	    .efi_guid = EFI_HII_DATABASE_PROTOCOL_GUID },
200         { .efi_guid_name = "HII string",
201 	    .efi_guid = EFI_HII_STRING_PROTOCOL_GUID },
202         { .efi_guid_name = "HII image",
203 	    .efi_guid = EFI_HII_IMAGE_PROTOCOL_GUID },
204         { .efi_guid_name = "HII font", .efi_guid = EFI_HII_FONT_PROTOCOL_GUID },
205         { .efi_guid_name = "HII config",
206 	    .efi_guid = EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID },
207         { .efi_guid_name = "MTFTP4 service binding",
208 	    .efi_guid = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID },
209         { .efi_guid_name = "MTFTP4", .efi_guid = EFI_MTFTP4_PROTOCOL_GUID },
210         { .efi_guid_name = "MTFTP6 service binding",
211 	    .efi_guid = EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID },
212         { .efi_guid_name = "MTFTP6", .efi_guid = EFI_MTFTP6_PROTOCOL_GUID },
213         { .efi_guid_name = "DHCP4 service binding",
214 	    .efi_guid = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID },
215         { .efi_guid_name = "DHCP4", .efi_guid = EFI_DHCP4_PROTOCOL_GUID },
216         { .efi_guid_name = "DHCP6 service binding",
217 	    .efi_guid = EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID },
218         { .efi_guid_name = "DHCP6", .efi_guid = EFI_DHCP6_PROTOCOL_GUID },
219         { .efi_guid_name = "SCSI io", .efi_guid = EFI_SCSI_IO_PROTOCOL_GUID },
220         { .efi_guid_name = "SCSI pass thru",
221 	    .efi_guid = EFI_SCSI_PASS_THRU_PROTOCOL_GUID },
222         { .efi_guid_name = "SCSI pass thru ext",
223 	    .efi_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID },
224         { .efi_guid_name = "Capsule arch",
225 	    .efi_guid = EFI_CAPSULE_ARCH_PROTOCOL_GUID },
226         { .efi_guid_name = "monotonic counter arch",
227 	    .efi_guid = EFI_MONOTONIC_COUNTER_ARCH_PROTOCOL_GUID },
228         { .efi_guid_name = "realtime clock arch",
229 	    .efi_guid = EFI_REALTIME_CLOCK_ARCH_PROTOCOL_GUID },
230         { .efi_guid_name = "variable arch",
231 	    .efi_guid = EFI_VARIABLE_ARCH_PROTOCOL_GUID },
232         { .efi_guid_name = "variable write arch",
233 	    .efi_guid = EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID },
234         { .efi_guid_name = "watchdog timer arch",
235 	    .efi_guid = EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID },
236         { .efi_guid_name = "ACPI support",
237 	    .efi_guid = EFI_ACPI_SUPPORT_PROTOCOL_GUID },
238         { .efi_guid_name = "BDS arch", .efi_guid = EFI_BDS_ARCH_PROTOCOL_GUID },
239         { .efi_guid_name = "metronome arch",
240 	    .efi_guid = EFI_METRONOME_ARCH_PROTOCOL_GUID },
241         { .efi_guid_name = "timer arch",
242 	    .efi_guid = EFI_TIMER_ARCH_PROTOCOL_GUID },
243         { .efi_guid_name = "DPC", .efi_guid = EFI_DPC_PROTOCOL_GUID },
244         { .efi_guid_name = "print2", .efi_guid = EFI_PRINT2_PROTOCOL_GUID },
245         { .efi_guid_name = "device path to text",
246 	    .efi_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID },
247         { .efi_guid_name = "reset arch",
248 	    .efi_guid = EFI_RESET_ARCH_PROTOCOL_GUID },
249         { .efi_guid_name = "CPU arch", .efi_guid = EFI_CPU_ARCH_PROTOCOL_GUID },
250         { .efi_guid_name = "CPU IO2", .efi_guid = EFI_CPU_IO2_PROTOCOL_GUID },
251         { .efi_guid_name = "Legacy 8259",
252 	    .efi_guid = EFI_LEGACY_8259_PROTOCOL_GUID },
253         { .efi_guid_name = "Security arch",
254 	    .efi_guid = EFI_SECURITY_ARCH_PROTOCOL_GUID },
255         { .efi_guid_name = "Security2 arch",
256 	    .efi_guid = EFI_SECURITY2_ARCH_PROTOCOL_GUID },
257         { .efi_guid_name = "Runtime arch",
258 	    .efi_guid = EFI_RUNTIME_ARCH_PROTOCOL_GUID },
259         { .efi_guid_name = "status code runtime",
260 	    .efi_guid = EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID },
261         { .efi_guid_name = "data hub", .efi_guid = EFI_DATA_HUB_PROTOCOL_GUID },
262         { .efi_guid_name = "PCD", .efi_guid = PCD_PROTOCOL_GUID },
263         { .efi_guid_name = "EFI PCD", .efi_guid = EFI_PCD_PROTOCOL_GUID },
264         { .efi_guid_name = "firmware volume block",
265 	    .efi_guid = EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID },
266         { .efi_guid_name = "firmware volume2",
267 	    .efi_guid = EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID },
268         { .efi_guid_name = "firmware volume dispatch",
269 	    .efi_guid = EFI_FIRMWARE_VOLUME_DISPATCH_PROTOCOL_GUID },
270         { .efi_guid_name = "lzma compress", .efi_guid = LZMA_COMPRESS_GUID },
271         { .efi_guid_name = "MP services",
272 	    .efi_guid = EFI_MP_SERVICES_PROTOCOL_GUID },
273         { .efi_guid_name = MTC_VARIABLE_NAME, .efi_guid = MTC_VENDOR_GUID },
274         { .efi_guid_name = "RTC", .efi_guid = { 0x378D7B65, 0x8DA9, 0x4773,
275 	    { 0xB6, 0xE4, 0xA4, 0x78, 0x26, 0xA8, 0x33, 0xE1} } },
276         { .efi_guid_name = "Active EDID",
277 	    .efi_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID },
278         { .efi_guid_name = "Discovered EDID",
279 	    .efi_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID }
280 };
281 
282 bool
283 efi_guid_to_str(const EFI_GUID *guid, char **sp)
284 {
285 	uint32_t status;
286 
287 	uuid_to_string((const uuid_t *)guid, sp, &status);
288 	return (status == uuid_s_ok ? true : false);
289 }
290 
291 bool
292 efi_str_to_guid(const char *s, EFI_GUID *guid)
293 {
294 	uint32_t status;
295 
296 	uuid_from_string(s, (uuid_t *)guid, &status);
297 	return (status == uuid_s_ok ? true : false);
298 }
299 
300 bool
301 efi_name_to_guid(const char *name, EFI_GUID *guid)
302 {
303 	uint32_t i;
304 
305 	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
306 		if (strcasecmp(name, efi_uuid_mapping[i].efi_guid_name) == 0) {
307 			*guid = efi_uuid_mapping[i].efi_guid;
308 			return (true);
309 		}
310 	}
311 	return (efi_str_to_guid(name, guid));
312 }
313 
314 bool
315 efi_guid_to_name(EFI_GUID *guid, char **name)
316 {
317 	uint32_t i;
318 	int rv;
319 
320 	for (i = 0; i < nitems(efi_uuid_mapping); i++) {
321 		rv = uuid_equal((uuid_t *)guid,
322 		    (uuid_t *)&efi_uuid_mapping[i].efi_guid, NULL);
323 		if (rv != 0) {
324 			*name = strdup(efi_uuid_mapping[i].efi_guid_name);
325 			if (*name == NULL)
326 				return (false);
327 			return (true);
328 		}
329 	}
330 	return (efi_guid_to_str(guid, name));
331 }
332 
333 void
334 efi_init_environment(void)
335 {
336 	char var[128];
337 
338 	snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16,
339 	    ST->Hdr.Revision & 0xffff);
340 	env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset);
341 }
342 
343 COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
344 
345 static int
346 efi_print_other_value(uint8_t *data, UINTN datasz)
347 {
348 	UINTN i;
349 	bool is_ascii = true;
350 
351 	printf(" = ");
352 	for (i = 0; i < datasz - 1; i++) {
353 		/*
354 		 * Quick hack to see if this ascii-ish string is printable
355 		 * range plus tab, cr and lf.
356 		 */
357 		if ((data[i] < 32 || data[i] > 126)
358 		    && data[i] != 9 && data[i] != 10 && data[i] != 13) {
359 			is_ascii = false;
360 			break;
361 		}
362 	}
363 	if (data[datasz - 1] != '\0')
364 		is_ascii = false;
365 	if (is_ascii == true) {
366 		printf("%s", data);
367 		if (pager_output("\n"))
368 			return (CMD_WARN);
369 	} else {
370 		if (pager_output("\n"))
371 			return (CMD_WARN);
372 		/*
373 		 * Dump hex bytes grouped by 4.
374 		 */
375 		for (i = 0; i < datasz; i++) {
376 			printf("%02x ", data[i]);
377 			if ((i + 1) % 4 == 0)
378 				printf(" ");
379 			if ((i + 1) % 20 == 0) {
380 				if (pager_output("\n"))
381 					return (CMD_WARN);
382 			}
383 		}
384 		if (pager_output("\n"))
385 			return (CMD_WARN);
386 	}
387 
388 	return (CMD_OK);
389 }
390 
391 /* This appears to be some sort of UEFI shell alias table. */
392 static int
393 efi_print_shell_str(const CHAR16 *varnamearg __unused, uint8_t *data,
394     UINTN datasz __unused)
395 {
396 	printf(" = %S", (CHAR16 *)data);
397 	if (pager_output("\n"))
398 		return (CMD_WARN);
399 	return (CMD_OK);
400 }
401 
402 const char *
403 efi_memory_type(EFI_MEMORY_TYPE type)
404 {
405 	const char *types[] = {
406 	    "Reserved",
407 	    "LoaderCode",
408 	    "LoaderData",
409 	    "BootServicesCode",
410 	    "BootServicesData",
411 	    "RuntimeServicesCode",
412 	    "RuntimeServicesData",
413 	    "ConventionalMemory",
414 	    "UnusableMemory",
415 	    "ACPIReclaimMemory",
416 	    "ACPIMemoryNVS",
417 	    "MemoryMappedIO",
418 	    "MemoryMappedIOPortSpace",
419 	    "PalCode",
420 	    "PersistentMemory"
421 	};
422 
423 	switch (type) {
424 	case EfiReservedMemoryType:
425 	case EfiLoaderCode:
426 	case EfiLoaderData:
427 	case EfiBootServicesCode:
428 	case EfiBootServicesData:
429 	case EfiRuntimeServicesCode:
430 	case EfiRuntimeServicesData:
431 	case EfiConventionalMemory:
432 	case EfiUnusableMemory:
433 	case EfiACPIReclaimMemory:
434 	case EfiACPIMemoryNVS:
435 	case EfiMemoryMappedIO:
436 	case EfiMemoryMappedIOPortSpace:
437 	case EfiPalCode:
438 	case EfiPersistentMemory:
439 		return (types[type]);
440 	default:
441 		return ("Unknown");
442 	}
443 }
444 
445 /* Print memory type table. */
446 static int
447 efi_print_mem_type(const CHAR16 *varnamearg __unused, uint8_t *data,
448     UINTN datasz)
449 {
450 	int i, n;
451 	EFI_MEMORY_TYPE_INFORMATION *ti;
452 
453 	ti = (EFI_MEMORY_TYPE_INFORMATION *)data;
454 	if (pager_output(" = \n"))
455 		return (CMD_WARN);
456 
457 	n = datasz / sizeof (EFI_MEMORY_TYPE_INFORMATION);
458 	for (i = 0; i < n && ti[i].NumberOfPages != 0; i++) {
459 		printf("\t%23s pages: %u", efi_memory_type(ti[i].Type),
460 		    ti[i].NumberOfPages);
461 		if (pager_output("\n"))
462 			return (CMD_WARN);
463 	}
464 
465 	return (CMD_OK);
466 }
467 
468 /*
469  * Print FreeBSD variables.
470  * We have LoaderPath and LoaderDev as CHAR16 strings.
471  */
472 static int
473 efi_print_freebsd(const CHAR16 *varnamearg, uint8_t *data,
474     UINTN datasz __unused)
475 {
476 	int rv = -1;
477 	char *var = NULL;
478 
479 	if (ucs2_to_utf8(varnamearg, &var) != 0)
480 		return (CMD_ERROR);
481 
482 	if (strcmp("LoaderPath", var) == 0 ||
483 	    strcmp("LoaderDev", var) == 0) {
484 		printf(" = ");
485 		printf("%S", (CHAR16 *)data);
486 
487 		if (pager_output("\n"))
488 			rv = CMD_WARN;
489 		else
490 			rv = CMD_OK;
491 	}
492 
493 	free(var);
494 	return (rv);
495 }
496 
497 /* Print global variables. */
498 static int
499 efi_print_global(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz)
500 {
501 	int rv = -1;
502 	char *var = NULL;
503 
504 	if (ucs2_to_utf8(varnamearg, &var) != 0)
505 		return (CMD_ERROR);
506 
507 	if (strcmp("AuditMode", var) == 0) {
508 		printf(" = ");
509 		printf("0x%x", *data);	/* 8-bit int */
510 		goto done;
511 	}
512 
513 	if (strcmp("BootOptionSupport", var) == 0) {
514 		printf(" = ");
515 		printf("0x%x", *((uint32_t *)data));	/* UINT32 */
516 		goto done;
517 	}
518 
519 	if (strcmp("BootCurrent", var) == 0 ||
520 	    strcmp("BootNext", var) == 0 ||
521 	    strcmp("Timeout", var) == 0) {
522 		printf(" = ");
523 		printf("%u", *((uint16_t *)data));	/* UINT16 */
524 		goto done;
525 	}
526 
527 	if (strcmp("BootOrder", var) == 0 ||
528 	    strcmp("DriverOrder", var) == 0) {
529 		UINTN i;
530 		UINT16 *u16 = (UINT16 *)data;
531 
532 		printf(" =");
533 		for (i = 0; i < datasz / sizeof (UINT16); i++)
534 			printf(" %u", u16[i]);
535 		goto done;
536 	}
537 	if (strncmp("Boot", var, 4) == 0 ||
538 	    strncmp("Driver", var, 6) == 0 ||
539 	    strncmp("SysPrep", var, 7) == 0 ||
540 	    strncmp("OsRecovery", var, 10) == 0) {
541 		UINT16 filepathlistlen;
542 		CHAR16 *text;
543 		int desclen;
544 		EFI_DEVICE_PATH *dp;
545 
546 		data += sizeof(UINT32);
547 		filepathlistlen = *(uint16_t *)data;
548 		data += sizeof (UINT16);
549 		text = (CHAR16 *)data;
550 
551 		for (desclen = 0; text[desclen] != 0; desclen++)
552 			;
553 		if (desclen != 0) {
554 			/* Add terminating zero and we have CHAR16. */
555 			desclen = (desclen + 1) * 2;
556 		}
557 
558 		printf(" = ");
559 		printf("%S", text);
560 		if (filepathlistlen != 0) {
561 			/* Output pathname from new line. */
562 			if (pager_output("\n")) {
563 				rv = CMD_WARN;
564 				goto done;
565 			}
566 			dp = malloc(filepathlistlen);
567 			if (dp == NULL)
568 				goto done;
569 
570 			memcpy(dp, data + desclen, filepathlistlen);
571 			text = efi_devpath_name(dp);
572 			if (text != NULL) {
573 				printf("\t%S", text);
574 				efi_free_devpath_name(text);
575 			}
576 			free(dp);
577 		}
578 		goto done;
579 	}
580 
581 	if (strcmp("ConIn", var) == 0 ||
582 	    strcmp("ConInDev", var) == 0 ||
583 	    strcmp("ConOut", var) == 0 ||
584 	    strcmp("ConOutDev", var) == 0 ||
585 	    strcmp("ErrOut", var) == 0 ||
586 	    strcmp("ErrOutDev", var) == 0) {
587 		CHAR16 *text;
588 
589 		printf(" = ");
590 		text = efi_devpath_name((EFI_DEVICE_PATH *)data);
591 		if (text != NULL) {
592 			printf("%S", text);
593 			efi_free_devpath_name(text);
594 		}
595 		goto done;
596 	}
597 
598 	if (strcmp("PlatformLang", var) == 0 ||
599 	    strcmp("PlatformLangCodes", var) == 0 ||
600 	    strcmp("LangCodes", var) == 0 ||
601 	    strcmp("Lang", var) == 0) {
602 		printf(" = ");
603 		printf("%s", data);	/* ASCII string */
604 		goto done;
605 	}
606 
607 	/*
608 	 * Feature bitmap from firmware to OS.
609 	 * Older UEFI provides UINT32, newer UINT64.
610 	 */
611 	if (strcmp("OsIndicationsSupported", var) == 0) {
612 		printf(" = ");
613 		if (datasz == 4)
614 			printf("0x%x", *((uint32_t *)data));
615 		else
616 			printf("0x%jx", *((uint64_t *)data));
617 		goto done;
618 	}
619 
620 	/* Fallback for anything else. */
621 	rv = efi_print_other_value(data, datasz);
622 done:
623 	if (rv == -1) {
624 		if (pager_output("\n"))
625 			rv = CMD_WARN;
626 		else
627 			rv = CMD_OK;
628 	}
629 	free(var);
630 	return (rv);
631 }
632 
633 static void
634 efi_print_var_attr(UINT32 attr)
635 {
636 	bool comma = false;
637 
638 	if (attr & EFI_VARIABLE_NON_VOLATILE) {
639 		printf("NV");
640 		comma = true;
641 	}
642 	if (attr & EFI_VARIABLE_BOOTSERVICE_ACCESS) {
643 		if (comma == true)
644 			printf(",");
645 		printf("BS");
646 		comma = true;
647 	}
648 	if (attr & EFI_VARIABLE_RUNTIME_ACCESS) {
649 		if (comma == true)
650 			printf(",");
651 		printf("RS");
652 		comma = true;
653 	}
654 	if (attr & EFI_VARIABLE_HARDWARE_ERROR_RECORD) {
655 		if (comma == true)
656 			printf(",");
657 		printf("HR");
658 		comma = true;
659 	}
660 	if (attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
661 		if (comma == true)
662 			printf(",");
663 		printf("AT");
664 		comma = true;
665 	}
666 }
667 
668 static int
669 efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag)
670 {
671 	UINTN		datasz;
672 	EFI_STATUS	status;
673 	UINT32		attr;
674 	char		*str;
675 	uint8_t		*data;
676 	int		rv = CMD_OK;
677 
678 	str = NULL;
679 	datasz = 0;
680 	status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, NULL);
681 	if (status != EFI_BUFFER_TOO_SMALL) {
682 		printf("Can't get the variable: error %#lx\n",
683 		    EFI_ERROR_CODE(status));
684 		return (CMD_ERROR);
685 	}
686 	data = malloc(datasz);
687 	if (data == NULL) {
688 		printf("Out of memory\n");
689 		return (CMD_ERROR);
690 	}
691 
692 	status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, data);
693 	if (status != EFI_SUCCESS) {
694 		printf("Can't get the variable: error %#lx\n",
695 		    EFI_ERROR_CODE(status));
696 		free(data);
697 		return (CMD_ERROR);
698 	}
699 
700 	if (efi_guid_to_name(matchguid, &str) == false) {
701 		rv = CMD_ERROR;
702 		goto done;
703 	}
704 	printf("%s ", str);
705 	efi_print_var_attr(attr);
706 	printf(" %S", varnamearg);
707 
708 	if (lflag == 0) {
709 		if (strcmp(str, "global") == 0)
710 			rv = efi_print_global(varnamearg, data, datasz);
711 		else if (strcmp(str, "freebsd") == 0)
712 			rv = efi_print_freebsd(varnamearg, data, datasz);
713 		else if (strcmp(str,
714 		    EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME) == 0)
715 			rv = efi_print_mem_type(varnamearg, data, datasz);
716 		else if (strcmp(str,
717 		    "47c7b227-c42a-11d2-8e57-00a0c969723b") == 0)
718 			rv = efi_print_shell_str(varnamearg, data, datasz);
719 		else if (strcmp(str, MTC_VARIABLE_NAME) == 0) {
720 			printf(" = ");
721 			printf("%u", *((uint32_t *)data));	/* UINT32 */
722 			rv = CMD_OK;
723 			if (pager_output("\n"))
724 				rv = CMD_WARN;
725 		} else
726 			rv = efi_print_other_value(data, datasz);
727 	} else if (pager_output("\n"))
728 		rv =  CMD_WARN;
729 
730 done:
731 	free(str);
732 	free(data);
733 	return (rv);
734 }
735 
736 static int
737 command_efi_show(int argc, char *argv[])
738 {
739 	/*
740 	 * efi-show [-a]
741 	 *	print all the env
742 	 * efi-show -g UUID
743 	 *	print all the env vars tagged with UUID
744 	 * efi-show -v var
745 	 *	search all the env vars and print the ones matching var
746 	 * efi-show -g UUID -v var
747 	 * efi-show UUID var
748 	 *	print all the env vars that match UUID and var
749 	 */
750 	/* NB: We assume EFI_GUID is the same as uuid_t */
751 	int		aflag = 0, gflag = 0, lflag = 0, vflag = 0;
752 	int		ch, rv;
753 	unsigned	i;
754 	EFI_STATUS	status;
755 	EFI_GUID	varguid = ZERO_GUID;
756 	EFI_GUID	matchguid = ZERO_GUID;
757 	CHAR16		*varname;
758 	CHAR16		*newnm;
759 	CHAR16		varnamearg[128];
760 	UINTN		varalloc;
761 	UINTN		varsz;
762 
763 	optind = 1;
764 	optreset = 1;
765 	opterr = 1;
766 
767 	while ((ch = getopt(argc, argv, "ag:lv:")) != -1) {
768 		switch (ch) {
769 		case 'a':
770 			aflag = 1;
771 			break;
772 		case 'g':
773 			gflag = 1;
774 			if (efi_name_to_guid(optarg, &matchguid) == false) {
775 				printf("uuid %s could not be parsed\n", optarg);
776 				return (CMD_ERROR);
777 			}
778 			break;
779 		case 'l':
780 			lflag = 1;
781 			break;
782 		case 'v':
783 			vflag = 1;
784 			if (strlen(optarg) >= nitems(varnamearg)) {
785 				printf("Variable %s is longer than %zu "
786 				    "characters\n", optarg, nitems(varnamearg));
787 				return (CMD_ERROR);
788 			}
789 			cpy8to16(optarg, varnamearg, nitems(varnamearg));
790 			break;
791 		default:
792 			return (CMD_ERROR);
793 		}
794 	}
795 
796 	if (argc == 1)		/* default is -a */
797 		aflag = 1;
798 
799 	if (aflag && (gflag || vflag)) {
800 		printf("-a isn't compatible with -g or -v\n");
801 		return (CMD_ERROR);
802 	}
803 
804 	if (aflag && optind < argc) {
805 		printf("-a doesn't take any args\n");
806 		return (CMD_ERROR);
807 	}
808 
809 	argc -= optind;
810 	argv += optind;
811 
812 	pager_open();
813 	if (vflag && gflag) {
814 		rv = efi_print_var(varnamearg, &matchguid, lflag);
815 		if (rv == CMD_WARN)
816 			rv = CMD_OK;
817 		pager_close();
818 		return (rv);
819 	}
820 
821 	if (argc == 2) {
822 		optarg = argv[0];
823 		if (strlen(optarg) >= nitems(varnamearg)) {
824 			printf("Variable %s is longer than %zu characters\n",
825 			    optarg, nitems(varnamearg));
826 			pager_close();
827 			return (CMD_ERROR);
828 		}
829 		for (i = 0; i < strlen(optarg); i++)
830 			varnamearg[i] = optarg[i];
831 		varnamearg[i] = 0;
832 		optarg = argv[1];
833 		if (efi_name_to_guid(optarg, &matchguid) == false) {
834 			printf("uuid %s could not be parsed\n", optarg);
835 			pager_close();
836 			return (CMD_ERROR);
837 		}
838 		rv = efi_print_var(varnamearg, &matchguid, lflag);
839 		if (rv == CMD_WARN)
840 			rv = CMD_OK;
841 		pager_close();
842 		return (rv);
843 	}
844 
845 	if (argc > 0) {
846 		printf("Too many args: %d\n", argc);
847 		pager_close();
848 		return (CMD_ERROR);
849 	}
850 
851 	/*
852 	 * Initiate the search -- note the standard takes pain
853 	 * to specify the initial call must be a poiner to a NULL
854 	 * character.
855 	 */
856 	varalloc = 1024;
857 	varname = malloc(varalloc);
858 	if (varname == NULL) {
859 		printf("Can't allocate memory to get variables\n");
860 		pager_close();
861 		return (CMD_ERROR);
862 	}
863 	varname[0] = 0;
864 	while (1) {
865 		varsz = varalloc;
866 		status = RS->GetNextVariableName(&varsz, varname, &varguid);
867 		if (status == EFI_BUFFER_TOO_SMALL) {
868 			varalloc = varsz;
869 			newnm = realloc(varname, varalloc);
870 			if (newnm == NULL) {
871 				printf("Can't allocate memory to get "
872 				    "variables\n");
873 				rv = CMD_ERROR;
874 				break;
875 			}
876 			varname = newnm;
877 			continue; /* Try again with bigger buffer */
878 		}
879 		if (status == EFI_NOT_FOUND) {
880 			rv = CMD_OK;
881 			break;
882 		}
883 		if (status != EFI_SUCCESS) {
884 			rv = CMD_ERROR;
885 			break;
886 		}
887 
888 		if (aflag) {
889 			rv = efi_print_var(varname, &varguid, lflag);
890 			if (rv != CMD_OK) {
891 				if (rv == CMD_WARN)
892 					rv = CMD_OK;
893 				break;
894 			}
895 			continue;
896 		}
897 		if (vflag) {
898 			if (wcscmp(varnamearg, varname) == 0) {
899 				rv = efi_print_var(varname, &varguid, lflag);
900 				if (rv != CMD_OK) {
901 					if (rv == CMD_WARN)
902 						rv = CMD_OK;
903 					break;
904 				}
905 				continue;
906 			}
907 		}
908 		if (gflag) {
909 			rv = uuid_equal((uuid_t *)&varguid,
910 			    (uuid_t *)&matchguid, NULL);
911 			if (rv != 0) {
912 				rv = efi_print_var(varname, &varguid, lflag);
913 				if (rv != CMD_OK) {
914 					if (rv == CMD_WARN)
915 						rv = CMD_OK;
916 					break;
917 				}
918 				continue;
919 			}
920 		}
921 	}
922 	free(varname);
923 	pager_close();
924 
925 	return (rv);
926 }
927 
928 COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
929 
930 static int
931 command_efi_set(int argc, char *argv[])
932 {
933 	char *uuid, *var, *val;
934 	CHAR16 wvar[128];
935 	EFI_GUID guid;
936 #if defined(ENABLE_UPDATES)
937 	EFI_STATUS err;
938 #endif
939 
940 	if (argc != 4) {
941 		printf("efi-set uuid var new-value\n");
942 		return (CMD_ERROR);
943 	}
944 	uuid = argv[1];
945 	var = argv[2];
946 	val = argv[3];
947 	if (efi_name_to_guid(uuid, &guid) == false) {
948 		printf("Invalid uuid %s\n", uuid);
949 		return (CMD_ERROR);
950 	}
951 	cpy8to16(var, wvar, nitems(wvar));
952 #if defined(ENABLE_UPDATES)
953 	err = RS->SetVariable(wvar, &guid, EFI_VARIABLE_NON_VOLATILE |
954 	    EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
955 	    strlen(val) + 1, val);
956 	if (EFI_ERROR(err)) {
957 		printf("Failed to set variable: error %lu\n",
958 		    EFI_ERROR_CODE(err));
959 		return (CMD_ERROR);
960 	}
961 #else
962 	printf("would set %s %s = %s\n", uuid, var, val);
963 #endif
964 	return (CMD_OK);
965 }
966 
967 COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
968 
969 static int
970 command_efi_unset(int argc, char *argv[])
971 {
972 	char *uuid, *var;
973 	CHAR16 wvar[128];
974 	EFI_GUID guid;
975 #if defined(ENABLE_UPDATES)
976 	EFI_STATUS err;
977 #endif
978 
979 	if (argc != 3) {
980 		printf("efi-unset uuid var\n");
981 		return (CMD_ERROR);
982 	}
983 	uuid = argv[1];
984 	var = argv[2];
985 	if (efi_name_to_guid(uuid, &guid) == false) {
986 		printf("Invalid uuid %s\n", uuid);
987 		return (CMD_ERROR);
988 	}
989 	cpy8to16(var, wvar, nitems(wvar));
990 #if defined(ENABLE_UPDATES)
991 	err = RS->SetVariable(wvar, &guid, 0, 0, NULL);
992 	if (EFI_ERROR(err)) {
993 		printf("Failed to unset variable: error %lu\n",
994 		    EFI_ERROR_CODE(err));
995 		return (CMD_ERROR);
996 	}
997 #else
998 	printf("would unset %s %s \n", uuid, var);
999 #endif
1000 	return (CMD_OK);
1001 }
1002