xref: /onnv-gate/usr/src/uts/common/io/vcons.c (revision 11913:283e725df792)
17688SAaron.Zang@Sun.COM /*
27688SAaron.Zang@Sun.COM  * CDDL HEADER START
37688SAaron.Zang@Sun.COM  *
47688SAaron.Zang@Sun.COM  * The contents of this file are subject to the terms of the
57688SAaron.Zang@Sun.COM  * Common Development and Distribution License (the "License").
67688SAaron.Zang@Sun.COM  * You may not use this file except in compliance with the License.
77688SAaron.Zang@Sun.COM  *
87688SAaron.Zang@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97688SAaron.Zang@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107688SAaron.Zang@Sun.COM  * See the License for the specific language governing permissions
117688SAaron.Zang@Sun.COM  * and limitations under the License.
127688SAaron.Zang@Sun.COM  *
137688SAaron.Zang@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147688SAaron.Zang@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157688SAaron.Zang@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167688SAaron.Zang@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177688SAaron.Zang@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187688SAaron.Zang@Sun.COM  *
197688SAaron.Zang@Sun.COM  * CDDL HEADER END
207688SAaron.Zang@Sun.COM  */
217688SAaron.Zang@Sun.COM 
227688SAaron.Zang@Sun.COM /*
23*11913SRoger.Faulkner@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
247688SAaron.Zang@Sun.COM  * Use is subject to license terms.
257688SAaron.Zang@Sun.COM  */
267688SAaron.Zang@Sun.COM 
277688SAaron.Zang@Sun.COM #include <sys/types.h>
287688SAaron.Zang@Sun.COM #include <sys/param.h>
297688SAaron.Zang@Sun.COM #include <sys/signal.h>
307688SAaron.Zang@Sun.COM #include <sys/cred.h>
317688SAaron.Zang@Sun.COM #include <sys/vnode.h>
327688SAaron.Zang@Sun.COM #include <sys/termios.h>
337688SAaron.Zang@Sun.COM #include <sys/termio.h>
347688SAaron.Zang@Sun.COM #include <sys/ttold.h>
357688SAaron.Zang@Sun.COM #include <sys/stropts.h>
367688SAaron.Zang@Sun.COM #include <sys/stream.h>
377688SAaron.Zang@Sun.COM #include <sys/strsun.h>
387688SAaron.Zang@Sun.COM #include <sys/tty.h>
397688SAaron.Zang@Sun.COM #include <sys/buf.h>
407688SAaron.Zang@Sun.COM #include <sys/uio.h>
417688SAaron.Zang@Sun.COM #include <sys/stat.h>
427688SAaron.Zang@Sun.COM #include <sys/sysmacros.h>
437688SAaron.Zang@Sun.COM #include <sys/errno.h>
447688SAaron.Zang@Sun.COM #include <sys/proc.h>
457688SAaron.Zang@Sun.COM #include <sys/procset.h>
467688SAaron.Zang@Sun.COM #include <sys/fault.h>
477688SAaron.Zang@Sun.COM #include <sys/siginfo.h>
487688SAaron.Zang@Sun.COM #include <sys/debug.h>
497688SAaron.Zang@Sun.COM #include <sys/kd.h>
507688SAaron.Zang@Sun.COM #include <sys/vt.h>
517688SAaron.Zang@Sun.COM #include <sys/vtdaemon.h>
527688SAaron.Zang@Sun.COM #include <sys/session.h>
537688SAaron.Zang@Sun.COM #include <sys/door.h>
547688SAaron.Zang@Sun.COM #include <sys/kmem.h>
557688SAaron.Zang@Sun.COM #include <sys/cpuvar.h>
567688SAaron.Zang@Sun.COM #include <sys/kbio.h>
577688SAaron.Zang@Sun.COM #include <sys/strredir.h>
587688SAaron.Zang@Sun.COM #include <sys/fs/snode.h>
597688SAaron.Zang@Sun.COM #include <sys/consdev.h>
607688SAaron.Zang@Sun.COM #include <sys/conf.h>
617688SAaron.Zang@Sun.COM #include <sys/cmn_err.h>
627688SAaron.Zang@Sun.COM #include <sys/console.h>
637688SAaron.Zang@Sun.COM #include <sys/promif.h>
647688SAaron.Zang@Sun.COM #include <sys/note.h>
657688SAaron.Zang@Sun.COM #include <sys/polled_io.h>
667688SAaron.Zang@Sun.COM #include <sys/systm.h>
677688SAaron.Zang@Sun.COM #include <sys/ddi.h>
687688SAaron.Zang@Sun.COM #include <sys/sunddi.h>
697688SAaron.Zang@Sun.COM #include <sys/sunndi.h>
707688SAaron.Zang@Sun.COM #include <sys/esunddi.h>
717688SAaron.Zang@Sun.COM #include <sys/sunldi.h>
727688SAaron.Zang@Sun.COM #include <sys/debug.h>
737688SAaron.Zang@Sun.COM #include <sys/console.h>
747688SAaron.Zang@Sun.COM #include <sys/ddi_impldefs.h>
757688SAaron.Zang@Sun.COM #include <sys/policy.h>
767688SAaron.Zang@Sun.COM #include <sys/tem.h>
777688SAaron.Zang@Sun.COM #include <sys/wscons.h>
787688SAaron.Zang@Sun.COM #include <sys/systm.h>
797688SAaron.Zang@Sun.COM #include <sys/modctl.h>
807688SAaron.Zang@Sun.COM #include <sys/vt_impl.h>
817688SAaron.Zang@Sun.COM #include <sys/consconfig_dacf.h>
827688SAaron.Zang@Sun.COM 
837688SAaron.Zang@Sun.COM /*
847688SAaron.Zang@Sun.COM  * This file belongs to wc STREAMS module which has a D_MTPERMODE
857688SAaron.Zang@Sun.COM  * inner perimeter. See "Locking Policy" comment in wscons.c for
867688SAaron.Zang@Sun.COM  * more information.
877688SAaron.Zang@Sun.COM  */
887688SAaron.Zang@Sun.COM 
897688SAaron.Zang@Sun.COM /*
907688SAaron.Zang@Sun.COM  * Minor	name		device file		Hotkeys
917688SAaron.Zang@Sun.COM  *
927688SAaron.Zang@Sun.COM  * 0	the system console	/dev/console		Alt + F1
937688SAaron.Zang@Sun.COM  * 0:	virtual console #1	/dev/vt/0		Alt + F1
947688SAaron.Zang@Sun.COM  *
957688SAaron.Zang@Sun.COM  * 2:   virtual console #2	/dev/vt/2		Alt + F2
967688SAaron.Zang@Sun.COM  * 3:	virtual console #3	/dev/vt/3		Alt + F3
977688SAaron.Zang@Sun.COM  * ......
987688SAaron.Zang@Sun.COM  * n:	virtual console #n	/dev/vt/n		Alt + Fn
997688SAaron.Zang@Sun.COM  *
1007688SAaron.Zang@Sun.COM  * Note that vtdaemon is running on /dev/vt/1 (minor=1),
1017688SAaron.Zang@Sun.COM  * which is not available to end users.
1027688SAaron.Zang@Sun.COM  *
1037688SAaron.Zang@Sun.COM  */
1047688SAaron.Zang@Sun.COM 
1057688SAaron.Zang@Sun.COM #define	VT_DAEMON_MINOR	1
1067688SAaron.Zang@Sun.COM #define	VT_IS_DAEMON(minor)	((minor) == VT_DAEMON_MINOR)
1077688SAaron.Zang@Sun.COM 
1087688SAaron.Zang@Sun.COM extern void	wc_get_size(vc_state_t *pvc);
1097688SAaron.Zang@Sun.COM extern boolean_t consconfig_console_is_tipline(void);
1107688SAaron.Zang@Sun.COM 
1117688SAaron.Zang@Sun.COM 
1127688SAaron.Zang@Sun.COM minor_t vc_last_console = VT_MINOR_INVALID;	/* the last used console */
1137688SAaron.Zang@Sun.COM volatile uint_t	vc_target_console;		/* arg (1..n) */
1147688SAaron.Zang@Sun.COM 
1157688SAaron.Zang@Sun.COM static volatile minor_t vc_inuse_max_minor = 0;
1167688SAaron.Zang@Sun.COM static list_t vc_waitactive_list;
1177688SAaron.Zang@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_target_console))
1187688SAaron.Zang@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_last_console))
1197688SAaron.Zang@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_inuse_max_minor))
1207688SAaron.Zang@Sun.COM _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_waitactive_list))
1217688SAaron.Zang@Sun.COM 
1227688SAaron.Zang@Sun.COM static int vt_pending_vtno = -1;
1237688SAaron.Zang@Sun.COM kmutex_t vt_pending_vtno_lock;
1247688SAaron.Zang@Sun.COM _NOTE(MUTEX_PROTECTS_DATA(vt_pending_vtno_lock, vt_pending_vtno))
1257688SAaron.Zang@Sun.COM 
1267688SAaron.Zang@Sun.COM static int vt_activate(uint_t vt_no, cred_t *credp);
1277688SAaron.Zang@Sun.COM static void vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size);
1287688SAaron.Zang@Sun.COM static void vt_copyin(queue_t *qp, mblk_t *mp, uint_t size);
1297688SAaron.Zang@Sun.COM static void vt_iocnak(queue_t *qp, mblk_t *mp, int error);
1307688SAaron.Zang@Sun.COM static void vt_iocack(queue_t *qp, mblk_t *mp);
1317688SAaron.Zang@Sun.COM 
1327688SAaron.Zang@Sun.COM static uint_t vt_minor2arg(minor_t minor);
1337688SAaron.Zang@Sun.COM static minor_t vt_arg2minor(uint_t arg);
1347688SAaron.Zang@Sun.COM 
1357688SAaron.Zang@Sun.COM /*
1367688SAaron.Zang@Sun.COM  * If the system console is directed to tipline, consider /dev/vt/0 as
1377688SAaron.Zang@Sun.COM  * not being used.
1387688SAaron.Zang@Sun.COM  * For other VT, if it is opened and tty is initialized, consider it
1397688SAaron.Zang@Sun.COM  * as being used.
1407688SAaron.Zang@Sun.COM  */
1417688SAaron.Zang@Sun.COM #define	VT_IS_INUSE(id)						\
1427688SAaron.Zang@Sun.COM 	(((vt_minor2vc(id))->vc_flags & WCS_ISOPEN) &&		\
1437688SAaron.Zang@Sun.COM 	((vt_minor2vc(id))->vc_flags & WCS_INIT) &&		\
1447688SAaron.Zang@Sun.COM 	(id != 0 || !consconfig_console_is_tipline()))
1457688SAaron.Zang@Sun.COM 
1467688SAaron.Zang@Sun.COM /*
1477688SAaron.Zang@Sun.COM  * the vt switching message is encoded as:
1487688SAaron.Zang@Sun.COM  *
1497688SAaron.Zang@Sun.COM  *   -------------------------------------------------------------
1507688SAaron.Zang@Sun.COM  *   |  \033  |  'Q'  |  vtno + 'A'  |  opcode  |  'z'  |  '\0'  |
1517688SAaron.Zang@Sun.COM  *   -------------------------------------------------------------
1527688SAaron.Zang@Sun.COM  */
1537688SAaron.Zang@Sun.COM #define	VT_MSG_SWITCH(mp)					\
1547688SAaron.Zang@Sun.COM 	((int)((mp)->b_wptr - (mp)->b_rptr) >= 5 &&		\
1557688SAaron.Zang@Sun.COM 	*((mp)->b_rptr) == '\033' &&				\
1567688SAaron.Zang@Sun.COM 	*((mp)->b_rptr + 1) == 'Q' &&				\
1577688SAaron.Zang@Sun.COM 	*((mp)->b_rptr + 4) == 'z')
1587688SAaron.Zang@Sun.COM 
1597688SAaron.Zang@Sun.COM #define	VT_MSG_VTNO(mp)		(*((mp)->b_rptr + 2) - 'A')
1607688SAaron.Zang@Sun.COM #define	VT_MSG_OPCODE(mp)	(*((mp)->b_rptr + 3))
1617688SAaron.Zang@Sun.COM 
1627688SAaron.Zang@Sun.COM #define	VT_DOORCALL_MAX_RETRY	3
1637688SAaron.Zang@Sun.COM 
1647688SAaron.Zang@Sun.COM static void
1657688SAaron.Zang@Sun.COM vt_init_ttycommon(tty_common_t *pcommon)
1667688SAaron.Zang@Sun.COM {
1677688SAaron.Zang@Sun.COM 	struct termios *termiosp;
1687688SAaron.Zang@Sun.COM 	int len;
1697688SAaron.Zang@Sun.COM 
1707688SAaron.Zang@Sun.COM 	mutex_init(&pcommon->t_excl, NULL, MUTEX_DEFAULT, NULL);
1717688SAaron.Zang@Sun.COM 	pcommon->t_iflag = 0;
1727688SAaron.Zang@Sun.COM 
1737688SAaron.Zang@Sun.COM 	/*
1747688SAaron.Zang@Sun.COM 	 * Get the default termios settings (cflag).
1757688SAaron.Zang@Sun.COM 	 * These are stored as a property in the
1767688SAaron.Zang@Sun.COM 	 * "options" node.
1777688SAaron.Zang@Sun.COM 	 */
1787688SAaron.Zang@Sun.COM 	if (ddi_getlongprop(DDI_DEV_T_ANY,
1797688SAaron.Zang@Sun.COM 	    ddi_root_node(), 0, "ttymodes",
1807688SAaron.Zang@Sun.COM 	    (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS) {
1817688SAaron.Zang@Sun.COM 
1827688SAaron.Zang@Sun.COM 		if (len == sizeof (struct termios))
1837688SAaron.Zang@Sun.COM 			pcommon->t_cflag = termiosp->c_cflag;
1847688SAaron.Zang@Sun.COM 		else
1857688SAaron.Zang@Sun.COM 			cmn_err(CE_WARN,
1867688SAaron.Zang@Sun.COM 			    "wc: Couldn't get ttymodes property!");
1877688SAaron.Zang@Sun.COM 
1887688SAaron.Zang@Sun.COM 		kmem_free(termiosp, len);
1897688SAaron.Zang@Sun.COM 	} else {
1907688SAaron.Zang@Sun.COM 		/*
1917688SAaron.Zang@Sun.COM 		 * Gack!  Whine about it.
1927688SAaron.Zang@Sun.COM 		 */
1937688SAaron.Zang@Sun.COM 		cmn_err(CE_WARN,
1947688SAaron.Zang@Sun.COM 		    "wc: Couldn't get ttymodes property!");
1957688SAaron.Zang@Sun.COM 	}
1967688SAaron.Zang@Sun.COM 
1977688SAaron.Zang@Sun.COM 	pcommon->t_iocpending = NULL;
1987688SAaron.Zang@Sun.COM }
1997688SAaron.Zang@Sun.COM 
2007688SAaron.Zang@Sun.COM static int
2017688SAaron.Zang@Sun.COM vt_config(uint_t count)
2027688SAaron.Zang@Sun.COM {
2037688SAaron.Zang@Sun.COM 	if (consmode != CONS_KFB)
2047688SAaron.Zang@Sun.COM 		return (ENOTSUP);
2057688SAaron.Zang@Sun.COM 
2067688SAaron.Zang@Sun.COM 	/* one for system console, one for vtdaemon */
2077688SAaron.Zang@Sun.COM 	if (count < 2)
2087688SAaron.Zang@Sun.COM 		return (ENXIO);
2097688SAaron.Zang@Sun.COM 
2107688SAaron.Zang@Sun.COM 	/*
2117688SAaron.Zang@Sun.COM 	 * Shouldn't allow to shrink the max vt minor to be smaller than
2127688SAaron.Zang@Sun.COM 	 * the max in used minor.
2137688SAaron.Zang@Sun.COM 	 */
2147688SAaron.Zang@Sun.COM 	if (count <= vc_inuse_max_minor)
2157688SAaron.Zang@Sun.COM 		return (EBUSY);
2167688SAaron.Zang@Sun.COM 
2177688SAaron.Zang@Sun.COM 	mutex_enter(&vc_lock);
2187688SAaron.Zang@Sun.COM 	vt_resize(count);
2197688SAaron.Zang@Sun.COM 	mutex_exit(&vc_lock);
2207688SAaron.Zang@Sun.COM 
2217688SAaron.Zang@Sun.COM 	return (0);
2227688SAaron.Zang@Sun.COM }
2237688SAaron.Zang@Sun.COM 
2247688SAaron.Zang@Sun.COM void
2257688SAaron.Zang@Sun.COM vt_clean(queue_t *q, vc_state_t *pvc)
2267688SAaron.Zang@Sun.COM {
2277688SAaron.Zang@Sun.COM 	ASSERT(MUTEX_HELD(&pvc->vc_state_lock));
2287688SAaron.Zang@Sun.COM 
2297688SAaron.Zang@Sun.COM 	if (pvc->vc_bufcallid != 0) {
2307688SAaron.Zang@Sun.COM 		qunbufcall(q, pvc->vc_bufcallid);
2317688SAaron.Zang@Sun.COM 		pvc->vc_bufcallid = 0;
2327688SAaron.Zang@Sun.COM 	}
2337688SAaron.Zang@Sun.COM 	if (pvc->vc_timeoutid != 0) {
2347688SAaron.Zang@Sun.COM 		(void) quntimeout(q, pvc->vc_timeoutid);
2357688SAaron.Zang@Sun.COM 		pvc->vc_timeoutid = 0;
2367688SAaron.Zang@Sun.COM 	}
2377688SAaron.Zang@Sun.COM 	ttycommon_close(&pvc->vc_ttycommon);
2387688SAaron.Zang@Sun.COM 
2397688SAaron.Zang@Sun.COM 	pvc->vc_flags &= ~WCS_INIT;
2407688SAaron.Zang@Sun.COM }
2417688SAaron.Zang@Sun.COM 
2427688SAaron.Zang@Sun.COM /*
2437688SAaron.Zang@Sun.COM  * Reply the VT_WAITACTIVE ioctl.
2447688SAaron.Zang@Sun.COM  * Argument 'close' usage:
2457688SAaron.Zang@Sun.COM  * B_TRUE:  the vt designated by argument 'minor' is being closed.
2467688SAaron.Zang@Sun.COM  * B_FALSE: the vt designated by argument 'minor' has been activated just now.
2477688SAaron.Zang@Sun.COM  */
2487688SAaron.Zang@Sun.COM static void
2497688SAaron.Zang@Sun.COM vc_waitactive_reply(int minor, boolean_t close)
2507688SAaron.Zang@Sun.COM {
2517688SAaron.Zang@Sun.COM 	vc_waitactive_msg_t *index, *tmp;
2527688SAaron.Zang@Sun.COM 	vc_state_t *pvc;
2537688SAaron.Zang@Sun.COM 
2547688SAaron.Zang@Sun.COM 	index = list_head(&vc_waitactive_list);
2557688SAaron.Zang@Sun.COM 
2567688SAaron.Zang@Sun.COM 	while (index != NULL) {
2577688SAaron.Zang@Sun.COM 		tmp = index;
2587688SAaron.Zang@Sun.COM 		index = list_next(&vc_waitactive_list, index);
2597688SAaron.Zang@Sun.COM 
2607688SAaron.Zang@Sun.COM 		if ((close && tmp->wa_msg_minor == minor) ||
2617688SAaron.Zang@Sun.COM 		    (!close && tmp->wa_wait_minor == minor)) {
2627688SAaron.Zang@Sun.COM 			list_remove(&vc_waitactive_list, tmp);
2637688SAaron.Zang@Sun.COM 			pvc = vt_minor2vc(tmp->wa_msg_minor);
2647688SAaron.Zang@Sun.COM 
2657688SAaron.Zang@Sun.COM 			if (close)
2667688SAaron.Zang@Sun.COM 				vt_iocnak(pvc->vc_wq, tmp->wa_mp, ENXIO);
2677688SAaron.Zang@Sun.COM 			else
2687688SAaron.Zang@Sun.COM 				vt_iocack(pvc->vc_wq, tmp->wa_mp);
2697688SAaron.Zang@Sun.COM 
2707688SAaron.Zang@Sun.COM 			kmem_free(tmp, sizeof (vc_waitactive_msg_t));
2717688SAaron.Zang@Sun.COM 		}
2727688SAaron.Zang@Sun.COM 	}
2737688SAaron.Zang@Sun.COM }
2747688SAaron.Zang@Sun.COM 
2757688SAaron.Zang@Sun.COM void
2767688SAaron.Zang@Sun.COM vt_close(queue_t *q, vc_state_t *pvc, cred_t *credp)
2777688SAaron.Zang@Sun.COM {
2787688SAaron.Zang@Sun.COM 	minor_t index;
2797688SAaron.Zang@Sun.COM 
2807688SAaron.Zang@Sun.COM 	mutex_enter(&pvc->vc_state_lock);
2817688SAaron.Zang@Sun.COM 	vt_clean(q, pvc);
2827688SAaron.Zang@Sun.COM 	pvc->vc_flags &= ~WCS_ISOPEN;
2837688SAaron.Zang@Sun.COM 	mutex_exit(&pvc->vc_state_lock);
2847688SAaron.Zang@Sun.COM 
2857688SAaron.Zang@Sun.COM 	tem_destroy(pvc->vc_tem, credp);
2867688SAaron.Zang@Sun.COM 	pvc->vc_tem = NULL;
2877688SAaron.Zang@Sun.COM 
2887688SAaron.Zang@Sun.COM 	index = pvc->vc_minor;
2897688SAaron.Zang@Sun.COM 	if (index == vc_inuse_max_minor) {
2907688SAaron.Zang@Sun.COM 		while ((--index > 0) && !VT_IS_INUSE(index))
2917688SAaron.Zang@Sun.COM 			;
2927688SAaron.Zang@Sun.COM 		vc_inuse_max_minor = index;
2937688SAaron.Zang@Sun.COM 	}
2947688SAaron.Zang@Sun.COM 
2957688SAaron.Zang@Sun.COM 	vc_waitactive_reply(pvc->vc_minor, B_TRUE);
2967688SAaron.Zang@Sun.COM }
2977688SAaron.Zang@Sun.COM 
2987688SAaron.Zang@Sun.COM static void
2997688SAaron.Zang@Sun.COM vt_init_tty(vc_state_t *pvc)
3007688SAaron.Zang@Sun.COM {
3017688SAaron.Zang@Sun.COM 	ASSERT(MUTEX_HELD(&pvc->vc_state_lock));
3027688SAaron.Zang@Sun.COM 
3037688SAaron.Zang@Sun.COM 	pvc->vc_flags |= WCS_INIT;
3047688SAaron.Zang@Sun.COM 	vt_init_ttycommon(&pvc->vc_ttycommon);
3057688SAaron.Zang@Sun.COM 	wc_get_size(pvc);
3067688SAaron.Zang@Sun.COM }
3077688SAaron.Zang@Sun.COM 
3087688SAaron.Zang@Sun.COM /*
3097688SAaron.Zang@Sun.COM  * minor 0:	/dev/vt/0	(index = 0, indicating the system console)
3107688SAaron.Zang@Sun.COM  * minor 1:	/dev/vt/1	(index = 1, vtdaemon special console)
3117688SAaron.Zang@Sun.COM  * minor 2:	/dev/vt/2	(index = 2, virtual consoles)
3127688SAaron.Zang@Sun.COM  * ......
3137688SAaron.Zang@Sun.COM  * minor n:	/dev/vt/n	(index = n)
3147688SAaron.Zang@Sun.COM  *
3157688SAaron.Zang@Sun.COM  *
3167688SAaron.Zang@Sun.COM  * The system console (minor 0), is opened firstly and used during console
3177688SAaron.Zang@Sun.COM  * configuration.  It also acts as the system hard console even when all
3187688SAaron.Zang@Sun.COM  * virtual consoles go off.
3197688SAaron.Zang@Sun.COM  *
3207688SAaron.Zang@Sun.COM  * In tipline case, minor 0 (/dev/vt/0) is reserved, and cannot be switched to.
3217688SAaron.Zang@Sun.COM  * And the system console is redirected to the tipline. During normal cases,
3227688SAaron.Zang@Sun.COM  * we can switch from virtual consoles to it by pressing 'Alt + F1'.
3237688SAaron.Zang@Sun.COM  *
3247688SAaron.Zang@Sun.COM  * minor 1 (/dev/vt/1) is reserved for vtdaemon special console, and it's
3257688SAaron.Zang@Sun.COM  * not available to end users.
3267688SAaron.Zang@Sun.COM  *
3277688SAaron.Zang@Sun.COM  * During early console configuration, consconfig_dacf opens wscons and then
3287688SAaron.Zang@Sun.COM  * issue a WC_OPEN_FB ioctl to kick off terminal init process. So during
3297688SAaron.Zang@Sun.COM  * consconfig_dacf first opening of wscons, tems (of type tem_state_t) is
3307688SAaron.Zang@Sun.COM  * not initialized. We do not initialize the tem_vt_state_t instance returned
3317688SAaron.Zang@Sun.COM  * by tem_init() for this open, since we do not have enough info to handle
3327688SAaron.Zang@Sun.COM  * normal terminal operation at this moment. This tem_vt_state_t instance
3337688SAaron.Zang@Sun.COM  * will get initialized when handling WC_OPEN_FB.
3347688SAaron.Zang@Sun.COM  */
3357688SAaron.Zang@Sun.COM int
3367688SAaron.Zang@Sun.COM vt_open(minor_t minor, queue_t *rq, cred_t *crp)
3377688SAaron.Zang@Sun.COM {
3387688SAaron.Zang@Sun.COM 	vc_state_t *pvc;
3397688SAaron.Zang@Sun.COM 
3407688SAaron.Zang@Sun.COM 	if (!vt_minor_valid(minor))
3417688SAaron.Zang@Sun.COM 		return (ENXIO);
3427688SAaron.Zang@Sun.COM 
3437688SAaron.Zang@Sun.COM 	pvc = vt_minor2vc(minor);
3447688SAaron.Zang@Sun.COM 	if (pvc == NULL)
3457688SAaron.Zang@Sun.COM 		return (ENXIO);
3467688SAaron.Zang@Sun.COM 
3477688SAaron.Zang@Sun.COM 	mutex_enter(&vc_lock);
3487688SAaron.Zang@Sun.COM 	mutex_enter(&pvc->vc_state_lock);
3497688SAaron.Zang@Sun.COM 
3507688SAaron.Zang@Sun.COM 	if (!(pvc->vc_flags & WCS_ISOPEN)) {
3517688SAaron.Zang@Sun.COM 		/*
3527688SAaron.Zang@Sun.COM 		 * vc_tem might not be intialized if !tems.ts_initialized,
3537688SAaron.Zang@Sun.COM 		 * and this only happens during console configuration.
3547688SAaron.Zang@Sun.COM 		 */
3557688SAaron.Zang@Sun.COM 		pvc->vc_tem = tem_init(crp);
3567688SAaron.Zang@Sun.COM 	}
3577688SAaron.Zang@Sun.COM 
3587688SAaron.Zang@Sun.COM 	if (!(pvc->vc_flags & WCS_INIT))
3597688SAaron.Zang@Sun.COM 		vt_init_tty(pvc);
3607688SAaron.Zang@Sun.COM 
3617688SAaron.Zang@Sun.COM 	/*
3627688SAaron.Zang@Sun.COM 	 * In normal case, the first screen is the system console;
3637688SAaron.Zang@Sun.COM 	 * In tipline case, the first screen is the first VT that gets started.
3647688SAaron.Zang@Sun.COM 	 */
3657688SAaron.Zang@Sun.COM 	if (vc_active_console == VT_MINOR_INVALID && minor != VT_DAEMON_MINOR)
3667688SAaron.Zang@Sun.COM 		if (minor == 0 || consmode == CONS_KFB) {
3677688SAaron.Zang@Sun.COM 			boolean_t unblank = B_FALSE;
3687688SAaron.Zang@Sun.COM 
3697688SAaron.Zang@Sun.COM 			vc_active_console = minor;
3707688SAaron.Zang@Sun.COM 			vc_last_console = minor;
3717688SAaron.Zang@Sun.COM 			if (minor != 0) {
3727688SAaron.Zang@Sun.COM 				/*
3737688SAaron.Zang@Sun.COM 				 * If we are not opening the system console
3747688SAaron.Zang@Sun.COM 				 * as the first console, clear the phyical
3757688SAaron.Zang@Sun.COM 				 * screen.
3767688SAaron.Zang@Sun.COM 				 */
3777688SAaron.Zang@Sun.COM 				unblank = B_TRUE;
3787688SAaron.Zang@Sun.COM 			}
3797688SAaron.Zang@Sun.COM 
3807688SAaron.Zang@Sun.COM 			tem_activate(pvc->vc_tem, unblank, crp);
3817688SAaron.Zang@Sun.COM 		}
3827688SAaron.Zang@Sun.COM 
3837688SAaron.Zang@Sun.COM 	if ((pvc->vc_ttycommon.t_flags & TS_XCLUDE) &&
3847688SAaron.Zang@Sun.COM 	    (secpolicy_excl_open(crp) != 0)) {
3857688SAaron.Zang@Sun.COM 		mutex_exit(&pvc->vc_state_lock);
3867688SAaron.Zang@Sun.COM 		mutex_exit(&vc_lock);
3877688SAaron.Zang@Sun.COM 		return (EBUSY);
3887688SAaron.Zang@Sun.COM 	}
3897688SAaron.Zang@Sun.COM 
3907688SAaron.Zang@Sun.COM 	if (minor > vc_inuse_max_minor)
3917688SAaron.Zang@Sun.COM 		vc_inuse_max_minor = minor;
3927688SAaron.Zang@Sun.COM 
3937688SAaron.Zang@Sun.COM 	pvc->vc_flags |= WCS_ISOPEN;
3947688SAaron.Zang@Sun.COM 	pvc->vc_ttycommon.t_readq = rq;
3957688SAaron.Zang@Sun.COM 	pvc->vc_ttycommon.t_writeq = WR(rq);
3967688SAaron.Zang@Sun.COM 
3977688SAaron.Zang@Sun.COM 	mutex_exit(&pvc->vc_state_lock);
3987688SAaron.Zang@Sun.COM 	mutex_exit(&vc_lock);
3997688SAaron.Zang@Sun.COM 
4007688SAaron.Zang@Sun.COM 	rq->q_ptr = pvc;
4017688SAaron.Zang@Sun.COM 	WR(rq)->q_ptr = pvc;
4027688SAaron.Zang@Sun.COM 	pvc->vc_wq = WR(rq);
4037688SAaron.Zang@Sun.COM 
4047688SAaron.Zang@Sun.COM 	qprocson(rq);
4057688SAaron.Zang@Sun.COM 	return (0);
4067688SAaron.Zang@Sun.COM }
4077688SAaron.Zang@Sun.COM 
4087688SAaron.Zang@Sun.COM static minor_t
4097688SAaron.Zang@Sun.COM vt_find_prev(minor_t cur)
4107688SAaron.Zang@Sun.COM {
4117688SAaron.Zang@Sun.COM 	minor_t i, t, max;
4127688SAaron.Zang@Sun.COM 
4137688SAaron.Zang@Sun.COM 	ASSERT(vc_active_console != VT_MINOR_INVALID);
4147688SAaron.Zang@Sun.COM 
4157688SAaron.Zang@Sun.COM 	max = VC_INSTANCES_COUNT;
4167688SAaron.Zang@Sun.COM 
4177688SAaron.Zang@Sun.COM 	for (i = cur - 1; (t = (i + max) % max) != cur; i--)
4187688SAaron.Zang@Sun.COM 		if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t))
4197688SAaron.Zang@Sun.COM 			return (t);
4207688SAaron.Zang@Sun.COM 
4217688SAaron.Zang@Sun.COM 	return (VT_MINOR_INVALID);
4227688SAaron.Zang@Sun.COM }
4237688SAaron.Zang@Sun.COM 
4247688SAaron.Zang@Sun.COM static minor_t
4257688SAaron.Zang@Sun.COM vt_find_next(minor_t cur)
4267688SAaron.Zang@Sun.COM {
4277688SAaron.Zang@Sun.COM 	minor_t i, t, max;
4287688SAaron.Zang@Sun.COM 
4297688SAaron.Zang@Sun.COM 	ASSERT(vc_active_console != VT_MINOR_INVALID);
4307688SAaron.Zang@Sun.COM 
4317688SAaron.Zang@Sun.COM 	max = VC_INSTANCES_COUNT;
4327688SAaron.Zang@Sun.COM 
4337688SAaron.Zang@Sun.COM 	for (i = cur + 1; (t = (i + max) % max) != cur; i++)
4347688SAaron.Zang@Sun.COM 		if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t))
4357688SAaron.Zang@Sun.COM 			return (t);
4367688SAaron.Zang@Sun.COM 
4377688SAaron.Zang@Sun.COM 	return (VT_MINOR_INVALID);
4387688SAaron.Zang@Sun.COM }
4397688SAaron.Zang@Sun.COM 
4407688SAaron.Zang@Sun.COM /* ARGSUSED */
4417688SAaron.Zang@Sun.COM void
4427688SAaron.Zang@Sun.COM vt_send_hotkeys(void *timeout_arg)
4437688SAaron.Zang@Sun.COM {
4447688SAaron.Zang@Sun.COM 	door_handle_t door;
4457688SAaron.Zang@Sun.COM 	vt_cmd_arg_t arg;
4467688SAaron.Zang@Sun.COM 	int error = 0;
4477688SAaron.Zang@Sun.COM 	int retries = 0;
4487688SAaron.Zang@Sun.COM 	door_arg_t door_arg;
4497688SAaron.Zang@Sun.COM 
4508260SLipeng.Sang@Sun.COM 	arg.vt_ev = VT_EV_HOTKEYS;
4517688SAaron.Zang@Sun.COM 
4528260SLipeng.Sang@Sun.COM 	mutex_enter(&vt_pending_vtno_lock);
4537688SAaron.Zang@Sun.COM 	arg.vt_num = vt_pending_vtno;
4548260SLipeng.Sang@Sun.COM 	mutex_exit(&vt_pending_vtno_lock);
4557688SAaron.Zang@Sun.COM 
4567688SAaron.Zang@Sun.COM 	/* only available in kernel context or user context */
4577688SAaron.Zang@Sun.COM 	if (door_ki_open(VT_DAEMON_DOOR_FILE, &door) != 0) {
4588260SLipeng.Sang@Sun.COM 		mutex_enter(&vt_pending_vtno_lock);
4597688SAaron.Zang@Sun.COM 		vt_pending_vtno = -1;
4607688SAaron.Zang@Sun.COM 		mutex_exit(&vt_pending_vtno_lock);
4617688SAaron.Zang@Sun.COM 		return;
4627688SAaron.Zang@Sun.COM 	}
4637688SAaron.Zang@Sun.COM 
4647688SAaron.Zang@Sun.COM 	door_arg.rbuf = NULL;
4657688SAaron.Zang@Sun.COM 	door_arg.rsize = 0;
4667688SAaron.Zang@Sun.COM 	door_arg.data_ptr = (void *)&arg;
4677688SAaron.Zang@Sun.COM 	door_arg.data_size = sizeof (arg);
4687688SAaron.Zang@Sun.COM 	door_arg.desc_ptr = NULL;
4697688SAaron.Zang@Sun.COM 	door_arg.desc_num = 0;
4707688SAaron.Zang@Sun.COM 
4717688SAaron.Zang@Sun.COM 	/*
4727688SAaron.Zang@Sun.COM 	 * Make door upcall
4737688SAaron.Zang@Sun.COM 	 */
4747688SAaron.Zang@Sun.COM 	while ((error = door_ki_upcall(door, &door_arg)) != 0 &&
4757688SAaron.Zang@Sun.COM 	    retries < VT_DOORCALL_MAX_RETRY)
4767688SAaron.Zang@Sun.COM 		if (error == EAGAIN || error == EINTR)
4777688SAaron.Zang@Sun.COM 			retries++;
4787688SAaron.Zang@Sun.COM 		else
4797688SAaron.Zang@Sun.COM 			break;
4807688SAaron.Zang@Sun.COM 
4817688SAaron.Zang@Sun.COM 	door_ki_rele(door);
4827688SAaron.Zang@Sun.COM 
4838260SLipeng.Sang@Sun.COM 	mutex_enter(&vt_pending_vtno_lock);
4847688SAaron.Zang@Sun.COM 	vt_pending_vtno = -1;
4857688SAaron.Zang@Sun.COM 	mutex_exit(&vt_pending_vtno_lock);
4867688SAaron.Zang@Sun.COM }
4877688SAaron.Zang@Sun.COM 
4887688SAaron.Zang@Sun.COM static boolean_t
4897688SAaron.Zang@Sun.COM vt_validate_hotkeys(int minor)
4907688SAaron.Zang@Sun.COM {
4917688SAaron.Zang@Sun.COM 	/*
4927688SAaron.Zang@Sun.COM 	 * minor should not succeed the existing minor numbers range.
4937688SAaron.Zang@Sun.COM 	 */
4947688SAaron.Zang@Sun.COM 	if (!vt_minor_valid(minor))
4957688SAaron.Zang@Sun.COM 		return (B_FALSE);
4967688SAaron.Zang@Sun.COM 
4977688SAaron.Zang@Sun.COM 	/*
4987688SAaron.Zang@Sun.COM 	 * Shouldn't switch to /dev/vt/1 or an unused vt.
4997688SAaron.Zang@Sun.COM 	 */
5007688SAaron.Zang@Sun.COM 	if (!VT_IS_DAEMON(minor) && VT_IS_INUSE(minor))
5017688SAaron.Zang@Sun.COM 		return (B_TRUE);
5027688SAaron.Zang@Sun.COM 
5037688SAaron.Zang@Sun.COM 	return (B_FALSE);
5047688SAaron.Zang@Sun.COM }
5057688SAaron.Zang@Sun.COM 
5067688SAaron.Zang@Sun.COM static void
5077688SAaron.Zang@Sun.COM vt_trigger_hotkeys(int vtno)
5087688SAaron.Zang@Sun.COM {
5097688SAaron.Zang@Sun.COM 	mutex_enter(&vt_pending_vtno_lock);
5107688SAaron.Zang@Sun.COM 
5117688SAaron.Zang@Sun.COM 	if (vt_pending_vtno != -1) {
5127688SAaron.Zang@Sun.COM 		mutex_exit(&vt_pending_vtno_lock);
5137688SAaron.Zang@Sun.COM 		return;
5147688SAaron.Zang@Sun.COM 	}
5157688SAaron.Zang@Sun.COM 
5167688SAaron.Zang@Sun.COM 	vt_pending_vtno = vtno;
5177688SAaron.Zang@Sun.COM 	mutex_exit(&vt_pending_vtno_lock);
5187688SAaron.Zang@Sun.COM 	(void) timeout(vt_send_hotkeys, NULL, 1);
5197688SAaron.Zang@Sun.COM }
5207688SAaron.Zang@Sun.COM 
5217688SAaron.Zang@Sun.COM /*
5227688SAaron.Zang@Sun.COM  * return value:
5237688SAaron.Zang@Sun.COM  *    0:    non msg of vt hotkeys
5247688SAaron.Zang@Sun.COM  *    1:    msg of vt hotkeys
5257688SAaron.Zang@Sun.COM  */
5267688SAaron.Zang@Sun.COM int
5277688SAaron.Zang@Sun.COM vt_check_hotkeys(mblk_t *mp)
5287688SAaron.Zang@Sun.COM {
5297688SAaron.Zang@Sun.COM 	int vtno = 0;
5307688SAaron.Zang@Sun.COM 	minor_t minor = 0;
5317688SAaron.Zang@Sun.COM 
5327688SAaron.Zang@Sun.COM 	/* LINTED E_PTRDIFF_OVERFLOW */
5337688SAaron.Zang@Sun.COM 	if (!VT_MSG_SWITCH(mp))
5347688SAaron.Zang@Sun.COM 		return (0);
5357688SAaron.Zang@Sun.COM 
5367688SAaron.Zang@Sun.COM 	switch (VT_MSG_OPCODE(mp)) {
5377688SAaron.Zang@Sun.COM 	case 'B':
5387688SAaron.Zang@Sun.COM 		/* find out the previous vt */
5397688SAaron.Zang@Sun.COM 		if (vc_active_console == VT_MINOR_INVALID)
5407688SAaron.Zang@Sun.COM 			return (1);
5417688SAaron.Zang@Sun.COM 
5427688SAaron.Zang@Sun.COM 		if (VT_IS_DAEMON(vc_active_console)) {
5437688SAaron.Zang@Sun.COM 			minor = vt_find_prev(vt_arg2minor(vc_target_console));
5447688SAaron.Zang@Sun.COM 			break;
5457688SAaron.Zang@Sun.COM 		}
5467688SAaron.Zang@Sun.COM 
5477688SAaron.Zang@Sun.COM 		minor = vt_find_prev(vc_active_console);
5487688SAaron.Zang@Sun.COM 		break;
5497688SAaron.Zang@Sun.COM 	case 'F':
5507688SAaron.Zang@Sun.COM 		/* find out the next vt */
5517688SAaron.Zang@Sun.COM 		if (vc_active_console == VT_MINOR_INVALID)
5527688SAaron.Zang@Sun.COM 			return (1);
5537688SAaron.Zang@Sun.COM 
5547688SAaron.Zang@Sun.COM 		if (VT_IS_DAEMON(vc_active_console)) {
5557688SAaron.Zang@Sun.COM 			minor = vt_find_next(vt_arg2minor(vc_target_console));
5567688SAaron.Zang@Sun.COM 			break;
5577688SAaron.Zang@Sun.COM 		}
5587688SAaron.Zang@Sun.COM 
5597688SAaron.Zang@Sun.COM 		minor = vt_find_next(vc_active_console);
5607688SAaron.Zang@Sun.COM 		break;
5617688SAaron.Zang@Sun.COM 	case 'H':
5627688SAaron.Zang@Sun.COM 		/* find out the specified vt */
5637688SAaron.Zang@Sun.COM 		minor = VT_MSG_VTNO(mp);
5647688SAaron.Zang@Sun.COM 
5657688SAaron.Zang@Sun.COM 		/* check for system console, Alt + F1 */
5667688SAaron.Zang@Sun.COM 		if (minor == 1)
5677688SAaron.Zang@Sun.COM 			minor = 0;
5687688SAaron.Zang@Sun.COM 		break;
5697688SAaron.Zang@Sun.COM 	case 'L':
5707688SAaron.Zang@Sun.COM 		/* find out the last vt */
5717688SAaron.Zang@Sun.COM 		if ((minor = vc_last_console) == VT_MINOR_INVALID)
5727688SAaron.Zang@Sun.COM 			return (1);
5737688SAaron.Zang@Sun.COM 		break;
5747688SAaron.Zang@Sun.COM 	default:
5757688SAaron.Zang@Sun.COM 		return (1);
5767688SAaron.Zang@Sun.COM 	}
5777688SAaron.Zang@Sun.COM 
5787688SAaron.Zang@Sun.COM 	if (!vt_validate_hotkeys(minor))
5797688SAaron.Zang@Sun.COM 		return (1);
5807688SAaron.Zang@Sun.COM 
5817688SAaron.Zang@Sun.COM 	/*
5827688SAaron.Zang@Sun.COM 	 * for system console, the argument of vtno for
5837688SAaron.Zang@Sun.COM 	 * vt_activate is 1, though its minor is 0
5847688SAaron.Zang@Sun.COM 	 */
5857688SAaron.Zang@Sun.COM 	if (minor == 0)
5867688SAaron.Zang@Sun.COM 		vtno = 1;	/* for system console */
5877688SAaron.Zang@Sun.COM 	else
5887688SAaron.Zang@Sun.COM 		vtno = minor;
5897688SAaron.Zang@Sun.COM 
5907688SAaron.Zang@Sun.COM 	vt_trigger_hotkeys(vtno);
5917688SAaron.Zang@Sun.COM 	return (1);
5927688SAaron.Zang@Sun.COM }
5937688SAaron.Zang@Sun.COM 
5947688SAaron.Zang@Sun.COM static void
5957688SAaron.Zang@Sun.COM vt_proc_sendsig(pid_t pid, int sig)
5967688SAaron.Zang@Sun.COM {
5977688SAaron.Zang@Sun.COM 	register proc_t *p;
5987688SAaron.Zang@Sun.COM 
5997688SAaron.Zang@Sun.COM 	if (pid <= 0)
6007688SAaron.Zang@Sun.COM 		return;
6017688SAaron.Zang@Sun.COM 
6027688SAaron.Zang@Sun.COM 	mutex_enter(&pidlock);
6037688SAaron.Zang@Sun.COM 	if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
6047688SAaron.Zang@Sun.COM 		mutex_exit(&pidlock);
6057688SAaron.Zang@Sun.COM 		return;
6067688SAaron.Zang@Sun.COM 	}
6077688SAaron.Zang@Sun.COM 
6087688SAaron.Zang@Sun.COM 	psignal(p, sig);
6097688SAaron.Zang@Sun.COM 	mutex_exit(&pidlock);
6107688SAaron.Zang@Sun.COM }
6117688SAaron.Zang@Sun.COM 
6127688SAaron.Zang@Sun.COM static int
6137688SAaron.Zang@Sun.COM vt_proc_exists(pid_t pid)
6147688SAaron.Zang@Sun.COM {
6157688SAaron.Zang@Sun.COM 	register proc_t *p;
6167688SAaron.Zang@Sun.COM 
6177688SAaron.Zang@Sun.COM 	if (pid <= 0)
6187688SAaron.Zang@Sun.COM 		return (EINVAL);
6197688SAaron.Zang@Sun.COM 
6207688SAaron.Zang@Sun.COM 	mutex_enter(&pidlock);
6217688SAaron.Zang@Sun.COM 	if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
6227688SAaron.Zang@Sun.COM 		mutex_exit(&pidlock);
6237688SAaron.Zang@Sun.COM 		return (ESRCH);
6247688SAaron.Zang@Sun.COM 	}
6257688SAaron.Zang@Sun.COM 	mutex_exit(&pidlock);
6267688SAaron.Zang@Sun.COM 
6277688SAaron.Zang@Sun.COM 	return (0);
6287688SAaron.Zang@Sun.COM }
6297688SAaron.Zang@Sun.COM 
630*11913SRoger.Faulkner@Sun.COM #define	SIG_VALID(x)	(((x) > 0) && ((x) <= MAXSIG) && \
6317688SAaron.Zang@Sun.COM 			((x) != SIGKILL) && ((x) != SIGSTOP))
6327688SAaron.Zang@Sun.COM 
6337688SAaron.Zang@Sun.COM static int
6347688SAaron.Zang@Sun.COM vt_setmode(vc_state_t *pvc, struct vt_mode *pmode)
6357688SAaron.Zang@Sun.COM {
6367688SAaron.Zang@Sun.COM 	if ((pmode->mode != VT_PROCESS) && (pmode->mode != VT_AUTO))
6377688SAaron.Zang@Sun.COM 		return (EINVAL);
6387688SAaron.Zang@Sun.COM 
6397688SAaron.Zang@Sun.COM 	if (!SIG_VALID(pmode->relsig) || !SIG_VALID(pmode->acqsig))
6407688SAaron.Zang@Sun.COM 		return (EINVAL);
6417688SAaron.Zang@Sun.COM 
6427688SAaron.Zang@Sun.COM 	if (pmode->mode == VT_PROCESS) {
6437688SAaron.Zang@Sun.COM 		pvc->vc_pid = curproc->p_pid;
6447688SAaron.Zang@Sun.COM 	} else {
6457688SAaron.Zang@Sun.COM 		pvc->vc_dispnum = 0;
6467688SAaron.Zang@Sun.COM 		pvc->vc_login = 0;
6477688SAaron.Zang@Sun.COM 	}
6487688SAaron.Zang@Sun.COM 
6497688SAaron.Zang@Sun.COM 	pvc->vc_switch_mode = pmode->mode;
6507688SAaron.Zang@Sun.COM 	pvc->vc_waitv = pmode->waitv;
6517688SAaron.Zang@Sun.COM 	pvc->vc_relsig = pmode->relsig;
6527688SAaron.Zang@Sun.COM 	pvc->vc_acqsig = pmode->acqsig;
6537688SAaron.Zang@Sun.COM 
6547688SAaron.Zang@Sun.COM 	return (0);
6557688SAaron.Zang@Sun.COM }
6567688SAaron.Zang@Sun.COM 
6577688SAaron.Zang@Sun.COM static void
6587688SAaron.Zang@Sun.COM vt_reset(vc_state_t *pvc)
6597688SAaron.Zang@Sun.COM {
6607688SAaron.Zang@Sun.COM 	pvc->vc_switch_mode = VT_AUTO;
6617688SAaron.Zang@Sun.COM 	pvc->vc_pid = -1;
6627688SAaron.Zang@Sun.COM 	pvc->vc_dispnum = 0;
6637688SAaron.Zang@Sun.COM 	pvc->vc_login = 0;
6647688SAaron.Zang@Sun.COM 	pvc->vc_switchto = VT_MINOR_INVALID;
6657688SAaron.Zang@Sun.COM }
6667688SAaron.Zang@Sun.COM 
6677688SAaron.Zang@Sun.COM /*
6687688SAaron.Zang@Sun.COM  * switch to vt_no from vc_active_console
6697688SAaron.Zang@Sun.COM  */
67010977SAaron.Zang@Sun.COM static int
6717688SAaron.Zang@Sun.COM vt_switch(uint_t vt_no, cred_t *credp)
6727688SAaron.Zang@Sun.COM {
6737688SAaron.Zang@Sun.COM 	vc_state_t *pvc_active = vt_minor2vc(vc_active_console);
6747688SAaron.Zang@Sun.COM 	vc_state_t *pvc = vt_minor2vc(vt_no);
6757688SAaron.Zang@Sun.COM 	minor_t index;
6767688SAaron.Zang@Sun.COM 
6777688SAaron.Zang@Sun.COM 	ASSERT(pvc_active && pvc);
6787688SAaron.Zang@Sun.COM 
67910977SAaron.Zang@Sun.COM 	/* sanity test for the target VT and the active VT */
68010977SAaron.Zang@Sun.COM 	if (!((pvc->vc_flags & WCS_ISOPEN) && (pvc->vc_flags & WCS_INIT)))
68110977SAaron.Zang@Sun.COM 		return (EINVAL);
68210977SAaron.Zang@Sun.COM 
68310977SAaron.Zang@Sun.COM 	if (!((pvc_active->vc_flags & WCS_ISOPEN) &&
68410977SAaron.Zang@Sun.COM 	    (pvc_active->vc_flags & WCS_INIT)))
68510977SAaron.Zang@Sun.COM 		return (EINVAL);
68610977SAaron.Zang@Sun.COM 
6877688SAaron.Zang@Sun.COM 	mutex_enter(&vc_lock);
6887688SAaron.Zang@Sun.COM 
6897688SAaron.Zang@Sun.COM 	tem_switch(pvc_active->vc_tem, pvc->vc_tem, credp);
6907688SAaron.Zang@Sun.COM 
6917688SAaron.Zang@Sun.COM 	if (!VT_IS_DAEMON(vc_active_console))
6927688SAaron.Zang@Sun.COM 		vc_last_console = vc_active_console;
6937688SAaron.Zang@Sun.COM 	else
6947688SAaron.Zang@Sun.COM 		vc_last_console = vt_arg2minor(vc_target_console);
6957688SAaron.Zang@Sun.COM 
6967688SAaron.Zang@Sun.COM 	vc_active_console = pvc->vc_minor;
6977688SAaron.Zang@Sun.COM 
6987688SAaron.Zang@Sun.COM 	if (pvc->vc_switch_mode == VT_PROCESS) {
6997688SAaron.Zang@Sun.COM 		pvc->vc_switchto = pvc->vc_minor;
7007688SAaron.Zang@Sun.COM 
7017688SAaron.Zang@Sun.COM 		/* send it an acquired signal */
7027688SAaron.Zang@Sun.COM 		vt_proc_sendsig(pvc->vc_pid, pvc->vc_acqsig);
7037688SAaron.Zang@Sun.COM 	}
7047688SAaron.Zang@Sun.COM 
7057688SAaron.Zang@Sun.COM 	vc_waitactive_reply(vc_active_console, B_FALSE);
7067688SAaron.Zang@Sun.COM 
7077688SAaron.Zang@Sun.COM 	mutex_exit(&vc_lock);
7087688SAaron.Zang@Sun.COM 
7097688SAaron.Zang@Sun.COM 	if (!VT_IS_DAEMON(vt_no)) {
7107688SAaron.Zang@Sun.COM 		/*
7117688SAaron.Zang@Sun.COM 		 * Applications that open the virtual console device may request
7127688SAaron.Zang@Sun.COM 		 * asynchronous notification of VT switching from a previous VT
7137688SAaron.Zang@Sun.COM 		 * to another one by setting the S_MSG flag in an I_SETSIG
7147688SAaron.Zang@Sun.COM 		 * STREAMS ioctl. Such processes receive a SIGPOLL signal when
7157688SAaron.Zang@Sun.COM 		 * a VT switching succeeds.
7167688SAaron.Zang@Sun.COM 		 */
7177688SAaron.Zang@Sun.COM 		for (index = 0; index < VC_INSTANCES_COUNT; index++) {
7187688SAaron.Zang@Sun.COM 			vc_state_t *tmp_pvc = vt_minor2vc(index);
7197688SAaron.Zang@Sun.COM 			mblk_t *mp;
7207688SAaron.Zang@Sun.COM 
7217688SAaron.Zang@Sun.COM 			if ((tmp_pvc->vc_flags & WCS_ISOPEN) &&
7227688SAaron.Zang@Sun.COM 			    (tmp_pvc->vc_flags & WCS_INIT) &&
7237688SAaron.Zang@Sun.COM 			    (mp = allocb(sizeof (unsigned char), BPRI_HI))) {
7247688SAaron.Zang@Sun.COM 				mp->b_datap->db_type = M_PCSIG;
7257688SAaron.Zang@Sun.COM 				*mp->b_wptr = SIGPOLL;
7267688SAaron.Zang@Sun.COM 				mp->b_wptr += sizeof (unsigned char);
7277688SAaron.Zang@Sun.COM 				putnext(RD(tmp_pvc->vc_wq), mp);
7287688SAaron.Zang@Sun.COM 			}
7297688SAaron.Zang@Sun.COM 		}
7307688SAaron.Zang@Sun.COM 	}
7317688SAaron.Zang@Sun.COM 
73210977SAaron.Zang@Sun.COM 	return (0);
73310977SAaron.Zang@Sun.COM 
7347688SAaron.Zang@Sun.COM }
7357688SAaron.Zang@Sun.COM 
7367688SAaron.Zang@Sun.COM /*
7377688SAaron.Zang@Sun.COM  * vt_no	from 0 to n
7387688SAaron.Zang@Sun.COM  *
7397688SAaron.Zang@Sun.COM  * 0	for the vtdaemon sepcial console (only vtdaemon will use it)
7407688SAaron.Zang@Sun.COM  * 1    for the system console (Alt + F1, or Alt + Ctrl + F1),
7417688SAaron.Zang@Sun.COM  *      aka Virtual Console #1
7427688SAaron.Zang@Sun.COM  *
7437688SAaron.Zang@Sun.COM  * 2    for Virtual Console #2
7447688SAaron.Zang@Sun.COM  * n    for Virtual Console #n
7457688SAaron.Zang@Sun.COM  */
7467688SAaron.Zang@Sun.COM static minor_t
7477688SAaron.Zang@Sun.COM vt_arg2minor(uint_t arg)
7487688SAaron.Zang@Sun.COM {
7497688SAaron.Zang@Sun.COM 	if (arg == 0)
7507688SAaron.Zang@Sun.COM 		return (1);
7517688SAaron.Zang@Sun.COM 
7527688SAaron.Zang@Sun.COM 	if (arg == 1)
7537688SAaron.Zang@Sun.COM 		return (0);
7547688SAaron.Zang@Sun.COM 
7557688SAaron.Zang@Sun.COM 	return (arg);
7567688SAaron.Zang@Sun.COM }
7577688SAaron.Zang@Sun.COM 
7587688SAaron.Zang@Sun.COM static uint_t
7597688SAaron.Zang@Sun.COM vt_minor2arg(minor_t minor)
7607688SAaron.Zang@Sun.COM {
7617688SAaron.Zang@Sun.COM 	if (minor == 0)
7627688SAaron.Zang@Sun.COM 		return (1);
7637688SAaron.Zang@Sun.COM 
7647688SAaron.Zang@Sun.COM 	if (VT_IS_DAEMON(minor)) {
7657688SAaron.Zang@Sun.COM 		/* here it should be the real console */
7667688SAaron.Zang@Sun.COM 		return (vc_target_console);
7677688SAaron.Zang@Sun.COM 	}
7687688SAaron.Zang@Sun.COM 
7697688SAaron.Zang@Sun.COM 	return (minor);
7707688SAaron.Zang@Sun.COM }
7717688SAaron.Zang@Sun.COM 
7727688SAaron.Zang@Sun.COM static int
7737688SAaron.Zang@Sun.COM vt_activate(uint_t vt_no, cred_t *credp)
7747688SAaron.Zang@Sun.COM {
7757688SAaron.Zang@Sun.COM 	vc_state_t *pvc;
7767688SAaron.Zang@Sun.COM 	minor_t minor;
7777688SAaron.Zang@Sun.COM 
7787688SAaron.Zang@Sun.COM 	minor = vt_arg2minor(vt_no);
7797688SAaron.Zang@Sun.COM 	if (!vt_minor_valid(minor))
7807688SAaron.Zang@Sun.COM 		return (ENXIO);
7817688SAaron.Zang@Sun.COM 	if (minor == vc_active_console) {
7827688SAaron.Zang@Sun.COM 		if (VT_IS_DAEMON(minor)) {
7837688SAaron.Zang@Sun.COM 			/*
7847688SAaron.Zang@Sun.COM 			 * vtdaemon is reactivating itself to do locking
7857688SAaron.Zang@Sun.COM 			 * on behalf of another console, so record current
7867688SAaron.Zang@Sun.COM 			 * target console as the last console.
7877688SAaron.Zang@Sun.COM 			 */
7887688SAaron.Zang@Sun.COM 			vc_last_console = vt_arg2minor(vc_target_console);
7897688SAaron.Zang@Sun.COM 		}
7907688SAaron.Zang@Sun.COM 
7917688SAaron.Zang@Sun.COM 		return (0);
7927688SAaron.Zang@Sun.COM 	}
7937688SAaron.Zang@Sun.COM 
7947688SAaron.Zang@Sun.COM 	/*
7957688SAaron.Zang@Sun.COM 	 * In tipline case, the system console is redirected to tipline
7967688SAaron.Zang@Sun.COM 	 * and thus is always available.
7977688SAaron.Zang@Sun.COM 	 */
7987688SAaron.Zang@Sun.COM 	if (minor == 0 && consconfig_console_is_tipline())
7997688SAaron.Zang@Sun.COM 		return (0);
8007688SAaron.Zang@Sun.COM 
8017688SAaron.Zang@Sun.COM 	if (!VT_IS_INUSE(minor))
8027688SAaron.Zang@Sun.COM 		return (ENXIO);
8037688SAaron.Zang@Sun.COM 
8047688SAaron.Zang@Sun.COM 	pvc = vt_minor2vc(minor);
8057688SAaron.Zang@Sun.COM 	if (pvc == NULL)
8067688SAaron.Zang@Sun.COM 		return (ENXIO);
8077688SAaron.Zang@Sun.COM 	if (pvc->vc_tem == NULL)
8087688SAaron.Zang@Sun.COM 		return (ENXIO);
8097688SAaron.Zang@Sun.COM 
8107688SAaron.Zang@Sun.COM 	pvc = vt_minor2vc(vc_active_console);
8117688SAaron.Zang@Sun.COM 	if (pvc == NULL)
8127688SAaron.Zang@Sun.COM 		return (ENXIO);
81310977SAaron.Zang@Sun.COM 	if (pvc->vc_switch_mode != VT_PROCESS)
81410977SAaron.Zang@Sun.COM 		return (vt_switch(minor, credp));
8157688SAaron.Zang@Sun.COM 
8167688SAaron.Zang@Sun.COM 	/*
8177688SAaron.Zang@Sun.COM 	 * Validate the process, reset the
8187688SAaron.Zang@Sun.COM 	 * vt to auto mode if failed.
8197688SAaron.Zang@Sun.COM 	 */
8207688SAaron.Zang@Sun.COM 	if (pvc->vc_pid == -1 || vt_proc_exists(pvc->vc_pid) != 0) {
8217688SAaron.Zang@Sun.COM 		/*
8227688SAaron.Zang@Sun.COM 		 * Xserver has not started up yet,
8237688SAaron.Zang@Sun.COM 		 * or it dose not exist.
8247688SAaron.Zang@Sun.COM 		 */
8257688SAaron.Zang@Sun.COM 		vt_reset(pvc);
8267688SAaron.Zang@Sun.COM 		return (0);
8277688SAaron.Zang@Sun.COM 	}
8287688SAaron.Zang@Sun.COM 
8297688SAaron.Zang@Sun.COM 	/*
8307688SAaron.Zang@Sun.COM 	 * Send the release signal to the process,
8317688SAaron.Zang@Sun.COM 	 * and wait VT_RELDISP ioctl from Xserver
8327688SAaron.Zang@Sun.COM 	 * after its leaving VT.
8337688SAaron.Zang@Sun.COM 	 */
8347688SAaron.Zang@Sun.COM 	vt_proc_sendsig(pvc->vc_pid, pvc->vc_relsig);
8357688SAaron.Zang@Sun.COM 	pvc->vc_switchto = minor;
8367688SAaron.Zang@Sun.COM 
8377688SAaron.Zang@Sun.COM 	/*
8387688SAaron.Zang@Sun.COM 	 * We don't need a timeout here, for if Xserver refuses
8397688SAaron.Zang@Sun.COM 	 * or fails to respond to release signal using VT_RELDISP,
8407688SAaron.Zang@Sun.COM 	 * we cannot successfully switch to our text mode. Actually
8417688SAaron.Zang@Sun.COM 	 * users can try again. At present we don't support force
8427688SAaron.Zang@Sun.COM 	 * switch.
8437688SAaron.Zang@Sun.COM 	 */
8447688SAaron.Zang@Sun.COM 	return (0);
8457688SAaron.Zang@Sun.COM }
8467688SAaron.Zang@Sun.COM 
8477688SAaron.Zang@Sun.COM static int
8487688SAaron.Zang@Sun.COM vt_reldisp(vc_state_t *pvc, int arg, cred_t *credp)
8497688SAaron.Zang@Sun.COM {
8507688SAaron.Zang@Sun.COM 	minor_t target_vtno = pvc->vc_switchto;
8517688SAaron.Zang@Sun.COM 
8527688SAaron.Zang@Sun.COM 	if ((pvc->vc_switch_mode != VT_PROCESS) ||
8537688SAaron.Zang@Sun.COM 	    (pvc->vc_minor != vc_active_console))
8547688SAaron.Zang@Sun.COM 		return (EACCES);
8557688SAaron.Zang@Sun.COM 
8567688SAaron.Zang@Sun.COM 	if (target_vtno == VT_MINOR_INVALID)
8577688SAaron.Zang@Sun.COM 		return (EINVAL);
8587688SAaron.Zang@Sun.COM 
8597688SAaron.Zang@Sun.COM 	pvc->vc_switchto = VT_MINOR_INVALID;
8607688SAaron.Zang@Sun.COM 
8617688SAaron.Zang@Sun.COM 	if (arg == VT_ACKACQ)
8627688SAaron.Zang@Sun.COM 		return (0);
8637688SAaron.Zang@Sun.COM 
8647688SAaron.Zang@Sun.COM 	if (arg == 0)
8657688SAaron.Zang@Sun.COM 		return (0); /* refuse to release */
8667688SAaron.Zang@Sun.COM 
8677688SAaron.Zang@Sun.COM 	/* Xserver has left VT */
86810977SAaron.Zang@Sun.COM 	return (vt_switch(target_vtno, credp));
8697688SAaron.Zang@Sun.COM }
8707688SAaron.Zang@Sun.COM 
8717688SAaron.Zang@Sun.COM void
8727688SAaron.Zang@Sun.COM vt_ioctl(queue_t *q, mblk_t *mp)
8737688SAaron.Zang@Sun.COM {
8747688SAaron.Zang@Sun.COM 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
8757688SAaron.Zang@Sun.COM 	struct iocblk	*iocp;
8767688SAaron.Zang@Sun.COM 	struct vt_mode vtmode;
8777688SAaron.Zang@Sun.COM 	struct vt_stat vtinfo;
8787688SAaron.Zang@Sun.COM 	struct vt_dispinfo vtdisp;
8797688SAaron.Zang@Sun.COM 	mblk_t *tmp;
8807688SAaron.Zang@Sun.COM 	int minor;
8817688SAaron.Zang@Sun.COM 	int arg;
8827688SAaron.Zang@Sun.COM 	int error = 0;
8837688SAaron.Zang@Sun.COM 	vc_waitactive_msg_t *wait_msg;
8847688SAaron.Zang@Sun.COM 
8857688SAaron.Zang@Sun.COM 	iocp = (struct iocblk *)(void *)mp->b_rptr;
8867688SAaron.Zang@Sun.COM 	if (consmode != CONS_KFB && iocp->ioc_cmd != VT_ENABLED) {
8877688SAaron.Zang@Sun.COM 		vt_iocnak(q, mp, EINVAL);
8887688SAaron.Zang@Sun.COM 		return;
8897688SAaron.Zang@Sun.COM 	}
8907688SAaron.Zang@Sun.COM 
8917688SAaron.Zang@Sun.COM 	switch (iocp->ioc_cmd) {
8927688SAaron.Zang@Sun.COM 	case VT_ENABLED:
8937688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
8947688SAaron.Zang@Sun.COM 			error = ENOMEM;
8957688SAaron.Zang@Sun.COM 			break;
8967688SAaron.Zang@Sun.COM 		}
8977688SAaron.Zang@Sun.COM 		*(int *)(void *)tmp->b_rptr = consmode;
8987688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (int);
8997688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (int));
9007688SAaron.Zang@Sun.COM 		return;
9017688SAaron.Zang@Sun.COM 
9027688SAaron.Zang@Sun.COM 	case KDSETMODE:
9037688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
9047688SAaron.Zang@Sun.COM 		if (arg != KD_TEXT && arg != KD_GRAPHICS) {
9057688SAaron.Zang@Sun.COM 			error = EINVAL;
9067688SAaron.Zang@Sun.COM 			break;
9077688SAaron.Zang@Sun.COM 		}
9087688SAaron.Zang@Sun.COM 		if (tem_get_fbmode(pvc->vc_tem) == arg)
9097688SAaron.Zang@Sun.COM 			break;
9107688SAaron.Zang@Sun.COM 
9117688SAaron.Zang@Sun.COM 		tem_set_fbmode(pvc->vc_tem, (uchar_t)arg, iocp->ioc_cr);
9127688SAaron.Zang@Sun.COM 
9137688SAaron.Zang@Sun.COM 		break;
9147688SAaron.Zang@Sun.COM 
9157688SAaron.Zang@Sun.COM 	case KDGETMODE:
9167688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
9177688SAaron.Zang@Sun.COM 			error = ENOMEM;
9187688SAaron.Zang@Sun.COM 			break;
9197688SAaron.Zang@Sun.COM 		}
9207688SAaron.Zang@Sun.COM 		*(int *)(void *)tmp->b_rptr = tem_get_fbmode(pvc->vc_tem);
9217688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (int);
9227688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (int));
9237688SAaron.Zang@Sun.COM 		return;
9247688SAaron.Zang@Sun.COM 
9257688SAaron.Zang@Sun.COM 	case VT_OPENQRY: /* return number of first free VT */
9267688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
9277688SAaron.Zang@Sun.COM 			error = ENOMEM;
9287688SAaron.Zang@Sun.COM 			break;
9297688SAaron.Zang@Sun.COM 		}
9307688SAaron.Zang@Sun.COM 
9317688SAaron.Zang@Sun.COM 		/* minors of 0 and 1 are not available to end users */
9327688SAaron.Zang@Sun.COM 		for (minor = 2; vt_minor_valid(minor); minor++)
9337688SAaron.Zang@Sun.COM 			if (!VT_IS_INUSE(minor))
9347688SAaron.Zang@Sun.COM 				break;
9357688SAaron.Zang@Sun.COM 
9367688SAaron.Zang@Sun.COM 		if (!vt_minor_valid(minor))
9377688SAaron.Zang@Sun.COM 			minor = -1;
9387688SAaron.Zang@Sun.COM 		*(int *)(void *)tmp->b_rptr = minor; /* /dev/vt/minor */
9397688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (int);
9407688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (int));
9417688SAaron.Zang@Sun.COM 		return;
9427688SAaron.Zang@Sun.COM 
9437688SAaron.Zang@Sun.COM 	case VT_GETMODE:
9447688SAaron.Zang@Sun.COM 		vtmode.mode = pvc->vc_switch_mode;
9457688SAaron.Zang@Sun.COM 		vtmode.waitv = pvc->vc_waitv;
9467688SAaron.Zang@Sun.COM 		vtmode.relsig = pvc->vc_relsig;
9477688SAaron.Zang@Sun.COM 		vtmode.acqsig = pvc->vc_acqsig;
9487688SAaron.Zang@Sun.COM 		vtmode.frsig = 0;
9497688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (struct vt_mode), BPRI_MED))) {
9507688SAaron.Zang@Sun.COM 			error = ENOMEM;
9517688SAaron.Zang@Sun.COM 			break;
9527688SAaron.Zang@Sun.COM 		}
9537688SAaron.Zang@Sun.COM 		*(struct vt_mode *)(void *)tmp->b_rptr = vtmode;
9547688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (struct vt_mode);
9557688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (struct vt_mode));
9567688SAaron.Zang@Sun.COM 		return;
9577688SAaron.Zang@Sun.COM 
9587688SAaron.Zang@Sun.COM 	case VT_SETMODE:
9597688SAaron.Zang@Sun.COM 		vt_copyin(q, mp, sizeof (struct vt_mode));
9607688SAaron.Zang@Sun.COM 		return;
9617688SAaron.Zang@Sun.COM 
9627688SAaron.Zang@Sun.COM 	case VT_SETDISPINFO:
9637688SAaron.Zang@Sun.COM 		/* always enforce sys_devices privilege for setdispinfo */
9647688SAaron.Zang@Sun.COM 		if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
9657688SAaron.Zang@Sun.COM 			break;
9667688SAaron.Zang@Sun.COM 
9677688SAaron.Zang@Sun.COM 		pvc->vc_dispnum = *(intptr_t *)(void *)mp->b_cont->b_rptr;
9687688SAaron.Zang@Sun.COM 		break;
9697688SAaron.Zang@Sun.COM 
9707688SAaron.Zang@Sun.COM 	case VT_SETDISPLOGIN:
9717688SAaron.Zang@Sun.COM 		pvc->vc_login = *(intptr_t *)(void *)mp->b_cont->b_rptr;
9727688SAaron.Zang@Sun.COM 		break;
9737688SAaron.Zang@Sun.COM 
9747688SAaron.Zang@Sun.COM 	case VT_GETDISPINFO:
9757688SAaron.Zang@Sun.COM 		vtdisp.v_pid = pvc->vc_pid;
9767688SAaron.Zang@Sun.COM 		vtdisp.v_dispnum = pvc->vc_dispnum;
9777688SAaron.Zang@Sun.COM 		vtdisp.v_login = pvc->vc_login;
9787688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (struct vt_dispinfo), BPRI_MED))) {
9797688SAaron.Zang@Sun.COM 			error = ENOMEM;
9807688SAaron.Zang@Sun.COM 			break;
9817688SAaron.Zang@Sun.COM 		}
9827688SAaron.Zang@Sun.COM 		*(struct vt_dispinfo *)(void *)tmp->b_rptr = vtdisp;
9837688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (struct vt_dispinfo);
9847688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (struct vt_dispinfo));
9857688SAaron.Zang@Sun.COM 		return;
9867688SAaron.Zang@Sun.COM 
9877688SAaron.Zang@Sun.COM 	case VT_RELDISP:
9887688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
9897688SAaron.Zang@Sun.COM 		error = vt_reldisp(pvc, arg, iocp->ioc_cr);
9907688SAaron.Zang@Sun.COM 		break;
9917688SAaron.Zang@Sun.COM 
9927688SAaron.Zang@Sun.COM 	case VT_CONFIG:
9937688SAaron.Zang@Sun.COM 		/* always enforce sys_devices privilege for config */
9947688SAaron.Zang@Sun.COM 		if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
9957688SAaron.Zang@Sun.COM 			break;
9967688SAaron.Zang@Sun.COM 
9977688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
9987688SAaron.Zang@Sun.COM 		error = vt_config(arg);
9997688SAaron.Zang@Sun.COM 		break;
10007688SAaron.Zang@Sun.COM 
10017688SAaron.Zang@Sun.COM 	case VT_ACTIVATE:
10027688SAaron.Zang@Sun.COM 		/* always enforce sys_devices privilege for secure switch */
10037688SAaron.Zang@Sun.COM 		if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
10047688SAaron.Zang@Sun.COM 			break;
10057688SAaron.Zang@Sun.COM 
10067688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
10077688SAaron.Zang@Sun.COM 		error = vt_activate(arg, iocp->ioc_cr);
10087688SAaron.Zang@Sun.COM 		break;
10097688SAaron.Zang@Sun.COM 
10107688SAaron.Zang@Sun.COM 	case VT_WAITACTIVE:
10117688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
10127688SAaron.Zang@Sun.COM 		arg = vt_arg2minor(arg);
10137688SAaron.Zang@Sun.COM 		if (!vt_minor_valid(arg)) {
10147688SAaron.Zang@Sun.COM 			error = ENXIO;
10157688SAaron.Zang@Sun.COM 			break;
10167688SAaron.Zang@Sun.COM 		}
10177688SAaron.Zang@Sun.COM 		if (arg == vc_active_console)
10187688SAaron.Zang@Sun.COM 			break;
10197688SAaron.Zang@Sun.COM 
10207688SAaron.Zang@Sun.COM 		wait_msg = kmem_zalloc(sizeof (vc_waitactive_msg_t),
10217688SAaron.Zang@Sun.COM 		    KM_NOSLEEP);
10227688SAaron.Zang@Sun.COM 		if (wait_msg == NULL) {
10237688SAaron.Zang@Sun.COM 			error = ENXIO;
10247688SAaron.Zang@Sun.COM 			break;
10257688SAaron.Zang@Sun.COM 		}
10267688SAaron.Zang@Sun.COM 
10277688SAaron.Zang@Sun.COM 		wait_msg->wa_mp = mp;
10287688SAaron.Zang@Sun.COM 		wait_msg->wa_msg_minor = pvc->vc_minor;
10297688SAaron.Zang@Sun.COM 		wait_msg->wa_wait_minor = arg;
10307688SAaron.Zang@Sun.COM 		list_insert_head(&vc_waitactive_list, wait_msg);
10317688SAaron.Zang@Sun.COM 
10327688SAaron.Zang@Sun.COM 		return;
10337688SAaron.Zang@Sun.COM 
10347688SAaron.Zang@Sun.COM 	case VT_GETSTATE:
10357688SAaron.Zang@Sun.COM 		/*
10367688SAaron.Zang@Sun.COM 		 * Here v_active is the argument for vt_activate,
10377688SAaron.Zang@Sun.COM 		 * not minor.
10387688SAaron.Zang@Sun.COM 		 */
10397688SAaron.Zang@Sun.COM 		vtinfo.v_active = vt_minor2arg(vc_active_console);
10407688SAaron.Zang@Sun.COM 		vtinfo.v_state = 3;	/* system console and vtdaemon */
10417688SAaron.Zang@Sun.COM 
10427688SAaron.Zang@Sun.COM 		/* we only support 16 vt states since the v_state is short */
10437688SAaron.Zang@Sun.COM 		for (minor = 2; minor < 16; minor++) {
10447688SAaron.Zang@Sun.COM 			pvc = vt_minor2vc(minor);
10457688SAaron.Zang@Sun.COM 			if (pvc == NULL)
10467688SAaron.Zang@Sun.COM 				break;
10477688SAaron.Zang@Sun.COM 			if (VT_IS_INUSE(minor))
10487688SAaron.Zang@Sun.COM 				vtinfo.v_state |= (1 << pvc->vc_minor);
10497688SAaron.Zang@Sun.COM 		}
10507688SAaron.Zang@Sun.COM 
10517688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (struct vt_stat), BPRI_MED))) {
10527688SAaron.Zang@Sun.COM 			error = ENOMEM;
10537688SAaron.Zang@Sun.COM 			break;
10547688SAaron.Zang@Sun.COM 		}
10557688SAaron.Zang@Sun.COM 		*(struct vt_stat *)(void *)tmp->b_rptr = vtinfo;
10567688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (struct vt_stat);
10577688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (struct vt_stat));
10587688SAaron.Zang@Sun.COM 		return;
10597688SAaron.Zang@Sun.COM 
10607688SAaron.Zang@Sun.COM 	case VT_SET_TARGET:
10617688SAaron.Zang@Sun.COM 		/* always enforce sys_devices privilege */
10627688SAaron.Zang@Sun.COM 		if ((error = secpolicy_console(iocp->ioc_cr)) != 0)
10637688SAaron.Zang@Sun.COM 			break;
10647688SAaron.Zang@Sun.COM 
10657688SAaron.Zang@Sun.COM 		arg = *(intptr_t *)(void *)mp->b_cont->b_rptr;
10667688SAaron.Zang@Sun.COM 
10677688SAaron.Zang@Sun.COM 		/* vtdaemon is doing authentication for this target console */
10687688SAaron.Zang@Sun.COM 		vc_target_console = arg;
10697688SAaron.Zang@Sun.COM 		break;
10707688SAaron.Zang@Sun.COM 
10717688SAaron.Zang@Sun.COM 	case VT_GETACTIVE:	/* get real active console (minor) */
10727688SAaron.Zang@Sun.COM 		if (!(tmp = allocb(sizeof (int), BPRI_MED))) {
10737688SAaron.Zang@Sun.COM 			error = ENOMEM;
10747688SAaron.Zang@Sun.COM 			break;
10757688SAaron.Zang@Sun.COM 		}
10767688SAaron.Zang@Sun.COM 		*(int *)(void *)tmp->b_rptr = vc_active_console;
10777688SAaron.Zang@Sun.COM 		tmp->b_wptr += sizeof (int);
10787688SAaron.Zang@Sun.COM 		vt_copyout(q, mp, tmp, sizeof (int));
10797688SAaron.Zang@Sun.COM 		return;
10807688SAaron.Zang@Sun.COM 
10817688SAaron.Zang@Sun.COM 	default:
10827688SAaron.Zang@Sun.COM 		error = ENXIO;
10837688SAaron.Zang@Sun.COM 		break;
10847688SAaron.Zang@Sun.COM 	}
10857688SAaron.Zang@Sun.COM 
10867688SAaron.Zang@Sun.COM 	if (error != 0)
10877688SAaron.Zang@Sun.COM 		vt_iocnak(q, mp, error);
10887688SAaron.Zang@Sun.COM 	else
10897688SAaron.Zang@Sun.COM 		vt_iocack(q, mp);
10907688SAaron.Zang@Sun.COM }
10917688SAaron.Zang@Sun.COM 
10927688SAaron.Zang@Sun.COM void
10937688SAaron.Zang@Sun.COM vt_miocdata(queue_t *qp, mblk_t *mp)
10947688SAaron.Zang@Sun.COM {
10957688SAaron.Zang@Sun.COM 	vc_state_t *pvc = (vc_state_t *)qp->q_ptr;
10967688SAaron.Zang@Sun.COM 	struct copyresp *copyresp;
10977688SAaron.Zang@Sun.COM 	struct vt_mode *pmode;
10987688SAaron.Zang@Sun.COM 	int error = 0;
10997688SAaron.Zang@Sun.COM 
11007688SAaron.Zang@Sun.COM 	copyresp = (struct copyresp *)(void *)mp->b_rptr;
11017688SAaron.Zang@Sun.COM 	if (copyresp->cp_rval) {
11027688SAaron.Zang@Sun.COM 		vt_iocnak(qp, mp, EAGAIN);
11037688SAaron.Zang@Sun.COM 		return;
11047688SAaron.Zang@Sun.COM 	}
11057688SAaron.Zang@Sun.COM 
11067688SAaron.Zang@Sun.COM 	switch (copyresp->cp_cmd) {
11077688SAaron.Zang@Sun.COM 	case VT_SETMODE:
11087688SAaron.Zang@Sun.COM 		pmode = (struct vt_mode *)(void *)mp->b_cont->b_rptr;
11097688SAaron.Zang@Sun.COM 		error = vt_setmode(pvc, pmode);
11107688SAaron.Zang@Sun.COM 		break;
11117688SAaron.Zang@Sun.COM 
11127688SAaron.Zang@Sun.COM 	case KDGETMODE:
11137688SAaron.Zang@Sun.COM 	case VT_OPENQRY:
11147688SAaron.Zang@Sun.COM 	case VT_GETMODE:
11157688SAaron.Zang@Sun.COM 	case VT_GETDISPINFO:
11167688SAaron.Zang@Sun.COM 	case VT_GETSTATE:
11177688SAaron.Zang@Sun.COM 	case VT_ENABLED:
11187688SAaron.Zang@Sun.COM 	case VT_GETACTIVE:
11197688SAaron.Zang@Sun.COM 		break;
11207688SAaron.Zang@Sun.COM 
11217688SAaron.Zang@Sun.COM 	default:
11227688SAaron.Zang@Sun.COM 		error = ENXIO;
11237688SAaron.Zang@Sun.COM 		break;
11247688SAaron.Zang@Sun.COM 	}
11257688SAaron.Zang@Sun.COM 
11267688SAaron.Zang@Sun.COM 	if (error != 0)
11277688SAaron.Zang@Sun.COM 		vt_iocnak(qp, mp, error);
11287688SAaron.Zang@Sun.COM 	else
11297688SAaron.Zang@Sun.COM 		vt_iocack(qp, mp);
11307688SAaron.Zang@Sun.COM }
11317688SAaron.Zang@Sun.COM 
11327688SAaron.Zang@Sun.COM static void
11337688SAaron.Zang@Sun.COM vt_iocack(queue_t *qp, mblk_t *mp)
11347688SAaron.Zang@Sun.COM {
11357688SAaron.Zang@Sun.COM 	struct iocblk	*iocbp = (struct iocblk *)(void *)mp->b_rptr;
11367688SAaron.Zang@Sun.COM 
11377688SAaron.Zang@Sun.COM 	mp->b_datap->db_type = M_IOCACK;
11387688SAaron.Zang@Sun.COM 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
11397688SAaron.Zang@Sun.COM 	iocbp->ioc_error = 0;
11407688SAaron.Zang@Sun.COM 	iocbp->ioc_count = 0;
11417688SAaron.Zang@Sun.COM 	iocbp->ioc_rval = 0;
11427688SAaron.Zang@Sun.COM 	if (mp->b_cont != NULL) {
11437688SAaron.Zang@Sun.COM 		freemsg(mp->b_cont);
11447688SAaron.Zang@Sun.COM 		mp->b_cont = NULL;
11457688SAaron.Zang@Sun.COM 	}
11467688SAaron.Zang@Sun.COM 	qreply(qp, mp);
11477688SAaron.Zang@Sun.COM }
11487688SAaron.Zang@Sun.COM 
11497688SAaron.Zang@Sun.COM static void
11507688SAaron.Zang@Sun.COM vt_iocnak(queue_t *qp, mblk_t *mp, int error)
11517688SAaron.Zang@Sun.COM {
11527688SAaron.Zang@Sun.COM 	struct iocblk *iocp = (struct iocblk *)(void *)mp->b_rptr;
11537688SAaron.Zang@Sun.COM 
11547688SAaron.Zang@Sun.COM 	mp->b_datap->db_type = M_IOCNAK;
11557688SAaron.Zang@Sun.COM 	iocp->ioc_rval = 0;
11567688SAaron.Zang@Sun.COM 	iocp->ioc_count = 0;
11577688SAaron.Zang@Sun.COM 	iocp->ioc_error = error;
11587688SAaron.Zang@Sun.COM 	if (mp->b_cont != NULL) {
11597688SAaron.Zang@Sun.COM 		freemsg(mp->b_cont);
11607688SAaron.Zang@Sun.COM 		mp->b_cont = NULL;
11617688SAaron.Zang@Sun.COM 	}
11627688SAaron.Zang@Sun.COM 	qreply(qp, mp);
11637688SAaron.Zang@Sun.COM }
11647688SAaron.Zang@Sun.COM 
11657688SAaron.Zang@Sun.COM static void
11667688SAaron.Zang@Sun.COM vt_copyin(queue_t *qp, mblk_t *mp, uint_t size)
11677688SAaron.Zang@Sun.COM {
11687688SAaron.Zang@Sun.COM 	struct copyreq  *cqp;
11697688SAaron.Zang@Sun.COM 
11707688SAaron.Zang@Sun.COM 	cqp = (struct copyreq *)(void *)mp->b_rptr;
11717688SAaron.Zang@Sun.COM 	cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr);
11727688SAaron.Zang@Sun.COM 	cqp->cq_size = size;
11737688SAaron.Zang@Sun.COM 	cqp->cq_flag = 0;
11747688SAaron.Zang@Sun.COM 	cqp->cq_private = (mblk_t *)NULL;
11757688SAaron.Zang@Sun.COM 	mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
11767688SAaron.Zang@Sun.COM 	mp->b_datap->db_type = M_COPYIN;
11777688SAaron.Zang@Sun.COM 	if (mp->b_cont)
11787688SAaron.Zang@Sun.COM 		freemsg(mp->b_cont);
11797688SAaron.Zang@Sun.COM 	mp->b_cont = (mblk_t *)NULL;
11807688SAaron.Zang@Sun.COM 	qreply(qp, mp);
11817688SAaron.Zang@Sun.COM }
11827688SAaron.Zang@Sun.COM 
11837688SAaron.Zang@Sun.COM static void
11847688SAaron.Zang@Sun.COM vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size)
11857688SAaron.Zang@Sun.COM {
11867688SAaron.Zang@Sun.COM 	struct copyreq  *cqp;
11877688SAaron.Zang@Sun.COM 
11887688SAaron.Zang@Sun.COM 	cqp = (struct copyreq *)(void *)mp->b_rptr;
11897688SAaron.Zang@Sun.COM 	cqp->cq_size = size;
11907688SAaron.Zang@Sun.COM 	cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr);
11917688SAaron.Zang@Sun.COM 	cqp->cq_flag = 0;
11927688SAaron.Zang@Sun.COM 	cqp->cq_private = (mblk_t *)NULL;
11937688SAaron.Zang@Sun.COM 	mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
11947688SAaron.Zang@Sun.COM 	mp->b_datap->db_type = M_COPYOUT;
11957688SAaron.Zang@Sun.COM 	if (mp->b_cont)
11967688SAaron.Zang@Sun.COM 		freemsg(mp->b_cont);
11977688SAaron.Zang@Sun.COM 	mp->b_cont = tmp;
11987688SAaron.Zang@Sun.COM 	qreply(qp, mp);
11997688SAaron.Zang@Sun.COM }
12007688SAaron.Zang@Sun.COM 
12017688SAaron.Zang@Sun.COM /*
12027688SAaron.Zang@Sun.COM  * Get vc state from minor.
12037688SAaron.Zang@Sun.COM  * Once a caller gets a vc_state_t from this function,
12047688SAaron.Zang@Sun.COM  * the vc_state_t is guaranteed not being freed before
12057688SAaron.Zang@Sun.COM  * the caller leaves this STREAMS module by the D_MTPERMOD
12067688SAaron.Zang@Sun.COM  * perimeter.
12077688SAaron.Zang@Sun.COM  */
12087688SAaron.Zang@Sun.COM vc_state_t *
12097688SAaron.Zang@Sun.COM vt_minor2vc(minor_t minor)
12107688SAaron.Zang@Sun.COM {
12117688SAaron.Zang@Sun.COM 	avl_index_t where;
12127688SAaron.Zang@Sun.COM 	vc_state_t target;
12137688SAaron.Zang@Sun.COM 
12147688SAaron.Zang@Sun.COM 	if (minor != VT_ACTIVE) {
12157688SAaron.Zang@Sun.COM 		target.vc_minor = minor;
12167688SAaron.Zang@Sun.COM 		return (avl_find(&vc_avl_root, &target, &where));
12177688SAaron.Zang@Sun.COM 	}
12187688SAaron.Zang@Sun.COM 
12197688SAaron.Zang@Sun.COM 	if (vc_active_console == VT_MINOR_INVALID)
12207688SAaron.Zang@Sun.COM 		target.vc_minor = 0;
12217688SAaron.Zang@Sun.COM 	else
12227688SAaron.Zang@Sun.COM 		target.vc_minor = vc_active_console;
12237688SAaron.Zang@Sun.COM 
12247688SAaron.Zang@Sun.COM 	return (avl_find(&vc_avl_root, &target, &where));
12257688SAaron.Zang@Sun.COM }
12267688SAaron.Zang@Sun.COM 
12277688SAaron.Zang@Sun.COM static void
12287688SAaron.Zang@Sun.COM vt_state_init(vc_state_t *vcptr, minor_t minor)
12297688SAaron.Zang@Sun.COM {
12307688SAaron.Zang@Sun.COM 	mutex_init(&vcptr->vc_state_lock, NULL, MUTEX_DRIVER, NULL);
12317688SAaron.Zang@Sun.COM 
12327688SAaron.Zang@Sun.COM 	mutex_enter(&vcptr->vc_state_lock);
12337688SAaron.Zang@Sun.COM 	vcptr->vc_flags = 0;
12347688SAaron.Zang@Sun.COM 	mutex_exit(&vcptr->vc_state_lock);
12357688SAaron.Zang@Sun.COM 
12367688SAaron.Zang@Sun.COM 	vcptr->vc_pid = -1;
12377688SAaron.Zang@Sun.COM 	vcptr->vc_dispnum = 0;
12387688SAaron.Zang@Sun.COM 	vcptr->vc_login = 0;
12397688SAaron.Zang@Sun.COM 	vcptr->vc_switchto = VT_MINOR_INVALID;
12407688SAaron.Zang@Sun.COM 	vcptr->vc_switch_mode = VT_AUTO;
12417688SAaron.Zang@Sun.COM 	vcptr->vc_relsig = SIGUSR1;
12427688SAaron.Zang@Sun.COM 	vcptr->vc_acqsig = SIGUSR1;
12437688SAaron.Zang@Sun.COM 	vcptr->vc_tem = NULL;
12447688SAaron.Zang@Sun.COM 	vcptr->vc_bufcallid = 0;
12457688SAaron.Zang@Sun.COM 	vcptr->vc_timeoutid = 0;
12467688SAaron.Zang@Sun.COM 	vcptr->vc_wq = NULL;
12477688SAaron.Zang@Sun.COM 	vcptr->vc_minor = minor;
12487688SAaron.Zang@Sun.COM }
12497688SAaron.Zang@Sun.COM 
12507688SAaron.Zang@Sun.COM void
12517688SAaron.Zang@Sun.COM vt_resize(uint_t count)
12527688SAaron.Zang@Sun.COM {
12537688SAaron.Zang@Sun.COM 	uint_t vc_num, i;
12547688SAaron.Zang@Sun.COM 
12557688SAaron.Zang@Sun.COM 	ASSERT(MUTEX_HELD(&vc_lock));
12567688SAaron.Zang@Sun.COM 
12577688SAaron.Zang@Sun.COM 	vc_num = VC_INSTANCES_COUNT;
12587688SAaron.Zang@Sun.COM 
12597688SAaron.Zang@Sun.COM 	if (count == vc_num)
12607688SAaron.Zang@Sun.COM 		return;
12617688SAaron.Zang@Sun.COM 
12627688SAaron.Zang@Sun.COM 	if (count > vc_num) {
12637688SAaron.Zang@Sun.COM 		for (i = vc_num; i < count; i++) {
12647688SAaron.Zang@Sun.COM 			vc_state_t *vcptr = kmem_zalloc(sizeof (vc_state_t),
12657688SAaron.Zang@Sun.COM 			    KM_SLEEP);
12667688SAaron.Zang@Sun.COM 			vt_state_init(vcptr, i);
12677688SAaron.Zang@Sun.COM 			avl_add(&vc_avl_root, vcptr);
12687688SAaron.Zang@Sun.COM 		}
12697688SAaron.Zang@Sun.COM 		return;
12707688SAaron.Zang@Sun.COM 	}
12717688SAaron.Zang@Sun.COM 
12727688SAaron.Zang@Sun.COM 	for (i = vc_num; i > count; i--) {
12737688SAaron.Zang@Sun.COM 		avl_index_t where;
12747688SAaron.Zang@Sun.COM 		vc_state_t target, *found;
12757688SAaron.Zang@Sun.COM 
12767688SAaron.Zang@Sun.COM 		target.vc_minor = i - 1;
12777688SAaron.Zang@Sun.COM 		found = avl_find(&vc_avl_root, &target, &where);
12787688SAaron.Zang@Sun.COM 		ASSERT(found != NULL && found->vc_flags == 0);
12797688SAaron.Zang@Sun.COM 		avl_remove(&vc_avl_root, found);
12807688SAaron.Zang@Sun.COM 		kmem_free(found, sizeof (vc_state_t));
12817688SAaron.Zang@Sun.COM 	}
12827688SAaron.Zang@Sun.COM }
12837688SAaron.Zang@Sun.COM 
12847688SAaron.Zang@Sun.COM static int
12857688SAaron.Zang@Sun.COM vc_avl_compare(const void *first, const void *second)
12867688SAaron.Zang@Sun.COM {
12877688SAaron.Zang@Sun.COM 	const vc_state_t *vcptr1 = first;
12887688SAaron.Zang@Sun.COM 	const vc_state_t *vcptr2 = second;
12897688SAaron.Zang@Sun.COM 
12907688SAaron.Zang@Sun.COM 	if (vcptr1->vc_minor < vcptr2->vc_minor)
12917688SAaron.Zang@Sun.COM 		return (-1);
12927688SAaron.Zang@Sun.COM 
12937688SAaron.Zang@Sun.COM 	if (vcptr1->vc_minor == vcptr2->vc_minor)
12947688SAaron.Zang@Sun.COM 		return (0);
12957688SAaron.Zang@Sun.COM 
12967688SAaron.Zang@Sun.COM 	return (1);
12977688SAaron.Zang@Sun.COM }
12987688SAaron.Zang@Sun.COM 
12997688SAaron.Zang@Sun.COM /*
13007688SAaron.Zang@Sun.COM  * Only called from wc init().
13017688SAaron.Zang@Sun.COM  */
13027688SAaron.Zang@Sun.COM void
13037688SAaron.Zang@Sun.COM vt_init(void)
13047688SAaron.Zang@Sun.COM {
13057688SAaron.Zang@Sun.COM #ifdef	__lock_lint
13067688SAaron.Zang@Sun.COM 	ASSERT(NO_COMPETING_THREADS);
13077688SAaron.Zang@Sun.COM #endif
13087688SAaron.Zang@Sun.COM 
13097688SAaron.Zang@Sun.COM 	avl_create(&vc_avl_root, vc_avl_compare, sizeof (vc_state_t),
13107688SAaron.Zang@Sun.COM 	    offsetof(vc_state_t, vc_avl_node));
13117688SAaron.Zang@Sun.COM 
13127688SAaron.Zang@Sun.COM 	list_create(&vc_waitactive_list, sizeof (vc_waitactive_msg_t),
13137688SAaron.Zang@Sun.COM 	    offsetof(vc_waitactive_msg_t, wa_list_node));
13147688SAaron.Zang@Sun.COM 
13157688SAaron.Zang@Sun.COM 	mutex_init(&vc_lock, NULL, MUTEX_DRIVER, NULL);
13167688SAaron.Zang@Sun.COM 	mutex_init(&vt_pending_vtno_lock, NULL, MUTEX_DRIVER, NULL);
13177688SAaron.Zang@Sun.COM }
1318