1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/param.h>
  28 #include <sys/signal.h>
  29 #include <sys/cred.h>
  30 #include <sys/vnode.h>
  31 #include <sys/termios.h>
  32 #include <sys/termio.h>
  33 #include <sys/ttold.h>
  34 #include <sys/stropts.h>
  35 #include <sys/stream.h>
  36 #include <sys/strsun.h>
  37 #include <sys/tty.h>
  38 #include <sys/buf.h>
  39 #include <sys/uio.h>
  40 #include <sys/stat.h>
  41 #include <sys/sysmacros.h>
  42 #include <sys/errno.h>
  43 #include <sys/proc.h>
  44 #include <sys/procset.h>
  45 #include <sys/fault.h>
  46 #include <sys/siginfo.h>
  47 #include <sys/debug.h>
  48 #include <sys/kd.h>
  49 #include <sys/vt.h>
  50 #include <sys/vtdaemon.h>
  51 #include <sys/session.h>
  52 #include <sys/door.h>
  53 #include <sys/kmem.h>
  54 #include <sys/cpuvar.h>
  55 #include <sys/kbio.h>
  56 #include <sys/strredir.h>
  57 #include <sys/fs/snode.h>
  58 #include <sys/consdev.h>
  59 #include <sys/conf.h>
  60 #include <sys/cmn_err.h>
  61 #include <sys/console.h>
  62 #include <sys/promif.h>
  63 #include <sys/note.h>
  64 #include <sys/polled_io.h>
  65 #include <sys/systm.h>
  66 #include <sys/ddi.h>
  67 #include <sys/sunddi.h>
  68 #include <sys/sunndi.h>
  69 #include <sys/esunddi.h>
  70 #include <sys/sunldi.h>
  71 #include <sys/debug.h>
  72 #include <sys/console.h>
  73 #include <sys/ddi_impldefs.h>
  74 #include <sys/policy.h>
  75 #include <sys/tem.h>
  76 #include <sys/wscons.h>
  77 #include <sys/systm.h>
  78 #include <sys/modctl.h>
  79 #include <sys/vt_impl.h>
  80 #include <sys/consconfig_dacf.h>
  81 
  82 /*
  83  * This file belongs to wc STREAMS module which has a D_MTPERMODE
  84  * inner perimeter. See "Locking Policy" comment in wscons.c for
  85  * more information.
  86  */
  87 
  88 /*
  89  * Minor        name            device file             Hotkeys
  90  *
  91  * 0    the system console      /dev/console            Alt + F1
  92  * 0:   virtual console #1      /dev/vt/0               Alt + F1
  93  *
  94  * 2:   virtual console #2      /dev/vt/2               Alt + F2
  95  * 3:   virtual console #3      /dev/vt/3               Alt + F3
  96  * ......
  97  * n:   virtual console #n      /dev/vt/n               Alt + Fn
  98  *
  99  * Note that vtdaemon is running on /dev/vt/1 (minor=1),
 100  * which is not available to end users.
 101  *
 102  */
 103 
 104 #define VT_DAEMON_MINOR 1
 105 #define VT_IS_DAEMON(minor)     ((minor) == VT_DAEMON_MINOR)
 106 
 107 extern void     wc_get_size(vc_state_t *pvc);
 108 extern boolean_t consconfig_console_is_tipline(void);
 109 
 110 
 111 minor_t vc_last_console = VT_MINOR_INVALID;     /* the last used console */
 112 volatile uint_t vc_target_console;              /* arg (1..n) */
 113 
 114 static volatile minor_t vc_inuse_max_minor = 0;
 115 static list_t vc_waitactive_list;
 116 
 117 static int vt_pending_vtno = -1;
 118 kmutex_t vt_pending_vtno_lock;
 119 
 120 static int vt_activate(uint_t vt_no, cred_t *credp);
 121 static void vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size);
 122 static void vt_copyin(queue_t *qp, mblk_t *mp, uint_t size);
 123 static void vt_iocnak(queue_t *qp, mblk_t *mp, int error);
 124 static void vt_iocack(queue_t *qp, mblk_t *mp);
 125 
 126 static uint_t vt_minor2arg(minor_t minor);
 127 static minor_t vt_arg2minor(uint_t arg);
 128 
 129 /*
 130  * If the system console is directed to tipline, consider /dev/vt/0 as
 131  * not being used.
 132  * For other VT, if it is opened and tty is initialized, consider it
 133  * as being used.
 134  */
 135 #define VT_IS_INUSE(id)                                         \
 136         (((vt_minor2vc(id))->vc_flags & WCS_ISOPEN) &&           \
 137         ((vt_minor2vc(id))->vc_flags & WCS_INIT) &&              \
 138         (id != 0 || !consconfig_console_is_tipline()))
 139 
 140 /*
 141  * the vt switching message is encoded as:
 142  *
 143  *   -------------------------------------------------------------
 144  *   |  \033  |  'Q'  |  vtno + 'A'  |  opcode  |  'z'  |  '\0'  |
 145  *   -------------------------------------------------------------
 146  */
 147 #define VT_MSG_SWITCH(mp)                                       \
 148         ((int)((mp)->b_wptr - (mp)->b_rptr) >= 5 &&            \
 149         *((mp)->b_rptr) == '\033' &&                         \
 150         *((mp)->b_rptr + 1) == 'Q' &&                                \
 151         *((mp)->b_rptr + 4) == 'z')
 152 
 153 #define VT_MSG_VTNO(mp)         (*((mp)->b_rptr + 2) - 'A')
 154 #define VT_MSG_OPCODE(mp)       (*((mp)->b_rptr + 3))
 155 
 156 #define VT_DOORCALL_MAX_RETRY   3
 157 
 158 static void
 159 vt_init_ttycommon(tty_common_t *pcommon)
 160 {
 161         struct termios *termiosp;
 162         int len;
 163 
 164         mutex_init(&pcommon->t_excl, NULL, MUTEX_DEFAULT, NULL);
 165         pcommon->t_iflag = 0;
 166 
 167         /*
 168          * Get the default termios settings (cflag).
 169          * These are stored as a property in the
 170          * "options" node.
 171          */
 172         if (ddi_getlongprop(DDI_DEV_T_ANY,
 173             ddi_root_node(), 0, "ttymodes",
 174             (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS) {
 175 
 176                 if (len == sizeof (struct termios))
 177                         pcommon->t_cflag = termiosp->c_cflag;
 178                 else
 179                         cmn_err(CE_WARN,
 180                             "wc: Couldn't get ttymodes property!");
 181 
 182                 kmem_free(termiosp, len);
 183         } else {
 184                 /*
 185                  * Gack!  Whine about it.
 186                  */
 187                 cmn_err(CE_WARN,
 188                     "wc: Couldn't get ttymodes property!");
 189         }
 190 
 191         pcommon->t_iocpending = NULL;
 192 }
 193 
 194 static int
 195 vt_config(uint_t count)
 196 {
 197         if (consmode != CONS_KFB)
 198                 return (ENOTSUP);
 199 
 200         /* one for system console, one for vtdaemon */
 201         if (count < 2)
 202                 return (ENXIO);
 203 
 204         /*
 205          * Shouldn't allow to shrink the max vt minor to be smaller than
 206          * the max in used minor.
 207          */
 208         if (count <= vc_inuse_max_minor)
 209                 return (EBUSY);
 210 
 211         mutex_enter(&vc_lock);
 212         vt_resize(count);
 213         mutex_exit(&vc_lock);
 214 
 215         return (0);
 216 }
 217 
 218 void
 219 vt_clean(queue_t *q, vc_state_t *pvc)
 220 {
 221         ASSERT(MUTEX_HELD(&pvc->vc_state_lock));
 222 
 223         if (pvc->vc_bufcallid != 0) {
 224                 qunbufcall(q, pvc->vc_bufcallid);
 225                 pvc->vc_bufcallid = 0;
 226         }
 227         if (pvc->vc_timeoutid != 0) {
 228                 (void) quntimeout(q, pvc->vc_timeoutid);
 229                 pvc->vc_timeoutid = 0;
 230         }
 231         ttycommon_close(&pvc->vc_ttycommon);
 232 
 233         pvc->vc_flags &= ~WCS_INIT;
 234 }
 235 
 236 /*
 237  * Reply the VT_WAITACTIVE ioctl.
 238  * Argument 'close' usage:
 239  * B_TRUE:  the vt designated by argument 'minor' is being closed.
 240  * B_FALSE: the vt designated by argument 'minor' has been activated just now.
 241  */
 242 static void
 243 vc_waitactive_reply(int minor, boolean_t close)
 244 {
 245         vc_waitactive_msg_t *index, *tmp;
 246         vc_state_t *pvc;
 247 
 248         index = list_head(&vc_waitactive_list);
 249 
 250         while (index != NULL) {
 251                 tmp = index;
 252                 index = list_next(&vc_waitactive_list, index);
 253 
 254                 if ((close && tmp->wa_msg_minor == minor) ||
 255                     (!close && tmp->wa_wait_minor == minor)) {
 256                         list_remove(&vc_waitactive_list, tmp);
 257                         pvc = vt_minor2vc(tmp->wa_msg_minor);
 258 
 259                         if (close)
 260                                 vt_iocnak(pvc->vc_wq, tmp->wa_mp, ENXIO);
 261                         else
 262                                 vt_iocack(pvc->vc_wq, tmp->wa_mp);
 263 
 264                         kmem_free(tmp, sizeof (vc_waitactive_msg_t));
 265                 }
 266         }
 267 }
 268 
 269 void
 270 vt_close(queue_t *q, vc_state_t *pvc, cred_t *credp)
 271 {
 272         minor_t index;
 273 
 274         mutex_enter(&pvc->vc_state_lock);
 275         vt_clean(q, pvc);
 276         pvc->vc_flags &= ~WCS_ISOPEN;
 277         mutex_exit(&pvc->vc_state_lock);
 278 
 279         tem_destroy(pvc->vc_tem, credp);
 280         pvc->vc_tem = NULL;
 281 
 282         index = pvc->vc_minor;
 283         if (index == vc_inuse_max_minor) {
 284                 while ((--index > 0) && !VT_IS_INUSE(index))
 285                         ;
 286                 vc_inuse_max_minor = index;
 287         }
 288 
 289         vc_waitactive_reply(pvc->vc_minor, B_TRUE);
 290 }
 291 
 292 static void
 293 vt_init_tty(vc_state_t *pvc)
 294 {
 295         ASSERT(MUTEX_HELD(&pvc->vc_state_lock));
 296 
 297         pvc->vc_flags |= WCS_INIT;
 298         vt_init_ttycommon(&pvc->vc_ttycommon);
 299         wc_get_size(pvc);
 300 }
 301 
 302 /*
 303  * minor 0:     /dev/vt/0       (index = 0, indicating the system console)
 304  * minor 1:     /dev/vt/1       (index = 1, vtdaemon special console)
 305  * minor 2:     /dev/vt/2       (index = 2, virtual consoles)
 306  * ......
 307  * minor n:     /dev/vt/n       (index = n)
 308  *
 309  *
 310  * The system console (minor 0), is opened firstly and used during console
 311  * configuration.  It also acts as the system hard console even when all
 312  * virtual consoles go off.
 313  *
 314  * In tipline case, minor 0 (/dev/vt/0) is reserved, and cannot be switched to.
 315  * And the system console is redirected to the tipline. During normal cases,
 316  * we can switch from virtual consoles to it by pressing 'Alt + F1'.
 317  *
 318  * minor 1 (/dev/vt/1) is reserved for vtdaemon special console, and it's
 319  * not available to end users.
 320  *
 321  * During early console configuration, consconfig_dacf opens wscons and then
 322  * issue a WC_OPEN_FB ioctl to kick off terminal init process. So during
 323  * consconfig_dacf first opening of wscons, tems (of type tem_state_t) is
 324  * not initialized. We do not initialize the tem_vt_state_t instance returned
 325  * by tem_init() for this open, since we do not have enough info to handle
 326  * normal terminal operation at this moment. This tem_vt_state_t instance
 327  * will get initialized when handling WC_OPEN_FB.
 328  */
 329 int
 330 vt_open(minor_t minor, queue_t *rq, cred_t *crp)
 331 {
 332         vc_state_t *pvc;
 333 
 334         if (!vt_minor_valid(minor))
 335                 return (ENXIO);
 336 
 337         pvc = vt_minor2vc(minor);
 338         if (pvc == NULL)
 339                 return (ENXIO);
 340 
 341         mutex_enter(&vc_lock);
 342         mutex_enter(&pvc->vc_state_lock);
 343 
 344         if (!(pvc->vc_flags & WCS_ISOPEN)) {
 345                 /*
 346                  * vc_tem might not be intialized if !tems.ts_initialized,
 347                  * and this only happens during console configuration.
 348                  */
 349                 pvc->vc_tem = tem_init(crp);
 350         }
 351 
 352         if (!(pvc->vc_flags & WCS_INIT))
 353                 vt_init_tty(pvc);
 354 
 355         /*
 356          * In normal case, the first screen is the system console;
 357          * In tipline case, the first screen is the first VT that gets started.
 358          */
 359         if (vc_active_console == VT_MINOR_INVALID && minor != VT_DAEMON_MINOR)
 360                 if (minor == 0 || consmode == CONS_KFB) {
 361                         boolean_t unblank = B_FALSE;
 362 
 363                         vc_active_console = minor;
 364                         vc_last_console = minor;
 365                         if (minor != 0) {
 366                                 /*
 367                                  * If we are not opening the system console
 368                                  * as the first console, clear the phyical
 369                                  * screen.
 370                                  */
 371                                 unblank = B_TRUE;
 372                         }
 373 
 374                         tem_activate(pvc->vc_tem, unblank, crp);
 375                 }
 376 
 377         if ((pvc->vc_ttycommon.t_flags & TS_XCLUDE) &&
 378             (secpolicy_excl_open(crp) != 0)) {
 379                 mutex_exit(&pvc->vc_state_lock);
 380                 mutex_exit(&vc_lock);
 381                 return (EBUSY);
 382         }
 383 
 384         if (minor > vc_inuse_max_minor)
 385                 vc_inuse_max_minor = minor;
 386 
 387         pvc->vc_flags |= WCS_ISOPEN;
 388         pvc->vc_ttycommon.t_readq = rq;
 389         pvc->vc_ttycommon.t_writeq = WR(rq);
 390 
 391         mutex_exit(&pvc->vc_state_lock);
 392         mutex_exit(&vc_lock);
 393 
 394         rq->q_ptr = pvc;
 395         WR(rq)->q_ptr = pvc;
 396         pvc->vc_wq = WR(rq);
 397 
 398         qprocson(rq);
 399         return (0);
 400 }
 401 
 402 static minor_t
 403 vt_find_prev(minor_t cur)
 404 {
 405         minor_t i, t, max;
 406 
 407         ASSERT(vc_active_console != VT_MINOR_INVALID);
 408 
 409         max = VC_INSTANCES_COUNT;
 410 
 411         for (i = cur - 1; (t = (i + max) % max) != cur; i--)
 412                 if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t))
 413                         return (t);
 414 
 415         return (VT_MINOR_INVALID);
 416 }
 417 
 418 static minor_t
 419 vt_find_next(minor_t cur)
 420 {
 421         minor_t i, t, max;
 422 
 423         ASSERT(vc_active_console != VT_MINOR_INVALID);
 424 
 425         max = VC_INSTANCES_COUNT;
 426 
 427         for (i = cur + 1; (t = (i + max) % max) != cur; i++)
 428                 if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t))
 429                         return (t);
 430 
 431         return (VT_MINOR_INVALID);
 432 }
 433 
 434 /* ARGSUSED */
 435 void
 436 vt_send_hotkeys(void *timeout_arg)
 437 {
 438         door_handle_t door;
 439         vt_cmd_arg_t arg;
 440         int error = 0;
 441         int retries = 0;
 442         door_arg_t door_arg;
 443 
 444         arg.vt_ev = VT_EV_HOTKEYS;
 445 
 446         mutex_enter(&vt_pending_vtno_lock);
 447         arg.vt_num = vt_pending_vtno;
 448         mutex_exit(&vt_pending_vtno_lock);
 449 
 450         /* only available in kernel context or user context */
 451         if (door_ki_open(VT_DAEMON_DOOR_FILE, &door) != 0) {
 452                 mutex_enter(&vt_pending_vtno_lock);
 453                 vt_pending_vtno = -1;
 454                 mutex_exit(&vt_pending_vtno_lock);
 455                 return;
 456         }
 457 
 458         door_arg.rbuf = NULL;
 459         door_arg.rsize = 0;
 460         door_arg.data_ptr = (void *)&arg;
 461         door_arg.data_size = sizeof (arg);
 462         door_arg.desc_ptr = NULL;
 463         door_arg.desc_num = 0;
 464 
 465         /*
 466          * Make door upcall
 467          */
 468         while ((error = door_ki_upcall(door, &door_arg)) != 0 &&
 469             retries < VT_DOORCALL_MAX_RETRY)
 470                 if (error == EAGAIN || error == EINTR)
 471                         retries++;
 472                 else
 473                         break;
 474 
 475         door_ki_rele(door);
 476 
 477         mutex_enter(&vt_pending_vtno_lock);
 478         vt_pending_vtno = -1;
 479         mutex_exit(&vt_pending_vtno_lock);
 480 }
 481 
 482 static boolean_t
 483 vt_validate_hotkeys(int minor)
 484 {
 485         /*
 486          * minor should not succeed the existing minor numbers range.
 487          */
 488         if (!vt_minor_valid(minor))
 489                 return (B_FALSE);
 490 
 491         /*
 492          * Shouldn't switch to /dev/vt/1 or an unused vt.
 493          */
 494         if (!VT_IS_DAEMON(minor) && VT_IS_INUSE(minor))
 495                 return (B_TRUE);
 496 
 497         return (B_FALSE);
 498 }
 499 
 500 static void
 501 vt_trigger_hotkeys(int vtno)
 502 {
 503         mutex_enter(&vt_pending_vtno_lock);
 504 
 505         if (vt_pending_vtno != -1) {
 506                 mutex_exit(&vt_pending_vtno_lock);
 507                 return;
 508         }
 509 
 510         vt_pending_vtno = vtno;
 511         mutex_exit(&vt_pending_vtno_lock);
 512         (void) timeout(vt_send_hotkeys, NULL, 1);
 513 }
 514 
 515 /*
 516  * return value:
 517  *    0:    non msg of vt hotkeys
 518  *    1:    msg of vt hotkeys
 519  */
 520 int
 521 vt_check_hotkeys(mblk_t *mp)
 522 {
 523         int vtno = 0;
 524         minor_t minor = 0;
 525 
 526         /* LINTED E_PTRDIFF_OVERFLOW */
 527         if (!VT_MSG_SWITCH(mp))
 528                 return (0);
 529 
 530         switch (VT_MSG_OPCODE(mp)) {
 531         case 'B':
 532                 /* find out the previous vt */
 533                 if (vc_active_console == VT_MINOR_INVALID)
 534                         return (1);
 535 
 536                 if (VT_IS_DAEMON(vc_active_console)) {
 537                         minor = vt_find_prev(vt_arg2minor(vc_target_console));
 538                         break;
 539                 }
 540 
 541                 minor = vt_find_prev(vc_active_console);
 542                 break;
 543         case 'F':
 544                 /* find out the next vt */
 545                 if (vc_active_console == VT_MINOR_INVALID)
 546                         return (1);
 547 
 548                 if (VT_IS_DAEMON(vc_active_console)) {
 549                         minor = vt_find_next(vt_arg2minor(vc_target_console));
 550                         break;
 551                 }
 552 
 553                 minor = vt_find_next(vc_active_console);
 554                 break;
 555         case 'H':
 556                 /* find out the specified vt */
 557                 minor = VT_MSG_VTNO(mp);
 558 
 559                 /* check for system console, Alt + F1 */
 560                 if (minor == 1)
 561                         minor = 0;
 562                 break;
 563         case 'L':
 564                 /* find out the last vt */
 565                 if ((minor = vc_last_console) == VT_MINOR_INVALID)
 566                         return (1);
 567                 break;
 568         default:
 569                 return (1);
 570         }
 571 
 572         if (!vt_validate_hotkeys(minor))
 573                 return (1);
 574 
 575         /*
 576          * for system console, the argument of vtno for
 577          * vt_activate is 1, though its minor is 0
 578          */
 579         if (minor == 0)
 580                 vtno = 1;       /* for system console */
 581         else
 582                 vtno = minor;
 583 
 584         vt_trigger_hotkeys(vtno);
 585         return (1);
 586 }
 587 
 588 static void
 589 vt_proc_sendsig(pid_t pid, int sig)
 590 {
 591         register proc_t *p;
 592 
 593         if (pid <= 0)
 594                 return;
 595 
 596         mutex_enter(&pidlock);
 597         if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
 598                 mutex_exit(&pidlock);
 599                 return;
 600         }
 601 
 602         psignal(p, sig);
 603         mutex_exit(&pidlock);
 604 }
 605 
 606 static int
 607 vt_proc_exists(pid_t pid)
 608 {
 609         register proc_t *p;
 610 
 611         if (pid <= 0)
 612                 return (EINVAL);
 613 
 614         mutex_enter(&pidlock);
 615         if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
 616                 mutex_exit(&pidlock);
 617                 return (ESRCH);
 618         }
 619         mutex_exit(&pidlock);
 620 
 621         return (0);
 622 }
 623 
 624 #define SIG_VALID(x)    (((x) > 0) && ((x) <= MAXSIG) && \
 625                         ((x) != SIGKILL) && ((x) != SIGSTOP))
 626 
 627 static int
 628 vt_setmode(vc_state_t *pvc, struct vt_mode *pmode)
 629 {
 630         if ((pmode->mode != VT_PROCESS) && (pmode->mode != VT_AUTO))
 631                 return (EINVAL);
 632 
 633         if (!SIG_VALID(pmode->relsig) || !SIG_VALID(pmode->acqsig))
 634                 return (EINVAL);
 635 
 636         if (pmode->mode == VT_PROCESS) {
 637                 pvc->vc_pid = curproc->p_pid;
 638         } else {
 639                 pvc->vc_dispnum = 0;
 640                 pvc->vc_login = 0;
 641         }
 642 
 643         pvc->vc_switch_mode = pmode->mode;
 644         pvc->vc_waitv = pmode->waitv;
 645         pvc->vc_relsig = pmode->relsig;
 646         pvc->vc_acqsig = pmode->acqsig;
 647 
 648         return (0);
 649 }
 650 
 651 static void
 652 vt_reset(vc_state_t *pvc)
 653 {
 654         pvc->vc_switch_mode = VT_AUTO;
 655         pvc->vc_pid = -1;
 656         pvc->vc_dispnum = 0;
 657         pvc->vc_login = 0;
 658         pvc->vc_switchto = VT_MINOR_INVALID;
 659 }
 660 
 661 /*
 662  * switch to vt_no from vc_active_console
 663  */
 664 static int
 665 vt_switch(uint_t vt_no, cred_t *credp)
 666 {
 667         vc_state_t *pvc_active = vt_minor2vc(vc_active_console);
 668         vc_state_t *pvc = vt_minor2vc(vt_no);
 669         minor_t index;
 670 
 671         ASSERT(pvc_active && pvc);
 672 
 673         /* sanity test for the target VT and the active VT */
 674         if (!((pvc->vc_flags & WCS_ISOPEN) && (pvc->vc_flags & WCS_INIT)))
 675                 return (EINVAL);
 676 
 677         if (!((pvc_active->vc_flags & WCS_ISOPEN) &&
 678             (pvc_active->vc_flags & WCS_INIT)))
 679                 return (EINVAL);
 680 
 681         mutex_enter(&vc_lock);
 682 
 683         tem_switch(pvc_active->vc_tem, pvc->vc_tem, credp);
 684 
 685         if (!VT_IS_DAEMON(vc_active_console))
 686                 vc_last_console = vc_active_console;
 687         else
 688                 vc_last_console = vt_arg2minor(vc_target_console);
 689 
 690         vc_active_console = pvc->vc_minor;
 691 
 692         if (pvc->vc_switch_mode == VT_PROCESS) {
 693                 pvc->vc_switchto = pvc->vc_minor;
 694 
 695                 /* send it an acquired signal */
 696                 vt_proc_sendsig(pvc->vc_pid, pvc->vc_acqsig);
 697         }
 698 
 699         vc_waitactive_reply(vc_active_console, B_FALSE);
 700 
 701         mutex_exit(&vc_lock);
 702 
 703         if (!VT_IS_DAEMON(vt_no)) {
 704                 /*
 705                  * Applications that open the virtual console device may request
 706                  * asynchronous notification of VT switching from a previous VT
 707                  * to another one by setting the S_MSG flag in an I_SETSIG
 708                  * STREAMS ioctl. Such processes receive a SIGPOLL signal when
 709                  * a VT switching succeeds.
 710                  */
 711                 for (index = 0; index < VC_INSTANCES_COUNT; index++) {
 712                         vc_state_t *tmp_pvc = vt_minor2vc(index);
 713                         mblk_t *mp;
 714 
 715                         if ((tmp_pvc->vc_flags & WCS_ISOPEN) &&
 716                             (tmp_pvc->vc_flags & WCS_INIT) &&
 717                             (mp = allocb(sizeof (unsigned char), BPRI_HI))) {
 718                                 mp->b_datap->db_type = M_PCSIG;
 719                                 *mp->b_wptr = SIGPOLL;
 720                                 mp->b_wptr += sizeof (unsigned char);
 721                                 putnext(RD(tmp_pvc->vc_wq), mp);
 722                         }
 723                 }
 724         }
 725 
 726         return (0);
 727 
 728 }
 729 
 730 /*
 731  * vt_no        from 0 to n
 732  *
 733  * 0    for the vtdaemon sepcial console (only vtdaemon will use it)
 734  * 1    for the system console (Alt + F1, or Alt + Ctrl + F1),
 735  *      aka Virtual Console #1
 736  *
 737  * 2    for Virtual Console #2
 738  * n    for Virtual Console #n
 739  */
 740 static minor_t
 741 vt_arg2minor(uint_t arg)
 742 {
 743         if (arg == 0)
 744                 return (1);
 745 
 746         if (arg == 1)
 747                 return (0);
 748 
 749         return (arg);
 750 }
 751 
 752 static uint_t
 753 vt_minor2arg(minor_t minor)
 754 {
 755         if (minor == 0)
 756                 return (1);
 757 
 758         if (VT_IS_DAEMON(minor)) {
 759                 /* here it should be the real console */
 760                 return (vc_target_console);
 761         }
 762 
 763         return (minor);
 764 }
 765 
 766 static int
 767 vt_activate(uint_t vt_no, cred_t *credp)
 768 {
 769         vc_state_t *pvc;
 770         minor_t minor;
 771 
 772         minor = vt_arg2minor(vt_no);
 773         if (!vt_minor_valid(minor))
 774                 return (ENXIO);
 775         if (minor == vc_active_console) {
 776                 if (VT_IS_DAEMON(minor)) {
 777                         /*
 778                          * vtdaemon is reactivating itself to do locking
 779                          * on behalf of another console, so record current
 780                          * target console as the last console.
 781                          */
 782                         vc_last_console = vt_arg2minor(vc_target_console);
 783                 }
 784 
 785                 return (0);
 786         }
 787 
 788         /*
 789          * In tipline case, the system console is redirected to tipline
 790          * and thus is always available.
 791          */
 792         if (minor == 0 && consconfig_console_is_tipline())
 793                 return (0);
 794 
 795         if (!VT_IS_INUSE(minor))
 796                 return (ENXIO);
 797 
 798         pvc = vt_minor2vc(minor);
 799         if (pvc == NULL)
 800                 return (ENXIO);
 801         if (pvc->vc_tem == NULL)
 802                 return (ENXIO);
 803 
 804         pvc = vt_minor2vc(vc_active_console);
 805         if (pvc == NULL)
 806                 return (ENXIO);
 807         if (pvc->vc_switch_mode != VT_PROCESS)
 808                 return (vt_switch(minor, credp));
 809 
 810         /*
 811          * Validate the process, reset the
 812          * vt to auto mode if failed.
 813          */
 814         if (pvc->vc_pid == -1 || vt_proc_exists(pvc->vc_pid) != 0) {
 815                 /*
 816                  * Xserver has not started up yet,
 817                  * or it dose not exist.
 818                  */
 819                 vt_reset(pvc);
 820                 return (0);
 821         }
 822 
 823         /*
 824          * Send the release signal to the process,
 825          * and wait VT_RELDISP ioctl from Xserver
 826          * after its leaving VT.
 827          */
 828         vt_proc_sendsig(pvc->vc_pid, pvc->vc_relsig);
 829         pvc->vc_switchto = minor;
 830 
 831         /*
 832          * We don't need a timeout here, for if Xserver refuses
 833          * or fails to respond to release signal using VT_RELDISP,
 834          * we cannot successfully switch to our text mode. Actually
 835          * users can try again. At present we don't support force
 836          * switch.
 837          */
 838         return (0);
 839 }
 840 
 841 static int
 842 vt_reldisp(vc_state_t *pvc, int arg, cred_t *credp)
 843 {
 844         minor_t target_vtno = pvc->vc_switchto;
 845 
 846         if ((pvc->vc_switch_mode != VT_PROCESS) ||
 847             (pvc->vc_minor != vc_active_console))
 848                 return (EACCES);
 849 
 850         if (target_vtno == VT_MINOR_INVALID)
 851                 return (EINVAL);
 852 
 853         pvc->vc_switchto = VT_MINOR_INVALID;
 854 
 855         if (arg == VT_ACKACQ)
 856                 return (0);
 857 
 858         if (arg == 0)
 859                 return (0); /* refuse to release */
 860 
 861         /* Xserver has left VT */
 862         return (vt_switch(target_vtno, credp));
 863 }
 864 
 865 void
 866 vt_ioctl(queue_t *q, mblk_t *mp)
 867 {
 868         vc_state_t *pvc = (vc_state_t *)q->q_ptr;
 869         struct iocblk   *iocp;
 870         struct vt_mode vtmode;
 871         struct vt_stat vtinfo;
 872         struct vt_dispinfo vtdisp;
 873         mblk_t *tmp;
 874         int minor;
 875         int arg;
 876         int error = 0;
 877         vc_waitactive_msg_t *wait_msg;
 878 
 879         iocp = (struct iocblk *)(void *)mp->b_rptr;
 880         if (consmode != CONS_KFB && iocp->ioc_cmd != VT_ENABLED) {
 881                 vt_iocnak(q, mp, EINVAL);
 882                 return;
 883         }
 884 
 885         switch (iocp->ioc_cmd) {
 886         case VT_ENABLED:
 887                 if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
 888                         error = ENOMEM;
 889                         break;
 890                 }
 891                 *(int *)(void *)tmp->b_rptr = consmode;
 892                 tmp->b_wptr += sizeof (int);
 893                 vt_copyout(q, mp, tmp, sizeof (int));
 894                 return;
 895 
 896         case KDSETMODE:
 897                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
 898                 if (arg != KD_TEXT && arg != KD_GRAPHICS) {
 899                         error = EINVAL;
 900                         break;
 901                 }
 902                 if (tem_get_fbmode(pvc->vc_tem) == arg)
 903                         break;
 904 
 905                 tem_set_fbmode(pvc->vc_tem, (uchar_t)arg, iocp->ioc_cr);
 906 
 907                 break;
 908 
 909         case KDGETMODE:
 910                 if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
 911                         error = ENOMEM;
 912                         break;
 913                 }
 914                 *(int *)(void *)tmp->b_rptr = tem_get_fbmode(pvc->vc_tem);
 915                 tmp->b_wptr += sizeof (int);
 916                 vt_copyout(q, mp, tmp, sizeof (int));
 917                 return;
 918 
 919         case VT_OPENQRY: /* return number of first free VT */
 920                 if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
 921                         error = ENOMEM;
 922                         break;
 923                 }
 924 
 925                 /* minors of 0 and 1 are not available to end users */
 926                 for (minor = 2; vt_minor_valid(minor); minor++)
 927                         if (!VT_IS_INUSE(minor))
 928                                 break;
 929 
 930                 if (!vt_minor_valid(minor))
 931                         minor = -1;
 932                 *(int *)(void *)tmp->b_rptr = minor; /* /dev/vt/minor */
 933                 tmp->b_wptr += sizeof (int);
 934                 vt_copyout(q, mp, tmp, sizeof (int));
 935                 return;
 936 
 937         case VT_GETMODE:
 938                 vtmode.mode = pvc->vc_switch_mode;
 939                 vtmode.waitv = pvc->vc_waitv;
 940                 vtmode.relsig = pvc->vc_relsig;
 941                 vtmode.acqsig = pvc->vc_acqsig;
 942                 vtmode.frsig = 0;
 943                 if (!(tmp = allocb(sizeof (struct vt_mode), BPRI_MED))) {
 944                         error = ENOMEM;
 945                         break;
 946                 }
 947                 *(struct vt_mode *)(void *)tmp->b_rptr = vtmode;
 948                 tmp->b_wptr += sizeof (struct vt_mode);
 949                 vt_copyout(q, mp, tmp, sizeof (struct vt_mode));
 950                 return;
 951 
 952         case VT_SETMODE:
 953                 vt_copyin(q, mp, sizeof (struct vt_mode));
 954                 return;
 955 
 956         case VT_SETDISPINFO:
 957                 /* always enforce sys_devices privilege for setdispinfo */
 958                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
 959                         break;
 960 
 961                 pvc->vc_dispnum = *(intptr_t *)(void *)mp->b_cont->b_rptr;
 962                 break;
 963 
 964         case VT_SETDISPLOGIN:
 965                 pvc->vc_login = *(intptr_t *)(void *)mp->b_cont->b_rptr;
 966                 break;
 967 
 968         case VT_GETDISPINFO:
 969                 vtdisp.v_pid = pvc->vc_pid;
 970                 vtdisp.v_dispnum = pvc->vc_dispnum;
 971                 vtdisp.v_login = pvc->vc_login;
 972                 if (!(tmp = allocb(sizeof (struct vt_dispinfo), BPRI_MED))) {
 973                         error = ENOMEM;
 974                         break;
 975                 }
 976                 *(struct vt_dispinfo *)(void *)tmp->b_rptr = vtdisp;
 977                 tmp->b_wptr += sizeof (struct vt_dispinfo);
 978                 vt_copyout(q, mp, tmp, sizeof (struct vt_dispinfo));
 979                 return;
 980 
 981         case VT_RELDISP:
 982                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
 983                 error = vt_reldisp(pvc, arg, iocp->ioc_cr);
 984                 break;
 985 
 986         case VT_CONFIG:
 987                 /* always enforce sys_devices privilege for config */
 988                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
 989                         break;
 990 
 991                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
 992                 error = vt_config(arg);
 993                 break;
 994 
 995         case VT_ACTIVATE:
 996                 /* always enforce sys_devices privilege for secure switch */
 997                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
 998                         break;
 999 
1000                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
1001                 error = vt_activate(arg, iocp->ioc_cr);
1002                 break;
1003 
1004         case VT_WAITACTIVE:
1005                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
1006                 arg = vt_arg2minor(arg);
1007                 if (!vt_minor_valid(arg)) {
1008                         error = ENXIO;
1009                         break;
1010                 }
1011                 if (arg == vc_active_console)
1012                         break;
1013 
1014                 wait_msg = kmem_zalloc(sizeof (vc_waitactive_msg_t),
1015                     KM_NOSLEEP);
1016                 if (wait_msg == NULL) {
1017                         error = ENXIO;
1018                         break;
1019                 }
1020 
1021                 wait_msg->wa_mp = mp;
1022                 wait_msg->wa_msg_minor = pvc->vc_minor;
1023                 wait_msg->wa_wait_minor = arg;
1024                 list_insert_head(&vc_waitactive_list, wait_msg);
1025 
1026                 return;
1027 
1028         case VT_GETSTATE:
1029                 /*
1030                  * Here v_active is the argument for vt_activate,
1031                  * not minor.
1032                  */
1033                 vtinfo.v_active = vt_minor2arg(vc_active_console);
1034                 vtinfo.v_state = 3;     /* system console and vtdaemon */
1035 
1036                 /* we only support 16 vt states since the v_state is short */
1037                 for (minor = 2; minor < 16; minor++) {
1038                         pvc = vt_minor2vc(minor);
1039                         if (pvc == NULL)
1040                                 break;
1041                         if (VT_IS_INUSE(minor))
1042                                 vtinfo.v_state |= (1 << pvc->vc_minor);
1043                 }
1044 
1045                 if (!(tmp = allocb(sizeof (struct vt_stat), BPRI_MED))) {
1046                         error = ENOMEM;
1047                         break;
1048                 }
1049                 *(struct vt_stat *)(void *)tmp->b_rptr = vtinfo;
1050                 tmp->b_wptr += sizeof (struct vt_stat);
1051                 vt_copyout(q, mp, tmp, sizeof (struct vt_stat));
1052                 return;
1053 
1054         case VT_SET_TARGET:
1055                 /* always enforce sys_devices privilege */
1056                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
1057                         break;
1058 
1059                 arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
1060 
1061                 /* vtdaemon is doing authentication for this target console */
1062                 vc_target_console = arg;
1063                 break;
1064 
1065         case VT_GETACTIVE:      /* get real active console (minor) */
1066                 if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
1067                         error = ENOMEM;
1068                         break;
1069                 }
1070                 *(int *)(void *)tmp->b_rptr = vc_active_console;
1071                 tmp->b_wptr += sizeof (int);
1072                 vt_copyout(q, mp, tmp, sizeof (int));
1073                 return;
1074 
1075         case VT_GET_CONSUSER:
1076                 if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
1077                         error = ENOMEM;
1078                         break;
1079                 }
1080 
1081                 if (vc_cons_user == VT_MINOR_INVALID) {
1082                         /*
1083                          * Return -1 if console user link points to
1084                          * /dev/console
1085                          */
1086                         *(int *)(void *)tmp->b_rptr = -1;
1087                 } else {
1088                         *(int *)(void *)tmp->b_rptr = vc_cons_user;
1089                 }
1090 
1091                 tmp->b_wptr += sizeof (int);
1092                 vt_copyout(q, mp, tmp, sizeof (int));
1093                 return;
1094 
1095         case VT_RESET_CONSUSER:
1096                 /* always enforce sys_devices privilege */
1097                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
1098                         break;
1099 
1100                 /* Ensure it comes from /dev/console */
1101                 if (pvc->vc_minor != 0) {
1102                         error = ENXIO;
1103                         break;
1104                 }
1105 
1106                 mutex_enter(&vc_lock);
1107                 vc_cons_user = VT_MINOR_INVALID;
1108                 mutex_exit(&vc_lock);
1109                 break;
1110 
1111         case VT_SET_CONSUSER:
1112                 /* always enforce sys_devices privilege */
1113                 if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
1114                         break;
1115 
1116                 mutex_enter(&vc_lock);
1117                 vc_cons_user = pvc->vc_minor;
1118                 mutex_exit(&vc_lock);
1119                 break;
1120 
1121         default:
1122                 error = ENXIO;
1123                 break;
1124         }
1125 
1126         if (error != 0)
1127                 vt_iocnak(q, mp, error);
1128         else
1129                 vt_iocack(q, mp);
1130 }
1131 
1132 void
1133 vt_miocdata(queue_t *qp, mblk_t *mp)
1134 {
1135         vc_state_t *pvc = (vc_state_t *)qp->q_ptr;
1136         struct copyresp *copyresp;
1137         struct vt_mode *pmode;
1138         int error = 0;
1139 
1140         copyresp = (struct copyresp *)(void *)mp->b_rptr;
1141         if (copyresp->cp_rval) {
1142                 vt_iocnak(qp, mp, EAGAIN);
1143                 return;
1144         }
1145 
1146         switch (copyresp->cp_cmd) {
1147         case VT_SETMODE:
1148                 pmode = (struct vt_mode *)(void *)mp->b_cont->b_rptr;
1149                 error = vt_setmode(pvc, pmode);
1150                 break;
1151 
1152         case KDGETMODE:
1153         case VT_OPENQRY:
1154         case VT_GETMODE:
1155         case VT_GETDISPINFO:
1156         case VT_GETSTATE:
1157         case VT_ENABLED:
1158         case VT_GETACTIVE:
1159                 break;
1160 
1161         default:
1162                 error = ENXIO;
1163                 break;
1164         }
1165 
1166         if (error != 0)
1167                 vt_iocnak(qp, mp, error);
1168         else
1169                 vt_iocack(qp, mp);
1170 }
1171 
1172 static void
1173 vt_iocack(queue_t *qp, mblk_t *mp)
1174 {
1175         struct iocblk   *iocbp = (struct iocblk *)(void *)mp->b_rptr;
1176 
1177         mp->b_datap->db_type = M_IOCACK;
1178         mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
1179         iocbp->ioc_error = 0;
1180         iocbp->ioc_count = 0;
1181         iocbp->ioc_rval = 0;
1182         if (mp->b_cont != NULL) {
1183                 freemsg(mp->b_cont);
1184                 mp->b_cont = NULL;
1185         }
1186         qreply(qp, mp);
1187 }
1188 
1189 static void
1190 vt_iocnak(queue_t *qp, mblk_t *mp, int error)
1191 {
1192         struct iocblk *iocp = (struct iocblk *)(void *)mp->b_rptr;
1193 
1194         mp->b_datap->db_type = M_IOCNAK;
1195         iocp->ioc_rval = 0;
1196         iocp->ioc_count = 0;
1197         iocp->ioc_error = error;
1198         if (mp->b_cont != NULL) {
1199                 freemsg(mp->b_cont);
1200                 mp->b_cont = NULL;
1201         }
1202         qreply(qp, mp);
1203 }
1204 
1205 static void
1206 vt_copyin(queue_t *qp, mblk_t *mp, uint_t size)
1207 {
1208         struct copyreq  *cqp;
1209 
1210         cqp = (struct copyreq *)(void *)mp->b_rptr;
1211         cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr);
1212         cqp->cq_size = size;
1213         cqp->cq_flag = 0;
1214         cqp->cq_private = (mblk_t *)NULL;
1215         mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
1216         mp->b_datap->db_type = M_COPYIN;
1217         if (mp->b_cont)
1218                 freemsg(mp->b_cont);
1219         mp->b_cont = (mblk_t *)NULL;
1220         qreply(qp, mp);
1221 }
1222 
1223 static void
1224 vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size)
1225 {
1226         struct copyreq  *cqp;
1227 
1228         cqp = (struct copyreq *)(void *)mp->b_rptr;
1229         cqp->cq_size = size;
1230         cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr);
1231         cqp->cq_flag = 0;
1232         cqp->cq_private = (mblk_t *)NULL;
1233         mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
1234         mp->b_datap->db_type = M_COPYOUT;
1235         if (mp->b_cont)
1236                 freemsg(mp->b_cont);
1237         mp->b_cont = tmp;
1238         qreply(qp, mp);
1239 }
1240 
1241 /*
1242  * Get vc state from minor.
1243  * Once a caller gets a vc_state_t from this function,
1244  * the vc_state_t is guaranteed not being freed before
1245  * the caller leaves this STREAMS module by the D_MTPERMOD
1246  * perimeter.
1247  */
1248 vc_state_t *
1249 vt_minor2vc(minor_t minor)
1250 {
1251         avl_index_t where;
1252         vc_state_t target;
1253 
1254         if (minor != VT_ACTIVE) {
1255                 target.vc_minor = minor;
1256                 return (avl_find(&vc_avl_root, &target, &where));
1257         }
1258 
1259         if (vc_active_console == VT_MINOR_INVALID)
1260                 target.vc_minor = 0;
1261         else
1262                 target.vc_minor = vc_active_console;
1263 
1264         return (avl_find(&vc_avl_root, &target, &where));
1265 }
1266 
1267 static void
1268 vt_state_init(vc_state_t *vcptr, minor_t minor)
1269 {
1270         mutex_init(&vcptr->vc_state_lock, NULL, MUTEX_DRIVER, NULL);
1271 
1272         mutex_enter(&vcptr->vc_state_lock);
1273         vcptr->vc_flags = 0;
1274         mutex_exit(&vcptr->vc_state_lock);
1275 
1276         vcptr->vc_pid = -1;
1277         vcptr->vc_dispnum = 0;
1278         vcptr->vc_login = 0;
1279         vcptr->vc_switchto = VT_MINOR_INVALID;
1280         vcptr->vc_switch_mode = VT_AUTO;
1281         vcptr->vc_relsig = SIGUSR1;
1282         vcptr->vc_acqsig = SIGUSR1;
1283         vcptr->vc_tem = NULL;
1284         vcptr->vc_bufcallid = 0;
1285         vcptr->vc_timeoutid = 0;
1286         vcptr->vc_wq = NULL;
1287         vcptr->vc_minor = minor;
1288 }
1289 
1290 void
1291 vt_resize(uint_t count)
1292 {
1293         uint_t vc_num, i;
1294 
1295         ASSERT(MUTEX_HELD(&vc_lock));
1296 
1297         vc_num = VC_INSTANCES_COUNT;
1298 
1299         if (count == vc_num)
1300                 return;
1301 
1302         if (count > vc_num) {
1303                 for (i = vc_num; i < count; i++) {
1304                         vc_state_t *vcptr = kmem_zalloc(sizeof (vc_state_t),
1305                             KM_SLEEP);
1306                         vt_state_init(vcptr, i);
1307                         avl_add(&vc_avl_root, vcptr);
1308                 }
1309                 return;
1310         }
1311 
1312         for (i = vc_num; i > count; i--) {
1313                 avl_index_t where;
1314                 vc_state_t target, *found;
1315 
1316                 target.vc_minor = i - 1;
1317                 found = avl_find(&vc_avl_root, &target, &where);
1318                 ASSERT(found != NULL && found->vc_flags == 0);
1319                 avl_remove(&vc_avl_root, found);
1320                 kmem_free(found, sizeof (vc_state_t));
1321         }
1322 }
1323 
1324 static int
1325 vc_avl_compare(const void *first, const void *second)
1326 {
1327         const vc_state_t *vcptr1 = first;
1328         const vc_state_t *vcptr2 = second;
1329 
1330         if (vcptr1->vc_minor < vcptr2->vc_minor)
1331                 return (-1);
1332 
1333         if (vcptr1->vc_minor == vcptr2->vc_minor)
1334                 return (0);
1335 
1336         return (1);
1337 }
1338 
1339 /*
1340  * Only called from wc init().
1341  */
1342 void
1343 vt_init(void)
1344 {
1345         avl_create(&vc_avl_root, vc_avl_compare, sizeof (vc_state_t),
1346             offsetof(vc_state_t, vc_avl_node));
1347 
1348         list_create(&vc_waitactive_list, sizeof (vc_waitactive_msg_t),
1349             offsetof(vc_waitactive_msg_t, wa_list_node));
1350 
1351         mutex_init(&vc_lock, NULL, MUTEX_DRIVER, NULL);
1352         mutex_init(&vt_pending_vtno_lock, NULL, MUTEX_DRIVER, NULL);
1353 }