xref: /freebsd/stand/kboot/libkboot/efi.c (revision 92ad79ec33fb5caf9a79c0bd8b33697b34c8e26d)
1 /*
2  * Copyright (c) 2024 Netflix, Inc
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/param.h>
8 #include <sys/linker.h>
9 #include "stand.h"
10 #include "bootstrap.h"
11 #include "efi.h"
12 #include "seg.h"
13 #include "util.h"
14 
15 vm_paddr_t efi_systbl_phys;
16 struct efi_map_header *efi_map_hdr;
17 uint32_t efi_map_size;
18 vm_paddr_t efi_map_phys_src;	/* From DTB */
19 vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
20 
21 void
efi_set_systbl(uint64_t tbl)22 efi_set_systbl(uint64_t tbl)
23 {
24 	efi_systbl_phys = tbl;
25 }
26 
27 #if 0
28 /* Note: This is useless since runtime-map is a subset */
29 void
30 efi_read_from_sysfs(void)
31 {
32 	uint32_t efisz, sz, map_size;
33 	int entries = 0;
34 	struct efi_md *map;		/* Really an array */
35 	char *buf;
36 	struct stat sb;
37 	char fn[100];
38 
39 	/*
40 	 * Count the number of entries we have. They are numbered from 0
41 	 * through entries - 1.
42 	 */
43 	do {
44 		printf("Looking at index %d\n", entries);
45 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", entries++);
46 	} while (stat(fn, &sb) == 0);
47 
48 	/*
49 	 * We incremented entries one past the first failure, so we need to
50 	 * adjust the count and the test for 'nothing found' is against 1.
51 	 */
52 	if (entries == 1)
53 		goto err;
54 	entries--;
55 
56 	/* XXX lots of copied code, refactor? */
57 	map_size = sizeof(struct efi_md) * entries;
58 	efisz = roundup2(sizeof(*efi_map_hdr), 16);
59 	sz = efisz + map_size;
60 	buf = malloc(efisz + map_size);
61 	if (buf == NULL)
62 		return;
63 	efi_map_hdr = (struct efi_map_header *)buf;
64 	efi_map_size = sz;
65 	map = (struct efi_md *)(buf + efisz);
66 	bzero(map, sz);
67 	efi_map_hdr->memory_size = map_size;
68 	efi_map_hdr->descriptor_size = sizeof(struct efi_md);
69 	efi_map_hdr->descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
70 	for (int i = 0; i < entries; i++) {
71 		struct efi_md *m;
72 
73 		printf("Populating index %d\n", i);
74 		m = map + i;
75 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/type", i);
76 		if (!file2u32(fn, &m->md_type))
77 			goto err;
78 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", i);
79 		if (!file2u64(fn, &m->md_phys))
80 			goto err;
81 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/virt_addr", i);
82 		if (!file2u64(fn, &m->md_virt))
83 			goto err;
84 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/num_pages", i);
85 		if (!file2u64(fn, &m->md_pages))
86 			goto err;
87 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/attribute", i);
88 		if (!file2u64(fn, &m->md_attr))
89 			goto err;
90 	}
91 	efi_map_phys_src = 0;
92 	printf("UEFI MAP:\n");
93 	print_efi_map(efi_map_hdr);
94 	printf("DONE\n");
95 	return;
96 err:
97 	printf("Parse error in reading current memory map\n");
98 }
99 #endif
100 
101 /*
102  * We may have no ability to read the PA that this map is in, so pass
103  * the address to FreeBSD via a rather odd flag entry as the first map
104  * so early boot can copy the memory map into this space and have the
105  * rest of the code cope.
106  */
107 bool
efi_read_from_pa(uint64_t pa,uint32_t map_size,uint32_t desc_size,uint32_t vers)108 efi_read_from_pa(uint64_t pa, uint32_t map_size, uint32_t desc_size, uint32_t vers)
109 {
110 	uint32_t efisz, sz;
111 	char *buf;
112 	int fd2, len;
113 	struct efi_md *map;		/* Really an array */
114 
115 	/*
116 	 * We may have no ability to read the PA that this map is in, so pass
117 	 * the address to FreeBSD via a rather odd flag entry as the first map
118 	 * so early boot can copy the memory map into this space and have the
119 	 * rest of the code cope. We also have to round the size of the header
120 	 * to 16 byte boundary.
121 	 */
122 	efisz = roundup2(sizeof(*efi_map_hdr), 16);
123 	sz = efisz + map_size;
124 	buf = malloc(efisz + map_size);
125 	if (buf == NULL)
126 		return false;
127 	efi_map_hdr = (struct efi_map_header *)buf;
128 	efi_map_size = sz;
129 	map = (struct efi_md *)(buf + efisz);
130 	bzero(map, sz);
131 	efi_map_hdr->memory_size = map_size;
132 	efi_map_hdr->descriptor_size = desc_size;
133 	efi_map_hdr->descriptor_version = vers;
134 
135 	/*
136 	 * Try to read in the actual UEFI map. This may fail, and that's OK. We just
137 	 * won't print the map.
138 	 */
139 	fd2 = open("host:/dev/mem", O_RDONLY);
140 	if (fd2 < 0)
141 		goto no_read;
142 	if (lseek(fd2, pa, SEEK_SET) < 0)
143 		goto no_read;
144 	len = read(fd2, map, sz);
145 	if (len != sz)
146 		goto no_read;
147 	efi_map_phys_src = 0;		/* Mark MODINFOMD_EFI_MAP as valid */
148 	close(fd2);
149 	printf("UEFI MAP:\n");
150 	print_efi_map(efi_map_hdr);
151 	return (true);
152 
153 no_read:				/* Just get it the trampoline */
154 	efi_map_phys_src = pa;
155 	close(fd2);
156 	return (true);
157 }
158 
159 void
foreach_efi_map_entry(struct efi_map_header * efihdr,efi_map_entry_cb cb,void * argp)160 foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
161 {
162 	struct efi_md *map, *p;
163 	size_t efisz;
164 	int ndesc, i;
165 
166 	/*
167 	 * Memory map data provided by UEFI via the GetMemoryMap
168 	 * Boot Services API.
169 	 */
170 	efisz = roundup2(sizeof(struct efi_map_header), 16);
171 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
172 
173 	if (efihdr->descriptor_size == 0)
174 		return;
175 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
176 
177 	for (i = 0, p = map; i < ndesc; i++,
178 	    p = efi_next_descriptor(p, efihdr->descriptor_size)) {
179 		cb(p, argp);
180 	}
181 }
182 
183 /* XXX REFACTOR WITH KERNEL */
184 static void
print_efi_map_entry(struct efi_md * p,void * argp __unused)185 print_efi_map_entry(struct efi_md *p, void *argp __unused)
186 {
187 	const char *type;
188 	static const char *types[] = {
189 		"Reserved",
190 		"LoaderCode",
191 		"LoaderData",
192 		"BootServicesCode",
193 		"BootServicesData",
194 		"RuntimeServicesCode",
195 		"RuntimeServicesData",
196 		"ConventionalMemory",
197 		"UnusableMemory",
198 		"ACPIReclaimMemory",
199 		"ACPIMemoryNVS",
200 		"MemoryMappedIO",
201 		"MemoryMappedIOPortSpace",
202 		"PalCode",
203 		"PersistentMemory"
204 	};
205 
206 	if (p->md_type < nitems(types))
207 		type = types[p->md_type];
208 	else
209 		type = "<INVALID>";
210 	printf("%23s %012lx %012lx %08lx ", type, p->md_phys,
211 	    p->md_virt, p->md_pages);
212 	if (p->md_attr & EFI_MD_ATTR_UC)
213 		printf("UC ");
214 	if (p->md_attr & EFI_MD_ATTR_WC)
215 		printf("WC ");
216 	if (p->md_attr & EFI_MD_ATTR_WT)
217 		printf("WT ");
218 	if (p->md_attr & EFI_MD_ATTR_WB)
219 		printf("WB ");
220 	if (p->md_attr & EFI_MD_ATTR_UCE)
221 		printf("UCE ");
222 	if (p->md_attr & EFI_MD_ATTR_WP)
223 		printf("WP ");
224 	if (p->md_attr & EFI_MD_ATTR_RP)
225 		printf("RP ");
226 	if (p->md_attr & EFI_MD_ATTR_XP)
227 		printf("XP ");
228 	if (p->md_attr & EFI_MD_ATTR_NV)
229 		printf("NV ");
230 	if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
231 		printf("MORE_RELIABLE ");
232 	if (p->md_attr & EFI_MD_ATTR_RO)
233 		printf("RO ");
234 	if (p->md_attr & EFI_MD_ATTR_RT)
235 		printf("RUNTIME");
236 	printf("\n");
237 }
238 
239 void
print_efi_map(struct efi_map_header * efihdr)240 print_efi_map(struct efi_map_header *efihdr)
241 {
242 	printf("%23s %12s %12s %8s %4s\n",
243 	    "Type", "Physical", "Virtual", "#Pages", "Attr");
244 
245 	foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL);
246 }
247 
248 void
efi_bi_loadsmap(struct preloaded_file * kfp)249 efi_bi_loadsmap(struct preloaded_file *kfp)
250 {
251 	/*
252 	 * Make a note of a systbl. This is nearly mandatory on AARCH64.
253 	 */
254 	if (efi_systbl_phys)
255 		file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys);
256 
257 	/*
258 	 * If we have efi_map_hdr, then it's a pointer to the PA where this
259 	 * memory map lives. The trampoline code will copy it over. If we don't
260 	 * have it, panic because /proc/iomem isn't sufficient and there's no
261 	 * hope.
262 	 */
263 	if (efi_map_hdr != NULL) {
264 		file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr);
265 		return;
266 	}
267 
268 	panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n");
269 }
270