vcons.c revision 12195:cf3a8ea2dcfd
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
107extern void	wc_get_size(vc_state_t *pvc);
108extern boolean_t consconfig_console_is_tipline(void);
109
110
111minor_t vc_last_console = VT_MINOR_INVALID;	/* the last used console */
112volatile uint_t	vc_target_console;		/* arg (1..n) */
113
114static volatile minor_t vc_inuse_max_minor = 0;
115static 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
121static int vt_pending_vtno = -1;
122kmutex_t vt_pending_vtno_lock;
123_NOTE(MUTEX_PROTECTS_DATA(vt_pending_vtno_lock, vt_pending_vtno))
124
125static int vt_activate(uint_t vt_no, cred_t *credp);
126static void vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size);
127static void vt_copyin(queue_t *qp, mblk_t *mp, uint_t size);
128static void vt_iocnak(queue_t *qp, mblk_t *mp, int error);
129static void vt_iocack(queue_t *qp, mblk_t *mp);
130
131static uint_t vt_minor2arg(minor_t minor);
132static 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
163static void
164vt_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
199static int
200vt_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
223void
224vt_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 */
247static void
248vc_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
274void
275vt_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
297static void
298vt_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 */
334int
335vt_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
407static minor_t
408vt_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
423static minor_t
424vt_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 */
440void
441vt_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
487static boolean_t
488vt_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
505static void
506vt_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 */
525int
526vt_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
593static void
594vt_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
611static int
612vt_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
632static int
633vt_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
656static void
657vt_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 */
669static int
670vt_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 */
745static minor_t
746vt_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
757static uint_t
758vt_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
771static int
772vt_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
846static int
847vt_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
870void
871vt_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
1137void
1138vt_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
1177static void
1178vt_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
1194static void
1195vt_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
1210static void
1211vt_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
1228static void
1229vt_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 */
1253vc_state_t *
1254vt_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
1272static void
1273vt_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
1295void
1296vt_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
1329static int
1330vc_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 */
1347void
1348vt_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