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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * Indirect console driver for Sun. 30 * 31 * Redirects all I/O to the device designated as the underlying "hardware" 32 * console, as given by the value of rconsvp. The implementation assumes that 33 * rconsvp denotes a STREAMS device; the assumption is justified since 34 * consoles must be capable of effecting tty semantics. 35 * 36 * rconsvp is set in autoconf.c:consconfig(), based on information obtained 37 * from the EEPROM. 38 * 39 * XXX: The driver still needs to be converted to use ANSI C consistently 40 * throughout. 41 */ 42 43 #include <sys/types.h> 44 #include <sys/open.h> 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/signal.h> 48 #include <sys/cred.h> 49 #include <sys/user.h> 50 #include <sys/proc.h> 51 #include <sys/disp.h> 52 #include <sys/file.h> 53 #include <sys/taskq.h> 54 #include <sys/log.h> 55 #include <sys/vnode.h> 56 #include <sys/uio.h> 57 #include <sys/stat.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 72 static int cnopen(dev_t *, int, int, struct cred *); 73 static int cnclose(dev_t, int, int, struct cred *); 74 static int cnread(dev_t, struct uio *, struct cred *); 75 static int cnwrite(dev_t, struct uio *, struct cred *); 76 static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *); 77 static int cnpoll(dev_t, short, int, short *, struct pollhead **); 78 static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 79 static int cn_attach(dev_info_t *, ddi_attach_cmd_t); 80 static int cn_detach(dev_info_t *, ddi_detach_cmd_t); 81 82 static dev_info_t *cn_dip; /* private copy of devinfo pointer */ 83 84 static struct cb_ops cn_cb_ops = { 85 86 cnopen, /* open */ 87 cnclose, /* close */ 88 nodev, /* strategy */ 89 nodev, /* print */ 90 nodev, /* dump */ 91 cnread, /* read */ 92 cnwrite, /* write */ 93 cnioctl, /* ioctl */ 94 nodev, /* devmap */ 95 nodev, /* mmap */ 96 nodev, /* segmap */ 97 cnpoll, /* poll */ 98 ddi_prop_op, /* cb_prop_op */ 99 0, /* streamtab */ 100 D_NEW | D_MP /* Driver compatibility flag */ 101 102 }; 103 104 static struct dev_ops cn_ops = { 105 106 DEVO_REV, /* devo_rev, */ 107 0, /* refcnt */ 108 cn_info, /* info */ 109 nulldev, /* identify */ 110 nulldev, /* probe */ 111 cn_attach, /* attach */ 112 cn_detach, /* detach */ 113 nodev, /* reset */ 114 &cn_cb_ops, /* driver operations */ 115 (struct bus_ops *)0, /* bus operations */ 116 NULL, /* power */ 117 ddi_quiesce_not_needed, /* quiesce */ 118 119 }; 120 121 /* 122 * Global variables associated with the console device: 123 * 124 * XXX: There are too many of these! 125 * moved to space.c to become resident in the kernel so that cons 126 * can be loadable. 127 */ 128 129 extern dev_t rconsdev; /* "hardware" console */ 130 extern vnode_t *rconsvp; /* pointer to vnode for that device */ 131 132 /* 133 * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info. 134 */ 135 extern dev_t uconsdev; /* What the user thinks is the console device */ 136 137 /* 138 * Private driver state: 139 */ 140 141 /* 142 * The underlying console device potentially can be opened through (at least) 143 * two paths: through this driver and through the underlying device's driver. 144 * To ensure that reference counts are meaningful and therefore that close 145 * routines are called at the right time, it's important to make sure that 146 * rconsvp's s_count field (i.e., the count on the underlying device) never 147 * has a contribution of more than one through this driver, regardless of how 148 * many times this driver's been opened. rconsopen keeps track of the 149 * necessary information to ensure this property. 150 */ 151 static uint_t rconsopen; 152 153 154 #include <sys/types.h> 155 #include <sys/conf.h> 156 #include <sys/param.h> 157 #include <sys/systm.h> 158 #include <sys/errno.h> 159 #include <sys/modctl.h> 160 161 162 extern int nodev(), nulldev(); 163 extern int dseekneg_flag; 164 extern struct mod_ops mod_driverops; 165 extern struct dev_ops cn_ops; 166 167 /* 168 * Module linkage information for the kernel. 169 */ 170 171 static struct modldrv modldrv = { 172 &mod_driverops, /* Type of module. This one is a pseudo driver */ 173 "Console redirection driver", 174 &cn_ops, /* driver ops */ 175 }; 176 177 static struct modlinkage modlinkage = { 178 MODREV_1, 179 &modldrv, 180 NULL 181 }; 182 183 int 184 _init(void) 185 { 186 return (mod_install(&modlinkage)); 187 } 188 189 int 190 _fini(void) 191 { 192 return (EBUSY); 193 } 194 195 int 196 _info(struct modinfo *modinfop) 197 { 198 return (mod_info(&modlinkage, modinfop)); 199 } 200 201 /* 202 * DDI glue routines 203 */ 204 static int 205 cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 206 { 207 if (cmd != DDI_ATTACH) 208 return (DDI_FAILURE); 209 210 if (ddi_create_minor_node(devi, "syscon", S_IFCHR, 211 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 212 return (DDI_FAILURE); 213 } 214 if (ddi_create_minor_node(devi, "systty", S_IFCHR, 215 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 216 ddi_remove_minor_node(devi, NULL); 217 return (DDI_FAILURE); 218 } 219 if (ddi_create_minor_node(devi, "console", S_IFCHR, 220 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 221 ddi_remove_minor_node(devi, NULL); 222 return (DDI_FAILURE); 223 } 224 cn_dip = devi; 225 return (DDI_SUCCESS); 226 } 227 228 static int 229 cn_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 */ 239 static int 240 cn_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 */ 274 static int 275 cnopen(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 286 287 /* 288 * XXX: Clean up inactive PIDs from previous opens if any. 289 * These would have been created as a result of an I_SETSIG 290 * issued against console. This is a workaround, and 291 * console driver must be correctly redesigned not to need 292 * this hook. 293 */ 294 if (vp->v_stream) { 295 str_cn_clean(vp); 296 } 297 298 /* 299 * XXX: Set hook to tell /proc about underlying console. (There's 300 * gotta be a better way...) 301 */ 302 if (state != OTYP_CHR || getminor(*dev) != 0) 303 return (ENXIO); 304 if (been_here == 0) { 305 uconsdev = *dev; 306 been_here = 1; 307 if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY, 308 0, &console_vnode, 0, 0) == 0) 309 console_taskq = taskq_create("console_taskq", 310 1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE, 311 LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE); 312 } 313 314 if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0) 315 return (err); 316 317 /* 318 * The underlying driver is not allowed to have cloned itself 319 * for this open. 320 */ 321 if (vp != rconsvp) { 322 /* 323 * It might happen that someone set rconsvp to NULL 324 * whilst we were in the middle of the open. 325 */ 326 if (rconsvp == NULL) { 327 (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 328 return (0); 329 } 330 cmn_err(CE_PANIC, "cnopen: cloned open"); 331 } 332 333 rconsopen++; 334 335 return (0); 336 } 337 338 /* ARGSUSED */ 339 static int 340 cnclose(dev_t dev, int flag, int state, struct cred *cred) 341 { 342 int err = 0; 343 vnode_t *vp; 344 345 /* 346 * Since this is the _last_ close, it's our last chance to close the 347 * underlying device. (Note that if someone else has the underlying 348 * hardware console device open, we won't get here, since spec_close 349 * will see s_count > 1.) 350 */ 351 if (state != OTYP_CHR) 352 return (ENXIO); 353 354 if (rconsvp == NULL) 355 return (0); 356 357 while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) { 358 err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 359 if (!err) { 360 vp->v_stream = NULL; 361 rconsopen--; 362 } 363 } 364 return (err); 365 } 366 367 /* ARGSUSED */ 368 static int 369 cnread(dev_t dev, struct uio *uio, struct cred *cred) 370 { 371 kcondvar_t sleep_forever; 372 kmutex_t sleep_forever_mutex; 373 374 if (rconsvp == NULL) { 375 /* 376 * Go to sleep forever. This seems like the least 377 * harmful thing to do if there's no console. 378 * EOF might be better if we're ending up single-user 379 * mode. 380 */ 381 cv_init(&sleep_forever, NULL, CV_DRIVER, NULL); 382 mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL); 383 mutex_enter(&sleep_forever_mutex); 384 (void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex); 385 mutex_exit(&sleep_forever_mutex); 386 return (EIO); 387 } 388 389 if (rconsvp->v_stream != NULL) 390 return (strread(rconsvp, uio, cred)); 391 else 392 return (cdev_read(rconsdev, uio, cred)); 393 } 394 395 /* ARGSUSED */ 396 static int 397 cnwrite(dev_t dev, struct uio *uio, struct cred *cred) 398 { 399 if (rconsvp == NULL) { 400 uio->uio_resid = 0; 401 return (0); 402 } 403 404 if (rconsvp->v_stream != NULL) 405 return (strwrite(rconsvp, uio, cred)); 406 else 407 return (cdev_write(rconsdev, uio, cred)); 408 } 409 410 /* ARGSUSED */ 411 static int 412 cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred, 413 int *rvalp) 414 { 415 416 /* currently we only support one ioctl */ 417 if (cmd != CONS_GETTERM) 418 return (EINVAL); 419 420 /* Confirm iwscn is immediate target of cn redirection */ 421 if (rconsvp != wsconsvp) 422 return (ENODEV); 423 424 /* 425 * If the redirection client is not wc, it should return 426 * error upon receiving the CONS_GETTERM ioctl. 427 * 428 * if it is wc, we know that the target supports the CONS_GETTERM 429 * ioctl, which very conviently has the exact same data 430 * format as this ioctl... so let's just pass it on. 431 */ 432 return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp)); 433 } 434 435 /* ARGSUSED */ 436 static int 437 cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred, 438 int *rvalp) 439 { 440 if (rconsvp == NULL) 441 return (0); 442 443 if ((cmd & _CNIOC_MASK) == _CNIOC) 444 return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp)); 445 else if (rconsvp->v_stream != NULL) 446 return (strioctl(rconsvp, cmd, arg, flag, U_TO_K, cred, 447 rvalp)); 448 else 449 return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp)); 450 } 451 452 /* ARGSUSED */ 453 static int 454 cnpoll(dev_t dev, short events, int anyyet, short *reventsp, 455 struct pollhead **phpp) 456 { 457 if (rconsvp == NULL) 458 return (nochpoll(dev, events, anyyet, reventsp, phpp)); 459 460 if (rconsvp->v_stream != NULL) 461 return (strpoll(rconsvp->v_stream, events, anyyet, reventsp, 462 phpp)); 463 else 464 return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp)); 465 } 466