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
efi_open(dev_t dev,int flags,int type,struct lwp * l)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
efi_close(dev_t dev,int flags,int type,struct lwp * l)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
efi_status_to_error(efi_status status)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 *
efi_map_pa(uint64_t addr,bool * directp)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
efi_unmap(void * ptr,bool direct)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
efi_ioctl_got_table(struct efi_get_table_ioc * ioc,void * ptr,size_t len)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
efi_ioctl_get_esrt(struct efi_get_table_ioc * ioc,struct EFI_SYSTEM_RESOURCE_TABLE * tab)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
efi_ioctl_get_table(struct efi_get_table_ioc * ioc)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
efi_ioctl_var_get(struct efi_var_ioc * var)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
efi_ioctl_var_next(struct efi_var_ioc * var)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
efi_ioctl_var_set(struct efi_var_ioc * var)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
efi_ioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)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
efi_register_ops(const struct efi_ops * ops)500 efi_register_ops(const struct efi_ops *ops)
501 {
502 KASSERT(efi_ops == NULL);
503 efi_ops = ops;
504 }
505
506 void
efiattach(int count)507 efiattach(int count)
508 {
509 }
510