xref: /freebsd/stand/kboot/kboot/arch/aarch64/load_addr.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
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 static bool
27 do_memory_from_fdt(int fd)
28 {
29 	struct stat sb;
30 	char *buf = NULL;
31 	int len, offset, fd2 = -1;
32 	uint32_t sz, ver, esz, efisz;
33 	uint64_t mmap_pa;
34 	const uint32_t *u32p;
35 	const uint64_t *u64p;
36 	struct efi_map_header *efihdr;
37 	struct efi_md *map;
38 
39 	if (fstat(fd, &sb) < 0)
40 		return false;
41 	buf = malloc(sb.st_size);
42 	if (buf == NULL)
43 		return false;
44 	len = read(fd, buf, sb.st_size);
45 	/* NB: we're reading this from sysfs, so mismatch OK */
46 	if (len <= 0)
47 		goto errout;
48 
49 	/*
50 	 * Look for /chosen to find these values:
51 	 * linux,uefi-system-table	PA of the UEFI System Table.
52 	 * linux,uefi-mmap-start	PA of the UEFI memory map
53 	 * linux,uefi-mmap-size		Size of mmap
54 	 * linux,uefi-mmap-desc-size	Size of each entry of mmap
55 	 * linux,uefi-mmap-desc-ver	Format version, should be 1
56 	 */
57 	offset = fdt_path_offset(buf, "/chosen");
58 	if (offset <= 0)
59 		goto errout;
60 	u64p = fdt_getprop(buf, offset, "linux,uefi-system-table", &len);
61 	if (u64p == NULL)
62 		goto errout;
63 	efi_systbl_phys = fdt64_to_cpu(*u64p);
64 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-ver", &len);
65 	if (u32p == NULL)
66 		goto errout;
67 	ver = fdt32_to_cpu(*u32p);
68 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-desc-size", &len);
69 	if (u32p == NULL)
70 		goto errout;
71 	esz = fdt32_to_cpu(*u32p);
72 	u32p = fdt_getprop(buf, offset, "linux,uefi-mmap-size", &len);
73 	if (u32p == NULL)
74 		goto errout;
75 	sz = fdt32_to_cpu(*u32p);
76 	u64p = fdt_getprop(buf, offset, "linux,uefi-mmap-start", &len);
77 	if (u64p == NULL)
78 		goto errout;
79 	mmap_pa = fdt64_to_cpu(*u64p);
80 	free(buf);
81 
82 	printf("UEFI MMAP: Ver %d Ent Size %d Tot Size %d PA %#lx\n",
83 	    ver, esz, sz, mmap_pa);
84 
85 	/*
86 	 * We may have no ability to read the PA that this map is in, so pass
87 	 * the address to FreeBSD via a rather odd flag entry as the first map
88 	 * so early boot can copy the memory map into this space and have the
89 	 * rest of the code cope.
90 	 */
91 	efisz = (sizeof(*efihdr) + 0xf) & ~0xf;
92 	buf = malloc(sz + efisz);
93 	if (buf == NULL)
94 		return false;
95 	efihdr = (struct efi_map_header *)buf;
96 	map = (struct efi_md *)((uint8_t *)efihdr + efisz);
97 	bzero(map, sz);
98 	efihdr->memory_size = sz;
99 	efihdr->descriptor_size = esz;
100 	efihdr->descriptor_version = ver;
101 
102 	/*
103 	 * Save EFI table. Either this will be an empty table filled in by the trampoline,
104 	 * or we'll read it below. Either way, set these two variables so we share the best
105 	 * UEFI memory map with the kernel.
106 	 */
107 	efi_map_hdr = efihdr;
108 	efi_map_size = sz + efisz;
109 
110 	/*
111 	 * Try to read in the actual UEFI map.
112 	 */
113 	fd2 = open("host:/dev/mem", O_RDONLY);
114 	if (fd2 < 0) {
115 		printf("Will read UEFI mem map in tramp: no /dev/mem, need CONFIG_DEVMEM=y\n");
116 		goto no_read;
117 	}
118 	if (lseek(fd2, mmap_pa, SEEK_SET) < 0) {
119 		printf("Will read UEFI mem map in tramp: lseek failed\n");
120 		goto no_read;
121 	}
122 	len = read(fd2, map, sz);
123 	if (len != sz) {
124 		if (len < 0 && errno == EPERM)
125 			printf("Will read UEFI mem map in tramp: kernel needs CONFIG_STRICT_DEVMEM=n\n");
126 		else
127 			printf("Will read UEFI mem map in tramp: lean = %d errno = %d\n", len, errno);
128 		goto no_read;
129 	}
130 	printf("Read UEFI mem map from physmem\n");
131 	efi_map_phys_src = 0; /* Mark MODINFOMD_EFI_MAP as valid */
132 	close(fd2);
133 	return true;	/* OK, we really have the memory map */
134 
135 no_read:
136 	efi_map_phys_src = mmap_pa;
137 	close(fd2);
138 	return true;	/* We can get it the trampoline */
139 
140 errout:
141 	free(buf);
142 	return false;
143 }
144 
145 bool
146 enumerate_memory_arch(void)
147 {
148 	int fd = -1;
149 	bool rv = false;
150 
151 	fd = open("host:/sys/firmware/fdt", O_RDONLY);
152 	if (fd != -1) {
153 		rv = do_memory_from_fdt(fd);
154 		close(fd);
155 		/*
156 		 * So, we have physaddr to the memory table. However, we can't
157 		 * open /dev/mem on some platforms to get the actual table. So
158 		 * we have to fall through to get it from /proc/iomem.
159 		 */
160 	}
161 	if (!rv) {
162 		printf("Could not obtain UEFI memory tables, expect failure\n");
163 	}
164 
165 	populate_avail_from_iomem();
166 
167 	print_avail();
168 
169 	return true;
170 }
171 
172 uint64_t
173 kboot_get_phys_load_segment(void)
174 {
175 #define HOLE_SIZE	(64ul << 20)
176 #define KERN_ALIGN	(2ul << 20)
177 	static uint64_t	s = 0;
178 
179 	if (s != 0)
180 		return (s);
181 
182 	s = first_avail(KERN_ALIGN, HOLE_SIZE, SYSTEM_RAM);
183 	if (s != 0)
184 		return (s);
185 	s = 0x40000000 | 0x4200000;	/* should never get here */
186 	printf("Falling back to crazy address %#lx\n", s);
187 	return (s);
188 }
189 
190 void
191 bi_loadsmap(struct preloaded_file *kfp)
192 {
193 
194 	/*
195 	 * Make a note of a systbl. This is nearly mandatory on AARCH64.
196 	 */
197 	if (efi_systbl_phys)
198 		file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof(efi_systbl_phys), &efi_systbl_phys);
199 
200 	/*
201 	 * If we have efi_map_hdr, then it's a pointer to the PA where this
202 	 * memory map lives. The trampoline code will copy it over. If we don't
203 	 * have it, we use whatever we found in /proc/iomap.
204 	 */
205 	if (efi_map_hdr != NULL) {
206 		file_addmetadata(kfp, MODINFOMD_EFI_MAP, efi_map_size, efi_map_hdr);
207 		return;
208 	}
209 	panic("Can't get UEFI memory map, nor a pointer to it, can't proceed.\n");
210 }
211