1*f7910aa7Skettenis /* $OpenBSD: efi.c,v 1.2 2024/07/10 10:53:55 kettenis Exp $ */
2f2f88681Skettenis /*
3f2f88681Skettenis * Copyright (c) 2022 3mdeb <contact@3mdeb.com>
4f2f88681Skettenis *
5f2f88681Skettenis * Permission to use, copy, modify, and distribute this software for any
6f2f88681Skettenis * purpose with or without fee is hereby granted, provided that the above
7f2f88681Skettenis * copyright notice and this permission notice appear in all copies.
8f2f88681Skettenis *
9f2f88681Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f2f88681Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f2f88681Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f2f88681Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f2f88681Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f2f88681Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f2f88681Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f2f88681Skettenis */
17f2f88681Skettenis
18f2f88681Skettenis #include <sys/param.h>
19f2f88681Skettenis #include <sys/systm.h>
20f2f88681Skettenis #include <sys/malloc.h>
21f2f88681Skettenis
22f2f88681Skettenis #include <dev/efi/efi.h>
23f2f88681Skettenis #include <dev/efi/efiio.h>
24f2f88681Skettenis #include <machine/efivar.h>
25f2f88681Skettenis
26f2f88681Skettenis struct cfdriver efi_cd = {
27f2f88681Skettenis NULL, "efi", DV_DULL
28f2f88681Skettenis };
29f2f88681Skettenis
30f2f88681Skettenis int efiioc_get_table(struct efi_softc *sc, void *);
31f2f88681Skettenis int efiioc_var_get(struct efi_softc *sc, void *);
32f2f88681Skettenis int efiioc_var_next(struct efi_softc *sc, void *);
33f2f88681Skettenis int efiioc_var_set(struct efi_softc *sc, void *);
34f2f88681Skettenis int efi_adapt_error(EFI_STATUS);
35f2f88681Skettenis
36*f7910aa7Skettenis EFI_GET_VARIABLE efi_get_variable;
37*f7910aa7Skettenis EFI_SET_VARIABLE efi_set_variable;
38*f7910aa7Skettenis EFI_GET_NEXT_VARIABLE_NAME efi_get_next_variable_name;
39*f7910aa7Skettenis
40f2f88681Skettenis int
efiopen(dev_t dev,int flag,int mode,struct proc * p)41f2f88681Skettenis efiopen(dev_t dev, int flag, int mode, struct proc *p)
42f2f88681Skettenis {
43f2f88681Skettenis return (efi_cd.cd_ndevs > 0 ? 0 : ENXIO);
44f2f88681Skettenis }
45f2f88681Skettenis
46f2f88681Skettenis int
eficlose(dev_t dev,int flag,int mode,struct proc * p)47f2f88681Skettenis eficlose(dev_t dev, int flag, int mode, struct proc *p)
48f2f88681Skettenis {
49f2f88681Skettenis return 0;
50f2f88681Skettenis }
51f2f88681Skettenis
52f2f88681Skettenis int
efiioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)53f2f88681Skettenis efiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
54f2f88681Skettenis {
55f2f88681Skettenis struct efi_softc *sc = efi_cd.cd_devs[0];
56f2f88681Skettenis int error;
57f2f88681Skettenis
58f2f88681Skettenis switch (cmd) {
59f2f88681Skettenis case EFIIOC_GET_TABLE:
60f2f88681Skettenis error = efiioc_get_table(sc, data);
61f2f88681Skettenis break;
62f2f88681Skettenis case EFIIOC_VAR_GET:
63f2f88681Skettenis error = efiioc_var_get(sc, data);
64f2f88681Skettenis break;
65f2f88681Skettenis case EFIIOC_VAR_NEXT:
66f2f88681Skettenis error = efiioc_var_next(sc, data);
67f2f88681Skettenis break;
68f2f88681Skettenis case EFIIOC_VAR_SET:
69f2f88681Skettenis error = efiioc_var_set(sc, data);
70f2f88681Skettenis break;
71f2f88681Skettenis default:
72f2f88681Skettenis error = ENOTTY;
73f2f88681Skettenis break;
74f2f88681Skettenis }
75f2f88681Skettenis
76f2f88681Skettenis return error;
77f2f88681Skettenis }
78f2f88681Skettenis
79f2f88681Skettenis int
efiioc_get_table(struct efi_softc * sc,void * data)80f2f88681Skettenis efiioc_get_table(struct efi_softc *sc, void *data)
81f2f88681Skettenis {
82f2f88681Skettenis EFI_GUID esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID;
83f2f88681Skettenis struct efi_get_table_ioc *ioc = data;
84f2f88681Skettenis char *buf = NULL;
85f2f88681Skettenis int error;
86f2f88681Skettenis
87f2f88681Skettenis /* Only ESRT is supported at the moment. */
88f2f88681Skettenis if (memcmp(&ioc->uuid, &esrt_guid, sizeof(ioc->uuid)) != 0)
89f2f88681Skettenis return EINVAL;
90f2f88681Skettenis
91f2f88681Skettenis /* ESRT might not be present. */
92f2f88681Skettenis if (sc->sc_esrt == NULL)
93f2f88681Skettenis return ENXIO;
94f2f88681Skettenis
95f2f88681Skettenis if (efi_enter_check(sc)) {
96f2f88681Skettenis free(buf, M_TEMP, ioc->table_len);
97f2f88681Skettenis return ENOSYS;
98f2f88681Skettenis }
99f2f88681Skettenis
100f2f88681Skettenis ioc->table_len = sizeof(*sc->sc_esrt) +
101f2f88681Skettenis sizeof(EFI_SYSTEM_RESOURCE_ENTRY) * sc->sc_esrt->FwResourceCount;
102f2f88681Skettenis
103f2f88681Skettenis /* Return table length to userspace. */
104f2f88681Skettenis if (ioc->buf == NULL) {
105f2f88681Skettenis efi_leave(sc);
106f2f88681Skettenis return 0;
107f2f88681Skettenis }
108f2f88681Skettenis
109f2f88681Skettenis /* Refuse to copy only part of the table. */
110f2f88681Skettenis if (ioc->buf_len < ioc->table_len) {
111f2f88681Skettenis efi_leave(sc);
112f2f88681Skettenis return EINVAL;
113f2f88681Skettenis }
114f2f88681Skettenis
115f2f88681Skettenis buf = malloc(ioc->table_len, M_TEMP, M_WAITOK);
116f2f88681Skettenis memcpy(buf, sc->sc_esrt, ioc->table_len);
117f2f88681Skettenis
118f2f88681Skettenis efi_leave(sc);
119f2f88681Skettenis
120f2f88681Skettenis error = copyout(buf, ioc->buf, ioc->table_len);
121f2f88681Skettenis free(buf, M_TEMP, ioc->table_len);
122f2f88681Skettenis
123f2f88681Skettenis return error;
124f2f88681Skettenis }
125f2f88681Skettenis
126f2f88681Skettenis int
efiioc_var_get(struct efi_softc * sc,void * data)127f2f88681Skettenis efiioc_var_get(struct efi_softc *sc, void *data)
128f2f88681Skettenis {
129f2f88681Skettenis struct efi_var_ioc *ioc = data;
130f2f88681Skettenis void *value = NULL;
131f2f88681Skettenis efi_char *name = NULL;
132f2f88681Skettenis size_t valuesize = ioc->datasize;
133f2f88681Skettenis EFI_STATUS status;
134f2f88681Skettenis int error;
135f2f88681Skettenis
136f2f88681Skettenis if (valuesize > 0)
137f2f88681Skettenis value = malloc(valuesize, M_TEMP, M_WAITOK);
138f2f88681Skettenis name = malloc(ioc->namesize, M_TEMP, M_WAITOK);
139f2f88681Skettenis error = copyin(ioc->name, name, ioc->namesize);
140f2f88681Skettenis if (error != 0)
141f2f88681Skettenis goto leave;
142f2f88681Skettenis
143f2f88681Skettenis /* NULL-terminated name must fit into namesize bytes. */
144f2f88681Skettenis if (name[ioc->namesize / sizeof(*name) - 1] != 0) {
145f2f88681Skettenis error = EINVAL;
146f2f88681Skettenis goto leave;
147f2f88681Skettenis }
148f2f88681Skettenis
149*f7910aa7Skettenis if (efi_get_variable) {
150*f7910aa7Skettenis status = efi_get_variable(name, (EFI_GUID *)&ioc->vendor,
151*f7910aa7Skettenis &ioc->attrib, &ioc->datasize, value);
152*f7910aa7Skettenis } else {
153f2f88681Skettenis if (efi_enter_check(sc)) {
154f2f88681Skettenis error = ENOSYS;
155f2f88681Skettenis goto leave;
156f2f88681Skettenis }
157f2f88681Skettenis status = sc->sc_rs->GetVariable(name, (EFI_GUID *)&ioc->vendor,
158f2f88681Skettenis &ioc->attrib, &ioc->datasize, value);
159f2f88681Skettenis efi_leave(sc);
160*f7910aa7Skettenis }
161f2f88681Skettenis
162f2f88681Skettenis if (status == EFI_BUFFER_TOO_SMALL) {
163f2f88681Skettenis /*
164f2f88681Skettenis * Return size of the value, which was set by EFI RT,
165f2f88681Skettenis * reporting no error to match FreeBSD's behaviour.
166f2f88681Skettenis */
167f2f88681Skettenis ioc->data = NULL;
168f2f88681Skettenis goto leave;
169f2f88681Skettenis }
170f2f88681Skettenis
171f2f88681Skettenis error = efi_adapt_error(status);
172f2f88681Skettenis if (error == 0)
173f2f88681Skettenis error = copyout(value, ioc->data, ioc->datasize);
174f2f88681Skettenis
175f2f88681Skettenis leave:
176f2f88681Skettenis free(value, M_TEMP, valuesize);
177f2f88681Skettenis free(name, M_TEMP, ioc->namesize);
178f2f88681Skettenis return error;
179f2f88681Skettenis }
180f2f88681Skettenis
181f2f88681Skettenis int
efiioc_var_next(struct efi_softc * sc,void * data)182f2f88681Skettenis efiioc_var_next(struct efi_softc *sc, void *data)
183f2f88681Skettenis {
184f2f88681Skettenis struct efi_var_ioc *ioc = data;
185f2f88681Skettenis efi_char *name;
186f2f88681Skettenis size_t namesize = ioc->namesize;
187f2f88681Skettenis EFI_STATUS status;
188f2f88681Skettenis int error;
189f2f88681Skettenis
190f2f88681Skettenis name = malloc(namesize, M_TEMP, M_WAITOK);
191f2f88681Skettenis error = copyin(ioc->name, name, namesize);
192f2f88681Skettenis if (error)
193f2f88681Skettenis goto leave;
194f2f88681Skettenis
195*f7910aa7Skettenis if (efi_get_next_variable_name) {
196*f7910aa7Skettenis status = efi_get_next_variable_name(&ioc->namesize,
197*f7910aa7Skettenis name, (EFI_GUID *)&ioc->vendor);
198*f7910aa7Skettenis } else {
199f2f88681Skettenis if (efi_enter_check(sc)) {
200f2f88681Skettenis error = ENOSYS;
201f2f88681Skettenis goto leave;
202f2f88681Skettenis }
203f2f88681Skettenis status = sc->sc_rs->GetNextVariableName(&ioc->namesize,
204f2f88681Skettenis name, (EFI_GUID *)&ioc->vendor);
205f2f88681Skettenis efi_leave(sc);
206*f7910aa7Skettenis }
207f2f88681Skettenis
208f2f88681Skettenis if (status == EFI_BUFFER_TOO_SMALL) {
209f2f88681Skettenis /*
210f2f88681Skettenis * Return size of the name, which was set by EFI RT,
211f2f88681Skettenis * reporting no error to match FreeBSD's behaviour.
212f2f88681Skettenis */
213f2f88681Skettenis ioc->name = NULL;
214f2f88681Skettenis goto leave;
215f2f88681Skettenis }
216f2f88681Skettenis
217f2f88681Skettenis error = efi_adapt_error(status);
218f2f88681Skettenis if (error == 0)
219f2f88681Skettenis error = copyout(name, ioc->name, ioc->namesize);
220f2f88681Skettenis
221f2f88681Skettenis leave:
222f2f88681Skettenis free(name, M_TEMP, namesize);
223f2f88681Skettenis return error;
224f2f88681Skettenis }
225f2f88681Skettenis
226f2f88681Skettenis int
efiioc_var_set(struct efi_softc * sc,void * data)227f2f88681Skettenis efiioc_var_set(struct efi_softc *sc, void *data)
228f2f88681Skettenis {
229f2f88681Skettenis struct efi_var_ioc *ioc = data;
230f2f88681Skettenis void *value = NULL;
231f2f88681Skettenis efi_char *name = NULL;
232f2f88681Skettenis EFI_STATUS status;
233f2f88681Skettenis int error;
234f2f88681Skettenis
235f2f88681Skettenis /* Zero datasize means variable deletion. */
236f2f88681Skettenis if (ioc->datasize > 0) {
237f2f88681Skettenis value = malloc(ioc->datasize, M_TEMP, M_WAITOK);
238f2f88681Skettenis error = copyin(ioc->data, value, ioc->datasize);
239f2f88681Skettenis if (error)
240f2f88681Skettenis goto leave;
241f2f88681Skettenis }
242f2f88681Skettenis
243f2f88681Skettenis name = malloc(ioc->namesize, M_TEMP, M_WAITOK);
244f2f88681Skettenis error = copyin(ioc->name, name, ioc->namesize);
245f2f88681Skettenis if (error)
246f2f88681Skettenis goto leave;
247f2f88681Skettenis
248f2f88681Skettenis /* NULL-terminated name must fit into namesize bytes. */
249f2f88681Skettenis if (name[ioc->namesize / sizeof(*name) - 1] != 0) {
250f2f88681Skettenis error = EINVAL;
251f2f88681Skettenis goto leave;
252f2f88681Skettenis }
253f2f88681Skettenis
254f2f88681Skettenis if (securelevel > 0) {
255f2f88681Skettenis error = EPERM;
256f2f88681Skettenis goto leave;
257f2f88681Skettenis }
258f2f88681Skettenis
259*f7910aa7Skettenis if (efi_set_variable) {
260*f7910aa7Skettenis status = efi_set_variable(name, (EFI_GUID *)&ioc->vendor,
261*f7910aa7Skettenis ioc->attrib, ioc->datasize, value);
262*f7910aa7Skettenis } else {
263f2f88681Skettenis if (efi_enter_check(sc)) {
264f2f88681Skettenis error = ENOSYS;
265f2f88681Skettenis goto leave;
266f2f88681Skettenis }
267f2f88681Skettenis status = sc->sc_rs->SetVariable(name, (EFI_GUID *)&ioc->vendor,
268f2f88681Skettenis ioc->attrib, ioc->datasize, value);
269f2f88681Skettenis efi_leave(sc);
270*f7910aa7Skettenis }
271f2f88681Skettenis
272f2f88681Skettenis error = efi_adapt_error(status);
273f2f88681Skettenis
274f2f88681Skettenis leave:
275f2f88681Skettenis free(value, M_TEMP, ioc->datasize);
276f2f88681Skettenis free(name, M_TEMP, ioc->namesize);
277f2f88681Skettenis return error;
278f2f88681Skettenis }
279f2f88681Skettenis
280f2f88681Skettenis int
efi_adapt_error(EFI_STATUS status)281f2f88681Skettenis efi_adapt_error(EFI_STATUS status)
282f2f88681Skettenis {
283f2f88681Skettenis switch (status) {
284f2f88681Skettenis case EFI_SUCCESS:
285f2f88681Skettenis return 0;
286f2f88681Skettenis case EFI_DEVICE_ERROR:
287f2f88681Skettenis return EIO;
288f2f88681Skettenis case EFI_INVALID_PARAMETER:
289f2f88681Skettenis return EINVAL;
290f2f88681Skettenis case EFI_NOT_FOUND:
291f2f88681Skettenis return ENOENT;
292f2f88681Skettenis case EFI_OUT_OF_RESOURCES:
293f2f88681Skettenis return EAGAIN;
294f2f88681Skettenis case EFI_SECURITY_VIOLATION:
295f2f88681Skettenis return EPERM;
296f2f88681Skettenis case EFI_UNSUPPORTED:
297f2f88681Skettenis return ENOSYS;
298f2f88681Skettenis case EFI_WRITE_PROTECTED:
299f2f88681Skettenis return EROFS;
300f2f88681Skettenis default:
301f2f88681Skettenis return EIO;
302f2f88681Skettenis }
303f2f88681Skettenis }
304