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