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