]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/efidev/efidev.c
usb_audio: Fix a typo in a source code comment
[FreeBSD/FreeBSD.git] / sys / dev / efidev / efidev.c
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
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                 void *buf = NULL;
61
62                 error = efi_copy_table(&egtioc->uuid, egtioc->buf ? &buf : NULL,
63                     egtioc->buf_len, &egtioc->table_len);
64
65                 if (error != 0 || egtioc->buf == NULL)
66                         break;
67
68                 if (egtioc->buf_len < egtioc->table_len) {
69                         error = EINVAL;
70                         free(buf, M_TEMP);
71                         break;
72                 }
73
74                 error = copyout(buf, egtioc->buf, egtioc->buf_len);
75                 free(buf, M_TEMP);
76
77                 break;
78         }
79         case EFIIOC_GET_TIME:
80         {
81                 struct efi_tm *tm = (struct efi_tm *)addr;
82
83                 error = efi_get_time(tm);
84                 break;
85         }
86         case EFIIOC_SET_TIME:
87         {
88                 struct efi_tm *tm = (struct efi_tm *)addr;
89
90                 error = efi_set_time(tm);
91                 break;
92         }
93         case EFIIOC_VAR_GET:
94         {
95                 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
96                 void *data;
97                 efi_char *name;
98
99                 data = malloc(ev->datasize, M_TEMP, M_WAITOK);
100                 name = malloc(ev->namesize, M_TEMP, M_WAITOK);
101                 error = copyin(ev->name, name, ev->namesize);
102                 if (error)
103                         goto vg_out;
104                 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
105                         error = EINVAL;
106                         goto vg_out;
107                 }
108
109                 error = efi_var_get(name, &ev->vendor, &ev->attrib,
110                     &ev->datasize, data);
111
112                 if (error == 0) {
113                         error = copyout(data, ev->data, ev->datasize);
114                 } else if (error == EOVERFLOW) {
115                         /*
116                          * Pass back the size we really need, but
117                          * convert the error to 0 so the copyout
118                          * happens. datasize was updated in the
119                          * efi_var_get call.
120                          */
121                         ev->data = NULL;
122                         error = 0;
123                 }
124 vg_out:
125                 free(data, M_TEMP);
126                 free(name, M_TEMP);
127                 break;
128         }
129         case EFIIOC_VAR_NEXT:
130         {
131                 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
132                 efi_char *name;
133
134                 name = malloc(ev->namesize, M_TEMP, M_WAITOK);
135                 error = copyin(ev->name, name, ev->namesize);
136                 if (error)
137                         goto vn_out;
138                 /* Note: namesize is the buffer size, not the string lenght */
139
140                 error = efi_var_nextname(&ev->namesize, name, &ev->vendor);
141                 if (error == 0) {
142                         error = copyout(name, ev->name, ev->namesize);
143                 } else if (error == EOVERFLOW) {
144                         ev->name = NULL;
145                         error = 0;
146                 }
147         vn_out:
148                 free(name, M_TEMP);
149                 break;
150         }
151         case EFIIOC_VAR_SET:
152         {
153                 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr;
154                 void *data = NULL;
155                 efi_char *name;
156
157                 /* datasize == 0 -> delete (more or less) */
158                 if (ev->datasize > 0)
159                         data = malloc(ev->datasize, M_TEMP, M_WAITOK);
160                 name = malloc(ev->namesize, M_TEMP, M_WAITOK);
161                 if (ev->datasize) {
162                         error = copyin(ev->data, data, ev->datasize);
163                         if (error)
164                                 goto vs_out;
165                 }
166                 error = copyin(ev->name, name, ev->namesize);
167                 if (error)
168                         goto vs_out;
169                 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) {
170                         error = EINVAL;
171                         goto vs_out;
172                 }
173
174                 error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize,
175                     data);
176 vs_out:
177                 free(data, M_TEMP);
178                 free(name, M_TEMP);
179                 break;
180         }
181         default:
182                 error = ENOTTY;
183                 break;
184         }
185
186         return (error);
187 }
188
189 static struct cdev *efidev;
190
191 static int
192 efidev_modevents(module_t m, int event, void *arg __unused)
193 {
194         struct make_dev_args mda;
195         int error;
196
197         switch (event) {
198         case MOD_LOAD:
199                 /*
200                  * If we have no efi environment, then don't create the device.
201                  */
202                 if (efi_rt_ok() != 0)
203                         return (0);
204                 make_dev_args_init(&mda);
205                 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
206                 mda.mda_devsw = &efi_cdevsw;
207                 mda.mda_uid = UID_ROOT;
208                 mda.mda_gid = GID_WHEEL;
209                 mda.mda_mode = 0700;
210                 error = make_dev_s(&mda, &efidev, "efi");
211                 return (error);
212
213         case MOD_UNLOAD:
214                 if (efidev != NULL)
215                         destroy_dev(efidev);
216                 efidev = NULL;
217                 return (0);
218
219         case MOD_SHUTDOWN:
220                 return (0);
221
222         default:
223                 return (EOPNOTSUPP);
224         }
225 }
226
227 static moduledata_t efidev_moddata = {
228         .name = "efidev",
229         .evhand = efidev_modevents,
230         .priv = NULL,
231 };
232
233 DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DRIVERS, SI_ORDER_ANY);
234 MODULE_VERSION(efidev, 1);
235 MODULE_DEPEND(efidev, efirt, 1, 1, 1);