efi.c revision 1.8 1 1.8 riastrad /* $NetBSD: efi.c,v 1.8 2023/05/22 16:28:16 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.8 riastrad __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.8 2023/05/22 16:28:16 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 KASSERT(datasize <= databufsize);
381 1.8 riastrad var->datasize = datasize;
382 1.1 jmcneill if (status == EFI_SUCCESS && databuf != NULL) {
383 1.1 jmcneill error = copyout(databuf, var->data, var->datasize);
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 KASSERT(namesize <= namebufsize);
427 1.8 riastrad var->namesize = namesize;
428 1.1 jmcneill if (status == EFI_SUCCESS) {
429 1.1 jmcneill error = copyout(namebuf, var->name, var->namesize);
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