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