1 /* $NetBSD: openfirmio.c,v 1.15 2019/12/06 06:38:39 mrg 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.15 2019/12/06 06:38:39 mrg 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
openfirmattach(int num)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
openfirmcheckid(int sid,int tid)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
openfirmgetstr(int len,char * user,char ** cpp)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
openfirmopen(dev_t dev,int flag,int mode,struct lwp * l)122 openfirmopen(dev_t dev, int flag, int mode, struct lwp *l)
123 {
124
125 return 0;
126 }
127
128 static int
openfirmioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)129 openfirmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
130 {
131 struct ofiocdesc *of;
132 int node, len, ok, error, s;
133 char *name, *value;
134
135 if (cmd == OFIOCGETOPTNODE) {
136 s = splhigh();
137 *(int *) data = OF_finddevice("/options");
138 splx(s);
139 return (0);
140 }
141
142 /* Verify node id */
143 of = (struct ofiocdesc *)data;
144 node = of->of_nodeid;
145 if (node != 0 && node != lastnode) {
146 /* Not an easy one, must search for it */
147 s = splhigh();
148 ok = openfirmcheckid(OF_peer(0), node);
149 splx(s);
150 if (!ok)
151 return (EINVAL);
152 lastnode = node;
153 }
154
155 name = value = NULL;
156 error = 0;
157 switch (cmd) {
158
159 case OFIOCGET:
160 if ((flags & FREAD) == 0)
161 return (EBADF);
162 if (node == 0)
163 return (EINVAL);
164 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
165 if (error)
166 break;
167 s = splhigh();
168 len = OF_getproplen(node, name);
169 splx(s);
170 if (len > of->of_buflen) {
171 error = ENOMEM;
172 break;
173 }
174 of->of_buflen = len;
175 /* -1 means no entry; 0 means no value */
176 if (len <= 0)
177 break;
178 value = malloc(len, M_TEMP, M_WAITOK);
179 if (value == NULL) {
180 error = ENOMEM;
181 break;
182 }
183 s = splhigh();
184 len = OF_getprop(node, name, (void *)value, len);
185 splx(s);
186 error = copyout(value, of->of_buf, len);
187 break;
188
189
190 case OFIOCSET:
191 if ((flags & FWRITE) == 0)
192 return (EBADF);
193 if (node == 0)
194 return (EINVAL);
195 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
196 if (error)
197 break;
198 error = openfirmgetstr(of->of_buflen, of->of_buf, &value);
199 if (error)
200 break;
201 s = splhigh();
202 len = OF_setprop(node, name, value, of->of_buflen + 1);
203 splx(s);
204
205 /*
206 * XXX
207 * some OF implementations return the buffer length including
208 * the trailing zero ( like macppc ) and some without ( like
209 * FirmWorks OF used in Shark )
210 */
211 if ((len != (of->of_buflen + 1)) && (len != of->of_buflen))
212 error = EINVAL;
213 break;
214
215 case OFIOCNEXTPROP: {
216 char newname[32];
217 if ((flags & FREAD) == 0)
218 return (EBADF);
219 if (node == 0)
220 return (EINVAL);
221 if (of->of_namelen != 0) {
222 error = openfirmgetstr(of->of_namelen, of->of_name,
223 &name);
224 if (error)
225 break;
226 }
227 s = splhigh();
228 ok = OF_nextprop(node, name, newname);
229 splx(s);
230 if (ok == 0) {
231 error = ENOENT;
232 break;
233 }
234 if (ok == -1) {
235 error = EINVAL;
236 break;
237 }
238 len = strlen(newname);
239 if (len > of->of_buflen)
240 len = of->of_buflen;
241 else
242 of->of_buflen = len;
243 error = copyout(newname, of->of_buf, len);
244 break;
245 }
246
247 case OFIOCGETNEXT:
248 if ((flags & FREAD) == 0)
249 return (EBADF);
250 s = splhigh();
251 node = OF_peer(node);
252 splx(s);
253 *(int *)data = lastnode = node;
254 break;
255
256 case OFIOCGETCHILD:
257 if ((flags & FREAD) == 0)
258 return (EBADF);
259 if (node == 0)
260 return (EINVAL);
261 s = splhigh();
262 node = OF_child(node);
263 splx(s);
264 *(int *)data = lastnode = node;
265 break;
266
267 case OFIOCFINDDEVICE:
268 if ((flags & FREAD) == 0)
269 return (EBADF);
270 error = openfirmgetstr(of->of_namelen, of->of_name, &name);
271 if (error)
272 break;
273 node = OF_finddevice(name);
274 if (node == 0 || node == -1) {
275 error = ENOENT;
276 break;
277 }
278 of->of_nodeid = lastnode = node;
279 break;
280
281 default:
282 return (ENOTTY);
283 }
284
285 if (name)
286 free(name, M_TEMP);
287 if (value)
288 free(value, M_TEMP);
289
290 return (error);
291 }
292