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