| /* |
| * Copyright (c) 2015 Netflix, Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/cdefs.h> |
| |
| #include <stand.h> |
| #include <string.h> |
| #include <efi.h> |
| #include <efichar.h> |
| #include <efilib.h> |
| #include <efigpt.h> /* Partition GUIDS */ |
| #include <Guid/MemoryTypeInformation.h> |
| #include <Guid/MtcVendor.h> |
| #include <Guid/ZeroGuid.h> |
| #include <Protocol/EdidActive.h> |
| #include <Protocol/EdidDiscovered.h> |
| #include <uuid.h> |
| #include <stdbool.h> |
| #include <sys/param.h> |
| #include "bootstrap.h" |
| #include "ficl.h" |
| |
| static struct efi_uuid_mapping { |
| const char *efi_guid_name; |
| EFI_GUID efi_guid; |
| } efi_uuid_mapping[] = { |
| { .efi_guid_name = "global", .efi_guid = EFI_GLOBAL_VARIABLE }, |
| { .efi_guid_name = "illumos", .efi_guid = ILLUMOS_BOOT_VAR_GUID }, |
| /* EFI Systab entry names. */ |
| { .efi_guid_name = "MPS Table", .efi_guid = MPS_TABLE_GUID }, |
| { .efi_guid_name = "ACPI Table", .efi_guid = ACPI_TABLE_GUID }, |
| { .efi_guid_name = "ACPI 2.0 Table", .efi_guid = ACPI_20_TABLE_GUID }, |
| { .efi_guid_name = "SMBIOS Table", .efi_guid = SMBIOS_TABLE_GUID }, |
| { .efi_guid_name = "SMBIOS3 Table", .efi_guid = SMBIOS3_TABLE_GUID }, |
| { .efi_guid_name = "DXE Table", .efi_guid = DXE_SERVICES_TABLE_GUID }, |
| { .efi_guid_name = "HOB List Table", .efi_guid = HOB_LIST_TABLE_GUID }, |
| { .efi_guid_name = EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME, |
| .efi_guid = EFI_MEMORY_TYPE_INFORMATION_GUID }, |
| { .efi_guid_name = "Debug Image Info Table", |
| .efi_guid = DEBUG_IMAGE_INFO_TABLE_GUID }, |
| { .efi_guid_name = "FDT Table", .efi_guid = FDT_TABLE_GUID }, |
| /* |
| * Protocol names for debug purposes. |
| * Can be removed along with lsefi command. |
| */ |
| { .efi_guid_name = "device path", .efi_guid = DEVICE_PATH_PROTOCOL }, |
| { .efi_guid_name = "block io", .efi_guid = BLOCK_IO_PROTOCOL }, |
| { .efi_guid_name = "disk io", .efi_guid = DISK_IO_PROTOCOL }, |
| { .efi_guid_name = "disk info", .efi_guid = |
| EFI_DISK_INFO_PROTOCOL_GUID }, |
| { .efi_guid_name = "simple fs", |
| .efi_guid = SIMPLE_FILE_SYSTEM_PROTOCOL }, |
| { .efi_guid_name = "load file", .efi_guid = LOAD_FILE_PROTOCOL }, |
| { .efi_guid_name = "device io", .efi_guid = DEVICE_IO_PROTOCOL }, |
| { .efi_guid_name = "unicode collation", |
| .efi_guid = UNICODE_COLLATION_PROTOCOL }, |
| { .efi_guid_name = "unicode collation2", |
| .efi_guid = EFI_UNICODE_COLLATION2_PROTOCOL_GUID }, |
| { .efi_guid_name = "simple network", |
| .efi_guid = EFI_SIMPLE_NETWORK_PROTOCOL }, |
| { .efi_guid_name = "simple text output", |
| .efi_guid = SIMPLE_TEXT_OUTPUT_PROTOCOL }, |
| { .efi_guid_name = "simple text input", |
| .efi_guid = SIMPLE_TEXT_INPUT_PROTOCOL }, |
| { .efi_guid_name = "simple text ex input", |
| .efi_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID }, |
| { .efi_guid_name = "console control", |
| .efi_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID }, |
| { .efi_guid_name = "stdin", .efi_guid = EFI_CONSOLE_IN_DEVICE_GUID }, |
| { .efi_guid_name = "stdout", .efi_guid = EFI_CONSOLE_OUT_DEVICE_GUID }, |
| { .efi_guid_name = "stderr", |
| .efi_guid = EFI_STANDARD_ERROR_DEVICE_GUID }, |
| { .efi_guid_name = "GOP", |
| .efi_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID }, |
| { .efi_guid_name = "UGA draw", .efi_guid = EFI_UGA_DRAW_PROTOCOL_GUID }, |
| { .efi_guid_name = "PXE base code", |
| .efi_guid = EFI_PXE_BASE_CODE_PROTOCOL }, |
| { .efi_guid_name = "PXE base code callback", |
| .efi_guid = EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL }, |
| { .efi_guid_name = "serial io", .efi_guid = SERIAL_IO_PROTOCOL }, |
| { .efi_guid_name = "loaded image", .efi_guid = LOADED_IMAGE_PROTOCOL }, |
| { .efi_guid_name = "loaded image device path", |
| .efi_guid = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID }, |
| { .efi_guid_name = "ISA io", .efi_guid = EFI_ISA_IO_PROTOCOL_GUID }, |
| { .efi_guid_name = "IDE controller init", |
| .efi_guid = EFI_IDE_CONTROLLER_INIT_PROTOCOL_GUID }, |
| { .efi_guid_name = "ISA ACPI", .efi_guid = EFI_ISA_ACPI_PROTOCOL_GUID }, |
| { .efi_guid_name = "PCI", .efi_guid = EFI_PCI_IO_PROTOCOL_GUID }, |
| { .efi_guid_name = "PCI root", .efi_guid = EFI_PCI_ROOT_IO_GUID }, |
| { .efi_guid_name = "PCI enumeration", |
| .efi_guid = EFI_PCI_ENUMERATION_COMPLETE_GUID }, |
| { .efi_guid_name = "Driver diagnostics", |
| .efi_guid = EFI_DRIVER_DIAGNOSTICS_PROTOCOL_GUID }, |
| { .efi_guid_name = "Driver diagnostics2", |
| .efi_guid = EFI_DRIVER_DIAGNOSTICS2_PROTOCOL_GUID }, |
| { .efi_guid_name = "simple pointer", |
| .efi_guid = EFI_SIMPLE_POINTER_PROTOCOL_GUID }, |
| { .efi_guid_name = "absolute pointer", |
| .efi_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID }, |
| { .efi_guid_name = "VLAN config", |
| .efi_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID }, |
| { .efi_guid_name = "ARP service binding", |
| .efi_guid = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "ARP", .efi_guid = EFI_ARP_PROTOCOL_GUID }, |
| { .efi_guid_name = "IPv4 service binding", |
| .efi_guid = EFI_IP4_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "IPv4", .efi_guid = EFI_IP4_PROTOCOL }, |
| { .efi_guid_name = "IPv4 config", |
| .efi_guid = EFI_IP4_CONFIG_PROTOCOL_GUID }, |
| { .efi_guid_name = "IPv6 service binding", |
| .efi_guid = EFI_IP6_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "IPv6", .efi_guid = EFI_IP6_PROTOCOL }, |
| { .efi_guid_name = "IPv6 config", |
| .efi_guid = EFI_IP6_CONFIG_PROTOCOL_GUID }, |
| { .efi_guid_name = "UDPv4", .efi_guid = EFI_UDP4_PROTOCOL }, |
| { .efi_guid_name = "UDPv4 service binding", |
| .efi_guid = EFI_UDP4_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "UDPv6", .efi_guid = EFI_UDP6_PROTOCOL }, |
| { .efi_guid_name = "UDPv6 service binding", |
| .efi_guid = EFI_UDP6_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "TCPv4", .efi_guid = EFI_TCP4_PROTOCOL }, |
| { .efi_guid_name = "TCPv4 service binding", |
| .efi_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "TCPv6", .efi_guid = EFI_TCP6_PROTOCOL }, |
| { .efi_guid_name = "TCPv6 service binding", |
| .efi_guid = EFI_TCP6_SERVICE_BINDING_PROTOCOL }, |
| { .efi_guid_name = "EFI System partition", |
| .efi_guid = EFI_PART_TYPE_EFI_SYSTEM_PART_GUID }, |
| { .efi_guid_name = "MBR legacy", |
| .efi_guid = EFI_PART_TYPE_LEGACY_MBR_GUID }, |
| { .efi_guid_name = "device tree", .efi_guid = EFI_DEVICE_TREE_GUID }, |
| { .efi_guid_name = "USB io", .efi_guid = EFI_USB_IO_PROTOCOL_GUID }, |
| { .efi_guid_name = "USB2 HC", .efi_guid = EFI_USB2_HC_PROTOCOL_GUID }, |
| { .efi_guid_name = "component name", |
| .efi_guid = EFI_COMPONENT_NAME_PROTOCOL_GUID }, |
| { .efi_guid_name = "component name2", |
| .efi_guid = EFI_COMPONENT_NAME2_PROTOCOL_GUID }, |
| { .efi_guid_name = "driver binding", |
| .efi_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "driver configuration", |
| .efi_guid = EFI_DRIVER_CONFIGURATION_PROTOCOL_GUID }, |
| { .efi_guid_name = "driver configuration2", |
| .efi_guid = EFI_DRIVER_CONFIGURATION2_PROTOCOL_GUID }, |
| { .efi_guid_name = "decompress", |
| .efi_guid = EFI_DECOMPRESS_PROTOCOL_GUID }, |
| { .efi_guid_name = "ebc interpreter", |
| .efi_guid = EFI_EBC_INTERPRETER_PROTOCOL_GUID }, |
| { .efi_guid_name = "network interface identifier", |
| .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL }, |
| { .efi_guid_name = "network interface identifier_31", |
| .efi_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_31 }, |
| { .efi_guid_name = "managed network service binding", |
| .efi_guid = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "managed network", |
| .efi_guid = EFI_MANAGED_NETWORK_PROTOCOL_GUID }, |
| { .efi_guid_name = "form browser", |
| .efi_guid = EFI_FORM_BROWSER2_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII config routing", |
| .efi_guid = EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII database", |
| .efi_guid = EFI_HII_DATABASE_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII string", |
| .efi_guid = EFI_HII_STRING_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII image", |
| .efi_guid = EFI_HII_IMAGE_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII font", .efi_guid = EFI_HII_FONT_PROTOCOL_GUID }, |
| { .efi_guid_name = "HII config", |
| .efi_guid = EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID }, |
| { .efi_guid_name = "MTFTP4 service binding", |
| .efi_guid = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "MTFTP4", .efi_guid = EFI_MTFTP4_PROTOCOL_GUID }, |
| { .efi_guid_name = "MTFTP6 service binding", |
| .efi_guid = EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "MTFTP6", .efi_guid = EFI_MTFTP6_PROTOCOL_GUID }, |
| { .efi_guid_name = "DHCP4 service binding", |
| .efi_guid = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "DHCP4", .efi_guid = EFI_DHCP4_PROTOCOL_GUID }, |
| { .efi_guid_name = "DHCP6 service binding", |
| .efi_guid = EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID }, |
| { .efi_guid_name = "DHCP6", .efi_guid = EFI_DHCP6_PROTOCOL_GUID }, |
| { .efi_guid_name = "SCSI io", .efi_guid = EFI_SCSI_IO_PROTOCOL_GUID }, |
| { .efi_guid_name = "SCSI pass thru", |
| .efi_guid = EFI_SCSI_PASS_THRU_PROTOCOL_GUID }, |
| { .efi_guid_name = "SCSI pass thru ext", |
| .efi_guid = EFI_EXT_SCSI_PASS_THRU_PROTOCOL_GUID }, |
| { .efi_guid_name = "Capsule arch", |
| .efi_guid = EFI_CAPSULE_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "monotonic counter arch", |
| .efi_guid = EFI_MONOTONIC_COUNTER_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "realtime clock arch", |
| .efi_guid = EFI_REALTIME_CLOCK_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "variable arch", |
| .efi_guid = EFI_VARIABLE_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "variable write arch", |
| .efi_guid = EFI_VARIABLE_WRITE_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "watchdog timer arch", |
| .efi_guid = EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "ACPI support", |
| .efi_guid = EFI_ACPI_SUPPORT_PROTOCOL_GUID }, |
| { .efi_guid_name = "BDS arch", .efi_guid = EFI_BDS_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "metronome arch", |
| .efi_guid = EFI_METRONOME_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "timer arch", |
| .efi_guid = EFI_TIMER_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "DPC", .efi_guid = EFI_DPC_PROTOCOL_GUID }, |
| { .efi_guid_name = "print2", .efi_guid = EFI_PRINT2_PROTOCOL_GUID }, |
| { .efi_guid_name = "device path to text", |
| .efi_guid = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID }, |
| { .efi_guid_name = "reset arch", |
| .efi_guid = EFI_RESET_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "CPU arch", .efi_guid = EFI_CPU_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "CPU IO2", .efi_guid = EFI_CPU_IO2_PROTOCOL_GUID }, |
| { .efi_guid_name = "Legacy 8259", |
| .efi_guid = EFI_LEGACY_8259_PROTOCOL_GUID }, |
| { .efi_guid_name = "Security arch", |
| .efi_guid = EFI_SECURITY_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "Security2 arch", |
| .efi_guid = EFI_SECURITY2_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "Runtime arch", |
| .efi_guid = EFI_RUNTIME_ARCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "status code runtime", |
| .efi_guid = EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID }, |
| { .efi_guid_name = "data hub", .efi_guid = EFI_DATA_HUB_PROTOCOL_GUID }, |
| { .efi_guid_name = "PCD", .efi_guid = PCD_PROTOCOL_GUID }, |
| { .efi_guid_name = "EFI PCD", .efi_guid = EFI_PCD_PROTOCOL_GUID }, |
| { .efi_guid_name = "firmware volume block", |
| .efi_guid = EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID }, |
| { .efi_guid_name = "firmware volume2", |
| .efi_guid = EFI_FIRMWARE_VOLUME2_PROTOCOL_GUID }, |
| { .efi_guid_name = "firmware volume dispatch", |
| .efi_guid = EFI_FIRMWARE_VOLUME_DISPATCH_PROTOCOL_GUID }, |
| { .efi_guid_name = "lzma compress", .efi_guid = LZMA_COMPRESS_GUID }, |
| { .efi_guid_name = "MP services", |
| .efi_guid = EFI_MP_SERVICES_PROTOCOL_GUID }, |
| { .efi_guid_name = MTC_VARIABLE_NAME, .efi_guid = MTC_VENDOR_GUID }, |
| { .efi_guid_name = "RTC", .efi_guid = { 0x378D7B65, 0x8DA9, 0x4773, |
| { 0xB6, 0xE4, 0xA4, 0x78, 0x26, 0xA8, 0x33, 0xE1} } }, |
| { .efi_guid_name = "Active EDID", |
| .efi_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID }, |
| { .efi_guid_name = "Discovered EDID", |
| .efi_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID } |
| }; |
| |
| bool |
| efi_guid_to_str(const EFI_GUID *guid, char **sp) |
| { |
| uint32_t status; |
| |
| uuid_to_string((const uuid_t *)guid, sp, &status); |
| return (status == uuid_s_ok ? true : false); |
| } |
| |
| bool |
| efi_str_to_guid(const char *s, EFI_GUID *guid) |
| { |
| uint32_t status; |
| |
| uuid_from_string(s, (uuid_t *)guid, &status); |
| return (status == uuid_s_ok ? true : false); |
| } |
| |
| bool |
| efi_name_to_guid(const char *name, EFI_GUID *guid) |
| { |
| uint32_t i; |
| |
| for (i = 0; i < nitems(efi_uuid_mapping); i++) { |
| if (strcasecmp(name, efi_uuid_mapping[i].efi_guid_name) == 0) { |
| *guid = efi_uuid_mapping[i].efi_guid; |
| return (true); |
| } |
| } |
| return (efi_str_to_guid(name, guid)); |
| } |
| |
| bool |
| efi_guid_to_name(EFI_GUID *guid, char **name) |
| { |
| uint32_t i; |
| int rv; |
| |
| for (i = 0; i < nitems(efi_uuid_mapping); i++) { |
| rv = uuid_equal((uuid_t *)guid, |
| (uuid_t *)&efi_uuid_mapping[i].efi_guid, NULL); |
| if (rv != 0) { |
| *name = strdup(efi_uuid_mapping[i].efi_guid_name); |
| if (*name == NULL) |
| return (false); |
| return (true); |
| } |
| } |
| return (efi_guid_to_str(guid, name)); |
| } |
| |
| void |
| efi_init_environment(void) |
| { |
| char var[128]; |
| |
| snprintf(var, sizeof(var), "%d.%02d", ST->Hdr.Revision >> 16, |
| ST->Hdr.Revision & 0xffff); |
| env_setenv("efi-version", EV_VOLATILE, var, env_noset, env_nounset); |
| } |
| |
| COMMAND_SET(efishow, "efi-show", "print some or all EFI variables", command_efi_show); |
| |
| static int |
| efi_print_other_value(uint8_t *data, UINTN datasz) |
| { |
| UINTN i; |
| bool is_ascii = true; |
| |
| printf(" = "); |
| for (i = 0; i < datasz - 1; i++) { |
| /* |
| * Quick hack to see if this ascii-ish string is printable |
| * range plus tab, cr and lf. |
| */ |
| if ((data[i] < 32 || data[i] > 126) |
| && data[i] != 9 && data[i] != 10 && data[i] != 13) { |
| is_ascii = false; |
| break; |
| } |
| } |
| if (data[datasz - 1] != '\0') |
| is_ascii = false; |
| if (is_ascii == true) { |
| printf("%s", data); |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| } else { |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| /* |
| * Dump hex bytes grouped by 4. |
| */ |
| for (i = 0; i < datasz; i++) { |
| printf("%02x ", data[i]); |
| if ((i + 1) % 4 == 0) |
| printf(" "); |
| if ((i + 1) % 20 == 0) { |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| } |
| } |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| } |
| |
| return (CMD_OK); |
| } |
| |
| /* This appears to be some sort of UEFI shell alias table. */ |
| static int |
| efi_print_shell_str(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz) |
| { |
| printf(" = %S", (CHAR16 *)data); |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| return (CMD_OK); |
| } |
| |
| const char * |
| efi_memory_type(EFI_MEMORY_TYPE type) |
| { |
| const char *types[] = { |
| "Reserved", |
| "LoaderCode", |
| "LoaderData", |
| "BootServicesCode", |
| "BootServicesData", |
| "RuntimeServicesCode", |
| "RuntimeServicesData", |
| "ConventionalMemory", |
| "UnusableMemory", |
| "ACPIReclaimMemory", |
| "ACPIMemoryNVS", |
| "MemoryMappedIO", |
| "MemoryMappedIOPortSpace", |
| "PalCode", |
| "PersistentMemory" |
| }; |
| |
| switch (type) { |
| case EfiReservedMemoryType: |
| case EfiLoaderCode: |
| case EfiLoaderData: |
| case EfiBootServicesCode: |
| case EfiBootServicesData: |
| case EfiRuntimeServicesCode: |
| case EfiRuntimeServicesData: |
| case EfiConventionalMemory: |
| case EfiUnusableMemory: |
| case EfiACPIReclaimMemory: |
| case EfiACPIMemoryNVS: |
| case EfiMemoryMappedIO: |
| case EfiMemoryMappedIOPortSpace: |
| case EfiPalCode: |
| case EfiPersistentMemory: |
| return (types[type]); |
| default: |
| return ("Unknown"); |
| } |
| } |
| |
| /* Print memory type table. */ |
| static int |
| efi_print_mem_type(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz) |
| { |
| int i, n; |
| EFI_MEMORY_TYPE_INFORMATION *ti; |
| |
| ti = (EFI_MEMORY_TYPE_INFORMATION *)data; |
| if (pager_output(" = \n")) |
| return (CMD_WARN); |
| |
| n = datasz / sizeof (EFI_MEMORY_TYPE_INFORMATION); |
| for (i = 0; i < n && ti[i].NumberOfPages != 0; i++) { |
| printf("\t%23s pages: %u", efi_memory_type(ti[i].Type), |
| ti[i].NumberOfPages); |
| if (pager_output("\n")) |
| return (CMD_WARN); |
| } |
| |
| return (CMD_OK); |
| } |
| |
| /* |
| * Print illumos variables. |
| * We have LoaderPath and LoaderDev as CHAR16 strings. |
| */ |
| static int |
| efi_print_illumos(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz) |
| { |
| int rv = -1; |
| char *var = NULL; |
| |
| if (ucs2_to_utf8(varnamearg, &var) != 0) |
| return (CMD_ERROR); |
| |
| if (strcmp("LoaderPath", var) == 0 || |
| strcmp("LoaderDev", var) == 0) { |
| printf(" = "); |
| printf("%S", (CHAR16 *)data); |
| |
| if (pager_output("\n")) |
| rv = CMD_WARN; |
| else |
| rv = CMD_OK; |
| } |
| |
| free(var); |
| return (rv); |
| } |
| |
| /* Print global variables. */ |
| static int |
| efi_print_global(const CHAR16 *varnamearg, uint8_t *data, UINTN datasz) |
| { |
| int rv = -1; |
| char *var = NULL; |
| |
| if (ucs2_to_utf8(varnamearg, &var) != 0) |
| return (CMD_ERROR); |
| |
| if (strcmp("AuditMode", var) == 0) { |
| printf(" = "); |
| printf("0x%x", *data); /* 8-bit int */ |
| goto done; |
| } |
| |
| if (strcmp("BootOptionSupport", var) == 0) { |
| printf(" = "); |
| printf("0x%x", *((uint32_t *)data)); /* UINT32 */ |
| goto done; |
| } |
| |
| if (strcmp("BootCurrent", var) == 0 || |
| strcmp("BootNext", var) == 0 || |
| strcmp("Timeout", var) == 0) { |
| printf(" = "); |
| printf("%u", *((uint16_t *)data)); /* UINT16 */ |
| goto done; |
| } |
| |
| if (strcmp("BootOrder", var) == 0 || |
| strcmp("DriverOrder", var) == 0) { |
| int i; |
| UINT16 *u16 = (UINT16 *)data; |
| |
| printf(" ="); |
| for (i = 0; i < datasz / sizeof (UINT16); i++) |
| printf(" %u", u16[i]); |
| goto done; |
| } |
| if (strncmp("Boot", var, 4) == 0 || |
| strncmp("Driver", var, 5) == 0 || |
| strncmp("SysPrep", var, 7) == 0 || |
| strncmp("OsRecovery", var, 10) == 0) { |
| UINT16 filepathlistlen; |
| CHAR16 *text; |
| int desclen; |
| EFI_DEVICE_PATH *dp; |
| |
| data += sizeof(UINT32); |
| filepathlistlen = *(uint16_t *)data; |
| data += sizeof (UINT16); |
| text = (CHAR16 *)data; |
| |
| for (desclen = 0; text[desclen] != 0; desclen++) |
| ; |
| if (desclen != 0) { |
| /* Add terminating zero and we have CHAR16. */ |
| desclen = (desclen + 1) * 2; |
| } |
| |
| printf(" = "); |
| printf("%S", text); |
| if (filepathlistlen != 0) { |
| /* Output pathname from new line. */ |
| if (pager_output("\n")) { |
| rv = CMD_WARN; |
| goto done; |
| } |
| dp = malloc(filepathlistlen); |
| if (dp == NULL) |
| goto done; |
| |
| memcpy(dp, data + desclen, filepathlistlen); |
| text = efi_devpath_name(dp); |
| if (text != NULL) { |
| printf("\t%S", text); |
| efi_free_devpath_name(text); |
| } |
| free(dp); |
| } |
| goto done; |
| } |
| |
| if (strcmp("ConIn", var) == 0 || |
| strcmp("ConInDev", var) == 0 || |
| strcmp("ConOut", var) == 0 || |
| strcmp("ConOutDev", var) == 0 || |
| strcmp("ErrOut", var) == 0 || |
| strcmp("ErrOutDev", var) == 0) { |
| CHAR16 *text; |
| |
| printf(" = "); |
| text = efi_devpath_name((EFI_DEVICE_PATH *)data); |
| if (text != NULL) { |
| printf("%S", text); |
| efi_free_devpath_name(text); |
| } |
| goto done; |
| } |
| |
| if (strcmp("PlatformLang", var) == 0 || |
| strcmp("PlatformLangCodes", var) == 0 || |
| strcmp("LangCodes", var) == 0 || |
| strcmp("Lang", var) == 0) { |
| printf(" = "); |
| printf("%s", data); /* ASCII string */ |
| goto done; |
| } |
| |
| /* |
| * Feature bitmap from firmware to OS. |
| * Older UEFI provides UINT32, newer UINT64. |
| */ |
| if (strcmp("OsIndicationsSupported", var) == 0) { |
| printf(" = "); |
| if (datasz == 4) |
| printf("0x%x", *((uint32_t *)data)); |
| else |
| printf("0x%jx", *((uint64_t *)data)); |
| goto done; |
| } |
| |
| /* Fallback for anything else. */ |
| rv = efi_print_other_value(data, datasz); |
| done: |
| if (rv == -1) { |
| if (pager_output("\n")) |
| rv = CMD_WARN; |
| else |
| rv = CMD_OK; |
| } |
| free(var); |
| return (rv); |
| } |
| |
| static void |
| efi_print_var_attr(UINT32 attr) |
| { |
| bool comma = false; |
| |
| if (attr & EFI_VARIABLE_NON_VOLATILE) { |
| printf("NV"); |
| comma = true; |
| } |
| if (attr & EFI_VARIABLE_BOOTSERVICE_ACCESS) { |
| if (comma == true) |
| printf(","); |
| printf("BS"); |
| comma = true; |
| } |
| if (attr & EFI_VARIABLE_RUNTIME_ACCESS) { |
| if (comma == true) |
| printf(","); |
| printf("RS"); |
| comma = true; |
| } |
| if (attr & EFI_VARIABLE_HARDWARE_ERROR_RECORD) { |
| if (comma == true) |
| printf(","); |
| printf("HR"); |
| comma = true; |
| } |
| if (attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { |
| if (comma == true) |
| printf(","); |
| printf("AT"); |
| comma = true; |
| } |
| } |
| |
| static int |
| efi_print_var(CHAR16 *varnamearg, EFI_GUID *matchguid, int lflag) |
| { |
| UINTN datasz; |
| EFI_STATUS status; |
| UINT32 attr; |
| char *str, *data; |
| int rv = CMD_OK; |
| |
| str = NULL; |
| datasz = 0; |
| status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, NULL); |
| if (status != EFI_BUFFER_TOO_SMALL) { |
| printf("Can't get the variable: error %#lx\n", |
| EFI_ERROR_CODE(status)); |
| return (CMD_ERROR); |
| } |
| data = malloc(datasz); |
| if (data == NULL) { |
| printf("Out of memory\n"); |
| return (CMD_ERROR); |
| } |
| |
| status = RS->GetVariable(varnamearg, matchguid, &attr, &datasz, data); |
| if (status != EFI_SUCCESS) { |
| printf("Can't get the variable: error %#lx\n", |
| EFI_ERROR_CODE(status)); |
| free(data); |
| return (CMD_ERROR); |
| } |
| |
| if (efi_guid_to_name(matchguid, &str) == false) { |
| rv = CMD_ERROR; |
| goto done; |
| } |
| printf("%s ", str); |
| efi_print_var_attr(attr); |
| printf(" %S", varnamearg); |
| |
| if (lflag == 0) { |
| if (strcmp(str, "global") == 0) |
| rv = efi_print_global(varnamearg, data, datasz); |
| else if (strcmp(str, "illumos") == 0) |
| rv = efi_print_illumos(varnamearg, data, datasz); |
| else if (strcmp(str, |
| EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME) == 0) |
| rv = efi_print_mem_type(varnamearg, data, datasz); |
| else if (strcmp(str, |
| "47c7b227-c42a-11d2-8e57-00a0c969723b") == 0) |
| rv = efi_print_shell_str(varnamearg, data, datasz); |
| else if (strcmp(str, MTC_VARIABLE_NAME) == 0) { |
| printf(" = "); |
| printf("%u", *((uint32_t *)data)); /* UINT32 */ |
| rv = CMD_OK; |
| if (pager_output("\n")) |
| rv = CMD_WARN; |
| } else |
| rv = efi_print_other_value(data, datasz); |
| } else if (pager_output("\n")) |
| rv = CMD_WARN; |
| |
| done: |
| free(str); |
| free(data); |
| return (rv); |
| } |
| |
| static int |
| command_efi_show(int argc, char *argv[]) |
| { |
| /* |
| * efi-show [-a] |
| * print all the env |
| * efi-show -g UUID |
| * print all the env vars tagged with UUID |
| * efi-show -v var |
| * search all the env vars and print the ones matching var |
| * efi-show -g UUID -v var |
| * efi-show UUID var |
| * print all the env vars that match UUID and var |
| */ |
| /* NB: We assume EFI_GUID is the same as uuid_t */ |
| int aflag = 0, gflag = 0, lflag = 0, vflag = 0; |
| int ch, rv; |
| unsigned i; |
| EFI_STATUS status; |
| EFI_GUID varguid = ZERO_GUID; |
| EFI_GUID matchguid = ZERO_GUID; |
| CHAR16 *varname; |
| CHAR16 *newnm; |
| CHAR16 varnamearg[128]; |
| UINTN varalloc; |
| UINTN varsz; |
| |
| optind = 1; |
| optreset = 1; |
| opterr = 1; |
| |
| while ((ch = getopt(argc, argv, "ag:lv:")) != -1) { |
| switch (ch) { |
| case 'a': |
| aflag = 1; |
| break; |
| case 'g': |
| gflag = 1; |
| if (efi_name_to_guid(optarg, &matchguid) == false) { |
| printf("uuid %s could not be parsed\n", optarg); |
| return (CMD_ERROR); |
| } |
| break; |
| case 'l': |
| lflag = 1; |
| break; |
| case 'v': |
| vflag = 1; |
| if (strlen(optarg) >= nitems(varnamearg)) { |
| printf("Variable %s is longer than %zd " |
| "characters\n", optarg, nitems(varnamearg)); |
| return (CMD_ERROR); |
| } |
| cpy8to16(optarg, varnamearg, nitems(varnamearg)); |
| break; |
| default: |
| return (CMD_ERROR); |
| } |
| } |
| |
| if (argc == 1) /* default is -a */ |
| aflag = 1; |
| |
| if (aflag && (gflag || vflag)) { |
| printf("-a isn't compatible with -g or -v\n"); |
| return (CMD_ERROR); |
| } |
| |
| if (aflag && optind < argc) { |
| printf("-a doesn't take any args\n"); |
| return (CMD_ERROR); |
| } |
| |
| argc -= optind; |
| argv += optind; |
| |
| pager_open(); |
| if (vflag && gflag) { |
| rv = efi_print_var(varnamearg, &matchguid, lflag); |
| if (rv == CMD_WARN) |
| rv = CMD_OK; |
| pager_close(); |
| return (rv); |
| } |
| |
| if (argc == 2) { |
| optarg = argv[0]; |
| if (strlen(optarg) >= nitems(varnamearg)) { |
| printf("Variable %s is longer than %zd characters\n", |
| optarg, nitems(varnamearg)); |
| pager_close(); |
| return (CMD_ERROR); |
| } |
| for (i = 0; i < strlen(optarg); i++) |
| varnamearg[i] = optarg[i]; |
| varnamearg[i] = 0; |
| optarg = argv[1]; |
| if (efi_name_to_guid(optarg, &matchguid) == false) { |
| printf("uuid %s could not be parsed\n", optarg); |
| pager_close(); |
| return (CMD_ERROR); |
| } |
| rv = efi_print_var(varnamearg, &matchguid, lflag); |
| if (rv == CMD_WARN) |
| rv = CMD_OK; |
| pager_close(); |
| return (rv); |
| } |
| |
| if (argc > 0) { |
| printf("Too many args: %d\n", argc); |
| pager_close(); |
| return (CMD_ERROR); |
| } |
| |
| /* |
| * Initiate the search -- note the standard takes pain |
| * to specify the initial call must be a poiner to a NULL |
| * character. |
| */ |
| varalloc = 1024; |
| varname = malloc(varalloc); |
| if (varname == NULL) { |
| printf("Can't allocate memory to get variables\n"); |
| pager_close(); |
| return (CMD_ERROR); |
| } |
| varname[0] = 0; |
| while (1) { |
| varsz = varalloc; |
| status = RS->GetNextVariableName(&varsz, varname, &varguid); |
| if (status == EFI_BUFFER_TOO_SMALL) { |
| varalloc = varsz; |
| newnm = realloc(varname, varalloc); |
| if (newnm == NULL) { |
| printf("Can't allocate memory to get " |
| "variables\n"); |
| rv = CMD_ERROR; |
| break; |
| } |
| varname = newnm; |
| continue; /* Try again with bigger buffer */ |
| } |
| if (status == EFI_NOT_FOUND) { |
| rv = CMD_OK; |
| break; |
| } |
| if (status != EFI_SUCCESS) { |
| rv = CMD_ERROR; |
| break; |
| } |
| |
| if (aflag) { |
| rv = efi_print_var(varname, &varguid, lflag); |
| if (rv != CMD_OK) { |
| if (rv == CMD_WARN) |
| rv = CMD_OK; |
| break; |
| } |
| continue; |
| } |
| if (vflag) { |
| if (wcscmp(varnamearg, varname) == 0) { |
| rv = efi_print_var(varname, &varguid, lflag); |
| if (rv != CMD_OK) { |
| if (rv == CMD_WARN) |
| rv = CMD_OK; |
| break; |
| } |
| continue; |
| } |
| } |
| if (gflag) { |
| rv = uuid_equal((uuid_t *)&varguid, |
| (uuid_t *)&matchguid, NULL); |
| if (rv != 0) { |
| rv = efi_print_var(varname, &varguid, lflag); |
| if (rv != CMD_OK) { |
| if (rv == CMD_WARN) |
| rv = CMD_OK; |
| break; |
| } |
| continue; |
| } |
| } |
| } |
| free(varname); |
| pager_close(); |
| |
| return (rv); |
| } |
| |
| COMMAND_SET(efiset, "efi-set", "set EFI variables", command_efi_set); |
| |
| static int |
| command_efi_set(int argc, char *argv[]) |
| { |
| char *uuid, *var, *val; |
| CHAR16 wvar[128]; |
| EFI_GUID guid; |
| EFI_STATUS err; |
| |
| if (argc != 4) { |
| printf("efi-set uuid var new-value\n"); |
| return (CMD_ERROR); |
| } |
| uuid = argv[1]; |
| var = argv[2]; |
| val = argv[3]; |
| if (efi_name_to_guid(uuid, &guid) == false) { |
| printf("Invalid uuid %s\n", uuid); |
| return (CMD_ERROR); |
| } |
| cpy8to16(var, wvar, nitems(wvar)); |
| #if 0 |
| err = RS->SetVariable(wvar, &guid, EFI_VARIABLE_NON_VOLATILE | |
| EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, |
| strlen(val) + 1, val); |
| if (EFI_ERROR(err)) { |
| printf("Failed to set variable: error %lu\n", |
| EFI_ERROR_CODE(err)); |
| return (CMD_ERROR); |
| } |
| #else |
| printf("would set %s %s = %s\n", uuid, var, val); |
| #endif |
| return (CMD_OK); |
| } |
| |
| COMMAND_SET(efiunset, "efi-unset", "delete / unset EFI variables", command_efi_unset); |
| |
| static int |
| command_efi_unset(int argc, char *argv[]) |
| { |
| char *uuid, *var; |
| CHAR16 wvar[128]; |
| EFI_GUID guid; |
| EFI_STATUS err; |
| |
| if (argc != 3) { |
| printf("efi-unset uuid var\n"); |
| return (CMD_ERROR); |
| } |
| uuid = argv[1]; |
| var = argv[2]; |
| if (efi_name_to_guid(uuid, &guid) == false) { |
| printf("Invalid uuid %s\n", uuid); |
| return (CMD_ERROR); |
| } |
| cpy8to16(var, wvar, nitems(wvar)); |
| #if 0 |
| err = RS->SetVariable(wvar, &guid, 0, 0, NULL); |
| if (EFI_ERROR(err)) { |
| printf("Failed to unset variable: error %lu\n", |
| EFI_ERROR_CODE(err)); |
| return (CMD_ERROR); |
| } |
| #else |
| printf("would unset %s %s \n", uuid, var); |
| #endif |
| return (CMD_OK); |
| } |
| |
| /* |
| * Loader interaction words and extras |
| * |
| * efi-setenv ( value n name n guid n attr -- 0 | -1) |
| * efi-getenv ( guid n addr n -- addr' n' | -1 ) |
| * efi-unsetenv ( name n guid n'' -- ) |
| */ |
| |
| /* |
| * efi-setenv |
| * efi-setenv ( value n name n guid n attr -- 0 | -1) |
| * |
| * Set environment variables using the SetVariable EFI runtime service. |
| * |
| * Value and guid are passed through in binary form (so guid needs to be |
| * converted to binary form from its string form). Name is converted from |
| * ASCII to CHAR16. Since ficl doesn't have support for internationalization, |
| * there's no native CHAR16 interface provided. |
| * |
| * attr is an int in the bitmask of the following attributes for this variable. |
| * |
| * 1 Non volatile |
| * 2 Boot service access |
| * 4 Run time access |
| * (corresponding to the same bits in the UEFI spec). |
| */ |
| static void |
| ficlEfiSetenv(ficlVm *pVM) |
| { |
| char *value = NULL, *guid = NULL; |
| CHAR16 *name = NULL; |
| int i; |
| char *namep, *valuep, *guidp; |
| int names, values, guids, attr; |
| EFI_STATUS status; |
| uuid_t u; |
| uint32_t ustatus; |
| char *error = NULL; |
| ficlStack *pStack = ficlVmGetDataStack(pVM); |
| |
| FICL_STACK_CHECK(pStack, 6, 0); |
| |
| attr = ficlStackPopInteger(pStack); |
| guids = ficlStackPopInteger(pStack); |
| guidp = (char*)ficlStackPopPointer(pStack); |
| names = ficlStackPopInteger(pStack); |
| namep = (char*)ficlStackPopPointer(pStack); |
| values = ficlStackPopInteger(pStack); |
| valuep = (char*)ficlStackPopPointer(pStack); |
| |
| guid = ficlMalloc(guids); |
| if (guid == NULL) |
| goto out; |
| memcpy(guid, guidp, guids); |
| uuid_from_string(guid, &u, &ustatus); |
| if (ustatus != uuid_s_ok) { |
| switch (ustatus) { |
| case uuid_s_bad_version: |
| error = "uuid: bad string"; |
| break; |
| case uuid_s_invalid_string_uuid: |
| error = "uuid: invalid string"; |
| break; |
| case uuid_s_no_memory: |
| error = "Out of memory"; |
| break; |
| default: |
| error = "uuid: Unknown error"; |
| break; |
| } |
| ficlStackPushInteger(pStack, -1); |
| goto out; |
| } |
| |
| name = ficlMalloc((names + 1) * sizeof (CHAR16)); |
| if (name == NULL) { |
| error = "Out of memory"; |
| goto out; |
| } |
| for (i = 0; i < names; i++) |
| name[i] = namep[i]; |
| name[names] = 0; |
| |
| value = ficlMalloc(values + 1); |
| if (value == NULL) { |
| error = "Out of memory"; |
| goto out; |
| } |
| memcpy(value, valuep, values); |
| |
| status = RS->SetVariable(name, (EFI_GUID *)&u, attr, values, value); |
| if (status == EFI_SUCCESS) { |
| ficlStackPushInteger(pStack, 0); |
| } else { |
| ficlStackPushInteger(pStack, -1); |
| error = "Error: SetVariable failed"; |
| } |
| |
| out: |
| ficlFree(name); |
| ficlFree(value); |
| ficlFree(guid); |
| if (error != NULL) |
| ficlVmThrowError(pVM, error); |
| } |
| |
| static void |
| ficlEfiGetenv(ficlVm *pVM) |
| { |
| char *name, *value; |
| char *namep; |
| int names; |
| |
| FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 2); |
| |
| names = ficlStackPopInteger(ficlVmGetDataStack(pVM)); |
| namep = (char*)ficlStackPopPointer(ficlVmGetDataStack(pVM)); |
| |
| name = ficlMalloc(names+1); |
| if (name == NULL) |
| ficlVmThrowError(pVM, "Error: out of memory"); |
| strncpy(name, namep, names); |
| name[names] = '\0'; |
| |
| value = getenv(name); |
| ficlFree(name); |
| |
| if(value != NULL) { |
| ficlStackPushPointer(ficlVmGetDataStack(pVM), value); |
| ficlStackPushInteger(ficlVmGetDataStack(pVM), strlen(value)); |
| } else { |
| ficlStackPushInteger(ficlVmGetDataStack(pVM), -1); |
| } |
| } |
| |
| static void |
| ficlEfiUnsetenv(ficlVm *pVM) |
| { |
| char *name; |
| char *namep; |
| int names; |
| |
| FICL_STACK_CHECK(ficlVmGetDataStack(pVM), 2, 0); |
| |
| names = ficlStackPopInteger(ficlVmGetDataStack(pVM)); |
| namep = (char*)ficlStackPopPointer(ficlVmGetDataStack(pVM)); |
| |
| name = ficlMalloc(names+1); |
| if (name == NULL) |
| ficlVmThrowError(pVM, "Error: out of memory"); |
| strncpy(name, namep, names); |
| name[names] = '\0'; |
| |
| unsetenv(name); |
| ficlFree(name); |
| } |
| |
| /* |
| * Build platform extensions into the system dictionary |
| */ |
| static void |
| ficlEfiCompilePlatform(ficlSystem *pSys) |
| { |
| ficlDictionary *dp = ficlSystemGetDictionary(pSys); |
| |
| FICL_SYSTEM_ASSERT(pSys, dp); |
| |
| ficlDictionarySetPrimitive(dp, "efi-setenv", ficlEfiSetenv, |
| FICL_WORD_DEFAULT); |
| ficlDictionarySetPrimitive(dp, "efi-getenv", ficlEfiGetenv, |
| FICL_WORD_DEFAULT); |
| ficlDictionarySetPrimitive(dp, "efi-unsetenv", ficlEfiUnsetenv, |
| FICL_WORD_DEFAULT); |
| } |
| |
| FICL_COMPILE_SET(ficlEfiCompilePlatform); |