diff options
author | Simon Glass <sjg@chromium.org> | 2015-08-04 12:33:59 -0600 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2015-08-05 08:44:07 -0600 |
commit | f1a0bafb5802416d42d685b343110c1557da1739 (patch) | |
tree | fb2d3289ce61f9cfc8651fdc9547feb9c2f49410 /common | |
parent | 16c220d0a79feed0326c29d64e4bb482e80276d4 (diff) |
efi: Add a command to display the memory map
The EFI memory map is passed from the stub to U-Boot in a table. Add a
command to display it in a vaguely readable fashion.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested on QEMU
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/cmd_efi.c | 257 |
2 files changed, 258 insertions, 0 deletions
diff --git a/common/Makefile b/common/Makefile index d6c1d4844dd..6dc4c899bcb 100644 --- a/common/Makefile +++ b/common/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_CMD_DTT) += cmd_dtt.o obj-$(CONFIG_CMD_ECHO) += cmd_echo.o obj-$(CONFIG_ENV_IS_IN_EEPROM) += cmd_eeprom.o obj-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o +obj-$(CONFIG_EFI_STUB) += cmd_efi.o obj-$(CONFIG_CMD_ELF) += cmd_elf.o obj-$(CONFIG_SYS_HUSH_PARSER) += cmd_exit.o obj-$(CONFIG_CMD_EXT4) += cmd_ext4.o diff --git a/common/cmd_efi.c b/common/cmd_efi.c new file mode 100644 index 00000000000..c76296e7259 --- /dev/null +++ b/common/cmd_efi.c @@ -0,0 +1,257 @@ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <efi.h> +#include <errno.h> +#include <malloc.h> + +static const char *const type_name[] = { + "reserved", + "loader_code", + "loader_data", + "bs_code", + "bs_data", + "rt_code", + "rt_data", + "conv", + "unusable", + "acpi_reclaim", + "acpi_nvs", + "io", + "io_port", + "pal_code", +}; + +static struct attr_info { + int shift; + const char *name; +} mem_attr[] = { + { EFI_MEMORY_UC_SHIFT, "uncached" }, + { EFI_MEMORY_WC_SHIFT, "write-coalescing" }, + { EFI_MEMORY_WT_SHIFT, "write-through" }, + { EFI_MEMORY_WB_SHIFT, "write-back" }, + { EFI_MEMORY_UCE_SHIFT, "uncached & exported" }, + { EFI_MEMORY_WP_SHIFT, "write-protect" }, + { EFI_MEMORY_RP_SHIFT, "read-protect" }, + { EFI_MEMORY_XP_SHIFT, "execute-protect" }, + { EFI_MEMORY_RUNTIME_SHIFT, "needs runtime mapping" } +}; + +/* Maximum different attribute values we can track */ +#define ATTR_SEEN_MAX 30 + +static inline bool is_boot_services(int type) +{ + return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA || + type == EFI_BOOT_SERVICES_CODE || + type == EFI_BOOT_SERVICES_DATA; +} + +static int h_cmp_entry(const void *v1, const void *v2) +{ + const struct efi_mem_desc *desc1 = v1; + const struct efi_mem_desc *desc2 = v2; + int64_t diff = desc1->physical_start - desc2->physical_start; + + /* + * Manually calculate the difference to avoid sign loss in the 64-bit + * to 32-bit conversion + */ + return diff < 0 ? -1 : diff > 0 ? 1 : 0; +} + +void *efi_build_mem_table(struct efi_entry_memmap *map, int size, bool skip_bs) +{ + struct efi_mem_desc *desc, *end, *base, *dest, *prev; + int count; + u64 addr; + + base = malloc(size + sizeof(*desc)); + if (!base) { + debug("%s: Cannot allocate %#x bytes\n", __func__, size); + return NULL; + } + end = (struct efi_mem_desc *)((ulong)map + size); + count = ((ulong)end - (ulong)map->desc) / map->desc_size; + memcpy(base, map->desc, (ulong)end - (ulong)map->desc); + qsort(base, count, map->desc_size, h_cmp_entry); + prev = NULL; + addr = 0; + dest = base; + end = base + count; + for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) { + bool merge = true; + int type = desc->type; + + if (skip_bs && is_boot_services(desc->type)) + type = EFI_CONVENTIONAL_MEMORY; + + memcpy(dest, desc, map->desc_size); + dest->type = type; + if (!skip_bs || !prev) + merge = false; + else if (desc->physical_start != addr) + merge = false; + else if (type != EFI_CONVENTIONAL_MEMORY) + merge = false; + else if (prev->type != EFI_CONVENTIONAL_MEMORY) + merge = false; + + if (merge) { + prev->num_pages += desc->num_pages; + } else { + prev = dest; + dest = efi_get_next_mem_desc(map, dest); + } + addr = desc->physical_start + (desc->num_pages << + EFI_PAGE_SHIFT); + } + + /* Mark the end */ + dest->type = EFI_TABLE_END; + + return base; +} + +static void efi_print_mem_table(struct efi_entry_memmap *map, + struct efi_mem_desc *desc, bool skip_bs) +{ + u64 attr_seen[ATTR_SEEN_MAX]; + int attr_seen_count; + int upto, i; + u64 addr; + + printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical", + "Virtual", "Size", "Attributes"); + + /* Keep track of all the different attributes we have seen */ + attr_seen_count = 0; + addr = 0; + for (upto = 0; desc->type != EFI_TABLE_END; + upto++, desc = efi_get_next_mem_desc(map, desc)) { + const char *name; + u64 size; + + if (skip_bs && is_boot_services(desc->type)) + continue; + if (desc->physical_start != addr) { + printf(" %-14s %010llx %10s %010llx\n", "<gap>", + addr, "", desc->physical_start - addr); + } + size = desc->num_pages << EFI_PAGE_SHIFT; + + name = desc->type < ARRAY_SIZE(type_name) ? + type_name[desc->type] : "<invalid>"; + printf("%2d %x:%-12s %010llx %010llx %010llx ", upto, + desc->type, name, desc->physical_start, + desc->virtual_start, size); + if (desc->attribute & EFI_MEMORY_RUNTIME) + putc('r'); + printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME); + putc('\n'); + + for (i = 0; i < attr_seen_count; i++) { + if (attr_seen[i] == desc->attribute) + break; + } + if (i == attr_seen_count && i < ATTR_SEEN_MAX) + attr_seen[attr_seen_count++] = desc->attribute; + addr = desc->physical_start + size; + } + + printf("\nAttributes key:\n"); + for (i = 0; i < attr_seen_count; i++) { + u64 attr = attr_seen[i]; + bool first; + int j; + + printf("%c%llx: ", attr & EFI_MEMORY_RUNTIME ? 'r' : ' ', + attr & ~EFI_MEMORY_RUNTIME); + for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) { + if (attr & (1ULL << mem_attr[j].shift)) { + if (first) + first = false; + else + printf(", "); + printf("%s", mem_attr[j].name); + } + } + putc('\n'); + } + if (skip_bs) + printf("*Some areas are merged (use 'all' to see)\n"); +} + +static int do_efi_mem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct efi_mem_desc *desc; + struct efi_entry_memmap *map; + int size, ret; + bool skip_bs; + + skip_bs = !argc || *argv[0] != 'a'; + ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); + switch (ret) { + case -ENOENT: + printf("No EFI table available\n"); + goto done; + case -EPROTONOSUPPORT: + printf("Incorrect EFI table version\n"); + goto done; + } + printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n", + gd->arch.table, map, size, map->version, map->desc_size); + if (map->version != EFI_MEM_DESC_VERSION) { + printf("Incorrect memory map version\n"); + ret = -EPROTONOSUPPORT; + goto done; + } + + desc = efi_build_mem_table(map, size, skip_bs); + if (!desc) { + ret = -ENOMEM; + goto done; + } + + efi_print_mem_table(map, desc, skip_bs); + free(desc); +done: + if (ret) + printf("Error: %d\n", ret); + + return ret ? CMD_RET_FAILURE : 0; +} + +static cmd_tbl_t efi_commands[] = { + U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""), +}; + +static int do_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *efi_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands)); + argc -= 2; + argv += 2; + if (!efi_cmd || argc > efi_cmd->maxargs) + return CMD_RET_USAGE; + + ret = efi_cmd->cmd(efi_cmd, flag, argc, argv); + + return cmd_process_error(efi_cmd, ret); +} + +U_BOOT_CMD( + efi, 3, 1, do_efi, + "EFI access", + "mem [all] Dump memory information [include boot services]" +); |