xref: /freebsd/sys/dev/ofw/openfirmio.c (revision e0c27215058b5786c78fcfb3963eebe61a989511)
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	OPENFIRM_MINOR	0
66 
67 static struct cdevsw openfirm_cdevsw = {
68 	.d_open =	nullopen,
69 	.d_close =	nullclose,
70 	.d_ioctl =	openfirm_ioctl,
71 	.d_name =	"openfirm",
72 };
73 
74 static phandle_t lastnode;	/* speed hack */
75 
76 static int openfirm_checkid(phandle_t, phandle_t);
77 static int openfirm_getstr(int, const char *, char **);
78 
79 /* Maximum accepted name length. */
80 #define	OFW_NAME_MAX	8191
81 
82 /*
83  * Verify target ID is valid (exists in the OPENPROM tree), as
84  * listed from node ID sid forward.
85  */
86 static int
87 openfirm_checkid(phandle_t sid, phandle_t tid)
88 {
89 
90 	for (; sid != 0; sid = OF_peer(sid))
91 		if (sid == tid || openfirm_checkid(OF_child(sid), tid))
92 			return (1);
93 
94 	return (0);
95 }
96 
97 static int
98 openfirm_getstr(int len, const char *user, char **cpp)
99 {
100 	int error;
101 	char *cp;
102 
103 	/* Reject obvious bogus requests */
104 	if ((u_int)len > OFW_NAME_MAX)
105 		return (ENAMETOOLONG);
106 
107 	*cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
108 	if (cp == NULL)
109 		return (ENOMEM);
110 	error = copyin(user, cp, len);
111 	cp[len] = '\0';
112 	return (error);
113 }
114 
115 int
116 openfirm_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
117     struct thread *td)
118 {
119 	struct ofiocdesc *of;
120 	phandle_t node;
121 	int len, ok, error;
122 	char *name, *value;
123 	char newname[32];
124 
125 	if ((flags & FREAD) == 0)
126 		return (EBADF);
127 
128 	of = (struct ofiocdesc *)data;
129 	switch (cmd) {
130 	case OFIOCGETOPTNODE:
131 		*(phandle_t *) data = OF_finddevice("/options");
132 		return (0);
133 	case OFIOCGET:
134 #if 0
135 	case OFIOCSET:
136 #endif
137 	case OFIOCNEXTPROP:
138 	case OFIOCFINDDEVICE:
139 	case OFIOCGETPROPLEN:
140 		node = of->of_nodeid;
141 		break;
142 	case OFIOCGETNEXT:
143 	case OFIOCGETCHILD:
144 		node = *(phandle_t *)data;
145 		break;
146 	default:
147 		return (ENOIOCTL);
148 	}
149 
150 	if (node != 0 && node != lastnode) {
151 		/* Not an easy one, must search for it */
152 		ok = openfirm_checkid(OF_peer(0), node);
153 		if (!ok)
154 			return (EINVAL);
155 		lastnode = node;
156 	}
157 
158 	name = value = NULL;
159 	error = 0;
160 	switch (cmd) {
161 
162 	case OFIOCGET:
163 	case OFIOCGETPROPLEN:
164 		if (node == 0)
165 			return (EINVAL);
166 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
167 		if (error)
168 			break;
169 		len = OF_getproplen(node, name);
170 		if (cmd == OFIOCGETPROPLEN) {
171 			of->of_buflen = len;
172 			break;
173 		}
174 		if (len > of->of_buflen) {
175 			error = ENOMEM;
176 			break;
177 		}
178 		of->of_buflen = len;
179 		/* -1 means no entry; 0 means no value */
180 		if (len <= 0)
181 			break;
182 		value = malloc(len, M_TEMP, M_WAITOK);
183 		if (value == NULL) {
184 			error = ENOMEM;
185 			break;
186 		}
187 		len = OF_getprop(node, name, (void *)value, len);
188 		error = copyout(value, of->of_buf, len);
189 		break;
190 
191 #if 0
192 	case OFIOCSET:
193 		if ((flags & FWRITE) == 0)
194 			return (EBADF);
195 		if (node == 0)
196 			return (EINVAL);
197 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
198 		if (error)
199 			break;
200 		error = openfirm_getstr(of->of_buflen, of->of_buf, &value);
201 		if (error)
202 			break;
203 		len = OF_setprop(node, name, value, of->of_buflen);
204 		if (len != of->of_buflen)
205 			error = EINVAL;
206 		break;
207 #endif
208 
209 	case OFIOCNEXTPROP:
210 		if (node == 0 || of->of_buflen < 0)
211 			return (EINVAL);
212 		if (of->of_namelen != 0) {
213 			error = openfirm_getstr(of->of_namelen, of->of_name,
214 			    &name);
215 			if (error)
216 				break;
217 		}
218 		ok = OF_nextprop(node, name, newname);
219 		if (ok == 0) {
220 			error = ENOENT;
221 			break;
222 		}
223 		if (ok == -1) {
224 			error = EINVAL;
225 			break;
226 		}
227 		len = strlen(newname) + 1;
228 		if (len > of->of_buflen)
229 			len = of->of_buflen;
230 		else
231 			of->of_buflen = len;
232 		error = copyout(newname, of->of_buf, len);
233 		break;
234 
235 	case OFIOCGETNEXT:
236 		node = OF_peer(node);
237 		*(phandle_t *)data = lastnode = node;
238 		break;
239 
240 	case OFIOCGETCHILD:
241 		if (node == 0)
242 			return (EINVAL);
243 		node = OF_child(node);
244 		*(phandle_t *)data = lastnode = node;
245 		break;
246 
247 	case OFIOCFINDDEVICE:
248 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
249 		if (error)
250 			break;
251 		node = OF_finddevice(name);
252 		if (node == 0 || node == -1) {
253 			error = ENOENT;
254 			break;
255 		}
256 		of->of_nodeid = lastnode = node;
257 		break;
258 	}
259 
260 	if (name != NULL)
261 		free(name, M_TEMP);
262 	if (value != NULL)
263 		free(value, M_TEMP);
264 
265 	return (error);
266 }
267 
268 static int
269 openfirm_modevent(module_t mod, int type, void *data)
270 {
271 	switch(type) {
272 	case MOD_LOAD:
273 		if (bootverbose)
274 			printf("openfirm: <OpenFirmware control device>\n");
275 		/*
276 		 * Allow only root access by default; this device may allow
277 		 * users to peek into firmware passwords, and likely to crash
278 		 * the machine on some boxen due to firmware quirks.
279 		 */
280 		openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR,
281 		    UID_ROOT, GID_WHEEL, 0600, "openfirm");
282 		return 0;
283 
284 	case MOD_UNLOAD:
285 		destroy_dev(openfirm_dev);
286 		return 0;
287 
288 	case MOD_SHUTDOWN:
289 		return 0;
290 
291 	default:
292 		return EOPNOTSUPP;
293 	}
294 }
295 
296 DEV_MODULE(openfirm, openfirm_modevent, NULL);
297