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 75*20289092SAndrew Turner /* 76*20289092SAndrew Turner * Check for 'options EARLY_PRINTF' that may have been used in old kernel 77*20289092SAndrew Turner * config files. If you are hitting this error you should update your 78*20289092SAndrew Turner * config to use 'options EARLY_PRINTF=<device name>', e.g. with the 79*20289092SAndrew Turner * Arm pl011 use: 80*20289092SAndrew Turner * 81*20289092SAndrew Turner * options EARLY_PRINTF=pl011 82*20289092SAndrew Turner */ 83*20289092SAndrew Turner #if CHECK_EARLY_PRINTF(1) 84*20289092SAndrew Turner #error Update your config to use 'options EARLY_PRINTF=<device name>' 85*20289092SAndrew Turner #endif 86*20289092SAndrew 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 106c9dba40cSEd Schouten static 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 141c9dba40cSEd Schouten void 142c9dba40cSEd Schouten cninit(void) 143c9dba40cSEd Schouten { 144c9dba40cSEd Schouten struct consdev *best_cn, *cn, **list; 145c9dba40cSEd Schouten 1469d6ae1e3SColin Percival TSENTER(); 147c9dba40cSEd Schouten /* 148c9dba40cSEd Schouten * Check if we should mute the console (for security reasons perhaps) 149c9dba40cSEd Schouten * It can be changes dynamically using sysctl kern.consmute 150c9dba40cSEd Schouten * once we are up and going. 151c9dba40cSEd Schouten * 152c9dba40cSEd Schouten */ 153c9dba40cSEd Schouten cn_mute = ((boothowto & (RB_MUTE 154c9dba40cSEd Schouten |RB_SINGLE 155c9dba40cSEd Schouten |RB_VERBOSE 156c9dba40cSEd Schouten |RB_ASKNAME)) == RB_MUTE); 157c9dba40cSEd Schouten 158c9dba40cSEd Schouten /* 1593ed7166aSKyle Evans * Bring up the kbd layer just in time for cnprobe. Console drivers 1603ed7166aSKyle Evans * have a dependency on kbd being ready, so this fits nicely between the 1613ed7166aSKyle Evans * machdep callers of cninit() and MI probing/initialization of consoles 1623ed7166aSKyle Evans * here. 1633ed7166aSKyle Evans */ 1643ed7166aSKyle Evans kbdinit(); 1653ed7166aSKyle Evans 1663ed7166aSKyle Evans /* 167c9dba40cSEd Schouten * Find the first console with the highest priority. 168c9dba40cSEd Schouten */ 169c9dba40cSEd Schouten best_cn = NULL; 170c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 171c9dba40cSEd Schouten cn = *list; 172c9dba40cSEd Schouten cnremove(cn); 1732992abe0SEd Schouten /* Skip cons_consdev. */ 1742992abe0SEd Schouten if (cn->cn_ops == NULL) 175c9dba40cSEd Schouten continue; 1762992abe0SEd Schouten cn->cn_ops->cn_probe(cn); 177c9dba40cSEd Schouten if (cn->cn_pri == CN_DEAD) 178c9dba40cSEd Schouten continue; 179c9dba40cSEd Schouten if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri) 180c9dba40cSEd Schouten best_cn = cn; 181c9dba40cSEd Schouten if (boothowto & RB_MULTIPLE) { 182c9dba40cSEd Schouten /* 183c9dba40cSEd Schouten * Initialize console, and attach to it. 184c9dba40cSEd Schouten */ 1852992abe0SEd Schouten cn->cn_ops->cn_init(cn); 186c9dba40cSEd Schouten cnadd(cn); 187c9dba40cSEd Schouten } 188c9dba40cSEd Schouten } 189c9dba40cSEd Schouten if (best_cn == NULL) 190c9dba40cSEd Schouten return; 191c9dba40cSEd Schouten if ((boothowto & RB_MULTIPLE) == 0) { 1922992abe0SEd Schouten best_cn->cn_ops->cn_init(best_cn); 193c9dba40cSEd Schouten cnadd(best_cn); 194c9dba40cSEd Schouten } 195c9dba40cSEd Schouten if (boothowto & RB_PAUSE) 1962bfdc1eeSWarner Losh console_pausing = true; 197c9dba40cSEd Schouten /* 198c9dba40cSEd Schouten * Make the best console the preferred console. 199c9dba40cSEd Schouten */ 200c9dba40cSEd Schouten cnselect(best_cn); 201dc61566fSZbigniew Bodek 202dc61566fSZbigniew Bodek #ifdef EARLY_PRINTF 203dc61566fSZbigniew Bodek /* 204dc61566fSZbigniew Bodek * Release early console. 205dc61566fSZbigniew Bodek */ 206dc61566fSZbigniew Bodek early_putc = NULL; 207dc61566fSZbigniew Bodek #endif 2089d6ae1e3SColin Percival TSEXIT(); 209c9dba40cSEd Schouten } 210c9dba40cSEd Schouten 211c9dba40cSEd Schouten void 2129806e82aSDimitry Andric cninit_finish(void) 213c9dba40cSEd Schouten { 2142bfdc1eeSWarner Losh console_pausing = false; 215c9dba40cSEd Schouten } 216c9dba40cSEd Schouten 217c9dba40cSEd Schouten /* add a new physical console to back the virtual console */ 218c9dba40cSEd Schouten int 219c9dba40cSEd Schouten cnadd(struct consdev *cn) 220c9dba40cSEd Schouten { 221c9dba40cSEd Schouten struct cn_device *cnd; 222c9dba40cSEd Schouten int i; 223c9dba40cSEd Schouten 224c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 225c9dba40cSEd Schouten if (cnd->cnd_cn == cn) 226c9dba40cSEd Schouten return (0); 227c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 228c9dba40cSEd Schouten cnd = &cn_devtab[i]; 229c9dba40cSEd Schouten if (cnd->cnd_cn == NULL) 230c9dba40cSEd Schouten break; 231c9dba40cSEd Schouten } 232c9dba40cSEd Schouten if (cnd->cnd_cn != NULL) 233c9dba40cSEd Schouten return (ENOMEM); 234c9dba40cSEd Schouten cnd->cnd_cn = cn; 235c9dba40cSEd Schouten if (cn->cn_name[0] == '\0') { 236c9dba40cSEd Schouten /* XXX: it is unclear if/where this print might output */ 237c9dba40cSEd Schouten printf("WARNING: console at %p has no name\n", cn); 238c9dba40cSEd Schouten } 239c9dba40cSEd Schouten STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next); 240c9dba40cSEd Schouten if (STAILQ_FIRST(&cn_devlist) == cnd) 241c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 242c9dba40cSEd Schouten 243c9dba40cSEd Schouten /* Add device to the active mask. */ 244c9dba40cSEd Schouten cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0); 245c9dba40cSEd Schouten 246c9dba40cSEd Schouten return (0); 247c9dba40cSEd Schouten } 248c9dba40cSEd Schouten 249c9dba40cSEd Schouten void 250c9dba40cSEd Schouten cnremove(struct consdev *cn) 251c9dba40cSEd Schouten { 252c9dba40cSEd Schouten struct cn_device *cnd; 253c9dba40cSEd Schouten int i; 254c9dba40cSEd Schouten 255c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 256a68d5a66SKyle Evans if (cnd->cnd_cn != cn) 257c9dba40cSEd Schouten continue; 258a68d5a66SKyle Evans if (STAILQ_FIRST(&cn_devlist) == cnd) 259a68d5a66SKyle Evans ttyconsdev_select(NULL); 260c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 261c9dba40cSEd Schouten cnd->cnd_cn = NULL; 262c9dba40cSEd Schouten 263c9dba40cSEd Schouten /* Remove this device from available mask. */ 264c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) 265c9dba40cSEd Schouten if (cnd == &cn_devtab[i]) { 266c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 267c9dba40cSEd Schouten break; 268c9dba40cSEd Schouten } 269c9dba40cSEd Schouten #if 0 270c9dba40cSEd Schouten /* 271c9dba40cSEd Schouten * XXX 272c9dba40cSEd Schouten * syscons gets really confused if console resources are 273c9dba40cSEd Schouten * freed after the system has initialized. 274c9dba40cSEd Schouten */ 275c9dba40cSEd Schouten if (cn->cn_term != NULL) 2762992abe0SEd Schouten cn->cn_ops->cn_term(cn); 277c9dba40cSEd Schouten #endif 278c9dba40cSEd Schouten return; 279c9dba40cSEd Schouten } 280c9dba40cSEd Schouten } 281c9dba40cSEd Schouten 282c9dba40cSEd Schouten void 283c9dba40cSEd Schouten cnselect(struct consdev *cn) 284c9dba40cSEd Schouten { 285c9dba40cSEd Schouten struct cn_device *cnd; 286c9dba40cSEd Schouten 287c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 288c9dba40cSEd Schouten if (cnd->cnd_cn != cn) 289c9dba40cSEd Schouten continue; 290c9dba40cSEd Schouten if (cnd == STAILQ_FIRST(&cn_devlist)) 291c9dba40cSEd Schouten return; 292c9dba40cSEd Schouten STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next); 293c9dba40cSEd Schouten STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next); 294c9dba40cSEd Schouten ttyconsdev_select(cnd->cnd_cn->cn_name); 295c9dba40cSEd Schouten return; 296c9dba40cSEd Schouten } 297c9dba40cSEd Schouten } 298c9dba40cSEd Schouten 299c9dba40cSEd Schouten void 300c9dba40cSEd Schouten cnavailable(struct consdev *cn, int available) 301c9dba40cSEd Schouten { 302c9dba40cSEd Schouten int i; 303c9dba40cSEd Schouten 304c9dba40cSEd Schouten for (i = 0; i < CNDEVTAB_SIZE; i++) { 305c9dba40cSEd Schouten if (cn_devtab[i].cnd_cn == cn) 306c9dba40cSEd Schouten break; 307c9dba40cSEd Schouten } 308c9dba40cSEd Schouten if (available) { 309c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 310c9dba40cSEd Schouten cons_avail_mask |= (1 << i); 311c9dba40cSEd Schouten cn->cn_flags &= ~CN_FLAG_NOAVAIL; 312c9dba40cSEd Schouten } else { 313c9dba40cSEd Schouten if (i < CNDEVTAB_SIZE) 314c9dba40cSEd Schouten cons_avail_mask &= ~(1 << i); 315c9dba40cSEd Schouten cn->cn_flags |= CN_FLAG_NOAVAIL; 316c9dba40cSEd Schouten } 317c9dba40cSEd Schouten } 318c9dba40cSEd Schouten 319c9dba40cSEd Schouten int 320c9dba40cSEd Schouten cnunavailable(void) 321c9dba40cSEd Schouten { 322c9dba40cSEd Schouten 323c9dba40cSEd Schouten return (cons_avail_mask == 0); 324c9dba40cSEd Schouten } 325c9dba40cSEd Schouten 326c9dba40cSEd Schouten /* 327c9dba40cSEd Schouten * sysctl_kern_console() provides output parseable in conscontrol(1). 328c9dba40cSEd Schouten */ 329c9dba40cSEd Schouten static int 330c9dba40cSEd Schouten sysctl_kern_console(SYSCTL_HANDLER_ARGS) 331c9dba40cSEd Schouten { 332c9dba40cSEd Schouten struct cn_device *cnd; 333c9dba40cSEd Schouten struct consdev *cp, **list; 334c9dba40cSEd Schouten char *p; 3352bfdc1eeSWarner Losh bool delete; 3362bfdc1eeSWarner Losh int error; 337c9dba40cSEd Schouten struct sbuf *sb; 338c9dba40cSEd Schouten 339657282e0SIan Lepore sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND | 340657282e0SIan Lepore SBUF_INCLUDENUL); 341c9dba40cSEd Schouten if (sb == NULL) 342c9dba40cSEd Schouten return (ENOMEM); 343c9dba40cSEd Schouten sbuf_clear(sb); 344c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) 345c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name); 3460a713948SAlexander Motin sbuf_putc(sb, '/'); 347c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 348c9dba40cSEd Schouten cp = *list; 349c9dba40cSEd Schouten if (cp->cn_name[0] != '\0') 350c9dba40cSEd Schouten sbuf_printf(sb, "%s,", cp->cn_name); 351c9dba40cSEd Schouten } 352c9dba40cSEd Schouten sbuf_finish(sb); 353c9dba40cSEd Schouten error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req); 354c9dba40cSEd Schouten if (error == 0 && req->newptr != NULL) { 355c9dba40cSEd Schouten p = sbuf_data(sb); 356c9dba40cSEd Schouten error = ENXIO; 3572bfdc1eeSWarner Losh delete = false; 358c9dba40cSEd Schouten if (*p == '-') { 3592bfdc1eeSWarner Losh delete = true; 360c9dba40cSEd Schouten p++; 361c9dba40cSEd Schouten } 362c9dba40cSEd Schouten SET_FOREACH(list, cons_set) { 363c9dba40cSEd Schouten cp = *list; 364c9dba40cSEd Schouten if (strcmp(p, cp->cn_name) != 0) 365c9dba40cSEd Schouten continue; 366c9dba40cSEd Schouten if (delete) { 367c9dba40cSEd Schouten cnremove(cp); 368c9dba40cSEd Schouten error = 0; 369c9dba40cSEd Schouten } else { 370c9dba40cSEd Schouten error = cnadd(cp); 371a68d5a66SKyle Evans if (error == 0) 372a68d5a66SKyle Evans cnselect(cp); 373c9dba40cSEd Schouten } 374c9dba40cSEd Schouten break; 375c9dba40cSEd Schouten } 376c9dba40cSEd Schouten } 377c9dba40cSEd Schouten sbuf_delete(sb); 378c9dba40cSEd Schouten return (error); 379c9dba40cSEd Schouten } 380c9dba40cSEd Schouten 3817029da5cSPawel Biernacki SYSCTL_PROC(_kern, OID_AUTO, console, 3827029da5cSPawel Biernacki CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 0, 0, 3837029da5cSPawel Biernacki sysctl_kern_console, "A", 3847029da5cSPawel Biernacki "Console device control"); 385c9dba40cSEd Schouten 3869976156fSAndriy Gapon void 3879806e82aSDimitry Andric cngrab(void) 3889976156fSAndriy Gapon { 3899976156fSAndriy Gapon struct cn_device *cnd; 3909976156fSAndriy Gapon struct consdev *cn; 3919976156fSAndriy Gapon 3929976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 3939976156fSAndriy Gapon cn = cnd->cnd_cn; 3949976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 3959976156fSAndriy Gapon cn->cn_ops->cn_grab(cn); 3969976156fSAndriy Gapon } 3979976156fSAndriy Gapon } 3989976156fSAndriy Gapon 3999976156fSAndriy Gapon void 4009806e82aSDimitry Andric cnungrab(void) 4019976156fSAndriy Gapon { 4029976156fSAndriy Gapon struct cn_device *cnd; 4039976156fSAndriy Gapon struct consdev *cn; 4049976156fSAndriy Gapon 4059976156fSAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 4069976156fSAndriy Gapon cn = cnd->cnd_cn; 4079976156fSAndriy Gapon if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) 4089976156fSAndriy Gapon cn->cn_ops->cn_ungrab(cn); 4099976156fSAndriy Gapon } 4109976156fSAndriy Gapon } 4119976156fSAndriy Gapon 412ec6faf94SAndriy Gapon void 4139806e82aSDimitry Andric cnresume(void) 414ec6faf94SAndriy Gapon { 415ec6faf94SAndriy Gapon struct cn_device *cnd; 416ec6faf94SAndriy Gapon struct consdev *cn; 417ec6faf94SAndriy Gapon 418ec6faf94SAndriy Gapon STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 419ec6faf94SAndriy Gapon cn = cnd->cnd_cn; 420ec6faf94SAndriy Gapon if (cn->cn_ops->cn_resume != NULL) 421ec6faf94SAndriy Gapon cn->cn_ops->cn_resume(cn); 422ec6faf94SAndriy Gapon } 423ec6faf94SAndriy Gapon } 424ec6faf94SAndriy Gapon 425c9dba40cSEd Schouten /* 426c9dba40cSEd Schouten * Low level console routines. 427c9dba40cSEd Schouten */ 428c9dba40cSEd Schouten int 429c9dba40cSEd Schouten cngetc(void) 430c9dba40cSEd Schouten { 431c9dba40cSEd Schouten int c; 432c9dba40cSEd Schouten 433c9dba40cSEd Schouten if (cn_mute) 434c9dba40cSEd Schouten return (-1); 435c9dba40cSEd Schouten while ((c = cncheckc()) == -1) 436298fbd16SAndriy Gapon cpu_spinwait(); 437c9dba40cSEd Schouten if (c == '\r') 438c9dba40cSEd Schouten c = '\n'; /* console input is always ICRNL */ 439c9dba40cSEd Schouten return (c); 440c9dba40cSEd Schouten } 441c9dba40cSEd Schouten 442c9dba40cSEd Schouten int 443c9dba40cSEd Schouten cncheckc(void) 444c9dba40cSEd Schouten { 445c9dba40cSEd Schouten struct cn_device *cnd; 446c9dba40cSEd Schouten struct consdev *cn; 447c9dba40cSEd Schouten int c; 448c9dba40cSEd Schouten 449c9dba40cSEd Schouten if (cn_mute) 450c9dba40cSEd Schouten return (-1); 451c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 452c9dba40cSEd Schouten cn = cnd->cnd_cn; 453c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 4542992abe0SEd Schouten c = cn->cn_ops->cn_getc(cn); 4552992abe0SEd Schouten if (c != -1) 456c9dba40cSEd Schouten return (c); 457c9dba40cSEd Schouten } 458c9dba40cSEd Schouten } 459c9dba40cSEd Schouten return (-1); 460c9dba40cSEd Schouten } 461c9dba40cSEd Schouten 462c9dba40cSEd Schouten void 4638e628542SAndriy Gapon cngets(char *cp, size_t size, int visible) 4648e628542SAndriy Gapon { 4658e628542SAndriy Gapon char *lp, *end; 4668e628542SAndriy Gapon int c; 4678e628542SAndriy Gapon 4688e628542SAndriy Gapon cngrab(); 4698e628542SAndriy Gapon 4708e628542SAndriy Gapon lp = cp; 4718e628542SAndriy Gapon end = cp + size - 1; 4728e628542SAndriy Gapon for (;;) { 4738e628542SAndriy Gapon c = cngetc() & 0177; 4748e628542SAndriy Gapon switch (c) { 4758e628542SAndriy Gapon case '\n': 4768e628542SAndriy Gapon case '\r': 4778e628542SAndriy Gapon cnputc(c); 4788e628542SAndriy Gapon *lp = '\0'; 4798e628542SAndriy Gapon cnungrab(); 4808e628542SAndriy Gapon return; 4818e628542SAndriy Gapon case '\b': 4828e628542SAndriy Gapon case '\177': 4838e628542SAndriy Gapon if (lp > cp) { 4849520f952SWarner Losh if (visible) 4859520f952SWarner Losh cnputs("\b \b"); 4868e628542SAndriy Gapon lp--; 4878e628542SAndriy Gapon } 4888e628542SAndriy Gapon continue; 4898e628542SAndriy Gapon case '\0': 4908e628542SAndriy Gapon continue; 4918e628542SAndriy Gapon default: 4928e628542SAndriy Gapon if (lp < end) { 4938e628542SAndriy Gapon switch (visible) { 4948e628542SAndriy Gapon case GETS_NOECHO: 4958e628542SAndriy Gapon break; 4968e628542SAndriy Gapon case GETS_ECHOPASS: 4978e628542SAndriy Gapon cnputc('*'); 4988e628542SAndriy Gapon break; 4998e628542SAndriy Gapon default: 5008e628542SAndriy Gapon cnputc(c); 5018e628542SAndriy Gapon break; 5028e628542SAndriy Gapon } 5038e628542SAndriy Gapon *lp++ = c; 5048e628542SAndriy Gapon } 5058e628542SAndriy Gapon } 5068e628542SAndriy Gapon } 5078e628542SAndriy Gapon } 5088e628542SAndriy Gapon 5098e628542SAndriy Gapon void 510c9dba40cSEd Schouten cnputc(int c) 511c9dba40cSEd Schouten { 512c9dba40cSEd Schouten struct cn_device *cnd; 513c9dba40cSEd Schouten struct consdev *cn; 514fe20aaecSRyan Libby const char *cp; 515c9dba40cSEd Schouten 51642c8459bSIan Lepore #ifdef EARLY_PRINTF 51742c8459bSIan Lepore if (early_putc != NULL) { 51842c8459bSIan Lepore if (c == '\n') 51942c8459bSIan Lepore early_putc('\r'); 52042c8459bSIan Lepore early_putc(c); 52142c8459bSIan Lepore return; 52242c8459bSIan Lepore } 52342c8459bSIan Lepore #endif 52442c8459bSIan Lepore 525c9dba40cSEd Schouten if (cn_mute || c == '\0') 526c9dba40cSEd Schouten return; 527c9dba40cSEd Schouten STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) { 528c9dba40cSEd Schouten cn = cnd->cnd_cn; 529c9dba40cSEd Schouten if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) { 530c9dba40cSEd Schouten if (c == '\n') 5312992abe0SEd Schouten cn->cn_ops->cn_putc(cn, '\r'); 5322992abe0SEd Schouten cn->cn_ops->cn_putc(cn, c); 533c9dba40cSEd Schouten } 534c9dba40cSEd Schouten } 535c9dba40cSEd Schouten if (console_pausing && c == '\n' && !kdb_active) { 536c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 537c9dba40cSEd Schouten cnputc(*cp); 538bf8696b4SAndriy Gapon cngrab(); 539c9dba40cSEd Schouten if (cngetc() == '.') 5402bfdc1eeSWarner Losh console_pausing = false; 541bf8696b4SAndriy Gapon cnungrab(); 542c9dba40cSEd Schouten cnputc('\r'); 543c9dba40cSEd Schouten for (cp = console_pausestr; *cp != '\0'; cp++) 544c9dba40cSEd Schouten cnputc(' '); 545c9dba40cSEd Schouten cnputc('\r'); 546c9dba40cSEd Schouten } 547c9dba40cSEd Schouten } 548c9dba40cSEd Schouten 549c9dba40cSEd Schouten void 5506858c2ccSConrad Meyer cnputsn(const char *p, size_t n) 551c9dba40cSEd Schouten { 5526858c2ccSConrad Meyer size_t i; 5532bfdc1eeSWarner Losh bool unlock_reqd = false; 554c9dba40cSEd Schouten 555bd6085c6SAlexander Motin if (mtx_initialized(&cnputs_mtx)) { 55604a8159dSHans Petter Selasky /* 55704a8159dSHans Petter Selasky * NOTE: Debug prints and/or witness printouts in 55804a8159dSHans Petter Selasky * console driver clients can cause the "cnputs_mtx" 55904a8159dSHans Petter Selasky * mutex to recurse. Simply return if that happens. 56004a8159dSHans Petter Selasky */ 56104a8159dSHans Petter Selasky if (mtx_owned(&cnputs_mtx)) 56204a8159dSHans Petter Selasky return; 563c9dba40cSEd Schouten mtx_lock_spin(&cnputs_mtx); 5642bfdc1eeSWarner Losh unlock_reqd = true; 565c9dba40cSEd Schouten } 566c9dba40cSEd Schouten 5676858c2ccSConrad Meyer for (i = 0; i < n; i++) 5686858c2ccSConrad Meyer cnputc(p[i]); 569c9dba40cSEd Schouten 570c9dba40cSEd Schouten if (unlock_reqd) 571c9dba40cSEd Schouten mtx_unlock_spin(&cnputs_mtx); 572c9dba40cSEd Schouten } 573c9dba40cSEd Schouten 5746858c2ccSConrad Meyer void 575fe20aaecSRyan Libby cnputs(const char *p) 5766858c2ccSConrad Meyer { 5776858c2ccSConrad Meyer cnputsn(p, strlen(p)); 5786858c2ccSConrad Meyer } 5796858c2ccSConrad Meyer 580bd6085c6SAlexander Motin static unsigned int consmsgbuf_size = 65536; 581bd6085c6SAlexander Motin SYSCTL_UINT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RWTUN, &consmsgbuf_size, 582bd6085c6SAlexander Motin 0, "Console tty buffer size"); 583c9dba40cSEd Schouten 584c9dba40cSEd Schouten /* 585c9dba40cSEd Schouten * Redirect console output to a tty. 586c9dba40cSEd Schouten */ 587bd6085c6SAlexander Motin int 588c9dba40cSEd Schouten constty_set(struct tty *tp) 589c9dba40cSEd Schouten { 590bd6085c6SAlexander Motin int size = consmsgbuf_size; 591bd6085c6SAlexander Motin void *buf = NULL; 592c9dba40cSEd Schouten 593bd6085c6SAlexander Motin tty_assert_locked(tp); 594bd6085c6SAlexander Motin if (constty == tp) 595bd6085c6SAlexander Motin return (0); 596bd6085c6SAlexander Motin if (constty != NULL) 597bd6085c6SAlexander Motin return (EBUSY); 598bd6085c6SAlexander Motin 599c9dba40cSEd Schouten if (consbuf == NULL) { 600bd6085c6SAlexander Motin tty_unlock(tp); 601bd6085c6SAlexander Motin buf = malloc(size, M_TTYCONS, M_WAITOK); 602bd6085c6SAlexander Motin tty_lock(tp); 603c9dba40cSEd Schouten } 604bd6085c6SAlexander Motin mtx_lock(&constty_mtx); 605bd6085c6SAlexander Motin if (constty != NULL) { 606bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 607bd6085c6SAlexander Motin free(buf, M_TTYCONS); 608bd6085c6SAlexander Motin return (EBUSY); 609bd6085c6SAlexander Motin } 610bd6085c6SAlexander Motin if (consbuf == NULL) { 611bd6085c6SAlexander Motin consbuf = buf; 612bd6085c6SAlexander Motin msgbuf_init(&consmsgbuf, buf, size); 613bd6085c6SAlexander Motin } else 614bd6085c6SAlexander Motin free(buf, M_TTYCONS); 615c9dba40cSEd Schouten constty = tp; 616bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 617bd6085c6SAlexander Motin 618bd6085c6SAlexander Motin callout_init_mtx(&conscallout, tty_getlock(tp), 0); 619bd6085c6SAlexander Motin constty_timeout(tp); 620bd6085c6SAlexander Motin return (0); 621c9dba40cSEd Schouten } 622c9dba40cSEd Schouten 623c9dba40cSEd Schouten /* 624c9dba40cSEd Schouten * Disable console redirection to a tty. 625c9dba40cSEd Schouten */ 626bd6085c6SAlexander Motin int 627bd6085c6SAlexander Motin constty_clear(struct tty *tp) 628c9dba40cSEd Schouten { 629c9dba40cSEd Schouten int c; 630c9dba40cSEd Schouten 631bd6085c6SAlexander Motin tty_assert_locked(tp); 632bd6085c6SAlexander Motin if (constty != tp) 633bd6085c6SAlexander Motin return (ENXIO); 634c9dba40cSEd Schouten callout_stop(&conscallout); 635bd6085c6SAlexander Motin mtx_lock(&constty_mtx); 636bd6085c6SAlexander Motin constty = NULL; 637bd6085c6SAlexander Motin mtx_unlock(&constty_mtx); 638c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) 639c9dba40cSEd Schouten cnputc(c); 640bd6085c6SAlexander Motin /* We never free consbuf because it can still be in use. */ 641bd6085c6SAlexander Motin return (0); 642c9dba40cSEd Schouten } 643c9dba40cSEd Schouten 644c9dba40cSEd Schouten /* Times per second to check for pending console tty messages. */ 645bd6085c6SAlexander Motin static int constty_wakeups_per_second = 15; 646c9dba40cSEd Schouten SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW, 647a0c87b74SGavin Atkinson &constty_wakeups_per_second, 0, 648a0c87b74SGavin Atkinson "Times per second to check for pending console tty messages"); 649c9dba40cSEd Schouten 650c9dba40cSEd Schouten static void 651c9dba40cSEd Schouten constty_timeout(void *arg) 652c9dba40cSEd Schouten { 653bd6085c6SAlexander Motin struct tty *tp = arg; 654c9dba40cSEd Schouten int c; 655c9dba40cSEd Schouten 656bd6085c6SAlexander Motin tty_assert_locked(tp); 657c9dba40cSEd Schouten while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { 658bd6085c6SAlexander Motin if (tty_putchar(tp, c) < 0) { 659bd6085c6SAlexander Motin constty_clear(tp); 660bd6085c6SAlexander Motin return; 661c9dba40cSEd Schouten } 662c9dba40cSEd Schouten } 663bd6085c6SAlexander Motin callout_reset_sbt(&conscallout, SBT_1S / constty_wakeups_per_second, 664bd6085c6SAlexander Motin 0, constty_timeout, tp, C_PREL(1)); 665c9dba40cSEd Schouten } 666c9dba40cSEd Schouten 667c9dba40cSEd Schouten /* 668c9dba40cSEd Schouten * Sysbeep(), if we have hardware for it 669c9dba40cSEd Schouten */ 670c9dba40cSEd Schouten 671c9dba40cSEd Schouten #ifdef HAS_TIMER_SPKR 672c9dba40cSEd Schouten 673072d5b98SWarner Losh static bool beeping; 674fadf3fb9SJohn Baldwin static struct callout beeping_timer; 675c9dba40cSEd Schouten 676c9dba40cSEd Schouten static void 677c9dba40cSEd Schouten sysbeepstop(void *chan) 678c9dba40cSEd Schouten { 679c9dba40cSEd Schouten 680c9dba40cSEd Schouten timer_spkr_release(); 681072d5b98SWarner Losh beeping = false; 682c9dba40cSEd Schouten } 683c9dba40cSEd Schouten 684c9dba40cSEd Schouten int 685072d5b98SWarner Losh sysbeep(int pitch, sbintime_t duration) 686c9dba40cSEd Schouten { 687c9dba40cSEd Schouten 688c9dba40cSEd Schouten if (timer_spkr_acquire()) { 689c9dba40cSEd Schouten if (!beeping) { 690c9dba40cSEd Schouten /* Something else owns it. */ 691c9dba40cSEd Schouten return (EBUSY); 692c9dba40cSEd Schouten } 693c9dba40cSEd Schouten } 694c9dba40cSEd Schouten timer_spkr_setfreq(pitch); 695c9dba40cSEd Schouten if (!beeping) { 696072d5b98SWarner Losh beeping = true; 697072d5b98SWarner Losh callout_reset_sbt(&beeping_timer, duration, 0, sysbeepstop, 698072d5b98SWarner Losh NULL, C_PREL(5)); 699c9dba40cSEd Schouten } 700c9dba40cSEd Schouten return (0); 701c9dba40cSEd Schouten } 702c9dba40cSEd Schouten 703fadf3fb9SJohn Baldwin static void 704fadf3fb9SJohn Baldwin sysbeep_init(void *unused) 705fadf3fb9SJohn Baldwin { 706fadf3fb9SJohn Baldwin 707fd90e2edSJung-uk Kim callout_init(&beeping_timer, 1); 708fadf3fb9SJohn Baldwin } 709fadf3fb9SJohn Baldwin SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL); 710c9dba40cSEd Schouten #else 711c9dba40cSEd Schouten 712c9dba40cSEd Schouten /* 713c9dba40cSEd Schouten * No hardware, no sound 714c9dba40cSEd Schouten */ 715c9dba40cSEd Schouten 716c9dba40cSEd Schouten int 717072d5b98SWarner Losh sysbeep(int pitch __unused, sbintime_t duration __unused) 718c9dba40cSEd Schouten { 719c9dba40cSEd Schouten 720c9dba40cSEd Schouten return (ENODEV); 721c9dba40cSEd Schouten } 722c9dba40cSEd Schouten 723c9dba40cSEd Schouten #endif 724c9dba40cSEd Schouten 72559644098SEd Maste /* 72659644098SEd Maste * Temporary support for sc(4) to vt(4) transition. 72759644098SEd Maste */ 728018147eeSEd Maste static unsigned vty_prefer; 729af3b2549SHans Petter Selasky static char vty_name[16]; 730af3b2549SHans Petter Selasky SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name, 731af3b2549SHans Petter Selasky 0, "Console vty driver"); 73259644098SEd Maste 73359644098SEd Maste int 73459644098SEd Maste vty_enabled(unsigned vty) 73559644098SEd Maste { 73659644098SEd Maste static unsigned vty_selected = 0; 73759644098SEd Maste 73859644098SEd Maste if (vty_selected == 0) { 73959644098SEd Maste TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name)); 74059644098SEd Maste do { 74159644098SEd Maste #if defined(DEV_SC) 74259644098SEd Maste if (strcmp(vty_name, "sc") == 0) { 74359644098SEd Maste vty_selected = VTY_SC; 74459644098SEd Maste break; 74559644098SEd Maste } 74659644098SEd Maste #endif 74759644098SEd Maste #if defined(DEV_VT) 74859644098SEd Maste if (strcmp(vty_name, "vt") == 0) { 74959644098SEd Maste vty_selected = VTY_VT; 75059644098SEd Maste break; 75159644098SEd Maste } 75259644098SEd Maste #endif 753018147eeSEd Maste if (vty_prefer != 0) { 754018147eeSEd Maste vty_selected = vty_prefer; 755018147eeSEd Maste break; 756018147eeSEd Maste } 7572d6f6d63SJean-Sébastien Pédron #if defined(DEV_VT) 75859644098SEd Maste vty_selected = VTY_VT; 7592d6f6d63SJean-Sébastien Pédron #elif defined(DEV_SC) 7602d6f6d63SJean-Sébastien Pédron vty_selected = VTY_SC; 76159644098SEd Maste #endif 76259644098SEd Maste } while (0); 76359644098SEd Maste 76459644098SEd Maste if (vty_selected == VTY_VT) 76559644098SEd Maste strcpy(vty_name, "vt"); 76659644098SEd Maste else if (vty_selected == VTY_SC) 76759644098SEd Maste strcpy(vty_name, "sc"); 76859644098SEd Maste } 76959644098SEd Maste return ((vty_selected & vty) != 0); 77059644098SEd Maste } 77159644098SEd Maste 772018147eeSEd Maste void 773018147eeSEd Maste vty_set_preferred(unsigned vty) 774018147eeSEd Maste { 775018147eeSEd Maste 776018147eeSEd Maste vty_prefer = vty; 777018147eeSEd Maste #if !defined(DEV_SC) 778969d3cc2SEd Maste vty_prefer &= ~VTY_SC; 779018147eeSEd Maste #endif 780018147eeSEd Maste #if !defined(DEV_VT) 781969d3cc2SEd Maste vty_prefer &= ~VTY_VT; 782018147eeSEd Maste #endif 783018147eeSEd Maste } 784