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