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