xref: /freebsd/sys/dev/ofw/openfirmio.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
1 /*	$NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna 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. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *	This product includes software developed by the University of
27  *	California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  *
44  *	@(#)openfirm.c	8.1 (Berkeley) 6/11/93
45  *
46  * $FreeBSD$
47  */
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/conf.h>
52 #include <sys/errno.h>
53 #include <sys/fcntl.h>
54 #include <sys/ioccom.h>
55 #include <sys/kernel.h>
56 #include <sys/malloc.h>
57 #include <sys/module.h>
58 
59 #include <dev/ofw/openfirmio.h>
60 
61 static dev_t openfirm_dev;
62 
63 static d_ioctl_t openfirm_ioctl;
64 
65 #define CDEV_MAJOR	177
66 #define	OPENFIRM_MINOR	0
67 
68 static struct cdevsw openfirm_cdevsw = {
69 	.d_open =	nullopen,
70 	.d_close =	nullclose,
71 	.d_ioctl =	openfirm_ioctl,
72 	.d_name =	"openfirm",
73 	.d_maj =	CDEV_MAJOR,
74 };
75 
76 static phandle_t lastnode;	/* speed hack */
77 
78 static int openfirm_checkid(phandle_t, phandle_t);
79 static int openfirm_getstr(int, const char *, char **);
80 
81 /* Maximum accepted name length. */
82 #define	OFW_NAME_MAX	8191
83 
84 /*
85  * Verify target ID is valid (exists in the OPENPROM tree), as
86  * listed from node ID sid forward.
87  */
88 static int
89 openfirm_checkid(phandle_t sid, phandle_t tid)
90 {
91 
92 	for (; sid != 0; sid = OF_peer(sid))
93 		if (sid == tid || openfirm_checkid(OF_child(sid), tid))
94 			return (1);
95 
96 	return (0);
97 }
98 
99 static int
100 openfirm_getstr(int len, const char *user, char **cpp)
101 {
102 	int error;
103 	char *cp;
104 
105 	/* Reject obvious bogus requests */
106 	if ((u_int)len > OFW_NAME_MAX)
107 		return (ENAMETOOLONG);
108 
109 	*cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
110 	if (cp == NULL)
111 		return (ENOMEM);
112 	error = copyin(user, cp, len);
113 	cp[len] = '\0';
114 	return (error);
115 }
116 
117 int
118 openfirm_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
119     struct thread *td)
120 {
121 	struct ofiocdesc *of;
122 	phandle_t node;
123 	int len, ok, error;
124 	char *name, *value;
125 	char newname[32];
126 
127 	of = (struct ofiocdesc *)data;
128 	switch (cmd) {
129 	case OFIOCGETOPTNODE:
130 		*(phandle_t *) data = OF_finddevice("/options");
131 		return (0);
132 	case OFIOCGET:
133 #if 0
134 	case OFIOCSET:
135 #endif
136 	case OFIOCNEXTPROP:
137 	case OFIOCFINDDEVICE:
138 		node = of->of_nodeid;
139 		break;
140 	case OFIOCGETNEXT:
141 	case OFIOCGETCHILD:
142 		node = *(phandle_t *)data;
143 		break;
144 	default:
145 		return (ENOTTY);
146 	}
147 
148 	if (node != 0 && node != lastnode) {
149 		/* Not an easy one, must search for it */
150 		ok = openfirm_checkid(OF_peer(0), node);
151 		if (!ok)
152 			return (EINVAL);
153 		lastnode = node;
154 	}
155 
156 	name = value = NULL;
157 	error = 0;
158 	switch (cmd) {
159 
160 	case OFIOCGET:
161 		if ((flags & FREAD) == 0)
162 			return (EBADF);
163 		if (node == 0)
164 			return (EINVAL);
165 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
166 		if (error)
167 			break;
168 		len = OF_getproplen(node, name);
169 		if (len > of->of_buflen) {
170 			error = ENOMEM;
171 			break;
172 		}
173 		of->of_buflen = len;
174 		/* -1 means no entry; 0 means no value */
175 		if (len <= 0)
176 			break;
177 		value = malloc(len, M_TEMP, M_WAITOK);
178 		if (value == NULL) {
179 			error = ENOMEM;
180 			break;
181 		}
182 		len = OF_getprop(node, name, (void *)value, len);
183 		error = copyout(value, of->of_buf, len);
184 		break;
185 
186 #if 0
187 	case OFIOCSET:
188 		if ((flags & FWRITE) == 0)
189 			return (EBADF);
190 		if (node == 0)
191 			return (EINVAL);
192 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
193 		if (error)
194 			break;
195 		error = openfirm_getstr(of->of_buflen, of->of_buf, &value);
196 		if (error)
197 			break;
198 		len = OF_setprop(node, name, value, of->of_buflen);
199 		if (len != of->of_buflen)
200 			error = EINVAL;
201 		break;
202 #endif
203 
204 	case OFIOCNEXTPROP:
205 		if ((flags & FREAD) == 0)
206 			return (EBADF);
207 		if (node == 0 || of->of_buflen < 0)
208 			return (EINVAL);
209 		if (of->of_namelen != 0) {
210 			error = openfirm_getstr(of->of_namelen, of->of_name,
211 			    &name);
212 			if (error)
213 				break;
214 		}
215 		ok = OF_nextprop(node, name, newname);
216 		if (ok == 0) {
217 			error = ENOENT;
218 			break;
219 		}
220 		if (ok == -1) {
221 			error = EINVAL;
222 			break;
223 		}
224 		len = strlen(newname) + 1;
225 		if (len > of->of_buflen)
226 			len = of->of_buflen;
227 		else
228 			of->of_buflen = len;
229 		error = copyout(newname, of->of_buf, len);
230 		break;
231 
232 	case OFIOCGETNEXT:
233 		if ((flags & FREAD) == 0)
234 			return (EBADF);
235 		node = OF_peer(node);
236 		*(phandle_t *)data = lastnode = node;
237 		break;
238 
239 	case OFIOCGETCHILD:
240 		if ((flags & FREAD) == 0)
241 			return (EBADF);
242 		if (node == 0)
243 			return (EINVAL);
244 		node = OF_child(node);
245 		*(phandle_t *)data = lastnode = node;
246 		break;
247 
248 	case OFIOCFINDDEVICE:
249 		if ((flags & FREAD) == 0)
250 			return (EBADF);
251 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
252 		if (error)
253 			break;
254 		node = OF_finddevice(name);
255 		if (node == 0 || node == -1) {
256 			error = ENOENT;
257 			break;
258 		}
259 		of->of_nodeid = lastnode = node;
260 		break;
261 	}
262 
263 	if (name != NULL)
264 		free(name, M_TEMP);
265 	if (value != NULL)
266 		free(value, M_TEMP);
267 
268 	return (error);
269 }
270 
271 static int
272 openfirm_modevent(module_t mod, int type, void *data)
273 {
274 	switch(type) {
275 	case MOD_LOAD:
276 		if (bootverbose)
277 			printf("openfirm: <OpenFirmware control device>\n");
278 		/*
279 		 * Allow only root access by default; this device may allow
280 		 * users to peek into firmware passwords, and likely to crash
281 		 * the machine on some boxen due to firmware quirks.
282 		 */
283 		openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR,
284 		    UID_ROOT, GID_WHEEL, 0600, "openfirm");
285 		return 0;
286 
287 	case MOD_UNLOAD:
288 		destroy_dev(openfirm_dev);
289 		return 0;
290 
291 	case MOD_SHUTDOWN:
292 		return 0;
293 
294 	default:
295 		return EOPNOTSUPP;
296 	}
297 }
298 
299 DEV_MODULE(openfirm, openfirm_modevent, NULL);
300