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: releng/8.1/sys/dev/ofw/openpromio.c 186347 2008-12-20 00:33:10Z nwhitehorn $");
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
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
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
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
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
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
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);
Cache object: 1df5291c4d71169e43806bbdd0ce790b
|