xref: /freebsd/stand/kboot/kboot/arch/aarch64/load_addr.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*-
2  * Copyright (c) 2022 Netflix, Inc
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/param.h>
8 #include <sys/efi.h>
9 #include <machine/metadata.h>
10 #include <sys/linker.h>
11 #include <fdt_platform.h>
12 #include <libfdt.h>
13 
14 #include "kboot.h"
15 #include "bootstrap.h"
16 
17 /*
18  * Info from dtb about the EFI system
19  */
20 vm_paddr_t efi_systbl_phys;
21 struct efi_map_header *efi_map_hdr;
22 uint32_t efi_map_size;
23 vm_paddr_t efi_map_phys_src;	/* From DTB */
24 vm_paddr_t efi_map_phys_dst;	/* From our memory map metadata module */
25 
26 typedef void (*efi_map_entry_cb)(struct efi_md *, void *argp);
27 
28 static void
29 foreach_efi_map_entry(struct efi_map_header *efihdr, efi_map_entry_cb cb, void *argp)
30 {
31 	struct efi_md *map, *p;
32 	size_t efisz;
33 	int ndesc, i;
34 
35 	/*
36 	 * Memory map data provided by UEFI via the GetMemoryMap
37 	 * Boot Services API.
38 	 */
39 	efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf;
40 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
41 
42 	if (efihdr->descriptor_size == 0)
43 		return;
44 	ndesc = efihdr->memory_size / efihdr->descriptor_size;
45 
46 	for (i = 0, p = map; i < ndesc; i++,
47 	    p = efi_next_descriptor(p, efihdr->descriptor_size)) {
48 		cb(p, argp);
49 	}
50 }
51 
52 static void
53 print_efi_map_entry(struct efi_md *p, void *argp __unused)
54 {
55 	const char *type;
56 	static const char *types[] = {
57 		"Reserved",
58 		"LoaderCode",
59 		"LoaderData",
60 		"BootServicesCode",
61 		"BootServicesData",
62 		"RuntimeServicesCode",
63 		"RuntimeServicesData",
64 		"ConventionalMemory",
65 		"UnusableMemory",
66 		"ACPIReclaimMemory",
67 		"ACPIMemoryNVS",
68 		"MemoryMappedIO",
69 		"MemoryMappedIOPortSpace",
70 		"PalCode",
71 		"PersistentMemory"
72 	};
73 
74 	if (p->md_type < nitems(types))
75 		type = types[p->md_type];
76 	else
77 		type = "<INVALID>";
78 	printf("%23s %012lx %012lx %08lx ", type, p->md_phys,
79 	    p->md_virt, p->md_pages);
80 	if (p->md_attr & EFI_MD_ATTR_UC)
81 		printf("UC ");
82 	if (p->md_attr & EFI_MD_ATTR_WC)
83 		printf("WC ");
84 	if (p->md_attr & EFI_MD_ATTR_WT)
85 		printf("WT ");
86 	if (p->md_attr & EFI_MD_ATTR_WB)
87 		printf("WB ");
88 	if (p->md_attr & EFI_MD_ATTR_UCE)
89 		printf("UCE ");
90 	if (p->md_attr & EFI_MD_ATTR_WP)
91 		printf("WP ");
92 	if (p->md_attr & EFI_MD_ATTR_RP)
93 		printf("RP ");
94 	if (p->md_attr & EFI_MD_ATTR_XP)
95 		printf("XP ");
96 	if (p->md_attr & EFI_MD_ATTR_NV)
97 		printf("NV ");
98 	if (p->md_attr & EFI_MD_ATTR_MORE_RELIABLE)
99 		printf("MORE_RELIABLE ");
100 	if (p->md_attr & EFI_MD_ATTR_RO)
101 		printf("RO ");
102 	if (p->md_attr & EFI_MD_ATTR_RT)
103 		printf("RUNTIME");
104 	printf("\n");
105 }
106 
107 static bool
108 do_memory_from_fdt(int fd)
109 {
110 	struct stat sb;
111 	char *buf = NULL;
112 	int len, offset, fd2 = -1;
113 	uint32_t sz, ver, esz, efisz;
114 	uint64_t mmap_pa;
115 	const uint32_t *u32p;
116 	const uint64_t *u64p;
117 	struct efi_map_header *efihdr;
118 	struct efi_md *map;
119 
120 	if (fstat(fd, &sb) < 0)
121 		return false;
122 	buf = malloc(sb.st_size);
123 	if (buf == NULL)
124 		return false;
125 	len = read(fd, buf, sb.st_size);
126 	/* NB: we're reading this from sysfs, so mismatch OK */
127 	if (len <= 0)
128 		goto errout;
129 
130 	/*
131 	 * Look for /chosen to find these values:
132 	 * linux,uefi-system-table	PA of the UEFI System Table.
133 	 * linux,uefi-mmap-start	PA of the UEFI memory map
134 	 * linux,uefi-mmap-size		Size of mmap
135 	 * linux,uefi-mmap-desc-size	Size of each entry of mmap
136 	 * linux,uefi-mmap-desc-ver	Format version, should be 1
137 	 */
138 	offset = fdt_path_offset(buf, "/chosen");
139 	if (offset <= 0)
140 		goto errout;
141 	u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len);
142 	if (u64p == NULL)
143 		goto errout;
144 	efi_systbl_phys = fdt64_to_cpu(*u64p);
145 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len);
146 	if (u32p == NULL)
147 		goto errout;
148 	ver = fdt32_to_cpu(*u32p);
149 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len);
150 	if (u32p == NULL)
151 		goto errout;
152 	esz = fdt32_to_cpu(*u32p);
153 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len);
154 	if (u32p == NULL)
155 		goto errout;
156 	sz = fdt32_to_cpu(*u32p);
157 	u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len);
158 	if (u64p == NULL)
159 		goto errout;
160 	mmap_pa = fdt64_to_cpu(*u64p);
161 	free(buf);
162 
163 	printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n",
164 	    ver, esz, sz, mmap_pa);
165 
166 	/*
167 	 * We may have no ability to read the PA that this map is in, so pass
168 	 * the address to FreeBSD via a rather odd flag entry as the first map
169 	 * so early boot can copy the memory map into this space and have the
170 	 * rest of the code cope.
171 	 */
172 	efisz = (sizeof(*efihdr) + 0xf) & ~0xf;
173 	buf = malloc(sz + efisz);
174 	if (buf == NULL)
175 		return false;
176 	efihdr = (struct efi_map_header *)buf;
177 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
178 	bzero(map, sz);
179 	efihdr->memory_size = sz;
180 	efihdr->descriptor_size = esz;
181 	efihdr->descriptor_version = ver;
182 
183 	/*
184 	 * Save EFI table. Either this will be an empty table filled in by the trampoline,
185 	 * or we'll read it below. Either way, set these two variables so we share the best
186 	 * UEFI memory map with the kernel.
187 	 */
188 	efi_map_hdr = efihdr;
189 	efi_map_size = sz + efisz;
190 
191 	/*
192 	 * Try to read in the actual UEFI map.
193 	 */
194 	fd2 = open("host:/dev/mem", O_RDONLY);
195 	if (fd2 < 0) {
196 		printf("Will read UEFI mem map in tramp: no /dev/mem, need CONFIG_DEVMEM=y\n");
197 		goto no_read;
198 	}
199 	if (lseek(fd2, mmap_pa, SEEK_SET) < 0) {
200 		printf("Will read UEFI mem map in tramp: lseek failed\n");
201 		goto no_read;
202 	}
203 	len = read(fd2, map, sz);
204 	if (len != sz) {
205 		if (len < 0 && errno == EPERM)
206 			printf("Will read UEFI mem map in tramp: kernel needs CONFIG_STRICT_DEVMEM=n\n");
207 		else
208 			printf("Will read UEFI mem map in tramp: lean = %d errno = %d\n", len, errno);
209 		goto no_read;
210 	}
211 	printf("Read UEFI mem map from physmem\n");
212 	efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */
213 	close(fd2);
214 	printf("UEFI MAP:\n");
215 	printf("%23s %12s %12s %8s %4s\n",
216 	    "Type", "Physical", "Virtual", "#Pages", "Attr");
217 	foreach_efi_map_entry(efihdr, print_efi_map_entry, NULL);
218 	return true;	/* OK, we really have the memory map */
219 
220 no_read:
221 	efi_map_phys_src = mmap_pa;
222 	close(fd2);
223 	return true;	/* We can get it the trampoline */
224 
225 errout:
226 	free(buf);
227 	return false;
228 }
229 
230 bool
231 enumerate_memory_arch(void)
232 {
233 	int fd = -1;
234 	bool rv = false;
235 
236 	fd = open("host:/sys/firmware/fdt", O_RDONLY);
237 	if (fd != -1) {
238 		rv = do_memory_from_fdt(fd);
239 		close(fd);
240 		/*
241 		 * So, we have physaddr to the memory table. However, we can't
242 		 * open /dev/mem on some platforms to get the actual table. So
243 		 * we have to fall through to get it from /proc/iomem.
244 		 */
245 	}
246 	if (!rv) {
247 		printf("Could not obtain UEFI memory tables, expect failure\n");
248 	}
249 
250 	populate_avail_from_iomem();
251 
252 	print_avail();
253 
254 	return true;
255 }
256 
257 uint64_t
258 kboot_get_phys_load_segment(void)
259 {
260 #define HOLE_SIZE	(64ul << 20)
261 #define KERN_ALIGN	(2ul << 20)
262 	static uint64_t	s = 0;
263 
264 	if (s != 0)
265 		return (s);
266 
267 	s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
268 	if (s != 0)
269 		return (s);
270 	s = 0x40000000 | 0x4200000;	/* should never get here */
271 	printf("Falling back to crazy address %#lx\n", s);
272 	return (s);
273 }
274 
275 void
276 bi_loadsmap(struct preloaded_file *kfp)
277 {
278 
279 	/*
280 	 * Make a note of a systbl. This is nearly mandatory on AARCH64.
281 	 */
282 	if (efi_systbl_phys)
283 		file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys);
284 
285 	/*
286 	 * If we have efi_map_hdr, then it's a pointer to the PA where this
287 	 * memory map lives. The trampoline code will copy it over. If we don't
288 	 * have it, we use whatever we found in /proc/iomap.
289 	 */
290 	if (efi_map_hdr != NULL) {
291 		file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr);
292 		return;
293 	}
294 	panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n");
295 }
296