1307070Simp/*- 2307070Simp * Copyright (c) 2016 Netflix, Inc. 3307070Simp * All rights reserved. 4307070Simp * 5307070Simp * Redistribution and use in source and binary forms, with or without 6307070Simp * modification, are permitted provided that the following conditions 7307070Simp * are met: 8307070Simp * 1. Redistributions of source code must retain the above copyright 9307070Simp * notice, this list of conditions and the following disclaimer 10307070Simp * in this position and unchanged. 11307070Simp * 2. Redistributions in binary form must reproduce the above copyright 12307070Simp * notice, this list of conditions and the following disclaimer in the 13307070Simp * documentation and/or other materials provided with the distribution. 14307070Simp * 15307070Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16307070Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17307070Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18307070Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19307070Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20307070Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21307070Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22307070Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23307070Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24307070Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25307070Simp */ 26307070Simp 27307070Simp#include <sys/cdefs.h> 28307070Simp__FBSDID("$FreeBSD: stable/11/sys/dev/efidev/efidev.c 332028 2018-04-04 13:58:18Z kevans $"); 29307070Simp 30307070Simp#include <sys/param.h> 31307070Simp#include <sys/systm.h> 32307070Simp#include <sys/kernel.h> 33307070Simp#include <sys/bus.h> 34307070Simp#include <sys/conf.h> 35307070Simp#include <sys/lock.h> 36307070Simp#include <sys/malloc.h> 37307070Simp#include <sys/module.h> 38307070Simp 39307070Simp#include <machine/efi.h> 40307070Simp#include <sys/efiio.h> 41307070Simp 42307070Simpstatic d_ioctl_t efidev_ioctl; 43307070Simp 44307070Simpstatic struct cdevsw efi_cdevsw = { 45307070Simp .d_name = "efi", 46307070Simp .d_version = D_VERSION, 47307070Simp .d_ioctl = efidev_ioctl, 48307070Simp}; 49307070Simp 50307070Simpstatic int 51307070Simpefidev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr, 52307070Simp int flags __unused, struct thread *td __unused) 53307070Simp{ 54307070Simp int error; 55307070Simp 56307070Simp switch (cmd) { 57307070Simp case EFIIOC_GET_TABLE: 58307070Simp { 59307070Simp struct efi_get_table_ioc *egtioc = 60307070Simp (struct efi_get_table_ioc *)addr; 61307070Simp 62307070Simp error = efi_get_table(&egtioc->uuid, &egtioc->ptr); 63307070Simp break; 64307070Simp } 65307070Simp case EFIIOC_GET_TIME: 66307070Simp { 67307070Simp struct efi_tm *tm = (struct efi_tm *)addr; 68307070Simp 69307070Simp error = efi_get_time(tm); 70307070Simp break; 71307070Simp } 72307070Simp case EFIIOC_SET_TIME: 73307070Simp { 74307070Simp struct efi_tm *tm = (struct efi_tm *)addr; 75307070Simp 76307070Simp error = efi_set_time(tm); 77307070Simp break; 78307070Simp } 79307070Simp case EFIIOC_VAR_GET: 80307070Simp { 81307070Simp struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 82307070Simp void *data; 83307070Simp efi_char *name; 84307070Simp 85307070Simp data = malloc(ev->datasize, M_TEMP, M_WAITOK); 86307070Simp name = malloc(ev->namesize, M_TEMP, M_WAITOK); 87307070Simp error = copyin(ev->name, name, ev->namesize); 88307070Simp if (error) 89307070Simp goto vg_out; 90307070Simp if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 91307070Simp error = EINVAL; 92307070Simp goto vg_out; 93307070Simp } 94307070Simp 95307070Simp error = efi_var_get(name, &ev->vendor, &ev->attrib, 96307070Simp &ev->datasize, data); 97307070Simp 98307070Simp if (error == 0) { 99307070Simp error = copyout(data, ev->data, ev->datasize); 100307070Simp } else if (error == EOVERFLOW) { 101307070Simp /* 102307070Simp * Pass back the size we really need, but 103307070Simp * convert the error to 0 so the copyout 104307070Simp * happens. datasize was updated in the 105307070Simp * efi_var_get call. 106307070Simp */ 107307070Simp ev->data = NULL; 108307070Simp error = 0; 109307070Simp } 110307070Simpvg_out: 111307070Simp free(data, M_TEMP); 112307070Simp free(name, M_TEMP); 113307070Simp break; 114307070Simp } 115307070Simp case EFIIOC_VAR_NEXT: 116307070Simp { 117307070Simp struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 118307070Simp efi_char *name; 119307070Simp 120307070Simp name = malloc(ev->namesize, M_TEMP, M_WAITOK); 121307070Simp error = copyin(ev->name, name, ev->namesize); 122307070Simp if (error) 123307070Simp goto vn_out; 124307070Simp /* Note: namesize is the buffer size, not the string lenght */ 125307070Simp 126307070Simp error = efi_var_nextname(&ev->namesize, name, &ev->vendor); 127307070Simp if (error == 0) { 128307070Simp error = copyout(name, ev->name, ev->namesize); 129307070Simp } else if (error == EOVERFLOW) { 130307070Simp ev->name = NULL; 131307070Simp error = 0; 132307070Simp } 133307070Simp vn_out: 134318576Skib free(name, M_TEMP); 135307070Simp break; 136307070Simp } 137307070Simp case EFIIOC_VAR_SET: 138307070Simp { 139307070Simp struct efi_var_ioc *ev = (struct efi_var_ioc *)addr; 140307070Simp void *data = NULL; 141307070Simp efi_char *name; 142307070Simp 143307070Simp /* datasize == 0 -> delete (more or less) */ 144307070Simp if (ev->datasize > 0) 145307070Simp data = malloc(ev->datasize, M_TEMP, M_WAITOK); 146307070Simp name = malloc(ev->namesize, M_TEMP, M_WAITOK); 147307070Simp if (ev->datasize) { 148307070Simp error = copyin(ev->data, data, ev->datasize); 149307070Simp if (error) 150307070Simp goto vs_out; 151307070Simp } 152307070Simp error = copyin(ev->name, name, ev->namesize); 153307070Simp if (error) 154307070Simp goto vs_out; 155307070Simp if (name[ev->namesize / sizeof(efi_char) - 1] != 0) { 156307070Simp error = EINVAL; 157307070Simp goto vs_out; 158307070Simp } 159307070Simp 160307070Simp error = efi_var_set(name, &ev->vendor, ev->attrib, ev->datasize, 161307070Simp data); 162307070Simpvs_out: 163318576Skib free(data, M_TEMP); 164307070Simp free(name, M_TEMP); 165307070Simp break; 166307070Simp } 167307070Simp default: 168307070Simp error = ENOTTY; 169307070Simp break; 170307070Simp } 171307070Simp 172307070Simp return (error); 173307070Simp} 174307070Simp 175318576Skibstatic struct cdev *efidev; 176318576Skib 177318576Skibstatic int 178318576Skibefidev_modevents(module_t m, int event, void *arg __unused) 179307070Simp{ 180318576Skib struct make_dev_args mda; 181318576Skib int error; 182307070Simp 183318576Skib switch (event) { 184318576Skib case MOD_LOAD: 185331021Skevans /* 186331021Skevans * If we have no efi environment, then don't create the device. 187331021Skevans */ 188331021Skevans if (efi_rt_ok() != 0) 189331021Skevans return (0); 190318576Skib make_dev_args_init(&mda); 191318576Skib mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 192318576Skib mda.mda_devsw = &efi_cdevsw; 193318576Skib mda.mda_uid = UID_ROOT; 194318576Skib mda.mda_gid = GID_WHEEL; 195318576Skib mda.mda_mode = 0700; 196318576Skib error = make_dev_s(&mda, &efidev, "efi"); 197318576Skib return (error); 198318576Skib 199318576Skib case MOD_UNLOAD: 200318576Skib if (efidev != NULL) 201318576Skib destroy_dev(efidev); 202318576Skib efidev = NULL; 203318576Skib return (0); 204318576Skib 205318576Skib case MOD_SHUTDOWN: 206318576Skib return (0); 207318576Skib 208318576Skib default: 209318576Skib return (EOPNOTSUPP); 210318576Skib } 211307070Simp} 212307070Simp 213318576Skibstatic moduledata_t efidev_moddata = { 214318576Skib .name = "efidev", 215318576Skib .evhand = efidev_modevents, 216318576Skib .priv = NULL, 217318576Skib}; 218307070Simp 219332028SkevansDECLARE_MODULE(efidev, efidev_moddata, SI_SUB_DRIVERS, SI_ORDER_ANY); 220318576SkibMODULE_VERSION(efidev, 1); 221318576SkibMODULE_DEPEND(efidev, efirt, 1, 1, 1); 222