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