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 <errno.h> 11 #include <malloc.h> 12 13 static const char *const type_name[] = { 14 "reserved", 15 "loader_code", 16 "loader_data", 17 "bs_code", 18 "bs_data", 19 "rt_code", 20 "rt_data", 21 "conv", 22 "unusable", 23 "acpi_reclaim", 24 "acpi_nvs", 25 "io", 26 "io_port", 27 "pal_code", 28 }; 29 30 static struct attr_info { 31 u64 val; 32 const char *name; 33 } mem_attr[] = { 34 { EFI_MEMORY_UC, "uncached" }, 35 { EFI_MEMORY_WC, "write-coalescing" }, 36 { EFI_MEMORY_WT, "write-through" }, 37 { EFI_MEMORY_WB, "write-back" }, 38 { EFI_MEMORY_UCE, "uncached & exported" }, 39 { EFI_MEMORY_WP, "write-protect" }, 40 { EFI_MEMORY_RP, "read-protect" }, 41 { EFI_MEMORY_XP, "execute-protect" }, 42 { EFI_MEMORY_NV, "non-volatile" }, 43 { EFI_MEMORY_MORE_RELIABLE, "higher reliability" }, 44 { EFI_MEMORY_RO, "read-only" }, 45 { EFI_MEMORY_RUNTIME, "needs runtime mapping" } 46 }; 47 48 /* Maximum different attribute values we can track */ 49 #define ATTR_SEEN_MAX 30 50 51 static inline bool is_boot_services(int type) 52 { 53 return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA || 54 type == EFI_BOOT_SERVICES_CODE || 55 type == EFI_BOOT_SERVICES_DATA; 56 } 57 58 static int h_cmp_entry(const void *v1, const void *v2) 59 { 60 const struct efi_mem_desc *desc1 = v1; 61 const struct efi_mem_desc *desc2 = v2; 62 int64_t diff = desc1->physical_start - desc2->physical_start; 63 64 /* 65 * Manually calculate the difference to avoid sign loss in the 64-bit 66 * to 32-bit conversion 67 */ 68 return diff < 0 ? -1 : diff > 0 ? 1 : 0; 69 } 70 71 void *efi_build_mem_table(struct efi_entry_memmap *map, int size, bool skip_bs) 72 { 73 struct efi_mem_desc *desc, *end, *base, *dest, *prev; 74 int count; 75 u64 addr; 76 77 base = malloc(size + sizeof(*desc)); 78 if (!base) { 79 debug("%s: Cannot allocate %#x bytes\n", __func__, size); 80 return NULL; 81 } 82 end = (struct efi_mem_desc *)((ulong)map + size); 83 count = ((ulong)end - (ulong)map->desc) / map->desc_size; 84 memcpy(base, map->desc, (ulong)end - (ulong)map->desc); 85 qsort(base, count, map->desc_size, h_cmp_entry); 86 prev = NULL; 87 addr = 0; 88 dest = base; 89 end = (struct efi_mem_desc *)((ulong)base + count * map->desc_size); 90 for (desc = base; desc < end; desc = efi_get_next_mem_desc(map, desc)) { 91 bool merge = true; 92 int type = desc->type; 93 94 if (skip_bs && is_boot_services(desc->type)) 95 type = EFI_CONVENTIONAL_MEMORY; 96 97 memcpy(dest, desc, map->desc_size); 98 dest->type = type; 99 if (!skip_bs || !prev) 100 merge = false; 101 else if (desc->physical_start != addr) 102 merge = false; 103 else if (type != EFI_CONVENTIONAL_MEMORY) 104 merge = false; 105 else if (prev->type != EFI_CONVENTIONAL_MEMORY) 106 merge = false; 107 108 if (merge) { 109 prev->num_pages += desc->num_pages; 110 } else { 111 prev = dest; 112 dest = efi_get_next_mem_desc(map, dest); 113 } 114 addr = desc->physical_start + (desc->num_pages << 115 EFI_PAGE_SHIFT); 116 } 117 118 /* Mark the end */ 119 dest->type = EFI_TABLE_END; 120 121 return base; 122 } 123 124 static void efi_print_mem_table(struct efi_entry_memmap *map, 125 struct efi_mem_desc *desc, bool skip_bs) 126 { 127 u64 attr_seen[ATTR_SEEN_MAX]; 128 int attr_seen_count; 129 int upto, i; 130 u64 addr; 131 132 printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical", 133 "Virtual", "Size", "Attributes"); 134 135 /* Keep track of all the different attributes we have seen */ 136 attr_seen_count = 0; 137 addr = 0; 138 for (upto = 0; desc->type != EFI_TABLE_END; 139 upto++, desc = efi_get_next_mem_desc(map, desc)) { 140 const char *name; 141 u64 size; 142 143 if (skip_bs && is_boot_services(desc->type)) 144 continue; 145 if (desc->physical_start != addr) { 146 printf(" %-14s %010llx %10s %010llx\n", "<gap>", 147 addr, "", desc->physical_start - addr); 148 } 149 size = desc->num_pages << EFI_PAGE_SHIFT; 150 151 name = desc->type < ARRAY_SIZE(type_name) ? 152 type_name[desc->type] : "<invalid>"; 153 printf("%2d %x:%-12s %010llx %010llx %010llx ", upto, 154 desc->type, name, desc->physical_start, 155 desc->virtual_start, size); 156 if (desc->attribute & EFI_MEMORY_RUNTIME) 157 putc('r'); 158 printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME); 159 putc('\n'); 160 161 for (i = 0; i < attr_seen_count; i++) { 162 if (attr_seen[i] == desc->attribute) 163 break; 164 } 165 if (i == attr_seen_count && i < ATTR_SEEN_MAX) 166 attr_seen[attr_seen_count++] = desc->attribute; 167 addr = desc->physical_start + size; 168 } 169 170 printf("\nAttributes key:\n"); 171 for (i = 0; i < attr_seen_count; i++) { 172 u64 attr = attr_seen[i]; 173 bool first; 174 int j; 175 176 printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ', 177 attr & ~EFI_MEMORY_RUNTIME); 178 for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) { 179 if (attr & mem_attr[j].val) { 180 if (first) 181 first = false; 182 else 183 printf(", "); 184 printf("%s", mem_attr[j].name); 185 } 186 } 187 putc('\n'); 188 } 189 if (skip_bs) 190 printf("*Some areas are merged (use 'all' to see)\n"); 191 } 192 193 static int do_efi_mem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 194 { 195 struct efi_mem_desc *desc; 196 struct efi_entry_memmap *map; 197 int size, ret; 198 bool skip_bs; 199 200 skip_bs = !argc || *argv[0] != 'a'; 201 ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size); 202 switch (ret) { 203 case -ENOENT: 204 printf("No EFI table available\n"); 205 goto done; 206 case -EPROTONOSUPPORT: 207 printf("Incorrect EFI table version\n"); 208 goto done; 209 } 210 printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n", 211 gd->arch.table, map, size, map->version, map->desc_size); 212 if (map->version != EFI_MEM_DESC_VERSION) { 213 printf("Incorrect memory map version\n"); 214 ret = -EPROTONOSUPPORT; 215 goto done; 216 } 217 218 desc = efi_build_mem_table(map, size, skip_bs); 219 if (!desc) { 220 ret = -ENOMEM; 221 goto done; 222 } 223 224 efi_print_mem_table(map, desc, skip_bs); 225 free(desc); 226 done: 227 if (ret) 228 printf("Error: %d\n", ret); 229 230 return ret ? CMD_RET_FAILURE : 0; 231 } 232 233 static cmd_tbl_t efi_commands[] = { 234 U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""), 235 }; 236 237 static int do_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 238 { 239 cmd_tbl_t *efi_cmd; 240 int ret; 241 242 if (argc < 2) 243 return CMD_RET_USAGE; 244 efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands)); 245 argc -= 2; 246 argv += 2; 247 if (!efi_cmd || argc > efi_cmd->maxargs) 248 return CMD_RET_USAGE; 249 250 ret = efi_cmd->cmd(efi_cmd, flag, argc, argv); 251 252 return cmd_process_error(efi_cmd, ret); 253 } 254 255 U_BOOT_CMD( 256 efi, 3, 1, do_efi, 257 "EFI access", 258 "mem [all] Dump memory information [include boot services]" 259 ); 260