1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 #include <stdarg.h>
5
6 #include <sys/ioctl.h>
7 #include <minix/i2c.h>
8 #include <string.h>
9 #include <sys/ioccom.h>
10 #include <stdarg.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <minix/if.h>
14 #include <minix/bpf.h>
15 #include <assert.h>
16
17 static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
18 i2c_ioctl_exec_t *in);
19 static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
20 minix_i2c_ioctl_exec_t *in);
21
rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t * out,i2c_ioctl_exec_t * in)22 static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
23 i2c_ioctl_exec_t *in)
24 {
25 memset(out, '\0', sizeof(minix_i2c_ioctl_exec_t));
26
27 out->iie_op = in->iie_op;
28 out->iie_addr = in->iie_addr;
29 out->iie_cmdlen = I2C_EXEC_MAX_CMDLEN < in->iie_cmdlen ?
30 I2C_EXEC_MAX_CMDLEN : in->iie_cmdlen;
31 out->iie_buflen = I2C_EXEC_MAX_BUFLEN < in->iie_buflen ?
32 I2C_EXEC_MAX_BUFLEN : in->iie_buflen;
33
34 if (in->iie_cmdlen > 0 && in->iie_cmd != NULL) {
35 memcpy(out->iie_cmd, in->iie_cmd, in->iie_cmdlen);
36 }
37
38 if (in->iie_buflen > 0 && in->iie_buf != NULL) {
39 memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
40 }
41 }
42
rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t * out,minix_i2c_ioctl_exec_t * in)43 static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
44 minix_i2c_ioctl_exec_t *in)
45 {
46 /* the only field that changes is iie_buf, everything else is the same */
47 if (in->iie_buflen > 0 ) {
48 memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
49 }
50 }
51
52 /*
53 * Convert a network interface related IOCTL with pointers to a flat format
54 * suitable for MINIX3. Return a pointer to the new data on success, or zero
55 * (with errno set) on failure. The original request code is given in
56 * 'request' and must be replaced by the new request code to be used.
57 */
58 static vir_bytes
ioctl_convert_if_to_minix(void * data,unsigned long * request)59 ioctl_convert_if_to_minix(void * data, unsigned long * request)
60 {
61 struct minix_ifmediareq *mifm;
62 struct ifmediareq *ifm;
63 struct minix_if_clonereq *mifcr;
64 struct if_clonereq *ifcr;
65
66 switch (*request) {
67 case SIOCGIFMEDIA:
68 ifm = (struct ifmediareq *)data;
69
70 mifm = (struct minix_ifmediareq *)malloc(sizeof(*mifm));
71 if (mifm != NULL) {
72 /*
73 * The count may exceed MINIX_IF_MAXMEDIA, and should
74 * be truncated as needed by the IF implementation.
75 */
76 memcpy(&mifm->mifm_ifm, ifm, sizeof(*ifm));
77
78 *request = MINIX_SIOCGIFMEDIA;
79 } else
80 errno = ENOMEM;
81
82 return (vir_bytes)mifm;
83
84 case SIOCIFGCLONERS:
85 ifcr = (struct if_clonereq *)data;
86
87 mifcr = (struct minix_if_clonereq *)malloc(sizeof(*mifcr));
88 if (mifcr != NULL) {
89 /*
90 * The count may exceed MINIX_IF_MAXCLONERS, and should
91 * be truncated as needed by the IF implementation.
92 */
93 memcpy(&mifcr->mifcr_ifcr, ifcr, sizeof(*ifcr));
94
95 *request = MINIX_SIOCIFGCLONERS;
96 } else
97 errno = ENOMEM;
98
99 return (vir_bytes)mifcr;
100
101 default:
102 assert(0);
103
104 errno = ENOTTY;
105 return 0;
106 }
107 }
108
109 /*
110 * Convert a the result of a network interface related IOCTL with pointers from
111 * the flat format used to make the call to MINIX3. Called on success only.
112 * The given request code is that of the (NetBSD-type) original.
113 */
114 static void
ioctl_convert_if_from_minix(vir_bytes addr,void * data,unsigned long request)115 ioctl_convert_if_from_minix(vir_bytes addr, void * data, unsigned long request)
116 {
117 struct minix_ifmediareq *mifm;
118 struct ifmediareq *ifm;
119 struct minix_if_clonereq *mifcr;
120 struct if_clonereq *ifcr;
121 int count;
122
123 switch (request) {
124 case SIOCGIFMEDIA:
125 mifm = (struct minix_ifmediareq *)addr;
126 ifm = (struct ifmediareq *)data;
127
128 memcpy(ifm, &mifm->mifm_ifm, sizeof(*ifm));
129
130 if (ifm->ifm_ulist != NULL && ifm->ifm_count > 0)
131 memcpy(ifm->ifm_ulist, mifm->mifm_list,
132 ifm->ifm_count * sizeof(ifm->ifm_ulist[0]));
133
134 break;
135
136 case SIOCIFGCLONERS:
137 mifcr = (struct minix_if_clonereq *)addr;
138 ifcr = (struct if_clonereq *)data;
139
140 memcpy(ifcr, &mifcr->mifcr_ifcr, sizeof(*ifcr));
141
142 count = (ifcr->ifcr_count < ifcr->ifcr_total) ?
143 ifcr->ifcr_count : ifcr->ifcr_total;
144 if (ifcr->ifcr_buffer != NULL && count > 0)
145 memcpy(ifcr->ifcr_buffer, mifcr->mifcr_buffer,
146 count * IFNAMSIZ);
147
148 break;
149
150 default:
151 assert(0);
152 }
153 }
154
155 /*
156 * Convert a BPF (Berkeley Packet Filter) related IOCTL with pointers to a flat
157 * format suitable for MINIX3. Return a pointer to the new data on success, or
158 * zero (with errno set) on failure. The original request code is given in
159 * 'request' and must be replaced by the new request code to be used.
160 */
161 static vir_bytes
ioctl_convert_bpf_to_minix(void * data,unsigned long * request)162 ioctl_convert_bpf_to_minix(void * data, unsigned long * request)
163 {
164 struct minix_bpf_program *mbf;
165 struct bpf_program *bf;
166 struct minix_bpf_dltlist *mbfl;
167 struct bpf_dltlist *bfl;
168
169 switch (*request) {
170 case BIOCSETF:
171 bf = (struct bpf_program *)data;
172
173 if (bf->bf_len > __arraycount(mbf->mbf_insns)) {
174 errno = EINVAL;
175 return 0;
176 }
177
178 mbf = (struct minix_bpf_program *)malloc(sizeof(*mbf));
179 if (mbf != NULL) {
180 mbf->mbf_len = bf->bf_len;
181 memcpy(mbf->mbf_insns, bf->bf_insns,
182 bf->bf_len * sizeof(mbf->mbf_insns[0]));
183
184 *request = MINIX_BIOCSETF;
185 } else
186 errno = ENOMEM;
187
188 return (vir_bytes)mbf;
189
190 case BIOCGDLTLIST:
191 bfl = (struct bpf_dltlist *)data;
192
193 mbfl = (struct minix_bpf_dltlist *)malloc(sizeof(*mbfl));
194 if (mbfl != NULL) {
195 /*
196 * The length may exceed MINIX_BPF_MAXDLT, and should
197 * be truncated as needed by the BPF implementation.
198 */
199 memcpy(&mbfl->mbfl_dltlist, bfl, sizeof(*bfl));
200
201 *request = MINIX_BIOCGDLTLIST;
202 } else
203 errno = ENOMEM;
204
205 return (vir_bytes)mbfl;
206
207 default:
208 assert(0);
209
210 errno = ENOTTY;
211 return 0;
212 }
213 }
214
215 /*
216 * Convert a the result of BPF (Berkeley Packet Filter) related IOCTL with
217 * pointers from the flat format used to make the call to MINIX3. Called on
218 * success only. The given request code is that of the (NetBSD-type) original.
219 */
220 static void
ioctl_convert_bpf_from_minix(vir_bytes addr,void * data,unsigned long request)221 ioctl_convert_bpf_from_minix(vir_bytes addr, void * data,
222 unsigned long request)
223 {
224 struct minix_bpf_dltlist *mbfl;
225 struct bpf_dltlist *bfl;
226
227 switch (request) {
228 case BIOCGDLTLIST:
229 mbfl = (struct minix_bpf_dltlist *)addr;
230 bfl = (struct bpf_dltlist *)data;
231
232 memcpy(bfl, &mbfl->mbfl_dltlist, sizeof(*bfl));
233
234 if (bfl->bfl_list != NULL && bfl->bfl_len > 0)
235 memcpy(bfl->bfl_list, mbfl->mbfl_list,
236 bfl->bfl_len * sizeof(bfl->bfl_list[0]));
237
238 break;
239
240 default:
241 assert(0);
242 }
243 }
244
245 /*
246 * Library implementation of FIOCLEX and FIONCLEX.
247 */
248 static int
ioctl_to_setfd(int fd,int mask,int val)249 ioctl_to_setfd(int fd, int mask, int val)
250 {
251 int fl;
252
253 if ((fl = fcntl(fd, F_GETFD)) == -1)
254 return -1;
255
256 fl = (fl & ~mask) | val;
257
258 return fcntl(fd, F_SETFD, fl);
259 }
260
261 /*
262 * Library implementation of FIONBIO and FIOASYNC.
263 */
264 static int
ioctl_to_setfl(int fd,void * data,int sfl)265 ioctl_to_setfl(int fd, void * data, int sfl)
266 {
267 int arg, fl;
268
269 arg = *(int *)data;
270
271 if ((fl = fcntl(fd, F_GETFL)) == -1)
272 return -1;
273
274 if (arg)
275 fl |= sfl;
276 else
277 fl &= ~sfl;
278
279 return fcntl(fd, F_SETFL, fl & ~O_ACCMODE);
280 }
281
282 /*
283 * Library implementation of various deprecated IOCTLs. These particular IOCTL
284 * calls change how the file descriptors behave, and have nothing to do with
285 * the actual open file. They should therefore be handled by VFS rather than
286 * individual device drivers. We rewrite them to use fcntl(2) instead here.
287 */
288 static int
ioctl_to_fcntl(int fd,unsigned long request,void * data)289 ioctl_to_fcntl(int fd, unsigned long request, void * data)
290 {
291 switch (request) {
292 case FIOCLEX:
293 return ioctl_to_setfd(fd, FD_CLOEXEC, FD_CLOEXEC);
294 case FIONCLEX:
295 return ioctl_to_setfd(fd, FD_CLOEXEC, 0);
296 case FIONBIO:
297 return ioctl_to_setfl(fd, data, O_NONBLOCK);
298 case FIOASYNC:
299 return ioctl_to_setfl(fd, data, O_ASYNC);
300 case FIOSETOWN: /* XXX TODO */
301 case FIOGETOWN: /* XXX TODO */
302 default:
303 errno = ENOTTY;
304 return -1;
305 }
306 }
307
ioctl(int fd,unsigned long request,...)308 int ioctl(int fd, unsigned long request, ...)
309 {
310 minix_i2c_ioctl_exec_t i2c;
311 int r, request_save;
312 message m;
313 vir_bytes addr;
314 void *data;
315 va_list ap;
316
317 va_start(ap, request);
318 data = va_arg(ap, void *);
319 va_end(ap);
320
321 /*
322 * To support compatibility with interfaces on other systems, certain
323 * requests are re-written to flat structures (i.e. without pointers).
324 */
325 request_save = request;
326
327 switch (request) {
328 case FIOCLEX:
329 case FIONCLEX:
330 case FIONBIO:
331 case FIOASYNC:
332 case FIOSETOWN:
333 case FIOGETOWN:
334 return ioctl_to_fcntl(fd, request, data);
335
336 case I2C_IOCTL_EXEC:
337 rewrite_i2c_netbsd_to_minix(&i2c, data);
338 addr = (vir_bytes) &i2c;
339 request = MINIX_I2C_IOCTL_EXEC;
340 break;
341
342 case SIOCGIFMEDIA:
343 case SIOCIFGCLONERS:
344 if ((addr = ioctl_convert_if_to_minix(data, &request)) == 0)
345 return -1; /* errno has already been set */
346 break;
347
348 case BIOCSETF:
349 case BIOCGDLTLIST:
350 if ((addr = ioctl_convert_bpf_to_minix(data, &request)) == 0)
351 return -1; /* errno has already been set */
352 break;
353
354 default:
355 /* Keep original as-is */
356 addr = (vir_bytes)data;
357 break;
358 }
359
360 memset(&m, 0, sizeof(m));
361 m.m_lc_vfs_ioctl.fd = fd;
362 m.m_lc_vfs_ioctl.req = request;
363 m.m_lc_vfs_ioctl.arg = addr;
364
365 r = _syscall(VFS_PROC_NR, VFS_IOCTL, &m);
366
367 /*
368 * Translate back to original form. Do this on failure as well, as
369 * temporarily allocated resources may have to be freed up again.
370 */
371 switch (request_save) {
372 case I2C_IOCTL_EXEC:
373 rewrite_i2c_minix_to_netbsd(data, &i2c);
374 break;
375
376 case SIOCGIFMEDIA:
377 case SIOCIFGCLONERS:
378 if (r == 0)
379 ioctl_convert_if_from_minix(addr, data, request_save);
380 free((void *)addr);
381 break;
382
383 case BIOCGDLTLIST:
384 if (r == 0)
385 ioctl_convert_bpf_from_minix(addr, data, request_save);
386 /* FALLTHROUGH */
387 case BIOCSETF:
388 free((void *)addr);
389 break;
390
391 default:
392 /* Nothing to do */
393 break;
394 }
395
396 return r;
397 }
398