1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 22e192b24SSimon Glass /* 32e192b24SSimon Glass * (C) Copyright 2015 Google, Inc 42e192b24SSimon Glass * Written by Simon Glass <sjg@chromium.org> 52e192b24SSimon Glass */ 62e192b24SSimon Glass 72e192b24SSimon Glass #include <common.h> 82e192b24SSimon Glass #include <command.h> 92e192b24SSimon Glass #include <efi.h> 102e192b24SSimon Glass #include <errno.h> 112e192b24SSimon Glass #include <malloc.h> 122e192b24SSimon Glass 132e192b24SSimon Glass static const char *const type_name[] = { 142e192b24SSimon Glass "reserved", 152e192b24SSimon Glass "loader_code", 162e192b24SSimon Glass "loader_data", 172e192b24SSimon Glass "bs_code", 182e192b24SSimon Glass "bs_data", 192e192b24SSimon Glass "rt_code", 202e192b24SSimon Glass "rt_data", 212e192b24SSimon Glass "conv", 222e192b24SSimon Glass "unusable", 232e192b24SSimon Glass "acpi_reclaim", 242e192b24SSimon Glass "acpi_nvs", 252e192b24SSimon Glass "io", 262e192b24SSimon Glass "io_port", 272e192b24SSimon Glass "pal_code", 282e192b24SSimon Glass }; 292e192b24SSimon Glass 302e192b24SSimon Glass static struct attr_info { 312e192b24SSimon Glass int shift; 322e192b24SSimon Glass const char *name; 332e192b24SSimon Glass } mem_attr[] = { 342e192b24SSimon Glass { EFI_MEMORY_UC_SHIFT, "uncached" }, 352e192b24SSimon Glass { EFI_MEMORY_WC_SHIFT, "write-coalescing" }, 362e192b24SSimon Glass { EFI_MEMORY_WT_SHIFT, "write-through" }, 372e192b24SSimon Glass { EFI_MEMORY_WB_SHIFT, "write-back" }, 382e192b24SSimon Glass { EFI_MEMORY_UCE_SHIFT, "uncached & exported" }, 392e192b24SSimon Glass { EFI_MEMORY_WP_SHIFT, "write-protect" }, 402e192b24SSimon Glass { EFI_MEMORY_RP_SHIFT, "read-protect" }, 412e192b24SSimon Glass { EFI_MEMORY_XP_SHIFT, "execute-protect" }, 422e192b24SSimon Glass { EFI_MEMORY_RUNTIME_SHIFT, "needs runtime mapping" } 432e192b24SSimon Glass }; 442e192b24SSimon Glass 452e192b24SSimon Glass /* Maximum different attribute values we can track */ 462e192b24SSimon Glass #define ATTR_SEEN_MAX 30 472e192b24SSimon Glass 482e192b24SSimon Glass static inline bool is_boot_services(int type) 492e192b24SSimon Glass { 502e192b24SSimon Glass return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA || 512e192b24SSimon Glass type == EFI_BOOT_SERVICES_CODE || 522e192b24SSimon Glass type == EFI_BOOT_SERVICES_DATA; 532e192b24SSimon Glass } 542e192b24SSimon Glass 552e192b24SSimon Glass static int h_cmp_entry(const void *v1, const void *v2) 562e192b24SSimon Glass { 572e192b24SSimon Glass const struct efi_mem_desc *desc1 = v1; 582e192b24SSimon Glass const struct efi_mem_desc *desc2 = v2; 592e192b24SSimon Glass int64_t diff = desc1->physical_start - desc2->physical_start; 602e192b24SSimon Glass 612e192b24SSimon Glass /* 622e192b24SSimon Glass * Manually calculate the difference to avoid sign loss in the 64-bit 632e192b24SSimon Glass * to 32-bit conversion 642e192b24SSimon Glass */ 652e192b24SSimon Glass return diff < 0 ? -1 : diff > 0 ? 1 : 0; 662e192b24SSimon Glass } 672e192b24SSimon Glass 682e192b24SSimon Glass void *efi_build_mem_table(struct efi_entry_memmap *map, int size, bool skip_bs) 692e192b24SSimon Glass { 702e192b24SSimon Glass struct efi_mem_desc *desc, *end, *base, *dest, *prev; 712e192b24SSimon Glass int count; 722e192b24SSimon Glass u64 addr; 732e192b24SSimon Glass 742e192b24SSimon Glass base = malloc(size + sizeof(*desc)); 752e192b24SSimon Glass if (!base) { 762e192b24SSimon Glass debug("%s: Cannot allocate %#x bytes\n", __func__, size); 772e192b24SSimon Glass return NULL; 782e192b24SSimon Glass } 792e192b24SSimon Glass end = (struct efi_mem_desc *)((ulong)map + size); 802e192b24SSimon Glass count = ((ulong)end - (ulong)map->desc) / map->desc_size; 812e192b24SSimon Glass memcpy(base, map->desc, (ulong)end - (ulong)map->desc); 822e192b24SSimon Glass qsort(base, count, map->desc_size, h_cmp_entry); 832e192b24SSimon Glass prev = NULL; 842e192b24SSimon Glass addr = 0; 852e192b24SSimon Glass dest = base; 862e192b24SSimon Glass end = base + count; 872e192b24SSimon Glass for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) { 882e192b24SSimon Glass bool merge = true; 892e192b24SSimon Glass int type = desc->type; 902e192b24SSimon Glass 912e192b24SSimon Glass if (skip_bs && is_boot_services(desc->type)) 922e192b24SSimon Glass type = EFI_CONVENTIONAL_MEMORY; 932e192b24SSimon Glass 942e192b24SSimon Glass memcpy(dest, desc, map->desc_size); 952e192b24SSimon Glass dest->type = type; 962e192b24SSimon Glass if (!skip_bs || !prev) 972e192b24SSimon Glass merge = false; 982e192b24SSimon Glass else if (desc->physical_start != addr) 992e192b24SSimon Glass merge = false; 1002e192b24SSimon Glass else if (type != EFI_CONVENTIONAL_MEMORY) 1012e192b24SSimon Glass merge = false; 1022e192b24SSimon Glass else if (prev->type != EFI_CONVENTIONAL_MEMORY) 1032e192b24SSimon Glass merge = false; 1042e192b24SSimon Glass 1052e192b24SSimon Glass if (merge) { 1062e192b24SSimon Glass prev->num_pages += desc->num_pages; 1072e192b24SSimon Glass } else { 1082e192b24SSimon Glass prev = dest; 1092e192b24SSimon Glass dest = efi_get_next_mem_desc(map, dest); 1102e192b24SSimon Glass } 1112e192b24SSimon Glass addr = desc->physical_start + (desc->num_pages << 1122e192b24SSimon Glass EFI_PAGE_SHIFT); 1132e192b24SSimon Glass } 1142e192b24SSimon Glass 1152e192b24SSimon Glass /* Mark the end */ 1162e192b24SSimon Glass dest->type = EFI_TABLE_END; 1172e192b24SSimon Glass 1182e192b24SSimon Glass return base; 1192e192b24SSimon Glass } 1202e192b24SSimon Glass 1212e192b24SSimon Glass static void efi_print_mem_table(struct efi_entry_memmap *map, 1222e192b24SSimon Glass struct efi_mem_desc *desc, bool skip_bs) 1232e192b24SSimon Glass { 1242e192b24SSimon Glass u64 attr_seen[ATTR_SEEN_MAX]; 1252e192b24SSimon Glass int attr_seen_count; 1262e192b24SSimon Glass int upto, i; 1272e192b24SSimon Glass u64 addr; 1282e192b24SSimon Glass 1292e192b24SSimon Glass printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical", 1302e192b24SSimon Glass "Virtual", "Size", "Attributes"); 1312e192b24SSimon Glass 1322e192b24SSimon Glass /* Keep track of all the different attributes we have seen */ 1332e192b24SSimon Glass attr_seen_count = 0; 1342e192b24SSimon Glass addr = 0; 1352e192b24SSimon Glass for (upto = 0; desc->type != EFI_TABLE_END; 1362e192b24SSimon Glass upto++, desc = efi_get_next_mem_desc(map, desc)) { 1372e192b24SSimon Glass const char *name; 1382e192b24SSimon Glass u64 size; 1392e192b24SSimon Glass 1402e192b24SSimon Glass if (skip_bs && is_boot_services(desc->type)) 1412e192b24SSimon Glass continue; 1422e192b24SSimon Glass if (desc->physical_start != addr) { 1432e192b24SSimon Glass printf(" %-14s %010llx %10s %010llx\n", "<gap>", 1442e192b24SSimon Glass addr, "", desc->physical_start - addr); 1452e192b24SSimon Glass } 1462e192b24SSimon Glass size = desc->num_pages << EFI_PAGE_SHIFT; 1472e192b24SSimon Glass 1482e192b24SSimon Glass name = desc->type < ARRAY_SIZE(type_name) ? 1492e192b24SSimon Glass type_name[desc->type] : "<invalid>"; 1502e192b24SSimon Glass printf("%2d %x:%-12s %010llx %010llx %010llx ", upto, 1512e192b24SSimon Glass desc->type, name, desc->physical_start, 1522e192b24SSimon Glass desc->virtual_start, size); 1532e192b24SSimon Glass if (desc->attribute & EFI_MEMORY_RUNTIME) 1542e192b24SSimon Glass putc('r'); 1552e192b24SSimon Glass printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME); 1562e192b24SSimon Glass putc('\n'); 1572e192b24SSimon Glass 1582e192b24SSimon Glass for (i = 0; i < attr_seen_count; i++) { 1592e192b24SSimon Glass if (attr_seen[i] == desc->attribute) 1602e192b24SSimon Glass break; 1612e192b24SSimon Glass } 1622e192b24SSimon Glass if (i == attr_seen_count && i < ATTR_SEEN_MAX) 1632e192b24SSimon Glass attr_seen[attr_seen_count++] = desc->attribute; 1642e192b24SSimon Glass addr = desc->physical_start + size; 1652e192b24SSimon Glass } 1662e192b24SSimon Glass 1672e192b24SSimon Glass printf("\nAttributes key:\n"); 1682e192b24SSimon Glass for (i = 0; i < attr_seen_count; i++) { 1692e192b24SSimon Glass u64 attr = attr_seen[i]; 1702e192b24SSimon Glass bool first; 1712e192b24SSimon Glass int j; 1722e192b24SSimon Glass 1732e192b24SSimon Glass printf("%c%llx: ", attr & EFI_MEMORY_RUNTIME ? 'r' : ' ', 1742e192b24SSimon Glass attr & ~EFI_MEMORY_RUNTIME); 1752e192b24SSimon Glass for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) { 1762e192b24SSimon Glass if (attr & (1ULL << mem_attr[j].shift)) { 1772e192b24SSimon Glass if (first) 1782e192b24SSimon Glass first = false; 1792e192b24SSimon Glass else 1802e192b24SSimon Glass printf(", "); 1812e192b24SSimon Glass printf("%s", mem_attr[j].name); 1822e192b24SSimon Glass } 1832e192b24SSimon Glass } 1842e192b24SSimon Glass putc('\n'); 1852e192b24SSimon Glass } 1862e192b24SSimon Glass if (skip_bs) 1872e192b24SSimon Glass printf("*Some areas are merged (use 'all' to see)\n"); 1882e192b24SSimon Glass } 1892e192b24SSimon Glass 1902e192b24SSimon Glass static int do_efi_mem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 1912e192b24SSimon Glass { 1922e192b24SSimon Glass struct efi_mem_desc *desc; 1932e192b24SSimon Glass struct efi_entry_memmap *map; 1942e192b24SSimon Glass int size, ret; 1952e192b24SSimon Glass bool skip_bs; 1962e192b24SSimon Glass 1972e192b24SSimon Glass skip_bs = !argc || *argv[0] != 'a'; 1982e192b24SSimon Glass ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); 1992e192b24SSimon Glass switch (ret) { 2002e192b24SSimon Glass case -ENOENT: 2012e192b24SSimon Glass printf("No EFI table available\n"); 2022e192b24SSimon Glass goto done; 2032e192b24SSimon Glass case -EPROTONOSUPPORT: 2042e192b24SSimon Glass printf("Incorrect EFI table version\n"); 2052e192b24SSimon Glass goto done; 2062e192b24SSimon Glass } 2072e192b24SSimon Glass printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n", 2082e192b24SSimon Glass gd->arch.table, map, size, map->version, map->desc_size); 2092e192b24SSimon Glass if (map->version != EFI_MEM_DESC_VERSION) { 2102e192b24SSimon Glass printf("Incorrect memory map version\n"); 2112e192b24SSimon Glass ret = -EPROTONOSUPPORT; 2122e192b24SSimon Glass goto done; 2132e192b24SSimon Glass } 2142e192b24SSimon Glass 2152e192b24SSimon Glass desc = efi_build_mem_table(map, size, skip_bs); 2162e192b24SSimon Glass if (!desc) { 2172e192b24SSimon Glass ret = -ENOMEM; 2182e192b24SSimon Glass goto done; 2192e192b24SSimon Glass } 2202e192b24SSimon Glass 2212e192b24SSimon Glass efi_print_mem_table(map, desc, skip_bs); 2222e192b24SSimon Glass free(desc); 2232e192b24SSimon Glass done: 2242e192b24SSimon Glass if (ret) 2252e192b24SSimon Glass printf("Error: %d\n", ret); 2262e192b24SSimon Glass 2272e192b24SSimon Glass return ret ? CMD_RET_FAILURE : 0; 2282e192b24SSimon Glass } 2292e192b24SSimon Glass 2302e192b24SSimon Glass static cmd_tbl_t efi_commands[] = { 2312e192b24SSimon Glass U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""), 2322e192b24SSimon Glass }; 2332e192b24SSimon Glass 2342e192b24SSimon Glass static int do_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 2352e192b24SSimon Glass { 2362e192b24SSimon Glass cmd_tbl_t *efi_cmd; 2372e192b24SSimon Glass int ret; 2382e192b24SSimon Glass 2392e192b24SSimon Glass if (argc < 2) 2402e192b24SSimon Glass return CMD_RET_USAGE; 2412e192b24SSimon Glass efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands)); 2422e192b24SSimon Glass argc -= 2; 2432e192b24SSimon Glass argv += 2; 2442e192b24SSimon Glass if (!efi_cmd || argc > efi_cmd->maxargs) 2452e192b24SSimon Glass return CMD_RET_USAGE; 2462e192b24SSimon Glass 2472e192b24SSimon Glass ret = efi_cmd->cmd(efi_cmd, flag, argc, argv); 2482e192b24SSimon Glass 2492e192b24SSimon Glass return cmd_process_error(efi_cmd, ret); 2502e192b24SSimon Glass } 2512e192b24SSimon Glass 2522e192b24SSimon Glass U_BOOT_CMD( 2532e192b24SSimon Glass efi, 3, 1, do_efi, 2542e192b24SSimon Glass "EFI access", 2552e192b24SSimon Glass "mem [all] Dump memory information [include boot services]" 2562e192b24SSimon Glass ); 257