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