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