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 }