1 1.10 riastrad /* $NetBSD: efi.c,v 1.10 2025/03/30 14:36:48 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.10 riastrad __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.10 2025/03/30 14:36:48 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.10 riastrad * Maximum length of an EFI variable name in bytes. The UEFI spec 52 1.10 riastrad * doesn't specify a constraint, but we want to limit the size to act 53 1.10 riastrad * as a guard rail against allocating too much kernel memory. 54 1.1 jmcneill */ 55 1.10 riastrad #define EFI_VARNAME_MAXBYTES 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.10 riastrad if (var->namesize > EFI_VARNAME_MAXBYTES) { 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.10 riastrad if (var->namesize > EFI_VARNAME_MAXBYTES) { 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.10 riastrad CTASSERT(EFI_VARNAME_MAXBYTES <= 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