xref: /netbsd-src/sys/dev/ofw/openfirmio.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: openfirmio.c,v 1.11 2007/03/04 06:02:15 christos 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.11 2007/03/04 06:02:15 christos 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, void *data, int flags, struct lwp *l)
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 
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 
187 		/*
188 		 * XXX
189 		 * some OF implementations return the buffer length including
190 		 * the trailing zero ( like macppc ) and some without ( like
191 		 * FirmWorks OF used in Shark )
192 		 */
193 		if ((len != (of->of_buflen + 1)) && (len != of->of_buflen))
194 			error = EINVAL;
195 		break;
196 
197 	case OFIOCNEXTPROP: {
198 		char newname[32];
199 		if ((flags & FREAD) == 0)
200 			return (EBADF);
201 		if (node == 0)
202 			return (EINVAL);
203 		if (of->of_namelen != 0) {
204 			error = openfirmgetstr(of->of_namelen, of->of_name,
205 			    &name);
206 			if (error)
207 				break;
208 		}
209 		s = splhigh();
210 		ok = OF_nextprop(node, name, newname);
211 		splx(s);
212 		if (ok == 0) {
213 			error = ENOENT;
214 			break;
215 		}
216 		if (ok == -1) {
217 			error = EINVAL;
218 			break;
219 		}
220 		len = strlen(newname);
221 		if (len > of->of_buflen)
222 			len = of->of_buflen;
223 		else
224 			of->of_buflen = len;
225 		error = copyout(newname, of->of_buf, len);
226 		break;
227 	}
228 
229 	case OFIOCGETNEXT:
230 		if ((flags & FREAD) == 0)
231 			return (EBADF);
232 		s = splhigh();
233 		node = OF_peer(node);
234 		splx(s);
235 		*(int *)data = lastnode = node;
236 		break;
237 
238 	case OFIOCGETCHILD:
239 		if ((flags & FREAD) == 0)
240 			return (EBADF);
241 		if (node == 0)
242 			return (EINVAL);
243 		s = splhigh();
244 		node = OF_child(node);
245 		splx(s);
246 		*(int *)data = lastnode = node;
247 		break;
248 
249 	case OFIOCFINDDEVICE:
250 		if ((flags & FREAD) == 0)
251 			return (EBADF);
252 		error = openfirmgetstr(of->of_namelen, of->of_name, &name);
253 		if (error)
254 			break;
255 		node = OF_finddevice(name);
256 		if (node == 0 || node == -1) {
257 			error = ENOENT;
258 			break;
259 		}
260 		of->of_nodeid = lastnode = node;
261 		break;
262 
263 	default:
264 		return (ENOTTY);
265 	}
266 
267 	if (name)
268 		free(name, M_TEMP);
269 	if (value)
270 		free(value, M_TEMP);
271 
272 	return (error);
273 }
274