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