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 __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, 6) == 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 } 1004