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
60static 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
281bool
282efi_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
290bool
291efi_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
299bool
300efi_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
313bool
314efi_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
332void
333efi_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
342COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show);
343
344static int
345efi_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. */
391static int
392efi_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
401const char *
402efi_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. */
445static int
446efi_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 */
471static int
472efi_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. */
497static int
498efi_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);
621done:
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
632static void
633efi_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
667static int
668efi_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
729done:
730	free(str);
731	free(data);
732	return (rv);
733}
734
735static int
736command_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
927COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set);
928
929static int
930command_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
966COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset);
967
968static int
969command_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