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