1105398Stmm/*	$NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna Exp $ */
2105398Stmm
3119418Sobrien#include <sys/cdefs.h>
4119418Sobrien__FBSDID("$FreeBSD$");
5119418Sobrien
6139749Simp/*-
7105398Stmm * Copyright (c) 1992, 1993
8105398Stmm *	The Regents of the University of California.  All rights reserved.
9105398Stmm *
10105398Stmm * This software was developed by the Computer Systems Engineering group
11105398Stmm * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
12105398Stmm * contributed to Berkeley.
13105398Stmm *
14105398Stmm * All advertising materials mentioning features or use of this software
15105398Stmm * must display the following acknowledgement:
16105398Stmm *	This product includes software developed by the University of
17105398Stmm *	California, Lawrence Berkeley Laboratory.
18105398Stmm *
19105398Stmm * Redistribution and use in source and binary forms, with or without
20105398Stmm * modification, are permitted provided that the following conditions
21105398Stmm * are met:
22105398Stmm * 1. Redistributions of source code must retain the above copyright
23105398Stmm *    notice, this list of conditions and the following disclaimer.
24105398Stmm * 2. Redistributions in binary form must reproduce the above copyright
25105398Stmm *    notice, this list of conditions and the following disclaimer in the
26105398Stmm *    documentation and/or other materials provided with the distribution.
27105398Stmm * 4. Neither the name of the University nor the names of its contributors
28105398Stmm *    may be used to endorse or promote products derived from this software
29105398Stmm *    without specific prior written permission.
30105398Stmm *
31105398Stmm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32105398Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33105398Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34105398Stmm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35105398Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36105398Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37105398Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38105398Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39105398Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40105398Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41105398Stmm * SUCH DAMAGE.
42105398Stmm *
43105398Stmm *	@(#)openfirm.c	8.1 (Berkeley) 6/11/93
44105398Stmm *
45105398Stmm */
46105398Stmm
47105398Stmm#include <sys/param.h>
48105398Stmm#include <sys/systm.h>
49105398Stmm#include <sys/conf.h>
50105398Stmm#include <sys/errno.h>
51105398Stmm#include <sys/fcntl.h>
52105398Stmm#include <sys/ioccom.h>
53105398Stmm#include <sys/kernel.h>
54105398Stmm#include <sys/malloc.h>
55105398Stmm#include <sys/module.h>
56105398Stmm
57105398Stmm#include <dev/ofw/openfirmio.h>
58105398Stmm
59130585Sphkstatic struct cdev *openfirm_dev;
60105398Stmm
61105398Stmmstatic d_ioctl_t openfirm_ioctl;
62105398Stmm
63105398Stmm#define	OPENFIRM_MINOR	0
64105398Stmm
65105398Stmmstatic struct cdevsw openfirm_cdevsw = {
66126080Sphk	.d_version =	D_VERSION,
67126080Sphk	.d_flags =	D_NEEDGIANT,
68111815Sphk	.d_ioctl =	openfirm_ioctl,
69111815Sphk	.d_name =	"openfirm",
70105398Stmm};
71105398Stmm
72105398Stmmstatic phandle_t lastnode;	/* speed hack */
73105398Stmm
74105398Stmmstatic int openfirm_checkid(phandle_t, phandle_t);
75105398Stmmstatic int openfirm_getstr(int, const char *, char **);
76105398Stmm
77105398Stmm/*
78105398Stmm * Verify target ID is valid (exists in the OPENPROM tree), as
79105398Stmm * listed from node ID sid forward.
80105398Stmm */
81105398Stmmstatic int
82105398Stmmopenfirm_checkid(phandle_t sid, phandle_t tid)
83105398Stmm{
84105398Stmm
85105398Stmm	for (; sid != 0; sid = OF_peer(sid))
86105398Stmm		if (sid == tid || openfirm_checkid(OF_child(sid), tid))
87105398Stmm			return (1);
88105398Stmm
89105398Stmm	return (0);
90105398Stmm}
91105398Stmm
92105398Stmmstatic int
93105398Stmmopenfirm_getstr(int len, const char *user, char **cpp)
94105398Stmm{
95105398Stmm	int error;
96105398Stmm	char *cp;
97105398Stmm
98194138Smarius	/* Reject obvious bogus requests. */
99129587Smarius	if ((u_int)len > OFIOCMAXNAME)
100105398Stmm		return (ENAMETOOLONG);
101105398Stmm
102111119Simp	*cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
103105398Stmm	if (cp == NULL)
104105398Stmm		return (ENOMEM);
105105398Stmm	error = copyin(user, cp, len);
106105398Stmm	cp[len] = '\0';
107105398Stmm	return (error);
108105398Stmm}
109105398Stmm
110105398Stmmint
111130585Sphkopenfirm_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
112105398Stmm    struct thread *td)
113105398Stmm{
114105398Stmm	struct ofiocdesc *of;
115105398Stmm	phandle_t node;
116105398Stmm	int len, ok, error;
117105398Stmm	char *name, *value;
118105398Stmm	char newname[32];
119105398Stmm
120116210Stmm	if ((flags & FREAD) == 0)
121116210Stmm		return (EBADF);
122116210Stmm
123105398Stmm	of = (struct ofiocdesc *)data;
124105398Stmm	switch (cmd) {
125105398Stmm	case OFIOCGETOPTNODE:
126105398Stmm		*(phandle_t *) data = OF_finddevice("/options");
127105398Stmm		return (0);
128105398Stmm	case OFIOCGET:
129105398Stmm	case OFIOCSET:
130105398Stmm	case OFIOCNEXTPROP:
131105398Stmm	case OFIOCFINDDEVICE:
132116210Stmm	case OFIOCGETPROPLEN:
133105398Stmm		node = of->of_nodeid;
134105398Stmm		break;
135105398Stmm	case OFIOCGETNEXT:
136105398Stmm	case OFIOCGETCHILD:
137105398Stmm		node = *(phandle_t *)data;
138105398Stmm		break;
139105398Stmm	default:
140116210Stmm		return (ENOIOCTL);
141105398Stmm	}
142105398Stmm
143105398Stmm	if (node != 0 && node != lastnode) {
144194138Smarius		/* Not an easy one, we must search for it. */
145105398Stmm		ok = openfirm_checkid(OF_peer(0), node);
146105398Stmm		if (!ok)
147105398Stmm			return (EINVAL);
148105398Stmm		lastnode = node;
149105398Stmm	}
150105398Stmm
151105398Stmm	name = value = NULL;
152105398Stmm	error = 0;
153105398Stmm	switch (cmd) {
154105398Stmm
155105398Stmm	case OFIOCGET:
156116210Stmm	case OFIOCGETPROPLEN:
157105398Stmm		if (node == 0)
158105398Stmm			return (EINVAL);
159105398Stmm		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
160105398Stmm		if (error)
161105398Stmm			break;
162105398Stmm		len = OF_getproplen(node, name);
163116210Stmm		if (cmd == OFIOCGETPROPLEN) {
164116210Stmm			of->of_buflen = len;
165116210Stmm			break;
166116210Stmm		}
167105398Stmm		if (len > of->of_buflen) {
168105398Stmm			error = ENOMEM;
169105398Stmm			break;
170105398Stmm		}
171105398Stmm		of->of_buflen = len;
172194138Smarius		/* -1 means no entry; 0 means no value. */
173105398Stmm		if (len <= 0)
174105398Stmm			break;
175111119Simp		value = malloc(len, M_TEMP, M_WAITOK);
176105398Stmm		if (value == NULL) {
177105398Stmm			error = ENOMEM;
178105398Stmm			break;
179105398Stmm		}
180105398Stmm		len = OF_getprop(node, name, (void *)value, len);
181105398Stmm		error = copyout(value, of->of_buf, len);
182105398Stmm		break;
183105398Stmm
184105398Stmm	case OFIOCSET:
185129587Smarius		/*
186129587Smarius		 * Note: Text string values for at least the /options node
187161836Smarius		 * have to be null-terminated and the length parameter must
188194138Smarius		 * include this terminating null.  However, like OF_getprop(),
189161836Smarius		 * OF_setprop() will return the actual length of the text
190129587Smarius		 * string, i.e. omitting the terminating null.
191129587Smarius		 */
192105398Stmm		if ((flags & FWRITE) == 0)
193105398Stmm			return (EBADF);
194105398Stmm		if (node == 0)
195105398Stmm			return (EINVAL);
196129587Smarius		if ((u_int)of->of_buflen > OFIOCMAXVALUE)
197129587Smarius			return (ENAMETOOLONG);
198105398Stmm		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
199105398Stmm		if (error)
200105398Stmm			break;
201129587Smarius		value = malloc(of->of_buflen, M_TEMP, M_WAITOK);
202129587Smarius		if (value == NULL) {
203129587Smarius			error = ENOMEM;
204129587Smarius			break;
205129587Smarius		}
206129587Smarius		error = copyin(of->of_buf, value, of->of_buflen);
207105398Stmm		if (error)
208105398Stmm			break;
209105398Stmm		len = OF_setprop(node, name, value, of->of_buflen);
210129587Smarius		if (len < 0)
211105398Stmm			error = EINVAL;
212129587Smarius		of->of_buflen = len;
213105398Stmm		break;
214105398Stmm
215105398Stmm	case OFIOCNEXTPROP:
216105398Stmm		if (node == 0 || of->of_buflen < 0)
217105398Stmm			return (EINVAL);
218105398Stmm		if (of->of_namelen != 0) {
219105398Stmm			error = openfirm_getstr(of->of_namelen, of->of_name,
220105398Stmm			    &name);
221105398Stmm			if (error)
222105398Stmm				break;
223105398Stmm		}
224186347Snwhitehorn		ok = OF_nextprop(node, name, newname, sizeof(newname));
225105398Stmm		if (ok == 0) {
226105398Stmm			error = ENOENT;
227105398Stmm			break;
228105398Stmm		}
229105398Stmm		if (ok == -1) {
230105398Stmm			error = EINVAL;
231105398Stmm			break;
232105398Stmm		}
233105398Stmm		len = strlen(newname) + 1;
234105398Stmm		if (len > of->of_buflen)
235105398Stmm			len = of->of_buflen;
236105398Stmm		else
237105398Stmm			of->of_buflen = len;
238105398Stmm		error = copyout(newname, of->of_buf, len);
239105398Stmm		break;
240105398Stmm
241105398Stmm	case OFIOCGETNEXT:
242105398Stmm		node = OF_peer(node);
243105398Stmm		*(phandle_t *)data = lastnode = node;
244105398Stmm		break;
245105398Stmm
246105398Stmm	case OFIOCGETCHILD:
247105398Stmm		if (node == 0)
248105398Stmm			return (EINVAL);
249105398Stmm		node = OF_child(node);
250105398Stmm		*(phandle_t *)data = lastnode = node;
251105398Stmm		break;
252105398Stmm
253105398Stmm	case OFIOCFINDDEVICE:
254105398Stmm		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
255105398Stmm		if (error)
256105398Stmm			break;
257105398Stmm		node = OF_finddevice(name);
258105398Stmm		if (node == 0 || node == -1) {
259105398Stmm			error = ENOENT;
260105398Stmm			break;
261105398Stmm		}
262105398Stmm		of->of_nodeid = lastnode = node;
263105398Stmm		break;
264105398Stmm	}
265105398Stmm
266105398Stmm	if (name != NULL)
267105398Stmm		free(name, M_TEMP);
268105398Stmm	if (value != NULL)
269105398Stmm		free(value, M_TEMP);
270105398Stmm
271105398Stmm	return (error);
272105398Stmm}
273105398Stmm
274105398Stmmstatic int
275105398Stmmopenfirm_modevent(module_t mod, int type, void *data)
276105398Stmm{
277194138Smarius
278105398Stmm	switch(type) {
279105398Stmm	case MOD_LOAD:
280105398Stmm		if (bootverbose)
281133862Smarius			printf("openfirm: <Open Firmware control device>\n");
282105398Stmm		/*
283105398Stmm		 * Allow only root access by default; this device may allow
284105398Stmm		 * users to peek into firmware passwords, and likely to crash
285105398Stmm		 * the machine on some boxen due to firmware quirks.
286105398Stmm		 */
287105398Stmm		openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR,
288105398Stmm		    UID_ROOT, GID_WHEEL, 0600, "openfirm");
289105398Stmm		return 0;
290105398Stmm
291105398Stmm	case MOD_UNLOAD:
292105398Stmm		destroy_dev(openfirm_dev);
293105398Stmm		return 0;
294105398Stmm
295105398Stmm	case MOD_SHUTDOWN:
296105398Stmm		return 0;
297105398Stmm
298105398Stmm	default:
299105398Stmm		return EOPNOTSUPP;
300105398Stmm	}
301105398Stmm}
302105398Stmm
303105398StmmDEV_MODULE(openfirm, openfirm_modevent, NULL);
304