xref: /freebsd/stand/kboot/kboot/arch/aarch64/load_addr.c (revision a9cd3b675e243648aa681bc6ce1bf3e788be88c8)
1091c255bSWarner Losh /*-
2091c255bSWarner Losh  * Copyright (c) 2022 Netflix, Inc
3091c255bSWarner Losh  *
4091c255bSWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5091c255bSWarner Losh  */
6091c255bSWarner Losh 
7091c255bSWarner Losh #include <sys/param.h>
8091c255bSWarner Losh #include <sys/efi.h>
9091c255bSWarner Losh #include <machine/metadata.h>
10091c255bSWarner Losh #include <sys/linker.h>
11091c255bSWarner Losh #include <fdt_platform.h>
12091c255bSWarner Losh #include <libfdt.h>
13091c255bSWarner Losh 
14091c255bSWarner Losh #include "kboot.h"
15091c255bSWarner Losh #include "bootstrap.h"
16091c255bSWarner Losh 
17091c255bSWarner Losh /*
18091c255bSWarner Losh  * Info from dtb about the EFI system
19091c255bSWarner Losh  */
20091c255bSWarner Losh vm_paddr_t efi_systbl_phys;
21091c255bSWarner Losh struct efi_map_header *efi_map_hdr;
22091c255bSWarner Losh uint32_t efi_map_size;
23091c255bSWarner Losh vm_paddr_t efi_map_phys_src;	/* From DTB */
24091c255bSWarner Losh vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
25091c255bSWarner Losh 
26*a9cd3b67SWarner Losh typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp);
27*a9cd3b67SWarner Losh 
28*a9cd3b67SWarner Losh static void
29*a9cd3b67SWarner Losh foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
30*a9cd3b67SWarner Losh {
31*a9cd3b67SWarner Losh 	struct efi_md *map, *p;
32*a9cd3b67SWarner Losh 	size_t efisz;
33*a9cd3b67SWarner Losh 	int ndesc, i;
34*a9cd3b67SWarner Losh 
35*a9cd3b67SWarner Losh 	/*
36*a9cd3b67SWarner Losh 	 * Memory map data provided by UEFI via the GetMemoryMap
37*a9cd3b67SWarner Losh 	 * Boot Services API.
38*a9cd3b67SWarner Losh 	 */
39*a9cd3b67SWarner Losh 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
40*a9cd3b67SWarner Losh 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
41*a9cd3b67SWarner Losh 
42*a9cd3b67SWarner Losh 	if (efihdr->descriptor_size == 0)
43*a9cd3b67SWarner Losh 		return;
44*a9cd3b67SWarner Losh 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
45*a9cd3b67SWarner Losh 
46*a9cd3b67SWarner Losh 	for (i = 0, p = map; i < ndesc; i++,
47*a9cd3b67SWarner Losh 	    p = efi_next_descriptor(p, efihdr->descriptor_size)) {
48*a9cd3b67SWarner Losh 		cb(p, argp);
49*a9cd3b67SWarner Losh 	}
50*a9cd3b67SWarner Losh }
51*a9cd3b67SWarner Losh 
52*a9cd3b67SWarner Losh static void
53*a9cd3b67SWarner Losh print_efi_map_entry(struct efi_md *p, void *argp __unused)
54*a9cd3b67SWarner Losh {
55*a9cd3b67SWarner Losh 	const char *type;
56*a9cd3b67SWarner Losh 	static const char *types[] = {
57*a9cd3b67SWarner Losh 		"Reserved",
58*a9cd3b67SWarner Losh 		"LoaderCode",
59*a9cd3b67SWarner Losh 		"LoaderData",
60*a9cd3b67SWarner Losh 		"BootServicesCode",
61*a9cd3b67SWarner Losh 		"BootServicesData",
62*a9cd3b67SWarner Losh 		"RuntimeServicesCode",
63*a9cd3b67SWarner Losh 		"RuntimeServicesData",
64*a9cd3b67SWarner Losh 		"ConventionalMemory",
65*a9cd3b67SWarner Losh 		"UnusableMemory",
66*a9cd3b67SWarner Losh 		"ACPIReclaimMemory",
67*a9cd3b67SWarner Losh 		"ACPIMemoryNVS",
68*a9cd3b67SWarner Losh 		"MemoryMappedIO",
69*a9cd3b67SWarner Losh 		"MemoryMappedIOPortSpace",
70*a9cd3b67SWarner Losh 		"PalCode",
71*a9cd3b67SWarner Losh 		"PersistentMemory"
72*a9cd3b67SWarner Losh 	};
73*a9cd3b67SWarner Losh 
74*a9cd3b67SWarner Losh 	if (p->md_type < nitems(types))
75*a9cd3b67SWarner Losh 		type = types[p->md_type];
76*a9cd3b67SWarner Losh 	else
77*a9cd3b67SWarner Losh 		type = "<INVALID>";
78*a9cd3b67SWarner Losh 	printf("%23s %012lx %012lx %08lx ", type, p->md_phys,
79*a9cd3b67SWarner Losh 	    p->md_virt, p->md_pages);
80*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_UC)
81*a9cd3b67SWarner Losh 		printf("UC ");
82*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WC)
83*a9cd3b67SWarner Losh 		printf("WC ");
84*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WT)
85*a9cd3b67SWarner Losh 		printf("WT ");
86*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WB)
87*a9cd3b67SWarner Losh 		printf("WB ");
88*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_UCE)
89*a9cd3b67SWarner Losh 		printf("UCE ");
90*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_WP)
91*a9cd3b67SWarner Losh 		printf("WP ");
92*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RP)
93*a9cd3b67SWarner Losh 		printf("RP ");
94*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_XP)
95*a9cd3b67SWarner Losh 		printf("XP ");
96*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_NV)
97*a9cd3b67SWarner Losh 		printf("NV ");
98*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
99*a9cd3b67SWarner Losh 		printf("MORE_RELIABLE ");
100*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RO)
101*a9cd3b67SWarner Losh 		printf("RO ");
102*a9cd3b67SWarner Losh 	if (p->md_attr & EFI_MD_ATTR_RT)
103*a9cd3b67SWarner Losh 		printf("RUNTIME");
104*a9cd3b67SWarner Losh 	printf("\n");
105*a9cd3b67SWarner Losh }
106*a9cd3b67SWarner Losh 
107091c255bSWarner Losh static bool
108091c255bSWarner Losh do_memory_from_fdt(int fd)
109091c255bSWarner Losh {
110091c255bSWarner Losh 	struct stat sb;
111091c255bSWarner Losh 	char *buf = NULL;
112091c255bSWarner Losh 	int len, offset, fd2 = -1;
113091c255bSWarner Losh 	uint32_t sz, ver, esz, efisz;
114091c255bSWarner Losh 	uint64_t mmap_pa;
115091c255bSWarner Losh 	const uint32_t *u32p;
116091c255bSWarner Losh 	const uint64_t *u64p;
117091c255bSWarner Losh 	struct efi_map_header *efihdr;
118091c255bSWarner Losh 	struct efi_md *map;
119091c255bSWarner Losh 
120091c255bSWarner Losh 	if (fstat(fd, &sb) < 0)
121091c255bSWarner Losh 		return false;
122091c255bSWarner Losh 	buf = malloc(sb.st_size);
123091c255bSWarner Losh 	if (buf == NULL)
124091c255bSWarner Losh 		return false;
125091c255bSWarner Losh 	len = read(fd, buf, sb.st_size);
126091c255bSWarner Losh 	/* NB: we're reading this from sysfs, so mismatch OK */
127091c255bSWarner Losh 	if (len <= 0)
128091c255bSWarner Losh 		goto errout;
129091c255bSWarner Losh 
130091c255bSWarner Losh 	/*
131091c255bSWarner Losh 	 * Look for /chosen to find these values:
132091c255bSWarner Losh 	 * linux,uefi-system-table	PA of the UEFI System Table.
133091c255bSWarner Losh 	 * linux,uefi-mmap-start	PA of the UEFI memory map
134091c255bSWarner Losh 	 * linux,uefi-mmap-size		Size of mmap
135091c255bSWarner Losh 	 * linux,uefi-mmap-desc-size	Size of each entry of mmap
136091c255bSWarner Losh 	 * linux,uefi-mmap-desc-ver	Format version, should be 1
137091c255bSWarner Losh 	 */
138091c255bSWarner Losh 	offset = fdt_path_offset(buf, "/chosen");
139091c255bSWarner Losh 	if (offset <= 0)
140091c255bSWarner Losh 		goto errout;
141091c255bSWarner Losh 	u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len);
142091c255bSWarner Losh 	if (u64p == NULL)
143091c255bSWarner Losh 		goto errout;
144091c255bSWarner Losh 	efi_systbl_phys = fdt64_to_cpu(*u64p);
145091c255bSWarner Losh 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len);
146091c255bSWarner Losh 	if (u32p == NULL)
147091c255bSWarner Losh 		goto errout;
148091c255bSWarner Losh 	ver = fdt32_to_cpu(*u32p);
149091c255bSWarner Losh 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len);
150091c255bSWarner Losh 	if (u32p == NULL)
151091c255bSWarner Losh 		goto errout;
152091c255bSWarner Losh 	esz = fdt32_to_cpu(*u32p);
153091c255bSWarner Losh 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len);
154091c255bSWarner Losh 	if (u32p == NULL)
155091c255bSWarner Losh 		goto errout;
156091c255bSWarner Losh 	sz = fdt32_to_cpu(*u32p);
157091c255bSWarner Losh 	u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len);
158091c255bSWarner Losh 	if (u64p == NULL)
159091c255bSWarner Losh 		goto errout;
160091c255bSWarner Losh 	mmap_pa = fdt64_to_cpu(*u64p);
161091c255bSWarner Losh 	free(buf);
162091c255bSWarner Losh 
163091c255bSWarner Losh 	printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n",
164091c255bSWarner Losh 	    ver, esz, sz, mmap_pa);
165091c255bSWarner Losh 
166091c255bSWarner Losh 	/*
167091c255bSWarner Losh 	 * We may have no ability to read the PA that this map is in, so pass
168091c255bSWarner Losh 	 * the address to FreeBSD via a rather odd flag entry as the first map
169091c255bSWarner Losh 	 * so early boot can copy the memory map into this space and have the
170091c255bSWarner Losh 	 * rest of the code cope.
171091c255bSWarner Losh 	 */
172091c255bSWarner Losh 	efisz = (sizeof(*efihdr) + 0xf) & ~0xf;
173091c255bSWarner Losh 	buf = malloc(sz + efisz);
174091c255bSWarner Losh 	if (buf == NULL)
175091c255bSWarner Losh 		return false;
176091c255bSWarner Losh 	efihdr = (struct efi_map_header *)buf;
177091c255bSWarner Losh 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
178091c255bSWarner Losh 	bzero(map, sz);
179091c255bSWarner Losh 	efihdr->memory_size = sz;
180091c255bSWarner Losh 	efihdr->descriptor_size = esz;
181091c255bSWarner Losh 	efihdr->descriptor_version = ver;
182091c255bSWarner Losh 
183091c255bSWarner Losh 	/*
184091c255bSWarner Losh 	 * Save EFI table. Either this will be an empty table filled in by the trampoline,
185091c255bSWarner Losh 	 * or we'll read it below. Either way, set these two variables so we share the best
186091c255bSWarner Losh 	 * UEFI memory map with the kernel.
187091c255bSWarner Losh 	 */
188091c255bSWarner Losh 	efi_map_hdr = efihdr;
189091c255bSWarner Losh 	efi_map_size = sz + efisz;
190091c255bSWarner Losh 
191091c255bSWarner Losh 	/*
192091c255bSWarner Losh 	 * Try to read in the actual UEFI map.
193091c255bSWarner Losh 	 */
194091c255bSWarner Losh 	fd2 = open("host:/dev/mem", O_RDONLY);
195091c255bSWarner Losh 	if (fd2 < 0) {
196091c255bSWarner Losh 		printf("Will read UEFI mem map in tramp: no /dev/mem, need CONFIG_DEVMEM=y\n");
197091c255bSWarner Losh 		goto no_read;
198091c255bSWarner Losh 	}
199091c255bSWarner Losh 	if (lseek(fd2, mmap_pa, SEEK_SET) < 0) {
200091c255bSWarner Losh 		printf("Will read UEFI mem map in tramp: lseek failed\n");
201091c255bSWarner Losh 		goto no_read;
202091c255bSWarner Losh 	}
203091c255bSWarner Losh 	len = read(fd2, map, sz);
204091c255bSWarner Losh 	if (len != sz) {
205091c255bSWarner Losh 		if (len < 0 && errno == EPERM)
206091c255bSWarner Losh 			printf("Will read UEFI mem map in tramp: kernel needs CONFIG_STRICT_DEVMEM=n\n");
207091c255bSWarner Losh 		else
208091c255bSWarner Losh 			printf("Will read UEFI mem map in tramp: lean = %d errno = %d\n", len, errno);
209091c255bSWarner Losh 		goto no_read;
210091c255bSWarner Losh 	}
211091c255bSWarner Losh 	printf("Read UEFI mem map from physmem\n");
212091c255bSWarner Losh 	efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */
213091c255bSWarner Losh 	close(fd2);
214*a9cd3b67SWarner Losh 	printf("UEFI MAP:\n");
215*a9cd3b67SWarner Losh 	printf("%23s %12s %12s %8s %4s\n",
216*a9cd3b67SWarner Losh 	    "Type", "Physical", "Virtual", "#Pages", "Attr");
217*a9cd3b67SWarner Losh 	foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL);
218091c255bSWarner Losh 	return true;	/* OK, we really have the memory map */
219091c255bSWarner Losh 
220091c255bSWarner Losh no_read:
221091c255bSWarner Losh 	efi_map_phys_src = mmap_pa;
222091c255bSWarner Losh 	close(fd2);
223091c255bSWarner Losh 	return true;	/* We can get it the trampoline */
224091c255bSWarner Losh 
225091c255bSWarner Losh errout:
226091c255bSWarner Losh 	free(buf);
227091c255bSWarner Losh 	return false;
228091c255bSWarner Losh }
229091c255bSWarner Losh 
230091c255bSWarner Losh bool
231091c255bSWarner Losh enumerate_memory_arch(void)
232091c255bSWarner Losh {
233091c255bSWarner Losh 	int fd = -1;
234091c255bSWarner Losh 	bool rv = false;
235091c255bSWarner Losh 
236091c255bSWarner Losh 	fd = open("host:/sys/firmware/fdt", O_RDONLY);
237091c255bSWarner Losh 	if (fd != -1) {
238091c255bSWarner Losh 		rv = do_memory_from_fdt(fd);
239091c255bSWarner Losh 		close(fd);
240091c255bSWarner Losh 		/*
241091c255bSWarner Losh 		 * So, we have physaddr to the memory table. However, we can't
242091c255bSWarner Losh 		 * open /dev/mem on some platforms to get the actual table. So
243091c255bSWarner Losh 		 * we have to fall through to get it from /proc/iomem.
244091c255bSWarner Losh 		 */
245091c255bSWarner Losh 	}
246091c255bSWarner Losh 	if (!rv) {
247091c255bSWarner Losh 		printf("Could not obtain UEFI memory tables, expect failure\n");
248091c255bSWarner Losh 	}
249091c255bSWarner Losh 
250091c255bSWarner Losh 	populate_avail_from_iomem();
251091c255bSWarner Losh 
252091c255bSWarner Losh 	print_avail();
253091c255bSWarner Losh 
254091c255bSWarner Losh 	return true;
255091c255bSWarner Losh }
256091c255bSWarner Losh 
257091c255bSWarner Losh uint64_t
258091c255bSWarner Losh kboot_get_phys_load_segment(void)
259091c255bSWarner Losh {
260091c255bSWarner Losh #define HOLE_SIZE	(64ul << 20)
261091c255bSWarner Losh #define KERN_ALIGN	(2ul << 20)
262091c255bSWarner Losh 	static uint64_t	s = 0;
263091c255bSWarner Losh 
264091c255bSWarner Losh 	if (s != 0)
265091c255bSWarner Losh 		return (s);
266091c255bSWarner Losh 
267091c255bSWarner Losh 	s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
268091c255bSWarner Losh 	if (s != 0)
269091c255bSWarner Losh 		return (s);
270091c255bSWarner Losh 	s = 0x40000000 | 0x4200000;	/* should never get here */
271091c255bSWarner Losh 	printf("Falling back to crazy address %#lx\n", s);
272091c255bSWarner Losh 	return (s);
273091c255bSWarner Losh }
274091c255bSWarner Losh 
275091c255bSWarner Losh void
276091c255bSWarner Losh bi_loadsmap(struct preloaded_file *kfp)
277091c255bSWarner Losh {
278091c255bSWarner Losh 
279091c255bSWarner Losh 	/*
280091c255bSWarner Losh 	 * Make a note of a systbl. This is nearly mandatory on AARCH64.
281091c255bSWarner Losh 	 */
282091c255bSWarner Losh 	if (efi_systbl_phys)
283091c255bSWarner Losh 		file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys);
284091c255bSWarner Losh 
285091c255bSWarner Losh 	/*
286091c255bSWarner Losh 	 * If we have efi_map_hdr, then it's a pointer to the PA where this
287091c255bSWarner Losh 	 * memory map lives. The trampoline code will copy it over. If we don't
288091c255bSWarner Losh 	 * have it, we use whatever we found in /proc/iomap.
289091c255bSWarner Losh 	 */
290091c255bSWarner Losh 	if (efi_map_hdr != NULL) {
291091c255bSWarner Losh 		file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr);
292091c255bSWarner Losh 		return;
293091c255bSWarner Losh 	}
294091c255bSWarner Losh 	panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n");
295091c255bSWarner Losh }
296