openpromio.c revision 331722
1/*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/11/sys/dev/ofw/openpromio.c 331722 2018-03-29 02:50:57Z eadler $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/conf.h>
34#include <sys/errno.h>
35#include <sys/fcntl.h>
36#include <sys/ioccom.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/module.h>
40
41#include <dev/ofw/openfirm.h>
42#include <dev/ofw/openpromio.h>
43
44/*
45 * This provides a Solaris compatible character device interface to
46 * Open Firmware.  It exists entirely for compatibility with software
47 * like X11, and only the features that are actually needed for that
48 * are implemented.  The interface sucks too much to actually use,
49 * new code should use the /dev/openfirm device.
50 */
51
52static d_open_t openprom_open;
53static d_close_t openprom_close;
54static d_ioctl_t openprom_ioctl;
55
56static int openprom_modevent(module_t mode, int type, void *data);
57static int openprom_node_valid(phandle_t node);
58static int openprom_node_search(phandle_t root, phandle_t node);
59
60static struct cdevsw openprom_cdevsw = {
61	.d_version =	D_VERSION,
62	.d_flags =	D_NEEDGIANT,
63	.d_open =	openprom_open,
64	.d_close =	openprom_close,
65	.d_ioctl =	openprom_ioctl,
66	.d_name =	"openprom",
67};
68
69static int openprom_is_open;
70static struct cdev *openprom_dev;
71static phandle_t openprom_node;
72
73static int
74openprom_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
75{
76
77	if (openprom_is_open != 0)
78		return (EBUSY);
79	openprom_is_open = 1;
80	return (0);
81}
82
83static int
84openprom_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
85{
86
87	openprom_is_open = 0;
88	return (0);
89}
90
91static int
92openprom_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags,
93    struct thread *td)
94{
95	struct openpromio *oprom;
96	phandle_t node;
97	uint32_t len;
98	size_t done;
99	int proplen;
100	char *prop;
101	char *buf;
102	int error;
103
104	if ((flags & FREAD) == 0)
105		return (EPERM);
106
107	prop = buf = NULL;
108	error = 0;
109	switch (cmd) {
110	case OPROMCHILD:
111	case OPROMNEXT:
112		if (data == NULL || *(void **)data == NULL)
113			return (EINVAL);
114		oprom = *(void **)data;
115		error = copyin(&oprom->oprom_size, &len, sizeof(len));
116		if (error != 0)
117			break;
118		if (len != sizeof(node)) {
119			error = EINVAL;
120			break;
121		}
122		error = copyin(&oprom->oprom_array, &node, sizeof(node));
123		if (error != 0)
124			break;
125		error = openprom_node_valid(node);
126		if (error != 0)
127			break;
128		switch (cmd) {
129		case OPROMCHILD:
130			node = OF_child(node);
131			break;
132		case OPROMNEXT:
133			node = OF_peer(node);
134			break;
135		}
136		error = copyout(&node, &oprom->oprom_array, sizeof(node));
137		if (error != 0)
138			break;
139		openprom_node = node;
140		break;
141	case OPROMGETPROP:
142	case OPROMNXTPROP:
143		if (data == NULL || *(void **)data == NULL)
144			return (EINVAL);
145		oprom = *(void **)data;
146		error = copyin(&oprom->oprom_size, &len, sizeof(len));
147		if (error != 0)
148			break;
149		if (len > OPROMMAXPARAM) {
150			error = EINVAL;
151			break;
152		}
153		prop = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
154		if (prop == NULL) {
155			error = ENOMEM;
156			break;
157		}
158		error = copyinstr(&oprom->oprom_array, prop, len, &done);
159		if (error != 0)
160			break;
161		buf = malloc(OPROMMAXPARAM, M_TEMP, M_WAITOK | M_ZERO);
162		if (buf == NULL) {
163			error = ENOMEM;
164			break;
165		}
166		node = openprom_node;
167		switch (cmd) {
168		case OPROMGETPROP:
169			proplen = OF_getproplen(node, prop);
170			if (proplen > OPROMMAXPARAM) {
171				error = EINVAL;
172				break;
173			}
174			error = OF_getprop(node, prop, buf, proplen);
175			break;
176		case OPROMNXTPROP:
177			error = OF_nextprop(node, prop, buf, OPROMMAXPARAM);
178			proplen = strlen(buf);
179			break;
180		}
181		if (error != -1) {
182			error = copyout(&proplen, &oprom->oprom_size,
183			    sizeof(proplen));
184			if (error == 0)
185				error = copyout(buf, &oprom->oprom_array,
186				    proplen + 1);
187		} else
188			error = EINVAL;
189		break;
190	default:
191		error = ENOIOCTL;
192		break;
193	}
194
195	if (prop != NULL)
196		free(prop, M_TEMP);
197	if (buf != NULL)
198		free(buf, M_TEMP);
199
200	return (error);
201}
202
203static int
204openprom_node_valid(phandle_t node)
205{
206
207	if (node == 0)
208		return (0);
209	return (openprom_node_search(OF_peer(0), node));
210}
211
212static int
213openprom_node_search(phandle_t root, phandle_t node)
214{
215	phandle_t child;
216
217	if (root == node)
218		return (0);
219	for (child = OF_child(root); child != 0 && child != -1;
220	    child = OF_peer(child))
221		if (openprom_node_search(child, node) == 0)
222			return (0);
223	return (EINVAL);
224}
225
226static int
227openprom_modevent(module_t mode, int type, void *data)
228{
229
230	switch (type) {
231	case MOD_LOAD:
232		openprom_dev = make_dev(&openprom_cdevsw, 0, UID_ROOT,
233		    GID_WHEEL, 0600, "openprom");
234		return (0);
235	case MOD_UNLOAD:
236		destroy_dev(openprom_dev);
237		return (0);
238	default:
239		return (EOPNOTSUPP);
240	}
241}
242
243DEV_MODULE(openprom, openprom_modevent, NULL);
244