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