openfirmio.c revision 1.13.28.1 1 /* $NetBSD: openfirmio.c,v 1.13.28.1 2019/06/10 22:07:15 christos 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.28.1 2019/06/10 22:07:15 christos 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 static dev_type_open(openfirmopen);
67 static dev_type_ioctl(openfirmioctl);
68
69 const struct cdevsw openfirm_cdevsw = {
70 .d_open = openfirmopen,
71 .d_close = nullclose,
72 .d_read = noread,
73 .d_write = nowrite,
74 .d_ioctl = openfirmioctl,
75 .d_stop = nostop,
76 .d_tty = notty,
77 .d_poll = nopoll,
78 .d_mmap = nommap,
79 .d_kqfilter = nokqfilter,
80 .d_discard = nodiscard,
81 .d_flag = 0
82 };
83
84 void
85 openfirmattach(int num)
86 {
87 /* nothing */
88 }
89
90 /*
91 * Verify target ID is valid (exists in the OPENPROM tree), as
92 * listed from node ID sid forward.
93 */
94 static int
95 openfirmcheckid(int sid, int tid)
96 {
97
98 for (; sid != 0; sid = OF_peer(sid))
99 if (sid == tid || openfirmcheckid(OF_child(sid), tid))
100 return (1);
101
102 return (0);
103 }
104
105 static int
106 openfirmgetstr(int len, char *user, char **cpp)
107 {
108 int error;
109 char *cp;
110
111 /* Reject obvious bogus requests */
112 if ((u_int)len > (8 * 1024) - 1)
113 return (ENAMETOOLONG);
114
115 *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK);
116 error = copyin(user, cp, len);
117 cp[len] = '\0';
118 return (error);
119 }
120
121 static int
122 openfirmopen(dev_t dev, int flag, int mode, struct lwp *l)
123 {
124
125 #ifdef __OPENFIRMIO_OPEN_CHECK_BROKEN
126 return __openfirmio_open_check_broken();
127 #else
128 return 0;
129 #endif
130 }
131
132 static int
133 openfirmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
134 {
135 struct ofiocdesc *of;
136 int node, len, ok, error, s;
137 char *name, *value;
138
139 if (cmd == OFIOCGETOPTNODE) {
140 s = splhigh();
141 *(int *) data = OF_finddevice("/options");
142 splx(s);
143 return (0);
144 }
145
146 /* Verify node id */
147 of = (struct ofiocdesc *)data;
148 node = of->of_nodeid;
149 if (node != 0 && node != lastnode) {
150 /* Not an easy one, must search for it */
151 s = splhigh();
152 ok = openfirmcheckid(OF_peer(0), node);
153 splx(s);
154 if (!ok)
155 return (EINVAL);
156 lastnode = node;
157 }
158
159 name = value = NULL;
160 error = 0;
161 switch (cmd) {
162
163 case OFIOCGET:
164 if ((flags & FREAD) == 0)
165 return (EBADF);
166 if (node == 0)
167 return (EINVAL);
168 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
169 if (error)
170 break;
171 s = splhigh();
172 len = OF_getproplen(node, name);
173 splx(s);
174 if (len > of->of_buflen) {
175 error = ENOMEM;
176 break;
177 }
178 of->of_buflen = len;
179 /* -1 means no entry; 0 means no value */
180 if (len <= 0)
181 break;
182 value = malloc(len, M_TEMP, M_WAITOK);
183 if (value == NULL) {
184 error = ENOMEM;
185 break;
186 }
187 s = splhigh();
188 len = OF_getprop(node, name, (void *)value, len);
189 splx(s);
190 error = copyout(value, of->of_buf, len);
191 break;
192
193
194 case OFIOCSET:
195 if ((flags & FWRITE) == 0)
196 return (EBADF);
197 if (node == 0)
198 return (EINVAL);
199 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
200 if (error)
201 break;
202 error = openfirmgetstr(of->of_buflen, of->of_buf, &value);
203 if (error)
204 break;
205 s = splhigh();
206 len = OF_setprop(node, name, value, of->of_buflen + 1);
207 splx(s);
208
209 /*
210 * XXX
211 * some OF implementations return the buffer length including
212 * the trailing zero ( like macppc ) and some without ( like
213 * FirmWorks OF used in Shark )
214 */
215 if ((len != (of->of_buflen + 1)) && (len != of->of_buflen))
216 error = EINVAL;
217 break;
218
219 case OFIOCNEXTPROP: {
220 char newname[32];
221 if ((flags & FREAD) == 0)
222 return (EBADF);
223 if (node == 0)
224 return (EINVAL);
225 if (of->of_namelen != 0) {
226 error = openfirmgetstr(of->of_namelen, of->of_name,
227 &name);
228 if (error)
229 break;
230 }
231 s = splhigh();
232 ok = OF_nextprop(node, name, newname);
233 splx(s);
234 if (ok == 0) {
235 error = ENOENT;
236 break;
237 }
238 if (ok == -1) {
239 error = EINVAL;
240 break;
241 }
242 len = strlen(newname);
243 if (len > of->of_buflen)
244 len = of->of_buflen;
245 else
246 of->of_buflen = len;
247 error = copyout(newname, of->of_buf, len);
248 break;
249 }
250
251 case OFIOCGETNEXT:
252 if ((flags & FREAD) == 0)
253 return (EBADF);
254 s = splhigh();
255 node = OF_peer(node);
256 splx(s);
257 *(int *)data = lastnode = node;
258 break;
259
260 case OFIOCGETCHILD:
261 if ((flags & FREAD) == 0)
262 return (EBADF);
263 if (node == 0)
264 return (EINVAL);
265 s = splhigh();
266 node = OF_child(node);
267 splx(s);
268 *(int *)data = lastnode = node;
269 break;
270
271 case OFIOCFINDDEVICE:
272 if ((flags & FREAD) == 0)
273 return (EBADF);
274 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
275 if (error)
276 break;
277 node = OF_finddevice(name);
278 if (node == 0 || node == -1) {
279 error = ENOENT;
280 break;
281 }
282 of->of_nodeid = lastnode = node;
283 break;
284
285 default:
286 return (ENOTTY);
287 }
288
289 if (name)
290 free(name, M_TEMP);
291 if (value)
292 free(value, M_TEMP);
293
294 return (error);
295 }
296