efi.c revision 1.7 1 /* $NetBSD: efi.c,v 1.7 2023/05/22 16:27:58 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.7 2023/05/22 16:27:58 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 datasize;
340 efi_status status;
341 int error;
342
343 if (var->name == NULL || var->namesize == 0 ||
344 (var->data != NULL && var->datasize == 0)) {
345 return EINVAL;
346 }
347 if (var->namesize > EFI_VARNAME_MAXLENGTH) {
348 return ENOMEM;
349 }
350
351 namebuf = kmem_alloc(var->namesize, KM_SLEEP);
352 error = copyin(var->name, namebuf, var->namesize);
353 if (error != 0) {
354 goto done;
355 }
356 if (namebuf[var->namesize / 2 - 1] != '\0') {
357 error = EINVAL;
358 goto done;
359 }
360 datasize = var->datasize;
361 if (datasize != 0) {
362 databuf = kmem_alloc(datasize, KM_SLEEP);
363 error = copyin(var->data, databuf, datasize);
364 if (error != 0) {
365 goto done;
366 }
367 }
368
369 status = efi_ops->efi_getvar(namebuf, &var->vendor, &var->attrib,
370 &var->datasize, databuf);
371 if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
372 error = efi_status_to_error(status);
373 goto done;
374 }
375 if (status == EFI_SUCCESS && databuf != NULL) {
376 error = copyout(databuf, var->data, var->datasize);
377 } else {
378 var->data = NULL;
379 }
380
381 done:
382 kmem_free(namebuf, var->namesize);
383 if (databuf != NULL) {
384 kmem_free(databuf, datasize);
385 }
386 return error;
387 }
388
389 static int
390 efi_ioctl_var_next(struct efi_var_ioc *var)
391 {
392 efi_status status;
393 uint16_t *namebuf;
394 size_t namesize;
395 int error;
396
397 if (var->name == NULL || var->namesize == 0) {
398 return EINVAL;
399 }
400 if (var->namesize > EFI_VARNAME_MAXLENGTH) {
401 return ENOMEM;
402 }
403
404 namesize = var->namesize;
405 namebuf = kmem_alloc(namesize, KM_SLEEP);
406 error = copyin(var->name, namebuf, namesize);
407 if (error != 0) {
408 goto done;
409 }
410
411 status = efi_ops->efi_nextvar(&var->namesize, namebuf, &var->vendor);
412 if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
413 error = efi_status_to_error(status);
414 goto done;
415 }
416 if (status == EFI_SUCCESS) {
417 error = copyout(namebuf, var->name, var->namesize);
418 } else {
419 var->name = NULL;
420 }
421
422 done:
423 kmem_free(namebuf, namesize);
424 return error;
425 }
426
427 static int
428 efi_ioctl_var_set(struct efi_var_ioc *var)
429 {
430 efi_status status;
431 uint16_t *namebuf;
432 uint16_t *databuf = NULL;
433 int error;
434
435 if (var->name == NULL || var->namesize == 0) {
436 return EINVAL;
437 }
438
439 namebuf = kmem_alloc(var->namesize, KM_SLEEP);
440 error = copyin(var->name, namebuf, var->namesize);
441 if (error != 0) {
442 goto done;
443 }
444 if (namebuf[var->namesize / 2 - 1] != '\0') {
445 error = EINVAL;
446 goto done;
447 }
448 if (var->datasize != 0) {
449 databuf = kmem_alloc(var->datasize, KM_SLEEP);
450 error = copyin(var->data, databuf, var->datasize);
451 if (error != 0) {
452 goto done;
453 }
454 }
455
456 status = efi_ops->efi_setvar(namebuf, &var->vendor, var->attrib,
457 var->datasize, databuf);
458 error = efi_status_to_error(status);
459
460 done:
461 kmem_free(namebuf, var->namesize);
462 if (databuf != NULL) {
463 kmem_free(databuf, var->datasize);
464 }
465 return error;
466 }
467
468 static int
469 efi_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
470 {
471 KASSERT(efi_ops != NULL);
472
473 switch (cmd) {
474 case EFIIOC_GET_TABLE:
475 return efi_ioctl_get_table(data);
476 case EFIIOC_VAR_GET:
477 return efi_ioctl_var_get(data);
478 case EFIIOC_VAR_NEXT:
479 return efi_ioctl_var_next(data);
480 case EFIIOC_VAR_SET:
481 return efi_ioctl_var_set(data);
482 }
483
484 return ENOTTY;
485 }
486
487 void
488 efi_register_ops(const struct efi_ops *ops)
489 {
490 KASSERT(efi_ops == NULL);
491 efi_ops = ops;
492 }
493
494 void
495 efiattach(int count)
496 {
497 }
498