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