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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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