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