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