kern_cons.c revision 196506
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/kern_cons.c 196506 2009-08-24 10:53:30Z ed $"); 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#include <machine/clock.h> 68 69static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); 70 71struct cn_device { 72 STAILQ_ENTRY(cn_device) cnd_next; 73 struct consdev *cnd_cn; 74}; 75 76#define CNDEVPATHMAX 32 77#define CNDEVTAB_SIZE 4 78static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 79static STAILQ_HEAD(, cn_device) cn_devlist = 80 STAILQ_HEAD_INITIALIZER(cn_devlist); 81 82int cons_avail_mask = 0; /* Bit mask. Each registered low level console 83 * which is currently unavailable for inpit 84 * (i.e., if it is in graphics mode) will have 85 * this bit cleared. 86 */ 87static int cn_mute; 88static char *consbuf; /* buffer used by `consmsgbuf' */ 89static struct callout conscallout; /* callout for outputting to constty */ 90struct msgbuf consmsgbuf; /* message buffer for console tty */ 91static u_char console_pausing; /* pause after each line during probe */ 92static char *console_pausestr= 93"<pause; press any key to proceed to next line or '.' to end pause mode>"; 94struct tty *constty; /* pointer to console "window" tty */ 95static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 96static int use_cnputs_mtx = 0; /* != 0 if cnputs_mtx locking reqd. */ 97 98static void constty_timeout(void *arg); 99 100static struct consdev cons_consdev; 101DATA_SET(cons_set, cons_consdev); 102SET_DECLARE(cons_set, struct consdev); 103 104void 105cninit(void) 106{ 107 struct consdev *best_cn, *cn, **list; 108 109 /* 110 * Check if we should mute the console (for security reasons perhaps) 111 * It can be changes dynamically using sysctl kern.consmute 112 * once we are up and going. 113 * 114 */ 115 cn_mute = ((boothowto & (RB_MUTE 116 |RB_SINGLE 117 |RB_VERBOSE 118 |RB_ASKNAME)) == RB_MUTE); 119 120 /* 121 * Find the first console with the highest priority. 122 */ 123 best_cn = NULL; 124 SET_FOREACH(list, cons_set) { 125 cn = *list; 126 cnremove(cn); 127 /* Skip cons_consdev. */ 128 if (cn->cn_ops == NULL) 129 continue; 130 cn->cn_ops->cn_probe(cn); 131 if (cn->cn_pri == CN_DEAD) 132 continue; 133 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 134 best_cn = cn; 135 if (boothowto & RB_MULTIPLE) { 136 /* 137 * Initialize console, and attach to it. 138 */ 139 cn->cn_ops->cn_init(cn); 140 cnadd(cn); 141 } 142 } 143 if (best_cn == NULL) 144 return; 145 if ((boothowto & RB_MULTIPLE) == 0) { 146 best_cn->cn_ops->cn_init(best_cn); 147 cnadd(best_cn); 148 } 149 if (boothowto & RB_PAUSE) 150 console_pausing = 1; 151 /* 152 * Make the best console the preferred console. 153 */ 154 cnselect(best_cn); 155} 156 157void 158cninit_finish() 159{ 160 console_pausing = 0; 161} 162 163/* add a new physical console to back the virtual console */ 164int 165cnadd(struct consdev *cn) 166{ 167 struct cn_device *cnd; 168 int i; 169 170 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 171 if (cnd->cnd_cn == cn) 172 return (0); 173 for (i = 0; i < CNDEVTAB_SIZE; i++) { 174 cnd = &cn_devtab[i]; 175 if (cnd->cnd_cn == NULL) 176 break; 177 } 178 if (cnd->cnd_cn != NULL) 179 return (ENOMEM); 180 cnd->cnd_cn = cn; 181 if (cn->cn_name[0] == '\0') { 182 /* XXX: it is unclear if/where this print might output */ 183 printf("WARNING: console at %p has no name\n", cn); 184 } 185 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 186 if (STAILQ_FIRST(&cn_devlist) == cnd) 187 ttyconsdev_select(cnd->cnd_cn->cn_name); 188 189 /* Add device to the active mask. */ 190 cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 191 192 return (0); 193} 194 195void 196cnremove(struct consdev *cn) 197{ 198 struct cn_device *cnd; 199 int i; 200 201 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 202 if (cnd->cnd_cn != cn) 203 continue; 204 if (STAILQ_FIRST(&cn_devlist) == cnd) 205 ttyconsdev_select(NULL); 206 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 207 cnd->cnd_cn = NULL; 208 209 /* Remove this device from available mask. */ 210 for (i = 0; i < CNDEVTAB_SIZE; i++) 211 if (cnd == &cn_devtab[i]) { 212 cons_avail_mask &= ~(1 << i); 213 break; 214 } 215#if 0 216 /* 217 * XXX 218 * syscons gets really confused if console resources are 219 * freed after the system has initialized. 220 */ 221 if (cn->cn_term != NULL) 222 cn->cn_ops->cn_term(cn); 223#endif 224 return; 225 } 226} 227 228void 229cnselect(struct consdev *cn) 230{ 231 struct cn_device *cnd; 232 233 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 234 if (cnd->cnd_cn != cn) 235 continue; 236 if (cnd == STAILQ_FIRST(&cn_devlist)) 237 return; 238 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 239 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 240 ttyconsdev_select(cnd->cnd_cn->cn_name); 241 return; 242 } 243} 244 245void 246cnavailable(struct consdev *cn, int available) 247{ 248 int i; 249 250 for (i = 0; i < CNDEVTAB_SIZE; i++) { 251 if (cn_devtab[i].cnd_cn == cn) 252 break; 253 } 254 if (available) { 255 if (i < CNDEVTAB_SIZE) 256 cons_avail_mask |= (1 << i); 257 cn->cn_flags &= ~CN_FLAG_NOAVAIL; 258 } else { 259 if (i < CNDEVTAB_SIZE) 260 cons_avail_mask &= ~(1 << i); 261 cn->cn_flags |= CN_FLAG_NOAVAIL; 262 } 263} 264 265int 266cnunavailable(void) 267{ 268 269 return (cons_avail_mask == 0); 270} 271 272/* 273 * sysctl_kern_console() provides output parseable in conscontrol(1). 274 */ 275static int 276sysctl_kern_console(SYSCTL_HANDLER_ARGS) 277{ 278 struct cn_device *cnd; 279 struct consdev *cp, **list; 280 char *p; 281 int delete, error; 282 struct sbuf *sb; 283 284 sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND); 285 if (sb == NULL) 286 return (ENOMEM); 287 sbuf_clear(sb); 288 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 289 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 290 sbuf_printf(sb, "/"); 291 SET_FOREACH(list, cons_set) { 292 cp = *list; 293 if (cp->cn_name[0] != '\0') 294 sbuf_printf(sb, "%s,", cp->cn_name); 295 } 296 sbuf_finish(sb); 297 error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 298 if (error == 0 && req->newptr != NULL) { 299 p = sbuf_data(sb); 300 error = ENXIO; 301 delete = 0; 302 if (*p == '-') { 303 delete = 1; 304 p++; 305 } 306 SET_FOREACH(list, cons_set) { 307 cp = *list; 308 if (strcmp(p, cp->cn_name) != 0) 309 continue; 310 if (delete) { 311 cnremove(cp); 312 error = 0; 313 } else { 314 error = cnadd(cp); 315 if (error == 0) 316 cnselect(cp); 317 } 318 break; 319 } 320 } 321 sbuf_delete(sb); 322 return (error); 323} 324 325SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW, 326 0, 0, sysctl_kern_console, "A", "Console device control"); 327 328/* 329 * User has changed the state of the console muting. 330 * This may require us to open or close the device in question. 331 */ 332static int 333sysctl_kern_consmute(SYSCTL_HANDLER_ARGS) 334{ 335 int error; 336 337 error = sysctl_handle_int(oidp, &cn_mute, 0, req); 338 if (error != 0 || req->newptr == NULL) 339 return (error); 340 return (error); 341} 342 343SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 344 0, sizeof(cn_mute), sysctl_kern_consmute, "I", ""); 345 346/* 347 * Low level console routines. 348 */ 349int 350cngetc(void) 351{ 352 int c; 353 354 if (cn_mute) 355 return (-1); 356 while ((c = cncheckc()) == -1) 357 ; 358 if (c == '\r') 359 c = '\n'; /* console input is always ICRNL */ 360 return (c); 361} 362 363int 364cncheckc(void) 365{ 366 struct cn_device *cnd; 367 struct consdev *cn; 368 int c; 369 370 if (cn_mute) 371 return (-1); 372 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 373 cn = cnd->cnd_cn; 374 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 375 c = cn->cn_ops->cn_getc(cn); 376 if (c != -1) 377 return (c); 378 } 379 } 380 return (-1); 381} 382 383void 384cnputc(int c) 385{ 386 struct cn_device *cnd; 387 struct consdev *cn; 388 char *cp; 389 390 if (cn_mute || c == '\0') 391 return; 392 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 393 cn = cnd->cnd_cn; 394 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 395 if (c == '\n') 396 cn->cn_ops->cn_putc(cn, '\r'); 397 cn->cn_ops->cn_putc(cn, c); 398 } 399 } 400 if (console_pausing && c == '\n' && !kdb_active) { 401 for (cp = console_pausestr; *cp != '\0'; cp++) 402 cnputc(*cp); 403 if (cngetc() == '.') 404 console_pausing = 0; 405 cnputc('\r'); 406 for (cp = console_pausestr; *cp != '\0'; cp++) 407 cnputc(' '); 408 cnputc('\r'); 409 } 410} 411 412void 413cnputs(char *p) 414{ 415 int c; 416 int unlock_reqd = 0; 417 418 if (use_cnputs_mtx) { 419 mtx_lock_spin(&cnputs_mtx); 420 unlock_reqd = 1; 421 } 422 423 while ((c = *p++) != '\0') 424 cnputc(c); 425 426 if (unlock_reqd) 427 mtx_unlock_spin(&cnputs_mtx); 428} 429 430static int consmsgbuf_size = 8192; 431SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0, 432 ""); 433 434/* 435 * Redirect console output to a tty. 436 */ 437void 438constty_set(struct tty *tp) 439{ 440 int size; 441 442 KASSERT(tp != NULL, ("constty_set: NULL tp")); 443 if (consbuf == NULL) { 444 size = consmsgbuf_size; 445 consbuf = malloc(size, M_TTYCONS, M_WAITOK); 446 msgbuf_init(&consmsgbuf, consbuf, size); 447 callout_init(&conscallout, 0); 448 } 449 constty = tp; 450 constty_timeout(NULL); 451} 452 453/* 454 * Disable console redirection to a tty. 455 */ 456void 457constty_clear(void) 458{ 459 int c; 460 461 constty = NULL; 462 if (consbuf == NULL) 463 return; 464 callout_stop(&conscallout); 465 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 466 cnputc(c); 467 free(consbuf, M_TTYCONS); 468 consbuf = NULL; 469} 470 471/* Times per second to check for pending console tty messages. */ 472static int constty_wakeups_per_second = 5; 473SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 474 &constty_wakeups_per_second, 0, ""); 475 476static void 477constty_timeout(void *arg) 478{ 479 int c; 480 481 if (constty != NULL) { 482 tty_lock(constty); 483 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { 484 if (tty_putchar(constty, c) < 0) { 485 tty_unlock(constty); 486 constty = NULL; 487 break; 488 } 489 } 490 491 if (constty != NULL) 492 tty_unlock(constty); 493 } 494 if (constty != NULL) { 495 callout_reset(&conscallout, hz / constty_wakeups_per_second, 496 constty_timeout, NULL); 497 } else { 498 /* Deallocate the constty buffer memory. */ 499 constty_clear(); 500 } 501} 502 503static void 504cn_drvinit(void *unused) 505{ 506 507 mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS); 508 use_cnputs_mtx = 1; 509} 510 511SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL); 512 513/* 514 * Sysbeep(), if we have hardware for it 515 */ 516 517#ifdef HAS_TIMER_SPKR 518 519static int beeping; 520 521static void 522sysbeepstop(void *chan) 523{ 524 525 timer_spkr_release(); 526 beeping = 0; 527} 528 529int 530sysbeep(int pitch, int period) 531{ 532 533 if (timer_spkr_acquire()) { 534 if (!beeping) { 535 /* Something else owns it. */ 536 return (EBUSY); 537 } 538 } 539 timer_spkr_setfreq(pitch); 540 if (!beeping) { 541 beeping = period; 542 timeout(sysbeepstop, (void *)NULL, period); 543 } 544 return (0); 545} 546 547#else 548 549/* 550 * No hardware, no sound 551 */ 552 553int 554sysbeep(int pitch __unused, int period __unused) 555{ 556 557 return (ENODEV); 558} 559 560#endif 561 562