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 } 1002