kern_cons.c revision 158941
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 158941 2006-05-26 10:24:00Z 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
123CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
124SET_DECLARE(cons_set, struct consdev);
125
126void
127cninit(void)
128{
129	struct consdev *best_cn, *cn, **list;
130
131	/*
132	 * Check if we should mute the console (for security reasons perhaps)
133	 * It can be changes dynamically using sysctl kern.consmute
134	 * once we are up and going.
135	 *
136	 */
137        cn_mute = ((boothowto & (RB_MUTE
138			|RB_SINGLE
139			|RB_VERBOSE
140			|RB_ASKNAME)) == RB_MUTE);
141
142	/*
143	 * Find the first console with the highest priority.
144	 */
145	best_cn = NULL;
146	SET_FOREACH(list, cons_set) {
147		cn = *list;
148		cnremove(cn);
149		if (cn->cn_probe == NULL)
150			continue;
151		cn->cn_probe(cn);
152		if (cn->cn_pri == CN_DEAD)
153			continue;
154		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
155			best_cn = cn;
156		if (boothowto & RB_MULTIPLE) {
157			/*
158			 * Initialize console, and attach to it.
159			 */
160			cnadd(cn);
161			cn->cn_init(cn);
162		}
163	}
164	if (best_cn == NULL)
165		return;
166	if ((boothowto & RB_MULTIPLE) == 0) {
167		cnadd(best_cn);
168		best_cn->cn_init(best_cn);
169	}
170	if (boothowto & RB_PAUSE)
171		console_pausing = 1;
172	/*
173	 * Make the best console the preferred console.
174	 */
175	cnselect(best_cn);
176}
177
178void
179cninit_finish()
180{
181	console_pausing = 0;
182}
183
184/* add a new physical console to back the virtual console */
185int
186cnadd(struct consdev *cn)
187{
188	struct cn_device *cnd;
189	int i;
190
191	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
192		if (cnd->cnd_cn == cn)
193			return (0);
194	for (i = 0; i < CNDEVTAB_SIZE; i++) {
195		cnd = &cn_devtab[i];
196		if (cnd->cnd_cn == NULL)
197			break;
198	}
199	if (cnd->cnd_cn != NULL)
200		return (ENOMEM);
201	cnd->cnd_cn = cn;
202	if (cn->cn_name[0] == '\0') {
203		/* XXX: it is unclear if/where this print might output */
204		printf("WARNING: console at %p has no name\n", cn);
205	}
206	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
207
208	/* Add device to the active mask. */
209	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
210
211	return (0);
212}
213
214void
215cnremove(struct consdev *cn)
216{
217	struct cn_device *cnd;
218	int i;
219
220	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
221		if (cnd->cnd_cn != cn)
222			continue;
223		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
224		if (cnd->cnd_vp != NULL)
225			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
226		cnd->cnd_vp = NULL;
227		cnd->cnd_cn = NULL;
228
229		/* Remove this device from available mask. */
230		for (i = 0; i < CNDEVTAB_SIZE; i++)
231			if (cnd == &cn_devtab[i]) {
232				cons_avail_mask &= ~(1 << i);
233				break;
234			}
235#if 0
236		/*
237		 * XXX
238		 * syscons gets really confused if console resources are
239		 * freed after the system has initialized.
240		 */
241		if (cn->cn_term != NULL)
242			cn->cn_term(cn);
243#endif
244		return;
245	}
246}
247
248void
249cnselect(struct consdev *cn)
250{
251	struct cn_device *cnd;
252
253	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
254		if (cnd->cnd_cn != cn)
255			continue;
256		if (cnd == STAILQ_FIRST(&cn_devlist))
257			return;
258		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
259		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
260		return;
261	}
262}
263
264void
265cnavailable(struct consdev *cn, int available)
266{
267	int i;
268
269	for (i = 0; i < CNDEVTAB_SIZE; i++) {
270		if (cn_devtab[i].cnd_cn == cn)
271			break;
272	}
273	if (available) {
274		if (i < CNDEVTAB_SIZE)
275			cons_avail_mask |= (1 << i);
276		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
277	} else {
278		if (i < CNDEVTAB_SIZE)
279			cons_avail_mask &= ~(1 << i);
280		cn->cn_flags |= CN_FLAG_NOAVAIL;
281	}
282}
283
284int
285cnunavailable(void)
286{
287
288	return (cons_avail_mask == 0);
289}
290
291/*
292 * XXX: rewrite to use sbufs instead
293 */
294
295static int
296sysctl_kern_console(SYSCTL_HANDLER_ARGS)
297{
298	struct cn_device *cnd;
299	struct consdev *cp, **list;
300	char *name, *p;
301	int delete, len, error;
302
303	len = 2;
304	SET_FOREACH(list, cons_set) {
305		cp = *list;
306		if (cp->cn_name[0] != '\0')
307			len += strlen(cp->cn_name) + 1;
308	}
309	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
310		len += strlen(cnd->cnd_cn->cn_name) + 1;
311	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
312	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
313	p = name;
314	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
315		p += sprintf(p, "%s,", cnd->cnd_cn->cn_name);
316	*p++ = '/';
317	SET_FOREACH(list, cons_set) {
318		cp = *list;
319		if (cp->cn_name[0] != '\0')
320			p += sprintf(p, "%s,", cp->cn_name);
321	}
322	error = sysctl_handle_string(oidp, name, len, req);
323	if (error == 0 && req->newptr != NULL) {
324		p = name;
325		error = ENXIO;
326		delete = 0;
327		if (*p == '-') {
328			delete = 1;
329			p++;
330		}
331		SET_FOREACH(list, cons_set) {
332			cp = *list;
333			if (strcmp(p, cp->cn_name) != 0)
334				continue;
335			if (delete) {
336				cnremove(cp);
337				error = 0;
338			} else {
339				error = cnadd(cp);
340				if (error == 0)
341					cnselect(cp);
342			}
343			break;
344		}
345	}
346	FREE(name, M_TEMP);
347	return (error);
348}
349
350SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
351	0, 0, sysctl_kern_console, "A", "Console device control");
352
353/*
354 * User has changed the state of the console muting.
355 * This may require us to open or close the device in question.
356 */
357static int
358sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
359{
360	int error;
361	int ocn_mute;
362
363	ocn_mute = cn_mute;
364	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
365	if (error != 0 || req->newptr == NULL)
366		return (error);
367	if (ocn_mute && !cn_mute && cn_is_open)
368		error = cnopen(NULL, openflag, 0, curthread);
369	else if (!ocn_mute && cn_mute && cn_is_open) {
370		error = cnclose(NULL, openflag, 0, curthread);
371		cn_is_open = 1;		/* XXX hack */
372	}
373	return (error);
374}
375
376SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
377	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
378
379static int
380cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
381{
382	char path[CNDEVPATHMAX];
383	struct nameidata nd;
384	struct vnode *vp;
385	struct cdev *dev;
386	struct cdevsw *csw;
387	int error;
388
389	if ((vp = cnd->cnd_vp) != NULL) {
390		if (!forceopen && vp->v_type != VBAD) {
391			dev = vp->v_rdev;
392			csw = dev_refthread(dev);
393			if (csw == NULL)
394				return (ENXIO);
395			error = (*csw->d_open)(dev, openflag, 0, td);
396			dev_relthread(dev);
397			return (error);
398		}
399		cnd->cnd_vp = NULL;
400		vn_close(vp, openflag, td->td_ucred, td);
401	}
402	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
403	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
404	error = vn_open(&nd, &openflag, 0, -1);
405	if (error == 0) {
406		NDFREE(&nd, NDF_ONLY_PNBUF);
407		VOP_UNLOCK(nd.ni_vp, 0, td);
408		if (nd.ni_vp->v_type == VCHR)
409			cnd->cnd_vp = nd.ni_vp;
410		else
411			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
412	}
413	return (cnd->cnd_vp != NULL);
414}
415
416static int
417cnopen(struct cdev *dev, int flag, int mode, struct thread *td)
418{
419	struct cn_device *cnd;
420
421	openflag = flag | FWRITE;	/* XXX */
422	cn_is_open = 1;			/* console is logically open */
423	if (cn_mute)
424		return (0);
425	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
426		cn_devopen(cnd, td, 0);
427	return (0);
428}
429
430static int
431cnclose(struct cdev *dev, int flag, int mode, struct thread *td)
432{
433	struct cn_device *cnd;
434	struct vnode *vp;
435
436	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
437		if ((vp = cnd->cnd_vp) == NULL)
438			continue;
439		cnd->cnd_vp = NULL;
440		vn_close(vp, openflag, td->td_ucred, td);
441	}
442	cn_is_open = 0;
443	return (0);
444}
445
446static int
447cnread(struct cdev *dev, struct uio *uio, int flag)
448{
449	struct cn_device *cnd;
450	struct cdevsw *csw;
451	int error;
452
453	cnd = STAILQ_FIRST(&cn_devlist);
454	if (cn_mute || CND_INVALID(cnd, curthread))
455		return (0);
456	dev = cnd->cnd_vp->v_rdev;
457	csw = dev_refthread(dev);
458	if (csw == NULL)
459		return (ENXIO);
460	error = (csw->d_read)(dev, uio, flag);
461	dev_relthread(dev);
462	return (error);
463}
464
465static int
466cnwrite(struct cdev *dev, struct uio *uio, int flag)
467{
468	struct cn_device *cnd;
469	struct cdevsw *csw;
470	int error;
471
472	cnd = STAILQ_FIRST(&cn_devlist);
473	if (cn_mute || CND_INVALID(cnd, curthread))
474		goto done;
475	if (constty)
476		dev = constty->t_dev;
477	else
478		dev = cnd->cnd_vp->v_rdev;
479	if (dev != NULL) {
480		log_console(uio);
481		csw = dev_refthread(dev);
482		if (csw == NULL)
483			return (ENXIO);
484		error = (csw->d_write)(dev, uio, flag);
485		dev_relthread(dev);
486		return (error);
487	}
488done:
489	uio->uio_resid = 0; /* dump the data */
490	return (0);
491}
492
493static int
494cnioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
495{
496	struct cn_device *cnd;
497	struct cdevsw *csw;
498	int error;
499
500	cnd = STAILQ_FIRST(&cn_devlist);
501	if (cn_mute || CND_INVALID(cnd, td))
502		return (0);
503	/*
504	 * Superuser can always use this to wrest control of console
505	 * output from the "virtual" console.
506	 */
507	if (cmd == TIOCCONS && constty) {
508		error = suser(td);
509		if (error)
510			return (error);
511		constty = NULL;
512		return (0);
513	}
514	dev = cnd->cnd_vp->v_rdev;
515	if (dev == NULL)
516		return (0);	/* XXX : ENOTTY ? */
517	csw = dev_refthread(dev);
518	if (csw == NULL)
519		return (ENXIO);
520	error = (csw->d_ioctl)(dev, cmd, data, flag, td);
521	dev_relthread(dev);
522	return (error);
523}
524
525/*
526 * XXX
527 * poll/kqfilter do not appear to be correct
528 */
529static int
530cnpoll(struct cdev *dev, int events, struct thread *td)
531{
532	struct cn_device *cnd;
533	struct cdevsw *csw;
534	int error;
535
536	cnd = STAILQ_FIRST(&cn_devlist);
537	if (cn_mute || CND_INVALID(cnd, td))
538		return (0);
539	dev = cnd->cnd_vp->v_rdev;
540	if (dev == NULL)
541		return (0);
542	csw = dev_refthread(dev);
543	if (csw == NULL)
544		return (ENXIO);
545	error = (csw->d_poll)(dev, events, td);
546	dev_relthread(dev);
547	return (error);
548}
549
550static int
551cnkqfilter(struct cdev *dev, struct knote *kn)
552{
553	struct cn_device *cnd;
554	struct cdevsw *csw;
555	int error;
556
557	cnd = STAILQ_FIRST(&cn_devlist);
558	if (cn_mute || CND_INVALID(cnd, curthread))
559		return (EINVAL);
560	dev = cnd->cnd_vp->v_rdev;
561	if (dev == NULL)
562		return (ENXIO);
563	csw = dev_refthread(dev);
564	if (csw == NULL)
565		return (ENXIO);
566	error = (csw->d_kqfilter)(dev, kn);
567	dev_relthread(dev);
568	return (error);
569}
570
571/*
572 * Low level console routines.
573 */
574int
575cngetc(void)
576{
577	int c;
578
579	if (cn_mute)
580		return (-1);
581	while ((c = cncheckc()) == -1)
582		;
583	if (c == '\r')
584		c = '\n';		/* console input is always ICRNL */
585	return (c);
586}
587
588int
589cncheckc(void)
590{
591	struct cn_device *cnd;
592	struct consdev *cn;
593	int c;
594
595	if (cn_mute)
596		return (-1);
597	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
598		cn = cnd->cnd_cn;
599		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
600			c = cn->cn_checkc(cn);
601			if (c != -1) {
602				return (c);
603			}
604		}
605	}
606	return (-1);
607}
608
609void
610cnputc(int c)
611{
612	struct cn_device *cnd;
613	struct consdev *cn;
614	char *cp;
615
616	if (cn_mute || c == '\0')
617		return;
618	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
619		cn = cnd->cnd_cn;
620		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
621			if (c == '\n')
622				cn->cn_putc(cn, '\r');
623			cn->cn_putc(cn, c);
624		}
625	}
626	if (console_pausing && c == '\n' && !kdb_active) {
627		for (cp = console_pausestr; *cp != '\0'; cp++)
628			cnputc(*cp);
629		if (cngetc() == '.')
630			console_pausing = 0;
631		cnputc('\r');
632		for (cp = console_pausestr; *cp != '\0'; cp++)
633			cnputc(' ');
634		cnputc('\r');
635	}
636}
637
638static int consmsgbuf_size = 8192;
639SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
640    "");
641
642/*
643 * Redirect console output to a tty.
644 */
645void
646constty_set(struct tty *tp)
647{
648	int size;
649
650	KASSERT(tp != NULL, ("constty_set: NULL tp"));
651	if (consbuf == NULL) {
652		size = consmsgbuf_size;
653		consbuf = malloc(size, M_TTYS, M_WAITOK);
654		msgbuf_init(&consmsgbuf, consbuf, size);
655		callout_init(&conscallout, 0);
656	}
657	constty = tp;
658	constty_timeout(NULL);
659}
660
661/*
662 * Disable console redirection to a tty.
663 */
664void
665constty_clear(void)
666{
667	int c;
668
669	constty = NULL;
670	if (consbuf == NULL)
671		return;
672	callout_stop(&conscallout);
673	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
674		cnputc(c);
675	free(consbuf, M_TTYS);
676	consbuf = NULL;
677}
678
679/* Times per second to check for pending console tty messages. */
680static int constty_wakeups_per_second = 5;
681SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
682    &constty_wakeups_per_second, 0, "");
683
684static void
685constty_timeout(void *arg)
686{
687	int c;
688
689	while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
690		if (tputchar(c, constty) < 0)
691			constty = NULL;
692	}
693	if (constty != NULL) {
694		callout_reset(&conscallout, hz / constty_wakeups_per_second,
695		    constty_timeout, NULL);
696	} else {
697		/* Deallocate the constty buffer memory. */
698		constty_clear();
699	}
700}
701
702static void
703cn_drvinit(void *unused)
704{
705
706	make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
707}
708
709SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
710