1 /* $NetBSD: putter.c,v 1.34 2014/03/16 05:20:28 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Ulla Tuominen Foundation and the Finnish Cultural Foundation and the 8 * Research Foundation of Helsinki University of Technology 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Pass-to-Userspace TransporTER: generic kernel-user request-response 34 * transport interface. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: putter.c,v 1.34 2014/03/16 05:20:28 dholland Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/conf.h> 43 #include <sys/file.h> 44 #include <sys/filedesc.h> 45 #include <sys/kmem.h> 46 #include <sys/poll.h> 47 #include <sys/stat.h> 48 #include <sys/socketvar.h> 49 #include <sys/module.h> 50 #include <sys/kauth.h> 51 52 #include <dev/putter/putter_sys.h> 53 54 /* 55 * Device routines. These are for when /dev/putter is initially 56 * opened before it has been cloned. 57 */ 58 59 dev_type_open(puttercdopen); 60 dev_type_close(puttercdclose); 61 dev_type_ioctl(puttercdioctl); 62 63 /* dev */ 64 const struct cdevsw putter_cdevsw = { 65 .d_open = puttercdopen, 66 .d_close = puttercdclose, 67 .d_read = noread, 68 .d_write = nowrite, 69 .d_ioctl = noioctl, 70 .d_stop = nostop, 71 .d_tty = notty, 72 .d_poll = nopoll, 73 .d_mmap = nommap, 74 .d_kqfilter = nokqfilter, 75 .d_flag = D_OTHER 76 }; 77 78 /* 79 * Configuration data. 80 * 81 * This is static-size for now. Will be redone for devfs. 82 */ 83 84 #define PUTTER_CONFSIZE 16 85 86 static struct putter_config { 87 int pc_minor; 88 int (*pc_config)(int, int, int); 89 } putterconf[PUTTER_CONFSIZE]; 90 91 static int 92 putter_configure(dev_t dev, int flags, int fmt, int fd) 93 { 94 struct putter_config *pc; 95 96 /* are we the catch-all node? */ 97 if (minor(dev) == PUTTER_MINOR_WILDCARD 98 || minor(dev) == PUTTER_MINOR_COMPAT) 99 return 0; 100 101 /* nopes? try to configure us */ 102 for (pc = putterconf; pc->pc_config; pc++) 103 if (minor(dev) == pc->pc_minor) 104 return pc->pc_config(fd, flags, fmt); 105 return ENXIO; 106 } 107 108 int 109 putter_register(putter_config_fn pcfn, int minor) 110 { 111 int i; 112 113 for (i = 0; i < PUTTER_CONFSIZE; i++) 114 if (putterconf[i].pc_config == NULL) 115 break; 116 if (i == PUTTER_CONFSIZE) 117 return EBUSY; 118 119 putterconf[i].pc_minor = minor; 120 putterconf[i].pc_config = pcfn; 121 return 0; 122 } 123 124 /* 125 * putter instance structures. these are always allocated and freed 126 * from the context of the transport user. 127 */ 128 struct putter_instance { 129 pid_t pi_pid; 130 int pi_idx; 131 int pi_fd; 132 struct selinfo pi_sel; 133 134 void *pi_private; 135 struct putter_ops *pi_pop; 136 137 uint8_t *pi_curput; 138 size_t pi_curres; 139 void *pi_curopaq; 140 struct timespec pi_atime; 141 struct timespec pi_mtime; 142 struct timespec pi_btime; 143 144 TAILQ_ENTRY(putter_instance) pi_entries; 145 }; 146 #define PUTTER_EMBRYO ((void *)-1) /* before attach */ 147 #define PUTTER_DEAD ((void *)-2) /* after detach */ 148 149 static TAILQ_HEAD(, putter_instance) putter_ilist 150 = TAILQ_HEAD_INITIALIZER(putter_ilist); 151 152 static int get_pi_idx(struct putter_instance *); 153 154 #ifdef DEBUG 155 #ifndef PUTTERDEBUG 156 #define PUTTERDEBUG 157 #endif 158 #endif 159 160 #ifdef PUTTERDEBUG 161 int putterdebug = 0; 162 #define DPRINTF(x) if (putterdebug > 0) printf x 163 #define DPRINTF_VERBOSE(x) if (putterdebug > 1) printf x 164 #else 165 #define DPRINTF(x) 166 #define DPRINTF_VERBOSE(x) 167 #endif 168 169 /* 170 * public init / deinit 171 */ 172 173 /* protects both the list and the contents of the list elements */ 174 static kmutex_t pi_mtx; 175 176 void putterattach(void); 177 178 void 179 putterattach(void) 180 { 181 182 mutex_init(&pi_mtx, MUTEX_DEFAULT, IPL_NONE); 183 } 184 185 #if 0 186 void 187 putter_destroy(void) 188 { 189 190 mutex_destroy(&pi_mtx); 191 } 192 #endif 193 194 /* 195 * fd routines, for cloner 196 */ 197 static int putter_fop_read(file_t *, off_t *, struct uio *, 198 kauth_cred_t, int); 199 static int putter_fop_write(file_t *, off_t *, struct uio *, 200 kauth_cred_t, int); 201 static int putter_fop_ioctl(file_t*, u_long, void *); 202 static int putter_fop_poll(file_t *, int); 203 static int putter_fop_stat(file_t *, struct stat *); 204 static int putter_fop_close(file_t *); 205 static int putter_fop_kqfilter(file_t *, struct knote *); 206 207 208 static const struct fileops putter_fileops = { 209 .fo_read = putter_fop_read, 210 .fo_write = putter_fop_write, 211 .fo_ioctl = putter_fop_ioctl, 212 .fo_fcntl = fnullop_fcntl, 213 .fo_poll = putter_fop_poll, 214 .fo_stat = putter_fop_stat, 215 .fo_close = putter_fop_close, 216 .fo_kqfilter = putter_fop_kqfilter, 217 .fo_restart = fnullop_restart, 218 }; 219 220 static int 221 putter_fop_read(file_t *fp, off_t *off, struct uio *uio, 222 kauth_cred_t cred, int flags) 223 { 224 struct putter_instance *pi = fp->f_data; 225 size_t origres, moved; 226 int error; 227 228 KERNEL_LOCK(1, NULL); 229 getnanotime(&pi->pi_atime); 230 231 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 232 printf("putter_fop_read: private %d not inited\n", pi->pi_idx); 233 KERNEL_UNLOCK_ONE(NULL); 234 return ENOENT; 235 } 236 237 if (pi->pi_curput == NULL) { 238 error = pi->pi_pop->pop_getout(pi->pi_private, uio->uio_resid, 239 fp->f_flag & O_NONBLOCK, &pi->pi_curput, 240 &pi->pi_curres, &pi->pi_curopaq); 241 if (error) { 242 KERNEL_UNLOCK_ONE(NULL); 243 return error; 244 } 245 } 246 247 origres = uio->uio_resid; 248 error = uiomove(pi->pi_curput, pi->pi_curres, uio); 249 moved = origres - uio->uio_resid; 250 DPRINTF(("putter_fop_read (%p): moved %zu bytes from %p, error %d\n", 251 pi, moved, pi->pi_curput, error)); 252 253 KASSERT(pi->pi_curres >= moved); 254 pi->pi_curres -= moved; 255 pi->pi_curput += moved; 256 257 if (pi->pi_curres == 0) { 258 pi->pi_pop->pop_releaseout(pi->pi_private, 259 pi->pi_curopaq, error); 260 pi->pi_curput = NULL; 261 } 262 263 KERNEL_UNLOCK_ONE(NULL); 264 return error; 265 } 266 267 static int 268 putter_fop_write(file_t *fp, off_t *off, struct uio *uio, 269 kauth_cred_t cred, int flags) 270 { 271 struct putter_instance *pi = fp->f_data; 272 struct putter_hdr pth; 273 uint8_t *buf; 274 size_t frsize; 275 int error; 276 277 KERNEL_LOCK(1, NULL); 278 getnanotime(&pi->pi_mtime); 279 280 DPRINTF(("putter_fop_write (%p): writing response, resid %zu\n", 281 pi->pi_private, uio->uio_resid)); 282 283 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 284 printf("putter_fop_write: putter %d not inited\n", pi->pi_idx); 285 KERNEL_UNLOCK_ONE(NULL); 286 return ENOENT; 287 } 288 289 error = uiomove(&pth, sizeof(struct putter_hdr), uio); 290 if (error) { 291 KERNEL_UNLOCK_ONE(NULL); 292 return error; 293 } 294 295 /* Sorry mate, the kernel doesn't buffer. */ 296 frsize = pth.pth_framelen - sizeof(struct putter_hdr); 297 if (uio->uio_resid < frsize) { 298 KERNEL_UNLOCK_ONE(NULL); 299 return EINVAL; 300 } 301 302 buf = kmem_alloc(frsize + sizeof(struct putter_hdr), KM_SLEEP); 303 memcpy(buf, &pth, sizeof(pth)); 304 error = uiomove(buf+sizeof(struct putter_hdr), frsize, uio); 305 if (error == 0) { 306 pi->pi_pop->pop_dispatch(pi->pi_private, 307 (struct putter_hdr *)buf); 308 } 309 kmem_free(buf, frsize + sizeof(struct putter_hdr)); 310 311 KERNEL_UNLOCK_ONE(NULL); 312 return error; 313 } 314 315 /* 316 * Poll query interface. The question is only if an event 317 * can be read from us. 318 */ 319 #define PUTTERPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI) 320 static int 321 putter_fop_poll(file_t *fp, int events) 322 { 323 struct putter_instance *pi = fp->f_data; 324 int revents; 325 326 KERNEL_LOCK(1, NULL); 327 328 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) { 329 printf("putter_fop_ioctl: putter %d not inited\n", pi->pi_idx); 330 KERNEL_UNLOCK_ONE(NULL); 331 return ENOENT; 332 } 333 334 revents = events & (POLLOUT | POLLWRNORM | POLLWRBAND); 335 if ((events & PUTTERPOLL_EVSET) == 0) { 336 KERNEL_UNLOCK_ONE(NULL); 337 return revents; 338 } 339 340 /* check queue */ 341 if (pi->pi_pop->pop_waitcount(pi->pi_private)) 342 revents |= PUTTERPOLL_EVSET; 343 else 344 selrecord(curlwp, &pi->pi_sel); 345 346 KERNEL_UNLOCK_ONE(NULL); 347 return revents; 348 } 349 350 /* 351 * device close = forced unmount. 352 * 353 * unmounting is a frightfully complex operation to avoid races 354 */ 355 static int 356 putter_fop_close(file_t *fp) 357 { 358 struct putter_instance *pi = fp->f_data; 359 int rv; 360 361 DPRINTF(("putter_fop_close: device closed\n")); 362 363 KERNEL_LOCK(1, NULL); 364 365 restart: 366 mutex_enter(&pi_mtx); 367 /* 368 * First check if the driver was never born. In that case 369 * remove the instance from the list. If mount is attempted later, 370 * it will simply fail. 371 */ 372 if (pi->pi_private == PUTTER_EMBRYO) { 373 TAILQ_REMOVE(&putter_ilist, pi, pi_entries); 374 mutex_exit(&pi_mtx); 375 376 DPRINTF(("putter_fop_close: data associated with fp %p was " 377 "embryonic\n", fp)); 378 379 goto out; 380 } 381 382 /* 383 * Next, analyze if unmount was called and the instance is dead. 384 * In this case we can just free the structure and go home, it 385 * was removed from the list by putter_rmprivate(). 386 */ 387 if (pi->pi_private == PUTTER_DEAD) { 388 mutex_exit(&pi_mtx); 389 390 DPRINTF(("putter_fop_close: putter associated with fp %p (%d) " 391 "dead, freeing\n", fp, pi->pi_idx)); 392 393 goto out; 394 } 395 396 /* 397 * So we have a reference. Proceed to unravel the 398 * underlying driver. 399 */ 400 mutex_exit(&pi_mtx); 401 402 /* hmm? suspicious locking? */ 403 if (pi->pi_curput != NULL) { 404 pi->pi_pop->pop_releaseout(pi->pi_private, pi->pi_curopaq, 405 ENXIO); 406 pi->pi_curput = NULL; 407 } 408 while ((rv = pi->pi_pop->pop_close(pi->pi_private)) == ERESTART) 409 goto restart; 410 411 out: 412 KERNEL_UNLOCK_ONE(NULL); 413 /* 414 * Finally, release the instance information. It was already 415 * removed from the list by putter_rmprivate() and we know it's 416 * dead, so no need to lock. 417 */ 418 kmem_free(pi, sizeof(struct putter_instance)); 419 420 return 0; 421 } 422 423 static int 424 putter_fop_stat(file_t *fp, struct stat *st) 425 { 426 struct putter_instance *pi = fp->f_data; 427 428 (void)memset(st, 0, sizeof(*st)); 429 KERNEL_LOCK(1, NULL); 430 st->st_dev = makedev(cdevsw_lookup_major(&putter_cdevsw), pi->pi_idx); 431 st->st_atimespec = pi->pi_atime; 432 st->st_mtimespec = pi->pi_mtime; 433 st->st_ctimespec = st->st_birthtimespec = pi->pi_btime; 434 st->st_uid = kauth_cred_geteuid(fp->f_cred); 435 st->st_gid = kauth_cred_getegid(fp->f_cred); 436 st->st_mode = S_IFCHR; 437 KERNEL_UNLOCK_ONE(NULL); 438 return 0; 439 } 440 441 static int 442 putter_fop_ioctl(file_t *fp, u_long cmd, void *data) 443 { 444 445 /* 446 * work already done in sys_ioctl(). skip sanity checks to enable 447 * setting non-blocking fd on an embryotic driver. 448 */ 449 if (cmd == FIONBIO) 450 return 0; 451 452 return EINVAL; 453 } 454 455 /* kqueue stuff */ 456 457 static void 458 filt_putterdetach(struct knote *kn) 459 { 460 struct putter_instance *pi = kn->kn_hook; 461 462 KERNEL_LOCK(1, NULL); 463 mutex_enter(&pi_mtx); 464 SLIST_REMOVE(&pi->pi_sel.sel_klist, kn, knote, kn_selnext); 465 mutex_exit(&pi_mtx); 466 KERNEL_UNLOCK_ONE(NULL); 467 } 468 469 static int 470 filt_putter(struct knote *kn, long hint) 471 { 472 struct putter_instance *pi = kn->kn_hook; 473 int error, rv; 474 475 KERNEL_LOCK(1, NULL); 476 error = 0; 477 mutex_enter(&pi_mtx); 478 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) 479 error = 1; 480 mutex_exit(&pi_mtx); 481 if (error) { 482 KERNEL_UNLOCK_ONE(NULL); 483 return 0; 484 } 485 486 kn->kn_data = pi->pi_pop->pop_waitcount(pi->pi_private); 487 rv = kn->kn_data != 0; 488 KERNEL_UNLOCK_ONE(NULL); 489 return rv; 490 } 491 492 static const struct filterops putter_filtops = 493 { 1, NULL, filt_putterdetach, filt_putter }; 494 495 static int 496 putter_fop_kqfilter(file_t *fp, struct knote *kn) 497 { 498 struct putter_instance *pi = fp->f_data; 499 struct klist *klist; 500 501 KERNEL_LOCK(1, NULL); 502 503 switch (kn->kn_filter) { 504 case EVFILT_READ: 505 klist = &pi->pi_sel.sel_klist; 506 kn->kn_fop = &putter_filtops; 507 kn->kn_hook = pi; 508 509 mutex_enter(&pi_mtx); 510 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 511 mutex_exit(&pi_mtx); 512 513 break; 514 case EVFILT_WRITE: 515 kn->kn_fop = &seltrue_filtops; 516 break; 517 default: 518 KERNEL_UNLOCK_ONE(NULL); 519 return EINVAL; 520 } 521 522 KERNEL_UNLOCK_ONE(NULL); 523 return 0; 524 } 525 526 int 527 puttercdopen(dev_t dev, int flags, int fmt, struct lwp *l) 528 { 529 struct putter_instance *pi; 530 file_t *fp; 531 int error, fd, idx; 532 proc_t *p; 533 534 p = curproc; 535 pi = kmem_alloc(sizeof(struct putter_instance), KM_SLEEP); 536 mutex_enter(&pi_mtx); 537 idx = get_pi_idx(pi); 538 539 pi->pi_pid = p->p_pid; 540 pi->pi_idx = idx; 541 pi->pi_curput = NULL; 542 pi->pi_curres = 0; 543 pi->pi_curopaq = NULL; 544 getnanotime(&pi->pi_btime); 545 pi->pi_atime = pi->pi_mtime = pi->pi_btime; 546 selinit(&pi->pi_sel); 547 mutex_exit(&pi_mtx); 548 549 if ((error = fd_allocfile(&fp, &fd)) != 0) 550 goto bad1; 551 552 if ((error = putter_configure(dev, flags, fmt, fd)) != 0) 553 goto bad2; 554 555 DPRINTF(("puttercdopen: registered embryonic pmp for pid: %d\n", 556 pi->pi_pid)); 557 558 error = fd_clone(fp, fd, FREAD|FWRITE, &putter_fileops, pi); 559 KASSERT(error == EMOVEFD); 560 return error; 561 562 bad2: 563 fd_abort(p, fp, fd); 564 bad1: 565 putter_detach(pi); 566 kmem_free(pi, sizeof(struct putter_instance)); 567 return error; 568 } 569 570 int 571 puttercdclose(dev_t dev, int flags, int fmt, struct lwp *l) 572 { 573 574 panic("puttercdclose impossible\n"); 575 576 return 0; 577 } 578 579 580 /* 581 * Set the private structure for the file descriptor. This is 582 * typically done immediately when the counterpart has knowledge 583 * about the private structure's address and the file descriptor 584 * (e.g. vfs mount routine). 585 * 586 * We only want to make sure that the caller had the right to open the 587 * device, we don't so much care about which context it gets in case 588 * the same process opened multiple (since they are equal at this point). 589 */ 590 struct putter_instance * 591 putter_attach(pid_t pid, int fd, void *ppriv, struct putter_ops *pop) 592 { 593 struct putter_instance *pi = NULL; 594 595 mutex_enter(&pi_mtx); 596 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) { 597 if (pi->pi_pid == pid && pi->pi_private == PUTTER_EMBRYO) { 598 pi->pi_private = ppriv; 599 pi->pi_fd = fd; 600 pi->pi_pop = pop; 601 break; 602 } 603 } 604 mutex_exit(&pi_mtx); 605 606 DPRINTF(("putter_setprivate: pi at %p (%d/%d)\n", pi, 607 pi ? pi->pi_pid : 0, pi ? pi->pi_fd : 0)); 608 609 return pi; 610 } 611 612 /* 613 * Remove fp <-> private mapping. 614 */ 615 void 616 putter_detach(struct putter_instance *pi) 617 { 618 619 mutex_enter(&pi_mtx); 620 TAILQ_REMOVE(&putter_ilist, pi, pi_entries); 621 pi->pi_private = PUTTER_DEAD; 622 mutex_exit(&pi_mtx); 623 seldestroy(&pi->pi_sel); 624 625 DPRINTF(("putter_nukebypmp: nuked %p\n", pi)); 626 } 627 628 void 629 putter_notify(struct putter_instance *pi) 630 { 631 632 selnotify(&pi->pi_sel, 0, 0); 633 } 634 635 /* search sorted list of instances for free minor, sorted insert arg */ 636 static int 637 get_pi_idx(struct putter_instance *pi_i) 638 { 639 struct putter_instance *pi; 640 int i; 641 642 KASSERT(mutex_owned(&pi_mtx)); 643 644 i = 0; 645 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) { 646 if (i != pi->pi_idx) 647 break; 648 i++; 649 } 650 651 pi_i->pi_private = PUTTER_EMBRYO; 652 653 if (pi == NULL) 654 TAILQ_INSERT_TAIL(&putter_ilist, pi_i, pi_entries); 655 else 656 TAILQ_INSERT_BEFORE(pi, pi_i, pi_entries); 657 658 return i; 659 } 660 661 MODULE(MODULE_CLASS_DRIVER, putter, NULL); 662 663 static int 664 putter_modcmd(modcmd_t cmd, void *arg) 665 { 666 #ifdef _MODULE 667 devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR; 668 669 switch (cmd) { 670 case MODULE_CMD_INIT: 671 putterattach(); 672 return devsw_attach("putter", NULL, &bmajor, 673 &putter_cdevsw, &cmajor); 674 case MODULE_CMD_FINI: 675 return ENOTTY; /* XXX: putterdetach */ 676 default: 677 return ENOTTY; 678 } 679 #else 680 if (cmd == MODULE_CMD_INIT) 681 return 0; 682 return ENOTTY; 683 #endif 684 } 685