iodev_machdep.c revision 270296
1/*- 2 * Copyright (c) 2010 Marcel Moolenaar 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 * 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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/10/sys/ia64/ia64/iodev_machdep.c 270296 2014-08-21 19:51:07Z emaste $"); 29 30#include <sys/param.h> 31#include <sys/conf.h> 32#include <sys/efi.h> 33#include <sys/fcntl.h> 34#include <sys/ioccom.h> 35#include <sys/malloc.h> 36#include <sys/priv.h> 37#include <sys/proc.h> 38#include <sys/systm.h> 39 40#include <machine/bus.h> 41#include <machine/iodev.h> 42 43static int iodev_efivar_getvar(struct iodev_efivar_req *req); 44static int iodev_efivar_nextname(struct iodev_efivar_req *req); 45static int iodev_efivar_setvar(struct iodev_efivar_req *req); 46 47/* ARGSUSED */ 48int 49iodev_open(struct thread *td __unused) 50{ 51 52 return (0); 53} 54 55/* ARGSUSED */ 56int 57iodev_close(struct thread *td __unused) 58{ 59 60 return (0); 61} 62 63int 64iodev_ioctl(u_long cmd, caddr_t data) 65{ 66 struct iodev_efivar_req *efivar_req; 67 int error; 68 69 switch (cmd) { 70 case IODEV_EFIVAR: 71 efivar_req = (struct iodev_efivar_req *)data; 72 efivar_req->result = 0; /* So it's well-defined */ 73 switch (efivar_req->access) { 74 case IODEV_EFIVAR_GETVAR: 75 error = iodev_efivar_getvar(efivar_req); 76 break; 77 case IODEV_EFIVAR_NEXTNAME: 78 error = iodev_efivar_nextname(efivar_req); 79 break; 80 case IODEV_EFIVAR_SETVAR: 81 error = iodev_efivar_setvar(efivar_req); 82 break; 83 default: 84 error = EINVAL; 85 break; 86 } 87 break; 88 default: 89 error = ENOIOCTL; 90 } 91 92 return (error); 93} 94 95static int 96iodev_efivar_getvar(struct iodev_efivar_req *req) 97{ 98 void *data; 99 efi_char *name; 100 int error; 101 102 if ((req->namesize & 1) != 0 || req->namesize < 4) 103 return (EINVAL); 104 if (req->datasize == 0) 105 return (EINVAL); 106 107 /* 108 * Pre-zero the allocated memory and don't copy the last 2 bytes 109 * of the name. That should be the closing nul character (ucs-2) 110 * and if not, then we ensured a nul-terminating string. This is 111 * to protect the firmware and thus ourselves. 112 */ 113 name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); 114 error = copyin(req->name, name, req->namesize - 2); 115 if (error) { 116 free(name, M_TEMP); 117 return (error); 118 } 119 120 data = malloc(req->datasize, M_TEMP, M_WAITOK); 121 error = efi_var_get(name, &req->vendor, &req->attrib, &req->datasize, 122 data); 123 if (error == EOVERFLOW || error == ENOENT) { 124 req->result = error; 125 error = 0; 126 } 127 if (!error && !req->result) 128 error = copyout(data, req->data, req->datasize); 129 130 free(data, M_TEMP); 131 free(name, M_TEMP); 132 return (error); 133} 134 135static int 136iodev_efivar_nextname(struct iodev_efivar_req *req) 137{ 138 efi_char *name; 139 int error; 140 141 /* Enforce a reasonable minimum size of the name buffer. */ 142 if (req->namesize < 4) 143 return (EINVAL); 144 145 name = malloc(req->namesize, M_TEMP, M_WAITOK); 146 error = copyin(req->name, name, req->namesize); 147 if (error) { 148 free(name, M_TEMP); 149 return (error); 150 } 151 152 error = efi_var_nextname(&req->namesize, name, &req->vendor); 153 if (error == EOVERFLOW || error == ENOENT) { 154 req->result = error; 155 error = 0; 156 } 157 if (!error && !req->result) 158 error = copyout(name, req->name, req->namesize); 159 160 free(name, M_TEMP); 161 return (error); 162} 163 164static int 165iodev_efivar_setvar(struct iodev_efivar_req *req) 166{ 167 void *data; 168 efi_char *name; 169 int error; 170 171 if ((req->namesize & 1) != 0 || req->namesize < 4) 172 return (EINVAL); 173 174 /* 175 * Pre-zero the allocated memory and don't copy the last 2 bytes 176 * of the name. That should be the closing nul character (ucs-2) 177 * and if not, then we ensured a nul-terminating string. This is 178 * to protect the firmware and thus ourselves. 179 */ 180 name = malloc(req->namesize, M_TEMP, M_WAITOK | M_ZERO); 181 error = copyin(req->name, name, req->namesize - 2); 182 if (error) { 183 free(name, M_TEMP); 184 return (error); 185 } 186 187 if (req->datasize) { 188 data = malloc(req->datasize, M_TEMP, M_WAITOK); 189 error = copyin(req->data, data, req->datasize); 190 if (error) { 191 free(data, M_TEMP); 192 free(name, M_TEMP); 193 return (error); 194 } 195 } else 196 data = NULL; 197 198 error = efi_var_set(name, &req->vendor, req->attrib, req->datasize, 199 data); 200 if (error == EAGAIN || error == ENOENT) { 201 req->result = error; 202 error = 0; 203 } 204 205 free(data, M_TEMP); 206 free(name, M_TEMP); 207 return (error); 208} 209