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.
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.
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]
23 * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015, Joyent, Inc. All rights reserved.
28 * Indirect console driver for Sun.
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.
35 * rconsvp is set in autoconf.c:consconfig(), based on information obtained
38 * XXX: The driver still needs to be converted to use ANSI C consistently
42 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/signal.h>
52 #include <sys/taskq.h>
54 #include <sys/vnode.h>
57 #include <sys/limits.h>
59 #include <sys/console.h>
60 #include <sys/consdev.h>
62 #include <sys/stream.h>
63 #include <sys/strsubr.h>
66 #include <sys/debug.h>
70 #include <sys/sunddi.h>
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
);
83 static dev_info_t
*cn_dip
; /* private copy of devinfo pointer */
85 static struct cb_ops cn_cb_ops
= {
99 ddi_prop_op
, /* cb_prop_op */
101 D_NEW
| D_MP
/* Driver compatibility flag */
105 static struct dev_ops cn_ops
= {
107 DEVO_REV
, /* devo_rev, */
110 nulldev
, /* identify */
112 cn_attach
, /* attach */
113 cn_detach
, /* detach */
115 &cn_cb_ops
, /* driver operations */
116 NULL
, /* bus operations */
118 ddi_quiesce_not_needed
, /* quiesce */
123 * Global variables associated with the console device:
125 * XXX: There are too many of these!
126 * moved to space.c to become resident in the kernel so that cons
130 extern dev_t rconsdev
; /* "hardware" console */
131 extern vnode_t
*rconsvp
; /* pointer to vnode for that device */
134 * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
136 extern dev_t uconsdev
; /* What the user thinks is the console device */
139 * Private driver state:
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.
152 static uint_t rconsopen
;
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>
163 extern int nodev(), nulldev();
164 extern int dseekneg_flag
;
165 extern struct mod_ops mod_driverops
;
166 extern struct dev_ops cn_ops
;
169 * Module linkage information for the kernel.
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 */
178 static struct modlinkage modlinkage
= {
187 return (mod_install(&modlinkage
));
197 _info(struct modinfo
*modinfop
)
199 return (mod_info(&modlinkage
, modinfop
));
206 cn_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
208 if (cmd
!= DDI_ATTACH
)
209 return (DDI_FAILURE
);
211 if (ddi_create_minor_node(devi
, "syscon", S_IFCHR
,
212 0, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
213 return (DDI_FAILURE
);
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
);
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
);
227 return (DDI_SUCCESS
);
231 cn_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
233 if (cmd
!= DDI_DETACH
)
234 return (DDI_FAILURE
);
235 ddi_remove_minor_node(devi
, NULL
);
237 return (DDI_SUCCESS
);
242 cn_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
244 int error
= DDI_FAILURE
;
247 case DDI_INFO_DEVT2DEVINFO
:
248 if (getminor((dev_t
)arg
) == 0 && cn_dip
!= NULL
) {
249 *result
= (void *) cn_dip
;
254 case DDI_INFO_DEVT2INSTANCE
:
255 if (getminor((dev_t
)arg
) == 0) {
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.
277 cnopen(dev_t
*dev
, int flag
, int state
, struct cred
*cred
)
280 static int been_here
;
281 vnode_t
*vp
= rconsvp
;
283 ASSERT(cred
!= NULL
);
289 * Enable virtual console I/O for console logging if needed.
291 if (vsconsvp
!= NULL
&& vsconsvp
->v_stream
== NULL
) {
292 if (fop_open(&vsconsvp
, FREAD
| FWRITE
, cred
, NULL
) != 0) {
293 cmn_err(CE_WARN
, "cnopen: failed to open vsconsvp "
294 "for virtual console logging");
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
310 * XXX: Set hook to tell /proc about underlying console. (There's
311 * gotta be a better way...)
313 if (state
!= OTYP_CHR
|| getminor(*dev
) != 0)
315 if (been_here
== 0) {
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
);
325 if ((err
= fop_open(&vp
, flag
, cred
, NULL
)) != 0)
329 * The underlying driver is not allowed to have cloned itself
334 * It might happen that someone set rconsvp to NULL
335 * whilst we were in the middle of the open.
337 if (rconsvp
== NULL
) {
338 (void) fop_close(vp
, flag
, 1, 0, cred
, NULL
);
341 cmn_err(CE_PANIC
, "cnopen: cloned open");
351 cnclose(dev_t dev
, int flag
, int state
, struct cred
*cred
)
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.)
362 if (state
!= OTYP_CHR
)
368 while ((rconsopen
!= 0) && ((vp
= rconsvp
) != NULL
)) {
369 err
= fop_close(vp
, flag
, 1, 0, cred
, NULL
);
379 cnread(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
381 kcondvar_t sleep_forever
;
382 kmutex_t sleep_forever_mutex
;
384 if (rconsvp
== NULL
) {
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
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
);
399 if (rconsvp
->v_stream
!= NULL
)
400 return (strread(rconsvp
, uio
, cred
));
402 return (cdev_read(rconsdev
, uio
, cred
));
407 cnwrite(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
409 if (rconsvp
== NULL
) {
415 * Output to virtual console for logging if enabled.
417 if (vsconsvp
!= NULL
&& vsconsvp
->v_stream
!= NULL
) {
419 struct iovec buf
[IOV_MAX_STACK
];
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
);
430 * strwrite modifies uio so need to make copy.
432 (void) uiodup(uio
, &uiod
.d_uio
, uiod
.d_iov
, uio
->uio_iovcnt
);
434 (void) strwrite(vsconsvp
, &uiod
.d_uio
, cred
);
436 kmem_free(uiod
.d_iov
, iovlen
);
439 if (rconsvp
->v_stream
!= NULL
)
440 return (strwrite(rconsvp
, uio
, cred
));
442 return (cdev_write(rconsdev
, uio
, cred
));
447 cnprivateioc(dev_t dev
, int cmd
, intptr_t arg
, int flag
, struct cred
*cred
,
451 /* currently we only support one ioctl */
452 if (cmd
!= CONS_GETTERM
)
455 /* Confirm iwscn is immediate target of cn redirection */
456 if (rconsvp
!= wsconsvp
)
460 * If the redirection client is not wc, it should return
461 * error upon receiving the CONS_GETTERM ioctl.
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.
467 return (cdev_ioctl(rconsdev
, CONS_GETTERM
, arg
, flag
, cred
, rvalp
));
472 cnioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
, struct cred
*cred
,
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
485 if (cmd
== VT_SET_CONSUSER
)
486 cmd
= VT_RESET_CONSUSER
;
488 if ((cmd
& _CNIOC_MASK
) == _CNIOC
)
489 return (cnprivateioc(dev
, cmd
, arg
, flag
, cred
, rvalp
));
491 if (rconsvp
->v_stream
!= NULL
)
492 return (strioctl(rconsvp
, cmd
, arg
, flag
, U_TO_K
,
495 return (cdev_ioctl(rconsdev
, cmd
, arg
, flag
, cred
, rvalp
));
500 cnpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
501 struct pollhead
**phpp
)
504 return (nochpoll(dev
, events
, anyyet
, reventsp
, phpp
));
506 if (rconsvp
->v_stream
!= NULL
)
507 return (strpoll(rconsvp
->v_stream
, events
, anyyet
, reventsp
,
510 return (cdev_poll(rconsdev
, events
, anyyet
, reventsp
, phpp
));