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 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 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 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 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 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 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 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 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 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 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