1 /* $NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna Exp $ */ 2 3 #include <sys/cdefs.h> 4 __FBSDID("$FreeBSD$"); 5 6 /* 7 * Copyright (c) 1992, 1993 8 * The Regents of the University of California. All rights reserved. 9 * 10 * This software was developed by the Computer Systems Engineering group 11 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 12 * contributed to Berkeley. 13 * 14 * All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Lawrence Berkeley Laboratory. 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions 21 * are met: 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 3. All advertising materials mentioning features or use of this software 28 * must display the following acknowledgement: 29 * This product includes software developed by the University of 30 * California, Berkeley and its contributors. 31 * 4. Neither the name of the University nor the names of its contributors 32 * may be used to endorse or promote products derived from this software 33 * without specific prior written permission. 34 * 35 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45 * SUCH DAMAGE. 46 * 47 * @(#)openfirm.c 8.1 (Berkeley) 6/11/93 48 * 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/conf.h> 54 #include <sys/errno.h> 55 #include <sys/fcntl.h> 56 #include <sys/ioccom.h> 57 #include <sys/kernel.h> 58 #include <sys/malloc.h> 59 #include <sys/module.h> 60 61 #include <dev/ofw/openfirmio.h> 62 63 static dev_t openfirm_dev; 64 65 static d_ioctl_t openfirm_ioctl; 66 67 #define OPENFIRM_MINOR 0 68 69 static struct cdevsw openfirm_cdevsw = { 70 .d_open = nullopen, 71 .d_close = nullclose, 72 .d_ioctl = openfirm_ioctl, 73 .d_name = "openfirm", 74 }; 75 76 static phandle_t lastnode; /* speed hack */ 77 78 static int openfirm_checkid(phandle_t, phandle_t); 79 static int openfirm_getstr(int, const char *, char **); 80 81 /* Maximum accepted name length. */ 82 #define OFW_NAME_MAX 8191 83 84 /* 85 * Verify target ID is valid (exists in the OPENPROM tree), as 86 * listed from node ID sid forward. 87 */ 88 static int 89 openfirm_checkid(phandle_t sid, phandle_t tid) 90 { 91 92 for (; sid != 0; sid = OF_peer(sid)) 93 if (sid == tid || openfirm_checkid(OF_child(sid), tid)) 94 return (1); 95 96 return (0); 97 } 98 99 static int 100 openfirm_getstr(int len, const char *user, char **cpp) 101 { 102 int error; 103 char *cp; 104 105 /* Reject obvious bogus requests */ 106 if ((u_int)len > OFW_NAME_MAX) 107 return (ENAMETOOLONG); 108 109 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 110 if (cp == NULL) 111 return (ENOMEM); 112 error = copyin(user, cp, len); 113 cp[len] = '\0'; 114 return (error); 115 } 116 117 int 118 openfirm_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, 119 struct thread *td) 120 { 121 struct ofiocdesc *of; 122 phandle_t node; 123 int len, ok, error; 124 char *name, *value; 125 char newname[32]; 126 127 if ((flags & FREAD) == 0) 128 return (EBADF); 129 130 of = (struct ofiocdesc *)data; 131 switch (cmd) { 132 case OFIOCGETOPTNODE: 133 *(phandle_t *) data = OF_finddevice("/options"); 134 return (0); 135 case OFIOCGET: 136 #if 0 137 case OFIOCSET: 138 #endif 139 case OFIOCNEXTPROP: 140 case OFIOCFINDDEVICE: 141 case OFIOCGETPROPLEN: 142 node = of->of_nodeid; 143 break; 144 case OFIOCGETNEXT: 145 case OFIOCGETCHILD: 146 node = *(phandle_t *)data; 147 break; 148 default: 149 return (ENOIOCTL); 150 } 151 152 if (node != 0 && node != lastnode) { 153 /* Not an easy one, must search for it */ 154 ok = openfirm_checkid(OF_peer(0), node); 155 if (!ok) 156 return (EINVAL); 157 lastnode = node; 158 } 159 160 name = value = NULL; 161 error = 0; 162 switch (cmd) { 163 164 case OFIOCGET: 165 case OFIOCGETPROPLEN: 166 if (node == 0) 167 return (EINVAL); 168 error = openfirm_getstr(of->of_namelen, of->of_name, &name); 169 if (error) 170 break; 171 len = OF_getproplen(node, name); 172 if (cmd == OFIOCGETPROPLEN) { 173 of->of_buflen = len; 174 break; 175 } 176 if (len > of->of_buflen) { 177 error = ENOMEM; 178 break; 179 } 180 of->of_buflen = len; 181 /* -1 means no entry; 0 means no value */ 182 if (len <= 0) 183 break; 184 value = malloc(len, M_TEMP, M_WAITOK); 185 if (value == NULL) { 186 error = ENOMEM; 187 break; 188 } 189 len = OF_getprop(node, name, (void *)value, len); 190 error = copyout(value, of->of_buf, len); 191 break; 192 193 #if 0 194 case OFIOCSET: 195 if ((flags & FWRITE) == 0) 196 return (EBADF); 197 if (node == 0) 198 return (EINVAL); 199 error = openfirm_getstr(of->of_namelen, of->of_name, &name); 200 if (error) 201 break; 202 error = openfirm_getstr(of->of_buflen, of->of_buf, &value); 203 if (error) 204 break; 205 len = OF_setprop(node, name, value, of->of_buflen); 206 if (len != of->of_buflen) 207 error = EINVAL; 208 break; 209 #endif 210 211 case OFIOCNEXTPROP: 212 if (node == 0 || of->of_buflen < 0) 213 return (EINVAL); 214 if (of->of_namelen != 0) { 215 error = openfirm_getstr(of->of_namelen, of->of_name, 216 &name); 217 if (error) 218 break; 219 } 220 ok = OF_nextprop(node, name, newname); 221 if (ok == 0) { 222 error = ENOENT; 223 break; 224 } 225 if (ok == -1) { 226 error = EINVAL; 227 break; 228 } 229 len = strlen(newname) + 1; 230 if (len > of->of_buflen) 231 len = of->of_buflen; 232 else 233 of->of_buflen = len; 234 error = copyout(newname, of->of_buf, len); 235 break; 236 237 case OFIOCGETNEXT: 238 node = OF_peer(node); 239 *(phandle_t *)data = lastnode = node; 240 break; 241 242 case OFIOCGETCHILD: 243 if (node == 0) 244 return (EINVAL); 245 node = OF_child(node); 246 *(phandle_t *)data = lastnode = node; 247 break; 248 249 case OFIOCFINDDEVICE: 250 error = openfirm_getstr(of->of_namelen, of->of_name, &name); 251 if (error) 252 break; 253 node = OF_finddevice(name); 254 if (node == 0 || node == -1) { 255 error = ENOENT; 256 break; 257 } 258 of->of_nodeid = lastnode = node; 259 break; 260 } 261 262 if (name != NULL) 263 free(name, M_TEMP); 264 if (value != NULL) 265 free(value, M_TEMP); 266 267 return (error); 268 } 269 270 static int 271 openfirm_modevent(module_t mod, int type, void *data) 272 { 273 switch(type) { 274 case MOD_LOAD: 275 if (bootverbose) 276 printf("openfirm: <OpenFirmware control device>\n"); 277 /* 278 * Allow only root access by default; this device may allow 279 * users to peek into firmware passwords, and likely to crash 280 * the machine on some boxen due to firmware quirks. 281 */ 282 openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR, 283 UID_ROOT, GID_WHEEL, 0600, "openfirm"); 284 return 0; 285 286 case MOD_UNLOAD: 287 destroy_dev(openfirm_dev); 288 return 0; 289 290 case MOD_SHUTDOWN: 291 return 0; 292 293 default: 294 return EOPNOTSUPP; 295 } 296 } 297 298 DEV_MODULE(openfirm, openfirm_modevent, NULL); 299