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 d_ioctl_t efidev_ioctl; 43 static d_open_t efidev_open; 44 static d_close_t efidev_close; 45 46 static struct dev_ops efi_ops = { 47 { "efi", 0, 0 }, 48 .d_open = efidev_open, 49 .d_close = efidev_close, 50 .d_ioctl = efidev_ioctl, 51 }; 52 53 static int 54 efidev_open(struct dev_open_args *ap) 55 { 56 return 0; 57 } 58 59 static int 60 efidev_close(struct dev_close_args *ap) 61 { 62 return 0; 63 } 64 65 static int 66 efidev_ioctl(struct dev_ioctl_args *ap) 67 { 68 u_long cmd = ap->a_cmd; 69 caddr_t addr = ap->a_data; 70 int error; 71 72 switch (cmd) { 73 case EFIIOC_GET_TABLE: 74 { 75 struct efi_get_table_ioc *egtioc = 76 (struct efi_get_table_ioc *)addr; 77 78 error = efi_get_table(&egtioc->uuid, &egtioc->ptr); 79 break; 80 } 81 case EFIIOC_GET_TIME: 82 { 83 struct efi_tm *tm = (struct efi_tm *)addr; 84 85 error = efi_get_time(tm); 86 break; 87 } 88 case EFIIOC_SET_TIME: 89 { 90 struct efi_tm *tm = (struct efi_tm *)addr; 91 92 error = efi_set_time(tm); 93 break; 94 } 95 case EFIIOC_VAR_GET: 96 { 97 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 98 void *data; 99 efi_char *name; 100 101 data = kmalloc(ev->datasize, M_TEMP, M_WAITOK); 102 name = kmalloc(ev->namesize, M_TEMP, M_WAITOK); 103 error = copyin(ev->name, name, ev->namesize); 104 if (error) 105 goto vg_out; 106 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 107 error = EINVAL; 108 goto vg_out; 109 } 110 111 error = efi_var_get(name, &ev->vendor, &ev->attrib, 112 &ev->datasize, data); 113 114 if (error == 0) { 115 error = copyout(data, ev->data, ev->datasize); 116 } else if (error == EOVERFLOW) { 117 /* 118 * Pass back the size we really need, but 119 * convert the error to 0 so the copyout 120 * happens. datasize was updated in the 121 * efi_var_get call. 122 */ 123 ev->data = NULL; 124 error = 0; 125 } 126 vg_out: 127 kfree(data, M_TEMP); 128 kfree(name, M_TEMP); 129 break; 130 } 131 case EFIIOC_VAR_NEXT: 132 { 133 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 134 efi_char *name; 135 136 name = kmalloc(ev->namesize, M_TEMP, M_WAITOK); 137 error = copyin(ev->name, name, ev->namesize); 138 if (error) 139 goto vn_out; 140 /* Note: namesize is the buffer size, not the string lenght */ 141 142 error = efi_var_nextname(&ev->namesize, name, &ev->vendor); 143 if (error == 0) { 144 error = copyout(name, ev->name, ev->namesize); 145 } else if (error == EOVERFLOW) { 146 ev->name = NULL; 147 error = 0; 148 } 149 vn_out: 150 kfree(name, M_TEMP); 151 break; 152 } 153 case EFIIOC_VAR_SET: 154 { 155 struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 156 void *data = NULL; 157 efi_char *name; 158 159 /* datasize == 0 -> delete (more or less) */ 160 if (ev->datasize > 0) 161 data = kmalloc(ev->datasize, M_TEMP, M_WAITOK); 162 name = kmalloc(ev->namesize, M_TEMP, M_WAITOK); 163 if (ev->datasize) { 164 error = copyin(ev->data, data, ev->datasize); 165 if (error) 166 goto vs_out; 167 } 168 error = copyin(ev->name, name, ev->namesize); 169 if (error) 170 goto vs_out; 171 if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 172 error = EINVAL; 173 goto vs_out; 174 } 175 176 error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize, 177 data); 178 vs_out: 179 kfree(data, M_TEMP); 180 kfree(name, M_TEMP); 181 break; 182 } 183 default: 184 error = ENOTTY; 185 break; 186 } 187 188 return (error); 189 } 190 191 static struct cdev *efidev; 192 193 static int 194 efidev_modevents(module_t m, int event, void *arg __unused) 195 { 196 switch (event) { 197 case MOD_LOAD: 198 efidev = make_dev(&efi_ops, 0, UID_ROOT, GID_WHEEL, 199 0700, "efi"); 200 return (0); 201 202 case MOD_UNLOAD: 203 if (efidev != NULL) 204 destroy_dev(efidev); 205 efidev = NULL; 206 return (0); 207 208 case MOD_SHUTDOWN: 209 return (0); 210 211 default: 212 return (EOPNOTSUPP); 213 } 214 } 215 216 static moduledata_t efidev_moddata = { 217 .name = "efidev", 218 .evhand = efidev_modevents, 219 .priv = NULL, 220 }; 221 222 DECLARE_MODULE(efidev, efidev_moddata, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY); 223 MODULE_VERSION(efidev, 1); 224 MODULE_DEPEND(efidev, efirt, 1, 1, 1); 225