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 */
25
26 /*
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>
69 #include <sys/vt.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, NULL }
179 };
180
181 int
182 _init(void)
183 {
184 return (mod_install(&modlinkage));
185 }
186
187 int
188 _fini(void)
189 {
190 return (EBUSY);
191 }
192
193 int
194 _info(struct modinfo *modinfop)
195 {
196 return (mod_info(&modlinkage, modinfop));
197 }
198
199 /*
200 * DDI glue routines
201 */
202 static int
203 cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
204 {
205 if (cmd != DDI_ATTACH)
206 return (DDI_FAILURE);
207
208 if (ddi_create_minor_node(devi, "syscon", S_IFCHR,
209 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
210 return (DDI_FAILURE);
211 }
212 if (ddi_create_minor_node(devi, "systty", S_IFCHR,
213 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
214 ddi_remove_minor_node(devi, NULL);
215 return (DDI_FAILURE);
216 }
217 if (ddi_create_minor_node(devi, "console", S_IFCHR,
218 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
219 ddi_remove_minor_node(devi, NULL);
220 return (DDI_FAILURE);
221 }
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 * Enable virtual console I/O for console logging if needed.
287 */
288 if (vsconsvp != NULL && vsconsvp->v_stream == NULL) {
289 if (VOP_OPEN(&vsconsvp, FREAD | FWRITE, cred, NULL) != 0) {
290 cmn_err(CE_WARN, "cnopen: failed to open vsconsvp "
291 "for virtual console logging");
292 }
293 }
294
295 /*
296 * XXX: Clean up inactive PIDs from previous opens if any.
297 * These would have been created as a result of an I_SETSIG
298 * issued against console. This is a workaround, and
299 * console driver must be correctly redesigned not to need
300 * this hook.
301 */
302 if (vp->v_stream) {
303 str_cn_clean(vp);
304 }
305
306 /*
307 * XXX: Set hook to tell /proc about underlying console. (There's
308 * gotta be a better way...)
309 */
310 if (state != OTYP_CHR || getminor(*dev) != 0)
311 return (ENXIO);
312 if (been_here == 0) {
313 uconsdev = *dev;
314 been_here = 1;
315 if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
316 0, &console_vnode, 0, 0) == 0)
317 console_taskq = taskq_create("console_taskq",
318 1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
319 LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
320 }
321
322 if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
323 return (err);
324
325 /*
326 * The underlying driver is not allowed to have cloned itself
327 * for this open.
328 */
329 if (vp != rconsvp) {
330 /*
331 * It might happen that someone set rconsvp to NULL
332 * whilst we were in the middle of the open.
333 */
334 if (rconsvp == NULL) {
335 (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
336 return (0);
337 }
338 cmn_err(CE_PANIC, "cnopen: cloned open");
339 }
340
341 rconsopen++;
342
343 return (0);
344 }
345
346 /* ARGSUSED */
347 static int
348 cnclose(dev_t dev, int flag, int state, struct cred *cred)
349 {
350 int err = 0;
351 vnode_t *vp;
352
353 /*
354 * Since this is the _last_ close, it's our last chance to close the
355 * underlying device. (Note that if someone else has the underlying
356 * hardware console device open, we won't get here, since spec_close
357 * will see s_count > 1.)
358 */
359 if (state != OTYP_CHR)
360 return (ENXIO);
361
362 if (rconsvp == NULL)
363 return (0);
364
365 while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
366 err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
367 if (!err) {
368 rconsopen--;
369 }
370 }
371 return (err);
372 }
373
374 /* ARGSUSED */
375 static int
376 cnread(dev_t dev, struct uio *uio, struct cred *cred)
377 {
378 kcondvar_t sleep_forever;
379 kmutex_t sleep_forever_mutex;
380
381 if (rconsvp == NULL) {
382 /*
383 * Go to sleep forever. This seems like the least
384 * harmful thing to do if there's no console.
385 * EOF might be better if we're ending up single-user
386 * mode.
387 */
388 cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
389 mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
390 mutex_enter(&sleep_forever_mutex);
391 (void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
392 mutex_exit(&sleep_forever_mutex);
393 return (EIO);
394 }
395
396 if (rconsvp->v_stream != NULL)
397 return (strread(rconsvp, uio, cred));
398 else
399 return (cdev_read(rconsdev, uio, cred));
400 }
401
402 /* ARGSUSED */
403 static int
404 cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
405 {
406 if (rconsvp == NULL) {
407 uio->uio_resid = 0;
408 return (0);
409 }
410
411 /*
412 * Output to virtual console for logging if enabled.
413 */
414 if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
415 struiod_t uiod;
416
417 /*
418 * strwrite modifies uio so need to make copy.
419 */
420 (void) uiodup(uio, &uiod.d_uio, uiod.d_iov,
421 sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
422
423 (void) strwrite(vsconsvp, &uiod.d_uio, cred);
424 }
425
426 if (rconsvp->v_stream != NULL)
427 return (strwrite(rconsvp, uio, cred));
428 else
429 return (cdev_write(rconsdev, uio, cred));
430 }
431
432 /* ARGSUSED */
433 static int
434 cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
435 int *rvalp)
436 {
437
438 /* currently we only support one ioctl */
439 if (cmd != CONS_GETTERM)
440 return (EINVAL);
441
442 /* Confirm iwscn is immediate target of cn redirection */
443 if (rconsvp != wsconsvp)
444 return (ENODEV);
445
446 /*
447 * If the redirection client is not wc, it should return
448 * error upon receiving the CONS_GETTERM ioctl.
449 *
450 * if it is wc, we know that the target supports the CONS_GETTERM
451 * ioctl, which very conviently has the exact same data
452 * format as this ioctl... so let's just pass it on.
453 */
454 return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
455 }
456
457 /* ARGSUSED */
458 static int
459 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
460 int *rvalp)
461 {
462 if (rconsvp == NULL)
463 return (0);
464
465 /*
466 * In wc, VT_SET_CONSUSER which comes from minor node 0
467 * has two sources -- either /dev/console or /dev/vt/0 .
468 * We need a way to differentiate them, so here we
469 * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
470 * ioctl.
471 */
472 if (cmd == VT_SET_CONSUSER)
473 cmd = VT_RESET_CONSUSER;
474
475 if ((cmd & _CNIOC_MASK) == _CNIOC)
476 return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
477
478 if (rconsvp->v_stream != NULL)
479 return (strioctl(rconsvp, cmd, arg, flag, U_TO_K,
480 cred, rvalp));
481
482 return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
483 }
484
485 /* ARGSUSED */
486 static int
487 cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
488 struct pollhead **phpp)
489 {
490 if (rconsvp == NULL)
491 return (nochpoll(dev, events, anyyet, reventsp, phpp));
492
493 if (rconsvp->v_stream != NULL)
494 return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
495 phpp));
496 else
497 return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
498 }