Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Indirect console driver for Sun.
     29  *
     30  * Redirects all I/O to the device designated as the underlying "hardware"
     31  * console, as given by the value of rconsvp.  The implementation assumes that
     32  * rconsvp denotes a STREAMS device; the assumption is justified since
     33  * consoles must be capable of effecting tty semantics.
     34  *
     35  * rconsvp is set in autoconf.c:consconfig(), based on information obtained
     36  * from the EEPROM.
     37  *
     38  * XXX:	The driver still needs to be converted to use ANSI C consistently
     39  *	throughout.
     40  */
     41 
     42 #include <sys/types.h>
     43 #include <sys/open.h>
     44 #include <sys/param.h>
     45 #include <sys/systm.h>
     46 #include <sys/signal.h>
     47 #include <sys/cred.h>
     48 #include <sys/user.h>
     49 #include <sys/proc.h>
     50 #include <sys/disp.h>
     51 #include <sys/file.h>
     52 #include <sys/taskq.h>
     53 #include <sys/log.h>
     54 #include <sys/vnode.h>
     55 #include <sys/uio.h>
     56 #include <sys/stat.h>
     57 
     58 #include <sys/console.h>
     59 #include <sys/consdev.h>
     60 
     61 #include <sys/stream.h>
     62 #include <sys/strsubr.h>
     63 #include <sys/poll.h>
     64 
     65 #include <sys/debug.h>
     66 
     67 #include <sys/conf.h>
     68 #include <sys/ddi.h>
     69 #include <sys/sunddi.h>
     70 
     71 static int cnopen(dev_t *, int, int, struct cred *);
     72 static int cnclose(dev_t, int, int, struct cred *);
     73 static int cnread(dev_t, struct uio *, struct cred *);
     74 static int cnwrite(dev_t, struct uio *, struct cred *);
     75 static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *);
     76 static int cnpoll(dev_t, short, int, short *, struct pollhead **);
     77 static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
     78 static int cn_attach(dev_info_t *, ddi_attach_cmd_t);
     79 static int cn_detach(dev_info_t *, ddi_detach_cmd_t);
     80 
     81 static dev_info_t *cn_dip;		/* private copy of devinfo pointer */
     82 
     83 static struct cb_ops cn_cb_ops = {
     84 
     85 	cnopen,			/* open */
     86 	cnclose,		/* close */
     87 	nodev,			/* strategy */
     88 	nodev,			/* print */
     89 	nodev,			/* dump */
     90 	cnread,			/* read */
     91 	cnwrite,		/* write */
     92 	cnioctl,		/* ioctl */
     93 	nodev,			/* devmap */
     94 	nodev,			/* mmap */
     95 	nodev, 			/* segmap */
     96 	cnpoll,			/* poll */
     97 	ddi_prop_op,		/* cb_prop_op */
     98 	0,			/* streamtab  */
     99 	D_NEW | D_MP		/* Driver compatibility flag */
    100 
    101 };
    102 
    103 static struct dev_ops cn_ops = {
    104 
    105 	DEVO_REV,		/* devo_rev, */
    106 	0,			/* refcnt  */
    107 	cn_info,		/* info */
    108 	nulldev,		/* identify */
    109 	nulldev,		/* probe */
    110 	cn_attach,		/* attach */
    111 	cn_detach,		/* detach */
    112 	nodev,			/* reset */
    113 	&cn_cb_ops,		/* driver operations */
    114 	(struct bus_ops *)0,	/* bus operations */
    115 	NULL,			/* power */
    116 	ddi_quiesce_not_needed,		/* quiesce */
    117 
    118 };
    119 
    120 /*
    121  * Global variables associated with the console device:
    122  *
    123  * XXX:	There are too many of these!
    124  * moved to space.c to become resident in the kernel so that cons
    125  * can be loadable.
    126  */
    127 
    128 extern dev_t	rconsdev;	/* "hardware" console */
    129 extern vnode_t	*rconsvp;	/* pointer to vnode for that device */
    130 
    131 /*
    132  * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
    133  */
    134 extern dev_t	uconsdev;	/* What the user thinks is the console device */
    135 
    136 /*
    137  * Private driver state:
    138  */
    139 
    140 /*
    141  * The underlying console device potentially can be opened through (at least)
    142  * two paths: through this driver and through the underlying device's driver.
    143  * To ensure that reference counts are meaningful and therefore that close
    144  * routines are called at the right time, it's important to make sure that
    145  * rconsvp's s_count field (i.e., the count on the underlying device) never
    146  * has a contribution of more than one through this driver, regardless of how
    147  * many times this driver's been opened.  rconsopen keeps track of the
    148  * necessary information to ensure this property.
    149  */
    150 static uint_t	rconsopen;
    151 
    152 
    153 #include <sys/types.h>
    154 #include <sys/conf.h>
    155 #include <sys/param.h>
    156 #include <sys/systm.h>
    157 #include <sys/errno.h>
    158 #include <sys/modctl.h>
    159 
    160 
    161 extern int nodev(), nulldev();
    162 extern int dseekneg_flag;
    163 extern struct mod_ops mod_driverops;
    164 extern struct dev_ops cn_ops;
    165 
    166 /*
    167  * Module linkage information for the kernel.
    168  */
    169 
    170 static struct modldrv modldrv = {
    171 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
    172 	"Console redirection driver",
    173 	&cn_ops,	/* driver ops */
    174 };
    175 
    176 static struct modlinkage modlinkage = {
    177 	MODREV_1,
    178 	&modldrv,
    179 	NULL
    180 };
    181 
    182 int
    183 _init(void)
    184 {
    185 	return (mod_install(&modlinkage));
    186 }
    187 
    188 int
    189 _fini(void)
    190 {
    191 	return (EBUSY);
    192 }
    193 
    194 int
    195 _info(struct modinfo *modinfop)
    196 {
    197 	return (mod_info(&modlinkage, modinfop));
    198 }
    199 
    200 /*
    201  * DDI glue routines
    202  */
    203 static int
    204 cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    205 {
    206 	if (cmd != DDI_ATTACH)
    207 		return (DDI_FAILURE);
    208 
    209 	if (ddi_create_minor_node(devi, "syscon", S_IFCHR,
    210 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
    211 		return (DDI_FAILURE);
    212 	}
    213 	if (ddi_create_minor_node(devi, "systty", S_IFCHR,
    214 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
    215 		ddi_remove_minor_node(devi, NULL);
    216 		return (DDI_FAILURE);
    217 	}
    218 	if (ddi_create_minor_node(devi, "console", S_IFCHR,
    219 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
    220 		ddi_remove_minor_node(devi, NULL);
    221 		return (DDI_FAILURE);
    222 	}
    223 
    224 	cn_dip = devi;
    225 	return (DDI_SUCCESS);
    226 }
    227 
    228 static int
    229 cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    230 {
    231 	if (cmd != DDI_DETACH)
    232 		return (DDI_FAILURE);
    233 	ddi_remove_minor_node(devi, NULL);
    234 	uconsdev = NODEV;
    235 	return (DDI_SUCCESS);
    236 }
    237 
    238 /* ARGSUSED */
    239 static int
    240 cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    241 {
    242 	int error = DDI_FAILURE;
    243 
    244 	switch (infocmd) {
    245 	case DDI_INFO_DEVT2DEVINFO:
    246 		if (getminor((dev_t)arg) == 0 && cn_dip != NULL) {
    247 			*result = (void *) cn_dip;
    248 			error = DDI_SUCCESS;
    249 		}
    250 		break;
    251 
    252 	case DDI_INFO_DEVT2INSTANCE:
    253 		if (getminor((dev_t)arg) == 0) {
    254 			*result = (void *)0;
    255 			error = DDI_SUCCESS;
    256 		}
    257 		break;
    258 
    259 	default:
    260 		break;
    261 	}
    262 
    263 	return (error);
    264 }
    265 
    266 /*
    267  * XXX	Caution: before allowing more than 256 minor devices on the
    268  *	console, make sure you understand the 'compatibility' hack
    269  *	in ufs_iget() that translates old dev_t's to new dev_t's.
    270  *	See bugid 1098104 for the sordid details.
    271  */
    272 
    273 /* ARGSUSED */
    274 static int
    275 cnopen(dev_t *dev, int flag, int state, struct cred *cred)
    276 {
    277 	int	err;
    278 	static int	been_here;
    279 	vnode_t	*vp = rconsvp;
    280 
    281 	ASSERT(cred != NULL);
    282 
    283 	if (rconsvp == NULL)
    284 		return (0);
    285 
    286 	/*
    287 	 * Enable virtual console I/O for console logging if needed.
    288 	 */
    289 	if (vsconsvp != NULL && vsconsvp->v_stream == NULL) {
    290 		if (VOP_OPEN(&vsconsvp, FREAD | FWRITE, cred, NULL) != 0) {
    291 			cmn_err(CE_WARN, "cnopen: failed to open vsconsvp "
    292 			    "for virtual console logging");
    293 		}
    294 	}
    295 
    296 	/*
    297 	 * XXX: Clean up inactive PIDs from previous opens if any.
    298 	 * These would have been created as a result of an I_SETSIG
    299 	 * issued against console.  This is a workaround, and
    300 	 * console driver must be correctly redesigned not to need
    301 	 * this hook.
    302 	 */
    303 	if (vp->v_stream) {
    304 		str_cn_clean(vp);
    305 	}
    306 
    307 	/*
    308 	 * XXX:	Set hook to tell /proc about underlying console.  (There's
    309 	 *	gotta be a better way...)
    310 	 */
    311 	if (state != OTYP_CHR || getminor(*dev) != 0)
    312 		return (ENXIO);
    313 	if (been_here == 0) {
    314 		uconsdev = *dev;
    315 		been_here = 1;
    316 		if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
    317 		    0, &console_vnode, 0, 0) == 0)
    318 			console_taskq = taskq_create("console_taskq",
    319 			    1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
    320 			    LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
    321 	}
    322 
    323 	if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
    324 		return (err);
    325 
    326 	/*
    327 	 * The underlying driver is not allowed to have cloned itself
    328 	 * for this open.
    329 	 */
    330 	if (vp != rconsvp) {
    331 		/*
    332 		 * It might happen that someone set rconsvp to NULL
    333 		 * whilst we were in the middle of the open.
    334 		 */
    335 		if (rconsvp == NULL) {
    336 			(void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
    337 			return (0);
    338 		}
    339 		cmn_err(CE_PANIC, "cnopen: cloned open");
    340 	}
    341 
    342 	rconsopen++;
    343 
    344 	return (0);
    345 }
    346 
    347 /* ARGSUSED */
    348 static int
    349 cnclose(dev_t dev, int flag, int state, struct cred *cred)
    350 {
    351 	int	err = 0;
    352 	vnode_t	*vp;
    353 
    354 	/*
    355 	 * Since this is the _last_ close, it's our last chance to close the
    356 	 * underlying device.  (Note that if someone else has the underlying
    357 	 * hardware console device open, we won't get here, since spec_close
    358 	 * will see s_count > 1.)
    359 	 */
    360 	if (state != OTYP_CHR)
    361 		return (ENXIO);
    362 
    363 	if (rconsvp == NULL)
    364 		return (0);
    365 
    366 	while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
    367 		err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
    368 		if (!err) {
    369 			rconsopen--;
    370 		}
    371 	}
    372 	return (err);
    373 }
    374 
    375 /* ARGSUSED */
    376 static int
    377 cnread(dev_t dev, struct uio *uio, struct cred *cred)
    378 {
    379 	kcondvar_t	sleep_forever;
    380 	kmutex_t	sleep_forever_mutex;
    381 
    382 	if (rconsvp == NULL) {
    383 		/*
    384 		 * Go to sleep forever.  This seems like the least
    385 		 * harmful thing to do if there's no console.
    386 		 * EOF might be better if we're ending up single-user
    387 		 * mode.
    388 		 */
    389 		cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
    390 		mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
    391 		mutex_enter(&sleep_forever_mutex);
    392 		(void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
    393 		mutex_exit(&sleep_forever_mutex);
    394 		return (EIO);
    395 	}
    396 
    397 	if (rconsvp->v_stream != NULL)
    398 		return (strread(rconsvp, uio, cred));
    399 	else
    400 		return (cdev_read(rconsdev, uio, cred));
    401 }
    402 
    403 /* ARGSUSED */
    404 static int
    405 cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
    406 {
    407 	if (rconsvp == NULL) {
    408 		uio->uio_resid = 0;
    409 		return (0);
    410 	}
    411 
    412 	/*
    413 	 * Output to virtual console for logging if enabled.
    414 	 */
    415 	if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
    416 		struiod_t uiod;
    417 
    418 		/*
    419 		 * strwrite modifies uio so need to make copy.
    420 		 */
    421 		(void) uiodup(uio, &uiod.d_uio, uiod.d_iov,
    422 		    sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
    423 
    424 		(void) strwrite(vsconsvp, &uiod.d_uio, cred);
    425 	}
    426 
    427 	if (rconsvp->v_stream != NULL)
    428 		return (strwrite(rconsvp, uio, cred));
    429 	else
    430 		return (cdev_write(rconsdev, uio, cred));
    431 }
    432 
    433 /* ARGSUSED */
    434 static int
    435 cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
    436 	int *rvalp)
    437 {
    438 
    439 	/* currently we only support one ioctl */
    440 	if (cmd != CONS_GETTERM)
    441 		return (EINVAL);
    442 
    443 	/* Confirm iwscn is immediate target of cn redirection */
    444 	if (rconsvp != wsconsvp)
    445 		return (ENODEV);
    446 
    447 	/*
    448 	 * If the redirection client is not wc, it should return
    449 	 * error upon receiving the CONS_GETTERM ioctl.
    450 	 *
    451 	 * if it is wc, we know that the target supports the CONS_GETTERM
    452 	 * ioctl, which very conviently has the exact same data
    453 	 * format as this ioctl...  so let's just pass it on.
    454 	 */
    455 	return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
    456 }
    457 
    458 /* ARGSUSED */
    459 static int
    460 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
    461 	int *rvalp)
    462 {
    463 	if (rconsvp == NULL)
    464 		return (0);
    465 
    466 	if ((cmd & _CNIOC_MASK) == _CNIOC)
    467 		return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
    468 	else if (rconsvp->v_stream != NULL)
    469 		return (strioctl(rconsvp, cmd, arg, flag, U_TO_K, cred,
    470 		    rvalp));
    471 	else
    472 		return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
    473 }
    474 
    475 /* ARGSUSED */
    476 static int
    477 cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
    478 	struct pollhead **phpp)
    479 {
    480 	if (rconsvp == NULL)
    481 		return (nochpoll(dev, events, anyyet, reventsp, phpp));
    482 
    483 	if (rconsvp->v_stream != NULL)
    484 		return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
    485 		    phpp));
    486 	else
    487 		return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
    488 }
    489