xref: /freebsd/stand/kboot/libkboot/efi.c (revision 92ad79ec33fb5caf9a79c0bd8b33697b34c8e26d)
1136d2dc0SWarner Losh /*
2136d2dc0SWarner Losh  * Copyright (c) 2024 Netflix, Inc
3136d2dc0SWarner Losh  *
4136d2dc0SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5136d2dc0SWarner Losh  */
6136d2dc0SWarner Losh 
7136d2dc0SWarner Losh #include <sys/param.h>
8*92ad79ecSWarner Losh #include <sys/linker.h>
9136d2dc0SWarner Losh #include "stand.h"
10*92ad79ecSWarner Losh #include "bootstrap.h"
11136d2dc0SWarner Losh #include "efi.h"
12988ee1ccSWarner Losh #include "seg.h"
13*92ad79ecSWarner Losh #include "util.h"
14*92ad79ecSWarner Losh 
15*92ad79ecSWarner Losh vm_paddr_t efi_systbl_phys;
16*92ad79ecSWarner Losh struct efi_map_header *efi_map_hdr;
17*92ad79ecSWarner Losh uint32_t efi_map_size;
18*92ad79ecSWarner Losh vm_paddr_t efi_map_phys_src;	/* From DTB */
19*92ad79ecSWarner Losh vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
20*92ad79ecSWarner Losh 
21*92ad79ecSWarner Losh void
efi_set_systbl(uint64_t tbl)22*92ad79ecSWarner Losh efi_set_systbl(uint64_t tbl)
23*92ad79ecSWarner Losh {
24*92ad79ecSWarner Losh 	efi_systbl_phys = tbl;
25*92ad79ecSWarner Losh }
26*92ad79ecSWarner Losh 
27*92ad79ecSWarner Losh #if 0
28*92ad79ecSWarner Losh /* Note: This is useless since runtime-map is a subset */
29*92ad79ecSWarner Losh void
30*92ad79ecSWarner Losh efi_read_from_sysfs(void)
31*92ad79ecSWarner Losh {
32*92ad79ecSWarner Losh 	uint32_t efisz, sz, map_size;
33*92ad79ecSWarner Losh 	int entries = 0;
34*92ad79ecSWarner Losh 	struct efi_md *map;		/* Really an array */
35*92ad79ecSWarner Losh 	char *buf;
36*92ad79ecSWarner Losh 	struct stat sb;
37*92ad79ecSWarner Losh 	char fn[100];
38*92ad79ecSWarner Losh 
39*92ad79ecSWarner Losh 	/*
40*92ad79ecSWarner Losh 	 * Count the number of entries we have. They are numbered from 0
41*92ad79ecSWarner Losh 	 * through entries - 1.
42*92ad79ecSWarner Losh 	 */
43*92ad79ecSWarner Losh 	do {
44*92ad79ecSWarner Losh 		printf("Looking at index %d\n", entries);
45*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", entries++);
46*92ad79ecSWarner Losh 	} while (stat(fn, &sb) == 0);
47*92ad79ecSWarner Losh 
48*92ad79ecSWarner Losh 	/*
49*92ad79ecSWarner Losh 	 * We incremented entries one past the first failure, so we need to
50*92ad79ecSWarner Losh 	 * adjust the count and the test for 'nothing found' is against 1.
51*92ad79ecSWarner Losh 	 */
52*92ad79ecSWarner Losh 	if (entries == 1)
53*92ad79ecSWarner Losh 		goto err;
54*92ad79ecSWarner Losh 	entries--;
55*92ad79ecSWarner Losh 
56*92ad79ecSWarner Losh 	/* XXX lots of copied code, refactor? */
57*92ad79ecSWarner Losh 	map_size = sizeof(struct efi_md) * entries;
58*92ad79ecSWarner Losh 	efisz = roundup2(sizeof(*efi_map_hdr), 16);
59*92ad79ecSWarner Losh 	sz = efisz + map_size;
60*92ad79ecSWarner Losh 	buf = malloc(efisz + map_size);
61*92ad79ecSWarner Losh 	if (buf == NULL)
62*92ad79ecSWarner Losh 		return;
63*92ad79ecSWarner Losh 	efi_map_hdr = (struct efi_map_header *)buf;
64*92ad79ecSWarner Losh 	efi_map_size = sz;
65*92ad79ecSWarner Losh 	map = (struct efi_md *)(buf + efisz);
66*92ad79ecSWarner Losh 	bzero(map, sz);
67*92ad79ecSWarner Losh 	efi_map_hdr->memory_size = map_size;
68*92ad79ecSWarner Losh 	efi_map_hdr->descriptor_size = sizeof(struct efi_md);
69*92ad79ecSWarner Losh 	efi_map_hdr->descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
70*92ad79ecSWarner Losh 	for (int i = 0; i < entries; i++) {
71*92ad79ecSWarner Losh 		struct efi_md *m;
72*92ad79ecSWarner Losh 
73*92ad79ecSWarner Losh 		printf("Populating index %d\n", i);
74*92ad79ecSWarner Losh 		m = map + i;
75*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/type", i);
76*92ad79ecSWarner Losh 		if (!file2u32(fn, &m->md_type))
77*92ad79ecSWarner Losh 			goto err;
78*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/phys_addr", i);
79*92ad79ecSWarner Losh 		if (!file2u64(fn, &m->md_phys))
80*92ad79ecSWarner Losh 			goto err;
81*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/virt_addr", i);
82*92ad79ecSWarner Losh 		if (!file2u64(fn, &m->md_virt))
83*92ad79ecSWarner Losh 			goto err;
84*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/num_pages", i);
85*92ad79ecSWarner Losh 		if (!file2u64(fn, &m->md_pages))
86*92ad79ecSWarner Losh 			goto err;
87*92ad79ecSWarner Losh 		snprintf(fn, sizeof(fn), "/sys/firmware/efi/runtime-map/%d/attribute", i);
88*92ad79ecSWarner Losh 		if (!file2u64(fn, &m->md_attr))
89*92ad79ecSWarner Losh 			goto err;
90*92ad79ecSWarner Losh 	}
91*92ad79ecSWarner Losh 	efi_map_phys_src = 0;
92*92ad79ecSWarner Losh 	printf("UEFI MAP:\n");
93*92ad79ecSWarner Losh 	print_efi_map(efi_map_hdr);
94*92ad79ecSWarner Losh 	printf("DONE\n");
95*92ad79ecSWarner Losh 	return;
96*92ad79ecSWarner Losh err:
97*92ad79ecSWarner Losh 	printf("Parse error in reading current memory map\n");
98*92ad79ecSWarner Losh }
99*92ad79ecSWarner Losh #endif
100*92ad79ecSWarner Losh 
101*92ad79ecSWarner Losh /*
102*92ad79ecSWarner Losh  * We may have no ability to read the PA that this map is in, so pass
103*92ad79ecSWarner Losh  * the address to FreeBSD via a rather odd flag entry as the first map
104*92ad79ecSWarner Losh  * so early boot can copy the memory map into this space and have the
105*92ad79ecSWarner Losh  * rest of the code cope.
106*92ad79ecSWarner Losh  */
107*92ad79ecSWarner Losh bool
efi_read_from_pa(uint64_t pa,uint32_t map_size,uint32_t desc_size,uint32_t vers)108*92ad79ecSWarner Losh efi_read_from_pa(uint64_t pa, uint32_t map_size, uint32_t desc_size, uint32_t vers)
109*92ad79ecSWarner Losh {
110*92ad79ecSWarner Losh 	uint32_t efisz, sz;
111*92ad79ecSWarner Losh 	char *buf;
112*92ad79ecSWarner Losh 	int fd2, len;
113*92ad79ecSWarner Losh 	struct efi_md *map;		/* Really an array */
114*92ad79ecSWarner Losh 
115*92ad79ecSWarner Losh 	/*
116*92ad79ecSWarner Losh 	 * We may have no ability to read the PA that this map is in, so pass
117*92ad79ecSWarner Losh 	 * the address to FreeBSD via a rather odd flag entry as the first map
118*92ad79ecSWarner Losh 	 * so early boot can copy the memory map into this space and have the
119*92ad79ecSWarner Losh 	 * rest of the code cope. We also have to round the size of the header
120*92ad79ecSWarner Losh 	 * to 16 byte boundary.
121*92ad79ecSWarner Losh 	 */
122*92ad79ecSWarner Losh 	efisz = roundup2(sizeof(*efi_map_hdr), 16);
123*92ad79ecSWarner Losh 	sz = efisz + map_size;
124*92ad79ecSWarner Losh 	buf = malloc(efisz + map_size);
125*92ad79ecSWarner Losh 	if (buf == NULL)
126*92ad79ecSWarner Losh 		return false;
127*92ad79ecSWarner Losh 	efi_map_hdr = (struct efi_map_header *)buf;
128*92ad79ecSWarner Losh 	efi_map_size = sz;
129*92ad79ecSWarner Losh 	map = (struct efi_md *)(buf + efisz);
130*92ad79ecSWarner Losh 	bzero(map, sz);
131*92ad79ecSWarner Losh 	efi_map_hdr->memory_size = map_size;
132*92ad79ecSWarner Losh 	efi_map_hdr->descriptor_size = desc_size;
133*92ad79ecSWarner Losh 	efi_map_hdr->descriptor_version = vers;
134*92ad79ecSWarner Losh 
135*92ad79ecSWarner Losh 	/*
136*92ad79ecSWarner Losh 	 * Try to read in the actual UEFI map. This may fail, and that's OK. We just
137*92ad79ecSWarner Losh 	 * won't print the map.
138*92ad79ecSWarner Losh 	 */
139*92ad79ecSWarner Losh 	fd2 = open("host:/dev/mem", O_RDONLY);
140*92ad79ecSWarner Losh 	if (fd2 < 0)
141*92ad79ecSWarner Losh 		goto no_read;
142*92ad79ecSWarner Losh 	if (lseek(fd2, pa, SEEK_SET) < 0)
143*92ad79ecSWarner Losh 		goto no_read;
144*92ad79ecSWarner Losh 	len = read(fd2, map, sz);
145*92ad79ecSWarner Losh 	if (len != sz)
146*92ad79ecSWarner Losh 		goto no_read;
147*92ad79ecSWarner Losh 	efi_map_phys_src = 0;		/* Mark MODINFOMD_EFI_MAP as valid */
148*92ad79ecSWarner Losh 	close(fd2);
149*92ad79ecSWarner Losh 	printf("UEFI MAP:\n");
150*92ad79ecSWarner Losh 	print_efi_map(efi_map_hdr);
151*92ad79ecSWarner Losh 	return (true);
152*92ad79ecSWarner Losh 
153*92ad79ecSWarner Losh no_read:				/* Just get it the trampoline */
154*92ad79ecSWarner Losh 	efi_map_phys_src = pa;
155*92ad79ecSWarner Losh 	close(fd2);
156*92ad79ecSWarner Losh 	return (true);
157*92ad79ecSWarner Losh }
158136d2dc0SWarner Losh 
159136d2dc0SWarner Losh void
foreach_efi_map_entry(struct efi_map_header * efihdr,efi_map_entry_cb cb,void * argp)160136d2dc0SWarner Losh foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
161136d2dc0SWarner Losh {
162136d2dc0SWarner Losh 	struct efi_md *map, *p;
163136d2dc0SWarner Losh 	size_t efisz;
164136d2dc0SWarner Losh 	int ndesc, i;
165136d2dc0SWarner Losh 
166136d2dc0SWarner Losh 	/*
167136d2dc0SWarner Losh 	 * Memory map data provided by UEFI via the GetMemoryMap
168136d2dc0SWarner Losh 	 * Boot Services API.
169136d2dc0SWarner Losh 	 */
170136d2dc0SWarner Losh 	efisz = roundup2(sizeof(struct efi_map_header), 16);
171136d2dc0SWarner Losh 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
172136d2dc0SWarner Losh 
173136d2dc0SWarner Losh 	if (efihdr->descriptor_size == 0)
174136d2dc0SWarner Losh 		return;
175136d2dc0SWarner Losh 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
176136d2dc0SWarner Losh 
177136d2dc0SWarner Losh 	for (i = 0, p = map; i < ndesc; i++,
178136d2dc0SWarner Losh 	    p = efi_next_descriptor(p, efihdr->descriptor_size)) {
179136d2dc0SWarner Losh 		cb(p, argp);
180136d2dc0SWarner Losh 	}
181136d2dc0SWarner Losh }
182136d2dc0SWarner Losh 
183988ee1ccSWarner Losh /* XXX REFACTOR WITH KERNEL */
184136d2dc0SWarner Losh static void
print_efi_map_entry(struct efi_md * p,void * argp __unused)185136d2dc0SWarner Losh print_efi_map_entry(struct efi_md *p, void *argp __unused)
186136d2dc0SWarner Losh {
187136d2dc0SWarner Losh 	const char *type;
188136d2dc0SWarner Losh 	static const char *types[] = {
189136d2dc0SWarner Losh 		"Reserved",
190136d2dc0SWarner Losh 		"LoaderCode",
191136d2dc0SWarner Losh 		"LoaderData",
192136d2dc0SWarner Losh 		"BootServicesCode",
193136d2dc0SWarner Losh 		"BootServicesData",
194136d2dc0SWarner Losh 		"RuntimeServicesCode",
195136d2dc0SWarner Losh 		"RuntimeServicesData",
196136d2dc0SWarner Losh 		"ConventionalMemory",
197136d2dc0SWarner Losh 		"UnusableMemory",
198136d2dc0SWarner Losh 		"ACPIReclaimMemory",
199136d2dc0SWarner Losh 		"ACPIMemoryNVS",
200136d2dc0SWarner Losh 		"MemoryMappedIO",
201136d2dc0SWarner Losh 		"MemoryMappedIOPortSpace",
202136d2dc0SWarner Losh 		"PalCode",
203136d2dc0SWarner Losh 		"PersistentMemory"
204136d2dc0SWarner Losh 	};
205136d2dc0SWarner Losh 
206136d2dc0SWarner Losh 	if (p->md_type < nitems(types))
207136d2dc0SWarner Losh 		type = types[p->md_type];
208136d2dc0SWarner Losh 	else
209136d2dc0SWarner Losh 		type = "<INVALID>";
210136d2dc0SWarner Losh 	printf("%23s %012lx %012lx %08lx ", type, p->md_phys,
211136d2dc0SWarner Losh 	    p->md_virt, p->md_pages);
212136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_UC)
213136d2dc0SWarner Losh 		printf("UC ");
214136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WC)
215136d2dc0SWarner Losh 		printf("WC ");
216136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WT)
217136d2dc0SWarner Losh 		printf("WT ");
218136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WB)
219136d2dc0SWarner Losh 		printf("WB ");
220136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_UCE)
221136d2dc0SWarner Losh 		printf("UCE ");
222136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WP)
223136d2dc0SWarner Losh 		printf("WP ");
224136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RP)
225136d2dc0SWarner Losh 		printf("RP ");
226136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_XP)
227136d2dc0SWarner Losh 		printf("XP ");
228136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_NV)
229136d2dc0SWarner Losh 		printf("NV ");
230136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
231136d2dc0SWarner Losh 		printf("MORE_RELIABLE ");
232136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RO)
233136d2dc0SWarner Losh 		printf("RO ");
234136d2dc0SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RT)
235136d2dc0SWarner Losh 		printf("RUNTIME");
236136d2dc0SWarner Losh 	printf("\n");
237136d2dc0SWarner Losh }
238136d2dc0SWarner Losh 
239136d2dc0SWarner Losh void
print_efi_map(struct efi_map_header * efihdr)240136d2dc0SWarner Losh print_efi_map(struct efi_map_header *efihdr)
241136d2dc0SWarner Losh {
242136d2dc0SWarner Losh 	printf("%23s %12s %12s %8s %4s\n",
243136d2dc0SWarner Losh 	    "Type", "Physical", "Virtual", "#Pages", "Attr");
244136d2dc0SWarner Losh 
245136d2dc0SWarner Losh 	foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL);
246136d2dc0SWarner Losh }
247988ee1ccSWarner Losh 
248*92ad79ecSWarner Losh void
efi_bi_loadsmap(struct preloaded_file * kfp)249*92ad79ecSWarner Losh efi_bi_loadsmap(struct preloaded_file *kfp)
250988ee1ccSWarner Losh {
251*92ad79ecSWarner Losh 	/*
252*92ad79ecSWarner Losh 	 * Make a note of a systbl. This is nearly mandatory on AARCH64.
253*92ad79ecSWarner Losh 	 */
254*92ad79ecSWarner Losh 	if (efi_systbl_phys)
255*92ad79ecSWarner Losh 		file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys);
256988ee1ccSWarner Losh 
257988ee1ccSWarner Losh 	/*
258*92ad79ecSWarner Losh 	 * If we have efi_map_hdr, then it's a pointer to the PA where this
259*92ad79ecSWarner Losh 	 * memory map lives. The trampoline code will copy it over. If we don't
260*92ad79ecSWarner Losh 	 * have it, panic because /proc/iomem isn't sufficient and there's no
261*92ad79ecSWarner Losh 	 * hope.
262988ee1ccSWarner Losh 	 */
263*92ad79ecSWarner Losh 	if (efi_map_hdr != NULL) {
264*92ad79ecSWarner Losh 		file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr);
265988ee1ccSWarner Losh 		return;
266988ee1ccSWarner Losh 	}
267988ee1ccSWarner Losh 
268*92ad79ecSWarner Losh 	panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n");
269988ee1ccSWarner Losh }
270