The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_cons.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/9.0/sys/kern/kern_cons.c 211102 2010-08-09 14:48:31Z gavin $");
   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                 /* 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 
  157 void
  158 cninit_finish()
  159 {
  160         console_pausing = 0;
  161 } 
  162 
  163 /* add a new physical console to back the virtual console */
  164 int
  165 cnadd(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 
  195 void
  196 cnremove(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 
  228 void
  229 cnselect(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 
  245 void
  246 cnavailable(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 
  265 int
  266 cnunavailable(void)
  267 {
  268 
  269         return (cons_avail_mask == 0);
  270 }
  271 
  272 /*
  273  * sysctl_kern_console() provides output parseable in conscontrol(1).
  274  */
  275 static int
  276 sysctl_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 
  325 SYSCTL_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  */
  332 static int
  333 sysctl_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 
  343 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
  344         0, sizeof(cn_mute), sysctl_kern_consmute, "I",
  345         "State of the console muting");
  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                         c = cn->cn_ops->cn_getc(cn);
  377                         if (c != -1)
  378                                 return (c);
  379                 }
  380         }
  381         return (-1);
  382 }
  383 
  384 void
  385 cnputc(int c)
  386 {
  387         struct cn_device *cnd;
  388         struct consdev *cn;
  389         char *cp;
  390 
  391         if (cn_mute || c == '\0')
  392                 return;
  393         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  394                 cn = cnd->cnd_cn;
  395                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
  396                         if (c == '\n')
  397                                 cn->cn_ops->cn_putc(cn, '\r');
  398                         cn->cn_ops->cn_putc(cn, c);
  399                 }
  400         }
  401         if (console_pausing && c == '\n' && !kdb_active) {
  402                 for (cp = console_pausestr; *cp != '\0'; cp++)
  403                         cnputc(*cp);
  404                 if (cngetc() == '.')
  405                         console_pausing = 0;
  406                 cnputc('\r');
  407                 for (cp = console_pausestr; *cp != '\0'; cp++)
  408                         cnputc(' ');
  409                 cnputc('\r');
  410         }
  411 }
  412 
  413 void
  414 cnputs(char *p)
  415 {
  416         int c;
  417         int unlock_reqd = 0;
  418 
  419         if (use_cnputs_mtx) {
  420                 mtx_lock_spin(&cnputs_mtx);
  421                 unlock_reqd = 1;
  422         }
  423 
  424         while ((c = *p++) != '\0')
  425                 cnputc(c);
  426 
  427         if (unlock_reqd)
  428                 mtx_unlock_spin(&cnputs_mtx);
  429 }
  430 
  431 static int consmsgbuf_size = 8192;
  432 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
  433     "Console tty buffer size");
  434 
  435 /*
  436  * Redirect console output to a tty.
  437  */
  438 void
  439 constty_set(struct tty *tp)
  440 {
  441         int size;
  442 
  443         KASSERT(tp != NULL, ("constty_set: NULL tp"));
  444         if (consbuf == NULL) {
  445                 size = consmsgbuf_size;
  446                 consbuf = malloc(size, M_TTYCONS, M_WAITOK);
  447                 msgbuf_init(&consmsgbuf, consbuf, size);
  448                 callout_init(&conscallout, 0);
  449         }
  450         constty = tp;
  451         constty_timeout(NULL);
  452 }
  453 
  454 /*
  455  * Disable console redirection to a tty.
  456  */
  457 void
  458 constty_clear(void)
  459 {
  460         int c;
  461 
  462         constty = NULL;
  463         if (consbuf == NULL)
  464                 return;
  465         callout_stop(&conscallout);
  466         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
  467                 cnputc(c);
  468         free(consbuf, M_TTYCONS);
  469         consbuf = NULL;
  470 }
  471 
  472 /* Times per second to check for pending console tty messages. */
  473 static int constty_wakeups_per_second = 5;
  474 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
  475     &constty_wakeups_per_second, 0,
  476     "Times per second to check for pending console tty messages");
  477 
  478 static void
  479 constty_timeout(void *arg)
  480 {
  481         int c;
  482 
  483         if (constty != NULL) {
  484                 tty_lock(constty);
  485                 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
  486                         if (tty_putchar(constty, c) < 0) {
  487                                 tty_unlock(constty);
  488                                 constty = NULL;
  489                                 break;
  490                         }
  491                 }
  492 
  493                 if (constty != NULL)
  494                         tty_unlock(constty);
  495         }
  496         if (constty != NULL) {
  497                 callout_reset(&conscallout, hz / constty_wakeups_per_second,
  498                     constty_timeout, NULL);
  499         } else {
  500                 /* Deallocate the constty buffer memory. */
  501                 constty_clear();
  502         }
  503 }
  504 
  505 static void
  506 cn_drvinit(void *unused)
  507 {
  508 
  509         mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
  510         use_cnputs_mtx = 1;
  511 }
  512 
  513 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
  514 
  515 /*
  516  * Sysbeep(), if we have hardware for it
  517  */
  518 
  519 #ifdef HAS_TIMER_SPKR
  520 
  521 static int beeping;
  522 
  523 static void
  524 sysbeepstop(void *chan)
  525 {
  526 
  527         timer_spkr_release();
  528         beeping = 0;
  529 }
  530 
  531 int
  532 sysbeep(int pitch, int period)
  533 {
  534 
  535         if (timer_spkr_acquire()) {
  536                 if (!beeping) {
  537                         /* Something else owns it. */
  538                         return (EBUSY);
  539                 }
  540         }
  541         timer_spkr_setfreq(pitch);
  542         if (!beeping) {
  543                 beeping = period;
  544                 timeout(sysbeepstop, (void *)NULL, period);
  545         }
  546         return (0);
  547 }
  548 
  549 #else
  550 
  551 /*
  552  * No hardware, no sound
  553  */
  554 
  555 int
  556 sysbeep(int pitch __unused, int period __unused)
  557 {
  558 
  559         return (ENODEV);
  560 }
  561 
  562 #endif
  563 

Cache object: b1e5e7e4b4c829c1ce0398d01cfa96d9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.