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$");
   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                 /*
  501                  * NOTE: Debug prints and/or witness printouts in
  502                  * console driver clients can cause the "cnputs_mtx"
  503                  * mutex to recurse. Simply return if that happens.
  504                  */
  505                 if (mtx_owned(&cnputs_mtx))
  506                         return;
  507                 mtx_lock_spin(&cnputs_mtx);
  508                 unlock_reqd = 1;
  509         }
  510 
  511         while ((c = *p++) != '\0')
  512                 cnputc(c);
  513 
  514         if (unlock_reqd)
  515                 mtx_unlock_spin(&cnputs_mtx);
  516 }
  517 
  518 static int consmsgbuf_size = 8192;
  519 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
  520     "Console tty buffer size");
  521 
  522 /*
  523  * Redirect console output to a tty.
  524  */
  525 void
  526 constty_set(struct tty *tp)
  527 {
  528         int size;
  529 
  530         KASSERT(tp != NULL, ("constty_set: NULL tp"));
  531         if (consbuf == NULL) {
  532                 size = consmsgbuf_size;
  533                 consbuf = malloc(size, M_TTYCONS, M_WAITOK);
  534                 msgbuf_init(&consmsgbuf, consbuf, size);
  535                 callout_init(&conscallout, 0);
  536         }
  537         constty = tp;
  538         constty_timeout(NULL);
  539 }
  540 
  541 /*
  542  * Disable console redirection to a tty.
  543  */
  544 void
  545 constty_clear(void)
  546 {
  547         int c;
  548 
  549         constty = NULL;
  550         if (consbuf == NULL)
  551                 return;
  552         callout_stop(&conscallout);
  553         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
  554                 cnputc(c);
  555         free(consbuf, M_TTYCONS);
  556         consbuf = NULL;
  557 }
  558 
  559 /* Times per second to check for pending console tty messages. */
  560 static int constty_wakeups_per_second = 5;
  561 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
  562     &constty_wakeups_per_second, 0,
  563     "Times per second to check for pending console tty messages");
  564 
  565 static void
  566 constty_timeout(void *arg)
  567 {
  568         int c;
  569 
  570         if (constty != NULL) {
  571                 tty_lock(constty);
  572                 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
  573                         if (tty_putchar(constty, c) < 0) {
  574                                 tty_unlock(constty);
  575                                 constty = NULL;
  576                                 break;
  577                         }
  578                 }
  579 
  580                 if (constty != NULL)
  581                         tty_unlock(constty);
  582         }
  583         if (constty != NULL) {
  584                 callout_reset(&conscallout, hz / constty_wakeups_per_second,
  585                     constty_timeout, NULL);
  586         } else {
  587                 /* Deallocate the constty buffer memory. */
  588                 constty_clear();
  589         }
  590 }
  591 
  592 static void
  593 cn_drvinit(void *unused)
  594 {
  595 
  596         mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
  597         use_cnputs_mtx = 1;
  598 }
  599 
  600 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
  601 
  602 /*
  603  * Sysbeep(), if we have hardware for it
  604  */
  605 
  606 #ifdef HAS_TIMER_SPKR
  607 
  608 static int beeping;
  609 
  610 static void
  611 sysbeepstop(void *chan)
  612 {
  613 
  614         timer_spkr_release();
  615         beeping = 0;
  616 }
  617 
  618 int
  619 sysbeep(int pitch, int period)
  620 {
  621 
  622         if (timer_spkr_acquire()) {
  623                 if (!beeping) {
  624                         /* Something else owns it. */
  625                         return (EBUSY);
  626                 }
  627         }
  628         timer_spkr_setfreq(pitch);
  629         if (!beeping) {
  630                 beeping = period;
  631                 timeout(sysbeepstop, (void *)NULL, period);
  632         }
  633         return (0);
  634 }
  635 
  636 #else
  637 
  638 /*
  639  * No hardware, no sound
  640  */
  641 
  642 int
  643 sysbeep(int pitch __unused, int period __unused)
  644 {
  645 
  646         return (ENODEV);
  647 }
  648 
  649 #endif
  650 

Cache object: 242ee5e6df179d65d03ab7e2a82da40a


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