kern_cons.c revision 158944
1/*-
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sys/kern/tty_cons.c 158944 2006-05-26 10:46:38Z phk $");
39
40#include "opt_ddb.h"
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/conf.h>
45#include <sys/cons.h>
46#include <sys/fcntl.h>
47#include <sys/kdb.h>
48#include <sys/kernel.h>
49#include <sys/malloc.h>
50#include <sys/msgbuf.h>
51#include <sys/namei.h>
52#include <sys/proc.h>
53#include <sys/queue.h>
54#include <sys/reboot.h>
55#include <sys/sysctl.h>
56#include <sys/tty.h>
57#include <sys/uio.h>
58#include <sys/vnode.h>
59
60#include <ddb/ddb.h>
61
62#include <machine/cpu.h>
63
64static	d_open_t	cnopen;
65static	d_close_t	cnclose;
66static	d_read_t	cnread;
67static	d_write_t	cnwrite;
68static	d_ioctl_t	cnioctl;
69static	d_poll_t	cnpoll;
70static	d_kqfilter_t	cnkqfilter;
71
72static struct cdevsw cn_cdevsw = {
73	.d_version =	D_VERSION,
74	.d_open =	cnopen,
75	.d_close =	cnclose,
76	.d_read =	cnread,
77	.d_write =	cnwrite,
78	.d_ioctl =	cnioctl,
79	.d_poll =	cnpoll,
80	.d_name =	"console",
81	.d_flags =	D_TTY | D_NEEDGIANT,
82	.d_kqfilter =	cnkqfilter,
83};
84
85struct cn_device {
86	STAILQ_ENTRY(cn_device) cnd_next;
87	struct		vnode *cnd_vp;
88	struct		consdev *cnd_cn;
89};
90
91#define CNDEVPATHMAX	32
92#define CNDEVTAB_SIZE	4
93static struct cn_device cn_devtab[CNDEVTAB_SIZE];
94static STAILQ_HEAD(, cn_device) cn_devlist =
95    STAILQ_HEAD_INITIALIZER(cn_devlist);
96
97#define CND_INVALID(cnd, td) 						\
98	(cnd == NULL || cnd->cnd_vp == NULL ||				\
99	    (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
100
101static dev_t	cn_udev_t;
102SYSCTL_OPAQUE(_machdep, OID_AUTO, consdev, CTLFLAG_RD,
103	&cn_udev_t, sizeof cn_udev_t, "T,struct cdev *", "");
104
105int	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
106				 * which is currently unavailable for inpit
107				 * (i.e., if it is in graphics mode) will have
108				 * this bit cleared.
109				 */
110static int cn_mute;
111static int openflag;			/* how /dev/console was opened */
112static int cn_is_open;
113static char *consbuf;			/* buffer used by `consmsgbuf' */
114static struct callout conscallout;	/* callout for outputting to constty */
115struct msgbuf consmsgbuf;		/* message buffer for console tty */
116static u_char console_pausing;		/* pause after each line during probe */
117static char *console_pausestr=
118"<pause; press any key to proceed to next line or '.' to end pause mode>";
119struct tty *constty;			/* pointer to console "window" tty */
120
121static void constty_timeout(void *arg);
122
123static struct consdev cons_consdev;
124DATA_SET(cons_set, cons_consdev);
125SET_DECLARE(cons_set, struct consdev);
126
127void
128cninit(void)
129{
130	struct consdev *best_cn, *cn, **list;
131
132	/*
133	 * Check if we should mute the console (for security reasons perhaps)
134	 * It can be changes dynamically using sysctl kern.consmute
135	 * once we are up and going.
136	 *
137	 */
138        cn_mute = ((boothowto & (RB_MUTE
139			|RB_SINGLE
140			|RB_VERBOSE
141			|RB_ASKNAME)) == RB_MUTE);
142
143	/*
144	 * Find the first console with the highest priority.
145	 */
146	best_cn = NULL;
147	SET_FOREACH(list, cons_set) {
148		cn = *list;
149		cnremove(cn);
150		if (cn->cn_probe == NULL)
151			continue;
152		cn->cn_probe(cn);
153		if (cn->cn_pri == CN_DEAD)
154			continue;
155		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
156			best_cn = cn;
157		if (boothowto & RB_MULTIPLE) {
158			/*
159			 * Initialize console, and attach to it.
160			 */
161			cnadd(cn);
162			cn->cn_init(cn);
163		}
164	}
165	if (best_cn == NULL)
166		return;
167	if ((boothowto & RB_MULTIPLE) == 0) {
168		cnadd(best_cn);
169		best_cn->cn_init(best_cn);
170	}
171	if (boothowto & RB_PAUSE)
172		console_pausing = 1;
173	/*
174	 * Make the best console the preferred console.
175	 */
176	cnselect(best_cn);
177}
178
179void
180cninit_finish()
181{
182	console_pausing = 0;
183}
184
185/* add a new physical console to back the virtual console */
186int
187cnadd(struct consdev *cn)
188{
189	struct cn_device *cnd;
190	int i;
191
192	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
193		if (cnd->cnd_cn == cn)
194			return (0);
195	for (i = 0; i < CNDEVTAB_SIZE; i++) {
196		cnd = &cn_devtab[i];
197		if (cnd->cnd_cn == NULL)
198			break;
199	}
200	if (cnd->cnd_cn != NULL)
201		return (ENOMEM);
202	cnd->cnd_cn = cn;
203	if (cn->cn_name[0] == '\0') {
204		/* XXX: it is unclear if/where this print might output */
205		printf("WARNING: console at %p has no name\n", cn);
206	}
207	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
208
209	/* Add device to the active mask. */
210	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
211
212	return (0);
213}
214
215void
216cnremove(struct consdev *cn)
217{
218	struct cn_device *cnd;
219	int i;
220
221	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
222		if (cnd->cnd_cn != cn)
223			continue;
224		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
225		if (cnd->cnd_vp != NULL)
226			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
227		cnd->cnd_vp = NULL;
228		cnd->cnd_cn = NULL;
229
230		/* Remove this device from available mask. */
231		for (i = 0; i < CNDEVTAB_SIZE; i++)
232			if (cnd == &cn_devtab[i]) {
233				cons_avail_mask &= ~(1 << i);
234				break;
235			}
236#if 0
237		/*
238		 * XXX
239		 * syscons gets really confused if console resources are
240		 * freed after the system has initialized.
241		 */
242		if (cn->cn_term != NULL)
243			cn->cn_term(cn);
244#endif
245		return;
246	}
247}
248
249void
250cnselect(struct consdev *cn)
251{
252	struct cn_device *cnd;
253
254	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
255		if (cnd->cnd_cn != cn)
256			continue;
257		if (cnd == STAILQ_FIRST(&cn_devlist))
258			return;
259		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
260		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
261		return;
262	}
263}
264
265void
266cnavailable(struct consdev *cn, int available)
267{
268	int i;
269
270	for (i = 0; i < CNDEVTAB_SIZE; i++) {
271		if (cn_devtab[i].cnd_cn == cn)
272			break;
273	}
274	if (available) {
275		if (i < CNDEVTAB_SIZE)
276			cons_avail_mask |= (1 << i);
277		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
278	} else {
279		if (i < CNDEVTAB_SIZE)
280			cons_avail_mask &= ~(1 << i);
281		cn->cn_flags |= CN_FLAG_NOAVAIL;
282	}
283}
284
285int
286cnunavailable(void)
287{
288
289	return (cons_avail_mask == 0);
290}
291
292/*
293 * XXX: rewrite to use sbufs instead
294 */
295
296static int
297sysctl_kern_console(SYSCTL_HANDLER_ARGS)
298{
299	struct cn_device *cnd;
300	struct consdev *cp, **list;
301	char *name, *p;
302	int delete, len, error;
303
304	len = 2;
305	SET_FOREACH(list, cons_set) {
306		cp = *list;
307		if (cp->cn_name[0] != '\0')
308			len += strlen(cp->cn_name) + 1;
309	}
310	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
311		len += strlen(cnd->cnd_cn->cn_name) + 1;
312	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
313	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
314	p = name;
315	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
316		p += sprintf(p, "%s,", cnd->cnd_cn->cn_name);
317	*p++ = '/';
318	SET_FOREACH(list, cons_set) {
319		cp = *list;
320		if (cp->cn_name[0] != '\0')
321			p += sprintf(p, "%s,", cp->cn_name);
322	}
323	error = sysctl_handle_string(oidp, name, len, req);
324	if (error == 0 && req->newptr != NULL) {
325		p = name;
326		error = ENXIO;
327		delete = 0;
328		if (*p == '-') {
329			delete = 1;
330			p++;
331		}
332		SET_FOREACH(list, cons_set) {
333			cp = *list;
334			if (strcmp(p, cp->cn_name) != 0)
335				continue;
336			if (delete) {
337				cnremove(cp);
338				error = 0;
339			} else {
340				error = cnadd(cp);
341				if (error == 0)
342					cnselect(cp);
343			}
344			break;
345		}
346	}
347	FREE(name, M_TEMP);
348	return (error);
349}
350
351SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
352	0, 0, sysctl_kern_console, "A", "Console device control");
353
354/*
355 * User has changed the state of the console muting.
356 * This may require us to open or close the device in question.
357 */
358static int
359sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
360{
361	int error;
362	int ocn_mute;
363
364	ocn_mute = cn_mute;
365	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
366	if (error != 0 || req->newptr == NULL)
367		return (error);
368	if (ocn_mute && !cn_mute && cn_is_open)
369		error = cnopen(NULL, openflag, 0, curthread);
370	else if (!ocn_mute && cn_mute && cn_is_open) {
371		error = cnclose(NULL, openflag, 0, curthread);
372		cn_is_open = 1;		/* XXX hack */
373	}
374	return (error);
375}
376
377SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
378	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
379
380static int
381cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
382{
383	char path[CNDEVPATHMAX];
384	struct nameidata nd;
385	struct vnode *vp;
386	struct cdev *dev;
387	struct cdevsw *csw;
388	int error;
389
390	if ((vp = cnd->cnd_vp) != NULL) {
391		if (!forceopen && vp->v_type != VBAD) {
392			dev = vp->v_rdev;
393			csw = dev_refthread(dev);
394			if (csw == NULL)
395				return (ENXIO);
396			error = (*csw->d_open)(dev, openflag, 0, td);
397			dev_relthread(dev);
398			return (error);
399		}
400		cnd->cnd_vp = NULL;
401		vn_close(vp, openflag, td->td_ucred, td);
402	}
403	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
404	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
405	error = vn_open(&nd, &openflag, 0, -1);
406	if (error == 0) {
407		NDFREE(&nd, NDF_ONLY_PNBUF);
408		VOP_UNLOCK(nd.ni_vp, 0, td);
409		if (nd.ni_vp->v_type == VCHR)
410			cnd->cnd_vp = nd.ni_vp;
411		else
412			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
413	}
414	return (cnd->cnd_vp != NULL);
415}
416
417static int
418cnopen(struct cdev *dev, int flag, int mode, struct thread *td)
419{
420	struct cn_device *cnd;
421
422	openflag = flag | FWRITE;	/* XXX */
423	cn_is_open = 1;			/* console is logically open */
424	if (cn_mute)
425		return (0);
426	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
427		cn_devopen(cnd, td, 0);
428	return (0);
429}
430
431static int
432cnclose(struct cdev *dev, int flag, int mode, struct thread *td)
433{
434	struct cn_device *cnd;
435	struct vnode *vp;
436
437	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
438		if ((vp = cnd->cnd_vp) == NULL)
439			continue;
440		cnd->cnd_vp = NULL;
441		vn_close(vp, openflag, td->td_ucred, td);
442	}
443	cn_is_open = 0;
444	return (0);
445}
446
447static int
448cnread(struct cdev *dev, struct uio *uio, int flag)
449{
450	struct cn_device *cnd;
451	struct cdevsw *csw;
452	int error;
453
454	cnd = STAILQ_FIRST(&cn_devlist);
455	if (cn_mute || CND_INVALID(cnd, curthread))
456		return (0);
457	dev = cnd->cnd_vp->v_rdev;
458	csw = dev_refthread(dev);
459	if (csw == NULL)
460		return (ENXIO);
461	error = (csw->d_read)(dev, uio, flag);
462	dev_relthread(dev);
463	return (error);
464}
465
466static int
467cnwrite(struct cdev *dev, struct uio *uio, int flag)
468{
469	struct cn_device *cnd;
470	struct cdevsw *csw;
471	int error;
472
473	cnd = STAILQ_FIRST(&cn_devlist);
474	if (cn_mute || CND_INVALID(cnd, curthread))
475		goto done;
476	if (constty)
477		dev = constty->t_dev;
478	else
479		dev = cnd->cnd_vp->v_rdev;
480	if (dev != NULL) {
481		log_console(uio);
482		csw = dev_refthread(dev);
483		if (csw == NULL)
484			return (ENXIO);
485		error = (csw->d_write)(dev, uio, flag);
486		dev_relthread(dev);
487		return (error);
488	}
489done:
490	uio->uio_resid = 0; /* dump the data */
491	return (0);
492}
493
494static int
495cnioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
496{
497	struct cn_device *cnd;
498	struct cdevsw *csw;
499	int error;
500
501	cnd = STAILQ_FIRST(&cn_devlist);
502	if (cn_mute || CND_INVALID(cnd, td))
503		return (0);
504	/*
505	 * Superuser can always use this to wrest control of console
506	 * output from the "virtual" console.
507	 */
508	if (cmd == TIOCCONS && constty) {
509		error = suser(td);
510		if (error)
511			return (error);
512		constty = NULL;
513		return (0);
514	}
515	dev = cnd->cnd_vp->v_rdev;
516	if (dev == NULL)
517		return (0);	/* XXX : ENOTTY ? */
518	csw = dev_refthread(dev);
519	if (csw == NULL)
520		return (ENXIO);
521	error = (csw->d_ioctl)(dev, cmd, data, flag, td);
522	dev_relthread(dev);
523	return (error);
524}
525
526/*
527 * XXX
528 * poll/kqfilter do not appear to be correct
529 */
530static int
531cnpoll(struct cdev *dev, int events, struct thread *td)
532{
533	struct cn_device *cnd;
534	struct cdevsw *csw;
535	int error;
536
537	cnd = STAILQ_FIRST(&cn_devlist);
538	if (cn_mute || CND_INVALID(cnd, td))
539		return (0);
540	dev = cnd->cnd_vp->v_rdev;
541	if (dev == NULL)
542		return (0);
543	csw = dev_refthread(dev);
544	if (csw == NULL)
545		return (ENXIO);
546	error = (csw->d_poll)(dev, events, td);
547	dev_relthread(dev);
548	return (error);
549}
550
551static int
552cnkqfilter(struct cdev *dev, struct knote *kn)
553{
554	struct cn_device *cnd;
555	struct cdevsw *csw;
556	int error;
557
558	cnd = STAILQ_FIRST(&cn_devlist);
559	if (cn_mute || CND_INVALID(cnd, curthread))
560		return (EINVAL);
561	dev = cnd->cnd_vp->v_rdev;
562	if (dev == NULL)
563		return (ENXIO);
564	csw = dev_refthread(dev);
565	if (csw == NULL)
566		return (ENXIO);
567	error = (csw->d_kqfilter)(dev, kn);
568	dev_relthread(dev);
569	return (error);
570}
571
572/*
573 * Low level console routines.
574 */
575int
576cngetc(void)
577{
578	int c;
579
580	if (cn_mute)
581		return (-1);
582	while ((c = cncheckc()) == -1)
583		;
584	if (c == '\r')
585		c = '\n';		/* console input is always ICRNL */
586	return (c);
587}
588
589int
590cncheckc(void)
591{
592	struct cn_device *cnd;
593	struct consdev *cn;
594	int c;
595
596	if (cn_mute)
597		return (-1);
598	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
599		cn = cnd->cnd_cn;
600		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
601			c = cn->cn_checkc(cn);
602			if (c != -1) {
603				return (c);
604			}
605		}
606	}
607	return (-1);
608}
609
610void
611cnputc(int c)
612{
613	struct cn_device *cnd;
614	struct consdev *cn;
615	char *cp;
616
617	if (cn_mute || c == '\0')
618		return;
619	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
620		cn = cnd->cnd_cn;
621		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
622			if (c == '\n')
623				cn->cn_putc(cn, '\r');
624			cn->cn_putc(cn, c);
625		}
626	}
627	if (console_pausing && c == '\n' && !kdb_active) {
628		for (cp = console_pausestr; *cp != '\0'; cp++)
629			cnputc(*cp);
630		if (cngetc() == '.')
631			console_pausing = 0;
632		cnputc('\r');
633		for (cp = console_pausestr; *cp != '\0'; cp++)
634			cnputc(' ');
635		cnputc('\r');
636	}
637}
638
639static int consmsgbuf_size = 8192;
640SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
641    "");
642
643/*
644 * Redirect console output to a tty.
645 */
646void
647constty_set(struct tty *tp)
648{
649	int size;
650
651	KASSERT(tp != NULL, ("constty_set: NULL tp"));
652	if (consbuf == NULL) {
653		size = consmsgbuf_size;
654		consbuf = malloc(size, M_TTYS, M_WAITOK);
655		msgbuf_init(&consmsgbuf, consbuf, size);
656		callout_init(&conscallout, 0);
657	}
658	constty = tp;
659	constty_timeout(NULL);
660}
661
662/*
663 * Disable console redirection to a tty.
664 */
665void
666constty_clear(void)
667{
668	int c;
669
670	constty = NULL;
671	if (consbuf == NULL)
672		return;
673	callout_stop(&conscallout);
674	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
675		cnputc(c);
676	free(consbuf, M_TTYS);
677	consbuf = NULL;
678}
679
680/* Times per second to check for pending console tty messages. */
681static int constty_wakeups_per_second = 5;
682SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
683    &constty_wakeups_per_second, 0, "");
684
685static void
686constty_timeout(void *arg)
687{
688	int c;
689
690	while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
691		if (tputchar(c, constty) < 0)
692			constty = NULL;
693	}
694	if (constty != NULL) {
695		callout_reset(&conscallout, hz / constty_wakeups_per_second,
696		    constty_timeout, NULL);
697	} else {
698		/* Deallocate the constty buffer memory. */
699		constty_clear();
700	}
701}
702
703static void
704cn_drvinit(void *unused)
705{
706
707	make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
708}
709
710SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
711