1c9dba40cSEd Schouten /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 4c9dba40cSEd Schouten * Copyright (c) 1988 University of Utah. 5c9dba40cSEd Schouten * Copyright (c) 1991 The Regents of the University of California. 6f389bc95SAndriy Gapon * Copyright (c) 1999 Michael Smith 7f389bc95SAndriy Gapon * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 8f389bc95SAndriy Gapon * 9c9dba40cSEd Schouten * All rights reserved. 10c9dba40cSEd Schouten * 11c9dba40cSEd Schouten * This code is derived from software contributed to Berkeley by 12c9dba40cSEd Schouten * the Systems Programming Group of the University of Utah Computer 13c9dba40cSEd Schouten * Science Department. 14c9dba40cSEd Schouten * 15c9dba40cSEd Schouten * Redistribution and use in source and binary forms, with or without 16c9dba40cSEd Schouten * modification, are permitted provided that the following conditions 17c9dba40cSEd Schouten * are met: 18c9dba40cSEd Schouten * 1. Redistributions of source code must retain the above copyright 19c9dba40cSEd Schouten * notice, this list of conditions and the following disclaimer. 20c9dba40cSEd Schouten * 2. Redistributions in binary form must reproduce the above copyright 21c9dba40cSEd Schouten * notice, this list of conditions and the following disclaimer in the 22c9dba40cSEd Schouten * documentation and/or other materials provided with the distribution. 2369a28758SEd Maste * 3. Neither the name of the University nor the names of its contributors 24c9dba40cSEd Schouten * may be used to endorse or promote products derived from this software 25c9dba40cSEd Schouten * without specific prior written permission. 26c9dba40cSEd Schouten * 27c9dba40cSEd Schouten * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28c9dba40cSEd Schouten * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29c9dba40cSEd Schouten * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30c9dba40cSEd Schouten * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31c9dba40cSEd Schouten * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32c9dba40cSEd Schouten * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33c9dba40cSEd Schouten * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34c9dba40cSEd Schouten * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35c9dba40cSEd Schouten * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36c9dba40cSEd Schouten * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37c9dba40cSEd Schouten * SUCH DAMAGE. 38c9dba40cSEd Schouten */ 39c9dba40cSEd Schouten 40c9dba40cSEd Schouten #include <sys/cdefs.h> 41c9dba40cSEd Schouten #include "opt_ddb.h" 4259644098SEd Maste #include "opt_syscons.h" 43c9dba40cSEd Schouten 44c9dba40cSEd Schouten #include <sys/param.h> 45c9dba40cSEd Schouten #include <sys/systm.h> 46c9dba40cSEd Schouten #include <sys/lock.h> 47c9dba40cSEd Schouten #include <sys/mutex.h> 48c9dba40cSEd Schouten #include <sys/conf.h> 49c9dba40cSEd Schouten #include <sys/cons.h> 50c9dba40cSEd Schouten #include <sys/fcntl.h> 513ed7166aSKyle Evans #include <sys/kbio.h> 52c9dba40cSEd Schouten #include <sys/kdb.h> 53c9dba40cSEd Schouten #include <sys/kernel.h> 54c9dba40cSEd Schouten #include <sys/malloc.h> 55c9dba40cSEd Schouten #include <sys/msgbuf.h> 56c9dba40cSEd Schouten #include <sys/namei.h> 57c9dba40cSEd Schouten #include <sys/priv.h> 58c9dba40cSEd Schouten #include <sys/proc.h> 59c9dba40cSEd Schouten #include <sys/queue.h> 60c9dba40cSEd Schouten #include <sys/reboot.h> 61c9dba40cSEd Schouten #include <sys/sysctl.h> 62c9dba40cSEd Schouten #include <sys/sbuf.h> 639d6ae1e3SColin Percival #include <sys/tslog.h> 64c9dba40cSEd Schouten #include <sys/tty.h> 65c9dba40cSEd Schouten #include <sys/uio.h> 66c9dba40cSEd Schouten #include <sys/vnode.h> 67c9dba40cSEd Schouten 68c9dba40cSEd Schouten #include <ddb/ddb.h> 69c9dba40cSEd Schouten 703ed7166aSKyle Evans #include <dev/kbd/kbdreg.h> 713ed7166aSKyle Evans 72c9dba40cSEd Schouten #include <machine/cpu.h> 73c9dba40cSEd Schouten #include <machine/clock.h> 74c9dba40cSEd Schouten 7520289092SAndrew Turner /* 7620289092SAndrew Turner * Check for 'options EARLY_PRINTF' that may have been used in old kernel 7720289092SAndrew Turner * config files. If you are hitting this error you should update your 7820289092SAndrew Turner * config to use 'options EARLY_PRINTF=<device name>', e.g. with the 7920289092SAndrew Turner * Arm pl011 use: 8020289092SAndrew Turner * 8120289092SAndrew Turner * options EARLY_PRINTF=pl011 8220289092SAndrew Turner */ 8320289092SAndrew Turner #if CHECK_EARLY_PRINTF(1) 8420289092SAndrew Turner #error Update your config to use 'options EARLY_PRINTF=<device name>' 8520289092SAndrew Turner #endif 8620289092SAndrew Turner 87c9dba40cSEd Schouten static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling"); 88c9dba40cSEd Schouten 89c9dba40cSEd Schouten struct cn_device { 90c9dba40cSEd Schouten STAILQ_ENTRY(cn_device) cnd_next; 91c9dba40cSEd Schouten struct consdev *cnd_cn; 92c9dba40cSEd Schouten }; 93c9dba40cSEd Schouten 94c9dba40cSEd Schouten #define CNDEVPATHMAX 32 95c9dba40cSEd Schouten #define CNDEVTAB_SIZE 4 96c9dba40cSEd Schouten static struct cn_device cn_devtab[CNDEVTAB_SIZE]; 97c9dba40cSEd Schouten static STAILQ_HEAD(, cn_device) cn_devlist = 98c9dba40cSEd Schouten STAILQ_HEAD_INITIALIZER(cn_devlist); 99c9dba40cSEd Schouten 100c9dba40cSEd Schouten int cons_avail_mask = 0; /* Bit mask. Each registered low level console 101c9dba40cSEd Schouten * which is currently unavailable for inpit 102c9dba40cSEd Schouten * (i.e., if it is in graphics mode) will have 103c9dba40cSEd Schouten * this bit cleared. 104c9dba40cSEd Schouten */ 105780766ebSMark Johnston 106*2cb49090SJustin Hibbits int cn_mute; 107780766ebSMark Johnston SYSCTL_INT(_kern, OID_AUTO, consmute, CTLFLAG_RW, &cn_mute, 0, 108780766ebSMark Johnston "State of the console muting"); 109780766ebSMark Johnston 110c9dba40cSEd Schouten static char *consbuf; /* buffer used by `consmsgbuf' */ 111c9dba40cSEd Schouten static struct callout conscallout; /* callout for outputting to constty */ 112c9dba40cSEd Schouten struct msgbuf consmsgbuf; /* message buffer for console tty */ 1132bfdc1eeSWarner Losh static bool console_pausing; /* pause after each line during probe */ 114fe20aaecSRyan Libby static const char console_pausestr[] = 115c9dba40cSEd Schouten "<pause; press any key to proceed to next line or '.' to end pause mode>"; 116c9dba40cSEd Schouten struct tty *constty; /* pointer to console "window" tty */ 117bd6085c6SAlexander Motin static struct mtx constty_mtx; /* Mutex for constty assignment. */ 118bd6085c6SAlexander Motin MTX_SYSINIT(constty_mtx, &constty_mtx, "constty_mtx", MTX_DEF); 119c9dba40cSEd Schouten static struct mtx cnputs_mtx; /* Mutex for cnputs(). */ 120bd6085c6SAlexander Motin MTX_SYSINIT(cnputs_mtx, &cnputs_mtx, "cnputs_mtx", MTX_SPIN | MTX_NOWITNESS); 121c9dba40cSEd Schouten 122c9dba40cSEd Schouten static void constty_timeout(void *arg); 123c9dba40cSEd Schouten 124c9dba40cSEd Schouten static struct consdev cons_consdev; 125c9dba40cSEd Schouten DATA_SET(cons_set, cons_consdev); 126c9dba40cSEd Schouten SET_DECLARE(cons_set, struct consdev); 127c9dba40cSEd Schouten 128f46412c0SKyle Evans /* 129f46412c0SKyle Evans * Stub for configurations that don't actually have a keyboard driver. Inclusion 130f46412c0SKyle Evans * of kbd.c is contingent on any number of keyboard/console drivers being 131f46412c0SKyle Evans * present in the kernel; rather than trying to catch them all, we'll just 132f46412c0SKyle Evans * maintain this weak kbdinit that will be overridden by the strong version in 133f46412c0SKyle Evans * kbd.c if it's present. 134f46412c0SKyle Evans */ 135f46412c0SKyle Evans __weak_symbol void 136f46412c0SKyle Evans kbdinit(void) 137f46412c0SKyle Evans { 138f46412c0SKyle Evans 139f46412c0SKyle Evans } 140f46412c0SKyle Evans 141*2cb49090SJustin Hibbits static void 142*2cb49090SJustin Hibbits mute_console(void *data __unused) 143*2cb49090SJustin Hibbits { 144*2cb49090SJustin Hibbits 145*2cb49090SJustin Hibbits if ((boothowto & (RB_MUTEMSGS | RB_VERBOSE)) == RB_MUTEMSGS) { 146*2cb49090SJustin Hibbits printf("-- Muting boot messages --\n"); 147*2cb49090SJustin Hibbits cn_mute = 1; 148*2cb49090SJustin Hibbits } 149*2cb49090SJustin Hibbits } 150*2cb49090SJustin Hibbits 151*2cb49090SJustin Hibbits SYSINIT(mute_console, SI_SUB_COPYRIGHT, SI_ORDER_ANY, mute_console, NULL); 152*2cb49090SJustin Hibbits 153c9dba40cSEd Schouten void 154c9dba40cSEd Schouten cninit(void) 155c9dba40cSEd Schouten { 156c9dba40cSEd Schouten struct consdev *best_cn, *cn, **list; 157c9dba40cSEd Schouten 1589d6ae1e3SColin Percival TSENTER(); 159c9dba40cSEd Schouten /* 160c9dba40cSEd Schouten * Check if we should mute the console (for security reasons perhaps) 161c9dba40cSEd Schouten * It can be changes dynamically using sysctl kern.consmute 162c9dba40cSEd Schouten * once we are up and going. 163c9dba40cSEd Schouten * 164c9dba40cSEd Schouten */ 165c9dba40cSEd Schouten cn_mute = ((boothowto & (RB_MUTE 166c9dba40cSEd Schouten |RB_SINGLE 167c9dba40cSEd Schouten |RB_VERBOSE 168c9dba40cSEd Schouten |RB_ASKNAME)) == RB_MUTE); 169c9dba40cSEd Schouten 170c9dba40cSEd Schouten /* 1713ed7166aSKyle Evans * Bring up the kbd layer just in time for cnprobe. Console drivers 1723ed7166aSKyle Evans * have a dependency on kbd being ready, so this fits nicely between the 1733ed7166aSKyle Evans * machdep callers of cninit() and MI probing/initialization of consoles 1743ed7166aSKyle Evans * here. 1753ed7166aSKyle Evans */ 1763ed7166aSKyle Evans kbdinit(); 1773ed7166aSKyle Evans 1783ed7166aSKyle Evans /* 179c9dba40cSEd Schouten * Find the first console with the highest priority. 180c9dba40cSEd Schouten */ 181c9dba40cSEd Schouten best_cn = NULL; 182c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 183c9dba40cSEd Schouten cn = *list; 184c9dba40cSEd Schouten cnremove(cn); 1852992abe0SEd Schouten /* Skip cons_consdev. */ 1862992abe0SEd Schouten if (cn->cn_ops == NULL) 187c9dba40cSEd Schouten continue; 1882992abe0SEd Schouten cn->cn_ops->cn_probe(cn); 189c9dba40cSEd Schouten if (cn->cn_pri == CN_DEAD) 190c9dba40cSEd Schouten continue; 191c9dba40cSEd Schouten if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 192c9dba40cSEd Schouten best_cn = cn; 193c9dba40cSEd Schouten if (boothowto & RB_MULTIPLE) { 194c9dba40cSEd Schouten /* 195c9dba40cSEd Schouten * Initialize console, and attach to it. 196c9dba40cSEd Schouten */ 1972992abe0SEd Schouten cn->cn_ops->cn_init(cn); 198c9dba40cSEd Schouten cnadd(cn); 199c9dba40cSEd Schouten } 200c9dba40cSEd Schouten } 201c9dba40cSEd Schouten if (best_cn == NULL) 202c9dba40cSEd Schouten return; 203c9dba40cSEd Schouten if ((boothowto & RB_MULTIPLE) == 0) { 2042992abe0SEd Schouten best_cn->cn_ops->cn_init(best_cn); 205c9dba40cSEd Schouten cnadd(best_cn); 206c9dba40cSEd Schouten } 207c9dba40cSEd Schouten if (boothowto & RB_PAUSE) 2082bfdc1eeSWarner Losh console_pausing = true; 209c9dba40cSEd Schouten /* 210c9dba40cSEd Schouten * Make the best console the preferred console. 211c9dba40cSEd Schouten */ 212c9dba40cSEd Schouten cnselect(best_cn); 213dc61566fSZbigniew Bodek 214dc61566fSZbigniew Bodek #ifdef EARLY_PRINTF 215dc61566fSZbigniew Bodek /* 216dc61566fSZbigniew Bodek * Release early console. 217dc61566fSZbigniew Bodek */ 218dc61566fSZbigniew Bodek early_putc = NULL; 219dc61566fSZbigniew Bodek #endif 2209d6ae1e3SColin Percival TSEXIT(); 221c9dba40cSEd Schouten } 222c9dba40cSEd Schouten 223c9dba40cSEd Schouten void 2249806e82aSDimitry Andric cninit_finish(void) 225c9dba40cSEd Schouten { 2262bfdc1eeSWarner Losh console_pausing = false; 227c9dba40cSEd Schouten } 228c9dba40cSEd Schouten 229c9dba40cSEd Schouten /* add a new physical console to back the virtual console */ 230c9dba40cSEd Schouten int 231c9dba40cSEd Schouten cnadd(struct consdev *cn) 232c9dba40cSEd Schouten { 233c9dba40cSEd Schouten struct cn_device *cnd; 234c9dba40cSEd Schouten int i; 235c9dba40cSEd Schouten 236c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 237c9dba40cSEd Schouten if (cnd->cnd_cn == cn) 238c9dba40cSEd Schouten return (0); 239c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 240c9dba40cSEd Schouten cnd = &cn_devtab[i]; 241c9dba40cSEd Schouten if (cnd->cnd_cn == NULL) 242c9dba40cSEd Schouten break; 243c9dba40cSEd Schouten } 244c9dba40cSEd Schouten if (cnd->cnd_cn != NULL) 245c9dba40cSEd Schouten return (ENOMEM); 246c9dba40cSEd Schouten cnd->cnd_cn = cn; 247c9dba40cSEd Schouten if (cn->cn_name[0] == '\0') { 248c9dba40cSEd Schouten /* XXX: it is unclear if/where this print might output */ 249c9dba40cSEd Schouten printf("WARNING: console at %p has no name\n", cn); 250c9dba40cSEd Schouten } 251c9dba40cSEd Schouten STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 252c9dba40cSEd Schouten if (STAILQ_FIRST(&cn_devlist) == cnd) 253c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 254c9dba40cSEd Schouten 255c9dba40cSEd Schouten /* Add device to the active mask. */ 256c9dba40cSEd Schouten cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 257c9dba40cSEd Schouten 258c9dba40cSEd Schouten return (0); 259c9dba40cSEd Schouten } 260c9dba40cSEd Schouten 261c9dba40cSEd Schouten void 262c9dba40cSEd Schouten cnremove(struct consdev *cn) 263c9dba40cSEd Schouten { 264c9dba40cSEd Schouten struct cn_device *cnd; 265c9dba40cSEd Schouten int i; 266c9dba40cSEd Schouten 267c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 268a68d5a66SKyle Evans if (cnd->cnd_cn != cn) 269c9dba40cSEd Schouten continue; 270a68d5a66SKyle Evans if (STAILQ_FIRST(&cn_devlist) == cnd) 271a68d5a66SKyle Evans ttyconsdev_select(NULL); 272c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 273c9dba40cSEd Schouten cnd->cnd_cn = NULL; 274c9dba40cSEd Schouten 275c9dba40cSEd Schouten /* Remove this device from available mask. */ 276c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) 277c9dba40cSEd Schouten if (cnd == &cn_devtab[i]) { 278c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 279c9dba40cSEd Schouten break; 280c9dba40cSEd Schouten } 281c9dba40cSEd Schouten #if 0 282c9dba40cSEd Schouten /* 283c9dba40cSEd Schouten * XXX 284c9dba40cSEd Schouten * syscons gets really confused if console resources are 285c9dba40cSEd Schouten * freed after the system has initialized. 286c9dba40cSEd Schouten */ 287c9dba40cSEd Schouten if (cn->cn_term != NULL) 2882992abe0SEd Schouten cn->cn_ops->cn_term(cn); 289c9dba40cSEd Schouten #endif 290c9dba40cSEd Schouten return; 291c9dba40cSEd Schouten } 292c9dba40cSEd Schouten } 293c9dba40cSEd Schouten 294c9dba40cSEd Schouten void 295c9dba40cSEd Schouten cnselect(struct consdev *cn) 296c9dba40cSEd Schouten { 297c9dba40cSEd Schouten struct cn_device *cnd; 298c9dba40cSEd Schouten 299c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 300c9dba40cSEd Schouten if (cnd->cnd_cn != cn) 301c9dba40cSEd Schouten continue; 302c9dba40cSEd Schouten if (cnd == STAILQ_FIRST(&cn_devlist)) 303c9dba40cSEd Schouten return; 304c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 305c9dba40cSEd Schouten STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 306c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 307c9dba40cSEd Schouten return; 308c9dba40cSEd Schouten } 309c9dba40cSEd Schouten } 310c9dba40cSEd Schouten 311c9dba40cSEd Schouten void 312c9dba40cSEd Schouten cnavailable(struct consdev *cn, int available) 313c9dba40cSEd Schouten { 314c9dba40cSEd Schouten int i; 315c9dba40cSEd Schouten 316c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 317c9dba40cSEd Schouten if (cn_devtab[i].cnd_cn == cn) 318c9dba40cSEd Schouten break; 319c9dba40cSEd Schouten } 320c9dba40cSEd Schouten if (available) { 321c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 322c9dba40cSEd Schouten cons_avail_mask |= (1 << i); 323c9dba40cSEd Schouten cn->cn_flags &= ~CN_FLAG_NOAVAIL; 324c9dba40cSEd Schouten } else { 325c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 326c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 327c9dba40cSEd Schouten cn->cn_flags |= CN_FLAG_NOAVAIL; 328c9dba40cSEd Schouten } 329c9dba40cSEd Schouten } 330c9dba40cSEd Schouten 331c9dba40cSEd Schouten int 332c9dba40cSEd Schouten cnunavailable(void) 333c9dba40cSEd Schouten { 334c9dba40cSEd Schouten 335c9dba40cSEd Schouten return (cons_avail_mask == 0); 336c9dba40cSEd Schouten } 337c9dba40cSEd Schouten 338c9dba40cSEd Schouten /* 339c9dba40cSEd Schouten * sysctl_kern_console() provides output parseable in conscontrol(1). 340c9dba40cSEd Schouten */ 341c9dba40cSEd Schouten static int 342c9dba40cSEd Schouten sysctl_kern_console(SYSCTL_HANDLER_ARGS) 343c9dba40cSEd Schouten { 344c9dba40cSEd Schouten struct cn_device *cnd; 345c9dba40cSEd Schouten struct consdev *cp, **list; 346c9dba40cSEd Schouten char *p; 3472bfdc1eeSWarner Losh bool delete; 3482bfdc1eeSWarner Losh int error; 349c9dba40cSEd Schouten struct sbuf *sb; 350c9dba40cSEd Schouten 351657282e0SIan Lepore sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND | 352657282e0SIan Lepore SBUF_INCLUDENUL); 353c9dba40cSEd Schouten if (sb == NULL) 354c9dba40cSEd Schouten return (ENOMEM); 355c9dba40cSEd Schouten sbuf_clear(sb); 356c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 357c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 3580a713948SAlexander Motin sbuf_putc(sb, '/'); 359c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 360c9dba40cSEd Schouten cp = *list; 361c9dba40cSEd Schouten if (cp->cn_name[0] != '\0') 362c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cp->cn_name); 363c9dba40cSEd Schouten } 364c9dba40cSEd Schouten sbuf_finish(sb); 365c9dba40cSEd Schouten error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 366c9dba40cSEd Schouten if (error == 0 && req->newptr != NULL) { 367c9dba40cSEd Schouten p = sbuf_data(sb); 368c9dba40cSEd Schouten error = ENXIO; 3692bfdc1eeSWarner Losh delete = false; 370c9dba40cSEd Schouten if (*p == '-') { 3712bfdc1eeSWarner Losh delete = true; 372c9dba40cSEd Schouten p++; 373c9dba40cSEd Schouten } 374c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 375c9dba40cSEd Schouten cp = *list; 376c9dba40cSEd Schouten if (strcmp(p, cp->cn_name) != 0) 377c9dba40cSEd Schouten continue; 378c9dba40cSEd Schouten if (delete) { 379c9dba40cSEd Schouten cnremove(cp); 380c9dba40cSEd Schouten error = 0; 381c9dba40cSEd Schouten } else { 382c9dba40cSEd Schouten error = cnadd(cp); 383a68d5a66SKyle Evans if (error == 0) 384a68d5a66SKyle Evans cnselect(cp); 385c9dba40cSEd Schouten } 386c9dba40cSEd Schouten break; 387c9dba40cSEd Schouten } 388c9dba40cSEd Schouten } 389c9dba40cSEd Schouten sbuf_delete(sb); 390c9dba40cSEd Schouten return (error); 391c9dba40cSEd Schouten } 392c9dba40cSEd Schouten 3937029da5cSPawel Biernacki SYSCTL_PROC(_kern, OID_AUTO, console, 3947029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, 0, 3957029da5cSPawel Biernacki sysctl_kern_console, "A", 3967029da5cSPawel Biernacki "Console device control"); 397c9dba40cSEd Schouten 3989976156fSAndriy Gapon void 3999806e82aSDimitry Andric cngrab(void) 4009976156fSAndriy Gapon { 4019976156fSAndriy Gapon struct cn_device *cnd; 4029976156fSAndriy Gapon struct consdev *cn; 4039976156fSAndriy Gapon 4049976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 4059976156fSAndriy Gapon cn = cnd->cnd_cn; 4069976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 4079976156fSAndriy Gapon cn->cn_ops->cn_grab(cn); 4089976156fSAndriy Gapon } 4099976156fSAndriy Gapon } 4109976156fSAndriy Gapon 4119976156fSAndriy Gapon void 4129806e82aSDimitry Andric cnungrab(void) 4139976156fSAndriy Gapon { 4149976156fSAndriy Gapon struct cn_device *cnd; 4159976156fSAndriy Gapon struct consdev *cn; 4169976156fSAndriy Gapon 4179976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 4189976156fSAndriy Gapon cn = cnd->cnd_cn; 4199976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 4209976156fSAndriy Gapon cn->cn_ops->cn_ungrab(cn); 4219976156fSAndriy Gapon } 4229976156fSAndriy Gapon } 4239976156fSAndriy Gapon 424ec6faf94SAndriy Gapon void 4259806e82aSDimitry Andric cnresume(void) 426ec6faf94SAndriy Gapon { 427ec6faf94SAndriy Gapon struct cn_device *cnd; 428ec6faf94SAndriy Gapon struct consdev *cn; 429ec6faf94SAndriy Gapon 430ec6faf94SAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 431ec6faf94SAndriy Gapon cn = cnd->cnd_cn; 432ec6faf94SAndriy Gapon if (cn->cn_ops->cn_resume != NULL) 433ec6faf94SAndriy Gapon cn->cn_ops->cn_resume(cn); 434ec6faf94SAndriy Gapon } 435ec6faf94SAndriy Gapon } 436ec6faf94SAndriy Gapon 437c9dba40cSEd Schouten /* 438c9dba40cSEd Schouten * Low level console routines. 439c9dba40cSEd Schouten */ 440c9dba40cSEd Schouten int 441c9dba40cSEd Schouten cngetc(void) 442c9dba40cSEd Schouten { 443c9dba40cSEd Schouten int c; 444c9dba40cSEd Schouten 445c9dba40cSEd Schouten if (cn_mute) 446c9dba40cSEd Schouten return (-1); 447c9dba40cSEd Schouten while ((c = cncheckc()) == -1) 448298fbd16SAndriy Gapon cpu_spinwait(); 449c9dba40cSEd Schouten if (c == '\r') 450c9dba40cSEd Schouten c = '\n'; /* console input is always ICRNL */ 451c9dba40cSEd Schouten return (c); 452c9dba40cSEd Schouten } 453c9dba40cSEd Schouten 454c9dba40cSEd Schouten int 455c9dba40cSEd Schouten cncheckc(void) 456c9dba40cSEd Schouten { 457c9dba40cSEd Schouten struct cn_device *cnd; 458c9dba40cSEd Schouten struct consdev *cn; 459c9dba40cSEd Schouten int c; 460c9dba40cSEd Schouten 461c9dba40cSEd Schouten if (cn_mute) 462c9dba40cSEd Schouten return (-1); 463c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 464c9dba40cSEd Schouten cn = cnd->cnd_cn; 465c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 4662992abe0SEd Schouten c = cn->cn_ops->cn_getc(cn); 4672992abe0SEd Schouten if (c != -1) 468c9dba40cSEd Schouten return (c); 469c9dba40cSEd Schouten } 470c9dba40cSEd Schouten } 471c9dba40cSEd Schouten return (-1); 472c9dba40cSEd Schouten } 473c9dba40cSEd Schouten 474c9dba40cSEd Schouten void 4758e628542SAndriy Gapon cngets(char *cp, size_t size, int visible) 4768e628542SAndriy Gapon { 4778e628542SAndriy Gapon char *lp, *end; 4788e628542SAndriy Gapon int c; 4798e628542SAndriy Gapon 4808e628542SAndriy Gapon cngrab(); 4818e628542SAndriy Gapon 4828e628542SAndriy Gapon lp = cp; 4838e628542SAndriy Gapon end = cp + size - 1; 4848e628542SAndriy Gapon for (;;) { 4858e628542SAndriy Gapon c = cngetc() & 0177; 4868e628542SAndriy Gapon switch (c) { 4878e628542SAndriy Gapon case '\n': 4888e628542SAndriy Gapon case '\r': 4898e628542SAndriy Gapon cnputc(c); 4908e628542SAndriy Gapon *lp = '\0'; 4918e628542SAndriy Gapon cnungrab(); 4928e628542SAndriy Gapon return; 4938e628542SAndriy Gapon case '\b': 4948e628542SAndriy Gapon case '\177': 4958e628542SAndriy Gapon if (lp > cp) { 4969520f952SWarner Losh if (visible) 4979520f952SWarner Losh cnputs("\b \b"); 4988e628542SAndriy Gapon lp--; 4998e628542SAndriy Gapon } 5008e628542SAndriy Gapon continue; 5018e628542SAndriy Gapon case '\0': 5028e628542SAndriy Gapon continue; 5038e628542SAndriy Gapon default: 5048e628542SAndriy Gapon if (lp < end) { 5058e628542SAndriy Gapon switch (visible) { 5068e628542SAndriy Gapon case GETS_NOECHO: 5078e628542SAndriy Gapon break; 5088e628542SAndriy Gapon case GETS_ECHOPASS: 5098e628542SAndriy Gapon cnputc('*'); 5108e628542SAndriy Gapon break; 5118e628542SAndriy Gapon default: 5128e628542SAndriy Gapon cnputc(c); 5138e628542SAndriy Gapon break; 5148e628542SAndriy Gapon } 5158e628542SAndriy Gapon *lp++ = c; 5168e628542SAndriy Gapon } 5178e628542SAndriy Gapon } 5188e628542SAndriy Gapon } 5198e628542SAndriy Gapon } 5208e628542SAndriy Gapon 5218e628542SAndriy Gapon void 522c9dba40cSEd Schouten cnputc(int c) 523c9dba40cSEd Schouten { 524c9dba40cSEd Schouten struct cn_device *cnd; 525c9dba40cSEd Schouten struct consdev *cn; 526fe20aaecSRyan Libby const char *cp; 527c9dba40cSEd Schouten 52842c8459bSIan Lepore #ifdef EARLY_PRINTF 52942c8459bSIan Lepore if (early_putc != NULL) { 53042c8459bSIan Lepore if (c == '\n') 53142c8459bSIan Lepore early_putc('\r'); 53242c8459bSIan Lepore early_putc(c); 53342c8459bSIan Lepore return; 53442c8459bSIan Lepore } 53542c8459bSIan Lepore #endif 53642c8459bSIan Lepore 537c9dba40cSEd Schouten if (cn_mute || c == '\0') 538c9dba40cSEd Schouten return; 539c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 540c9dba40cSEd Schouten cn = cnd->cnd_cn; 541c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 542c9dba40cSEd Schouten if (c == '\n') 5432992abe0SEd Schouten cn->cn_ops->cn_putc(cn, '\r'); 5442992abe0SEd Schouten cn->cn_ops->cn_putc(cn, c); 545c9dba40cSEd Schouten } 546c9dba40cSEd Schouten } 547c9dba40cSEd Schouten if (console_pausing && c == '\n' && !kdb_active) { 548c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 549c9dba40cSEd Schouten cnputc(*cp); 550bf8696b4SAndriy Gapon cngrab(); 551c9dba40cSEd Schouten if (cngetc() == '.') 5522bfdc1eeSWarner Losh console_pausing = false; 553bf8696b4SAndriy Gapon cnungrab(); 554c9dba40cSEd Schouten cnputc('\r'); 555c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 556c9dba40cSEd Schouten cnputc(' '); 557c9dba40cSEd Schouten cnputc('\r'); 558c9dba40cSEd Schouten } 559c9dba40cSEd Schouten } 560c9dba40cSEd Schouten 561c9dba40cSEd Schouten void 5626858c2ccSConrad Meyer cnputsn(const char *p, size_t n) 563c9dba40cSEd Schouten { 5646858c2ccSConrad Meyer size_t i; 5652bfdc1eeSWarner Losh bool unlock_reqd = false; 566c9dba40cSEd Schouten 567bd6085c6SAlexander Motin if (mtx_initialized(&cnputs_mtx)) { 56804a8159dSHans Petter Selasky /* 56904a8159dSHans Petter Selasky * NOTE: Debug prints and/or witness printouts in 57004a8159dSHans Petter Selasky * console driver clients can cause the "cnputs_mtx" 57104a8159dSHans Petter Selasky * mutex to recurse. Simply return if that happens. 57204a8159dSHans Petter Selasky */ 57304a8159dSHans Petter Selasky if (mtx_owned(&cnputs_mtx)) 57404a8159dSHans Petter Selasky return; 575c9dba40cSEd Schouten mtx_lock_spin(&cnputs_mtx); 5762bfdc1eeSWarner Losh unlock_reqd = true; 577c9dba40cSEd Schouten } 578c9dba40cSEd Schouten 5796858c2ccSConrad Meyer for (i = 0; i < n; i++) 5806858c2ccSConrad Meyer cnputc(p[i]); 581c9dba40cSEd Schouten 582c9dba40cSEd Schouten if (unlock_reqd) 583c9dba40cSEd Schouten mtx_unlock_spin(&cnputs_mtx); 584c9dba40cSEd Schouten } 585c9dba40cSEd Schouten 5866858c2ccSConrad Meyer void 587fe20aaecSRyan Libby cnputs(const char *p) 5886858c2ccSConrad Meyer { 5896858c2ccSConrad Meyer cnputsn(p, strlen(p)); 5906858c2ccSConrad Meyer } 5916858c2ccSConrad Meyer 592bd6085c6SAlexander Motin static unsigned int consmsgbuf_size = 65536; 593bd6085c6SAlexander Motin SYSCTL_UINT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RWTUN, &consmsgbuf_size, 594bd6085c6SAlexander Motin 0, "Console tty buffer size"); 595c9dba40cSEd Schouten 596c9dba40cSEd Schouten /* 597c9dba40cSEd Schouten * Redirect console output to a tty. 598c9dba40cSEd Schouten */ 599bd6085c6SAlexander Motin int 600c9dba40cSEd Schouten constty_set(struct tty *tp) 601c9dba40cSEd Schouten { 602bd6085c6SAlexander Motin int size = consmsgbuf_size; 603bd6085c6SAlexander Motin void *buf = NULL; 604c9dba40cSEd Schouten 605bd6085c6SAlexander Motin tty_assert_locked(tp); 606bd6085c6SAlexander Motin if (constty == tp) 607bd6085c6SAlexander Motin return (0); 608bd6085c6SAlexander Motin if (constty != NULL) 609bd6085c6SAlexander Motin return (EBUSY); 610bd6085c6SAlexander Motin 611c9dba40cSEd Schouten if (consbuf == NULL) { 612bd6085c6SAlexander Motin tty_unlock(tp); 613bd6085c6SAlexander Motin buf = malloc(size, M_TTYCONS, M_WAITOK); 614bd6085c6SAlexander Motin tty_lock(tp); 615c9dba40cSEd Schouten } 616bd6085c6SAlexander Motin mtx_lock(&constty_mtx); 617bd6085c6SAlexander Motin if (constty != NULL) { 618bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 619bd6085c6SAlexander Motin free(buf, M_TTYCONS); 620bd6085c6SAlexander Motin return (EBUSY); 621bd6085c6SAlexander Motin } 622bd6085c6SAlexander Motin if (consbuf == NULL) { 623bd6085c6SAlexander Motin consbuf = buf; 624bd6085c6SAlexander Motin msgbuf_init(&consmsgbuf, buf, size); 625bd6085c6SAlexander Motin } else 626bd6085c6SAlexander Motin free(buf, M_TTYCONS); 627c9dba40cSEd Schouten constty = tp; 628bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 629bd6085c6SAlexander Motin 630bd6085c6SAlexander Motin callout_init_mtx(&conscallout, tty_getlock(tp), 0); 631bd6085c6SAlexander Motin constty_timeout(tp); 632bd6085c6SAlexander Motin return (0); 633c9dba40cSEd Schouten } 634c9dba40cSEd Schouten 635c9dba40cSEd Schouten /* 636c9dba40cSEd Schouten * Disable console redirection to a tty. 637c9dba40cSEd Schouten */ 638bd6085c6SAlexander Motin int 639bd6085c6SAlexander Motin constty_clear(struct tty *tp) 640c9dba40cSEd Schouten { 641c9dba40cSEd Schouten int c; 642c9dba40cSEd Schouten 643bd6085c6SAlexander Motin tty_assert_locked(tp); 644bd6085c6SAlexander Motin if (constty != tp) 645bd6085c6SAlexander Motin return (ENXIO); 646c9dba40cSEd Schouten callout_stop(&conscallout); 647bd6085c6SAlexander Motin mtx_lock(&constty_mtx); 648bd6085c6SAlexander Motin constty = NULL; 649bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 650c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 651c9dba40cSEd Schouten cnputc(c); 652bd6085c6SAlexander Motin /* We never free consbuf because it can still be in use. */ 653bd6085c6SAlexander Motin return (0); 654c9dba40cSEd Schouten } 655c9dba40cSEd Schouten 656c9dba40cSEd Schouten /* Times per second to check for pending console tty messages. */ 657bd6085c6SAlexander Motin static int constty_wakeups_per_second = 15; 658c9dba40cSEd Schouten SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 659a0c87b74SGavin Atkinson &constty_wakeups_per_second, 0, 660a0c87b74SGavin Atkinson "Times per second to check for pending console tty messages"); 661c9dba40cSEd Schouten 662c9dba40cSEd Schouten static void 663c9dba40cSEd Schouten constty_timeout(void *arg) 664c9dba40cSEd Schouten { 665bd6085c6SAlexander Motin struct tty *tp = arg; 666c9dba40cSEd Schouten int c; 667c9dba40cSEd Schouten 668bd6085c6SAlexander Motin tty_assert_locked(tp); 669c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { 670bd6085c6SAlexander Motin if (tty_putchar(tp, c) < 0) { 671bd6085c6SAlexander Motin constty_clear(tp); 672bd6085c6SAlexander Motin return; 673c9dba40cSEd Schouten } 674c9dba40cSEd Schouten } 675bd6085c6SAlexander Motin callout_reset_sbt(&conscallout, SBT_1S / constty_wakeups_per_second, 676bd6085c6SAlexander Motin 0, constty_timeout, tp, C_PREL(1)); 677c9dba40cSEd Schouten } 678c9dba40cSEd Schouten 679c9dba40cSEd Schouten /* 680c9dba40cSEd Schouten * Sysbeep(), if we have hardware for it 681c9dba40cSEd Schouten */ 682c9dba40cSEd Schouten 683c9dba40cSEd Schouten #ifdef HAS_TIMER_SPKR 684c9dba40cSEd Schouten 685072d5b98SWarner Losh static bool beeping; 686fadf3fb9SJohn Baldwin static struct callout beeping_timer; 687c9dba40cSEd Schouten 688c9dba40cSEd Schouten static void 689c9dba40cSEd Schouten sysbeepstop(void *chan) 690c9dba40cSEd Schouten { 691c9dba40cSEd Schouten 692c9dba40cSEd Schouten timer_spkr_release(); 693072d5b98SWarner Losh beeping = false; 694c9dba40cSEd Schouten } 695c9dba40cSEd Schouten 696c9dba40cSEd Schouten int 697072d5b98SWarner Losh sysbeep(int pitch, sbintime_t duration) 698c9dba40cSEd Schouten { 699c9dba40cSEd Schouten 700c9dba40cSEd Schouten if (timer_spkr_acquire()) { 701c9dba40cSEd Schouten if (!beeping) { 702c9dba40cSEd Schouten /* Something else owns it. */ 703c9dba40cSEd Schouten return (EBUSY); 704c9dba40cSEd Schouten } 705c9dba40cSEd Schouten } 706c9dba40cSEd Schouten timer_spkr_setfreq(pitch); 707c9dba40cSEd Schouten if (!beeping) { 708072d5b98SWarner Losh beeping = true; 709072d5b98SWarner Losh callout_reset_sbt(&beeping_timer, duration, 0, sysbeepstop, 710072d5b98SWarner Losh NULL, C_PREL(5)); 711c9dba40cSEd Schouten } 712c9dba40cSEd Schouten return (0); 713c9dba40cSEd Schouten } 714c9dba40cSEd Schouten 715fadf3fb9SJohn Baldwin static void 716fadf3fb9SJohn Baldwin sysbeep_init(void *unused) 717fadf3fb9SJohn Baldwin { 718fadf3fb9SJohn Baldwin 719fd90e2edSJung-uk Kim callout_init(&beeping_timer, 1); 720fadf3fb9SJohn Baldwin } 721fadf3fb9SJohn Baldwin SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL); 722c9dba40cSEd Schouten #else 723c9dba40cSEd Schouten 724c9dba40cSEd Schouten /* 725c9dba40cSEd Schouten * No hardware, no sound 726c9dba40cSEd Schouten */ 727c9dba40cSEd Schouten 728c9dba40cSEd Schouten int 729072d5b98SWarner Losh sysbeep(int pitch __unused, sbintime_t duration __unused) 730c9dba40cSEd Schouten { 731c9dba40cSEd Schouten 732c9dba40cSEd Schouten return (ENODEV); 733c9dba40cSEd Schouten } 734c9dba40cSEd Schouten 735c9dba40cSEd Schouten #endif 736c9dba40cSEd Schouten 73759644098SEd Maste /* 73859644098SEd Maste * Temporary support for sc(4) to vt(4) transition. 73959644098SEd Maste */ 740018147eeSEd Maste static unsigned vty_prefer; 741af3b2549SHans Petter Selasky static char vty_name[16]; 742af3b2549SHans Petter Selasky SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name, 743af3b2549SHans Petter Selasky 0, "Console vty driver"); 74459644098SEd Maste 74559644098SEd Maste int 74659644098SEd Maste vty_enabled(unsigned vty) 74759644098SEd Maste { 74859644098SEd Maste static unsigned vty_selected = 0; 74959644098SEd Maste 75059644098SEd Maste if (vty_selected == 0) { 75159644098SEd Maste TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name)); 75259644098SEd Maste do { 75359644098SEd Maste #if defined(DEV_SC) 75459644098SEd Maste if (strcmp(vty_name, "sc") == 0) { 75559644098SEd Maste vty_selected = VTY_SC; 75659644098SEd Maste break; 75759644098SEd Maste } 75859644098SEd Maste #endif 75959644098SEd Maste #if defined(DEV_VT) 76059644098SEd Maste if (strcmp(vty_name, "vt") == 0) { 76159644098SEd Maste vty_selected = VTY_VT; 76259644098SEd Maste break; 76359644098SEd Maste } 76459644098SEd Maste #endif 765018147eeSEd Maste if (vty_prefer != 0) { 766018147eeSEd Maste vty_selected = vty_prefer; 767018147eeSEd Maste break; 768018147eeSEd Maste } 7692d6f6d63SJean-Sébastien Pédron #if defined(DEV_VT) 77059644098SEd Maste vty_selected = VTY_VT; 7712d6f6d63SJean-Sébastien Pédron #elif defined(DEV_SC) 7722d6f6d63SJean-Sébastien Pédron vty_selected = VTY_SC; 77359644098SEd Maste #endif 77459644098SEd Maste } while (0); 77559644098SEd Maste 77659644098SEd Maste if (vty_selected == VTY_VT) 77759644098SEd Maste strcpy(vty_name, "vt"); 77859644098SEd Maste else if (vty_selected == VTY_SC) 77959644098SEd Maste strcpy(vty_name, "sc"); 78059644098SEd Maste } 78159644098SEd Maste return ((vty_selected & vty) != 0); 78259644098SEd Maste } 78359644098SEd Maste 784018147eeSEd Maste void 785018147eeSEd Maste vty_set_preferred(unsigned vty) 786018147eeSEd Maste { 787018147eeSEd Maste 788018147eeSEd Maste vty_prefer = vty; 789018147eeSEd Maste #if !defined(DEV_SC) 790969d3cc2SEd Maste vty_prefer &= ~VTY_SC; 791018147eeSEd Maste #endif 792018147eeSEd Maste #if !defined(DEV_VT) 793969d3cc2SEd Maste vty_prefer &= ~VTY_VT; 794018147eeSEd Maste #endif 795018147eeSEd Maste } 796