1 /* $NetBSD: openfirmio.c,v 1.13 2014/07/25 08:10:37 dholland 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.13 2014/07/25 08:10:37 dholland 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 .d_open = nullopen, 70 .d_close = nullclose, 71 .d_read = noread, 72 .d_write = nowrite, 73 .d_ioctl = openfirmioctl, 74 .d_stop = nostop, 75 .d_tty = notty, 76 .d_poll = nopoll, 77 .d_mmap = nommap, 78 .d_kqfilter = nokqfilter, 79 .d_discard = nodiscard, 80 .d_flag = 0 81 }; 82 83 void 84 openfirmattach(int num) 85 { 86 /* nothing */ 87 } 88 89 /* 90 * Verify target ID is valid (exists in the OPENPROM tree), as 91 * listed from node ID sid forward. 92 */ 93 static int 94 openfirmcheckid(int sid, int tid) 95 { 96 97 for (; sid != 0; sid = OF_peer(sid)) 98 if (sid == tid || openfirmcheckid(OF_child(sid), tid)) 99 return (1); 100 101 return (0); 102 } 103 104 static int 105 openfirmgetstr(int len, char *user, char **cpp) 106 { 107 int error; 108 char *cp; 109 110 /* Reject obvious bogus requests */ 111 if ((u_int)len > (8 * 1024) - 1) 112 return (ENAMETOOLONG); 113 114 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 115 error = copyin(user, cp, len); 116 cp[len] = '\0'; 117 return (error); 118 } 119 120 int 121 openfirmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 122 { 123 struct ofiocdesc *of; 124 int node, len, ok, error, s; 125 char *name, *value; 126 127 if (cmd == OFIOCGETOPTNODE) { 128 s = splhigh(); 129 *(int *) data = OF_finddevice("/options"); 130 splx(s); 131 return (0); 132 } 133 134 /* Verify node id */ 135 of = (struct ofiocdesc *)data; 136 node = of->of_nodeid; 137 if (node != 0 && node != lastnode) { 138 /* Not an easy one, must search for it */ 139 s = splhigh(); 140 ok = openfirmcheckid(OF_peer(0), node); 141 splx(s); 142 if (!ok) 143 return (EINVAL); 144 lastnode = node; 145 } 146 147 name = value = NULL; 148 error = 0; 149 switch (cmd) { 150 151 case OFIOCGET: 152 if ((flags & FREAD) == 0) 153 return (EBADF); 154 if (node == 0) 155 return (EINVAL); 156 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 157 if (error) 158 break; 159 s = splhigh(); 160 len = OF_getproplen(node, name); 161 splx(s); 162 if (len > of->of_buflen) { 163 error = ENOMEM; 164 break; 165 } 166 of->of_buflen = len; 167 /* -1 means no entry; 0 means no value */ 168 if (len <= 0) 169 break; 170 value = malloc(len, M_TEMP, M_WAITOK); 171 if (value == NULL) { 172 error = ENOMEM; 173 break; 174 } 175 s = splhigh(); 176 len = OF_getprop(node, name, (void *)value, len); 177 splx(s); 178 error = copyout(value, of->of_buf, len); 179 break; 180 181 182 case OFIOCSET: 183 if ((flags & FWRITE) == 0) 184 return (EBADF); 185 if (node == 0) 186 return (EINVAL); 187 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 188 if (error) 189 break; 190 error = openfirmgetstr(of->of_buflen, of->of_buf, &value); 191 if (error) 192 break; 193 s = splhigh(); 194 len = OF_setprop(node, name, value, of->of_buflen + 1); 195 splx(s); 196 197 /* 198 * XXX 199 * some OF implementations return the buffer length including 200 * the trailing zero ( like macppc ) and some without ( like 201 * FirmWorks OF used in Shark ) 202 */ 203 if ((len != (of->of_buflen + 1)) && (len != of->of_buflen)) 204 error = EINVAL; 205 break; 206 207 case OFIOCNEXTPROP: { 208 char newname[32]; 209 if ((flags & FREAD) == 0) 210 return (EBADF); 211 if (node == 0) 212 return (EINVAL); 213 if (of->of_namelen != 0) { 214 error = openfirmgetstr(of->of_namelen, of->of_name, 215 &name); 216 if (error) 217 break; 218 } 219 s = splhigh(); 220 ok = OF_nextprop(node, name, newname); 221 splx(s); 222 if (ok == 0) { 223 error = ENOENT; 224 break; 225 } 226 if (ok == -1) { 227 error = EINVAL; 228 break; 229 } 230 len = strlen(newname); 231 if (len > of->of_buflen) 232 len = of->of_buflen; 233 else 234 of->of_buflen = len; 235 error = copyout(newname, of->of_buf, len); 236 break; 237 } 238 239 case OFIOCGETNEXT: 240 if ((flags & FREAD) == 0) 241 return (EBADF); 242 s = splhigh(); 243 node = OF_peer(node); 244 splx(s); 245 *(int *)data = lastnode = node; 246 break; 247 248 case OFIOCGETCHILD: 249 if ((flags & FREAD) == 0) 250 return (EBADF); 251 if (node == 0) 252 return (EINVAL); 253 s = splhigh(); 254 node = OF_child(node); 255 splx(s); 256 *(int *)data = lastnode = node; 257 break; 258 259 case OFIOCFINDDEVICE: 260 if ((flags & FREAD) == 0) 261 return (EBADF); 262 error = openfirmgetstr(of->of_namelen, of->of_name, &name); 263 if (error) 264 break; 265 node = OF_finddevice(name); 266 if (node == 0 || node == -1) { 267 error = ENOENT; 268 break; 269 } 270 of->of_nodeid = lastnode = node; 271 break; 272 273 default: 274 return (ENOTTY); 275 } 276 277 if (name) 278 free(name, M_TEMP); 279 if (value) 280 free(value, M_TEMP); 281 282 return (error); 283 } 284