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