xref: /illumos-gate/usr/src/uts/common/io/cons.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Indirect console driver for Sun.
31  *
32  * Redirects all I/O to the device designated as the underlying "hardware"
33  * console, as given by the value of rconsvp.  The implementation assumes that
34  * rconsvp denotes a STREAMS device; the assumption is justified since
35  * consoles must be capable of effecting tty semantics.
36  *
37  * rconsvp is set in autoconf.c:consconfig(), based on information obtained
38  * from the EEPROM.
39  *
40  * XXX:	The driver still needs to be converted to use ANSI C consistently
41  *	throughout.
42  */
43 
44 #include <sys/types.h>
45 #include <sys/open.h>
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/signal.h>
49 #include <sys/cred.h>
50 #include <sys/user.h>
51 #include <sys/proc.h>
52 #include <sys/disp.h>
53 #include <sys/file.h>
54 #include <sys/taskq.h>
55 #include <sys/log.h>
56 #include <sys/vnode.h>
57 #include <sys/uio.h>
58 #include <sys/stat.h>
59 
60 #include <sys/console.h>
61 #include <sys/consdev.h>
62 
63 #include <sys/stream.h>
64 #include <sys/strsubr.h>
65 #include <sys/poll.h>
66 
67 #include <sys/debug.h>
68 
69 #include <sys/conf.h>
70 #include <sys/ddi.h>
71 #include <sys/sunddi.h>
72 
73 static int cnopen(dev_t *, int, int, struct cred *);
74 static int cnclose(dev_t, int, int, struct cred *);
75 static int cnread(dev_t, struct uio *, struct cred *);
76 static int cnwrite(dev_t, struct uio *, struct cred *);
77 static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *);
78 static int cnpoll(dev_t, short, int, short *, struct pollhead **);
79 static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
80 static int cn_attach(dev_info_t *, ddi_attach_cmd_t);
81 static int cn_detach(dev_info_t *, ddi_detach_cmd_t);
82 
83 static dev_info_t *cn_dip;		/* private copy of devinfo pointer */
84 
85 static struct cb_ops cn_cb_ops = {
86 
87 	cnopen,			/* open */
88 	cnclose,		/* close */
89 	nodev,			/* strategy */
90 	nodev,			/* print */
91 	nodev,			/* dump */
92 	cnread,			/* read */
93 	cnwrite,		/* write */
94 	cnioctl,		/* ioctl */
95 	nodev,			/* devmap */
96 	nodev,			/* mmap */
97 	nodev, 			/* segmap */
98 	cnpoll,			/* poll */
99 	ddi_prop_op,		/* cb_prop_op */
100 	0,			/* streamtab  */
101 	D_NEW | D_MP		/* Driver compatibility flag */
102 
103 };
104 
105 static struct dev_ops cn_ops = {
106 
107 	DEVO_REV,		/* devo_rev, */
108 	0,			/* refcnt  */
109 	cn_info,		/* info */
110 	nulldev,		/* identify */
111 	nulldev,		/* probe */
112 	cn_attach,		/* attach */
113 	cn_detach,		/* detach */
114 	nodev,			/* reset */
115 	&cn_cb_ops,		/* driver operations */
116 	(struct bus_ops *)0	/* bus operations */
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 becone 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 %I%",
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 	cn_dip = devi;
224 	return (DDI_SUCCESS);
225 }
226 
227 static int
228 cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
229 {
230 	if (cmd != DDI_DETACH)
231 		return (DDI_FAILURE);
232 	ddi_remove_minor_node(devi, NULL);
233 	uconsdev = NODEV;
234 	return (DDI_SUCCESS);
235 }
236 
237 /* ARGSUSED */
238 static int
239 cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
240 {
241 	int error = DDI_FAILURE;
242 
243 	switch (infocmd) {
244 	case DDI_INFO_DEVT2DEVINFO:
245 		if (getminor((dev_t)arg) == 0 && cn_dip != NULL) {
246 			*result = (void *) cn_dip;
247 			error = DDI_SUCCESS;
248 		}
249 		break;
250 
251 	case DDI_INFO_DEVT2INSTANCE:
252 		if (getminor((dev_t)arg) == 0) {
253 			*result = (void *)0;
254 			error = DDI_SUCCESS;
255 		}
256 		break;
257 
258 	default:
259 		break;
260 	}
261 
262 	return (error);
263 }
264 
265 /*
266  * XXX	Caution: before allowing more than 256 minor devices on the
267  *	console, make sure you understand the 'compatibility' hack
268  *	in ufs_iget() that translates old dev_t's to new dev_t's.
269  *	See bugid 1098104 for the sordid details.
270  */
271 
272 /* ARGSUSED */
273 static int
274 cnopen(dev_t *dev, int flag, int state, struct cred *cred)
275 {
276 	int	err;
277 	static int	been_here;
278 	vnode_t	*vp = rconsvp;
279 
280 	ASSERT(cred != NULL);
281 
282 	if (rconsvp == NULL)
283 		return (0);
284 
285 
286 	/*
287 	 * XXX: Clean up inactive PIDs from previous opens if any.
288 	 * These would have been created as a result of an I_SETSIG
289 	 * issued against console.  This is a workaround, and
290 	 * console driver must be correctly redesigned not to need
291 	 * this hook.
292 	 */
293 	if (vp->v_stream) {
294 		str_cn_clean(vp);
295 	}
296 
297 	/*
298 	 * XXX:	Set hook to tell /proc about underlying console.  (There's
299 	 *	gotta be a better way...)
300 	 */
301 	if (state != OTYP_CHR || getminor(*dev) != 0)
302 		return (ENXIO);
303 	if (been_here == 0) {
304 		uconsdev = *dev;
305 		been_here = 1;
306 		if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
307 		    0, &console_vnode, 0, 0) == 0)
308 			console_taskq = taskq_create("console_taskq",
309 			    1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
310 			    LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
311 	}
312 
313 	if ((err = VOP_OPEN(&vp, flag, cred)) != 0)
314 		return (err);
315 
316 	/*
317 	 * The underlying driver is not allowed to have cloned itself
318 	 * for this open.
319 	 */
320 	if (vp != rconsvp) {
321 		/*
322 		 * It might happen that someone set rconsvp to NULL
323 		 * whilst we were in the middle of the open.
324 		 */
325 		if (rconsvp == NULL) {
326 			(void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred);
327 			return (0);
328 		}
329 		cmn_err(CE_PANIC, "cnopen: cloned open");
330 	}
331 
332 	rconsopen++;
333 
334 	return (0);
335 }
336 
337 /* ARGSUSED */
338 static int
339 cnclose(dev_t dev, int flag, int state, struct cred *cred)
340 {
341 	int	err = 0;
342 	vnode_t	*vp;
343 
344 	/*
345 	 * Since this is the _last_ close, it's our last chance to close the
346 	 * underlying device.  (Note that if someone else has the underlying
347 	 * hardware console device open, we won't get here, since spec_close
348 	 * will see s_count > 1.)
349 	 */
350 	if (state != OTYP_CHR)
351 		return (ENXIO);
352 
353 	if (rconsvp == NULL)
354 		return (0);
355 
356 	while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
357 		err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred);
358 		if (!err) {
359 			vp->v_stream = NULL;
360 			rconsopen--;
361 		}
362 	}
363 	return (err);
364 }
365 
366 /* ARGSUSED */
367 static int
368 cnread(dev_t dev, struct uio *uio, struct cred *cred)
369 {
370 	kcondvar_t	sleep_forever;
371 	kmutex_t	sleep_forever_mutex;
372 
373 	if (rconsvp == NULL) {
374 		/*
375 		 * Go to sleep forever.  This seems like the least
376 		 * harmful thing to do if there's no console.
377 		 * EOF might be better if we're ending up single-user
378 		 * mode.
379 		 */
380 		cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
381 		mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
382 		mutex_enter(&sleep_forever_mutex);
383 		(void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
384 		mutex_exit(&sleep_forever_mutex);
385 		return (EIO);
386 	}
387 
388 	if (rconsvp->v_stream != NULL)
389 		return (strread(rconsvp, uio, cred));
390 	else
391 		return (cdev_read(rconsdev, uio, cred));
392 }
393 
394 /* ARGSUSED */
395 static int
396 cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
397 {
398 	if (rconsvp == NULL) {
399 		uio->uio_resid = 0;
400 		return (0);
401 	}
402 
403 	if (rconsvp->v_stream != NULL)
404 		return (strwrite(rconsvp, uio, cred));
405 	else
406 		return (cdev_write(rconsdev, uio, cred));
407 }
408 
409 /* ARGSUSED */
410 static int
411 cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
412 	int *rvalp)
413 {
414 
415 	/* currently we only support one ioctl */
416 	if (cmd != CONS_GETTERM)
417 		return (EINVAL);
418 
419 	/* Confirm iwscn is immediate target of cn redirection */
420 	if (rconsvp != wsconsvp)
421 		return (ENODEV);
422 
423 	/*
424 	 * If the redirection client is not wc, it should return
425 	 * error upon receiving the CONS_GETTERM ioctl.
426 	 *
427 	 * if it is wc, we know that the target supports the CONS_GETTERM
428 	 * ioctl, which very conviently has the exact same data
429 	 * format as this ioctl...  so let's just pass it on.
430 	 */
431 	return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
432 }
433 
434 /* ARGSUSED */
435 static int
436 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
437 	int *rvalp)
438 {
439 	if (rconsvp == NULL)
440 		return (0);
441 
442 	if ((cmd & _CNIOC_MASK) == _CNIOC)
443 		return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
444 	else if (rconsvp->v_stream != NULL)
445 		return (strioctl(rconsvp, cmd, arg, flag, U_TO_K, cred,
446 		    rvalp));
447 	else
448 		return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
449 }
450 
451 /* ARGSUSED */
452 static int
453 cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
454 	struct pollhead **phpp)
455 {
456 	if (rconsvp == NULL)
457 		return (nochpoll(dev, events, anyyet, reventsp, phpp));
458 
459 	if (rconsvp->v_stream != NULL)
460 		return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
461 		    phpp));
462 	else
463 		return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
464 }
465