1 /* $OpenBSD: efi_machdep.c,v 1.6 2023/01/14 12:11:11 kettenis Exp $ */
2
3 /*
4 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/systm.h>
22
23 #include <uvm/uvm_extern.h>
24
25 #include <machine/cpufunc.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 #include <machine/fpu.h>
29 #include <machine/pcb.h>
30
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/fdt.h>
33
34 #include <dev/efi/efi.h>
35 #include <machine/efivar.h>
36
37 /*
38 * We need a large address space to allow identity mapping of physical
39 * memory on some machines.
40 */
41 #define EFI_SPACE_BITS 48
42
43 extern uint32_t mmap_size;
44 extern uint32_t mmap_desc_size;
45 extern uint32_t mmap_desc_ver;
46
47 extern EFI_MEMORY_DESCRIPTOR *mmap;
48
49 uint64_t efi_acpi_table;
50 uint64_t efi_smbios_table;
51
52 int efi_match(struct device *, void *, void *);
53 void efi_attach(struct device *, struct device *, void *);
54
55 const struct cfattach efi_ca = {
56 sizeof(struct efi_softc), efi_match, efi_attach
57 };
58
59 void efi_map_runtime(struct efi_softc *);
60 int efi_gettime(struct todr_chip_handle *, struct timeval *);
61 int efi_settime(struct todr_chip_handle *, struct timeval *);
62
63 label_t efi_jmpbuf;
64
65 int
efi_match(struct device * parent,void * match,void * aux)66 efi_match(struct device *parent, void *match, void *aux)
67 {
68 struct fdt_attach_args *faa = aux;
69
70 return (strcmp(faa->fa_name, "efi") == 0);
71 }
72
73 void
efi_attach(struct device * parent,struct device * self,void * aux)74 efi_attach(struct device *parent, struct device *self, void *aux)
75 {
76 struct efi_softc *sc = (struct efi_softc *)self;
77 struct fdt_attach_args *faa = aux;
78 uint64_t system_table;
79 bus_space_handle_t ioh;
80 EFI_SYSTEM_TABLE *st;
81 EFI_TIME time;
82 EFI_STATUS status;
83 uint16_t major, minor;
84 int node, i;
85
86 node = OF_finddevice("/chosen");
87 KASSERT(node != -1);
88
89 system_table = OF_getpropint64(node, "openbsd,uefi-system-table", 0);
90 KASSERT(system_table);
91
92 if (bus_space_map(faa->fa_iot, system_table, sizeof(EFI_SYSTEM_TABLE),
93 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_CACHEABLE, &ioh)) {
94 printf(": can't map system table\n");
95 return;
96 }
97
98 st = bus_space_vaddr(faa->fa_iot, ioh);
99 sc->sc_rs = st->RuntimeServices;
100
101 major = st->Hdr.Revision >> 16;
102 minor = st->Hdr.Revision & 0xffff;
103 printf(": UEFI %d.%d", major, minor / 10);
104 if (minor % 10)
105 printf(".%d", minor % 10);
106 printf("\n");
107
108 /* Early implementations can be buggy. */
109 if (major < 2 || (major == 2 && minor < 10))
110 return;
111
112 efi_map_runtime(sc);
113
114 /*
115 * Activate our pmap such that we can access the
116 * FirmwareVendor and ConfigurationTable fields.
117 */
118 efi_enter(sc);
119 if (st->FirmwareVendor) {
120 printf("%s: ", sc->sc_dev.dv_xname);
121 for (i = 0; st->FirmwareVendor[i]; i++)
122 printf("%c", st->FirmwareVendor[i]);
123 printf(" rev 0x%x\n", st->FirmwareRevision);
124 }
125 for (i = 0; i < st->NumberOfTableEntries; i++) {
126 EFI_CONFIGURATION_TABLE *ct = &st->ConfigurationTable[i];
127 static EFI_GUID acpi_guid = EFI_ACPI_20_TABLE_GUID;
128 static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
129 static EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
130
131 if (efi_guidcmp(&acpi_guid, &ct->VendorGuid) == 0)
132 efi_acpi_table = (uint64_t)ct->VendorTable;
133 if (efi_guidcmp(&smbios_guid, &ct->VendorGuid) == 0)
134 efi_smbios_table = (uint64_t)ct->VendorTable;
135 if (efi_guidcmp(&smbios3_guid, &ct->VendorGuid) == 0)
136 efi_smbios_table = (uint64_t)ct->VendorTable;
137 }
138 efi_leave(sc);
139
140 if (efi_smbios_table != 0) {
141 struct fdt_reg reg = { .addr = efi_smbios_table };
142 struct fdt_attach_args fa;
143
144 fa.fa_name = "smbios";
145 fa.fa_iot = faa->fa_iot;
146 fa.fa_reg = ®
147 fa.fa_nreg = 1;
148 config_found(self, &fa, NULL);
149 }
150
151 if (efi_enter_check(sc))
152 return;
153 status = sc->sc_rs->GetTime(&time, NULL);
154 efi_leave(sc);
155 if (status != EFI_SUCCESS)
156 return;
157
158 /*
159 * EDK II implementations provide an implementation of
160 * GetTime() that returns a fixed compiled-in time on hardware
161 * without a (supported) RTC. So only use this interface as a
162 * last resort.
163 */
164 sc->sc_todr.cookie = sc;
165 sc->sc_todr.todr_gettime = efi_gettime;
166 sc->sc_todr.todr_settime = efi_settime;
167 sc->sc_todr.todr_quality = -1000;
168 todr_attach(&sc->sc_todr);
169 }
170
171 void
efi_map_runtime(struct efi_softc * sc)172 efi_map_runtime(struct efi_softc *sc)
173 {
174 EFI_MEMORY_DESCRIPTOR *desc;
175 int i;
176
177 /*
178 * We don't really want some random executable non-OpenBSD
179 * code lying around in kernel space. So create a separate
180 * pmap and only activate it when we call runtime services.
181 */
182 sc->sc_pm = pmap_create();
183 sc->sc_pm->pm_privileged = 1;
184 sc->sc_pm->have_4_level_pt = 1;
185
186 desc = mmap;
187 for (i = 0; i < mmap_size / mmap_desc_size; i++) {
188 if (desc->Attribute & EFI_MEMORY_RUNTIME) {
189 vaddr_t va = desc->VirtualStart;
190 paddr_t pa = desc->PhysicalStart;
191 int npages = desc->NumberOfPages;
192 vm_prot_t prot = PROT_READ | PROT_WRITE;
193
194 #ifdef EFI_DEBUG
195 printf("type 0x%x pa 0x%llx va 0x%llx pages 0x%llx attr 0x%llx\n",
196 desc->Type, desc->PhysicalStart,
197 desc->VirtualStart, desc->NumberOfPages,
198 desc->Attribute);
199 #endif
200
201 /*
202 * If the virtual address is still zero, use
203 * an identity mapping.
204 */
205 if (va == 0)
206 va = pa;
207
208 /*
209 * Normal memory is expected to be "write
210 * back" cacheable. Everything else is mapped
211 * as device memory.
212 */
213 if ((desc->Attribute & EFI_MEMORY_WB) == 0)
214 pa |= PMAP_DEVICE;
215
216 /*
217 * Only make pages marked as runtime service code
218 * executable. This violates the standard but it
219 * seems we can get away with it.
220 */
221 if (desc->Type == EfiRuntimeServicesCode)
222 prot |= PROT_EXEC;
223
224 if (desc->Attribute & EFI_MEMORY_RP)
225 prot &= ~PROT_READ;
226 if (desc->Attribute & EFI_MEMORY_XP)
227 prot &= ~PROT_EXEC;
228 if (desc->Attribute & EFI_MEMORY_RO)
229 prot &= ~PROT_WRITE;
230
231 while (npages--) {
232 pmap_enter(sc->sc_pm, va, pa, prot,
233 prot | PMAP_WIRED);
234 va += PAGE_SIZE;
235 pa += PAGE_SIZE;
236 }
237 }
238
239 desc = NextMemoryDescriptor(desc, mmap_desc_size);
240 }
241 }
242
243 void
efi_fault(void)244 efi_fault(void)
245 {
246 longjmp(&efi_jmpbuf);
247 }
248
249 void
efi_enter(struct efi_softc * sc)250 efi_enter(struct efi_softc *sc)
251 {
252 struct pmap *pm = sc->sc_pm;
253 uint64_t tcr;
254
255 sc->sc_psw = intr_disable();
256 WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa);
257 __asm volatile("isb");
258 tcr = READ_SPECIALREG(tcr_el1);
259 tcr &= ~TCR_T0SZ(0x3f);
260 tcr |= TCR_T0SZ(64 - EFI_SPACE_BITS);
261 WRITE_SPECIALREG(tcr_el1, tcr);
262 cpu_setttb(pm->pm_asid, pm->pm_pt0pa);
263
264 fpu_kernel_enter();
265
266 curcpu()->ci_curpcb->pcb_onfault = (void *)efi_fault;
267 }
268
269 void
efi_leave(struct efi_softc * sc)270 efi_leave(struct efi_softc *sc)
271 {
272 struct pmap *pm = curcpu()->ci_curpm;
273 uint64_t tcr;
274
275 curcpu()->ci_curpcb->pcb_onfault = NULL;
276
277 fpu_kernel_exit();
278
279 WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa);
280 __asm volatile("isb");
281 tcr = READ_SPECIALREG(tcr_el1);
282 tcr &= ~TCR_T0SZ(0x3f);
283 tcr |= TCR_T0SZ(64 - USER_SPACE_BITS);
284 WRITE_SPECIALREG(tcr_el1, tcr);
285 cpu_setttb(pm->pm_asid, pm->pm_pt0pa);
286 intr_restore(sc->sc_psw);
287 }
288
289 int
efi_gettime(struct todr_chip_handle * handle,struct timeval * tv)290 efi_gettime(struct todr_chip_handle *handle, struct timeval *tv)
291 {
292 struct efi_softc *sc = handle->cookie;
293 struct clock_ymdhms dt;
294 EFI_TIME time;
295 EFI_STATUS status;
296
297 if (efi_enter_check(sc))
298 return EFAULT;
299 status = sc->sc_rs->GetTime(&time, NULL);
300 efi_leave(sc);
301 if (status != EFI_SUCCESS)
302 return EIO;
303
304 dt.dt_year = time.Year;
305 dt.dt_mon = time.Month;
306 dt.dt_day = time.Day;
307 dt.dt_hour = time.Hour;
308 dt.dt_min = time.Minute;
309 dt.dt_sec = time.Second;
310
311 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
312 dt.dt_day > 31 || dt.dt_day == 0 ||
313 dt.dt_mon > 12 || dt.dt_mon == 0 ||
314 dt.dt_year < POSIX_BASE_YEAR)
315 return EINVAL;
316
317 tv->tv_sec = clock_ymdhms_to_secs(&dt);
318 tv->tv_usec = 0;
319 return 0;
320 }
321
322 int
efi_settime(struct todr_chip_handle * handle,struct timeval * tv)323 efi_settime(struct todr_chip_handle *handle, struct timeval *tv)
324 {
325 struct efi_softc *sc = handle->cookie;
326 struct clock_ymdhms dt;
327 EFI_TIME time;
328 EFI_STATUS status;
329
330 clock_secs_to_ymdhms(tv->tv_sec, &dt);
331
332 time.Year = dt.dt_year;
333 time.Month = dt.dt_mon;
334 time.Day = dt.dt_day;
335 time.Hour = dt.dt_hour;
336 time.Minute = dt.dt_min;
337 time.Second = dt.dt_sec;
338 time.Nanosecond = 0;
339 time.TimeZone = 0;
340 time.Daylight = 0;
341
342 if (efi_enter_check(sc))
343 return EFAULT;
344 status = sc->sc_rs->SetTime(&time);
345 efi_leave(sc);
346 if (status != EFI_SUCCESS)
347 return EIO;
348 return 0;
349 }
350