1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2015 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7#include <common.h> 8#include <command.h> 9#include <efi.h> 10#include <efi_api.h> 11#include <errno.h> 12#include <log.h> 13#include <malloc.h> 14#include <sort.h> 15#include <uuid.h> 16#include <asm/global_data.h> 17 18DECLARE_GLOBAL_DATA_PTR; 19 20static const char *const type_name[] = { 21 "reserved", 22 "loader_code", 23 "loader_data", 24 "bs_code", 25 "bs_data", 26 "rt_code", 27 "rt_data", 28 "conv", 29 "unusable", 30 "acpi_reclaim", 31 "acpi_nvs", 32 "io", 33 "io_port", 34 "pal_code", 35}; 36 37static struct attr_info { 38 u64 val; 39 const char *name; 40} mem_attr[] = { 41 { EFI_MEMORY_UC, "uncached" }, 42 { EFI_MEMORY_WC, "write-coalescing" }, 43 { EFI_MEMORY_WT, "write-through" }, 44 { EFI_MEMORY_WB, "write-back" }, 45 { EFI_MEMORY_UCE, "uncached & exported" }, 46 { EFI_MEMORY_WP, "write-protect" }, 47 { EFI_MEMORY_RP, "read-protect" }, 48 { EFI_MEMORY_XP, "execute-protect" }, 49 { EFI_MEMORY_NV, "non-volatile" }, 50 { EFI_MEMORY_MORE_RELIABLE, "higher reliability" }, 51 { EFI_MEMORY_RO, "read-only" }, 52 { EFI_MEMORY_SP, "specific purpose" }, 53 { EFI_MEMORY_RUNTIME, "needs runtime mapping" } 54}; 55 56/* Maximum different attribute values we can track */ 57#define ATTR_SEEN_MAX 30 58 59static inline bool is_boot_services(int type) 60{ 61 return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA || 62 type == EFI_BOOT_SERVICES_CODE || 63 type == EFI_BOOT_SERVICES_DATA; 64} 65 66static int h_cmp_entry(const void *v1, const void *v2) 67{ 68 const struct efi_mem_desc *desc1 = v1; 69 const struct efi_mem_desc *desc2 = v2; 70 int64_t diff = desc1->physical_start - desc2->physical_start; 71 72 /* 73 * Manually calculate the difference to avoid sign loss in the 64-bit 74 * to 32-bit conversion 75 */ 76 return diff < 0 ? -1 : diff > 0 ? 1 : 0; 77} 78 79/** 80 * efi_build_mem_table() - make a sorted copy of the memory table 81 * 82 * @desc_base: Pointer to EFI memory map table 83 * @size: Size of table in bytes 84 * @desc_size: Size of each @desc_base record 85 * @skip_bs: True to skip boot-time memory and merge it with conventional 86 * memory. This will significantly reduce the number of table 87 * entries. 88 * Return: pointer to the new table. It should be freed with free() by the 89 * caller. 90 */ 91static void *efi_build_mem_table(struct efi_mem_desc *desc_base, int size, 92 int desc_size, bool skip_bs) 93{ 94 struct efi_mem_desc *desc, *end, *base, *dest, *prev; 95 int count; 96 u64 addr; 97 98 base = malloc(size + sizeof(*desc)); 99 if (!base) { 100 debug("%s: Cannot allocate %#x bytes\n", __func__, size); 101 return NULL; 102 } 103 end = (void *)desc_base + size; 104 count = ((ulong)end - (ulong)desc_base) / desc_size; 105 memcpy(base, desc_base, (ulong)end - (ulong)desc_base); 106 qsort(base, count, desc_size, h_cmp_entry); 107 prev = NULL; 108 addr = 0; 109 dest = base; 110 end = (struct efi_mem_desc *)((ulong)base + count * desc_size); 111 for (desc = base; desc < end; 112 desc = efi_get_next_mem_desc(desc, desc_size)) { 113 bool merge = true; 114 u32 type = desc->type; 115 116 if (type >= EFI_MAX_MEMORY_TYPE) { 117 printf("Memory map contains invalid entry type %u\n", 118 type); 119 continue; 120 } 121 122 if (skip_bs && is_boot_services(desc->type)) 123 type = EFI_CONVENTIONAL_MEMORY; 124 125 memcpy(dest, desc, desc_size); 126 dest->type = type; 127 if (!skip_bs || !prev) 128 merge = false; 129 else if (desc->physical_start != addr) 130 merge = false; 131 else if (type != EFI_CONVENTIONAL_MEMORY) 132 merge = false; 133 else if (prev->type != EFI_CONVENTIONAL_MEMORY) 134 merge = false; 135 136 if (merge) { 137 prev->num_pages += desc->num_pages; 138 } else { 139 prev = dest; 140 dest = efi_get_next_mem_desc(dest, desc_size); 141 } 142 addr = desc->physical_start + (desc->num_pages << 143 EFI_PAGE_SHIFT); 144 } 145 146 /* Mark the end */ 147 dest->type = EFI_MAX_MEMORY_TYPE; 148 149 return base; 150} 151 152static void efi_print_mem_table(struct efi_mem_desc *desc, int desc_size, 153 bool skip_bs) 154{ 155 u64 attr_seen[ATTR_SEEN_MAX]; 156 int attr_seen_count; 157 int upto, i; 158 u64 addr; 159 160 printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical", 161 "Virtual", "Size", "Attributes"); 162 163 /* Keep track of all the different attributes we have seen */ 164 attr_seen_count = 0; 165 addr = 0; 166 for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE; 167 upto++, desc = efi_get_next_mem_desc(desc, desc_size)) { 168 const char *name; 169 u64 size; 170 171 if (skip_bs && is_boot_services(desc->type)) 172 continue; 173 if (desc->physical_start != addr) { 174 printf(" %-14s %010llx %10s %010llx\n", "<gap>", 175 addr, "", desc->physical_start - addr); 176 } 177 size = desc->num_pages << EFI_PAGE_SHIFT; 178 179 name = desc->type < ARRAY_SIZE(type_name) ? 180 type_name[desc->type] : "<invalid>"; 181 printf("%2d %x:%-12s %010llx %010llx %010llx ", upto, 182 desc->type, name, desc->physical_start, 183 desc->virtual_start, size); 184 if (desc->attribute & EFI_MEMORY_RUNTIME) 185 putc('r'); 186 printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME); 187 putc('\n'); 188 189 for (i = 0; i < attr_seen_count; i++) { 190 if (attr_seen[i] == desc->attribute) 191 break; 192 } 193 if (i == attr_seen_count && i < ATTR_SEEN_MAX) 194 attr_seen[attr_seen_count++] = desc->attribute; 195 addr = desc->physical_start + size; 196 } 197 198 printf("\nAttributes key:\n"); 199 for (i = 0; i < attr_seen_count; i++) { 200 u64 attr = attr_seen[i]; 201 bool first; 202 int j; 203 204 printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ', 205 attr & ~EFI_MEMORY_RUNTIME); 206 for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) { 207 if (attr & mem_attr[j].val) { 208 if (first) 209 first = false; 210 else 211 printf(", "); 212 printf("%s", mem_attr[j].name); 213 } 214 } 215 putc('\n'); 216 } 217 if (skip_bs) 218 printf("*Some areas are merged (use 'all' to see)\n"); 219} 220 221static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc, 222 char *const argv[]) 223{ 224 struct efi_mem_desc *orig, *desc; 225 uint version, key; 226 int desc_size; 227 int size, ret; 228 bool skip_bs; 229 230 skip_bs = !argc || *argv[0] != 'a'; 231 if (IS_ENABLED(CONFIG_EFI_APP)) { 232 ret = efi_get_mmap(&orig, &size, &key, &desc_size, &version); 233 if (ret) { 234 printf("Cannot read memory map (err=%d)\n", ret); 235 return CMD_RET_FAILURE; 236 } 237 } else { 238 struct efi_entry_memmap *map; 239 240 ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); 241 switch (ret) { 242 case -ENOENT: 243 printf("No EFI table available\n"); 244 goto done; 245 case -EPROTONOSUPPORT: 246 printf("Incorrect EFI table version\n"); 247 goto done; 248 } 249 orig = map->desc; 250 desc_size = map->desc_size; 251 version = map->version; 252 } 253 printf("EFI table at %lx, memory map %p, size %x, key %x, version %x, descr. size %#x\n", 254 gd->arch.table, orig, size, key, version, desc_size); 255 if (version != EFI_MEM_DESC_VERSION) { 256 printf("Incorrect memory map version\n"); 257 ret = -EPROTONOSUPPORT; 258 goto done; 259 } 260 261 desc = efi_build_mem_table(orig, size, desc_size, skip_bs); 262 if (!desc) { 263 ret = -ENOMEM; 264 goto done; 265 } 266 267 efi_print_mem_table(desc, desc_size, skip_bs); 268 free(desc); 269 if (IS_ENABLED(CONFIG_EFI_APP)) 270 free(orig); 271done: 272 if (ret) 273 printf("Error: %d\n", ret); 274 275 return ret ? CMD_RET_FAILURE : 0; 276} 277 278static int do_efi_tables(struct cmd_tbl *cmdtp, int flag, int argc, 279 char *const argv[]) 280{ 281 struct efi_system_table *systab; 282 283 if (IS_ENABLED(CONFIG_EFI_APP)) { 284 systab = efi_get_sys_table(); 285 if (!systab) { 286 printf("Cannot read system table\n"); 287 return CMD_RET_FAILURE; 288 } 289 } else { 290 int size; 291 int ret; 292 293 ret = efi_info_get(EFIET_SYS_TABLE, (void **)&systab, &size); 294 if (ret) /* this should not happen */ 295 return CMD_RET_FAILURE; 296 } 297 298 efi_show_tables(systab); 299 300 return 0; 301} 302 303static struct cmd_tbl efi_commands[] = { 304 U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""), 305 U_BOOT_CMD_MKENT(tables, 1, 1, do_efi_tables, "", ""), 306}; 307 308static int do_efi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 309{ 310 struct cmd_tbl *efi_cmd; 311 int ret; 312 313 if (argc < 2) 314 return CMD_RET_USAGE; 315 efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands)); 316 argc -= 2; 317 argv += 2; 318 if (!efi_cmd || argc > efi_cmd->maxargs) 319 return CMD_RET_USAGE; 320 321 ret = efi_cmd->cmd(efi_cmd, flag, argc, argv); 322 323 return cmd_process_error(efi_cmd, ret); 324} 325 326U_BOOT_CMD( 327 efi, 3, 1, do_efi, 328 "EFI access", 329 "mem [all] Dump memory information [include boot services]\n" 330 "tables Dump tables" 331); 332