1307070Simp/*-
2307070Simp * Copyright (c) 2016 Netflix, Inc.
3307070Simp * All rights reserved.
4307070Simp *
5307070Simp * Redistribution and use in source and binary forms, with or without
6307070Simp * modification, are permitted provided that the following conditions
7307070Simp * are met:
8307070Simp * 1. Redistributions of source code must retain the above copyright
9307070Simp *    notice, this list of conditions and the following disclaimer
10307070Simp *    in this position and unchanged.
11307070Simp * 2. Redistributions in binary form must reproduce the above copyright
12307070Simp *    notice, this list of conditions and the following disclaimer in the
13307070Simp *    documentation and/or other materials provided with the distribution.
14307070Simp *
15307070Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16307070Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17307070Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18307070Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19307070Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20307070Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21307070Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22307070Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23307070Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24307070Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25307070Simp */
26307070Simp
27307070Simp#include <sys/cdefs.h>
28307070Simp__FBSDID("$FreeBSD: stable/11/sys/dev/efidev/efidev.c 332028 2018-04-04 13:58:18Z kevans $");
29307070Simp
30307070Simp#include <sys/param.h>
31307070Simp#include <sys/systm.h>
32307070Simp#include <sys/kernel.h>
33307070Simp#include <sys/bus.h>
34307070Simp#include <sys/conf.h>
35307070Simp#include <sys/lock.h>
36307070Simp#include <sys/malloc.h>
37307070Simp#include <sys/module.h>
38307070Simp
39307070Simp#include <machine/efi.h>
40307070Simp#include <sys/efiio.h>
41307070Simp
42307070Simpstatic d_ioctl_t efidev_ioctl;
43307070Simp
44307070Simpstatic struct cdevsw efi_cdevsw = {
45307070Simp	.d_name = "efi",
46307070Simp	.d_version = D_VERSION,
47307070Simp	.d_ioctl = efidev_ioctl,
48307070Simp};
49307070Simp
50307070Simpstatic int
51307070Simpefidev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
52307070Simp    int flags __unused, struct thread *td __unused)
53307070Simp{
54307070Simp	int error;
55307070Simp
56307070Simp	switch (cmd) {
57307070Simp	case EFIIOC_GET_TABLE:
58307070Simp	{
59307070Simp		struct efi_get_table_ioc *egtioc =
60307070Simp		    (struct efi_get_table_ioc *)addr;
61307070Simp
62307070Simp		error = efi_get_table(&egtioc->uuid, &egtioc->ptr);
63307070Simp		break;
64307070Simp	}
65307070Simp	case EFIIOC_GET_TIME:
66307070Simp	{
67307070Simp		struct efi_tm *tm = (struct efi_tm *)addr;
68307070Simp
69307070Simp		error = efi_get_time(tm);
70307070Simp		break;
71307070Simp	}
72307070Simp	case EFIIOC_SET_TIME:
73307070Simp	{
74307070Simp		struct efi_tm *tm = (struct efi_tm *)addr;
75307070Simp
76307070Simp		error = efi_set_time(tm);
77307070Simp		break;
78307070Simp	}
79307070Simp	case EFIIOC_VAR_GET:
80307070Simp	{
81307070Simp		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
82307070Simp		void *data;
83307070Simp		efi_char *name;
84307070Simp
85307070Simp		data = malloc(ev->datasize, M_TEMP, M_WAITOK);
86307070Simp		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
87307070Simp		error = copyin(ev->name, name, ev->namesize);
88307070Simp		if (error)
89307070Simp			goto vg_out;
90307070Simp		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
91307070Simp			error = EINVAL;
92307070Simp			goto vg_out;
93307070Simp		}
94307070Simp
95307070Simp		error = efi_var_get(name, &ev->vendor, &ev->attrib,
96307070Simp		    &ev->datasize, data);
97307070Simp
98307070Simp		if (error == 0) {
99307070Simp			error = copyout(data, ev->data, ev->datasize);
100307070Simp		} else if (error == EOVERFLOW) {
101307070Simp			/*
102307070Simp			 * Pass back the size we really need, but
103307070Simp			 * convert the error to 0 so the copyout
104307070Simp			 * happens. datasize was updated in the
105307070Simp			 * efi_var_get call.
106307070Simp			 */
107307070Simp			ev->data = NULL;
108307070Simp			error = 0;
109307070Simp		}
110307070Simpvg_out:
111307070Simp		free(data, M_TEMP);
112307070Simp		free(name, M_TEMP);
113307070Simp		break;
114307070Simp	}
115307070Simp	case EFIIOC_VAR_NEXT:
116307070Simp	{
117307070Simp		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
118307070Simp		efi_char *name;
119307070Simp
120307070Simp		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
121307070Simp		error = copyin(ev->name, name, ev->namesize);
122307070Simp		if (error)
123307070Simp			goto vn_out;
124307070Simp		/* Note: namesize is the buffer size, not the string lenght */
125307070Simp
126307070Simp		error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
127307070Simp		if (error == 0) {
128307070Simp			error = copyout(name, ev->name, ev->namesize);
129307070Simp		} else if (error == EOVERFLOW) {
130307070Simp			ev->name = NULL;
131307070Simp			error = 0;
132307070Simp		}
133307070Simp	vn_out:
134318576Skib		free(name, M_TEMP);
135307070Simp		break;
136307070Simp	}
137307070Simp	case EFIIOC_VAR_SET:
138307070Simp	{
139307070Simp		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
140307070Simp		void *data = NULL;
141307070Simp		efi_char *name;
142307070Simp
143307070Simp		/* datasize == 0 -> delete (more or less) */
144307070Simp		if (ev->datasize > 0)
145307070Simp			data = malloc(ev->datasize, M_TEMP, M_WAITOK);
146307070Simp		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
147307070Simp		if (ev->datasize) {
148307070Simp			error = copyin(ev->data, data, ev->datasize);
149307070Simp			if (error)
150307070Simp				goto vs_out;
151307070Simp		}
152307070Simp		error = copyin(ev->name, name, ev->namesize);
153307070Simp		if (error)
154307070Simp			goto vs_out;
155307070Simp		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
156307070Simp			error = EINVAL;
157307070Simp			goto vs_out;
158307070Simp		}
159307070Simp
160307070Simp		error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
161307070Simp		    data);
162307070Simpvs_out:
163318576Skib		free(data, M_TEMP);
164307070Simp		free(name, M_TEMP);
165307070Simp		break;
166307070Simp	}
167307070Simp	default:
168307070Simp		error = ENOTTY;
169307070Simp		break;
170307070Simp	}
171307070Simp
172307070Simp	return (error);
173307070Simp}
174307070Simp
175318576Skibstatic struct cdev *efidev;
176318576Skib
177318576Skibstatic int
178318576Skibefidev_modevents(module_t m, int event, void *arg __unused)
179307070Simp{
180318576Skib	struct make_dev_args mda;
181318576Skib	int error;
182307070Simp
183318576Skib	switch (event) {
184318576Skib	case MOD_LOAD:
185331021Skevans		/*
186331021Skevans		 * If we have no efi environment, then don't create the device.
187331021Skevans		 */
188331021Skevans		if (efi_rt_ok() != 0)
189331021Skevans			return (0);
190318576Skib		make_dev_args_init(&mda);
191318576Skib		mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
192318576Skib		mda.mda_devsw = &efi_cdevsw;
193318576Skib		mda.mda_uid = UID_ROOT;
194318576Skib		mda.mda_gid = GID_WHEEL;
195318576Skib		mda.mda_mode = 0700;
196318576Skib		error = make_dev_s(&mda, &efidev, "efi");
197318576Skib		return (error);
198318576Skib
199318576Skib	case MOD_UNLOAD:
200318576Skib		if (efidev != NULL)
201318576Skib			destroy_dev(efidev);
202318576Skib		efidev = NULL;
203318576Skib		return (0);
204318576Skib
205318576Skib	case MOD_SHUTDOWN:
206318576Skib		return (0);
207318576Skib
208318576Skib	default:
209318576Skib		return (EOPNOTSUPP);
210318576Skib	}
211307070Simp}
212307070Simp
213318576Skibstatic moduledata_t efidev_moddata = {
214318576Skib	.name = "efidev",
215318576Skib	.evhand = efidev_modevents,
216318576Skib	.priv = NULL,
217318576Skib};
218307070Simp
219332028SkevansDECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DRIVERS, SI_ORDER_ANY);
220318576SkibMODULE_VERSION(efidev, 1);
221318576SkibMODULE_DEPEND(efidev, efirt, 1, 1, 1);
222