xref: /openbmc/u-boot/cmd/efi.c (revision c882163b09b8a2c52e3dd8acd7d296d6d06d1f2e)
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