blob: d2400acbbbafb998dadd4cc74f461081d6629570 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glassf1a0baf2015-08-04 12:33:59 -06002/*
3 * (C) Copyright 2015 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glassf1a0baf2015-08-04 12:33:59 -06005 */
6
7#include <common.h>
8#include <command.h>
9#include <efi.h>
10#include <errno.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.h>
Simon Glassf1a0baf2015-08-04 12:33:59 -060012#include <malloc.h>
Simon Glass8bef79b2019-11-14 12:57:19 -070013#include <sort.h>
Simon Glass401d1c42020-10-30 21:38:53 -060014#include <asm/global_data.h>
Simon Glassf1a0baf2015-08-04 12:33:59 -060015
16static const char *const type_name[] = {
17 "reserved",
18 "loader_code",
19 "loader_data",
20 "bs_code",
21 "bs_data",
22 "rt_code",
23 "rt_data",
24 "conv",
25 "unusable",
26 "acpi_reclaim",
27 "acpi_nvs",
28 "io",
29 "io_port",
30 "pal_code",
31};
32
33static struct attr_info {
Eugeniu Rosca9b891832018-07-14 22:53:30 +020034 u64 val;
Simon Glassf1a0baf2015-08-04 12:33:59 -060035 const char *name;
36} mem_attr[] = {
Eugeniu Rosca9b891832018-07-14 22:53:30 +020037 { EFI_MEMORY_UC, "uncached" },
38 { EFI_MEMORY_WC, "write-coalescing" },
39 { EFI_MEMORY_WT, "write-through" },
40 { EFI_MEMORY_WB, "write-back" },
41 { EFI_MEMORY_UCE, "uncached & exported" },
42 { EFI_MEMORY_WP, "write-protect" },
43 { EFI_MEMORY_RP, "read-protect" },
44 { EFI_MEMORY_XP, "execute-protect" },
Eugeniu Roscac3a40cc2018-07-14 22:53:31 +020045 { EFI_MEMORY_NV, "non-volatile" },
46 { EFI_MEMORY_MORE_RELIABLE, "higher reliability" },
47 { EFI_MEMORY_RO, "read-only" },
Heinrich Schuchardt255a4732020-05-19 07:20:46 +020048 { EFI_MEMORY_SP, "specific purpose" },
Eugeniu Rosca9b891832018-07-14 22:53:30 +020049 { EFI_MEMORY_RUNTIME, "needs runtime mapping" }
Simon Glassf1a0baf2015-08-04 12:33:59 -060050};
51
52/* Maximum different attribute values we can track */
53#define ATTR_SEEN_MAX 30
54
55static inline bool is_boot_services(int type)
56{
57 return type == EFI_LOADER_CODE || type == EFI_LOADER_DATA ||
58 type == EFI_BOOT_SERVICES_CODE ||
59 type == EFI_BOOT_SERVICES_DATA;
60}
61
62static int h_cmp_entry(const void *v1, const void *v2)
63{
64 const struct efi_mem_desc *desc1 = v1;
65 const struct efi_mem_desc *desc2 = v2;
66 int64_t diff = desc1->physical_start - desc2->physical_start;
67
68 /*
69 * Manually calculate the difference to avoid sign loss in the 64-bit
70 * to 32-bit conversion
71 */
72 return diff < 0 ? -1 : diff > 0 ? 1 : 0;
73}
74
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +020075/**
76 * efi_build_mem_table() - make a sorted copy of the memory table
77 *
Simon Glassce1dc0c2022-01-04 03:51:11 -070078 * @desc_base: Pointer to EFI memory map table
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +020079 * @size: Size of table in bytes
Simon Glassce1dc0c2022-01-04 03:51:11 -070080 * @desc_size: Size of each @desc_base record
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +020081 * @skip_bs: True to skip boot-time memory and merge it with conventional
82 * memory. This will significantly reduce the number of table
83 * entries.
84 * Return: pointer to the new table. It should be freed with free() by the
85 * caller.
86 */
Simon Glassce1dc0c2022-01-04 03:51:11 -070087static void *efi_build_mem_table(struct efi_mem_desc *desc_base, int size,
88 int desc_size, bool skip_bs)
Simon Glassf1a0baf2015-08-04 12:33:59 -060089{
90 struct efi_mem_desc *desc, *end, *base, *dest, *prev;
91 int count;
92 u64 addr;
93
94 base = malloc(size + sizeof(*desc));
95 if (!base) {
96 debug("%s: Cannot allocate %#x bytes\n", __func__, size);
97 return NULL;
98 }
Simon Glassce1dc0c2022-01-04 03:51:11 -070099 end = (void *)desc_base + size;
100 count = ((ulong)end - (ulong)desc_base) / desc_size;
101 memcpy(base, desc_base, (ulong)end - (ulong)desc_base);
102 qsort(base, count, desc_size, h_cmp_entry);
Simon Glassf1a0baf2015-08-04 12:33:59 -0600103 prev = NULL;
104 addr = 0;
105 dest = base;
Simon Glassce1dc0c2022-01-04 03:51:11 -0700106 end = (struct efi_mem_desc *)((ulong)base + count * desc_size);
107 for (desc = base; desc < end;
108 desc = efi_get_next_mem_desc(desc, desc_size)) {
Simon Glassf1a0baf2015-08-04 12:33:59 -0600109 bool merge = true;
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +0200110 u32 type = desc->type;
111
112 if (type >= EFI_MAX_MEMORY_TYPE) {
113 printf("Memory map contains invalid entry type %u\n",
114 type);
115 continue;
116 }
Simon Glassf1a0baf2015-08-04 12:33:59 -0600117
118 if (skip_bs && is_boot_services(desc->type))
119 type = EFI_CONVENTIONAL_MEMORY;
120
Simon Glassce1dc0c2022-01-04 03:51:11 -0700121 memcpy(dest, desc, desc_size);
Simon Glassf1a0baf2015-08-04 12:33:59 -0600122 dest->type = type;
123 if (!skip_bs || !prev)
124 merge = false;
125 else if (desc->physical_start != addr)
126 merge = false;
127 else if (type != EFI_CONVENTIONAL_MEMORY)
128 merge = false;
129 else if (prev->type != EFI_CONVENTIONAL_MEMORY)
130 merge = false;
131
132 if (merge) {
133 prev->num_pages += desc->num_pages;
134 } else {
135 prev = dest;
Simon Glassce1dc0c2022-01-04 03:51:11 -0700136 dest = efi_get_next_mem_desc(dest, desc_size);
Simon Glassf1a0baf2015-08-04 12:33:59 -0600137 }
138 addr = desc->physical_start + (desc->num_pages <<
139 EFI_PAGE_SHIFT);
140 }
141
142 /* Mark the end */
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +0200143 dest->type = EFI_MAX_MEMORY_TYPE;
Simon Glassf1a0baf2015-08-04 12:33:59 -0600144
145 return base;
146}
147
Simon Glassce1dc0c2022-01-04 03:51:11 -0700148static void efi_print_mem_table(struct efi_mem_desc *desc, int desc_size,
149 bool skip_bs)
Simon Glassf1a0baf2015-08-04 12:33:59 -0600150{
151 u64 attr_seen[ATTR_SEEN_MAX];
152 int attr_seen_count;
153 int upto, i;
154 u64 addr;
155
156 printf(" # %-14s %10s %10s %10s %s\n", "Type", "Physical",
157 "Virtual", "Size", "Attributes");
158
159 /* Keep track of all the different attributes we have seen */
160 attr_seen_count = 0;
161 addr = 0;
Heinrich Schuchardt5b94e262020-08-27 12:39:03 +0200162 for (upto = 0; desc->type != EFI_MAX_MEMORY_TYPE;
Simon Glassce1dc0c2022-01-04 03:51:11 -0700163 upto++, desc = efi_get_next_mem_desc(desc, desc_size)) {
Simon Glassf1a0baf2015-08-04 12:33:59 -0600164 const char *name;
165 u64 size;
166
167 if (skip_bs && is_boot_services(desc->type))
168 continue;
169 if (desc->physical_start != addr) {
170 printf(" %-14s %010llx %10s %010llx\n", "<gap>",
171 addr, "", desc->physical_start - addr);
172 }
173 size = desc->num_pages << EFI_PAGE_SHIFT;
174
175 name = desc->type < ARRAY_SIZE(type_name) ?
176 type_name[desc->type] : "<invalid>";
177 printf("%2d %x:%-12s %010llx %010llx %010llx ", upto,
178 desc->type, name, desc->physical_start,
179 desc->virtual_start, size);
180 if (desc->attribute & EFI_MEMORY_RUNTIME)
181 putc('r');
182 printf("%llx", desc->attribute & ~EFI_MEMORY_RUNTIME);
183 putc('\n');
184
185 for (i = 0; i < attr_seen_count; i++) {
186 if (attr_seen[i] == desc->attribute)
187 break;
188 }
189 if (i == attr_seen_count && i < ATTR_SEEN_MAX)
190 attr_seen[attr_seen_count++] = desc->attribute;
191 addr = desc->physical_start + size;
192 }
193
194 printf("\nAttributes key:\n");
195 for (i = 0; i < attr_seen_count; i++) {
196 u64 attr = attr_seen[i];
197 bool first;
198 int j;
199
Eugeniu Roscadbb148b2018-07-14 22:53:32 +0200200 printf("%c%llx: ", (attr & EFI_MEMORY_RUNTIME) ? 'r' : ' ',
Simon Glassf1a0baf2015-08-04 12:33:59 -0600201 attr & ~EFI_MEMORY_RUNTIME);
202 for (j = 0, first = true; j < ARRAY_SIZE(mem_attr); j++) {
Eugeniu Rosca9b891832018-07-14 22:53:30 +0200203 if (attr & mem_attr[j].val) {
Simon Glassf1a0baf2015-08-04 12:33:59 -0600204 if (first)
205 first = false;
206 else
207 printf(", ");
208 printf("%s", mem_attr[j].name);
209 }
210 }
211 putc('\n');
212 }
213 if (skip_bs)
214 printf("*Some areas are merged (use 'all' to see)\n");
215}
216
Simon Glass09140112020-05-10 11:40:03 -0600217static int do_efi_mem(struct cmd_tbl *cmdtp, int flag, int argc,
218 char *const argv[])
Simon Glassf1a0baf2015-08-04 12:33:59 -0600219{
220 struct efi_mem_desc *desc;
221 struct efi_entry_memmap *map;
222 int size, ret;
223 bool skip_bs;
224
225 skip_bs = !argc || *argv[0] != 'a';
226 ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
227 switch (ret) {
228 case -ENOENT:
229 printf("No EFI table available\n");
230 goto done;
231 case -EPROTONOSUPPORT:
232 printf("Incorrect EFI table version\n");
233 goto done;
234 }
235 printf("EFI table at %lx, memory map %p, size %x, version %x, descr. size %#x\n",
236 gd->arch.table, map, size, map->version, map->desc_size);
237 if (map->version != EFI_MEM_DESC_VERSION) {
238 printf("Incorrect memory map version\n");
239 ret = -EPROTONOSUPPORT;
240 goto done;
241 }
242
Simon Glassce1dc0c2022-01-04 03:51:11 -0700243 desc = efi_build_mem_table(map->desc, size, map->desc_size, skip_bs);
Simon Glassf1a0baf2015-08-04 12:33:59 -0600244 if (!desc) {
245 ret = -ENOMEM;
246 goto done;
247 }
248
Simon Glassce1dc0c2022-01-04 03:51:11 -0700249 efi_print_mem_table(desc, map->desc_size, skip_bs);
Simon Glassf1a0baf2015-08-04 12:33:59 -0600250 free(desc);
251done:
252 if (ret)
253 printf("Error: %d\n", ret);
254
255 return ret ? CMD_RET_FAILURE : 0;
256}
257
Simon Glass09140112020-05-10 11:40:03 -0600258static struct cmd_tbl efi_commands[] = {
Simon Glassf1a0baf2015-08-04 12:33:59 -0600259 U_BOOT_CMD_MKENT(mem, 1, 1, do_efi_mem, "", ""),
260};
261
Simon Glass09140112020-05-10 11:40:03 -0600262static int do_efi(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Simon Glassf1a0baf2015-08-04 12:33:59 -0600263{
Simon Glass09140112020-05-10 11:40:03 -0600264 struct cmd_tbl *efi_cmd;
Simon Glassf1a0baf2015-08-04 12:33:59 -0600265 int ret;
266
267 if (argc < 2)
268 return CMD_RET_USAGE;
269 efi_cmd = find_cmd_tbl(argv[1], efi_commands, ARRAY_SIZE(efi_commands));
270 argc -= 2;
271 argv += 2;
272 if (!efi_cmd || argc > efi_cmd->maxargs)
273 return CMD_RET_USAGE;
274
275 ret = efi_cmd->cmd(efi_cmd, flag, argc, argv);
276
277 return cmd_process_error(efi_cmd, ret);
278}
279
280U_BOOT_CMD(
281 efi, 3, 1, do_efi,
282 "EFI access",
283 "mem [all] Dump memory information [include boot services]"
284);