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 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 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