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 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_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 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 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