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