xref: /trueos/sys/dev/ofw/openpromio.c (revision 7ee29ca49959a5c2b0ba8f60cc7885a1bcf12691)
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$");
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 
52 static d_open_t openprom_open;
53 static d_close_t openprom_close;
54 static d_ioctl_t openprom_ioctl;
55 
56 static int openprom_modevent(module_t mode, int type, void *data);
57 static int openprom_node_valid(phandle_t node);
58 static int openprom_node_search(phandle_t root, phandle_t node);
59 
60 static 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 
69 static int openprom_is_open;
70 static struct cdev *openprom_dev;
71 static phandle_t openprom_node;
72 
73 static int
openprom_open(struct cdev * dev,int oflags,int devtype,struct thread * td)74 openprom_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 
83 static int
openprom_close(struct cdev * dev,int fflag,int devtype,struct thread * td)84 openprom_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
85 {
86 
87 	openprom_is_open = 0;
88 	return (0);
89 }
90 
91 static int
openprom_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int flags,struct thread * td)92 openprom_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 
203 static int
openprom_node_valid(phandle_t node)204 openprom_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 
212 static int
openprom_node_search(phandle_t root,phandle_t node)213 openprom_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 
226 static int
openprom_modevent(module_t mode,int type,void * data)227 openprom_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 
243 DEV_MODULE(openprom, openprom_modevent, NULL);
244