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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This is a dacf module based upon the Extensions to Device Autoconfiguration 28 * project. See PSARC/1998/212 for more details. 29 * 30 * This module provides the dacf functions 31 * to be called after a driver has attached and before it detaches. 32 * The post attach functionality is used to autoconfigure a serial console 33 * multiplexer if the OBP console is a multiplexer. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/cmn_err.h> 39 #include <sys/user.h> 40 #include <sys/vfs.h> 41 #include <sys/vnode.h> 42 #include <sys/systm.h> 43 #include <sys/file.h> 44 #include <sys/klwp.h> 45 #include <sys/stropts.h> 46 #include <sys/stream.h> 47 #include <sys/strsubr.h> 48 49 #include <sys/consdev.h> 50 #include <sys/kbio.h> 51 #include <sys/debug.h> 52 #include <sys/reboot.h> 53 #include <sys/termios.h> 54 #include <sys/clock.h> 55 56 #include <sys/kstr.h> 57 #include <sys/ddi.h> 58 #include <sys/sunddi.h> 59 #include <sys/sunndi.h> 60 #include <sys/modctl.h> 61 #include <sys/ddi_impldefs.h> 62 #include <sys/ndi_impldefs.h> 63 64 #include <sys/errno.h> 65 #include <sys/devops.h> 66 #include <sys/note.h> 67 #include <sys/open.h> 68 #include <sys/kmem.h> 69 #include <sys/dacf.h> 70 #include <sys/promif.h> 71 72 #include "ttymux_dacf.h" 73 74 #pragma weak find_platform_consoles 75 extern char *find_platform_consoles(sm_mux_state_t *_m, dev_info_t *_di, 76 dev_t _d, uint_t _f); 77 78 #define platform_consoles(_m, _di, _d, _f) \ 79 (find_platform_consoles != NULL \ 80 ? find_platform_consoles(_m, _di, _d, _f) \ 81 : (nulldev(_m, _di, _d, _f), (char *)0)) 82 83 /* 84 * External functions 85 */ 86 extern uintptr_t space_fetch(char *key); 87 extern int space_store(char *key, uintptr_t ptr); 88 extern void ttymux_dprintf(int l, const char *fmt, ...); 89 extern int prom_ihandle_to_path(ihandle_t, char *, uint_t); 90 extern void prom_interpret(char *, uintptr_t, uintptr_t, uintptr_t, 91 uintptr_t, uintptr_t); 92 extern ihandle_t prom_stdin_ihandle(); 93 extern ihandle_t prom_stdout_ihandle(); 94 95 extern vnode_t *rconsvp; /* redirection device */ 96 97 /* 98 * Dacf entry points 99 */ 100 static int ttymux_config(dacf_infohdl_t, dacf_arghdl_t, int); 101 102 /* 103 * Internal functions 104 */ 105 static dacf_op_t ttymuxconfig_op[] = { 106 { DACF_OPID_POSTATTACH, ttymux_config }, 107 { DACF_OPID_END, NULL }, 108 }; 109 110 static dacf_opset_t opsets[] = { 111 { "ttymux_config", ttymuxconfig_op }, 112 { NULL, NULL } 113 }; 114 115 struct dacfsw dacfsw = { 116 DACF_MODREV_1, 117 opsets, 118 }; 119 120 struct modldacf modldacf = { 121 &mod_dacfops, /* Type of module */ 122 "ttymux DACF", 123 &dacfsw 124 }; 125 126 struct modlinkage modlinkage = { 127 MODREV_1, (void *)&modldacf, NULL 128 }; 129 130 /*LINTLIBRARY*/ 131 132 /* 133 * The following minor nodes can be linked underneath the serial 134 * console multiplexer. 135 * These are the only ones currently tested. 136 * (NOTE: Devices of device_type serial are also allowed to be used as 137 * additional consoles). 138 * Disallow plumbing of untested node types. 139 */ 140 static const char * const supported_types[] = { 141 DDI_NT_SERIAL, (char *const)NULL 142 }; 143 144 #define OFLAGS FREAD|FWRITE|FNOCTTY|FNONBLOCK 145 146 #define INPUT_ALIAS "multiplexer-input-devices" 147 #define OUTPUT_ALIAS "multiplexer-output-devices" 148 #define OBPDEV 0x100 149 #define FORTH_STRINGLEN 1024 150 #define MUXDEVTYPE "SUNW,serial-multiplexer" 151 152 static char fth_fmt[] = 153 "\" get-device-list\" " /* ( method-str method-len ) */ 154 "h# %p " /* ( method-str method-len ihandle ) */ 155 "$call-method " /* ( ihandle_n-1 ... ihandle n ) */ 156 "dup " /* ( ihandle_n-1 ... ihandle n n ) */ 157 "h# %p " /* ( ihandle_n-1 ... ihandle n n numfound ) */ 158 "l! " /* ( ihandle_n-1 ... ihandle n ) */ 159 "0 " /* ( ihandle_n-1 ... ihandle n 0 ) */ 160 "do " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */ 161 " i " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index ) */ 162 " h# %x " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index max) */ 163 " < if " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */ 164 " h# %p " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf ) */ 165 " i " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf index) */ 166 " 4 * + " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf' ) */ 167 " l! " /* ( ihandle_n-1 ... ihandle_i+1 ) */ 168 " else " /* */ 169 " drop " /* ( ihandle_n-1 ... ihandle_i+1 ) */ 170 " then " /* */ 171 "loop "; /* ( ihandle_n-1 ... ihandle_i+1 ) */ 172 173 int 174 _init(void) 175 { 176 return (mod_install(&modlinkage)); 177 } 178 179 int 180 _fini() 181 { 182 return (mod_remove(&modlinkage)); 183 } 184 185 int 186 _info(struct modinfo *modinfop) 187 { 188 return (mod_info(&modlinkage, modinfop)); 189 } 190 191 static int 192 ioctl_cmd(vnode_t *avp, int cmd, void *data, int datasize, int *bytecnt) 193 { 194 struct strioctl ios; 195 int rval; 196 197 ios.ic_timout = 0; 198 ios.ic_cmd = cmd; 199 ios.ic_dp = (char *)data; 200 ios.ic_len = datasize; 201 202 rval = kstr_ioctl(avp, I_STR, (intptr_t)&ios); 203 if (bytecnt) 204 *bytecnt = ios.ic_len; 205 return (rval); 206 } 207 208 /* 209 * How many consoles are actually linked underneath the Solaris console 210 * multiplexer. 211 */ 212 static int 213 usable_consoles(sm_mux_state_t *sp, uint_t *iconsoles, uint_t *oconsoles) 214 { 215 uint_t j, cnt, icnt = 0u, ocnt = 0u; 216 217 mutex_enter(&sp->sm_cons_mutex); 218 for (j = 0, cnt = 0; j < sp->sm_cons_cnt; j++) 219 if (sp->sm_cons_links[j].sm_muxid != 0) { 220 sm_console_t *cn = &sp->sm_cons_links[j]; 221 if (cn->sm_mode & FORINPUT) 222 icnt += 1; 223 if (cn->sm_mode & FOROUTPUT) 224 ocnt += 1; 225 if (cn->sm_mode == FORIO) 226 cnt += 1; 227 } 228 mutex_exit(&sp->sm_cons_mutex); 229 *iconsoles = icnt; 230 *oconsoles = ocnt; 231 return (cnt); 232 } 233 234 /* 235 * Before linking a device underneath a serial multiplexer check that 236 * its minor node type is supported. 237 */ 238 static boolean_t 239 compatible_console(dev_t dev) 240 { 241 int circ; 242 boolean_t compatible; 243 char *const *nodetype; 244 struct ddi_minor_data *dmdp; 245 dev_info_t *dip; 246 char devtype[32]; 247 int len; 248 249 /* 250 * Find the node nodetype to verify that the current version of 251 * the code supports its use as a console 252 * Supported types are listed in the array supported_types 253 */ 254 if ((dip = e_ddi_hold_devi_by_dev(dev, 0)) == NULL) { 255 ttymux_dprintf(DPRINT_L2, "No dip for %d:%d\n", 256 getmajor(dev), getminor(dev)); 257 return (B_FALSE); 258 } 259 260 compatible = B_FALSE; 261 len = sizeof (devtype); 262 263 ndi_devi_enter(dip, &circ); 264 for (dmdp = DEVI(dip)->devi_minor; dmdp != NULL; dmdp = dmdp->next) { 265 struct ddi_minor_data *mdp = dmdp; 266 267 if (mdp->ddm_dev == dev) { 268 269 ttymux_dprintf(DPRINT_L0, "compat: matched dev\n"); 270 /* 271 * check the OBP device_type property first 272 * its a good bet that it will be compatible 273 * if it has the value serial. 274 */ 275 if (ddi_prop_op(DDI_DEV_T_ANY, dip, 276 PROP_LEN_AND_VAL_BUF, 0, "device_type", 277 (caddr_t)devtype, &len) == DDI_PROP_SUCCESS && 278 strcmp(devtype, "serial") == 0) { 279 compatible = B_TRUE; 280 } else { 281 for (nodetype = 282 (char *const *)&supported_types[0]; 283 *nodetype != (char *const)NULL; 284 nodetype++) { 285 if (strcmp(*nodetype, 286 mdp->ddm_node_type) == 0) { 287 compatible = B_TRUE; 288 break; 289 } 290 } 291 } 292 break; 293 } 294 } 295 ndi_devi_exit(dip, circ); 296 ddi_release_devi(dip); 297 298 /* 299 * The current version of the implementation has only been tested 300 * with a serial multiplexer. 301 */ 302 303 ttymux_dprintf(DPRINT_L0, "%d:%d is %s\n", getmajor(dev), 304 getminor(dev), (compatible) ? "compatible" : "incompatible"); 305 306 return (compatible); 307 } 308 309 /* 310 * get-device-list ( -- [ihandle n-1, ... ihandle], n ) 311 * Call the "get-device-list" method of an OBP device. 312 * ihdl - ihandle of the OBP device whose method is to be called 313 * ihdls - array of ihandles returned to the caller 314 * maxi - length of the ihdls array 315 */ 316 static int 317 get_device_list(ihandle_t ihdl, ihandle_t *ihdls, size_t maxi) 318 { 319 int numfound = -1; 320 char fstr[FORTH_STRINGLEN]; 321 322 if (snprintf(fstr, FORTH_STRINGLEN, fth_fmt, (caddr32_t)ihdl, 323 &numfound, maxi, ihdls) > FORTH_STRINGLEN) { 324 ttymux_dprintf(DPRINT_L3, 325 "WARNING: forth buffer size is too small.\n"); 326 return (0); 327 } 328 329 prom_interpret(fstr, 0, 0, 0, 0, 0); 330 331 ttymux_dprintf(DPRINT_L0, "ihdl 0x%p cnt %d\n", 332 (caddr32_t)ihdl, numfound); 333 334 return (numfound); 335 } 336 337 /* 338 * Read an OBP property and return the result in propval. 339 * The caller is responsible for freeing the memory. 340 */ 341 static int 342 read_prop(pnode_t node, char *propname, char **propval) 343 { 344 int proplen = -1; 345 346 if (node == OBP_BADNODE || 347 (proplen = prom_getproplen(node, propname)) <= 0) 348 return (proplen); 349 350 *propval = kmem_zalloc(proplen + 1, KM_SLEEP); 351 (void) prom_getprop(node, propname, *propval); 352 353 return (proplen); 354 } 355 356 /* 357 * Parse a white space separated list of tokens and call 358 * the input action with each parsed token. 359 */ 360 static void 361 parse(sm_mux_state_t *ms, char *p, 362 void (*action)(sm_mux_state_t *, char *, void *), void *arg) 363 { 364 char *e, *tok = NULL; 365 366 if (p == 0 || *p == 0) 367 return; 368 369 e = p + strlen(p); 370 371 do { 372 switch (*p) { 373 case ' ': 374 case '\t': 375 if (tok != NULL) { 376 *p = 0; 377 action(ms, tok, arg); 378 tok = NULL; 379 *p = ' '; 380 } 381 break; 382 default: 383 if (tok == NULL) { 384 tok = p; 385 } 386 break; 387 } 388 } while (++p < e); 389 390 if (tok != NULL) 391 action(ms, tok, arg); 392 } 393 394 /* 395 * Search for a console structure matching a device path. 396 * Return a new initialized structure if one does not exist. 397 */ 398 sm_console_t * 399 get_aconsole(sm_mux_state_t *ms, char *path) 400 { 401 sm_console_t *cn; 402 int j; 403 404 for (cn = ms->sm_cons_links, j = 0; 405 j < ms->sm_cons_cnt; cn++, j++) { 406 if (cn->sm_path && strcmp(cn->sm_path, path) == 0) 407 break; 408 } 409 if (j == ms->sm_cons_cnt) { 410 if (j + 1 == TTYMUX_MAX_LINKS) { 411 cn = NULL; 412 } else { 413 bzero((caddr_t)cn, sizeof (*cn)); 414 ms->sm_cons_cnt += 1; 415 } 416 } 417 return (cn); 418 } 419 420 /* 421 * Create a new console structure representing the device 422 * identified by path. The void * argument indicates which I/O 423 * mode the device will support. 424 */ 425 static void 426 add_aconsole(sm_mux_state_t *ms, char *path, void *arg) 427 { 428 sm_console_t *cn; 429 char *cpath; 430 431 if (*path == '/') { 432 cpath = kmem_alloc(strlen(path) + 1, KM_SLEEP); 433 (void) strcpy(cpath, path); 434 } else if (read_prop(prom_alias_node(), path, &cpath) <= 0) { 435 return; 436 } 437 438 /* 439 * Device paths should have a minor name - if its missing assume 440 * it should be :a! 441 */ 442 if (strrchr(cpath, ':') == NULL) { 443 char *p; 444 size_t len = strlen(cpath) + 1; 445 446 p = kmem_zalloc(len + 2, KM_SLEEP); 447 (void) strcpy(p, cpath); 448 (void) strcat(p, ":a"); /* assume :a ! */ 449 kmem_free(cpath, len); 450 cpath = p; 451 } 452 if ((cn = get_aconsole(ms, cpath)) != NULL) { 453 cn->sm_obp_con = ((uint_t)(uintptr_t)arg & OBPDEV) ? 454 B_TRUE : B_FALSE; 455 cn->sm_mode |= (io_mode_t)((uint_t)(uintptr_t)arg & FORIO); 456 if (cn->sm_path != NULL) 457 kmem_free(cn->sm_path, strlen(cn->sm_path) + 1); 458 cn->sm_path = cpath; 459 } else { 460 ttymux_dprintf(DPRINT_L3, "Too many " 461 " consoles - ignoring %s\n", cpath); 462 kmem_free(cpath, strlen(cpath) + 1); 463 } 464 } 465 466 /* 467 * Discover which consoles OBP is using. 468 */ 469 static int 470 find_obp_consoles(sm_mux_state_t *ms, io_mode_t mode) 471 { 472 sm_console_t *cn; 473 int i, cnt; 474 char *devpath; 475 ihandle_t ihdls[TTYMUX_MAX_LINKS]; 476 ihandle_t stdihdl; 477 478 if (mode == FORINPUT) 479 stdihdl = ms->sm_cons_stdin.sm_i_ihdl; 480 else if (mode == FOROUTPUT) 481 stdihdl = ms->sm_cons_stdout.sm_o_ihdl; 482 else 483 return (EINVAL); 484 devpath = kmem_alloc(MAXPATHLEN+2, KM_SLEEP); 485 486 cnt = get_device_list(stdihdl, ihdls, TTYMUX_MAX_LINKS); 487 488 for (i = 0; i < cnt; i++) { 489 490 if (prom_ihandle_to_path(ihdls[i], devpath, MAXPATHLEN) == 0) 491 continue; 492 /* 493 * If the minor name is not part of the path and there is 494 * more than one minor node then ddi_pathname_to_dev_t 495 * can fail to resolve the path correctly (it's an OBP 496 * problem)!!! If there's no minor name then assume the default 497 * minor name (:a). 498 */ 499 if (strrchr(devpath, ':') == NULL) 500 (void) strcat(devpath, ":a"); /* assume :a ! */ 501 502 if ((cn = get_aconsole(ms, devpath)) == 0) { 503 ttymux_dprintf(DPRINT_L3, "Too many " 504 " consoles - ignoring %s\n", devpath); 505 continue; 506 } 507 508 cn->sm_mode |= mode; 509 cn->sm_obp_con = B_TRUE; 510 if (mode == FORINPUT) 511 cn->sm_i_ihdl = ihdls[i]; 512 else 513 cn->sm_o_ihdl = ihdls[i]; 514 if (cn->sm_path == NULL) { 515 cn->sm_path = kmem_alloc(strlen(devpath) + 1, KM_SLEEP); 516 (void) strcpy(cn->sm_path, devpath); 517 } 518 519 } 520 kmem_free(devpath, MAXPATHLEN + 2); 521 522 return (0); 523 } 524 525 /* 526 * Convert a file system path into a dev_t 527 */ 528 static dev_t 529 fs_devtype(char *fspath) 530 { 531 vnode_t *vp = NULL; 532 dev_t dev; 533 534 if (fspath == 0 || 535 vn_open(fspath, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0) { 536 return (NODEV); 537 } else { 538 dev = vp->v_rdev; 539 VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); 540 VN_RELE(vp); 541 return (dev); 542 } 543 } 544 545 /* 546 * Convert a device tree path into a dev_t 547 */ 548 static dev_t 549 di_devtype(char *path) 550 { 551 dev_t dev; 552 553 if (path == 0 || *path == 0) 554 return (NODEV); 555 556 ttymux_dprintf(DPRINT_L0, "loading device %s\n", path); 557 dev = ddi_pathname_to_dev_t(path); 558 559 return (dev); 560 } 561 562 563 static int 564 open_stream(vnode_t **vp, int *fd, dev_t dev) 565 { 566 file_t *fp; 567 int rv; 568 569 /* create a vnode for the device and open it */ 570 *vp = makespecvp(dev, VCHR); 571 if ((rv = VOP_OPEN(vp, FREAD+FWRITE+FNOCTTY, CRED(), NULL)) != 0) { 572 goto out2; 573 } 574 /* Associate a file pointer with the vnode */ 575 if ((rv = falloc(*vp, FREAD+FWRITE+FNOCTTY, &fp, NULL)) != 0) { 576 goto out1; 577 } 578 mutex_exit(&fp->f_tlock); /* must be called single threaded */ 579 /* Allocate a file descriptor (any non-negative integer will suffice) */ 580 if ((*fd = ufalloc(0)) == -1) { 581 rv = EMFILE; 582 goto out1; 583 } 584 /* associate the file pointer with the fd */ 585 setf(*fd, fp); 586 return (0); 587 588 out1: 589 VOP_CLOSE(*vp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); 590 out2: 591 VN_RELE(*vp); 592 return (rv); 593 } 594 595 /* 596 * Plumb a device specified by the sm_console_t argument underneath the 597 * serial multiplexer indicated by the vnode_t argument. 598 */ 599 static int 600 link_aconsole(vnode_t *mux_avp, sm_console_t *cn) 601 { 602 vnode_t *lvp; 603 int lfd; 604 int rv, rval; 605 ttymux_assoc_t assoc; 606 struct termios tc; 607 608 ASSERT(cn->sm_path); 609 610 /* get an open vnode for the device */ 611 if ((rv = open_stream(&lvp, &lfd, cn->sm_dev)) != 0) 612 return (rv); 613 614 /* 615 * Enable the receiver on the lower device since it will 616 * be used by OBP. 617 */ 618 if ((rv = ioctl_cmd(lvp, TCGETS, &tc, sizeof (tc), 0)) == 0) { 619 tc.c_cflag |= CREAD; 620 rv = ioctl_cmd(lvp, TCSETS, &tc, sizeof (tc), 0); 621 } 622 if (rv != 0) 623 ttymux_dprintf(DPRINT_L3, 624 "DACF: Failed to enable console receiver [error %d]\n", rv); 625 626 /* 627 * Pop all the modules off the stream prior to linking it. 628 */ 629 do { 630 rv = strioctl(lvp, I_POP, 0, 0, K_TO_K, CRED(), &rval); 631 } while (rv == 0); 632 633 if (rv != EINVAL) { 634 ttymux_dprintf(DPRINT_L3, 635 "Failed to pop all modules: error %d", rv); 636 goto out; 637 } 638 639 if ((rv = strioctl(mux_avp, I_PLINK, (intptr_t)lfd, 640 FREAD+FWRITE+FNOCTTY, K_TO_K, CRED(), &(cn->sm_muxid))) != 0) { 641 642 ttymux_dprintf(DPRINT_L3, 643 "Failed to link device: error %d", rv); 644 goto out; 645 } 646 /* close the linked device */ 647 (void) closeandsetf(lfd, NULL); 648 /* 649 * Now tell the mux to associate the new stream 650 */ 651 assoc.ttymux_udev = mux_avp->v_rdev; 652 assoc.ttymux_ldev = cn->sm_dev; 653 assoc.ttymux_linkid = cn->sm_muxid; 654 assoc.ttymux_tag = 0; 655 assoc.ttymux_ioflag = cn->sm_mode; 656 if ((rv = ioctl_cmd(mux_avp, TTYMUX_ASSOC, 657 (void *)&assoc, sizeof (assoc), 0)) != 0) { 658 ttymux_dprintf(DPRINT_L3, 659 "Failed to associate %d:%d with the console\n", 660 getmajor(cn->sm_dev), getminor(cn->sm_dev)); 661 662 if (strioctl(mux_avp, I_PUNLINK, (intptr_t)cn->sm_muxid, 0, 663 K_TO_K, CRED(), &rval) != 0) 664 ttymux_dprintf(DPRINT_L3, 665 "Can't unlink %d:%d - Closing vnode\n", 666 getmajor(cn->sm_dev), getminor(cn->sm_dev)); 667 668 } 669 return (rv); 670 671 out: 672 VOP_CLOSE(lvp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); 673 VN_RELE(lvp); 674 return (rv); 675 } 676 677 static int 678 enable_aconsole(sm_mux_state_t *ms, sm_console_t *cn, vnode_t *muxvp) 679 { 680 ttymux_assoc_t assoc; 681 682 ASSERT(cn && cn->sm_dev != NODEV); 683 684 assoc.ttymux_ldev = cn->sm_dev; 685 686 cn->sm_muxid = (ioctl_cmd(muxvp, TTYMUX_GETLINK, 687 (void *)&assoc, sizeof (assoc), 0) == 0) ? assoc.ttymux_linkid : 0; 688 689 if (cn->sm_muxid != 0) 690 return (0); /* already linked */ 691 else 692 return (link_aconsole(muxvp, cn)); 693 } 694 695 /* 696 * Enable all discovered consoles such that they can provide real I/O. 697 * The discovered list is stored in the sm_mux_state_t pointer. 698 */ 699 static int 700 enable_all_consoles(sm_mux_state_t *ms, vnode_t *muxvp) 701 { 702 sm_console_t *cn; 703 uint_t j; 704 705 ttymux_dprintf(DPRINT_L0, "Enable %d devices\n", ms->sm_cons_cnt); 706 for (cn = ms->sm_cons_links, j = 0; 707 j < ms->sm_cons_cnt; cn++, j++) { 708 709 if (cn->sm_path == NULL) 710 continue; 711 712 if ((strstr(cn->sm_path, "/dev") == cn->sm_path && 713 (cn->sm_dev = fs_devtype(cn->sm_path)) == NODEV) || 714 (cn->sm_dev = di_devtype(cn->sm_path)) == NODEV) { 715 716 ttymux_dprintf(DPRINT_L0, 717 "Cannot find a driver for device: %s\n", 718 cn->sm_path ? cn->sm_path : ""); 719 continue; 720 } 721 ttymux_dprintf(DPRINT_L0, "Enabling %d:%d\n", 722 getmajor(cn->sm_dev), getminor(cn->sm_dev)); 723 724 /* 725 * Refuse requests to use devices as consoles which have an 726 * unsupported minor node type. 727 */ 728 if (compatible_console(cn->sm_dev) == B_FALSE) 729 continue; 730 731 /* 732 * Enable a console device by linking the target console 733 * underneath the ttymux minor node that has been specified 734 * in a DACF reservation (see /etc/dacf.conf). 735 */ 736 (void) enable_aconsole(ms, cn, muxvp); 737 } 738 return (0); 739 } 740 741 static int 742 find_consoles(sm_mux_state_t *ms, dev_info_t *dip, dev_t dev) 743 { 744 int len; 745 char *propval; 746 char devtype[32]; 747 pnode_t node; 748 uint_t flags; 749 750 /* 751 * Look for target consoles based on options node properties 752 */ 753 node = prom_optionsnode(); 754 if ((len = read_prop(node, INPUT_ALIAS, &propval)) > 0) { 755 parse(ms, propval, add_aconsole, (void *)FORINPUT); 756 kmem_free(propval, len + 1); 757 } 758 if ((len = read_prop(node, OUTPUT_ALIAS, &propval)) > 0) { 759 parse(ms, propval, add_aconsole, (void *)FOROUTPUT); 760 kmem_free(propval, len + 1); 761 } 762 763 /* 764 * Look for platform specific target consoles. 765 * Assume that they are OBP consoles and used for both input and output. 766 */ 767 flags = (uint_t)FORIO | OBPDEV; 768 if ((propval = platform_consoles(ms, dip, dev, flags)) != NULL) { 769 parse(ms, propval, add_aconsole, (void *)(uintptr_t)flags); 770 kmem_free(propval, strlen(propval) + 1); 771 } 772 773 /* 774 * Discover which consoles OBP is actually using according to 775 * interfaces proposed by case number FWARC/262. 776 */ 777 len = sizeof (devtype); 778 if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0, 779 "device_type", (caddr_t)devtype, &len) == DDI_PROP_SUCCESS && 780 strcmp(devtype, "serial") == 0 && 781 ddi_prop_exists(DDI_DEV_T_ANY, dip, 0, MUXDEVTYPE) == 1) { 782 783 (void) find_obp_consoles(ms, FORINPUT); 784 (void) find_obp_consoles(ms, FOROUTPUT); 785 786 } 787 ttymux_dprintf(DPRINT_L0, "%d consoles configured\n", 788 ms->sm_cons_cnt); 789 return (ms->sm_cons_cnt); 790 } 791 792 static int 793 validate_reservation(dacf_infohdl_t di, dev_info_t **dip, dev_t *dev, 794 io_mode_t *mode) 795 { 796 char *dname, *nname, *ipath, *opath; 797 798 if ((dname = (char *)dacf_driver_name(di)) == NULL) 799 return (EINVAL); 800 801 if ((*dip = dacf_devinfo_node(di)) == NULL) 802 return (EINVAL); 803 804 *dev = makedevice(ddi_driver_major(*dip), dacf_minor_number(di)); 805 806 if (*dev == NODEV || *dip == NULL || strcmp(dname, TTYMUX_DRVNAME) != 0) 807 return (EINVAL); 808 else if (getminor(*dev) != (minor_t)0) 809 return (EINVAL); /* minor 0 is special */ 810 else if (rconsvp != NULL || space_fetch(TTYMUXPTR) != NULL) 811 return (EAGAIN); /* already configured */ 812 813 opath = prom_stdoutpath(); 814 ipath = prom_stdinpath(); 815 nname = ddi_node_name(*dip); 816 *mode = 0; 817 818 if (ipath != NULL && strstr(ipath, nname) != 0) 819 *mode = FORINPUT; 820 if (opath != NULL && strstr(opath, nname) != 0) 821 *mode |= FOROUTPUT; 822 if ((*mode & FORIO) == 0) 823 return (EINVAL); 824 if ((*mode & FOROUTPUT) == 0) { 825 ttymux_dprintf(DPRINT_L3, 826 "Warning: multiplexer is not the output device\n"); 827 } 828 if ((*mode & FORINPUT) == 0) { 829 ttymux_dprintf(DPRINT_L3, 830 "Warning: multiplexer is not the input device\n"); 831 } 832 return (0); 833 } 834 835 /* 836 * This operation set is for configuring the ttymux driver for use as 837 * the system console. 838 * It must run before consconfig configures the Solaris console. 839 */ 840 /*ARGSUSED*/ 841 static int 842 ttymux_config(dacf_infohdl_t info_hdl, dacf_arghdl_t arg_hdl, int flags) 843 { 844 sm_mux_state_t *ms; 845 io_mode_t mode; 846 dev_t dev; 847 dev_info_t *dip; 848 uint_t i, icnt = 0, ocnt = 0; 849 int rv; 850 vnode_t *muxvp; 851 852 ttymux_dprintf(DPRINT_L0, "\n"); 853 854 if ((rv = validate_reservation(info_hdl, &dip, &dev, &mode)) != 0) { 855 ttymux_dprintf(DPRINT_L0, "reservation ignored (%d)\n", rv); 856 return (DACF_SUCCESS); 857 } 858 859 ms = kmem_zalloc(sizeof (*ms), KM_SLEEP); 860 861 mutex_init(&ms->sm_cons_mutex, NULL, MUTEX_DRIVER, NULL); 862 863 for (i = 0; i < TTYMUX_MAX_LINKS; i++) 864 ms->sm_cons_links[i].sm_dev = NODEV; 865 866 ms->sm_cons_stdin.sm_dev = ms->sm_cons_stdout.sm_dev = NODEV; 867 if (mode & FORINPUT) 868 ms->sm_cons_stdin.sm_dev = dev; 869 if (mode & FOROUTPUT) 870 ms->sm_cons_stdout.sm_dev = dev; 871 ms->sm_cons_stdin.sm_i_ihdl = prom_stdin_ihandle(); 872 ms->sm_cons_stdout.sm_o_ihdl = prom_stdout_ihandle(); 873 874 if (prom_is_openprom()) { 875 pnode_t node = prom_optionsnode(); 876 877 if (prom_getproplen(node, INPUT_ALIAS) > 0) { 878 ms->sm_ialias = kmem_alloc( 879 strlen(INPUT_ALIAS) + 1, KM_SLEEP); 880 (void) strcpy(ms->sm_ialias, INPUT_ALIAS); 881 } 882 if (prom_getproplen(node, OUTPUT_ALIAS) > 0) { 883 ms->sm_oalias = kmem_alloc( 884 strlen(OUTPUT_ALIAS) + 1, KM_SLEEP); 885 (void) strcpy(ms->sm_oalias, OUTPUT_ALIAS); 886 } 887 } 888 889 (void) find_consoles(ms, dip, dev); 890 /* Store the console list for use by the ttymux driver */ 891 if (space_store(TTYMUXPTR, (uintptr_t)ms) != 0) { 892 ttymux_dprintf(DPRINT_L3, "Named pointer error\n"); 893 if (ms->sm_ialias) 894 kmem_free(ms->sm_ialias, strlen(ms->sm_ialias) + 1); 895 if (ms->sm_oalias) 896 kmem_free(ms->sm_oalias, strlen(ms->sm_oalias) + 1); 897 for (i = 0; i < ms->sm_cons_cnt; i++) { 898 sm_console_t *cn = &ms->sm_cons_links[i]; 899 if (cn->sm_path) 900 kmem_free(cn->sm_path, strlen(cn->sm_path) + 1); 901 } 902 kmem_free(ms, sizeof (*ms)); 903 return (DACF_FAILURE); 904 } 905 906 muxvp = dacf_makevp(info_hdl); 907 908 if ((rv = VOP_OPEN(&muxvp, OFLAGS, CRED(), NULL)) == 0) { 909 910 (void) enable_all_consoles(ms, muxvp); 911 (void) usable_consoles(ms, &icnt, &ocnt); 912 913 VOP_CLOSE(muxvp, OFLAGS, 1, (offset_t)0, CRED(), NULL); 914 VN_RELE(muxvp); 915 } else { 916 ttymux_dprintf(DPRINT_L3, 917 "Error %d opening the console device\n", rv); 918 VN_RELE(muxvp); 919 return (DACF_FAILURE); 920 } 921 922 if (icnt == 0 && (mode & FORINPUT)) 923 ttymux_dprintf(DPRINT_L3, "No input consoles configured.\n"); 924 if (ocnt == 0 && (mode & FOROUTPUT)) 925 ttymux_dprintf(DPRINT_L3, "No output consoles configured.\n"); 926 927 ttymux_dprintf(DPRINT_L0, "mux config complete\n"); 928 929 return (DACF_SUCCESS); 930 }