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