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