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 static int 241 cn_info(dev_info_t *dip __unused, ddi_info_cmd_t infocmd, void *arg, 242 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 static int 350 cnclose(dev_t dev __unused, int flag, int state, struct cred *cred) 351 { 352 int err = 0; 353 vnode_t *vp; 354 355 /* 356 * Since this is the _last_ close, it's our last chance to close the 357 * underlying device. (Note that if someone else has the underlying 358 * hardware console device open, we won't get here, since spec_close 359 * will see s_count > 1.) 360 */ 361 if (state != OTYP_CHR) 362 return (ENXIO); 363 364 if (rconsvp == NULL) 365 return (0); 366 367 while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) { 368 err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 369 if (!err) { 370 rconsopen--; 371 } 372 } 373 return (err); 374 } 375 376 static int 377 cnread(dev_t dev __unused, 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 static int 404 cnwrite(dev_t dev __unused, 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 struct iovec buf[IOV_MAX_STACK]; 417 int iovlen = 0; 418 419 if (uio->uio_iovcnt > IOV_MAX_STACK) { 420 iovlen = uio->uio_iovcnt * sizeof (iovec_t); 421 uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP); 422 } else { 423 uiod.d_iov = buf; 424 } 425 426 /* 427 * strwrite modifies uio so need to make copy. 428 */ 429 (void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt); 430 431 (void) strwrite(vsconsvp, &uiod.d_uio, cred); 432 if (iovlen != 0) 433 kmem_free(uiod.d_iov, iovlen); 434 } 435 436 if (rconsvp->v_stream != NULL) 437 return (strwrite(rconsvp, uio, cred)); 438 else 439 return (cdev_write(rconsdev, uio, cred)); 440 } 441 442 static int 443 cnprivateioc(dev_t dev __unused, int cmd, intptr_t arg, int flag, 444 struct cred *cred, int *rvalp) 445 { 446 if (cmd == CONS_GETDEV) { 447 /* 448 * The user has requested the device number of the redirection 449 * client. 450 */ 451 STRUCT_DECL(cons_getdev, cnd); 452 STRUCT_INIT(cnd, flag); 453 454 bzero(STRUCT_BUF(cnd), STRUCT_SIZE(cnd)); 455 456 if ((flag & DATAMODEL_MASK) == DATAMODEL_ILP32) { 457 dev32_t rconsdev32; 458 459 if (cmpldev(&rconsdev32, rconsdev) != 1) { 460 return (EOVERFLOW); 461 } 462 463 STRUCT_FSET(cnd, cnd_rconsdev, rconsdev32); 464 } else { 465 STRUCT_FSET(cnd, cnd_rconsdev, rconsdev); 466 } 467 468 if (ddi_copyout(STRUCT_BUF(cnd), (void *)arg, 469 STRUCT_SIZE(cnd), flag) != 0) { 470 return (EFAULT); 471 } 472 473 return (0); 474 } 475 476 if (cmd != CONS_GETTERM) 477 return (EINVAL); 478 479 /* Confirm iwscn is immediate target of cn redirection */ 480 if (rconsvp != wsconsvp) 481 return (ENODEV); 482 483 /* 484 * If the redirection client is not wc, it should return 485 * error upon receiving the CONS_GETTERM ioctl. 486 * 487 * if it is wc, we know that the target supports the CONS_GETTERM 488 * ioctl, which very conviently has the exact same data 489 * format as this ioctl... so let's just pass it on. 490 */ 491 return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp)); 492 } 493 494 /* ARGSUSED */ 495 static int 496 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred, 497 int *rvalp) 498 { 499 if (rconsvp == NULL) 500 return (0); 501 502 /* 503 * In wc, VT_SET_CONSUSER which comes from minor node 0 504 * has two sources -- either /dev/console or /dev/vt/0 . 505 * We need a way to differentiate them, so here we 506 * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER 507 * ioctl. 508 */ 509 if (cmd == VT_SET_CONSUSER) 510 cmd = VT_RESET_CONSUSER; 511 512 if ((cmd & _CNIOC_MASK) == _CNIOC) 513 return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp)); 514 515 if (rconsvp->v_stream != NULL) 516 return (strioctl(rconsvp, cmd, arg, flag, U_TO_K, 517 cred, rvalp)); 518 519 return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp)); 520 } 521 522 /* ARGSUSED */ 523 static int 524 cnpoll(dev_t dev, short events, int anyyet, short *reventsp, 525 struct pollhead **phpp) 526 { 527 if (rconsvp == NULL) 528 return (nochpoll(dev, events, anyyet, reventsp, phpp)); 529 530 if (rconsvp->v_stream != NULL) 531 return (strpoll(rconsvp->v_stream, events, anyyet, reventsp, 532 phpp)); 533 else 534 return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp)); 535 } 536