kern_cons.c revision 158944
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 158944 2006-05-26 10:46:38Z phk $"); 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 72static struct cdevsw cn_cdevsw = { 73 .d_version = D_VERSION, 74 .d_open = cnopen, 75 .d_close = cnclose, 76 .d_read = cnread, 77 .d_write = cnwrite, 78 .d_ioctl = cnioctl, 79 .d_poll = cnpoll, 80 .d_name = "console", 81 .d_flags = D_TTY | D_NEEDGIANT, 82 .d_kqfilter = cnkqfilter, 83}; 84 85struct cn_device { 86 STAILQ_ENTRY(cn_device) cnd_next; 87 struct vnode *cnd_vp; 88 struct consdev *cnd_cn; 89}; 90 91#define CNDEVPATHMAX 32 92#define CNDEVTAB_SIZE 4 93static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 94static STAILQ_HEAD(, cn_device) cn_devlist = 95 STAILQ_HEAD_INITIALIZER(cn_devlist); 96 97#define CND_INVALID(cnd, td) \ 98 (cnd == NULL || cnd->cnd_vp == NULL || \ 99 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1))) 100 101static dev_t cn_udev_t; 102SYSCTL_OPAQUE(_machdep, OID_AUTO, consdev, CTLFLAG_RD, 103 &cn_udev_t, sizeof cn_udev_t, "T,struct cdev *", ""); 104 105int cons_avail_mask = 0; /* Bit mask. Each registered low level console 106 * which is currently unavailable for inpit 107 * (i.e., if it is in graphics mode) will have 108 * this bit cleared. 109 */ 110static int cn_mute; 111static int openflag; /* how /dev/console was opened */ 112static int cn_is_open; 113static char *consbuf; /* buffer used by `consmsgbuf' */ 114static struct callout conscallout; /* callout for outputting to constty */ 115struct msgbuf consmsgbuf; /* message buffer for console tty */ 116static u_char console_pausing; /* pause after each line during probe */ 117static char *console_pausestr= 118"<pause; press any key to proceed to next line or '.' to end pause mode>"; 119struct tty *constty; /* pointer to console "window" tty */ 120 121static void constty_timeout(void *arg); 122 123static struct consdev cons_consdev; 124DATA_SET(cons_set, cons_consdev); 125SET_DECLARE(cons_set, struct consdev); 126 127void 128cninit(void) 129{ 130 struct consdev *best_cn, *cn, **list; 131 132 /* 133 * Check if we should mute the console (for security reasons perhaps) 134 * It can be changes dynamically using sysctl kern.consmute 135 * once we are up and going. 136 * 137 */ 138 cn_mute = ((boothowto & (RB_MUTE 139 |RB_SINGLE 140 |RB_VERBOSE 141 |RB_ASKNAME)) == RB_MUTE); 142 143 /* 144 * Find the first console with the highest priority. 145 */ 146 best_cn = NULL; 147 SET_FOREACH(list, cons_set) { 148 cn = *list; 149 cnremove(cn); 150 if (cn->cn_probe == NULL) 151 continue; 152 cn->cn_probe(cn); 153 if (cn->cn_pri == CN_DEAD) 154 continue; 155 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 156 best_cn = cn; 157 if (boothowto & RB_MULTIPLE) { 158 /* 159 * Initialize console, and attach to it. 160 */ 161 cnadd(cn); 162 cn->cn_init(cn); 163 } 164 } 165 if (best_cn == NULL) 166 return; 167 if ((boothowto & RB_MULTIPLE) == 0) { 168 cnadd(best_cn); 169 best_cn->cn_init(best_cn); 170 } 171 if (boothowto & RB_PAUSE) 172 console_pausing = 1; 173 /* 174 * Make the best console the preferred console. 175 */ 176 cnselect(best_cn); 177} 178 179void 180cninit_finish() 181{ 182 console_pausing = 0; 183} 184 185/* add a new physical console to back the virtual console */ 186int 187cnadd(struct consdev *cn) 188{ 189 struct cn_device *cnd; 190 int i; 191 192 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 193 if (cnd->cnd_cn == cn) 194 return (0); 195 for (i = 0; i < CNDEVTAB_SIZE; i++) { 196 cnd = &cn_devtab[i]; 197 if (cnd->cnd_cn == NULL) 198 break; 199 } 200 if (cnd->cnd_cn != NULL) 201 return (ENOMEM); 202 cnd->cnd_cn = cn; 203 if (cn->cn_name[0] == '\0') { 204 /* XXX: it is unclear if/where this print might output */ 205 printf("WARNING: console at %p has no name\n", cn); 206 } 207 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 208 209 /* Add device to the active mask. */ 210 cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 211 212 return (0); 213} 214 215void 216cnremove(struct consdev *cn) 217{ 218 struct cn_device *cnd; 219 int i; 220 221 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 222 if (cnd->cnd_cn != cn) 223 continue; 224 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 225 if (cnd->cnd_vp != NULL) 226 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL); 227 cnd->cnd_vp = NULL; 228 cnd->cnd_cn = NULL; 229 230 /* Remove this device from available mask. */ 231 for (i = 0; i < CNDEVTAB_SIZE; i++) 232 if (cnd == &cn_devtab[i]) { 233 cons_avail_mask &= ~(1 << i); 234 break; 235 } 236#if 0 237 /* 238 * XXX 239 * syscons gets really confused if console resources are 240 * freed after the system has initialized. 241 */ 242 if (cn->cn_term != NULL) 243 cn->cn_term(cn); 244#endif 245 return; 246 } 247} 248 249void 250cnselect(struct consdev *cn) 251{ 252 struct cn_device *cnd; 253 254 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 255 if (cnd->cnd_cn != cn) 256 continue; 257 if (cnd == STAILQ_FIRST(&cn_devlist)) 258 return; 259 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 260 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 261 return; 262 } 263} 264 265void 266cnavailable(struct consdev *cn, int available) 267{ 268 int i; 269 270 for (i = 0; i < CNDEVTAB_SIZE; i++) { 271 if (cn_devtab[i].cnd_cn == cn) 272 break; 273 } 274 if (available) { 275 if (i < CNDEVTAB_SIZE) 276 cons_avail_mask |= (1 << i); 277 cn->cn_flags &= ~CN_FLAG_NOAVAIL; 278 } else { 279 if (i < CNDEVTAB_SIZE) 280 cons_avail_mask &= ~(1 << i); 281 cn->cn_flags |= CN_FLAG_NOAVAIL; 282 } 283} 284 285int 286cnunavailable(void) 287{ 288 289 return (cons_avail_mask == 0); 290} 291 292/* 293 * XXX: rewrite to use sbufs instead 294 */ 295 296static int 297sysctl_kern_console(SYSCTL_HANDLER_ARGS) 298{ 299 struct cn_device *cnd; 300 struct consdev *cp, **list; 301 char *name, *p; 302 int delete, len, error; 303 304 len = 2; 305 SET_FOREACH(list, cons_set) { 306 cp = *list; 307 if (cp->cn_name[0] != '\0') 308 len += strlen(cp->cn_name) + 1; 309 } 310 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 311 len += strlen(cnd->cnd_cn->cn_name) + 1; 312 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX; 313 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO); 314 p = name; 315 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 316 p += sprintf(p, "%s,", cnd->cnd_cn->cn_name); 317 *p++ = '/'; 318 SET_FOREACH(list, cons_set) { 319 cp = *list; 320 if (cp->cn_name[0] != '\0') 321 p += sprintf(p, "%s,", cp->cn_name); 322 } 323 error = sysctl_handle_string(oidp, name, len, req); 324 if (error == 0 && req->newptr != NULL) { 325 p = name; 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 FREE(name, M_TEMP); 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, -1); 406 if (error == 0) { 407 NDFREE(&nd, NDF_ONLY_PNBUF); 408 VOP_UNLOCK(nd.ni_vp, 0, td); 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 = suser(td); 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 c = cn->cn_checkc(cn); 602 if (c != -1) { 603 return (c); 604 } 605 } 606 } 607 return (-1); 608} 609 610void 611cnputc(int c) 612{ 613 struct cn_device *cnd; 614 struct consdev *cn; 615 char *cp; 616 617 if (cn_mute || c == '\0') 618 return; 619 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 620 cn = cnd->cnd_cn; 621 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 622 if (c == '\n') 623 cn->cn_putc(cn, '\r'); 624 cn->cn_putc(cn, c); 625 } 626 } 627 if (console_pausing && c == '\n' && !kdb_active) { 628 for (cp = console_pausestr; *cp != '\0'; cp++) 629 cnputc(*cp); 630 if (cngetc() == '.') 631 console_pausing = 0; 632 cnputc('\r'); 633 for (cp = console_pausestr; *cp != '\0'; cp++) 634 cnputc(' '); 635 cnputc('\r'); 636 } 637} 638 639static int consmsgbuf_size = 8192; 640SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, 641 ""); 642 643/* 644 * Redirect console output to a tty. 645 */ 646void 647constty_set(struct tty *tp) 648{ 649 int size; 650 651 KASSERT(tp != NULL, ("constty_set: NULL tp")); 652 if (consbuf == NULL) { 653 size = consmsgbuf_size; 654 consbuf = malloc(size, M_TTYS, M_WAITOK); 655 msgbuf_init(&consmsgbuf, consbuf, size); 656 callout_init(&conscallout, 0); 657 } 658 constty = tp; 659 constty_timeout(NULL); 660} 661 662/* 663 * Disable console redirection to a tty. 664 */ 665void 666constty_clear(void) 667{ 668 int c; 669 670 constty = NULL; 671 if (consbuf == NULL) 672 return; 673 callout_stop(&conscallout); 674 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 675 cnputc(c); 676 free(consbuf, M_TTYS); 677 consbuf = NULL; 678} 679 680/* Times per second to check for pending console tty messages. */ 681static int constty_wakeups_per_second = 5; 682SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 683 &constty_wakeups_per_second, 0, ""); 684 685static void 686constty_timeout(void *arg) 687{ 688 int c; 689 690 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) { 691 if (tputchar(c, constty) < 0) 692 constty = NULL; 693 } 694 if (constty != NULL) { 695 callout_reset(&conscallout, hz / constty_wakeups_per_second, 696 constty_timeout, NULL); 697 } else { 698 /* Deallocate the constty buffer memory. */ 699 constty_clear(); 700 } 701} 702 703static void 704cn_drvinit(void *unused) 705{ 706 707 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console"); 708} 709 710SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL) 711