env.c revision 346482
1/*
2 * Copyright (c) 2015 Netflix, Inc. All Rights Reserved.
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: stable/11/stand/efi/libefi/env.c 346482 2019-04-21 04:26:02Z kevans $");
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
63static 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
284bool
285efi_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
293bool
294efi_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
302bool
303efi_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
316bool
317efi_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
335void
336efi_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
345COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
346
347static int
348efi_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. */
394static int
395efi_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
404const char *
405efi_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. */
448static int
449efi_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 */
474static int
475efi_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. */
500static int
501efi_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);
624done:
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
635static void
636efi_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
670static int
671efi_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
732done:
733	free(str);
734	free(data);
735	return (rv);
736}
737
738static int
739command_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
930COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
931
932static int
933command_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
969COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
970
971static int
972command_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