1*9a26e7c0Smrg /* $NetBSD: openfirmio.c,v 1.15 2019/12/06 06:38:39 mrg Exp $ */
27d085e8eSmatt
37d085e8eSmatt /*
47d085e8eSmatt * Copyright (c) 1992, 1993
57d085e8eSmatt * The Regents of the University of California. All rights reserved.
67d085e8eSmatt *
77d085e8eSmatt * This software was developed by the Computer Systems Engineering group
87d085e8eSmatt * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
97d085e8eSmatt * contributed to Berkeley.
107d085e8eSmatt *
117d085e8eSmatt * All advertising materials mentioning features or use of this software
127d085e8eSmatt * must display the following acknowledgement:
137d085e8eSmatt * This product includes software developed by the University of
147d085e8eSmatt * California, Lawrence Berkeley Laboratory.
157d085e8eSmatt *
167d085e8eSmatt * Redistribution and use in source and binary forms, with or without
177d085e8eSmatt * modification, are permitted provided that the following conditions
187d085e8eSmatt * are met:
197d085e8eSmatt * 1. Redistributions of source code must retain the above copyright
207d085e8eSmatt * notice, this list of conditions and the following disclaimer.
217d085e8eSmatt * 2. Redistributions in binary form must reproduce the above copyright
227d085e8eSmatt * notice, this list of conditions and the following disclaimer in the
237d085e8eSmatt * documentation and/or other materials provided with the distribution.
24aad01611Sagc * 3. Neither the name of the University nor the names of its contributors
257d085e8eSmatt * may be used to endorse or promote products derived from this software
267d085e8eSmatt * without specific prior written permission.
277d085e8eSmatt *
287d085e8eSmatt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
297d085e8eSmatt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
307d085e8eSmatt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
317d085e8eSmatt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
327d085e8eSmatt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
337d085e8eSmatt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
347d085e8eSmatt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
357d085e8eSmatt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
367d085e8eSmatt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
377d085e8eSmatt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
387d085e8eSmatt * SUCH DAMAGE.
397d085e8eSmatt *
407d085e8eSmatt * @(#)openfirm.c 8.1 (Berkeley) 6/11/93
417d085e8eSmatt */
427d085e8eSmatt
43ab5d9d2bSlukem #include <sys/cdefs.h>
44*9a26e7c0Smrg __KERNEL_RCSID(0, "$NetBSD: openfirmio.c,v 1.15 2019/12/06 06:38:39 mrg Exp $");
45ab5d9d2bSlukem
467d085e8eSmatt #include <sys/param.h>
477d085e8eSmatt #include <sys/systm.h>
487d085e8eSmatt #include <sys/errno.h>
497d085e8eSmatt #include <sys/fcntl.h>
507d085e8eSmatt #include <sys/ioctl.h>
517d085e8eSmatt #include <sys/malloc.h>
527d085e8eSmatt #include <sys/conf.h>
537d085e8eSmatt #include <sys/device.h>
54e0cc03a0Sjdolecek #include <sys/event.h>
557d085e8eSmatt
567d085e8eSmatt #include <dev/ofw/openfirm.h>
577d085e8eSmatt #include <dev/ofw/openfirmio.h>
587d085e8eSmatt
597d085e8eSmatt static int lastnode; /* speed hack */
607d085e8eSmatt
617d085e8eSmatt static int openfirmcheckid (int, int);
627d085e8eSmatt static int openfirmgetstr (int, char *, char **);
637d085e8eSmatt
647d085e8eSmatt void openfirmattach (int);
657d085e8eSmatt
663d2bd524Smrg static dev_type_open(openfirmopen);
673d2bd524Smrg static dev_type_ioctl(openfirmioctl);
6877a6b82bSgehenna
6977a6b82bSgehenna const struct cdevsw openfirm_cdevsw = {
703d2bd524Smrg .d_open = openfirmopen,
71a68f9396Sdholland .d_close = nullclose,
72a68f9396Sdholland .d_read = noread,
73a68f9396Sdholland .d_write = nowrite,
74a68f9396Sdholland .d_ioctl = openfirmioctl,
75a68f9396Sdholland .d_stop = nostop,
76a68f9396Sdholland .d_tty = notty,
77a68f9396Sdholland .d_poll = nopoll,
78a68f9396Sdholland .d_mmap = nommap,
79a68f9396Sdholland .d_kqfilter = nokqfilter,
80f9228f42Sdholland .d_discard = nodiscard,
81a68f9396Sdholland .d_flag = 0
8277a6b82bSgehenna };
8377a6b82bSgehenna
847d085e8eSmatt void
openfirmattach(int num)857d085e8eSmatt openfirmattach(int num)
867d085e8eSmatt {
877d085e8eSmatt /* nothing */
887d085e8eSmatt }
897d085e8eSmatt
907d085e8eSmatt /*
917d085e8eSmatt * Verify target ID is valid (exists in the OPENPROM tree), as
927d085e8eSmatt * listed from node ID sid forward.
937d085e8eSmatt */
947d085e8eSmatt static int
openfirmcheckid(int sid,int tid)957d085e8eSmatt openfirmcheckid(int sid, int tid)
967d085e8eSmatt {
977d085e8eSmatt
987d085e8eSmatt for (; sid != 0; sid = OF_peer(sid))
997d085e8eSmatt if (sid == tid || openfirmcheckid(OF_child(sid), tid))
1007d085e8eSmatt return (1);
1017d085e8eSmatt
1027d085e8eSmatt return (0);
1037d085e8eSmatt }
1047d085e8eSmatt
1057d085e8eSmatt static int
openfirmgetstr(int len,char * user,char ** cpp)1067d085e8eSmatt openfirmgetstr(int len, char *user, char **cpp)
1077d085e8eSmatt {
1087d085e8eSmatt int error;
1097d085e8eSmatt char *cp;
1107d085e8eSmatt
1117d085e8eSmatt /* Reject obvious bogus requests */
1127d085e8eSmatt if ((u_int)len > (8 * 1024) - 1)
1137d085e8eSmatt return (ENAMETOOLONG);
1147d085e8eSmatt
1157d085e8eSmatt *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
1167d085e8eSmatt error = copyin(user, cp, len);
1177d085e8eSmatt cp[len] = '\0';
1187d085e8eSmatt return (error);
1197d085e8eSmatt }
1207d085e8eSmatt
1213d2bd524Smrg static int
openfirmopen(dev_t dev,int flag,int mode,struct lwp * l)1223d2bd524Smrg openfirmopen(dev_t dev, int flag, int mode, struct lwp *l)
1233d2bd524Smrg {
1243d2bd524Smrg
1253d2bd524Smrg return 0;
1263d2bd524Smrg }
1273d2bd524Smrg
1283d2bd524Smrg static int
openfirmioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)12953524e44Schristos openfirmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
1307d085e8eSmatt {
1317d085e8eSmatt struct ofiocdesc *of;
1327d085e8eSmatt int node, len, ok, error, s;
1337d085e8eSmatt char *name, *value;
1347d085e8eSmatt
1351936d056Smatt if (cmd == OFIOCGETOPTNODE) {
1361936d056Smatt s = splhigh();
1371936d056Smatt *(int *) data = OF_finddevice("/options");
1381936d056Smatt splx(s);
1391936d056Smatt return (0);
1401936d056Smatt }
1411936d056Smatt
1427d085e8eSmatt /* Verify node id */
1437d085e8eSmatt of = (struct ofiocdesc *)data;
1447d085e8eSmatt node = of->of_nodeid;
1457d085e8eSmatt if (node != 0 && node != lastnode) {
1467d085e8eSmatt /* Not an easy one, must search for it */
1477d085e8eSmatt s = splhigh();
1487d085e8eSmatt ok = openfirmcheckid(OF_peer(0), node);
1497d085e8eSmatt splx(s);
1507d085e8eSmatt if (!ok)
1517d085e8eSmatt return (EINVAL);
1527d085e8eSmatt lastnode = node;
1537d085e8eSmatt }
1547d085e8eSmatt
1557d085e8eSmatt name = value = NULL;
1567d085e8eSmatt error = 0;
1577d085e8eSmatt switch (cmd) {
1587d085e8eSmatt
1597d085e8eSmatt case OFIOCGET:
1607d085e8eSmatt if ((flags & FREAD) == 0)
1617d085e8eSmatt return (EBADF);
1627d085e8eSmatt if (node == 0)
1637d085e8eSmatt return (EINVAL);
1647d085e8eSmatt error = openfirmgetstr(of->of_namelen, of->of_name, &name);
1657d085e8eSmatt if (error)
1667d085e8eSmatt break;
1677d085e8eSmatt s = splhigh();
1687d085e8eSmatt len = OF_getproplen(node, name);
1697d085e8eSmatt splx(s);
1707d085e8eSmatt if (len > of->of_buflen) {
1717d085e8eSmatt error = ENOMEM;
1727d085e8eSmatt break;
1737d085e8eSmatt }
1747d085e8eSmatt of->of_buflen = len;
1757d085e8eSmatt /* -1 means no entry; 0 means no value */
1767d085e8eSmatt if (len <= 0)
1777d085e8eSmatt break;
1787d085e8eSmatt value = malloc(len, M_TEMP, M_WAITOK);
1797d085e8eSmatt if (value == NULL) {
1807d085e8eSmatt error = ENOMEM;
1817d085e8eSmatt break;
1827d085e8eSmatt }
1837d085e8eSmatt s = splhigh();
1847d085e8eSmatt len = OF_getprop(node, name, (void *)value, len);
1857d085e8eSmatt splx(s);
1867d085e8eSmatt error = copyout(value, of->of_buf, len);
1877d085e8eSmatt break;
1887d085e8eSmatt
18957fb2c65Smacallan
1907d085e8eSmatt case OFIOCSET:
1917d085e8eSmatt if ((flags & FWRITE) == 0)
1927d085e8eSmatt return (EBADF);
1937d085e8eSmatt if (node == 0)
1947d085e8eSmatt return (EINVAL);
1957d085e8eSmatt error = openfirmgetstr(of->of_namelen, of->of_name, &name);
1967d085e8eSmatt if (error)
1977d085e8eSmatt break;
1987d085e8eSmatt error = openfirmgetstr(of->of_buflen, of->of_buf, &value);
1997d085e8eSmatt if (error)
2007d085e8eSmatt break;
2017d085e8eSmatt s = splhigh();
2027d085e8eSmatt len = OF_setprop(node, name, value, of->of_buflen + 1);
2037d085e8eSmatt splx(s);
20457fb2c65Smacallan
20557fb2c65Smacallan /*
20657fb2c65Smacallan * XXX
20757fb2c65Smacallan * some OF implementations return the buffer length including
20857fb2c65Smacallan * the trailing zero ( like macppc ) and some without ( like
20957fb2c65Smacallan * FirmWorks OF used in Shark )
21057fb2c65Smacallan */
21157fb2c65Smacallan if ((len != (of->of_buflen + 1)) && (len != of->of_buflen))
2127d085e8eSmatt error = EINVAL;
2137d085e8eSmatt break;
2147d085e8eSmatt
2157d085e8eSmatt case OFIOCNEXTPROP: {
2161936d056Smatt char newname[32];
2177d085e8eSmatt if ((flags & FREAD) == 0)
2187d085e8eSmatt return (EBADF);
2197d085e8eSmatt if (node == 0)
2207d085e8eSmatt return (EINVAL);
2211936d056Smatt if (of->of_namelen != 0) {
2227d085e8eSmatt error = openfirmgetstr(of->of_namelen, of->of_name,
2237d085e8eSmatt &name);
2247d085e8eSmatt if (error)
2257d085e8eSmatt break;
2267d085e8eSmatt }
2277d085e8eSmatt s = splhigh();
2287d085e8eSmatt ok = OF_nextprop(node, name, newname);
2297d085e8eSmatt splx(s);
2301936d056Smatt if (ok == 0) {
2311936d056Smatt error = ENOENT;
2321936d056Smatt break;
2331936d056Smatt }
2347d085e8eSmatt if (ok == -1) {
2357d085e8eSmatt error = EINVAL;
2367d085e8eSmatt break;
2377d085e8eSmatt }
2387d085e8eSmatt len = strlen(newname);
2397d085e8eSmatt if (len > of->of_buflen)
2407d085e8eSmatt len = of->of_buflen;
2417d085e8eSmatt else
2427d085e8eSmatt of->of_buflen = len;
2437d085e8eSmatt error = copyout(newname, of->of_buf, len);
2447d085e8eSmatt break;
2457d085e8eSmatt }
2467d085e8eSmatt
2477d085e8eSmatt case OFIOCGETNEXT:
2487d085e8eSmatt if ((flags & FREAD) == 0)
2497d085e8eSmatt return (EBADF);
2507d085e8eSmatt s = splhigh();
2517d085e8eSmatt node = OF_peer(node);
2527d085e8eSmatt splx(s);
2537d085e8eSmatt *(int *)data = lastnode = node;
2547d085e8eSmatt break;
2557d085e8eSmatt
2567d085e8eSmatt case OFIOCGETCHILD:
2577d085e8eSmatt if ((flags & FREAD) == 0)
2587d085e8eSmatt return (EBADF);
2597d085e8eSmatt if (node == 0)
2607d085e8eSmatt return (EINVAL);
2617d085e8eSmatt s = splhigh();
2627d085e8eSmatt node = OF_child(node);
2637d085e8eSmatt splx(s);
2647d085e8eSmatt *(int *)data = lastnode = node;
2657d085e8eSmatt break;
2667d085e8eSmatt
2677d085e8eSmatt case OFIOCFINDDEVICE:
2687d085e8eSmatt if ((flags & FREAD) == 0)
2697d085e8eSmatt return (EBADF);
2707d085e8eSmatt error = openfirmgetstr(of->of_namelen, of->of_name, &name);
2717d085e8eSmatt if (error)
2727d085e8eSmatt break;
2737d085e8eSmatt node = OF_finddevice(name);
2747d085e8eSmatt if (node == 0 || node == -1) {
2757d085e8eSmatt error = ENOENT;
2767d085e8eSmatt break;
2777d085e8eSmatt }
2781936d056Smatt of->of_nodeid = lastnode = node;
2797d085e8eSmatt break;
2807d085e8eSmatt
2817d085e8eSmatt default:
2827d085e8eSmatt return (ENOTTY);
2837d085e8eSmatt }
2847d085e8eSmatt
2857d085e8eSmatt if (name)
2867d085e8eSmatt free(name, M_TEMP);
2877d085e8eSmatt if (value)
2887d085e8eSmatt free(value, M_TEMP);
2897d085e8eSmatt
2907d085e8eSmatt return (error);
2917d085e8eSmatt }
292