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  * Copyright (c) 1999 Michael Smith
    5  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
    6  *
    7  * All rights reserved.
    8  *
    9  * This code is derived from software contributed to Berkeley by
   10  * the Systems Programming Group of the University of Utah Computer
   11  * Science Department.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 4. Neither the name of the University nor the names of its contributors
   22  *    may be used to endorse or promote products derived from this software
   23  *    without specific prior written permission.
   24  *
   25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   35  * SUCH DAMAGE.
   36  *
   37  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
   38  */
   39 
   40 #include <sys/cdefs.h>
   41 __FBSDID("$FreeBSD: releng/9.2/sys/kern/kern_cons.c 241637 2012-10-17 11:30:14Z avg $");
   42 
   43 #include "opt_ddb.h"
   44 
   45 #include <sys/param.h>
   46 #include <sys/systm.h>
   47 #include <sys/lock.h>
   48 #include <sys/mutex.h>
   49 #include <sys/conf.h>
   50 #include <sys/cons.h>
   51 #include <sys/fcntl.h>
   52 #include <sys/kdb.h>
   53 #include <sys/kernel.h>
   54 #include <sys/malloc.h>
   55 #include <sys/msgbuf.h>
   56 #include <sys/namei.h>
   57 #include <sys/priv.h>
   58 #include <sys/proc.h>
   59 #include <sys/queue.h>
   60 #include <sys/reboot.h>
   61 #include <sys/sysctl.h>
   62 #include <sys/sbuf.h>
   63 #include <sys/tty.h>
   64 #include <sys/uio.h>
   65 #include <sys/vnode.h>
   66 
   67 #include <ddb/ddb.h>
   68 
   69 #include <machine/cpu.h>
   70 #include <machine/clock.h>
   71 
   72 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
   73 
   74 struct cn_device {
   75         STAILQ_ENTRY(cn_device) cnd_next;
   76         struct          consdev *cnd_cn;
   77 };
   78 
   79 #define CNDEVPATHMAX    32
   80 #define CNDEVTAB_SIZE   4
   81 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
   82 static STAILQ_HEAD(, cn_device) cn_devlist =
   83     STAILQ_HEAD_INITIALIZER(cn_devlist);
   84 
   85 int     cons_avail_mask = 0;    /* Bit mask. Each registered low level console
   86                                  * which is currently unavailable for inpit
   87                                  * (i.e., if it is in graphics mode) will have
   88                                  * this bit cleared.
   89                                  */
   90 static int cn_mute;
   91 static char *consbuf;                   /* buffer used by `consmsgbuf' */
   92 static struct callout conscallout;      /* callout for outputting to constty */
   93 struct msgbuf consmsgbuf;               /* message buffer for console tty */
   94 static u_char console_pausing;          /* pause after each line during probe */
   95 static char *console_pausestr=
   96 "<pause; press any key to proceed to next line or '.' to end pause mode>";
   97 struct tty *constty;                    /* pointer to console "window" tty */
   98 static struct mtx cnputs_mtx;           /* Mutex for cnputs(). */
   99 static int use_cnputs_mtx = 0;          /* != 0 if cnputs_mtx locking reqd. */
  100 
  101 static void constty_timeout(void *arg);
  102 
  103 static struct consdev cons_consdev;
  104 DATA_SET(cons_set, cons_consdev);
  105 SET_DECLARE(cons_set, struct consdev);
  106 
  107 void
  108 cninit(void)
  109 {
  110         struct consdev *best_cn, *cn, **list;
  111 
  112         /*
  113          * Check if we should mute the console (for security reasons perhaps)
  114          * It can be changes dynamically using sysctl kern.consmute
  115          * once we are up and going.
  116          * 
  117          */
  118         cn_mute = ((boothowto & (RB_MUTE
  119                         |RB_SINGLE
  120                         |RB_VERBOSE
  121                         |RB_ASKNAME)) == RB_MUTE);
  122 
  123         /*
  124          * Find the first console with the highest priority.
  125          */
  126         best_cn = NULL;
  127         SET_FOREACH(list, cons_set) {
  128                 cn = *list;
  129                 cnremove(cn);
  130                 /* Skip cons_consdev. */
  131                 if (cn->cn_ops == NULL)
  132                         continue;
  133                 cn->cn_ops->cn_probe(cn);
  134                 if (cn->cn_pri == CN_DEAD)
  135                         continue;
  136                 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
  137                         best_cn = cn;
  138                 if (boothowto & RB_MULTIPLE) {
  139                         /*
  140                          * Initialize console, and attach to it.
  141                          */
  142                         cn->cn_ops->cn_init(cn);
  143                         cnadd(cn);
  144                 }
  145         }
  146         if (best_cn == NULL)
  147                 return;
  148         if ((boothowto & RB_MULTIPLE) == 0) {
  149                 best_cn->cn_ops->cn_init(best_cn);
  150                 cnadd(best_cn);
  151         }
  152         if (boothowto & RB_PAUSE)
  153                 console_pausing = 1;
  154         /*
  155          * Make the best console the preferred console.
  156          */
  157         cnselect(best_cn);
  158 }
  159 
  160 void
  161 cninit_finish()
  162 {
  163         console_pausing = 0;
  164 } 
  165 
  166 /* add a new physical console to back the virtual console */
  167 int
  168 cnadd(struct consdev *cn)
  169 {
  170         struct cn_device *cnd;
  171         int i;
  172 
  173         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
  174                 if (cnd->cnd_cn == cn)
  175                         return (0);
  176         for (i = 0; i < CNDEVTAB_SIZE; i++) {
  177                 cnd = &cn_devtab[i];
  178                 if (cnd->cnd_cn == NULL)
  179                         break;
  180         }
  181         if (cnd->cnd_cn != NULL)
  182                 return (ENOMEM);
  183         cnd->cnd_cn = cn;
  184         if (cn->cn_name[0] == '\0') {
  185                 /* XXX: it is unclear if/where this print might output */
  186                 printf("WARNING: console at %p has no name\n", cn);
  187         }
  188         STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
  189         if (STAILQ_FIRST(&cn_devlist) == cnd)
  190                 ttyconsdev_select(cnd->cnd_cn->cn_name);
  191 
  192         /* Add device to the active mask. */
  193         cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
  194 
  195         return (0);
  196 }
  197 
  198 void
  199 cnremove(struct consdev *cn)
  200 {
  201         struct cn_device *cnd;
  202         int i;
  203 
  204         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  205                 if (cnd->cnd_cn != cn)
  206                         continue;
  207                 if (STAILQ_FIRST(&cn_devlist) == cnd)
  208                         ttyconsdev_select(NULL);
  209                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
  210                 cnd->cnd_cn = NULL;
  211 
  212                 /* Remove this device from available mask. */
  213                 for (i = 0; i < CNDEVTAB_SIZE; i++) 
  214                         if (cnd == &cn_devtab[i]) {
  215                                 cons_avail_mask &= ~(1 << i);
  216                                 break;
  217                         }
  218 #if 0
  219                 /*
  220                  * XXX
  221                  * syscons gets really confused if console resources are
  222                  * freed after the system has initialized.
  223                  */
  224                 if (cn->cn_term != NULL)
  225                         cn->cn_ops->cn_term(cn);
  226 #endif
  227                 return;
  228         }
  229 }
  230 
  231 void
  232 cnselect(struct consdev *cn)
  233 {
  234         struct cn_device *cnd;
  235 
  236         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  237                 if (cnd->cnd_cn != cn)
  238                         continue;
  239                 if (cnd == STAILQ_FIRST(&cn_devlist))
  240                         return;
  241                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
  242                 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
  243                 ttyconsdev_select(cnd->cnd_cn->cn_name);
  244                 return;
  245         }
  246 }
  247 
  248 void
  249 cnavailable(struct consdev *cn, int available)
  250 {
  251         int i;
  252 
  253         for (i = 0; i < CNDEVTAB_SIZE; i++) {
  254                 if (cn_devtab[i].cnd_cn == cn)
  255                         break;
  256         }
  257         if (available) {
  258                 if (i < CNDEVTAB_SIZE)
  259                         cons_avail_mask |= (1 << i); 
  260                 cn->cn_flags &= ~CN_FLAG_NOAVAIL;
  261         } else {
  262                 if (i < CNDEVTAB_SIZE)
  263                         cons_avail_mask &= ~(1 << i);
  264                 cn->cn_flags |= CN_FLAG_NOAVAIL;
  265         }
  266 }
  267 
  268 int
  269 cnunavailable(void)
  270 {
  271 
  272         return (cons_avail_mask == 0);
  273 }
  274 
  275 /*
  276  * sysctl_kern_console() provides output parseable in conscontrol(1).
  277  */
  278 static int
  279 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
  280 {
  281         struct cn_device *cnd;
  282         struct consdev *cp, **list;
  283         char *p;
  284         int delete, error;
  285         struct sbuf *sb;
  286 
  287         sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
  288         if (sb == NULL)
  289                 return (ENOMEM);
  290         sbuf_clear(sb);
  291         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
  292                 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
  293         sbuf_printf(sb, "/");
  294         SET_FOREACH(list, cons_set) {
  295                 cp = *list;
  296                 if (cp->cn_name[0] != '\0')
  297                         sbuf_printf(sb, "%s,", cp->cn_name);
  298         }
  299         sbuf_finish(sb);
  300         error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
  301         if (error == 0 && req->newptr != NULL) {
  302                 p = sbuf_data(sb);
  303                 error = ENXIO;
  304                 delete = 0;
  305                 if (*p == '-') {
  306                         delete = 1;
  307                         p++;
  308                 }
  309                 SET_FOREACH(list, cons_set) {
  310                         cp = *list;
  311                         if (strcmp(p, cp->cn_name) != 0)
  312                                 continue;
  313                         if (delete) {
  314                                 cnremove(cp);
  315                                 error = 0;
  316                         } else {
  317                                 error = cnadd(cp);
  318                                 if (error == 0)
  319                                         cnselect(cp);
  320                         }
  321                         break;
  322                 }
  323         }
  324         sbuf_delete(sb);
  325         return (error);
  326 }
  327 
  328 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
  329         0, 0, sysctl_kern_console, "A", "Console device control");
  330 
  331 /*
  332  * User has changed the state of the console muting.
  333  * This may require us to open or close the device in question.
  334  */
  335 static int
  336 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
  337 {
  338         int error;
  339 
  340         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
  341         if (error != 0 || req->newptr == NULL)
  342                 return (error);
  343         return (error);
  344 }
  345 
  346 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
  347         0, sizeof(cn_mute), sysctl_kern_consmute, "I",
  348         "State of the console muting");
  349 
  350 void
  351 cngrab()
  352 {
  353         struct cn_device *cnd;
  354         struct consdev *cn;
  355 
  356         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  357                 cn = cnd->cnd_cn;
  358                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
  359                         cn->cn_ops->cn_grab(cn);
  360         }
  361 }
  362 
  363 void
  364 cnungrab()
  365 {
  366         struct cn_device *cnd;
  367         struct consdev *cn;
  368 
  369         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  370                 cn = cnd->cnd_cn;
  371                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
  372                         cn->cn_ops->cn_ungrab(cn);
  373         }
  374 }
  375 
  376 /*
  377  * Low level console routines.
  378  */
  379 int
  380 cngetc(void)
  381 {
  382         int c;
  383 
  384         if (cn_mute)
  385                 return (-1);
  386         while ((c = cncheckc()) == -1)
  387                 cpu_spinwait();
  388         if (c == '\r')
  389                 c = '\n';               /* console input is always ICRNL */
  390         return (c);
  391 }
  392 
  393 int
  394 cncheckc(void)
  395 {
  396         struct cn_device *cnd;
  397         struct consdev *cn;
  398         int c;
  399 
  400         if (cn_mute)
  401                 return (-1);
  402         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  403                 cn = cnd->cnd_cn;
  404                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
  405                         c = cn->cn_ops->cn_getc(cn);
  406                         if (c != -1)
  407                                 return (c);
  408                 }
  409         }
  410         return (-1);
  411 }
  412 
  413 void
  414 cngets(char *cp, size_t size, int visible)
  415 {
  416         char *lp, *end;
  417         int c;
  418 
  419         cngrab();
  420 
  421         lp = cp;
  422         end = cp + size - 1;
  423         for (;;) {
  424                 c = cngetc() & 0177;
  425                 switch (c) {
  426                 case '\n':
  427                 case '\r':
  428                         cnputc(c);
  429                         *lp = '\0';
  430                         cnungrab();
  431                         return;
  432                 case '\b':
  433                 case '\177':
  434                         if (lp > cp) {
  435                                 if (visible) {
  436                                         cnputc(c);
  437                                         cnputs(" \b");
  438                                 }
  439                                 lp--;
  440                         }
  441                         continue;
  442                 case '\0':
  443                         continue;
  444                 default:
  445                         if (lp < end) {
  446                                 switch (visible) {
  447                                 case GETS_NOECHO:
  448                                         break;
  449                                 case GETS_ECHOPASS:
  450                                         cnputc('*');
  451                                         break;
  452                                 default:
  453                                         cnputc(c);
  454                                         break;
  455                                 }
  456                                 *lp++ = c;
  457                         }
  458                 }
  459         }
  460 }
  461 
  462 void
  463 cnputc(int c)
  464 {
  465         struct cn_device *cnd;
  466         struct consdev *cn;
  467         char *cp;
  468 
  469         if (cn_mute || c == '\0')
  470                 return;
  471         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
  472                 cn = cnd->cnd_cn;
  473                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
  474                         if (c == '\n')
  475                                 cn->cn_ops->cn_putc(cn, '\r');
  476                         cn->cn_ops->cn_putc(cn, c);
  477                 }
  478         }
  479         if (console_pausing && c == '\n' && !kdb_active) {
  480                 for (cp = console_pausestr; *cp != '\0'; cp++)
  481                         cnputc(*cp);
  482                 cngrab();
  483                 if (cngetc() == '.')
  484                         console_pausing = 0;
  485                 cnungrab();
  486                 cnputc('\r');
  487                 for (cp = console_pausestr; *cp != '\0'; cp++)
  488                         cnputc(' ');
  489                 cnputc('\r');
  490         }
  491 }
  492 
  493 void
  494 cnputs(char *p)
  495 {
  496         int c;
  497         int unlock_reqd = 0;
  498 
  499         if (use_cnputs_mtx) {
  500                 mtx_lock_spin(&cnputs_mtx);
  501                 unlock_reqd = 1;
  502         }
  503 
  504         while ((c = *p++) != '\0')
  505                 cnputc(c);
  506 
  507         if (unlock_reqd)
  508                 mtx_unlock_spin(&cnputs_mtx);
  509 }
  510 
  511 static int consmsgbuf_size = 8192;
  512 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
  513     "Console tty buffer size");
  514 
  515 /*
  516  * Redirect console output to a tty.
  517  */
  518 void
  519 constty_set(struct tty *tp)
  520 {
  521         int size;
  522 
  523         KASSERT(tp != NULL, ("constty_set: NULL tp"));
  524         if (consbuf == NULL) {
  525                 size = consmsgbuf_size;
  526                 consbuf = malloc(size, M_TTYCONS, M_WAITOK);
  527                 msgbuf_init(&consmsgbuf, consbuf, size);
  528                 callout_init(&conscallout, 0);
  529         }
  530         constty = tp;
  531         constty_timeout(NULL);
  532 }
  533 
  534 /*
  535  * Disable console redirection to a tty.
  536  */
  537 void
  538 constty_clear(void)
  539 {
  540         int c;
  541 
  542         constty = NULL;
  543         if (consbuf == NULL)
  544                 return;
  545         callout_stop(&conscallout);
  546         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
  547                 cnputc(c);
  548         free(consbuf, M_TTYCONS);
  549         consbuf = NULL;
  550 }
  551 
  552 /* Times per second to check for pending console tty messages. */
  553 static int constty_wakeups_per_second = 5;
  554 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
  555     &constty_wakeups_per_second, 0,
  556     "Times per second to check for pending console tty messages");
  557 
  558 static void
  559 constty_timeout(void *arg)
  560 {
  561         int c;
  562 
  563         if (constty != NULL) {
  564                 tty_lock(constty);
  565                 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
  566                         if (tty_putchar(constty, c) < 0) {
  567                                 tty_unlock(constty);
  568                                 constty = NULL;
  569                                 break;
  570                         }
  571                 }
  572 
  573                 if (constty != NULL)
  574                         tty_unlock(constty);
  575         }
  576         if (constty != NULL) {
  577                 callout_reset(&conscallout, hz / constty_wakeups_per_second,
  578                     constty_timeout, NULL);
  579         } else {
  580                 /* Deallocate the constty buffer memory. */
  581                 constty_clear();
  582         }
  583 }
  584 
  585 static void
  586 cn_drvinit(void *unused)
  587 {
  588 
  589         mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
  590         use_cnputs_mtx = 1;
  591 }
  592 
  593 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
  594 
  595 /*
  596  * Sysbeep(), if we have hardware for it
  597  */
  598 
  599 #ifdef HAS_TIMER_SPKR
  600 
  601 static int beeping;
  602 
  603 static void
  604 sysbeepstop(void *chan)
  605 {
  606 
  607         timer_spkr_release();
  608         beeping = 0;
  609 }
  610 
  611 int
  612 sysbeep(int pitch, int period)
  613 {
  614 
  615         if (timer_spkr_acquire()) {
  616                 if (!beeping) {
  617                         /* Something else owns it. */
  618                         return (EBUSY);
  619                 }
  620         }
  621         timer_spkr_setfreq(pitch);
  622         if (!beeping) {
  623                 beeping = period;
  624                 timeout(sysbeepstop, (void *)NULL, period);
  625         }
  626         return (0);
  627 }
  628 
  629 #else
  630 
  631 /*
  632  * No hardware, no sound
  633  */
  634 
  635 int
  636 sysbeep(int pitch __unused, int period __unused)
  637 {
  638 
  639         return (ENODEV);
  640 }
  641 
  642 #endif
  643 

Cache object: 2a92f8120ce5af2b2cb3cb06b5f6efb0


[ 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.