openfirmio.c revision 119418
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 119418 2003-08-24 17:55:58Z obrien $");
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
63static dev_t openfirm_dev;
64
65static d_ioctl_t openfirm_ioctl;
66
67#define	OPENFIRM_MINOR	0
68
69static struct cdevsw openfirm_cdevsw = {
70	.d_open =	nullopen,
71	.d_close =	nullclose,
72	.d_ioctl =	openfirm_ioctl,
73	.d_name =	"openfirm",
74};
75
76static phandle_t lastnode;	/* speed hack */
77
78static int openfirm_checkid(phandle_t, phandle_t);
79static 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 */
88static int
89openfirm_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
99static int
100openfirm_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
117int
118openfirm_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
270static int
271openfirm_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
298DEV_MODULE(openfirm, openfirm_modevent, NULL);
299