xref: /netbsd-src/sys/dev/ofw/openfirmio.c (revision 9a26e7c0b27cb6938dff7bede63397f579fa73c1)
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