openfirmio.c revision 1.13 1 /* $NetBSD: openfirmio.c,v 1.13 2014/07/25 08:10:37 dholland Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
10 *
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 * This product includes software developed by the University of
14 * California, Lawrence Berkeley Laboratory.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * @(#)openfirm.c 8.1 (Berkeley) 6/11/93
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: openfirmio.c,v 1.13 2014/07/25 08:10:37 dholland Exp $");
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/errno.h>
49 #include <sys/fcntl.h>
50 #include <sys/ioctl.h>
51 #include <sys/malloc.h>
52 #include <sys/conf.h>
53 #include <sys/device.h>
54 #include <sys/event.h>
55
56 #include <dev/ofw/openfirm.h>
57 #include <dev/ofw/openfirmio.h>
58
59 static int lastnode; /* speed hack */
60
61 static int openfirmcheckid (int, int);
62 static int openfirmgetstr (int, char *, char **);
63
64 void openfirmattach (int);
65
66 dev_type_ioctl(openfirmioctl);
67
68 const struct cdevsw openfirm_cdevsw = {
69 .d_open = nullopen,
70 .d_close = nullclose,
71 .d_read = noread,
72 .d_write = nowrite,
73 .d_ioctl = openfirmioctl,
74 .d_stop = nostop,
75 .d_tty = notty,
76 .d_poll = nopoll,
77 .d_mmap = nommap,
78 .d_kqfilter = nokqfilter,
79 .d_discard = nodiscard,
80 .d_flag = 0
81 };
82
83 void
84 openfirmattach(int num)
85 {
86 /* nothing */
87 }
88
89 /*
90 * Verify target ID is valid (exists in the OPENPROM tree), as
91 * listed from node ID sid forward.
92 */
93 static int
94 openfirmcheckid(int sid, int tid)
95 {
96
97 for (; sid != 0; sid = OF_peer(sid))
98 if (sid == tid || openfirmcheckid(OF_child(sid), tid))
99 return (1);
100
101 return (0);
102 }
103
104 static int
105 openfirmgetstr(int len, char *user, char **cpp)
106 {
107 int error;
108 char *cp;
109
110 /* Reject obvious bogus requests */
111 if ((u_int)len > (8 * 1024) - 1)
112 return (ENAMETOOLONG);
113
114 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
115 error = copyin(user, cp, len);
116 cp[len] = '\0';
117 return (error);
118 }
119
120 int
121 openfirmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
122 {
123 struct ofiocdesc *of;
124 int node, len, ok, error, s;
125 char *name, *value;
126
127 if (cmd == OFIOCGETOPTNODE) {
128 s = splhigh();
129 *(int *) data = OF_finddevice("/options");
130 splx(s);
131 return (0);
132 }
133
134 /* Verify node id */
135 of = (struct ofiocdesc *)data;
136 node = of->of_nodeid;
137 if (node != 0 && node != lastnode) {
138 /* Not an easy one, must search for it */
139 s = splhigh();
140 ok = openfirmcheckid(OF_peer(0), node);
141 splx(s);
142 if (!ok)
143 return (EINVAL);
144 lastnode = node;
145 }
146
147 name = value = NULL;
148 error = 0;
149 switch (cmd) {
150
151 case OFIOCGET:
152 if ((flags & FREAD) == 0)
153 return (EBADF);
154 if (node == 0)
155 return (EINVAL);
156 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
157 if (error)
158 break;
159 s = splhigh();
160 len = OF_getproplen(node, name);
161 splx(s);
162 if (len > of->of_buflen) {
163 error = ENOMEM;
164 break;
165 }
166 of->of_buflen = len;
167 /* -1 means no entry; 0 means no value */
168 if (len <= 0)
169 break;
170 value = malloc(len, M_TEMP, M_WAITOK);
171 if (value == NULL) {
172 error = ENOMEM;
173 break;
174 }
175 s = splhigh();
176 len = OF_getprop(node, name, (void *)value, len);
177 splx(s);
178 error = copyout(value, of->of_buf, len);
179 break;
180
181
182 case OFIOCSET:
183 if ((flags & FWRITE) == 0)
184 return (EBADF);
185 if (node == 0)
186 return (EINVAL);
187 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
188 if (error)
189 break;
190 error = openfirmgetstr(of->of_buflen, of->of_buf, &value);
191 if (error)
192 break;
193 s = splhigh();
194 len = OF_setprop(node, name, value, of->of_buflen + 1);
195 splx(s);
196
197 /*
198 * XXX
199 * some OF implementations return the buffer length including
200 * the trailing zero ( like macppc ) and some without ( like
201 * FirmWorks OF used in Shark )
202 */
203 if ((len != (of->of_buflen + 1)) && (len != of->of_buflen))
204 error = EINVAL;
205 break;
206
207 case OFIOCNEXTPROP: {
208 char newname[32];
209 if ((flags & FREAD) == 0)
210 return (EBADF);
211 if (node == 0)
212 return (EINVAL);
213 if (of->of_namelen != 0) {
214 error = openfirmgetstr(of->of_namelen, of->of_name,
215 &name);
216 if (error)
217 break;
218 }
219 s = splhigh();
220 ok = OF_nextprop(node, name, newname);
221 splx(s);
222 if (ok == 0) {
223 error = ENOENT;
224 break;
225 }
226 if (ok == -1) {
227 error = EINVAL;
228 break;
229 }
230 len = strlen(newname);
231 if (len > of->of_buflen)
232 len = of->of_buflen;
233 else
234 of->of_buflen = len;
235 error = copyout(newname, of->of_buf, len);
236 break;
237 }
238
239 case OFIOCGETNEXT:
240 if ((flags & FREAD) == 0)
241 return (EBADF);
242 s = splhigh();
243 node = OF_peer(node);
244 splx(s);
245 *(int *)data = lastnode = node;
246 break;
247
248 case OFIOCGETCHILD:
249 if ((flags & FREAD) == 0)
250 return (EBADF);
251 if (node == 0)
252 return (EINVAL);
253 s = splhigh();
254 node = OF_child(node);
255 splx(s);
256 *(int *)data = lastnode = node;
257 break;
258
259 case OFIOCFINDDEVICE:
260 if ((flags & FREAD) == 0)
261 return (EBADF);
262 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
263 if (error)
264 break;
265 node = OF_finddevice(name);
266 if (node == 0 || node == -1) {
267 error = ENOENT;
268 break;
269 }
270 of->of_nodeid = lastnode = node;
271 break;
272
273 default:
274 return (ENOTTY);
275 }
276
277 if (name)
278 free(name, M_TEMP);
279 if (value)
280 free(value, M_TEMP);
281
282 return (error);
283 }
284