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_GET_WAKETIME: 94 { 95 struct efi_waketime_ioc *wt = (struct efi_waketime_ioc *)addr; 96 97 error = efi_get_waketime(&wt->enabled, &wt->pending, 98 &wt->waketime); 99 break; 100 } 101 case EFIIOC_SET_WAKETIME: 102 { 103 struct efi_waketime_ioc *wt = (struct efi_waketime_ioc *)addr; 104 105 error = efi_set_waketime(wt->enabled, &wt->waketime); 106 break; 107 } 108 case EFIIOC_VAR_GET: 109 { 110 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 111 void *data; 112 efi_char *name; 113 114 data = malloc(ev->datasize, M_TEMP, M_WAITOK); 115 name = malloc(ev->namesize, M_TEMP, M_WAITOK); 116 error = copyin(ev->name, name, ev->namesize); 117 if (error) 118 goto vg_out; 119 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 120 error = EINVAL; 121 goto vg_out; 122 } 123 124 error = efi_var_get(name, &ev->vendor, &ev->attrib, 125 &ev->datasize, data); 126 127 if (error == 0) { 128 error = copyout(data, ev->data, ev->datasize); 129 } else if (error == EOVERFLOW) { 130 /* 131 * Pass back the size we really need, but 132 * convert the error to 0 so the copyout 133 * happens. datasize was updated in the 134 * efi_var_get call. 135 */ 136 ev->data = NULL; 137 error = 0; 138 } 139 vg_out: 140 free(data, M_TEMP); 141 free(name, M_TEMP); 142 break; 143 } 144 case EFIIOC_VAR_NEXT: 145 { 146 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 147 efi_char *name; 148 149 name = malloc(ev->namesize, M_TEMP, M_WAITOK); 150 error = copyin(ev->name, name, ev->namesize); 151 if (error) 152 goto vn_out; 153 /* Note: namesize is the buffer size, not the string lenght */ 154 155 error = efi_var_nextname(&ev->namesize, name, &ev->vendor); 156 if (error == 0) { 157 error = copyout(name, ev->name, ev->namesize); 158 } else if (error == EOVERFLOW) { 159 ev->name = NULL; 160 error = 0; 161 } 162 vn_out: 163 free(name, M_TEMP); 164 break; 165 } 166 case EFIIOC_VAR_SET: 167 { 168 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 169 void *data = NULL; 170 efi_char *name; 171 172 /* datasize == 0 -> delete (more or less) */ 173 if (ev->datasize > 0) 174 data = malloc(ev->datasize, M_TEMP, M_WAITOK); 175 name = malloc(ev->namesize, M_TEMP, M_WAITOK); 176 if (ev->datasize) { 177 error = copyin(ev->data, data, ev->datasize); 178 if (error) 179 goto vs_out; 180 } 181 error = copyin(ev->name, name, ev->namesize); 182 if (error) 183 goto vs_out; 184 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 185 error = EINVAL; 186 goto vs_out; 187 } 188 189 error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize, 190 data); 191 vs_out: 192 free(data, M_TEMP); 193 free(name, M_TEMP); 194 break; 195 } 196 default: 197 error = ENOTTY; 198 break; 199 } 200 201 return (error); 202 } 203 204 static struct cdev *efidev; 205 206 static int 207 efidev_modevents(module_t m, int event, void *arg __unused) 208 { 209 struct make_dev_args mda; 210 int error; 211 212 switch (event) { 213 case MOD_LOAD: 214 /* 215 * If we have no efi environment, then don't create the device. 216 */ 217 if (efi_rt_ok() != 0) 218 return (0); 219 make_dev_args_init(&mda); 220 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 221 mda.mda_devsw = &efi_cdevsw; 222 mda.mda_uid = UID_ROOT; 223 mda.mda_gid = GID_WHEEL; 224 mda.mda_mode = 0700; 225 error = make_dev_s(&mda, &efidev, "efi"); 226 return (error); 227 228 case MOD_UNLOAD: 229 if (efidev != NULL) 230 destroy_dev(efidev); 231 efidev = NULL; 232 return (0); 233 234 case MOD_SHUTDOWN: 235 return (0); 236 237 default: 238 return (EOPNOTSUPP); 239 } 240 } 241 242 static moduledata_t efidev_moddata = { 243 .name = "efidev", 244 .evhand = efidev_modevents, 245 .priv = NULL, 246 }; 247 248 DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DRIVERS, SI_ORDER_ANY); 249 MODULE_VERSION(efidev, 1); 250 MODULE_DEPEND(efidev, efirt, 1, 1, 1); 251