Print this page
    
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/io/cons.c
          +++ new/usr/src/uts/common/io/cons.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright 2015, Joyent, Inc. All rights reserved.
  25   25   */
  26   26  
  27   27  /*
  28   28   * Indirect console driver for Sun.
  29   29   *
  30   30   * Redirects all I/O to the device designated as the underlying "hardware"
  31   31   * console, as given by the value of rconsvp.  The implementation assumes that
  32   32   * rconsvp denotes a STREAMS device; the assumption is justified since
  33   33   * consoles must be capable of effecting tty semantics.
  34   34   *
  35   35   * rconsvp is set in autoconf.c:consconfig(), based on information obtained
  36   36   * from the EEPROM.
  37   37   *
  38   38   * XXX: The driver still needs to be converted to use ANSI C consistently
  39   39   *      throughout.
  40   40   */
  41   41  
  42   42  #include <sys/types.h>
  43   43  #include <sys/open.h>
  44   44  #include <sys/param.h>
  45   45  #include <sys/systm.h>
  46   46  #include <sys/signal.h>
  47   47  #include <sys/cred.h>
  48   48  #include <sys/user.h>
  49   49  #include <sys/proc.h>
  50   50  #include <sys/disp.h>
  51   51  #include <sys/file.h>
  52   52  #include <sys/taskq.h>
  53   53  #include <sys/log.h>
  54   54  #include <sys/vnode.h>
  55   55  #include <sys/uio.h>
  56   56  #include <sys/stat.h>
  57   57  #include <sys/limits.h>
  58   58  
  59   59  #include <sys/console.h>
  60   60  #include <sys/consdev.h>
  61   61  
  62   62  #include <sys/stream.h>
  63   63  #include <sys/strsubr.h>
  64   64  #include <sys/poll.h>
  65   65  
  66   66  #include <sys/debug.h>
  67   67  
  68   68  #include <sys/conf.h>
  69   69  #include <sys/ddi.h>
  70   70  #include <sys/sunddi.h>
  71   71  #include <sys/vt.h>
  72   72  
  73   73  static int cnopen(dev_t *, int, int, struct cred *);
  74   74  static int cnclose(dev_t, int, int, struct cred *);
  75   75  static int cnread(dev_t, struct uio *, struct cred *);
  76   76  static int cnwrite(dev_t, struct uio *, struct cred *);
  77   77  static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *);
  78   78  static int cnpoll(dev_t, short, int, short *, struct pollhead **);
  79   79  static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  80   80  static int cn_attach(dev_info_t *, ddi_attach_cmd_t);
  81   81  static int cn_detach(dev_info_t *, ddi_detach_cmd_t);
  82   82  
  83   83  static dev_info_t *cn_dip;              /* private copy of devinfo pointer */
  84   84  
  85   85  static struct cb_ops cn_cb_ops = {
  86   86  
  87   87          cnopen,                 /* open */
  88   88          cnclose,                /* close */
  89   89          nodev,                  /* strategy */
  90   90          nodev,                  /* print */
  91   91          nodev,                  /* dump */
  92   92          cnread,                 /* read */
  93   93          cnwrite,                /* write */
  94   94          cnioctl,                /* ioctl */
  95   95          nodev,                  /* devmap */
  96   96          nodev,                  /* mmap */
  97   97          nodev,                  /* segmap */
  98   98          cnpoll,                 /* poll */
  99   99          ddi_prop_op,            /* cb_prop_op */
 100  100          0,                      /* streamtab  */
 101  101          D_NEW | D_MP            /* Driver compatibility flag */
 102  102  
 103  103  };
 104  104  
 105  105  static struct dev_ops cn_ops = {
 106  106  
 107  107          DEVO_REV,               /* devo_rev, */
 108  108          0,                      /* refcnt  */
 109  109          cn_info,                /* info */
 110  110          nulldev,                /* identify */
 111  111          nulldev,                /* probe */
 112  112          cn_attach,              /* attach */
 113  113          cn_detach,              /* detach */
 114  114          nodev,                  /* reset */
 115  115          &cn_cb_ops,             /* driver operations */
 116  116          (struct bus_ops *)0,    /* bus operations */
 117  117          NULL,                   /* power */
 118  118          ddi_quiesce_not_needed,         /* quiesce */
 119  119  
 120  120  };
 121  121  
 122  122  /*
 123  123   * Global variables associated with the console device:
 124  124   *
 125  125   * XXX: There are too many of these!
 126  126   * moved to space.c to become resident in the kernel so that cons
 127  127   * can be loadable.
 128  128   */
 129  129  
 130  130  extern dev_t    rconsdev;       /* "hardware" console */
 131  131  extern vnode_t  *rconsvp;       /* pointer to vnode for that device */
 132  132  
 133  133  /*
 134  134   * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
 135  135   */
 136  136  extern dev_t    uconsdev;       /* What the user thinks is the console device */
 137  137  
 138  138  /*
 139  139   * Private driver state:
 140  140   */
 141  141  
 142  142  /*
 143  143   * The underlying console device potentially can be opened through (at least)
 144  144   * two paths: through this driver and through the underlying device's driver.
 145  145   * To ensure that reference counts are meaningful and therefore that close
 146  146   * routines are called at the right time, it's important to make sure that
 147  147   * rconsvp's s_count field (i.e., the count on the underlying device) never
 148  148   * has a contribution of more than one through this driver, regardless of how
 149  149   * many times this driver's been opened.  rconsopen keeps track of the
 150  150   * necessary information to ensure this property.
 151  151   */
 152  152  static uint_t   rconsopen;
 153  153  
 154  154  
 155  155  #include <sys/types.h>
 156  156  #include <sys/conf.h>
 157  157  #include <sys/param.h>
 158  158  #include <sys/systm.h>
 159  159  #include <sys/errno.h>
 160  160  #include <sys/modctl.h>
 161  161  
 162  162  
 163  163  extern int nodev(), nulldev();
 164  164  extern int dseekneg_flag;
 165  165  extern struct mod_ops mod_driverops;
 166  166  extern struct dev_ops cn_ops;
 167  167  
 168  168  /*
 169  169   * Module linkage information for the kernel.
 170  170   */
 171  171  
 172  172  static struct modldrv modldrv = {
 173  173          &mod_driverops, /* Type of module.  This one is a pseudo driver */
 174  174          "Console redirection driver",
 175  175          &cn_ops,        /* driver ops */
 176  176  };
 177  177  
 178  178  static struct modlinkage modlinkage = {
 179  179          MODREV_1,
 180  180          &modldrv,
 181  181          NULL
 182  182  };
 183  183  
 184  184  int
 185  185  _init(void)
 186  186  {
 187  187          return (mod_install(&modlinkage));
 188  188  }
 189  189  
 190  190  int
 191  191  _fini(void)
 192  192  {
 193  193          return (EBUSY);
 194  194  }
 195  195  
 196  196  int
 197  197  _info(struct modinfo *modinfop)
 198  198  {
 199  199          return (mod_info(&modlinkage, modinfop));
 200  200  }
 201  201  
 202  202  /*
 203  203   * DDI glue routines
 204  204   */
 205  205  static int
 206  206  cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 207  207  {
 208  208          if (cmd != DDI_ATTACH)
 209  209                  return (DDI_FAILURE);
 210  210  
 211  211          if (ddi_create_minor_node(devi, "syscon", S_IFCHR,
 212  212              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 213  213                  return (DDI_FAILURE);
 214  214          }
 215  215          if (ddi_create_minor_node(devi, "systty", S_IFCHR,
 216  216              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 217  217                  ddi_remove_minor_node(devi, NULL);
 218  218                  return (DDI_FAILURE);
 219  219          }
 220  220          if (ddi_create_minor_node(devi, "console", S_IFCHR,
 221  221              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 222  222                  ddi_remove_minor_node(devi, NULL);
 223  223                  return (DDI_FAILURE);
 224  224          }
 225  225  
 226  226          cn_dip = devi;
 227  227          return (DDI_SUCCESS);
 228  228  }
 229  229  
 230  230  static int
 231  231  cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 232  232  {
 233  233          if (cmd != DDI_DETACH)
 234  234                  return (DDI_FAILURE);
 235  235          ddi_remove_minor_node(devi, NULL);
 236  236          uconsdev = NODEV;
 237  237          return (DDI_SUCCESS);
 238  238  }
 239  239  
 240  240  /* ARGSUSED */
 241  241  static int
 242  242  cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 243  243  {
 244  244          int error = DDI_FAILURE;
 245  245  
 246  246          switch (infocmd) {
 247  247          case DDI_INFO_DEVT2DEVINFO:
 248  248                  if (getminor((dev_t)arg) == 0 && cn_dip != NULL) {
 249  249                          *result = (void *) cn_dip;
 250  250                          error = DDI_SUCCESS;
 251  251                  }
 252  252                  break;
 253  253  
 254  254          case DDI_INFO_DEVT2INSTANCE:
 255  255                  if (getminor((dev_t)arg) == 0) {
 256  256                          *result = (void *)0;
 257  257                          error = DDI_SUCCESS;
 258  258                  }
 259  259                  break;
 260  260  
 261  261          default:
 262  262                  break;
 263  263          }
 264  264  
 265  265          return (error);
 266  266  }
 267  267  
 268  268  /*
 269  269   * XXX  Caution: before allowing more than 256 minor devices on the
 270  270   *      console, make sure you understand the 'compatibility' hack
 271  271   *      in ufs_iget() that translates old dev_t's to new dev_t's.
 272  272   *      See bugid 1098104 for the sordid details.
 273  273   */
 274  274  
 275  275  /* ARGSUSED */
 276  276  static int
 277  277  cnopen(dev_t *dev, int flag, int state, struct cred *cred)
 278  278  {
 279  279          int     err;
 280  280          static int      been_here;
 281  281          vnode_t *vp = rconsvp;
 282  282  
 283  283          ASSERT(cred != NULL);
 284  284  
 285  285          if (rconsvp == NULL)
 286  286                  return (0);
 287  287  
 288  288          /*
 289  289           * Enable virtual console I/O for console logging if needed.
 290  290           */
 291  291          if (vsconsvp != NULL && vsconsvp->v_stream == NULL) {
 292  292                  if (VOP_OPEN(&vsconsvp, FREAD | FWRITE, cred, NULL) != 0) {
 293  293                          cmn_err(CE_WARN, "cnopen: failed to open vsconsvp "
 294  294                              "for virtual console logging");
 295  295                  }
 296  296          }
 297  297  
 298  298          /*
 299  299           * XXX: Clean up inactive PIDs from previous opens if any.
 300  300           * These would have been created as a result of an I_SETSIG
 301  301           * issued against console.  This is a workaround, and
 302  302           * console driver must be correctly redesigned not to need
 303  303           * this hook.
 304  304           */
 305  305          if (vp->v_stream) {
 306  306                  str_cn_clean(vp);
 307  307          }
 308  308  
 309  309          /*
 310  310           * XXX: Set hook to tell /proc about underlying console.  (There's
 311  311           *      gotta be a better way...)
 312  312           */
 313  313          if (state != OTYP_CHR || getminor(*dev) != 0)
 314  314                  return (ENXIO);
 315  315          if (been_here == 0) {
 316  316                  uconsdev = *dev;
 317  317                  been_here = 1;
 318  318                  if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
 319  319                      0, &console_vnode, 0, 0) == 0)
 320  320                          console_taskq = taskq_create("console_taskq",
 321  321                              1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
 322  322                              LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
 323  323          }
 324  324  
 325  325          if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
 326  326                  return (err);
 327  327  
 328  328          /*
 329  329           * The underlying driver is not allowed to have cloned itself
 330  330           * for this open.
 331  331           */
 332  332          if (vp != rconsvp) {
 333  333                  /*
 334  334                   * It might happen that someone set rconsvp to NULL
 335  335                   * whilst we were in the middle of the open.
 336  336                   */
 337  337                  if (rconsvp == NULL) {
 338  338                          (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
 339  339                          return (0);
 340  340                  }
 341  341                  cmn_err(CE_PANIC, "cnopen: cloned open");
 342  342          }
 343  343  
 344  344          rconsopen++;
 345  345  
 346  346          return (0);
 347  347  }
 348  348  
 349  349  /* ARGSUSED */
 350  350  static int
 351  351  cnclose(dev_t dev, int flag, int state, struct cred *cred)
 352  352  {
 353  353          int     err = 0;
 354  354          vnode_t *vp;
 355  355  
 356  356          /*
 357  357           * Since this is the _last_ close, it's our last chance to close the
 358  358           * underlying device.  (Note that if someone else has the underlying
 359  359           * hardware console device open, we won't get here, since spec_close
 360  360           * will see s_count > 1.)
 361  361           */
 362  362          if (state != OTYP_CHR)
 363  363                  return (ENXIO);
 364  364  
 365  365          if (rconsvp == NULL)
 366  366                  return (0);
 367  367  
 368  368          while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
 369  369                  err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
 370  370                  if (!err) {
 371  371                          rconsopen--;
 372  372                  }
 373  373          }
 374  374          return (err);
 375  375  }
 376  376  
 377  377  /* ARGSUSED */
 378  378  static int
 379  379  cnread(dev_t dev, struct uio *uio, struct cred *cred)
 380  380  {
 381  381          kcondvar_t      sleep_forever;
 382  382          kmutex_t        sleep_forever_mutex;
 383  383  
 384  384          if (rconsvp == NULL) {
 385  385                  /*
 386  386                   * Go to sleep forever.  This seems like the least
 387  387                   * harmful thing to do if there's no console.
 388  388                   * EOF might be better if we're ending up single-user
 389  389                   * mode.
 390  390                   */
 391  391                  cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
 392  392                  mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
 393  393                  mutex_enter(&sleep_forever_mutex);
 394  394                  (void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
 395  395                  mutex_exit(&sleep_forever_mutex);
 396  396                  return (EIO);
 397  397          }
 398  398  
 399  399          if (rconsvp->v_stream != NULL)
 400  400                  return (strread(rconsvp, uio, cred));
 401  401          else
 402  402                  return (cdev_read(rconsdev, uio, cred));
 403  403  }
 404  404  
 405  405  /* ARGSUSED */
 406  406  static int
 407  407  cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
 408  408  {
 409  409          if (rconsvp == NULL) {
 410  410                  uio->uio_resid = 0;
 411  411                  return (0);
 412  412          }
 413  413  
 414  414          /*
 415  415           * Output to virtual console for logging if enabled.
 416  416           */
 417  417          if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
 418  418                  struiod_t uiod;
 419  419                  struct iovec buf[IOV_MAX_STACK];
 420  420                  int iovlen = 0;
 421  421  
 422  422                  if (uio->uio_iovcnt > IOV_MAX_STACK) {
 423  423                          iovlen = uio->uio_iovcnt * sizeof (iovec_t);
 424  424                          uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
 425  425                  } else {
 426  426                          uiod.d_iov = buf;
 427  427                  }
 428  428  
 429  429                  /*
 430  430                   * strwrite modifies uio so need to make copy.
 431  431                   */
 432  432                  (void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt);
 433  433  
 434  434                  (void) strwrite(vsconsvp, &uiod.d_uio, cred);
 435  435                  if (iovlen != 0)
 436  436                          kmem_free(uiod.d_iov, iovlen);
 437  437          }
 438  438  
 439  439          if (rconsvp->v_stream != NULL)
 440  440                  return (strwrite(rconsvp, uio, cred));
 441  441          else
 442  442                  return (cdev_write(rconsdev, uio, cred));
 443  443  }
 444  444  
 445  445  /* ARGSUSED */
 446  446  static int
 447  447  cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
 448  448          int *rvalp)
 449  449  {
 450  450  
 451  451          /* currently we only support one ioctl */
 452  452          if (cmd != CONS_GETTERM)
 453  453                  return (EINVAL);
 454  454  
 455  455          /* Confirm iwscn is immediate target of cn redirection */
 456  456          if (rconsvp != wsconsvp)
 457  457                  return (ENODEV);
 458  458  
 459  459          /*
 460  460           * If the redirection client is not wc, it should return
 461  461           * error upon receiving the CONS_GETTERM ioctl.
 462  462           *
 463  463           * if it is wc, we know that the target supports the CONS_GETTERM
 464  464           * ioctl, which very conviently has the exact same data
 465  465           * format as this ioctl...  so let's just pass it on.
 466  466           */
 467  467          return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
 468  468  }
 469  469  
 470  470  /* ARGSUSED */
 471  471  static int
 472  472  cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
 473  473          int *rvalp)
 474  474  {
 475  475          if (rconsvp == NULL)
 476  476                  return (0);
 477  477  
 478  478          /*
 479  479           * In wc, VT_SET_CONSUSER which comes from minor node 0
 480  480           * has two sources -- either /dev/console or /dev/vt/0 .
 481  481           * We need a way to differentiate them, so here we
 482  482           * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
 483  483           * ioctl.
 484  484           */
 485  485          if (cmd == VT_SET_CONSUSER)
 486  486                  cmd = VT_RESET_CONSUSER;
 487  487  
 488  488          if ((cmd & _CNIOC_MASK) == _CNIOC)
 489  489                  return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
 490  490  
 491  491          if (rconsvp->v_stream != NULL)
 492  492                  return (strioctl(rconsvp, cmd, arg, flag, U_TO_K,
 493  493                      cred, rvalp));
 494  494  
 495  495          return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
 496  496  }
 497  497  
 498  498  /* ARGSUSED */
 499  499  static int
 500  500  cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
 501  501          struct pollhead **phpp)
 502  502  {
 503  503          if (rconsvp == NULL)
 504  504                  return (nochpoll(dev, events, anyyet, reventsp, phpp));
 505  505  
 506  506          if (rconsvp->v_stream != NULL)
 507  507                  return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
 508  508                      phpp));
 509  509          else
 510  510                  return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
 511  511  }
  
    | 
      ↓ open down ↓ | 
    511 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX