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