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