diff --git a/MAINTAINERS b/MAINTAINERS index f1f8818d6b..4fabb75eda 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -471,6 +471,8 @@ F: lib/efi*/ F: test/py/tests/test_efi* F: test/unicode_ut.c F: cmd/bootefi.c +F: cmd/efidebug.c +F: cmd/nvedit_efi.c F: tools/file2include.c FPGA diff --git a/cmd/Kconfig b/cmd/Kconfig index ed8d85bfa6..4bcc5c4557 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -420,6 +420,16 @@ config CMD_ENV_FLAGS be deleted. This command shows the variables that have special flags. +config CMD_NVEDIT_EFI + bool "env [set|print] -e - set/print UEFI variables" + depends on EFI_LOADER + default y + imply HEXDUMP + help + UEFI variables are encoded as some form of U-Boot variables. + If enabled, we are allowed to set/print UEFI variables using + "env" command with "-e" option without knowing details. + endmenu menu "Memory commands" @@ -1397,6 +1407,16 @@ config CMD_DISPLAY displayed on a simple board-specific display. Implement display_putc() to use it. +config CMD_EFIDEBUG + bool "efidebug - display/configure UEFI environment" + depends on EFI_LOADER + default n + help + Enable the 'efidebug' command which provides a subset of UEFI + shell utility with simplified functionality. It will be useful + particularly for managing boot parameters as well as examining + various EFI status for debugging. + config CMD_LED bool "led" depends on LED diff --git a/cmd/Makefile b/cmd/Makefile index a127a99539..acb85f49fb 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_CMD_ECHO) += echo.o obj-$(CONFIG_ENV_IS_IN_EEPROM) += eeprom.o obj-$(CONFIG_CMD_EEPROM) += eeprom.o obj-$(CONFIG_EFI_STUB) += efi.o +obj-$(CONFIG_CMD_EFIDEBUG) += efidebug.o obj-$(CONFIG_CMD_ELF) += elf.o obj-$(CONFIG_HUSH_PARSER) += exit.o obj-$(CONFIG_CMD_EXT4) += ext4.o @@ -98,6 +99,7 @@ obj-$(CONFIG_CMD_MTD) += mtd.o obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_NET) += net.o +obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o obj-$(CONFIG_CMD_ONENAND) += onenand.o obj-$(CONFIG_CMD_OSD) += osd.o obj-$(CONFIG_CMD_PART) += part.o diff --git a/cmd/efidebug.c b/cmd/efidebug.c new file mode 100644 index 0000000000..5072a7b39b --- /dev/null +++ b/cmd/efidebug.c @@ -0,0 +1,1046 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UEFI Shell-like command + * + * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BS systab.boottime +#define RT systab.runtime + +/** + * efi_get_device_handle_info() - get information of UEFI device + * + * @handle: Handle of UEFI device + * @dev_path_text: Pointer to text of device path + * Return: 0 on success, -1 on failure + * + * Currently return a formatted text of device path. + */ +static int efi_get_device_handle_info(efi_handle_t handle, u16 **dev_path_text) +{ + struct efi_device_path *dp; + efi_status_t ret; + + ret = EFI_CALL(BS->open_protocol(handle, &efi_guid_device_path, + (void **)&dp, NULL /* FIXME */, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret == EFI_SUCCESS) { + *dev_path_text = efi_dp_str(dp); + return 0; + } else { + return -1; + } +} + +#define EFI_HANDLE_WIDTH ((int)sizeof(efi_handle_t) * 2) + +static const char spc[] = " "; +static const char sep[] = "================"; + +/** + * do_efi_show_devices() - show UEFI devices + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "devices" sub-command. + * Show all UEFI devices and their information. + */ +static int do_efi_show_devices(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_handle_t *handles; + efi_uintn_t num, i; + u16 *dev_path_text; + efi_status_t ret; + + ret = EFI_CALL(BS->locate_handle_buffer(ALL_HANDLES, NULL, NULL, + &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Device%.*s Device Path\n", EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ====================\n", EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + if (!efi_get_device_handle_info(handles[i], &dev_path_text)) { + printf("%p %ls\n", handles[i], dev_path_text); + efi_free_pool(dev_path_text); + } + } + + EFI_CALL(BS->free_pool(handles)); + + return CMD_RET_SUCCESS; +} + +/** + * efi_get_driver_handle_info() - get information of UEFI driver + * + * @handle: Handle of UEFI device + * @driver_name: Driver name + * @image_path: Pointer to text of device path + * Return: 0 on success, -1 on failure + * + * Currently return no useful information as all UEFI drivers are + * built-in.. + */ +static int efi_get_driver_handle_info(efi_handle_t handle, u16 **driver_name, + u16 **image_path) +{ + struct efi_handler *handler; + struct efi_loaded_image *image; + efi_status_t ret; + + /* + * driver name + * TODO: support EFI_COMPONENT_NAME2_PROTOCOL + */ + *driver_name = NULL; + + /* image name */ + ret = efi_search_protocol(handle, &efi_guid_loaded_image, &handler); + if (ret != EFI_SUCCESS) { + *image_path = NULL; + return 0; + } + + image = handler->protocol_interface; + *image_path = efi_dp_str(image->file_path); + + return 0; +} + +/** + * do_efi_show_drivers() - show UEFI drivers + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "drivers" sub-command. + * Show all UEFI drivers and their information. + */ +static int do_efi_show_drivers(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_handle_t *handles; + efi_uintn_t num, i; + u16 *driver_name, *image_path_text; + efi_status_t ret; + + ret = EFI_CALL(BS->locate_handle_buffer( + BY_PROTOCOL, &efi_guid_driver_binding_protocol, + NULL, &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Driver%.*s Name Image Path\n", + EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ==================== ====================\n", + EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + if (!efi_get_driver_handle_info(handles[i], &driver_name, + &image_path_text)) { + if (image_path_text) + printf("%p %-20ls %ls\n", handles[i], + driver_name, image_path_text); + else + printf("%p %-20ls \n", + handles[i], driver_name); + EFI_CALL(BS->free_pool(driver_name)); + EFI_CALL(BS->free_pool(image_path_text)); + } + } + + EFI_CALL(BS->free_pool(handles)); + + return CMD_RET_SUCCESS; +} + +static const struct { + const char *text; + const efi_guid_t guid; +} guid_list[] = { + { + "Device Path", + DEVICE_PATH_GUID, + }, + { + "Device Path To Text", + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID, + }, + { + "Device Path Utilities", + EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID, + }, + { + "Unicode Collation 2", + EFI_UNICODE_COLLATION_PROTOCOL2_GUID, + }, + { + "Driver Binding", + EFI_DRIVER_BINDING_PROTOCOL_GUID, + }, + { + "Simple Text Input", + EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID, + }, + { + "Simple Text Input Ex", + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID, + }, + { + "Simple Text Output", + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID, + }, + { + "Block IO", + BLOCK_IO_GUID, + }, + { + "Simple File System", + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, + }, + { + "Loaded Image", + LOADED_IMAGE_PROTOCOL_GUID, + }, + { + "GOP", + EFI_GOP_GUID, + }, +}; + +/** + * get_guid_text - get string of protocol guid + * @guid: Protocol guid + * Return: String + * + * Return string for display to represent the protocol. + */ +static const char *get_guid_text(const efi_guid_t *guid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(guid_list); i++) + if (!guidcmp(&guid_list[i].guid, guid)) + break; + + if (i != ARRAY_SIZE(guid_list)) + return guid_list[i].text; + else + return NULL; +} + +/** + * do_efi_show_handles() - show UEFI handles + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "dh" sub-command. + * Show all UEFI handles and their information, currently all protocols + * added to handle. + */ +static int do_efi_show_handles(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_handle_t *handles; + efi_guid_t **guid; + efi_uintn_t num, count, i, j; + const char *guid_text; + efi_status_t ret; + + ret = EFI_CALL(BS->locate_handle_buffer(ALL_HANDLES, NULL, NULL, + &num, &handles)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + if (!num) + return CMD_RET_SUCCESS; + + printf("Handle%.*s Protocols\n", EFI_HANDLE_WIDTH - 6, spc); + printf("%.*s ====================\n", EFI_HANDLE_WIDTH, sep); + for (i = 0; i < num; i++) { + printf("%p", handles[i]); + ret = EFI_CALL(BS->protocols_per_handle(handles[i], &guid, + &count)); + if (ret || !count) { + putc('\n'); + continue; + } + + for (j = 0; j < count; j++) { + if (j) + printf(", "); + else + putc(' '); + + guid_text = get_guid_text(guid[j]); + if (guid_text) + puts(guid_text); + else + printf("%pUl", guid[j]); + } + putc('\n'); + } + + EFI_CALL(BS->free_pool(handles)); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_show_images() - show UEFI images + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "images" sub-command. + * Show all UEFI loaded images and their information. + */ +static int do_efi_show_images(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_print_image_infos(NULL); + + return CMD_RET_SUCCESS; +} + +static const char * const efi_mem_type_string[] = { + [EFI_RESERVED_MEMORY_TYPE] = "RESERVED", + [EFI_LOADER_CODE] = "LOADER CODE", + [EFI_LOADER_DATA] = "LOADER DATA", + [EFI_BOOT_SERVICES_CODE] = "BOOT CODE", + [EFI_BOOT_SERVICES_DATA] = "BOOT DATA", + [EFI_RUNTIME_SERVICES_CODE] = "RUNTIME CODE", + [EFI_RUNTIME_SERVICES_DATA] = "RUNTIME DATA", + [EFI_CONVENTIONAL_MEMORY] = "CONVENTIONAL", + [EFI_UNUSABLE_MEMORY] = "UNUSABLE MEM", + [EFI_ACPI_RECLAIM_MEMORY] = "ACPI RECLAIM MEM", + [EFI_ACPI_MEMORY_NVS] = "ACPI NVS", + [EFI_MMAP_IO] = "IO", + [EFI_MMAP_IO_PORT] = "IO PORT", + [EFI_PAL_CODE] = "PAL", +}; + +static const struct efi_mem_attrs { + const u64 bit; + const char *text; +} efi_mem_attrs[] = { + {EFI_MEMORY_UC, "UC"}, + {EFI_MEMORY_UC, "UC"}, + {EFI_MEMORY_WC, "WC"}, + {EFI_MEMORY_WT, "WT"}, + {EFI_MEMORY_WB, "WB"}, + {EFI_MEMORY_UCE, "UCE"}, + {EFI_MEMORY_WP, "WP"}, + {EFI_MEMORY_RP, "RP"}, + {EFI_MEMORY_XP, "WP"}, + {EFI_MEMORY_NV, "NV"}, + {EFI_MEMORY_MORE_RELIABLE, "REL"}, + {EFI_MEMORY_RO, "RO"}, + {EFI_MEMORY_RUNTIME, "RT"}, +}; + +/** + * print_memory_attributes() - print memory map attributes + * @attributes: Attribute value + * + * Print memory map attributes + */ +static void print_memory_attributes(u64 attributes) +{ + int sep, i; + + for (sep = 0, i = 0; i < ARRAY_SIZE(efi_mem_attrs); i++) + if (attributes & efi_mem_attrs[i].bit) { + if (sep) { + putc('|'); + } else { + putc(' '); + sep = 1; + } + puts(efi_mem_attrs[i].text); + } +} + +#define EFI_PHYS_ADDR_WIDTH (int)(sizeof(efi_physical_addr_t) * 2) + +/** + * do_efi_show_memmap() - show UEFI memory map + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "memmap" sub-command. + * Show UEFI memory map. + */ +static int do_efi_show_memmap(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_mem_desc *memmap = NULL, *map; + efi_uintn_t map_size = 0; + const char *type; + int i; + efi_status_t ret; + + ret = EFI_CALL(BS->get_memory_map(&map_size, memmap, NULL, NULL, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + map_size += sizeof(struct efi_mem_desc); /* for my own */ + ret = EFI_CALL(BS->allocate_pool(EFI_LOADER_DATA, + map_size, (void *)&memmap)); + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + ret = EFI_CALL(BS->get_memory_map(&map_size, memmap, + NULL, NULL, NULL)); + } + if (ret != EFI_SUCCESS) { + EFI_CALL(BS->free_pool(memmap)); + return CMD_RET_FAILURE; + } + + printf("Type Start%.*s End%.*s Attributes\n", + EFI_PHYS_ADDR_WIDTH - 5, spc, EFI_PHYS_ADDR_WIDTH - 3, spc); + printf("================ %.*s %.*s ==========\n", + EFI_PHYS_ADDR_WIDTH, sep, EFI_PHYS_ADDR_WIDTH, sep); + for (i = 0, map = memmap; i < map_size / sizeof(*map); map++, i++) { + if (map->type < EFI_MAX_MEMORY_TYPE) + type = efi_mem_type_string[map->type]; + else + type = "(unknown)"; + + printf("%-16s %.*llx-%.*llx", type, + EFI_PHYS_ADDR_WIDTH, + map->physical_start, + EFI_PHYS_ADDR_WIDTH, + map->physical_start + map->num_pages * EFI_PAGE_SIZE); + + print_memory_attributes(map->attribute); + putc('\n'); + } + + EFI_CALL(BS->free_pool(memmap)); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_boot_add() - set UEFI load option + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "boot add" sub-command. + * Create or change UEFI load option. + * - boot add