xref: /freebsd/sys/dev/efidev/efidev.c (revision f79d484dff6cb0e8249bb489572727bab7ecbcf1)
1*f79d484dSWarner Losh /*-
2*f79d484dSWarner Losh  * Copyright (c) 2016 Netflix, Inc.
3*f79d484dSWarner Losh  * All rights reserved.
4*f79d484dSWarner Losh  *
5*f79d484dSWarner Losh  * Redistribution and use in source and binary forms, with or without
6*f79d484dSWarner Losh  * modification, are permitted provided that the following conditions
7*f79d484dSWarner Losh  * are met:
8*f79d484dSWarner Losh  * 1. Redistributions of source code must retain the above copyright
9*f79d484dSWarner Losh  *    notice, this list of conditions and the following disclaimer
10*f79d484dSWarner Losh  *    in this position and unchanged.
11*f79d484dSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
12*f79d484dSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
13*f79d484dSWarner Losh  *    documentation and/or other materials provided with the distribution.
14*f79d484dSWarner Losh  *
15*f79d484dSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*f79d484dSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*f79d484dSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*f79d484dSWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*f79d484dSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*f79d484dSWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*f79d484dSWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*f79d484dSWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*f79d484dSWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*f79d484dSWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*f79d484dSWarner Losh  */
26*f79d484dSWarner Losh 
27*f79d484dSWarner Losh #include <sys/cdefs.h>
28*f79d484dSWarner Losh __FBSDID("$FreeBSD$");
29*f79d484dSWarner Losh 
30*f79d484dSWarner Losh #include <sys/param.h>
31*f79d484dSWarner Losh #include <sys/systm.h>
32*f79d484dSWarner Losh #include <sys/kernel.h>
33*f79d484dSWarner Losh #include <sys/bus.h>
34*f79d484dSWarner Losh #include <sys/conf.h>
35*f79d484dSWarner Losh #include <sys/lock.h>
36*f79d484dSWarner Losh #include <sys/malloc.h>
37*f79d484dSWarner Losh #include <sys/module.h>
38*f79d484dSWarner Losh 
39*f79d484dSWarner Losh #include <machine/efi.h>
40*f79d484dSWarner Losh #include <sys/efiio.h>
41*f79d484dSWarner Losh 
42*f79d484dSWarner Losh static d_ioctl_t efidev_ioctl;
43*f79d484dSWarner Losh 
44*f79d484dSWarner Losh static struct cdevsw efi_cdevsw = {
45*f79d484dSWarner Losh 	.d_name = "efi",
46*f79d484dSWarner Losh 	.d_version = D_VERSION,
47*f79d484dSWarner Losh 	.d_ioctl = efidev_ioctl,
48*f79d484dSWarner Losh };
49*f79d484dSWarner Losh 
50*f79d484dSWarner Losh /* ARGSUSED */
51*f79d484dSWarner Losh static int
52*f79d484dSWarner Losh efidev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr,
53*f79d484dSWarner Losh     int flags __unused, struct thread *td __unused)
54*f79d484dSWarner Losh {
55*f79d484dSWarner Losh 	int error;
56*f79d484dSWarner Losh 
57*f79d484dSWarner Losh 	switch (cmd) {
58*f79d484dSWarner Losh 	case EFIIOC_GET_TABLE:
59*f79d484dSWarner Losh 	{
60*f79d484dSWarner Losh 		struct efi_get_table_ioc *egtioc =
61*f79d484dSWarner Losh 		    (struct efi_get_table_ioc *)addr;
62*f79d484dSWarner Losh 
63*f79d484dSWarner Losh 		error = efi_get_table(&egtioc->uuid, &egtioc->ptr);
64*f79d484dSWarner Losh 		break;
65*f79d484dSWarner Losh 	}
66*f79d484dSWarner Losh 	case EFIIOC_GET_TIME:
67*f79d484dSWarner Losh 	{
68*f79d484dSWarner Losh 		struct efi_tm *tm = (struct efi_tm *)addr;
69*f79d484dSWarner Losh 
70*f79d484dSWarner Losh 		error = efi_get_time(tm);
71*f79d484dSWarner Losh 		break;
72*f79d484dSWarner Losh 	}
73*f79d484dSWarner Losh 	case EFIIOC_SET_TIME:
74*f79d484dSWarner Losh 	{
75*f79d484dSWarner Losh 		struct efi_tm *tm = (struct efi_tm *)addr;
76*f79d484dSWarner Losh 
77*f79d484dSWarner Losh 		error = efi_set_time(tm);
78*f79d484dSWarner Losh 		break;
79*f79d484dSWarner Losh 	}
80*f79d484dSWarner Losh 	case EFIIOC_VAR_GET:
81*f79d484dSWarner Losh 	{
82*f79d484dSWarner Losh 		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
83*f79d484dSWarner Losh 		void *data;
84*f79d484dSWarner Losh 		efi_char *name;
85*f79d484dSWarner Losh 
86*f79d484dSWarner Losh 		data = malloc(ev->datasize, M_TEMP, M_WAITOK);
87*f79d484dSWarner Losh 		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
88*f79d484dSWarner Losh 		error = copyin(ev->name, name, ev->namesize);
89*f79d484dSWarner Losh 		if (error)
90*f79d484dSWarner Losh 			goto vg_out;
91*f79d484dSWarner Losh 		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
92*f79d484dSWarner Losh 			error = EINVAL;
93*f79d484dSWarner Losh 			goto vg_out;
94*f79d484dSWarner Losh 		}
95*f79d484dSWarner Losh 
96*f79d484dSWarner Losh 		error = efi_var_get(name, &ev->vendor, &ev->attrib,
97*f79d484dSWarner Losh 		    &ev->datasize, data);
98*f79d484dSWarner Losh 
99*f79d484dSWarner Losh 		if (error == 0) {
100*f79d484dSWarner Losh 			error = copyout(data, ev->data, ev->datasize);
101*f79d484dSWarner Losh 		} else if (error == EOVERFLOW) {
102*f79d484dSWarner Losh 			/*
103*f79d484dSWarner Losh 			 * Pass back the size we really need, but
104*f79d484dSWarner Losh 			 * convert the error to 0 so the copyout
105*f79d484dSWarner Losh 			 * happens. datasize was updated in the
106*f79d484dSWarner Losh 			 * efi_var_get call.
107*f79d484dSWarner Losh 			 */
108*f79d484dSWarner Losh 			ev->data = NULL;
109*f79d484dSWarner Losh 			error = 0;
110*f79d484dSWarner Losh 		}
111*f79d484dSWarner Losh vg_out:
112*f79d484dSWarner Losh 		free(data, M_TEMP);
113*f79d484dSWarner Losh 		free(name, M_TEMP);
114*f79d484dSWarner Losh 		break;
115*f79d484dSWarner Losh 	}
116*f79d484dSWarner Losh 	case EFIIOC_VAR_NEXT:
117*f79d484dSWarner Losh 	{
118*f79d484dSWarner Losh 		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
119*f79d484dSWarner Losh 		efi_char *name;
120*f79d484dSWarner Losh 
121*f79d484dSWarner Losh 		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
122*f79d484dSWarner Losh 		if (name == NULL) {
123*f79d484dSWarner Losh 			error = ENOMEM;
124*f79d484dSWarner Losh 			goto vn_out;
125*f79d484dSWarner Losh 		}
126*f79d484dSWarner Losh 		error = copyin(ev->name, name, ev->namesize);
127*f79d484dSWarner Losh 		if (error)
128*f79d484dSWarner Losh 			goto vn_out;
129*f79d484dSWarner Losh 		/* Note: namesize is the buffer size, not the string lenght */
130*f79d484dSWarner Losh 
131*f79d484dSWarner Losh 		error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
132*f79d484dSWarner Losh 		if (error == 0) {
133*f79d484dSWarner Losh 			error = copyout(name, ev->name, ev->namesize);
134*f79d484dSWarner Losh 		} else if (error == EOVERFLOW) {
135*f79d484dSWarner Losh 			ev->name = NULL;
136*f79d484dSWarner Losh 			error = 0;
137*f79d484dSWarner Losh 		}
138*f79d484dSWarner Losh 	vn_out:
139*f79d484dSWarner Losh 		if (name != NULL)
140*f79d484dSWarner Losh 			free(name, M_TEMP);
141*f79d484dSWarner Losh 		break;
142*f79d484dSWarner Losh 	}
143*f79d484dSWarner Losh 	case EFIIOC_VAR_SET:
144*f79d484dSWarner Losh 	{
145*f79d484dSWarner Losh 		struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
146*f79d484dSWarner Losh 		void *data = NULL;
147*f79d484dSWarner Losh 		efi_char *name;
148*f79d484dSWarner Losh 
149*f79d484dSWarner Losh 		/* datasize == 0 -> delete (more or less) */
150*f79d484dSWarner Losh 		if (ev->datasize > 0)
151*f79d484dSWarner Losh 			data = malloc(ev->datasize, M_TEMP, M_WAITOK);
152*f79d484dSWarner Losh 		name = malloc(ev->namesize, M_TEMP, M_WAITOK);
153*f79d484dSWarner Losh 		if (ev->datasize) {
154*f79d484dSWarner Losh 			error = copyin(ev->data, data, ev->datasize);
155*f79d484dSWarner Losh 			if (error)
156*f79d484dSWarner Losh 				goto vs_out;
157*f79d484dSWarner Losh 		}
158*f79d484dSWarner Losh 		error = copyin(ev->name, name, ev->namesize);
159*f79d484dSWarner Losh 		if (error)
160*f79d484dSWarner Losh 			goto vs_out;
161*f79d484dSWarner Losh 		if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
162*f79d484dSWarner Losh 			error = EINVAL;
163*f79d484dSWarner Losh 			goto vs_out;
164*f79d484dSWarner Losh 		}
165*f79d484dSWarner Losh 
166*f79d484dSWarner Losh 		error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
167*f79d484dSWarner Losh 		    data);
168*f79d484dSWarner Losh vs_out:
169*f79d484dSWarner Losh 		if (data != NULL)
170*f79d484dSWarner Losh 			free(data, M_TEMP);
171*f79d484dSWarner Losh 		free(name, M_TEMP);
172*f79d484dSWarner Losh 		break;
173*f79d484dSWarner Losh 	}
174*f79d484dSWarner Losh 	default:
175*f79d484dSWarner Losh 		error = ENOTTY;
176*f79d484dSWarner Losh 		break;
177*f79d484dSWarner Losh 	}
178*f79d484dSWarner Losh 
179*f79d484dSWarner Losh 	return (error);
180*f79d484dSWarner Losh }
181*f79d484dSWarner Losh 
182*f79d484dSWarner Losh int
183*f79d484dSWarner Losh efidev_init(struct cdev **cdev)
184*f79d484dSWarner Losh {
185*f79d484dSWarner Losh 
186*f79d484dSWarner Losh 	*cdev = make_dev(&efi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0700,
187*f79d484dSWarner Losh 	    "efidev");
188*f79d484dSWarner Losh 
189*f79d484dSWarner Losh 	return (0);
190*f79d484dSWarner Losh }
191*f79d484dSWarner Losh 
192*f79d484dSWarner Losh int
193*f79d484dSWarner Losh efidev_uninit(struct cdev *cdev)
194*f79d484dSWarner Losh {
195*f79d484dSWarner Losh 
196*f79d484dSWarner Losh 	destroy_dev(cdev);
197*f79d484dSWarner Losh 
198*f79d484dSWarner Losh 	return (0);
199*f79d484dSWarner Losh }
200