xref: /illumos-gate/usr/src/uts/common/io/cons.c (revision 3cac7b0d73edf3f2674ad0f64d1fff3d2e59ae8c)
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 (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2015, Joyent, Inc. All rights reserved.
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 #include <sys/limits.h>
58 
59 #include <sys/console.h>
60 #include <sys/consdev.h>
61 
62 #include <sys/stream.h>
63 #include <sys/strsubr.h>
64 #include <sys/poll.h>
65 
66 #include <sys/debug.h>
67 
68 #include <sys/conf.h>
69 #include <sys/ddi.h>
70 #include <sys/sunddi.h>
71 #include <sys/vt.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 	NULL,			/* power */
118 	ddi_quiesce_not_needed,		/* quiesce */
119 
120 };
121 
122 /*
123  * Global variables associated with the console device:
124  *
125  * XXX:	There are too many of these!
126  * moved to space.c to become resident in the kernel so that cons
127  * can be loadable.
128  */
129 
130 extern dev_t	rconsdev;	/* "hardware" console */
131 extern vnode_t	*rconsvp;	/* pointer to vnode for that device */
132 
133 /*
134  * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
135  */
136 extern dev_t	uconsdev;	/* What the user thinks is the console device */
137 
138 /*
139  * Private driver state:
140  */
141 
142 /*
143  * The underlying console device potentially can be opened through (at least)
144  * two paths: through this driver and through the underlying device's driver.
145  * To ensure that reference counts are meaningful and therefore that close
146  * routines are called at the right time, it's important to make sure that
147  * rconsvp's s_count field (i.e., the count on the underlying device) never
148  * has a contribution of more than one through this driver, regardless of how
149  * many times this driver's been opened.  rconsopen keeps track of the
150  * necessary information to ensure this property.
151  */
152 static uint_t	rconsopen;
153 
154 
155 #include <sys/types.h>
156 #include <sys/conf.h>
157 #include <sys/param.h>
158 #include <sys/systm.h>
159 #include <sys/errno.h>
160 #include <sys/modctl.h>
161 
162 
163 extern int nodev(), nulldev();
164 extern int dseekneg_flag;
165 extern struct mod_ops mod_driverops;
166 extern struct dev_ops cn_ops;
167 
168 /*
169  * Module linkage information for the kernel.
170  */
171 
172 static struct modldrv modldrv = {
173 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
174 	"Console redirection driver",
175 	&cn_ops,	/* driver ops */
176 };
177 
178 static struct modlinkage modlinkage = {
179 	MODREV_1,
180 	&modldrv,
181 	NULL
182 };
183 
184 int
185 _init(void)
186 {
187 	return (mod_install(&modlinkage));
188 }
189 
190 int
191 _fini(void)
192 {
193 	return (EBUSY);
194 }
195 
196 int
197 _info(struct modinfo *modinfop)
198 {
199 	return (mod_info(&modlinkage, modinfop));
200 }
201 
202 /*
203  * DDI glue routines
204  */
205 static int
206 cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
207 {
208 	if (cmd != DDI_ATTACH)
209 		return (DDI_FAILURE);
210 
211 	if (ddi_create_minor_node(devi, "syscon", S_IFCHR,
212 	    0, DDI_PSEUDO, 0) == DDI_FAILURE) {
213 		return (DDI_FAILURE);
214 	}
215 	if (ddi_create_minor_node(devi, "systty", S_IFCHR,
216 	    0, DDI_PSEUDO, 0) == DDI_FAILURE) {
217 		ddi_remove_minor_node(devi, NULL);
218 		return (DDI_FAILURE);
219 	}
220 	if (ddi_create_minor_node(devi, "console", S_IFCHR,
221 	    0, DDI_PSEUDO, 0) == DDI_FAILURE) {
222 		ddi_remove_minor_node(devi, NULL);
223 		return (DDI_FAILURE);
224 	}
225 
226 	cn_dip = devi;
227 	return (DDI_SUCCESS);
228 }
229 
230 static int
231 cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
232 {
233 	if (cmd != DDI_DETACH)
234 		return (DDI_FAILURE);
235 	ddi_remove_minor_node(devi, NULL);
236 	uconsdev = NODEV;
237 	return (DDI_SUCCESS);
238 }
239 
240 /* ARGSUSED */
241 static int
242 cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
243 {
244 	int error = DDI_FAILURE;
245 
246 	switch (infocmd) {
247 	case DDI_INFO_DEVT2DEVINFO:
248 		if (getminor((dev_t)arg) == 0 && cn_dip != NULL) {
249 			*result = (void *) cn_dip;
250 			error = DDI_SUCCESS;
251 		}
252 		break;
253 
254 	case DDI_INFO_DEVT2INSTANCE:
255 		if (getminor((dev_t)arg) == 0) {
256 			*result = (void *)0;
257 			error = DDI_SUCCESS;
258 		}
259 		break;
260 
261 	default:
262 		break;
263 	}
264 
265 	return (error);
266 }
267 
268 /*
269  * XXX	Caution: before allowing more than 256 minor devices on the
270  *	console, make sure you understand the 'compatibility' hack
271  *	in ufs_iget() that translates old dev_t's to new dev_t's.
272  *	See bugid 1098104 for the sordid details.
273  */
274 
275 /* ARGSUSED */
276 static int
277 cnopen(dev_t *dev, int flag, int state, struct cred *cred)
278 {
279 	int	err;
280 	static int	been_here;
281 	vnode_t	*vp = rconsvp;
282 
283 	ASSERT(cred != NULL);
284 
285 	if (rconsvp == NULL)
286 		return (0);
287 
288 	/*
289 	 * Enable virtual console I/O for console logging if needed.
290 	 */
291 	if (vsconsvp != NULL && vsconsvp->v_stream == NULL) {
292 		if (VOP_OPEN(&vsconsvp, FREAD | FWRITE, cred, NULL) != 0) {
293 			cmn_err(CE_WARN, "cnopen: failed to open vsconsvp "
294 			    "for virtual console logging");
295 		}
296 	}
297 
298 	/*
299 	 * XXX: Clean up inactive PIDs from previous opens if any.
300 	 * These would have been created as a result of an I_SETSIG
301 	 * issued against console.  This is a workaround, and
302 	 * console driver must be correctly redesigned not to need
303 	 * this hook.
304 	 */
305 	if (vp->v_stream) {
306 		str_cn_clean(vp);
307 	}
308 
309 	/*
310 	 * XXX:	Set hook to tell /proc about underlying console.  (There's
311 	 *	gotta be a better way...)
312 	 */
313 	if (state != OTYP_CHR || getminor(*dev) != 0)
314 		return (ENXIO);
315 	if (been_here == 0) {
316 		uconsdev = *dev;
317 		been_here = 1;
318 		if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
319 		    0, &console_vnode, 0, 0) == 0)
320 			console_taskq = taskq_create("console_taskq",
321 			    1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
322 			    LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
323 	}
324 
325 	if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
326 		return (err);
327 
328 	/*
329 	 * The underlying driver is not allowed to have cloned itself
330 	 * for this open.
331 	 */
332 	if (vp != rconsvp) {
333 		/*
334 		 * It might happen that someone set rconsvp to NULL
335 		 * whilst we were in the middle of the open.
336 		 */
337 		if (rconsvp == NULL) {
338 			(void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
339 			return (0);
340 		}
341 		cmn_err(CE_PANIC, "cnopen: cloned open");
342 	}
343 
344 	rconsopen++;
345 
346 	return (0);
347 }
348 
349 /* ARGSUSED */
350 static int
351 cnclose(dev_t dev, int flag, int state, struct cred *cred)
352 {
353 	int	err = 0;
354 	vnode_t	*vp;
355 
356 	/*
357 	 * Since this is the _last_ close, it's our last chance to close the
358 	 * underlying device.  (Note that if someone else has the underlying
359 	 * hardware console device open, we won't get here, since spec_close
360 	 * will see s_count > 1.)
361 	 */
362 	if (state != OTYP_CHR)
363 		return (ENXIO);
364 
365 	if (rconsvp == NULL)
366 		return (0);
367 
368 	while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
369 		err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
370 		if (!err) {
371 			rconsopen--;
372 		}
373 	}
374 	return (err);
375 }
376 
377 /* ARGSUSED */
378 static int
379 cnread(dev_t dev, struct uio *uio, struct cred *cred)
380 {
381 	kcondvar_t	sleep_forever;
382 	kmutex_t	sleep_forever_mutex;
383 
384 	if (rconsvp == NULL) {
385 		/*
386 		 * Go to sleep forever.  This seems like the least
387 		 * harmful thing to do if there's no console.
388 		 * EOF might be better if we're ending up single-user
389 		 * mode.
390 		 */
391 		cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
392 		mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
393 		mutex_enter(&sleep_forever_mutex);
394 		(void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
395 		mutex_exit(&sleep_forever_mutex);
396 		return (EIO);
397 	}
398 
399 	if (rconsvp->v_stream != NULL)
400 		return (strread(rconsvp, uio, cred));
401 	else
402 		return (cdev_read(rconsdev, uio, cred));
403 }
404 
405 /* ARGSUSED */
406 static int
407 cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
408 {
409 	if (rconsvp == NULL) {
410 		uio->uio_resid = 0;
411 		return (0);
412 	}
413 
414 	/*
415 	 * Output to virtual console for logging if enabled.
416 	 */
417 	if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
418 		struiod_t uiod;
419 		struct iovec buf[IOV_MAX_STACK];
420 		int iovlen = 0;
421 
422 		if (uio->uio_iovcnt > IOV_MAX_STACK) {
423 			iovlen = uio->uio_iovcnt * sizeof (iovec_t);
424 			uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
425 		} else {
426 			uiod.d_iov = buf;
427 		}
428 
429 		/*
430 		 * strwrite modifies uio so need to make copy.
431 		 */
432 		(void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt);
433 
434 		(void) strwrite(vsconsvp, &uiod.d_uio, cred);
435 		if (iovlen != 0)
436 			kmem_free(uiod.d_iov, iovlen);
437 	}
438 
439 	if (rconsvp->v_stream != NULL)
440 		return (strwrite(rconsvp, uio, cred));
441 	else
442 		return (cdev_write(rconsdev, uio, cred));
443 }
444 
445 /* ARGSUSED */
446 static int
447 cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
448 	int *rvalp)
449 {
450 
451 	/* currently we only support one ioctl */
452 	if (cmd != CONS_GETTERM)
453 		return (EINVAL);
454 
455 	/* Confirm iwscn is immediate target of cn redirection */
456 	if (rconsvp != wsconsvp)
457 		return (ENODEV);
458 
459 	/*
460 	 * If the redirection client is not wc, it should return
461 	 * error upon receiving the CONS_GETTERM ioctl.
462 	 *
463 	 * if it is wc, we know that the target supports the CONS_GETTERM
464 	 * ioctl, which very conviently has the exact same data
465 	 * format as this ioctl...  so let's just pass it on.
466 	 */
467 	return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
468 }
469 
470 /* ARGSUSED */
471 static int
472 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
473 	int *rvalp)
474 {
475 	if (rconsvp == NULL)
476 		return (0);
477 
478 	/*
479 	 * In wc, VT_SET_CONSUSER which comes from minor node 0
480 	 * has two sources -- either /dev/console or /dev/vt/0 .
481 	 * We need a way to differentiate them, so here we
482 	 * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
483 	 * ioctl.
484 	 */
485 	if (cmd == VT_SET_CONSUSER)
486 		cmd = VT_RESET_CONSUSER;
487 
488 	if ((cmd & _CNIOC_MASK) == _CNIOC)
489 		return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
490 
491 	if (rconsvp->v_stream != NULL)
492 		return (strioctl(rconsvp, cmd, arg, flag, U_TO_K,
493 		    cred, rvalp));
494 
495 	return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
496 }
497 
498 /* ARGSUSED */
499 static int
500 cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
501 	struct pollhead **phpp)
502 {
503 	if (rconsvp == NULL)
504 		return (nochpoll(dev, events, anyyet, reventsp, phpp));
505 
506 	if (rconsvp->v_stream != NULL)
507 		return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
508 		    phpp));
509 	else
510 		return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
511 }
512