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