kern_cons.c revision 175294
1/*- 2 * Copyright (c) 1988 University of Utah. 3 * Copyright (c) 1991 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 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 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: head/sys/kern/tty_cons.c 175294 2008-01-13 14:44:15Z attilio $"); 39 40#include "opt_ddb.h" 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/conf.h> 47#include <sys/cons.h> 48#include <sys/fcntl.h> 49#include <sys/kdb.h> 50#include <sys/kernel.h> 51#include <sys/malloc.h> 52#include <sys/msgbuf.h> 53#include <sys/namei.h> 54#include <sys/priv.h> 55#include <sys/proc.h> 56#include <sys/queue.h> 57#include <sys/reboot.h> 58#include <sys/sysctl.h> 59#include <sys/sbuf.h> 60#include <sys/tty.h> 61#include <sys/uio.h> 62#include <sys/vnode.h> 63 64#include <ddb/ddb.h> 65 66#include <machine/cpu.h> 67 68static d_open_t cnopen; 69static d_close_t cnclose; 70static d_read_t cnread; 71static d_write_t cnwrite; 72static d_ioctl_t cnioctl; 73static d_poll_t cnpoll; 74static d_kqfilter_t cnkqfilter; 75 76static struct cdevsw cn_cdevsw = { 77 .d_version = D_VERSION, 78 .d_open = cnopen, 79 .d_close = cnclose, 80 .d_read = cnread, 81 .d_write = cnwrite, 82 .d_ioctl = cnioctl, 83 .d_poll = cnpoll, 84 .d_name = "console", 85 .d_flags = D_TTY | D_NEEDGIANT, 86 .d_kqfilter = cnkqfilter, 87}; 88 89struct cn_device { 90 STAILQ_ENTRY(cn_device) cnd_next; 91 struct vnode *cnd_vp; 92 struct consdev *cnd_cn; 93}; 94 95#define CNDEVPATHMAX 32 96#define CNDEVTAB_SIZE 4 97static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 98static STAILQ_HEAD(, cn_device) cn_devlist = 99 STAILQ_HEAD_INITIALIZER(cn_devlist); 100 101#define CND_INVALID(cnd, td) \ 102 (cnd == NULL || cnd->cnd_vp == NULL || \ 103 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1))) 104 105static dev_t cn_udev_t; 106SYSCTL_OPAQUE(_machdep, OID_AUTO, consdev, CTLFLAG_RD, 107 &cn_udev_t, sizeof cn_udev_t, "T,struct cdev *", ""); 108 109int cons_avail_mask = 0; /* Bit mask. Each registered low level console 110 * which is currently unavailable for inpit 111 * (i.e., if it is in graphics mode) will have 112 * this bit cleared. 113 */ 114static int cn_mute; 115static int openflag; /* how /dev/console was opened */ 116static int cn_is_open; 117static char *consbuf; /* buffer used by `consmsgbuf' */ 118static struct callout conscallout; /* callout for outputting to constty */ 119struct msgbuf consmsgbuf; /* message buffer for console tty */ 120static u_char console_pausing; /* pause after each line during probe */ 121static char *console_pausestr= 122"<pause; press any key to proceed to next line or '.' to end pause mode>"; 123struct tty *constty; /* pointer to console "window" tty */ 124static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 125static int use_cnputs_mtx = 0; /* != 0 if cnputs_mtx locking reqd. */ 126 127static void constty_timeout(void *arg); 128 129static struct consdev cons_consdev; 130DATA_SET(cons_set, cons_consdev); 131SET_DECLARE(cons_set, struct consdev); 132 133void 134cninit(void) 135{ 136 struct consdev *best_cn, *cn, **list; 137 138 /* 139 * Check if we should mute the console (for security reasons perhaps) 140 * It can be changes dynamically using sysctl kern.consmute 141 * once we are up and going. 142 * 143 */ 144 cn_mute = ((boothowto & (RB_MUTE 145 |RB_SINGLE 146 |RB_VERBOSE 147 |RB_ASKNAME)) == RB_MUTE); 148 149 /* 150 * Find the first console with the highest priority. 151 */ 152 best_cn = NULL; 153 SET_FOREACH(list, cons_set) { 154 cn = *list; 155 cnremove(cn); 156 if (cn->cn_probe == NULL) 157 continue; 158 cn->cn_probe(cn); 159 if (cn->cn_pri == CN_DEAD) 160 continue; 161 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 162 best_cn = cn; 163 if (boothowto & RB_MULTIPLE) { 164 /* 165 * Initialize console, and attach to it. 166 */ 167 cn->cn_init(cn); 168 cnadd(cn); 169 } 170 } 171 if (best_cn == NULL) 172 return; 173 if ((boothowto & RB_MULTIPLE) == 0) { 174 best_cn->cn_init(best_cn); 175 cnadd(best_cn); 176 } 177 if (boothowto & RB_PAUSE) 178 console_pausing = 1; 179 /* 180 * Make the best console the preferred console. 181 */ 182 cnselect(best_cn); 183} 184 185void 186cninit_finish() 187{ 188 console_pausing = 0; 189} 190 191/* add a new physical console to back the virtual console */ 192int 193cnadd(struct consdev *cn) 194{ 195 struct cn_device *cnd; 196 int i; 197 198 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 199 if (cnd->cnd_cn == cn) 200 return (0); 201 for (i = 0; i < CNDEVTAB_SIZE; i++) { 202 cnd = &cn_devtab[i]; 203 if (cnd->cnd_cn == NULL) 204 break; 205 } 206 if (cnd->cnd_cn != NULL) 207 return (ENOMEM); 208 cnd->cnd_cn = cn; 209 if (cn->cn_name[0] == '\0') { 210 /* XXX: it is unclear if/where this print might output */ 211 printf("WARNING: console at %p has no name\n", cn); 212 } 213 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 214 215 /* Add device to the active mask. */ 216 cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 217 218 return (0); 219} 220 221void 222cnremove(struct consdev *cn) 223{ 224 struct cn_device *cnd; 225 int i; 226 227 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 228 if (cnd->cnd_cn != cn) 229 continue; 230 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 231 if (cnd->cnd_vp != NULL) 232 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL); 233 cnd->cnd_vp = NULL; 234 cnd->cnd_cn = NULL; 235 236 /* Remove this device from available mask. */ 237 for (i = 0; i < CNDEVTAB_SIZE; i++) 238 if (cnd == &cn_devtab[i]) { 239 cons_avail_mask &= ~(1 << i); 240 break; 241 } 242#if 0 243 /* 244 * XXX 245 * syscons gets really confused if console resources are 246 * freed after the system has initialized. 247 */ 248 if (cn->cn_term != NULL) 249 cn->cn_term(cn); 250#endif 251 return; 252 } 253} 254 255void 256cnselect(struct consdev *cn) 257{ 258 struct cn_device *cnd; 259 260 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 261 if (cnd->cnd_cn != cn) 262 continue; 263 if (cnd == STAILQ_FIRST(&cn_devlist)) 264 return; 265 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 266 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 267 return; 268 } 269} 270 271void 272cnavailable(struct consdev *cn, int available) 273{ 274 int i; 275 276 for (i = 0; i < CNDEVTAB_SIZE; i++) { 277 if (cn_devtab[i].cnd_cn == cn) 278 break; 279 } 280 if (available) { 281 if (i < CNDEVTAB_SIZE) 282 cons_avail_mask |= (1 << i); 283 cn->cn_flags &= ~CN_FLAG_NOAVAIL; 284 } else { 285 if (i < CNDEVTAB_SIZE) 286 cons_avail_mask &= ~(1 << i); 287 cn->cn_flags |= CN_FLAG_NOAVAIL; 288 } 289} 290 291int 292cnunavailable(void) 293{ 294 295 return (cons_avail_mask == 0); 296} 297 298/* 299 * sysctl_kern_console() provides output parseable in conscontrol(1). 300 */ 301static int 302sysctl_kern_console(SYSCTL_HANDLER_ARGS) 303{ 304 struct cn_device *cnd; 305 struct consdev *cp, **list; 306 char *p; 307 int delete, error; 308 struct sbuf *sb; 309 310 sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND); 311 if (sb == NULL) 312 return (ENOMEM); 313 sbuf_clear(sb); 314 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 315 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 316 sbuf_printf(sb, "/"); 317 SET_FOREACH(list, cons_set) { 318 cp = *list; 319 if (cp->cn_name[0] != '\0') 320 sbuf_printf(sb, "%s,", cp->cn_name); 321 } 322 sbuf_finish(sb); 323 error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 324 if (error == 0 && req->newptr != NULL) { 325 p = sbuf_data(sb); 326 error = ENXIO; 327 delete = 0; 328 if (*p == '-') { 329 delete = 1; 330 p++; 331 } 332 SET_FOREACH(list, cons_set) { 333 cp = *list; 334 if (strcmp(p, cp->cn_name) != 0) 335 continue; 336 if (delete) { 337 cnremove(cp); 338 error = 0; 339 } else { 340 error = cnadd(cp); 341 if (error == 0) 342 cnselect(cp); 343 } 344 break; 345 } 346 } 347 sbuf_delete(sb); 348 return (error); 349} 350 351SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 352 0, 0, sysctl_kern_console, "A", "Console device control"); 353 354/* 355 * User has changed the state of the console muting. 356 * This may require us to open or close the device in question. 357 */ 358static int 359sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) 360{ 361 int error; 362 int ocn_mute; 363 364 ocn_mute = cn_mute; 365 error = sysctl_handle_int(oidp, &cn_mute, 0, req); 366 if (error != 0 || req->newptr == NULL) 367 return (error); 368 if (ocn_mute && !cn_mute && cn_is_open) 369 error = cnopen(NULL, openflag, 0, curthread); 370 else if (!ocn_mute && cn_mute && cn_is_open) { 371 error = cnclose(NULL, openflag, 0, curthread); 372 cn_is_open = 1; /* XXX hack */ 373 } 374 return (error); 375} 376 377SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 378 0, sizeof(cn_mute), sysctl_kern_consmute, "I", ""); 379 380static int 381cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen) 382{ 383 char path[CNDEVPATHMAX]; 384 struct nameidata nd; 385 struct vnode *vp; 386 struct cdev *dev; 387 struct cdevsw *csw; 388 int error; 389 390 if ((vp = cnd->cnd_vp) != NULL) { 391 if (!forceopen && vp->v_type != VBAD) { 392 dev = vp->v_rdev; 393 csw = dev_refthread(dev); 394 if (csw == NULL) 395 return (ENXIO); 396 error = (*csw->d_open)(dev, openflag, 0, td); 397 dev_relthread(dev); 398 return (error); 399 } 400 cnd->cnd_vp = NULL; 401 vn_close(vp, openflag, td->td_ucred, td); 402 } 403 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name); 404 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td); 405 error = vn_open(&nd, &openflag, 0, NULL); 406 if (error == 0) { 407 NDFREE(&nd, NDF_ONLY_PNBUF); 408 VOP_UNLOCK(nd.ni_vp, 0); 409 if (nd.ni_vp->v_type == VCHR) 410 cnd->cnd_vp = nd.ni_vp; 411 else 412 vn_close(nd.ni_vp, openflag, td->td_ucred, td); 413 } 414 return (cnd->cnd_vp != NULL); 415} 416 417static int 418cnopen(struct cdev *dev, int flag, int mode, struct thread *td) 419{ 420 struct cn_device *cnd; 421 422 openflag = flag | FWRITE; /* XXX */ 423 cn_is_open = 1; /* console is logically open */ 424 if (cn_mute) 425 return (0); 426 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 427 cn_devopen(cnd, td, 0); 428 return (0); 429} 430 431static int 432cnclose(struct cdev *dev, int flag, int mode, struct thread *td) 433{ 434 struct cn_device *cnd; 435 struct vnode *vp; 436 437 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 438 if ((vp = cnd->cnd_vp) == NULL) 439 continue; 440 cnd->cnd_vp = NULL; 441 vn_close(vp, openflag, td->td_ucred, td); 442 } 443 cn_is_open = 0; 444 return (0); 445} 446 447static int 448cnread(struct cdev *dev, struct uio *uio, int flag) 449{ 450 struct cn_device *cnd; 451 struct cdevsw *csw; 452 int error; 453 454 cnd = STAILQ_FIRST(&cn_devlist); 455 if (cn_mute || CND_INVALID(cnd, curthread)) 456 return (0); 457 dev = cnd->cnd_vp->v_rdev; 458 csw = dev_refthread(dev); 459 if (csw == NULL) 460 return (ENXIO); 461 error = (csw->d_read)(dev, uio, flag); 462 dev_relthread(dev); 463 return (error); 464} 465 466static int 467cnwrite(struct cdev *dev, struct uio *uio, int flag) 468{ 469 struct cn_device *cnd; 470 struct cdevsw *csw; 471 int error; 472 473 cnd = STAILQ_FIRST(&cn_devlist); 474 if (cn_mute || CND_INVALID(cnd, curthread)) 475 goto done; 476 if (constty) 477 dev = constty->t_dev; 478 else 479 dev = cnd->cnd_vp->v_rdev; 480 if (dev != NULL) { 481 log_console(uio); 482 csw = dev_refthread(dev); 483 if (csw == NULL) 484 return (ENXIO); 485 error = (csw->d_write)(dev, uio, flag); 486 dev_relthread(dev); 487 return (error); 488 } 489done: 490 uio->uio_resid = 0; /* dump the data */ 491 return (0); 492} 493 494static int 495cnioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 496{ 497 struct cn_device *cnd; 498 struct cdevsw *csw; 499 int error; 500 501 cnd = STAILQ_FIRST(&cn_devlist); 502 if (cn_mute || CND_INVALID(cnd, td)) 503 return (0); 504 /* 505 * Superuser can always use this to wrest control of console 506 * output from the "virtual" console. 507 */ 508 if (cmd == TIOCCONS && constty) { 509 error = priv_check(td, PRIV_TTY_CONSOLE); 510 if (error) 511 return (error); 512 constty = NULL; 513 return (0); 514 } 515 dev = cnd->cnd_vp->v_rdev; 516 if (dev == NULL) 517 return (0); /* XXX : ENOTTY ? */ 518 csw = dev_refthread(dev); 519 if (csw == NULL) 520 return (ENXIO); 521 error = (csw->d_ioctl)(dev, cmd, data, flag, td); 522 dev_relthread(dev); 523 return (error); 524} 525 526/* 527 * XXX 528 * poll/kqfilter do not appear to be correct 529 */ 530static int 531cnpoll(struct cdev *dev, int events, struct thread *td) 532{ 533 struct cn_device *cnd; 534 struct cdevsw *csw; 535 int error; 536 537 cnd = STAILQ_FIRST(&cn_devlist); 538 if (cn_mute || CND_INVALID(cnd, td)) 539 return (0); 540 dev = cnd->cnd_vp->v_rdev; 541 if (dev == NULL) 542 return (0); 543 csw = dev_refthread(dev); 544 if (csw == NULL) 545 return (ENXIO); 546 error = (csw->d_poll)(dev, events, td); 547 dev_relthread(dev); 548 return (error); 549} 550 551static int 552cnkqfilter(struct cdev *dev, struct knote *kn) 553{ 554 struct cn_device *cnd; 555 struct cdevsw *csw; 556 int error; 557 558 cnd = STAILQ_FIRST(&cn_devlist); 559 if (cn_mute || CND_INVALID(cnd, curthread)) 560 return (EINVAL); 561 dev = cnd->cnd_vp->v_rdev; 562 if (dev == NULL) 563 return (ENXIO); 564 csw = dev_refthread(dev); 565 if (csw == NULL) 566 return (ENXIO); 567 error = (csw->d_kqfilter)(dev, kn); 568 dev_relthread(dev); 569 return (error); 570} 571 572/* 573 * Low level console routines. 574 */ 575int 576cngetc(void) 577{ 578 int c; 579 580 if (cn_mute) 581 return (-1); 582 while ((c = cncheckc()) == -1) 583 ; 584 if (c == '\r') 585 c = '\n'; /* console input is always ICRNL */ 586 return (c); 587} 588 589int 590cncheckc(void) 591{ 592 struct cn_device *cnd; 593 struct consdev *cn; 594 int c; 595 596 if (cn_mute) 597 return (-1); 598 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 599 cn = cnd->cnd_cn; 600 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 601 if (cn->cn_checkc != NULL) 602 c = cn->cn_checkc(cn); 603 else 604 c = cn->cn_getc(cn); 605 if (c != -1) { 606 return (c); 607 } 608 } 609 } 610 return (-1); 611} 612 613void 614cnputc(int c) 615{ 616 struct cn_device *cnd; 617 struct consdev *cn; 618 char *cp; 619 620 if (cn_mute || c == '\0') 621 return; 622 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 623 cn = cnd->cnd_cn; 624 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 625 if (c == '\n') 626 cn->cn_putc(cn, '\r'); 627 cn->cn_putc(cn, c); 628 } 629 } 630 if (console_pausing && c == '\n' && !kdb_active) { 631 for (cp = console_pausestr; *cp != '\0'; cp++) 632 cnputc(*cp); 633 if (cngetc() == '.') 634 console_pausing = 0; 635 cnputc('\r'); 636 for (cp = console_pausestr; *cp != '\0'; cp++) 637 cnputc(' '); 638 cnputc('\r'); 639 } 640} 641 642void 643cnputs(char *p) 644{ 645 int c; 646 int unlock_reqd = 0; 647 648 if (use_cnputs_mtx) { 649 mtx_lock_spin(&cnputs_mtx); 650 unlock_reqd = 1; 651 } 652 653 while ((c = *p++) != '\0') 654 cnputc(c); 655 656 if (unlock_reqd) 657 mtx_unlock_spin(&cnputs_mtx); 658} 659 660static int consmsgbuf_size = 8192; 661SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, 662 ""); 663 664/* 665 * Redirect console output to a tty. 666 */ 667void 668constty_set(struct tty *tp) 669{ 670 int size; 671 672 KASSERT(tp != NULL, ("constty_set: NULL tp")); 673 if (consbuf == NULL) { 674 size = consmsgbuf_size; 675 consbuf = malloc(size, M_TTYS, M_WAITOK); 676 msgbuf_init(&consmsgbuf, consbuf, size); 677 callout_init(&conscallout, 0); 678 } 679 constty = tp; 680 constty_timeout(NULL); 681} 682 683/* 684 * Disable console redirection to a tty. 685 */ 686void 687constty_clear(void) 688{ 689 int c; 690 691 constty = NULL; 692 if (consbuf == NULL) 693 return; 694 callout_stop(&conscallout); 695 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 696 cnputc(c); 697 free(consbuf, M_TTYS); 698 consbuf = NULL; 699} 700 701/* Times per second to check for pending console tty messages. */ 702static int constty_wakeups_per_second = 5; 703SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 704 &constty_wakeups_per_second, 0, ""); 705 706static void 707constty_timeout(void *arg) 708{ 709 int c; 710 711 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { 712 if (tputchar(c, constty) < 0) 713 constty = NULL; 714 } 715 if (constty != NULL) { 716 callout_reset(&conscallout, hz / constty_wakeups_per_second, 717 constty_timeout, NULL); 718 } else { 719 /* Deallocate the constty buffer memory. */ 720 constty_clear(); 721 } 722} 723 724static void 725cn_drvinit(void *unused) 726{ 727 728 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console"); 729 730 mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS); 731 use_cnputs_mtx = 1; 732} 733 734SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL) 735