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