Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: efi.c,v 1.10 2025/03/30 14:36:48 riastradh Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2021 Jared McNeill <jmcneill (at) 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.10 2025/03/30 14:36:48 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 in bytes. The UEFI spec
     52  * doesn't specify a constraint, but we want to limit the size to act
     53  * as a guard rail against allocating too much kernel memory.
     54  */
     55 #define	EFI_VARNAME_MAXBYTES		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
     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
    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
    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 *
    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
    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
    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
    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
    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
    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_MAXBYTES) {
    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
    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_MAXBYTES) {
    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_MAXBYTES <= 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
    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
    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
    500 efi_register_ops(const struct efi_ops *ops)
    501 {
    502 	KASSERT(efi_ops == NULL);
    503 	efi_ops = ops;
    504 }
    505 
    506 void
    507 efiattach(int count)
    508 {
    509 }
    510