xref: /openbsd-src/sys/arch/arm64/dev/efi_machdep.c (revision f2f886813ecce43460715d9633751066dbbedd5c)
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 = &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