xref: /netbsd-src/sys/dev/efi.c (revision 320f2e6f88d556a8b4792d288ebcf676462225ac)
1 /* $NetBSD: efi.c,v 1.9 2023/05/24 00:02:51 riastradh Exp $ */
2 
3 /*-
4  * Copyright (c) 2021 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * This pseudo-driver implements a /dev/efi character device that provides
31  * ioctls for using UEFI runtime time and variable services.
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.9 2023/05/24 00:02:51 riastradh Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/conf.h>
39 #include <sys/kmem.h>
40 #include <sys/atomic.h>
41 #include <sys/efiio.h>
42 
43 #include <uvm/uvm_extern.h>
44 
45 #include <dev/efivar.h>
46 #include <dev/mm.h>
47 
48 #include "ioconf.h"
49 
50 /*
51  * Maximum length of an EFI variable name. The UEFI spec doesn't specify a
52  * constraint, but we want to limit the size to act as a guard rail against
53  * allocating too much kernel memory.
54  */
55 #define	EFI_VARNAME_MAXLENGTH		EFI_PAGE_SIZE
56 
57 /*
58  * Pointer to arch specific EFI backend.
59  */
60 static const struct efi_ops *efi_ops = NULL;
61 
62 /*
63  * Only allow one user of /dev/efi at a time. Even though the MD EFI backends
64  * should serialize individual UEFI RT calls, the UEFI specification says
65  * that a SetVariable() call between calls to GetNextVariableName() may
66  * produce unpredictable results, and we want to avoid this.
67  */
68 static volatile u_int efi_isopen = 0;
69 
70 static dev_type_open(efi_open);
71 static dev_type_close(efi_close);
72 static dev_type_ioctl(efi_ioctl);
73 
74 const struct cdevsw efi_cdevsw = {
75 	.d_open =	efi_open,
76 	.d_close =	efi_close,
77 	.d_ioctl =	efi_ioctl,
78 	.d_read =	noread,
79 	.d_write =	nowrite,
80 	.d_stop =	nostop,
81 	.d_tty =	notty,
82 	.d_poll =	nopoll,
83 	.d_mmap =	nommap,
84 	.d_kqfilter =	nokqfilter,
85 	.d_discard =	nodiscard,
86 	.d_flag =	D_OTHER | D_MPSAFE,
87 };
88 
89 static int
efi_open(dev_t dev,int flags,int type,struct lwp * l)90 efi_open(dev_t dev, int flags, int type, struct lwp *l)
91 {
92 
93 	if (efi_ops == NULL) {
94 		return ENXIO;
95 	}
96 	if (atomic_swap_uint(&efi_isopen, 1) == 1) {
97 		return EBUSY;
98 	}
99 	membar_acquire();
100 	return 0;
101 }
102 
103 static int
efi_close(dev_t dev,int flags,int type,struct lwp * l)104 efi_close(dev_t dev, int flags, int type, struct lwp *l)
105 {
106 
107 	KASSERT(efi_isopen);
108 	atomic_store_release(&efi_isopen, 0);
109 	return 0;
110 }
111 
112 static int
efi_status_to_error(efi_status status)113 efi_status_to_error(efi_status status)
114 {
115 	switch (status) {
116 	case EFI_SUCCESS:
117 		return 0;
118 	case EFI_INVALID_PARAMETER:
119 		return EINVAL;
120 	case EFI_UNSUPPORTED:
121 		return EOPNOTSUPP;
122 	case EFI_BUFFER_TOO_SMALL:
123 		return ERANGE;
124 	case EFI_DEVICE_ERROR:
125 		return EIO;
126 	case EFI_WRITE_PROTECTED:
127 		return EROFS;
128 	case EFI_OUT_OF_RESOURCES:
129 		return ENOMEM;
130 	case EFI_NOT_FOUND:
131 		return ENOENT;
132 	case EFI_SECURITY_VIOLATION:
133 		return EACCES;
134 	default:
135 		return EIO;
136 	}
137 }
138 
139 /* XXX move to efi.h */
140 #define	EFI_SYSTEM_RESOURCE_TABLE_GUID					      \
141 	{0xb122a263,0x3661,0x4f68,0x99,0x29,{0x78,0xf8,0xb0,0xd6,0x21,0x80}}
142 #define	EFI_PROPERTIES_TABLE						      \
143 	{0x880aaca3,0x4adc,0x4a04,0x90,0x79,{0xb7,0x47,0x34,0x08,0x25,0xe5}}
144 
145 #define	EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION	1
146 
147 struct EFI_SYSTEM_RESOURCE_ENTRY {
148 	struct uuid	FwClass;
149 	uint32_t	FwType;
150 	uint32_t	FwVersion;
151 	uint32_t	LowestSupportedFwVersion;
152 	uint32_t	CapsuleFlags;
153 	uint32_t	LastAttemptVersion;
154 	uint32_t	LastAttemptStatus;
155 };
156 
157 struct EFI_SYSTEM_RESOURCE_TABLE {
158 	uint32_t	FwResourceCount;
159 	uint32_t	FwResourceCountMax;
160 	uint64_t	FwResourceVersion;
161 	struct EFI_SYSTEM_RESOURCE_ENTRY	Entries[];
162 };
163 
164 static void *
efi_map_pa(uint64_t addr,bool * directp)165 efi_map_pa(uint64_t addr, bool *directp)
166 {
167 	paddr_t pa = addr;
168 	vaddr_t va;
169 
170 	/*
171 	 * Verify the address is not truncated by conversion to
172 	 * paddr_t.  This might happen with a 64-bit EFI booting a
173 	 * 32-bit OS.
174 	 */
175 	if (pa != addr)
176 		return NULL;
177 
178 	/*
179 	 * Try direct-map if we have it.  If it works, note that it was
180 	 * direct-mapped for efi_unmap.
181 	 */
182 #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS
183 	if (mm_md_direct_mapped_phys(pa, &va)) {
184 		*directp = true;
185 		return (void *)va;
186 	}
187 #endif
188 
189 	/*
190 	 * No direct map.  Reserve a page of kernel virtual address
191 	 * space, with no backing, to map to the physical address.
192 	 */
193 	va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
194 	    UVM_KMF_VAONLY|UVM_KMF_WAITVA);
195 	KASSERT(va != 0);
196 
197 	/*
198 	 * Map the kva page to the physical address and update the
199 	 * kernel pmap so we can use it.
200 	 */
201 	pmap_kenter_pa(va, pa, VM_PROT_READ, 0);
202 	pmap_update(pmap_kernel());
203 
204 	/*
205 	 * Success!  Return the VA and note that it was not
206 	 * direct-mapped for efi_unmap.
207 	 */
208 	*directp = false;
209 	return (void *)va;
210 }
211 
212 static void
efi_unmap(void * ptr,bool direct)213 efi_unmap(void *ptr, bool direct)
214 {
215 	vaddr_t va = (vaddr_t)ptr;
216 
217 	/*
218 	 * If it was direct-mapped, nothing to do here.
219 	 */
220 	if (direct)
221 		return;
222 
223 	/*
224 	 * First remove the mapping from the kernel pmap so that it can
225 	 * be reused, before we free the kva and let anyone else reuse
226 	 * it.
227 	 */
228 	pmap_kremove(va, PAGE_SIZE);
229 	pmap_update(pmap_kernel());
230 
231 	/*
232 	 * Next free the kva so it can be reused by someone else.
233 	 */
234 	uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY);
235 }
236 
237 static int
efi_ioctl_got_table(struct efi_get_table_ioc * ioc,void * ptr,size_t len)238 efi_ioctl_got_table(struct efi_get_table_ioc *ioc, void *ptr, size_t len)
239 {
240 
241 	/*
242 	 * Return the actual table length.
243 	 */
244 	ioc->table_len = len;
245 
246 	/*
247 	 * Copy out as much as we can into the user's allocated buffer.
248 	 */
249 	return copyout(ptr, ioc->buf, MIN(ioc->buf_len, len));
250 }
251 
252 static int
efi_ioctl_get_esrt(struct efi_get_table_ioc * ioc,struct EFI_SYSTEM_RESOURCE_TABLE * tab)253 efi_ioctl_get_esrt(struct efi_get_table_ioc *ioc,
254     struct EFI_SYSTEM_RESOURCE_TABLE *tab)
255 {
256 
257 	/*
258 	 * Verify the firmware resource version is one we understand.
259 	 */
260 	if (tab->FwResourceVersion !=
261 	    EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION)
262 		return ENOENT;
263 
264 	/*
265 	 * Verify the resource count fits within the single page we
266 	 * have mapped.
267 	 *
268 	 * XXX What happens if it doesn't?  Are we expected to map more
269 	 * than one page, according to the table header?  The UEFI spec
270 	 * is unclear on this.
271 	 */
272 	const size_t entry_space = PAGE_SIZE -
273 	    offsetof(struct EFI_SYSTEM_RESOURCE_TABLE, Entries);
274 	if (tab->FwResourceCount > entry_space/sizeof(tab->Entries[0]))
275 		return ENOENT;
276 
277 	/*
278 	 * Success!  Return everything through the last table entry.
279 	 */
280 	const size_t len = offsetof(struct EFI_SYSTEM_RESOURCE_TABLE,
281 	    Entries[tab->FwResourceCount]);
282 	return efi_ioctl_got_table(ioc, tab, len);
283 }
284 
285 static int
efi_ioctl_get_table(struct efi_get_table_ioc * ioc)286 efi_ioctl_get_table(struct efi_get_table_ioc *ioc)
287 {
288 	uint64_t addr;
289 	bool direct;
290 	efi_status status;
291 	int error;
292 
293 	/*
294 	 * If the platform doesn't support it yet, fail now.
295 	 */
296 	if (efi_ops->efi_gettab == NULL)
297 		return ENODEV;
298 
299 	/*
300 	 * Get the address of the requested table out of the EFI
301 	 * configuration table.
302 	 */
303 	status = efi_ops->efi_gettab(&ioc->uuid, &addr);
304 	if (status != EFI_SUCCESS)
305 		return efi_status_to_error(status);
306 
307 	/*
308 	 * UEFI provides no generic way to identify the size of the
309 	 * table, so we have to bake knowledge of every vendor GUID
310 	 * into this code to safely expose the right amount of data to
311 	 * userland.
312 	 *
313 	 * We even have to bake knowledge of which ones are physically
314 	 * addressed and which ones might be virtually addressed
315 	 * according to the vendor GUID into this code, although for
316 	 * the moment we never use RT->SetVirtualAddressMap so we only
317 	 * ever have to deal with physical addressing.
318 	 */
319 	if (memcmp(&ioc->uuid, &(struct uuid)EFI_SYSTEM_RESOURCE_TABLE_GUID,
320 		sizeof(ioc->uuid)) == 0) {
321 		struct EFI_SYSTEM_RESOURCE_TABLE *tab;
322 
323 		if ((tab = efi_map_pa(addr, &direct)) == NULL)
324 			return ENOENT;
325 		error = efi_ioctl_get_esrt(ioc, tab);
326 		efi_unmap(tab, direct);
327 	} else {
328 		error = ENOENT;
329 	}
330 
331 	return error;
332 }
333 
334 static int
efi_ioctl_var_get(struct efi_var_ioc * var)335 efi_ioctl_var_get(struct efi_var_ioc *var)
336 {
337 	uint16_t *namebuf;
338 	void *databuf = NULL;
339 	size_t databufsize;
340 	unsigned long datasize;
341 	efi_status status;
342 	int error;
343 
344 	if (var->name == NULL || var->namesize == 0 ||
345 	    (var->data != NULL && var->datasize == 0)) {
346 		return EINVAL;
347 	}
348 	if (var->namesize > EFI_VARNAME_MAXLENGTH) {
349 		return ENOMEM;
350 	}
351 	if (var->datasize > ULONG_MAX) { /* XXX stricter limit */
352 		return ENOMEM;
353 	}
354 
355 	namebuf = kmem_alloc(var->namesize, KM_SLEEP);
356 	error = copyin(var->name, namebuf, var->namesize);
357 	if (error != 0) {
358 		goto done;
359 	}
360 	if (namebuf[var->namesize / 2 - 1] != '\0') {
361 		error = EINVAL;
362 		goto done;
363 	}
364 	databufsize = var->datasize;
365 	if (databufsize != 0) {
366 		databuf = kmem_alloc(databufsize, KM_SLEEP);
367 		error = copyin(var->data, databuf, databufsize);
368 		if (error != 0) {
369 			goto done;
370 		}
371 	}
372 
373 	datasize = databufsize;
374 	status = efi_ops->efi_getvar(namebuf, &var->vendor, &var->attrib,
375 	    &datasize, databuf);
376 	if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
377 		error = efi_status_to_error(status);
378 		goto done;
379 	}
380 	var->datasize = datasize;
381 	if (status == EFI_SUCCESS && databufsize != 0) {
382 		error = copyout(databuf, var->data,
383 		    MIN(datasize, databufsize));
384 	} else {
385 		var->data = NULL;
386 	}
387 
388 done:
389 	kmem_free(namebuf, var->namesize);
390 	if (databuf != NULL) {
391 		kmem_free(databuf, databufsize);
392 	}
393 	return error;
394 }
395 
396 static int
efi_ioctl_var_next(struct efi_var_ioc * var)397 efi_ioctl_var_next(struct efi_var_ioc *var)
398 {
399 	efi_status status;
400 	uint16_t *namebuf;
401 	size_t namebufsize;
402 	unsigned long namesize;
403 	int error;
404 
405 	if (var->name == NULL || var->namesize == 0) {
406 		return EINVAL;
407 	}
408 	if (var->namesize > EFI_VARNAME_MAXLENGTH) {
409 		return ENOMEM;
410 	}
411 
412 	namebufsize = var->namesize;
413 	namebuf = kmem_alloc(namebufsize, KM_SLEEP);
414 	error = copyin(var->name, namebuf, namebufsize);
415 	if (error != 0) {
416 		goto done;
417 	}
418 
419 	CTASSERT(EFI_VARNAME_MAXLENGTH <= ULONG_MAX);
420 	namesize = namebufsize;
421 	status = efi_ops->efi_nextvar(&namesize, namebuf, &var->vendor);
422 	if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
423 		error = efi_status_to_error(status);
424 		goto done;
425 	}
426 	var->namesize = namesize;
427 	if (status == EFI_SUCCESS) {
428 		error = copyout(namebuf, var->name,
429 		    MIN(namesize, namebufsize));
430 	} else {
431 		var->name = NULL;
432 	}
433 
434 done:
435 	kmem_free(namebuf, namebufsize);
436 	return error;
437 }
438 
439 static int
efi_ioctl_var_set(struct efi_var_ioc * var)440 efi_ioctl_var_set(struct efi_var_ioc *var)
441 {
442 	efi_status status;
443 	uint16_t *namebuf;
444 	uint16_t *databuf = NULL;
445 	int error;
446 
447 	if (var->name == NULL || var->namesize == 0) {
448 		return EINVAL;
449 	}
450 
451 	namebuf = kmem_alloc(var->namesize, KM_SLEEP);
452 	error = copyin(var->name, namebuf, var->namesize);
453 	if (error != 0) {
454 		goto done;
455 	}
456 	if (namebuf[var->namesize / 2 - 1] != '\0') {
457 		error = EINVAL;
458 		goto done;
459 	}
460 	if (var->datasize != 0) {
461 		databuf = kmem_alloc(var->datasize, KM_SLEEP);
462 		error = copyin(var->data, databuf, var->datasize);
463 		if (error != 0) {
464 			goto done;
465 		}
466 	}
467 
468 	status = efi_ops->efi_setvar(namebuf, &var->vendor, var->attrib,
469 	    var->datasize, databuf);
470 	error = efi_status_to_error(status);
471 
472 done:
473 	kmem_free(namebuf, var->namesize);
474 	if (databuf != NULL) {
475 		kmem_free(databuf, var->datasize);
476 	}
477 	return error;
478 }
479 
480 static int
efi_ioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)481 efi_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
482 {
483 	KASSERT(efi_ops != NULL);
484 
485 	switch (cmd) {
486 	case EFIIOC_GET_TABLE:
487 		return efi_ioctl_get_table(data);
488 	case EFIIOC_VAR_GET:
489 		return efi_ioctl_var_get(data);
490 	case EFIIOC_VAR_NEXT:
491 		return efi_ioctl_var_next(data);
492 	case EFIIOC_VAR_SET:
493 		return efi_ioctl_var_set(data);
494 	}
495 
496 	return ENOTTY;
497 }
498 
499 void
efi_register_ops(const struct efi_ops * ops)500 efi_register_ops(const struct efi_ops *ops)
501 {
502 	KASSERT(efi_ops == NULL);
503 	efi_ops = ops;
504 }
505 
506 void
efiattach(int count)507 efiattach(int count)
508 {
509 }
510