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