kern_cons.c revision 105354
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 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
39 * $FreeBSD: head/sys/kern/tty_cons.c 105354 2002-10-17 20:03:38Z robert $
40 */
41
42#include "opt_ddb.h"
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/conf.h>
47#include <sys/cons.h>
48#include <sys/fcntl.h>
49#include <sys/kernel.h>
50#include <sys/malloc.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
72#define	CDEV_MAJOR	0
73static struct cdevsw cn_cdevsw = {
74	/* open */	cnopen,
75	/* close */	cnclose,
76	/* read */	cnread,
77	/* write */	cnwrite,
78	/* ioctl */	cnioctl,
79	/* poll */	cnpoll,
80	/* mmap */	nommap,
81	/* strategy */	nostrategy,
82	/* name */	"console",
83	/* maj */	CDEV_MAJOR,
84	/* dump */	nodump,
85	/* psize */	nopsize,
86	/* flags */	D_TTY | D_KQFILTER,
87	/* kqfilter */	cnkqfilter,
88};
89
90struct cn_device {
91	STAILQ_ENTRY(cn_device) cnd_next;
92	char		cnd_name[16];
93	struct		vnode *cnd_vp;
94	struct		consdev *cnd_cn;
95};
96
97#define CNDEVPATHMAX	32
98#define CNDEVTAB_SIZE	4
99static struct cn_device cn_devtab[CNDEVTAB_SIZE];
100static STAILQ_HEAD(, cn_device) cn_devlist =
101    STAILQ_HEAD_INITIALIZER(cn_devlist);
102
103#define CND_INVALID(cnd, td) 						\
104	(cnd == NULL || cnd->cnd_vp == NULL ||				\
105	    (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
106
107static udev_t	cn_udev_t;
108SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
109	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
110
111int	cons_unavail = 0;	/* XXX:
112				 * physical console not available for
113				 * input (i.e., it is in graphics mode)
114				 */
115static int cn_mute;
116static int openflag;			/* how /dev/console was opened */
117static int cn_is_open;
118static dev_t cn_devfsdev;		/* represents the device private info */
119static u_char console_pausing;		/* pause after each line during probe */
120static char *console_pausestr=
121"<pause; press any key to proceed to next line or '.' to end pause mode>";
122
123void	cndebug(char *);
124
125CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
126SET_DECLARE(cons_set, struct consdev);
127
128void
129cninit(void)
130{
131	struct consdev *best_cn, *cn, **list;
132
133	/*
134	 * Check if we should mute the console (for security reasons perhaps)
135	 * It can be changes dynamically using sysctl kern.consmute
136	 * once we are up and going.
137	 *
138	 */
139        cn_mute = ((boothowto & (RB_MUTE
140			|RB_SINGLE
141			|RB_VERBOSE
142			|RB_ASKNAME
143			|RB_CONFIG)) == RB_MUTE);
144
145	/*
146	 * Find the first console with the highest priority.
147	 */
148	best_cn = NULL;
149	SET_FOREACH(list, cons_set) {
150		cn = *list;
151		cnremove(cn);
152		if (cn->cn_probe == NULL)
153			continue;
154		cn->cn_probe(cn);
155		if (cn->cn_pri == CN_DEAD)
156			continue;
157		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
158			best_cn = cn;
159		if (boothowto & RB_MULTIPLE) {
160			/*
161			 * Initialize console, and attach to it.
162			 */
163			cnadd(cn);
164			cn->cn_init(cn);
165		}
166	}
167	if (best_cn == NULL)
168		return;
169	if ((boothowto & RB_MULTIPLE) == 0) {
170		cnadd(best_cn);
171		best_cn->cn_init(best_cn);
172	}
173	if (boothowto & RB_PAUSE)
174		console_pausing = 1;
175	/*
176	 * Make the best console the preferred console.
177	 */
178	cnselect(best_cn);
179}
180
181void
182cninit_finish()
183{
184	console_pausing = 0;
185}
186
187/* add a new physical console to back the virtual console */
188int
189cnadd(struct consdev *cn)
190{
191	struct cn_device *cnd;
192	int i;
193
194	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
195		if (cnd->cnd_cn == cn)
196			return (0);
197	for (i = 0; i < CNDEVTAB_SIZE; i++) {
198		cnd = &cn_devtab[i];
199		if (cnd->cnd_cn == NULL)
200			break;
201	}
202	if (cnd->cnd_cn != NULL)
203		return (ENOMEM);
204	cnd->cnd_cn = cn;
205	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
206	return (0);
207}
208
209void
210cnremove(struct consdev *cn)
211{
212	struct cn_device *cnd;
213
214	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
215		if (cnd->cnd_cn != cn)
216			continue;
217		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
218		if (cnd->cnd_vp != NULL)
219			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
220		cnd->cnd_vp = NULL;
221		cnd->cnd_cn = NULL;
222		cnd->cnd_name[0] = '\0';
223#if 0
224		/*
225		 * XXX
226		 * syscons gets really confused if console resources are
227		 * freed after the system has initialized.
228		 */
229		if (cn->cn_term != NULL)
230			cn->cn_term(cn);
231#endif
232		return;
233	}
234}
235
236void
237cnselect(struct consdev *cn)
238{
239	struct cn_device *cnd;
240
241	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
242		if (cnd->cnd_cn != cn)
243			continue;
244		if (cnd == STAILQ_FIRST(&cn_devlist))
245			return;
246		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
247		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
248		return;
249	}
250}
251
252void
253cndebug(char *str)
254{
255	int i, len;
256
257	len = strlen(str);
258	cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
259	for (i = 0; i < len; i++)
260		cnputc(str[i]);
261	cnputc('\n');
262}
263
264static int
265sysctl_kern_console(SYSCTL_HANDLER_ARGS)
266{
267	struct cn_device *cnd;
268	struct consdev *cp, **list;
269	char *name, *p;
270	int delete, len, error;
271
272	len = 2;
273	SET_FOREACH(list, cons_set) {
274		cp = *list;
275		if (cp->cn_dev != NULL)
276			len += strlen(devtoname(cp->cn_dev)) + 1;
277	}
278	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
279		len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
280	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
281	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
282	p = name;
283	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
284		p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
285	*p++ = '/';
286	SET_FOREACH(list, cons_set) {
287		cp = *list;
288		if (cp->cn_dev != NULL)
289			p += sprintf(p, "%s,", devtoname(cp->cn_dev));
290	}
291	error = sysctl_handle_string(oidp, name, len, req);
292	if (error == 0 && req->newptr != NULL) {
293		p = name;
294		error = ENXIO;
295		delete = 0;
296		if (*p == '-') {
297			delete = 1;
298			p++;
299		}
300		SET_FOREACH(list, cons_set) {
301			cp = *list;
302			if (cp->cn_dev == NULL ||
303			    strcmp(p, devtoname(cp->cn_dev)) != 0)
304				continue;
305			if (delete) {
306				cnremove(cp);
307				error = 0;
308			} else {
309				error = cnadd(cp);
310				if (error == 0)
311					cnselect(cp);
312			}
313			break;
314		}
315	}
316	FREE(name, M_TEMP);
317	return (error);
318}
319
320SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
321	0, 0, sysctl_kern_console, "A", "Console device control");
322
323/*
324 * User has changed the state of the console muting.
325 * This may require us to open or close the device in question.
326 */
327static int
328sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
329{
330	int error;
331	int ocn_mute;
332
333	ocn_mute = cn_mute;
334	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
335	if (error != 0 || req->newptr == NULL)
336		return (error);
337	if (ocn_mute && !cn_mute && cn_is_open)
338		error = cnopen(NODEV, openflag, 0, curthread);
339	else if (!ocn_mute && cn_mute && cn_is_open) {
340		error = cnclose(NODEV, openflag, 0, curthread);
341		cn_is_open = 1;		/* XXX hack */
342	}
343	return (error);
344}
345
346SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
347	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
348
349static int
350cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
351{
352	char path[CNDEVPATHMAX];
353	struct nameidata nd;
354	struct vnode *vp;
355	dev_t dev;
356	int error;
357
358	if ((vp = cnd->cnd_vp) != NULL) {
359		if (!forceopen && vp->v_type != VBAD) {
360			dev = vp->v_rdev;
361			return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
362		}
363		cnd->cnd_vp = NULL;
364		vn_close(vp, openflag, td->td_ucred, td);
365	}
366	if (cnd->cnd_name[0] == '\0') {
367		strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
368		    sizeof(cnd->cnd_name));
369	}
370	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
371	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
372	error = vn_open(&nd, &openflag, 0);
373	if (error == 0) {
374		NDFREE(&nd, NDF_ONLY_PNBUF);
375		VOP_UNLOCK(nd.ni_vp, 0, td);
376		if (nd.ni_vp->v_type == VCHR)
377			cnd->cnd_vp = nd.ni_vp;
378		else
379			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
380	}
381	return (cnd->cnd_vp != NULL);
382}
383
384static int
385cnopen(dev_t dev, int flag, int mode, struct thread *td)
386{
387	struct cn_device *cnd;
388
389	openflag = flag | FWRITE;	/* XXX */
390	cn_is_open = 1;			/* console is logically open */
391	if (cn_mute)
392		return (0);
393	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
394		cn_devopen(cnd, td, 0);
395	return (0);
396}
397
398static int
399cnclose(dev_t dev, int flag, int mode, struct thread *td)
400{
401	struct cn_device *cnd;
402	struct vnode *vp;
403
404	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
405		if ((vp = cnd->cnd_vp) == NULL)
406			continue;
407		cnd->cnd_vp = NULL;
408		vn_close(vp, openflag, td->td_ucred, td);
409	}
410	cn_is_open = 0;
411	return (0);
412}
413
414static int
415cnread(dev_t dev, struct uio *uio, int flag)
416{
417	struct cn_device *cnd;
418
419	cnd = STAILQ_FIRST(&cn_devlist);
420	if (cn_mute || CND_INVALID(cnd, curthread))
421		return (0);
422	dev = cnd->cnd_vp->v_rdev;
423	return ((*devsw(dev)->d_read)(dev, uio, flag));
424}
425
426static int
427cnwrite(dev_t dev, struct uio *uio, int flag)
428{
429	struct cn_device *cnd;
430
431	cnd = STAILQ_FIRST(&cn_devlist);
432	if (cn_mute || CND_INVALID(cnd, curthread))
433		goto done;
434	if (constty)
435		dev = constty->t_dev;
436	else
437		dev = cnd->cnd_vp->v_rdev;
438	if (dev != NULL) {
439		log_console(uio);
440		return ((*devsw(dev)->d_write)(dev, uio, flag));
441	}
442done:
443	uio->uio_resid = 0; /* dump the data */
444	return (0);
445}
446
447static int
448cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
449{
450	struct cn_device *cnd;
451	int error;
452
453	cnd = STAILQ_FIRST(&cn_devlist);
454	if (cn_mute || CND_INVALID(cnd, td))
455		return (0);
456	/*
457	 * Superuser can always use this to wrest control of console
458	 * output from the "virtual" console.
459	 */
460	if (cmd == TIOCCONS && constty) {
461		error = suser(td);
462		if (error)
463			return (error);
464		constty = NULL;
465		return (0);
466	}
467	dev = cnd->cnd_vp->v_rdev;
468	if (dev != NULL)
469		return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
470	return (0);
471}
472
473/*
474 * XXX
475 * poll/kqfilter do not appear to be correct
476 */
477static int
478cnpoll(dev_t dev, int events, struct thread *td)
479{
480	struct cn_device *cnd;
481
482	cnd = STAILQ_FIRST(&cn_devlist);
483	if (cn_mute || CND_INVALID(cnd, td))
484		return (0);
485	dev = cnd->cnd_vp->v_rdev;
486	if (dev != NULL)
487		return ((*devsw(dev)->d_poll)(dev, events, td));
488	return (0);
489}
490
491static int
492cnkqfilter(dev_t dev, struct knote *kn)
493{
494	struct cn_device *cnd;
495
496	cnd = STAILQ_FIRST(&cn_devlist);
497	if (cn_mute || CND_INVALID(cnd, curthread))
498		return (1);
499	dev = cnd->cnd_vp->v_rdev;
500	if (dev != NULL)
501		return ((*devsw(dev)->d_kqfilter)(dev, kn));
502	return (1);
503}
504
505/*
506 * Low level console routines.
507 */
508int
509cngetc(void)
510{
511	int c;
512
513	if (cn_mute)
514		return (-1);
515	while ((c = cncheckc()) == -1)
516		;
517	if (c == '\r')
518		c = '\n';		/* console input is always ICRNL */
519	return (c);
520}
521
522int
523cncheckc(void)
524{
525	struct cn_device *cnd;
526	struct consdev *cn;
527	int c;
528
529	if (cn_mute)
530		return (-1);
531	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
532		cn = cnd->cnd_cn;
533		c = cn->cn_checkc(cn->cn_dev);
534		if (c != -1) {
535			return (c);
536		}
537	}
538	return (-1);
539}
540
541void
542cnputc(int c)
543{
544	struct cn_device *cnd;
545	struct consdev *cn;
546	char *cp;
547
548	if (cn_mute || c == '\0')
549		return;
550	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
551		cn = cnd->cnd_cn;
552		if (c == '\n')
553			cn->cn_putc(cn->cn_dev, '\r');
554		cn->cn_putc(cn->cn_dev, c);
555	}
556#ifdef DDB
557	if (console_pausing && !db_active && (c == '\n')) {
558#else
559	if (console_pausing && (c == '\n')) {
560#endif
561		for (cp = console_pausestr; *cp != '\0'; cp++)
562			cnputc(*cp);
563		if (cngetc() == '.')
564			console_pausing = 0;
565		cnputc('\r');
566		for (cp = console_pausestr; *cp != '\0'; cp++)
567			cnputc(' ');
568		cnputc('\r');
569	}
570}
571
572void
573cndbctl(int on)
574{
575	struct cn_device *cnd;
576	struct consdev *cn;
577	static int refcount;
578
579	if (!on)
580		refcount--;
581	if (refcount == 0)
582		STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
583			cn = cnd->cnd_cn;
584			if (cn->cn_dbctl != NULL)
585				cn->cn_dbctl(cn->cn_dev, on);
586		}
587	if (on)
588		refcount++;
589}
590
591static void
592cn_drvinit(void *unused)
593{
594
595	cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
596	    "console");
597}
598
599SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
600