openfirmio.c revision 331722
11040SN/A/*	$NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna Exp $ */
23261SN/A
31040SN/A#include <sys/cdefs.h>
41040SN/A__FBSDID("$FreeBSD: stable/11/sys/dev/ofw/openfirmio.c 331722 2018-03-29 02:50:57Z eadler $");
51040SN/A
61040SN/A/*-
72362SN/A * Copyright (c) 1992, 1993
81040SN/A *	The Regents of the University of California.  All rights reserved.
92362SN/A *
101040SN/A * This software was developed by the Computer Systems Engineering group
111040SN/A * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
121040SN/A * contributed to Berkeley.
131040SN/A *
141040SN/A * All advertising materials mentioning features or use of this software
151040SN/A * must display the following acknowledgement:
161040SN/A *	This product includes software developed by the University of
171040SN/A *	California, Lawrence Berkeley Laboratory.
181040SN/A *
191040SN/A * Redistribution and use in source and binary forms, with or without
201040SN/A * modification, are permitted provided that the following conditions
212362SN/A * are met:
222362SN/A * 1. Redistributions of source code must retain the above copyright
232362SN/A *    notice, this list of conditions and the following disclaimer.
241040SN/A * 2. Redistributions in binary form must reproduce the above copyright
251040SN/A *    notice, this list of conditions and the following disclaimer in the
261040SN/A *    documentation and/or other materials provided with the distribution.
271040SN/A * 4. Neither the name of the University nor the names of its contributors
281040SN/A *    may be used to endorse or promote products derived from this software
291040SN/A *    without specific prior written permission.
301040SN/A *
311040SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
321040SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
331040SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
341040SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
351040SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
361040SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
371040SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
381040SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
391040SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
401040SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
411040SN/A * SUCH DAMAGE.
421040SN/A *
431040SN/A *	@(#)openfirm.c	8.1 (Berkeley) 6/11/93
441040SN/A *
4512745Smartin */
461040SN/A
471040SN/A#include <sys/param.h>
481040SN/A#include <sys/systm.h>
491040SN/A#include <sys/conf.h>
501040SN/A#include <sys/errno.h>
511040SN/A#include <sys/fcntl.h>
521040SN/A#include <sys/ioccom.h>
531040SN/A#include <sys/kernel.h>
541040SN/A#include <sys/malloc.h>
551040SN/A#include <sys/module.h>
561040SN/A
571040SN/A#include <dev/ofw/openfirmio.h>
581040SN/A
591040SN/Astatic struct cdev *openfirm_dev;
601040SN/A
611040SN/Astatic d_ioctl_t openfirm_ioctl;
621040SN/A
631040SN/A#define	OPENFIRM_MINOR	0
641040SN/A
651040SN/Astatic struct cdevsw openfirm_cdevsw = {
661040SN/A	.d_version =	D_VERSION,
671040SN/A	.d_flags =	D_NEEDGIANT,
681040SN/A	.d_ioctl =	openfirm_ioctl,
691040SN/A	.d_name =	"openfirm",
701040SN/A};
711040SN/A
721040SN/Astatic phandle_t lastnode;	/* speed hack */
732736SN/A
741040SN/Astatic int openfirm_checkid(phandle_t, phandle_t);
751040SN/Astatic int openfirm_getstr(int, const char *, char **);
761040SN/A
771040SN/A/*
781040SN/A * Verify target ID is valid (exists in the OPENPROM tree), as
791040SN/A * listed from node ID sid forward.
801040SN/A */
811040SN/Astatic int
821040SN/Aopenfirm_checkid(phandle_t sid, phandle_t tid)
832736SN/A{
841040SN/A
851040SN/A	for (; sid != 0; sid = OF_peer(sid))
861040SN/A		if (sid == tid || openfirm_checkid(OF_child(sid), tid))
871040SN/A			return (1);
881040SN/A
891040SN/A	return (0);
901040SN/A}
911040SN/A
921040SN/Astatic int
931040SN/Aopenfirm_getstr(int len, const char *user, char **cpp)
942736SN/A{
951040SN/A	int error;
961040SN/A	char *cp;
97
98	/* Reject obvious bogus requests. */
99	if ((u_int)len > OFIOCMAXNAME)
100		return (ENAMETOOLONG);
101
102	*cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
103	if (cp == NULL)
104		return (ENOMEM);
105	error = copyin(user, cp, len);
106	cp[len] = '\0';
107	return (error);
108}
109
110int
111openfirm_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
112    struct thread *td)
113{
114	struct ofiocdesc *of;
115	phandle_t node;
116	int len, ok, error;
117	char *name, *value;
118	char newname[32];
119
120	if ((flags & FREAD) == 0)
121		return (EBADF);
122
123	of = (struct ofiocdesc *)data;
124	switch (cmd) {
125	case OFIOCGETOPTNODE:
126		*(phandle_t *) data = OF_finddevice("/options");
127		return (0);
128	case OFIOCGET:
129	case OFIOCSET:
130	case OFIOCNEXTPROP:
131	case OFIOCFINDDEVICE:
132	case OFIOCGETPROPLEN:
133		node = of->of_nodeid;
134		break;
135	case OFIOCGETNEXT:
136	case OFIOCGETCHILD:
137		node = *(phandle_t *)data;
138		break;
139	default:
140		return (ENOIOCTL);
141	}
142
143	if (node != 0 && node != lastnode) {
144		/* Not an easy one, we must search for it. */
145		ok = openfirm_checkid(OF_peer(0), node);
146		if (!ok)
147			return (EINVAL);
148		lastnode = node;
149	}
150
151	name = value = NULL;
152	error = 0;
153	switch (cmd) {
154
155	case OFIOCGET:
156	case OFIOCGETPROPLEN:
157		if (node == 0)
158			return (EINVAL);
159		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
160		if (error)
161			break;
162		len = OF_getproplen(node, name);
163		if (cmd == OFIOCGETPROPLEN) {
164			of->of_buflen = len;
165			break;
166		}
167		if (len > of->of_buflen) {
168			error = ENOMEM;
169			break;
170		}
171		of->of_buflen = len;
172		/* -1 means no entry; 0 means no value. */
173		if (len <= 0)
174			break;
175		value = malloc(len, M_TEMP, M_WAITOK);
176		if (value == NULL) {
177			error = ENOMEM;
178			break;
179		}
180		len = OF_getprop(node, name, (void *)value, len);
181		error = copyout(value, of->of_buf, len);
182		break;
183
184	case OFIOCSET:
185		/*
186		 * Note: Text string values for at least the /options node
187		 * have to be null-terminated and the length parameter must
188		 * include this terminating null.  However, like OF_getprop(),
189		 * OF_setprop() will return the actual length of the text
190		 * string, i.e. omitting the terminating null.
191		 */
192		if ((flags & FWRITE) == 0)
193			return (EBADF);
194		if (node == 0)
195			return (EINVAL);
196		if ((u_int)of->of_buflen > OFIOCMAXVALUE)
197			return (ENAMETOOLONG);
198		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
199		if (error)
200			break;
201		value = malloc(of->of_buflen, M_TEMP, M_WAITOK);
202		if (value == NULL) {
203			error = ENOMEM;
204			break;
205		}
206		error = copyin(of->of_buf, value, of->of_buflen);
207		if (error)
208			break;
209		len = OF_setprop(node, name, value, of->of_buflen);
210		if (len < 0)
211			error = EINVAL;
212		of->of_buflen = len;
213		break;
214
215	case OFIOCNEXTPROP:
216		if (node == 0 || of->of_buflen < 0)
217			return (EINVAL);
218		if (of->of_namelen != 0) {
219			error = openfirm_getstr(of->of_namelen, of->of_name,
220			    &name);
221			if (error)
222				break;
223		}
224		ok = OF_nextprop(node, name, newname, sizeof(newname));
225		if (ok == 0) {
226			error = ENOENT;
227			break;
228		}
229		if (ok == -1) {
230			error = EINVAL;
231			break;
232		}
233		len = strlen(newname) + 1;
234		if (len > of->of_buflen)
235			len = of->of_buflen;
236		else
237			of->of_buflen = len;
238		error = copyout(newname, of->of_buf, len);
239		break;
240
241	case OFIOCGETNEXT:
242		node = OF_peer(node);
243		*(phandle_t *)data = lastnode = node;
244		break;
245
246	case OFIOCGETCHILD:
247		if (node == 0)
248			return (EINVAL);
249		node = OF_child(node);
250		*(phandle_t *)data = lastnode = node;
251		break;
252
253	case OFIOCFINDDEVICE:
254		error = openfirm_getstr(of->of_namelen, of->of_name, &name);
255		if (error)
256			break;
257		node = OF_finddevice(name);
258		if (node == 0 || node == -1) {
259			error = ENOENT;
260			break;
261		}
262		of->of_nodeid = lastnode = node;
263		break;
264	}
265
266	if (name != NULL)
267		free(name, M_TEMP);
268	if (value != NULL)
269		free(value, M_TEMP);
270
271	return (error);
272}
273
274static int
275openfirm_modevent(module_t mod, int type, void *data)
276{
277
278	switch(type) {
279	case MOD_LOAD:
280		if (bootverbose)
281			printf("openfirm: <Open Firmware control device>\n");
282		/*
283		 * Allow only root access by default; this device may allow
284		 * users to peek into firmware passwords, and likely to crash
285		 * the machine on some boxen due to firmware quirks.
286		 */
287		openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR,
288		    UID_ROOT, GID_WHEEL, 0600, "openfirm");
289		return 0;
290
291	case MOD_UNLOAD:
292		destroy_dev(openfirm_dev);
293		return 0;
294
295	case MOD_SHUTDOWN:
296		return 0;
297
298	default:
299		return EOPNOTSUPP;
300	}
301}
302
303DEV_MODULE(openfirm, openfirm_modevent, NULL);
304