xref: /freebsd/sys/dev/ofw/openfirmio.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*	$NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna Exp $ */
2 
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 
6 /*
7  * Copyright (c) 1992, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * This software was developed by the Computer Systems Engineering group
11  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
12  * contributed to Berkeley.
13  *
14  * All advertising materials mentioning features or use of this software
15  * must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Lawrence Berkeley Laboratory.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  * 1. Redistributions of source code must retain the above copyright
23  *    notice, this list of conditions and the following disclaimer.
24  * 2. Redistributions in binary form must reproduce the above copyright
25  *    notice, this list of conditions and the following disclaimer in the
26  *    documentation and/or other materials provided with the distribution.
27  * 3. All advertising materials mentioning features or use of this software
28  *    must display the following acknowledgement:
29  *	This product includes software developed by the University of
30  *	California, Berkeley and its contributors.
31  * 4. Neither the name of the University nor the names of its contributors
32  *    may be used to endorse or promote products derived from this software
33  *    without specific prior written permission.
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45  * SUCH DAMAGE.
46  *
47  *	@(#)openfirm.c	8.1 (Berkeley) 6/11/93
48  *
49  */
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/conf.h>
54 #include <sys/errno.h>
55 #include <sys/fcntl.h>
56 #include <sys/ioccom.h>
57 #include <sys/kernel.h>
58 #include <sys/malloc.h>
59 #include <sys/module.h>
60 
61 #include <dev/ofw/openfirmio.h>
62 
63 static dev_t openfirm_dev;
64 
65 static d_ioctl_t openfirm_ioctl;
66 
67 #define	OPENFIRM_MINOR	0
68 
69 static struct cdevsw openfirm_cdevsw = {
70 	.d_open =	nullopen,
71 	.d_close =	nullclose,
72 	.d_ioctl =	openfirm_ioctl,
73 	.d_name =	"openfirm",
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 	if ((flags & FREAD) == 0)
128 		return (EBADF);
129 
130 	of = (struct ofiocdesc *)data;
131 	switch (cmd) {
132 	case OFIOCGETOPTNODE:
133 		*(phandle_t *) data = OF_finddevice("/options");
134 		return (0);
135 	case OFIOCGET:
136 #if 0
137 	case OFIOCSET:
138 #endif
139 	case OFIOCNEXTPROP:
140 	case OFIOCFINDDEVICE:
141 	case OFIOCGETPROPLEN:
142 		node = of->of_nodeid;
143 		break;
144 	case OFIOCGETNEXT:
145 	case OFIOCGETCHILD:
146 		node = *(phandle_t *)data;
147 		break;
148 	default:
149 		return (ENOIOCTL);
150 	}
151 
152 	if (node != 0 && node != lastnode) {
153 		/* Not an easy one, must search for it */
154 		ok = openfirm_checkid(OF_peer(0), node);
155 		if (!ok)
156 			return (EINVAL);
157 		lastnode = node;
158 	}
159 
160 	name = value = NULL;
161 	error = 0;
162 	switch (cmd) {
163 
164 	case OFIOCGET:
165 	case OFIOCGETPROPLEN:
166 		if (node == 0)
167 			return (EINVAL);
168 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
169 		if (error)
170 			break;
171 		len = OF_getproplen(node, name);
172 		if (cmd == OFIOCGETPROPLEN) {
173 			of->of_buflen = len;
174 			break;
175 		}
176 		if (len > of->of_buflen) {
177 			error = ENOMEM;
178 			break;
179 		}
180 		of->of_buflen = len;
181 		/* -1 means no entry; 0 means no value */
182 		if (len <= 0)
183 			break;
184 		value = malloc(len, M_TEMP, M_WAITOK);
185 		if (value == NULL) {
186 			error = ENOMEM;
187 			break;
188 		}
189 		len = OF_getprop(node, name, (void *)value, len);
190 		error = copyout(value, of->of_buf, len);
191 		break;
192 
193 #if 0
194 	case OFIOCSET:
195 		if ((flags & FWRITE) == 0)
196 			return (EBADF);
197 		if (node == 0)
198 			return (EINVAL);
199 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
200 		if (error)
201 			break;
202 		error = openfirm_getstr(of->of_buflen, of->of_buf, &value);
203 		if (error)
204 			break;
205 		len = OF_setprop(node, name, value, of->of_buflen);
206 		if (len != of->of_buflen)
207 			error = EINVAL;
208 		break;
209 #endif
210 
211 	case OFIOCNEXTPROP:
212 		if (node == 0 || of->of_buflen < 0)
213 			return (EINVAL);
214 		if (of->of_namelen != 0) {
215 			error = openfirm_getstr(of->of_namelen, of->of_name,
216 			    &name);
217 			if (error)
218 				break;
219 		}
220 		ok = OF_nextprop(node, name, newname);
221 		if (ok == 0) {
222 			error = ENOENT;
223 			break;
224 		}
225 		if (ok == -1) {
226 			error = EINVAL;
227 			break;
228 		}
229 		len = strlen(newname) + 1;
230 		if (len > of->of_buflen)
231 			len = of->of_buflen;
232 		else
233 			of->of_buflen = len;
234 		error = copyout(newname, of->of_buf, len);
235 		break;
236 
237 	case OFIOCGETNEXT:
238 		node = OF_peer(node);
239 		*(phandle_t *)data = lastnode = node;
240 		break;
241 
242 	case OFIOCGETCHILD:
243 		if (node == 0)
244 			return (EINVAL);
245 		node = OF_child(node);
246 		*(phandle_t *)data = lastnode = node;
247 		break;
248 
249 	case OFIOCFINDDEVICE:
250 		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
251 		if (error)
252 			break;
253 		node = OF_finddevice(name);
254 		if (node == 0 || node == -1) {
255 			error = ENOENT;
256 			break;
257 		}
258 		of->of_nodeid = lastnode = node;
259 		break;
260 	}
261 
262 	if (name != NULL)
263 		free(name, M_TEMP);
264 	if (value != NULL)
265 		free(value, M_TEMP);
266 
267 	return (error);
268 }
269 
270 static int
271 openfirm_modevent(module_t mod, int type, void *data)
272 {
273 	switch(type) {
274 	case MOD_LOAD:
275 		if (bootverbose)
276 			printf("openfirm: <OpenFirmware control device>\n");
277 		/*
278 		 * Allow only root access by default; this device may allow
279 		 * users to peek into firmware passwords, and likely to crash
280 		 * the machine on some boxen due to firmware quirks.
281 		 */
282 		openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR,
283 		    UID_ROOT, GID_WHEEL, 0600, "openfirm");
284 		return 0;
285 
286 	case MOD_UNLOAD:
287 		destroy_dev(openfirm_dev);
288 		return 0;
289 
290 	case MOD_SHUTDOWN:
291 		return 0;
292 
293 	default:
294 		return EOPNOTSUPP;
295 	}
296 }
297 
298 DEV_MODULE(openfirm, openfirm_modevent, NULL);
299