blob: 6ef1b0b9f75976f3413f79583186c02b66494a2b [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
lq150181fea9cb92006-01-12 18:17:46 -08005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
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 */
lq150181fea9cb92006-01-12 18:17:46 -080021
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070022/*
rui zang - Sun Microsystems - Beijing Chinaceeba6f2010-04-21 13:58:16 +080023 * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070024 */
25
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070026/*
27 * Indirect console driver for Sun.
28 *
29 * Redirects all I/O to the device designated as the underlying "hardware"
30 * console, as given by the value of rconsvp. The implementation assumes that
31 * rconsvp denotes a STREAMS device; the assumption is justified since
32 * consoles must be capable of effecting tty semantics.
33 *
34 * rconsvp is set in autoconf.c:consconfig(), based on information obtained
35 * from the EEPROM.
36 *
37 * XXX: The driver still needs to be converted to use ANSI C consistently
38 * throughout.
39 */
40
41#include <sys/types.h>
42#include <sys/open.h>
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/signal.h>
46#include <sys/cred.h>
47#include <sys/user.h>
48#include <sys/proc.h>
49#include <sys/disp.h>
50#include <sys/file.h>
51#include <sys/taskq.h>
52#include <sys/log.h>
53#include <sys/vnode.h>
54#include <sys/uio.h>
55#include <sys/stat.h>
56
57#include <sys/console.h>
58#include <sys/consdev.h>
59
60#include <sys/stream.h>
61#include <sys/strsubr.h>
62#include <sys/poll.h>
63
64#include <sys/debug.h>
65
66#include <sys/conf.h>
67#include <sys/ddi.h>
68#include <sys/sunddi.h>
rui zang - Sun Microsystems - Beijing Chinaceeba6f2010-04-21 13:58:16 +080069#include <sys/vt.h>
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070070
71static int cnopen(dev_t *, int, int, struct cred *);
72static int cnclose(dev_t, int, int, struct cred *);
73static int cnread(dev_t, struct uio *, struct cred *);
74static int cnwrite(dev_t, struct uio *, struct cred *);
75static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *);
76static int cnpoll(dev_t, short, int, short *, struct pollhead **);
77static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
78static int cn_attach(dev_info_t *, ddi_attach_cmd_t);
79static int cn_detach(dev_info_t *, ddi_detach_cmd_t);
80
81static dev_info_t *cn_dip; /* private copy of devinfo pointer */
82
83static 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
103static 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 */
Sherry Moore19397402008-09-22 16:30:26 -0700114 (struct bus_ops *)0, /* bus operations */
115 NULL, /* power */
116 ddi_quiesce_not_needed, /* quiesce */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700117
118};
119
120/*
121 * Global variables associated with the console device:
122 *
123 * XXX: There are too many of these!
amwda6c28a2007-10-25 16:34:29 -0700124 * moved to space.c to become resident in the kernel so that cons
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700125 * can be loadable.
126 */
127
128extern dev_t rconsdev; /* "hardware" console */
129extern 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 */
134extern 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 */
150static 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
161extern int nodev(), nulldev();
162extern int dseekneg_flag;
163extern struct mod_ops mod_driverops;
164extern struct dev_ops cn_ops;
165
166/*
167 * Module linkage information for the kernel.
168 */
169
170static struct modldrv modldrv = {
171 &mod_driverops, /* Type of module. This one is a pseudo driver */
Sherry Moore19397402008-09-22 16:30:26 -0700172 "Console redirection driver",
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700173 &cn_ops, /* driver ops */
174};
175
176static struct modlinkage modlinkage = {
177 MODREV_1,
178 &modldrv,
179 NULL
180};
181
182int
183_init(void)
184{
185 return (mod_install(&modlinkage));
186}
187
188int
189_fini(void)
190{
191 return (EBUSY);
192}
193
194int
195_info(struct modinfo *modinfop)
196{
197 return (mod_info(&modlinkage, modinfop));
198}
199
200/*
201 * DDI glue routines
202 */
203static int
204cn_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 }
rui zang - Sun Microsystems - Beijing Chinaaecfc012008-09-25 14:01:48 +0800223
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700224 cn_dip = devi;
225 return (DDI_SUCCESS);
226}
227
228static int
229cn_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 */
239static int
240cn_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 */
274static int
275cnopen(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
James Anderson361ed642009-07-09 14:36:20 -0700286 /*
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 }
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700295
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
amwda6c28a2007-10-25 16:34:29 -0700323 if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700324 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) {
amwda6c28a2007-10-25 16:34:29 -0700336 (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700337 return (0);
338 }
339 cmn_err(CE_PANIC, "cnopen: cloned open");
340 }
341
342 rconsopen++;
343
344 return (0);
345}
346
347/* ARGSUSED */
348static int
349cnclose(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)) {
amwda6c28a2007-10-25 16:34:29 -0700367 err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700368 if (!err) {
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700369 rconsopen--;
370 }
371 }
372 return (err);
373}
374
375/* ARGSUSED */
376static int
377cnread(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 */
404static int
405cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
406{
407 if (rconsvp == NULL) {
408 uio->uio_resid = 0;
409 return (0);
410 }
411
James Anderson361ed642009-07-09 14:36:20 -0700412 /*
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
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700427 if (rconsvp->v_stream != NULL)
428 return (strwrite(rconsvp, uio, cred));
429 else
430 return (cdev_write(rconsdev, uio, cred));
431}
432
433/* ARGSUSED */
434static int
lq150181fea9cb92006-01-12 18:17:46 -0800435cnprivateioc(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 */
459static int
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700460cnioctl(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
rui zang - Sun Microsystems - Beijing Chinaceeba6f2010-04-21 13:58:16 +0800466 /*
467 * In wc, VT_SET_CONSUSER which comes from minor node 0
468 * has two sources -- either /dev/console or /dev/vt/0 .
469 * We need a way to differentiate them, so here we
470 * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
471 * ioctl.
472 */
473 if (cmd == VT_SET_CONSUSER)
474 cmd = VT_RESET_CONSUSER;
475
lq150181fea9cb92006-01-12 18:17:46 -0800476 if ((cmd & _CNIOC_MASK) == _CNIOC)
477 return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
rui zang - Sun Microsystems - Beijing Chinaceeba6f2010-04-21 13:58:16 +0800478
479 if (rconsvp->v_stream != NULL)
480 return (strioctl(rconsvp, cmd, arg, flag, U_TO_K,
481 cred, rvalp));
482
483 return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700484}
485
486/* ARGSUSED */
487static int
488cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
489 struct pollhead **phpp)
490{
491 if (rconsvp == NULL)
492 return (nochpoll(dev, events, anyyet, reventsp, phpp));
493
494 if (rconsvp->v_stream != NULL)
495 return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
496 phpp));
497 else
498 return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
499}