kern_cons.c revision 62573
14Srgrimes/*
24Srgrimes * Copyright (c) 1988 University of Utah.
34Srgrimes * Copyright (c) 1991 The Regents of the University of California.
44Srgrimes * All rights reserved.
54Srgrimes *
64Srgrimes * This code is derived from software contributed to Berkeley by
74Srgrimes * the Systems Programming Group of the University of Utah Computer
84Srgrimes * Science Department.
94Srgrimes *
104Srgrimes * Redistribution and use in source and binary forms, with or without
114Srgrimes * modification, are permitted provided that the following conditions
124Srgrimes * are met:
134Srgrimes * 1. Redistributions of source code must retain the above copyright
144Srgrimes *    notice, this list of conditions and the following disclaimer.
154Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
164Srgrimes *    notice, this list of conditions and the following disclaimer in the
174Srgrimes *    documentation and/or other materials provided with the distribution.
184Srgrimes * 3. All advertising materials mentioning features or use of this software
194Srgrimes *    must display the following acknowledgement:
204Srgrimes *	This product includes software developed by the University of
214Srgrimes *	California, Berkeley and its contributors.
224Srgrimes * 4. Neither the name of the University nor the names of its contributors
234Srgrimes *    may be used to endorse or promote products derived from this software
244Srgrimes *    without specific prior written permission.
254Srgrimes *
264Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
274Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
284Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
294Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
304Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
314Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
324Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
334Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
344Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
354Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
364Srgrimes * SUCH DAMAGE.
374Srgrimes *
38620Srgrimes *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
3950477Speter * $FreeBSD: head/sys/kern/tty_cons.c 62573 2000-07-04 11:25:35Z phk $
404Srgrimes */
414Srgrimes
422056Swollman#include <sys/param.h>
431549Srgrimes#include <sys/systm.h>
445764Sbde#include <sys/conf.h>
4556525Sbde#include <sys/cons.h>
4612675Sjulian#include <sys/kernel.h>
4718951Sjulian#include <sys/reboot.h>
4812701Sphk#include <sys/sysctl.h>
492056Swollman#include <sys/proc.h>
502056Swollman#include <sys/tty.h>
5134924Sbde#include <sys/uio.h>
524Srgrimes
5312701Sphk#include <machine/cpu.h>
544Srgrimes
5512675Sjulianstatic	d_open_t	cnopen;
5612675Sjulianstatic	d_close_t	cnclose;
5712675Sjulianstatic	d_read_t	cnread;
5812675Sjulianstatic	d_write_t	cnwrite;
5912675Sjulianstatic	d_ioctl_t	cnioctl;
6029368Speterstatic	d_poll_t	cnpoll;
6112675Sjulian
6238485Sbde#define	CDEV_MAJOR	0
6347625Sphkstatic struct cdevsw cn_cdevsw = {
6447625Sphk	/* open */	cnopen,
6547625Sphk	/* close */	cnclose,
6647625Sphk	/* read */	cnread,
6747625Sphk	/* write */	cnwrite,
6847625Sphk	/* ioctl */	cnioctl,
6947625Sphk	/* poll */	cnpoll,
7047625Sphk	/* mmap */	nommap,
7147625Sphk	/* strategy */	nostrategy,
7247625Sphk	/* name */	"console",
7347625Sphk	/* maj */	CDEV_MAJOR,
7447625Sphk	/* dump */	nodump,
7547625Sphk	/* psize */	nopsize,
7647625Sphk	/* flags */	D_TTY,
7747625Sphk	/* bmaj */	-1
7838485Sbde};
7912675Sjulian
8027982Sjulianstatic dev_t	cn_dev_t; 	/* seems to be never really used */
8149049Syokotastatic udev_t	cn_udev_t;
8241612SeivindSYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
8349049Syokota	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
8427982Sjulian
8518951Sjulianstatic int cn_mute;
8612701Sphk
877680Sjoergint	cons_unavail = 0;	/* XXX:
887680Sjoerg				 * physical console not available for
897680Sjoerg				 * input (i.e., it is in graphics mode)
907680Sjoerg				 */
914Srgrimes
9227982Sjulianstatic u_char cn_is_open;		/* nonzero if logical console is open */
9327982Sjulianstatic int openmode, openflag;		/* how /dev/console was openned */
9456525Sbdestatic dev_t cn_devfsdev;		/* represents the device private info */
9527982Sjulianstatic u_char cn_phys_is_open;		/* nonzero if physical device is open */
965764Sbdestatic d_close_t *cn_phys_close;	/* physical device close function */
9727982Sjulianstatic d_open_t *cn_phys_open;		/* physical device open function */
9856525Sbde       struct consdev *cn_tab;		/* physical console device info */
995764Sbde
10055823SyokotaCONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
10142373Syokota
102798Swollmanvoid
1034Srgrimescninit()
1044Srgrimes{
10556525Sbde	struct consdev *best_cp, *cp, **list;
1064Srgrimes
1074Srgrimes	/*
10810665Sbde	 * Find the first console with the highest priority.
1094Srgrimes	 */
11010665Sbde	best_cp = NULL;
11148242Speter	list = (struct consdev **)cons_set.ls_items;
11242373Syokota	while ((cp = *list++) != NULL) {
11342373Syokota		if (cp->cn_probe == NULL)
11442373Syokota			continue;
1154Srgrimes		(*cp->cn_probe)(cp);
1164Srgrimes		if (cp->cn_pri > CN_DEAD &&
11710665Sbde		    (best_cp == NULL || cp->cn_pri > best_cp->cn_pri))
11810665Sbde			best_cp = cp;
1194Srgrimes	}
12010665Sbde
1214Srgrimes	/*
12218951Sjulian	 * Check if we should mute the console (for security reasons perhaps)
12318951Sjulian	 * It can be changes dynamically using sysctl kern.consmute
12418951Sjulian	 * once we are up and going.
12518951Sjulian	 *
12618951Sjulian	 */
12718951Sjulian        cn_mute = ((boothowto & (RB_MUTE
12818951Sjulian			|RB_SINGLE
12918951Sjulian			|RB_VERBOSE
13018951Sjulian			|RB_ASKNAME
13118951Sjulian			|RB_CONFIG)) == RB_MUTE);
13218951Sjulian
13318951Sjulian	/*
13410665Sbde	 * If no console, give up.
1354Srgrimes	 */
13610665Sbde	if (best_cp == NULL) {
13748104Syokota		if (cn_tab != NULL && cn_tab->cn_term != NULL)
13848104Syokota			(*cn_tab->cn_term)(cn_tab);
13910665Sbde		cn_tab = best_cp;
1404Srgrimes		return;
14110665Sbde	}
14210665Sbde
1434Srgrimes	/*
14410665Sbde	 * Initialize console, then attach to it.  This ordering allows
14510665Sbde	 * debugging using the previous console, if any.
14610665Sbde	 */
14710665Sbde	(*best_cp->cn_init)(best_cp);
14848104Syokota	if (cn_tab != NULL && cn_tab != best_cp) {
14948104Syokota		/* Turn off the previous console.  */
15048104Syokota		if (cn_tab->cn_term != NULL)
15148104Syokota			(*cn_tab->cn_term)(cn_tab);
15248104Syokota	}
15310665Sbde	cn_tab = best_cp;
15410665Sbde}
15510665Sbde
15610665Sbdevoid
15710665Sbdecninit_finish()
15810665Sbde{
15910665Sbde	struct cdevsw *cdp;
16010665Sbde
16127982Sjulian	if ((cn_tab == NULL) || cn_mute)
16210665Sbde		return;
16310665Sbde
16410665Sbde	/*
1655764Sbde	 * Hook the open and close functions.
1665764Sbde	 */
16746676Sphk	cdp = devsw(cn_tab->cn_dev);
16856582Sbde	if (cdp != NULL) {
16956582Sbde		cn_phys_close = cdp->d_close;
17056582Sbde		cdp->d_close = cnclose;
17156582Sbde		cn_phys_open = cdp->d_open;
17256582Sbde		cdp->d_open = cnopen;
17356582Sbde	}
17449049Syokota	cn_dev_t = cn_tab->cn_dev;
17549049Syokota	cn_udev_t = dev2udev(cn_dev_t);
1764Srgrimes}
1774Srgrimes
17827983Sjulianstatic void
17927983Sjuliancnuninit(void)
18027982Sjulian{
18127982Sjulian	struct cdevsw *cdp;
18227982Sjulian
18327982Sjulian	if (cn_tab == NULL)
18427982Sjulian		return;
18527982Sjulian
18627982Sjulian	/*
18727982Sjulian	 * Unhook the open and close functions.
18827982Sjulian	 */
18946676Sphk	cdp = devsw(cn_tab->cn_dev);
19056582Sbde	if (cdp != NULL) {
19156582Sbde		cdp->d_close = cn_phys_close;
19256582Sbde		cdp->d_open = cn_phys_open;
19356582Sbde	}
19427982Sjulian	cn_phys_close = NULL;
19527982Sjulian	cn_phys_open = NULL;
19649049Syokota	cn_dev_t = NODEV;
19749049Syokota	cn_udev_t = NOUDEV;
19827982Sjulian}
19927982Sjulian
20027982Sjulian/*
20127982Sjulian * User has changed the state of the console muting.
20227982Sjulian * This may require us to open or close the device in question.
20327982Sjulian */
20412675Sjulianstatic int
20562573Sphksysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
20627982Sjulian{
20727982Sjulian	int error;
20827982Sjulian	int ocn_mute;
20927982Sjulian
21027982Sjulian	ocn_mute = cn_mute;
21127982Sjulian	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
21227982Sjulian	if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) {
21327982Sjulian		if(ocn_mute && !cn_mute) {
21427982Sjulian			/*
21527982Sjulian			 * going from muted to unmuted.. open the physical dev
21627982Sjulian			 * if the console has been openned
21727982Sjulian			 */
21827982Sjulian			cninit_finish();
21927982Sjulian			if(cn_is_open)
22027982Sjulian				/* XXX curproc is not what we want really */
22127982Sjulian				error = cnopen(cn_dev_t, openflag,
22227982Sjulian					openmode, curproc);
22327982Sjulian			/* if it failed, back it out */
22427982Sjulian			if ( error != 0) cnuninit();
22527982Sjulian		} else if (!ocn_mute && cn_mute) {
22627982Sjulian			/*
22727982Sjulian			 * going from unmuted to muted.. close the physical dev
22827982Sjulian			 * if it's only open via /dev/console
22927982Sjulian			 */
23027982Sjulian			if(cn_is_open)
23127982Sjulian				error = cnclose(cn_dev_t, openflag,
23227982Sjulian					openmode, curproc);
23327982Sjulian			if ( error == 0) cnuninit();
23427982Sjulian		}
23527982Sjulian		if (error != 0) {
23627982Sjulian			/*
23727982Sjulian	 		 * back out the change if there was an error
23827982Sjulian			 */
23927982Sjulian			cn_mute = ocn_mute;
24027982Sjulian		}
24127982Sjulian	}
24227982Sjulian	return (error);
24327982Sjulian}
24427982Sjulian
24527982SjulianSYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
24627982Sjulian	0, sizeof cn_mute, sysctl_kern_consmute, "I", "");
24727982Sjulian
24827982Sjulianstatic int
2494Srgrimescnopen(dev, flag, mode, p)
2504Srgrimes	dev_t dev;
2514Srgrimes	int flag, mode;
2524Srgrimes	struct proc *p;
2534Srgrimes{
2545764Sbde	dev_t cndev, physdev;
25527982Sjulian	int retval = 0;
2561007Sdg
25756582Sbde	if (cn_tab == NULL || cn_phys_open == NULL)
2584Srgrimes		return (0);
2595764Sbde	cndev = cn_tab->cn_dev;
2605764Sbde	physdev = (major(dev) == major(cndev) ? dev : cndev);
26127982Sjulian	/*
26227982Sjulian	 * If mute is active, then non console opens don't get here
26327982Sjulian	 * so we don't need to check for that. They
26427982Sjulian	 * bypass this and go straight to the device.
26527982Sjulian	 */
26627982Sjulian	if(!cn_mute)
26727982Sjulian		retval = (*cn_phys_open)(physdev, flag, mode, p);
2685764Sbde	if (retval == 0) {
26927982Sjulian		/*
27027982Sjulian		 * check if we openned it via /dev/console or
27127982Sjulian		 * via the physical entry (e.g. /dev/sio0).
27227982Sjulian		 */
2735764Sbde		if (dev == cndev)
2745764Sbde			cn_phys_is_open = 1;
27527982Sjulian		else if (physdev == cndev) {
27627982Sjulian			openmode = mode;
27727982Sjulian			openflag = flag;
2785764Sbde			cn_is_open = 1;
27927982Sjulian		}
28051654Sphk		dev->si_tty = physdev->si_tty;
2815764Sbde	}
2825764Sbde	return (retval);
2834Srgrimes}
2848876Srgrimes
28512675Sjulianstatic int
2864Srgrimescnclose(dev, flag, mode, p)
2874Srgrimes	dev_t dev;
2884Srgrimes	int flag, mode;
2894Srgrimes	struct proc *p;
2904Srgrimes{
2915764Sbde	dev_t cndev;
29251654Sphk	struct tty *cn_tp;
2931007Sdg
29456582Sbde	if (cn_tab == NULL || cn_phys_open == NULL)
2954Srgrimes		return (0);
2965764Sbde	cndev = cn_tab->cn_dev;
29751654Sphk	cn_tp = cndev->si_tty;
29827982Sjulian	/*
29927982Sjulian	 * act appropriatly depending on whether it's /dev/console
30027982Sjulian	 * or the pysical device (e.g. /dev/sio) that's being closed.
30127982Sjulian	 * in either case, don't actually close the device unless
30227982Sjulian	 * both are closed.
30327982Sjulian	 */
3045764Sbde	if (dev == cndev) {
3057588Sjoerg		/* the physical device is about to be closed */
3065764Sbde		cn_phys_is_open = 0;
3077588Sjoerg		if (cn_is_open) {
3087588Sjoerg			if (cn_tp) {
3097588Sjoerg				/* perform a ttyhalfclose() */
3107588Sjoerg				/* reset session and proc group */
3117588Sjoerg				cn_tp->t_pgrp = NULL;
3127588Sjoerg				cn_tp->t_session = NULL;
3137588Sjoerg			}
3145764Sbde			return (0);
3157588Sjoerg		}
3165764Sbde	} else if (major(dev) != major(cndev)) {
3177588Sjoerg		/* the logical console is about to be closed */
3185764Sbde		cn_is_open = 0;
3195764Sbde		if (cn_phys_is_open)
3205764Sbde			return (0);
3215764Sbde		dev = cndev;
3225764Sbde	}
32327982Sjulian	if(cn_phys_close)
32427982Sjulian		return ((*cn_phys_close)(dev, flag, mode, p));
32527982Sjulian	return (0);
3264Srgrimes}
3278876Srgrimes
32812675Sjulianstatic int
3294Srgrimescnread(dev, uio, flag)
3304Srgrimes	dev_t dev;
3314Srgrimes	struct uio *uio;
332798Swollman	int flag;
3334Srgrimes{
33456582Sbde
33556582Sbde	if (cn_tab == NULL || cn_phys_open == NULL)
3364Srgrimes		return (0);
3374Srgrimes	dev = cn_tab->cn_dev;
33846676Sphk	return ((*devsw(dev)->d_read)(dev, uio, flag));
3394Srgrimes}
3408876Srgrimes
34112675Sjulianstatic int
3424Srgrimescnwrite(dev, uio, flag)
3434Srgrimes	dev_t dev;
3444Srgrimes	struct uio *uio;
345798Swollman	int flag;
3464Srgrimes{
34756582Sbde
34856582Sbde	if (cn_tab == NULL || cn_phys_open == NULL) {
34927982Sjulian		uio->uio_resid = 0; /* dump the data */
3504Srgrimes		return (0);
35127982Sjulian	}
3521021Sdg	if (constty)
3534Srgrimes		dev = constty->t_dev;
3544Srgrimes	else
3554Srgrimes		dev = cn_tab->cn_dev;
35646676Sphk	return ((*devsw(dev)->d_write)(dev, uio, flag));
3574Srgrimes}
3588876Srgrimes
35912675Sjulianstatic int
3604Srgrimescnioctl(dev, cmd, data, flag, p)
3614Srgrimes	dev_t dev;
36236735Sdfr	u_long cmd;
3634Srgrimes	caddr_t data;
364798Swollman	int flag;
3654Srgrimes	struct proc *p;
3664Srgrimes{
3674Srgrimes	int error;
3684Srgrimes
36956582Sbde	if (cn_tab == NULL || cn_phys_open == NULL)
3704Srgrimes		return (0);
3714Srgrimes	/*
3724Srgrimes	 * Superuser can always use this to wrest control of console
3734Srgrimes	 * output from the "virtual" console.
3744Srgrimes	 */
3754Srgrimes	if (cmd == TIOCCONS && constty) {
37646116Sphk		error = suser(p);
3774Srgrimes		if (error)
3784Srgrimes			return (error);
3794Srgrimes		constty = NULL;
3804Srgrimes		return (0);
3814Srgrimes	}
3824Srgrimes	dev = cn_tab->cn_dev;
38346676Sphk	return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, p));
3844Srgrimes}
3854Srgrimes
38612675Sjulianstatic int
38729368Spetercnpoll(dev, events, p)
3884Srgrimes	dev_t dev;
38929368Speter	int events;
3904Srgrimes	struct proc *p;
3914Srgrimes{
39218951Sjulian	if ((cn_tab == NULL) || cn_mute)
3934Srgrimes		return (1);
3946712Spst
3956712Spst	dev = cn_tab->cn_dev;
3966712Spst
39746676Sphk	return ((*devsw(dev)->d_poll)(dev, events, p));
3984Srgrimes}
3994Srgrimes
400798Swollmanint
4014Srgrimescngetc()
4024Srgrimes{
4035160Sjoerg	int c;
40418951Sjulian	if ((cn_tab == NULL) || cn_mute)
40519268Sjulian		return (-1);
4065160Sjoerg	c = (*cn_tab->cn_getc)(cn_tab->cn_dev);
4075160Sjoerg	if (c == '\r') c = '\n'; /* console input is always ICRNL */
4085160Sjoerg	return (c);
4094Srgrimes}
4104Srgrimes
4113728Sphkint
4123728Sphkcncheckc()
4133728Sphk{
41418951Sjulian	if ((cn_tab == NULL) || cn_mute)
41518287Sbde		return (-1);
4163728Sphk	return ((*cn_tab->cn_checkc)(cn_tab->cn_dev));
4173728Sphk}
4183728Sphk
419798Swollmanvoid
4204Srgrimescnputc(c)
4214Srgrimes	register int c;
4224Srgrimes{
42318951Sjulian	if ((cn_tab == NULL) || cn_mute)
4244Srgrimes		return;
4254Srgrimes	if (c) {
4264Srgrimes		if (c == '\n')
4274Srgrimes			(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
4289217Sbde		(*cn_tab->cn_putc)(cn_tab->cn_dev, c);
4294Srgrimes	}
4304Srgrimes}
4314Srgrimes
43255823Syokotavoid
43355823Syokotacndbctl(on)
43455823Syokota	int on;
43555823Syokota{
43655823Syokota	static int refcount;
43755823Syokota
43855823Syokota	if (cn_tab == NULL)
43955823Syokota		return;
44055823Syokota	if (!on)
44155823Syokota		refcount--;
44255823Syokota	if (refcount == 0 && cn_tab->cn_dbctl != NULL)
44355823Syokota		(*cn_tab->cn_dbctl)(cn_tab->cn_dev, on);
44455823Syokota	if (on)
44555823Syokota		refcount++;
44655823Syokota}
44755823Syokota
44812675Sjulianstatic void
44912675Sjuliancn_drvinit(void *unused)
45012517Sjulian{
45112517Sjulian
45256525Sbde	cn_devfsdev = make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
45356525Sbde	    "console");
45412517Sjulian}
45512517Sjulian
45612517SjulianSYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
457