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 }