kern_cons.c revision 268366
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: stable/10/sys/kern/kern_cons.c 268366 2014-07-07 14:16:05Z ray $");
42
43#include "opt_ddb.h"
44#include "opt_syscons.h"
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/lock.h>
49#include <sys/mutex.h>
50#include <sys/conf.h>
51#include <sys/cons.h>
52#include <sys/fcntl.h>
53#include <sys/kdb.h>
54#include <sys/kernel.h>
55#include <sys/malloc.h>
56#include <sys/msgbuf.h>
57#include <sys/namei.h>
58#include <sys/priv.h>
59#include <sys/proc.h>
60#include <sys/queue.h>
61#include <sys/reboot.h>
62#include <sys/sysctl.h>
63#include <sys/sbuf.h>
64#include <sys/tty.h>
65#include <sys/uio.h>
66#include <sys/vnode.h>
67
68#include <ddb/ddb.h>
69
70#include <machine/cpu.h>
71#include <machine/clock.h>
72
73static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
74
75struct cn_device {
76	STAILQ_ENTRY(cn_device) cnd_next;
77	struct		consdev *cnd_cn;
78};
79
80#define CNDEVPATHMAX	32
81#define CNDEVTAB_SIZE	4
82static struct cn_device cn_devtab[CNDEVTAB_SIZE];
83static STAILQ_HEAD(, cn_device) cn_devlist =
84    STAILQ_HEAD_INITIALIZER(cn_devlist);
85
86int	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
87				 * which is currently unavailable for inpit
88				 * (i.e., if it is in graphics mode) will have
89				 * this bit cleared.
90				 */
91static int cn_mute;
92static char *consbuf;			/* buffer used by `consmsgbuf' */
93static struct callout conscallout;	/* callout for outputting to constty */
94struct msgbuf consmsgbuf;		/* message buffer for console tty */
95static u_char console_pausing;		/* pause after each line during probe */
96static char *console_pausestr=
97"<pause; press any key to proceed to next line or '.' to end pause mode>";
98struct tty *constty;			/* pointer to console "window" tty */
99static struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
100static int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
101
102static void constty_timeout(void *arg);
103
104static struct consdev cons_consdev;
105DATA_SET(cons_set, cons_consdev);
106SET_DECLARE(cons_set, struct consdev);
107
108void
109cninit(void)
110{
111	struct consdev *best_cn, *cn, **list;
112
113	/*
114	 * Check if we should mute the console (for security reasons perhaps)
115	 * It can be changes dynamically using sysctl kern.consmute
116	 * once we are up and going.
117	 *
118	 */
119        cn_mute = ((boothowto & (RB_MUTE
120			|RB_SINGLE
121			|RB_VERBOSE
122			|RB_ASKNAME)) == RB_MUTE);
123
124	/*
125	 * Find the first console with the highest priority.
126	 */
127	best_cn = NULL;
128	SET_FOREACH(list, cons_set) {
129		cn = *list;
130		cnremove(cn);
131		/* Skip cons_consdev. */
132		if (cn->cn_ops == NULL)
133			continue;
134		cn->cn_ops->cn_probe(cn);
135		if (cn->cn_pri == CN_DEAD)
136			continue;
137		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
138			best_cn = cn;
139		if (boothowto & RB_MULTIPLE) {
140			/*
141			 * Initialize console, and attach to it.
142			 */
143			cn->cn_ops->cn_init(cn);
144			cnadd(cn);
145		}
146	}
147	if (best_cn == NULL)
148		return;
149	if ((boothowto & RB_MULTIPLE) == 0) {
150		best_cn->cn_ops->cn_init(best_cn);
151		cnadd(best_cn);
152	}
153	if (boothowto & RB_PAUSE)
154		console_pausing = 1;
155	/*
156	 * Make the best console the preferred console.
157	 */
158	cnselect(best_cn);
159}
160
161void
162cninit_finish()
163{
164	console_pausing = 0;
165}
166
167/* add a new physical console to back the virtual console */
168int
169cnadd(struct consdev *cn)
170{
171	struct cn_device *cnd;
172	int i;
173
174	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
175		if (cnd->cnd_cn == cn)
176			return (0);
177	for (i = 0; i < CNDEVTAB_SIZE; i++) {
178		cnd = &cn_devtab[i];
179		if (cnd->cnd_cn == NULL)
180			break;
181	}
182	if (cnd->cnd_cn != NULL)
183		return (ENOMEM);
184	cnd->cnd_cn = cn;
185	if (cn->cn_name[0] == '\0') {
186		/* XXX: it is unclear if/where this print might output */
187		printf("WARNING: console at %p has no name\n", cn);
188	}
189	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
190	if (STAILQ_FIRST(&cn_devlist) == cnd)
191		ttyconsdev_select(cnd->cnd_cn->cn_name);
192
193	/* Add device to the active mask. */
194	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
195
196	return (0);
197}
198
199void
200cnremove(struct consdev *cn)
201{
202	struct cn_device *cnd;
203	int i;
204
205	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
206		if (cnd->cnd_cn != cn)
207			continue;
208		if (STAILQ_FIRST(&cn_devlist) == cnd)
209			ttyconsdev_select(NULL);
210		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
211		cnd->cnd_cn = NULL;
212
213		/* Remove this device from available mask. */
214		for (i = 0; i < CNDEVTAB_SIZE; i++)
215			if (cnd == &cn_devtab[i]) {
216				cons_avail_mask &= ~(1 << i);
217				break;
218			}
219#if 0
220		/*
221		 * XXX
222		 * syscons gets really confused if console resources are
223		 * freed after the system has initialized.
224		 */
225		if (cn->cn_term != NULL)
226			cn->cn_ops->cn_term(cn);
227#endif
228		return;
229	}
230}
231
232void
233cnselect(struct consdev *cn)
234{
235	struct cn_device *cnd;
236
237	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
238		if (cnd->cnd_cn != cn)
239			continue;
240		if (cnd == STAILQ_FIRST(&cn_devlist))
241			return;
242		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
243		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
244		ttyconsdev_select(cnd->cnd_cn->cn_name);
245		return;
246	}
247}
248
249void
250cnavailable(struct consdev *cn, int available)
251{
252	int i;
253
254	for (i = 0; i < CNDEVTAB_SIZE; i++) {
255		if (cn_devtab[i].cnd_cn == cn)
256			break;
257	}
258	if (available) {
259		if (i < CNDEVTAB_SIZE)
260			cons_avail_mask |= (1 << i);
261		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
262	} else {
263		if (i < CNDEVTAB_SIZE)
264			cons_avail_mask &= ~(1 << i);
265		cn->cn_flags |= CN_FLAG_NOAVAIL;
266	}
267}
268
269int
270cnunavailable(void)
271{
272
273	return (cons_avail_mask == 0);
274}
275
276/*
277 * sysctl_kern_console() provides output parseable in conscontrol(1).
278 */
279static int
280sysctl_kern_console(SYSCTL_HANDLER_ARGS)
281{
282	struct cn_device *cnd;
283	struct consdev *cp, **list;
284	char *p;
285	int delete, error;
286	struct sbuf *sb;
287
288	sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
289	if (sb == NULL)
290		return (ENOMEM);
291	sbuf_clear(sb);
292	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
293		sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
294	sbuf_printf(sb, "/");
295	SET_FOREACH(list, cons_set) {
296		cp = *list;
297		if (cp->cn_name[0] != '\0')
298			sbuf_printf(sb, "%s,", cp->cn_name);
299	}
300	sbuf_finish(sb);
301	error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
302	if (error == 0 && req->newptr != NULL) {
303		p = sbuf_data(sb);
304		error = ENXIO;
305		delete = 0;
306		if (*p == '-') {
307			delete = 1;
308			p++;
309		}
310		SET_FOREACH(list, cons_set) {
311			cp = *list;
312			if (strcmp(p, cp->cn_name) != 0)
313				continue;
314			if (delete) {
315				cnremove(cp);
316				error = 0;
317			} else {
318				error = cnadd(cp);
319				if (error == 0)
320					cnselect(cp);
321			}
322			break;
323		}
324	}
325	sbuf_delete(sb);
326	return (error);
327}
328
329SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
330	0, 0, sysctl_kern_console, "A", "Console device control");
331
332/*
333 * User has changed the state of the console muting.
334 * This may require us to open or close the device in question.
335 */
336static int
337sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
338{
339	int error;
340
341	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
342	if (error != 0 || req->newptr == NULL)
343		return (error);
344	return (error);
345}
346
347SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
348	0, sizeof(cn_mute), sysctl_kern_consmute, "I",
349	"State of the console muting");
350
351void
352cngrab()
353{
354	struct cn_device *cnd;
355	struct consdev *cn;
356
357	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
358		cn = cnd->cnd_cn;
359		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
360			cn->cn_ops->cn_grab(cn);
361	}
362}
363
364void
365cnungrab()
366{
367	struct cn_device *cnd;
368	struct consdev *cn;
369
370	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
371		cn = cnd->cnd_cn;
372		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
373			cn->cn_ops->cn_ungrab(cn);
374	}
375}
376
377/*
378 * Low level console routines.
379 */
380int
381cngetc(void)
382{
383	int c;
384
385	if (cn_mute)
386		return (-1);
387	while ((c = cncheckc()) == -1)
388		cpu_spinwait();
389	if (c == '\r')
390		c = '\n';		/* console input is always ICRNL */
391	return (c);
392}
393
394int
395cncheckc(void)
396{
397	struct cn_device *cnd;
398	struct consdev *cn;
399	int c;
400
401	if (cn_mute)
402		return (-1);
403	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
404		cn = cnd->cnd_cn;
405		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
406			c = cn->cn_ops->cn_getc(cn);
407			if (c != -1)
408				return (c);
409		}
410	}
411	return (-1);
412}
413
414void
415cngets(char *cp, size_t size, int visible)
416{
417	char *lp, *end;
418	int c;
419
420	cngrab();
421
422	lp = cp;
423	end = cp + size - 1;
424	for (;;) {
425		c = cngetc() & 0177;
426		switch (c) {
427		case '\n':
428		case '\r':
429			cnputc(c);
430			*lp = '\0';
431			cnungrab();
432			return;
433		case '\b':
434		case '\177':
435			if (lp > cp) {
436				if (visible)
437					cnputs("\b \b");
438				lp--;
439			}
440			continue;
441		case '\0':
442			continue;
443		default:
444			if (lp < end) {
445				switch (visible) {
446				case GETS_NOECHO:
447					break;
448				case GETS_ECHOPASS:
449					cnputc('*');
450					break;
451				default:
452					cnputc(c);
453					break;
454				}
455				*lp++ = c;
456			}
457		}
458	}
459}
460
461void
462cnputc(int c)
463{
464	struct cn_device *cnd;
465	struct consdev *cn;
466	char *cp;
467
468#ifdef EARLY_PRINTF
469	if (early_putc != NULL) {
470		if (c == '\n')
471			early_putc('\r');
472		early_putc(c);
473		return;
474	}
475#endif
476
477	if (cn_mute || c == '\0')
478		return;
479	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
480		cn = cnd->cnd_cn;
481		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
482			if (c == '\n')
483				cn->cn_ops->cn_putc(cn, '\r');
484			cn->cn_ops->cn_putc(cn, c);
485		}
486	}
487	if (console_pausing && c == '\n' && !kdb_active) {
488		for (cp = console_pausestr; *cp != '\0'; cp++)
489			cnputc(*cp);
490		cngrab();
491		if (cngetc() == '.')
492			console_pausing = 0;
493		cnungrab();
494		cnputc('\r');
495		for (cp = console_pausestr; *cp != '\0'; cp++)
496			cnputc(' ');
497		cnputc('\r');
498	}
499}
500
501void
502cnputs(char *p)
503{
504	int c;
505	int unlock_reqd = 0;
506
507	if (use_cnputs_mtx) {
508		mtx_lock_spin(&cnputs_mtx);
509		unlock_reqd = 1;
510	}
511
512	while ((c = *p++) != '\0')
513		cnputc(c);
514
515	if (unlock_reqd)
516		mtx_unlock_spin(&cnputs_mtx);
517}
518
519static int consmsgbuf_size = 8192;
520SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
521    "Console tty buffer size");
522
523/*
524 * Redirect console output to a tty.
525 */
526void
527constty_set(struct tty *tp)
528{
529	int size;
530
531	KASSERT(tp != NULL, ("constty_set: NULL tp"));
532	if (consbuf == NULL) {
533		size = consmsgbuf_size;
534		consbuf = malloc(size, M_TTYCONS, M_WAITOK);
535		msgbuf_init(&consmsgbuf, consbuf, size);
536		callout_init(&conscallout, 0);
537	}
538	constty = tp;
539	constty_timeout(NULL);
540}
541
542/*
543 * Disable console redirection to a tty.
544 */
545void
546constty_clear(void)
547{
548	int c;
549
550	constty = NULL;
551	if (consbuf == NULL)
552		return;
553	callout_stop(&conscallout);
554	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
555		cnputc(c);
556	free(consbuf, M_TTYCONS);
557	consbuf = NULL;
558}
559
560/* Times per second to check for pending console tty messages. */
561static int constty_wakeups_per_second = 5;
562SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
563    &constty_wakeups_per_second, 0,
564    "Times per second to check for pending console tty messages");
565
566static void
567constty_timeout(void *arg)
568{
569	int c;
570
571	if (constty != NULL) {
572		tty_lock(constty);
573		while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
574			if (tty_putchar(constty, c) < 0) {
575				tty_unlock(constty);
576				constty = NULL;
577				break;
578			}
579		}
580
581		if (constty != NULL)
582			tty_unlock(constty);
583	}
584	if (constty != NULL) {
585		callout_reset(&conscallout, hz / constty_wakeups_per_second,
586		    constty_timeout, NULL);
587	} else {
588		/* Deallocate the constty buffer memory. */
589		constty_clear();
590	}
591}
592
593static void
594cn_drvinit(void *unused)
595{
596
597	mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
598	use_cnputs_mtx = 1;
599}
600
601SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
602
603/*
604 * Sysbeep(), if we have hardware for it
605 */
606
607#ifdef HAS_TIMER_SPKR
608
609static int beeping;
610
611static void
612sysbeepstop(void *chan)
613{
614
615	timer_spkr_release();
616	beeping = 0;
617}
618
619int
620sysbeep(int pitch, int period)
621{
622
623	if (timer_spkr_acquire()) {
624		if (!beeping) {
625			/* Something else owns it. */
626			return (EBUSY);
627		}
628	}
629	timer_spkr_setfreq(pitch);
630	if (!beeping) {
631		beeping = period;
632		timeout(sysbeepstop, (void *)NULL, period);
633	}
634	return (0);
635}
636
637#else
638
639/*
640 * No hardware, no sound
641 */
642
643int
644sysbeep(int pitch __unused, int period __unused)
645{
646
647	return (ENODEV);
648}
649
650#endif
651
652/*
653 * Temporary support for sc(4) to vt(4) transition.
654 */
655static char vty_name[16] = "";
656SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN, vty_name, 0,
657    "Console vty driver");
658
659int
660vty_enabled(unsigned vty)
661{
662	static unsigned vty_selected = 0;
663
664	if (vty_selected == 0) {
665		TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
666		do {
667#if defined(DEV_SC)
668			if (strcmp(vty_name, "sc") == 0) {
669				vty_selected = VTY_SC;
670				break;
671			}
672#endif
673#if defined(DEV_VT)
674			if (strcmp(vty_name, "vt") == 0) {
675				vty_selected = VTY_VT;
676				break;
677			}
678#endif
679#if defined(DEV_SC)
680			vty_selected = VTY_SC;
681#elif defined(DEV_VT)
682			vty_selected = VTY_VT;
683#endif
684		} while (0);
685
686		if (vty_selected == VTY_VT)
687			strcpy(vty_name, "vt");
688		else if (vty_selected == VTY_SC)
689			strcpy(vty_name, "sc");
690	}
691	return ((vty_selected & vty) != 0);
692}
693
694