xref: /openbsd-src/sys/dev/efi/efi.c (revision f7910aa7534d27bc146c2addca49ce5c8b5f8c36)
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