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.
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.
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]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/signal.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>
41 #include <sys/sysmacros.h>
42 #include <sys/errno.h>
44 #include <sys/procset.h>
45 #include <sys/fault.h>
46 #include <sys/siginfo.h>
47 #include <sys/debug.h>
50 #include <sys/vtdaemon.h>
51 #include <sys/session.h>
54 #include <sys/cpuvar.h>
56 #include <sys/strredir.h>
57 #include <sys/fs/snode.h>
58 #include <sys/consdev.h>
60 #include <sys/cmn_err.h>
61 #include <sys/console.h>
62 #include <sys/promif.h>
64 #include <sys/polled_io.h>
65 #include <sys/systm.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>
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>
83 * This file belongs to wc STREAMS module which has a D_MTPERMODE
84 * inner perimeter. See "Locking Policy" comment in wscons.c for
89 * Minor name device file Hotkeys
91 * 0 the system console /dev/console Alt + F1
92 * 0: virtual console #1 /dev/vt/0 Alt + F1
94 * 2: virtual console #2 /dev/vt/2 Alt + F2
95 * 3: virtual console #3 /dev/vt/3 Alt + F3
97 * n: virtual console #n /dev/vt/n Alt + Fn
99 * Note that vtdaemon is running on /dev/vt/1 (minor=1),
100 * which is not available to end users.
104 #define VT_DAEMON_MINOR 1
105 #define VT_IS_DAEMON(minor) ((minor) == VT_DAEMON_MINOR)
107 extern void wc_get_size(vc_state_t
*pvc
);
108 extern boolean_t
consconfig_console_is_tipline(void);
111 minor_t vc_last_console
= VT_MINOR_INVALID
; /* the last used console */
112 volatile uint_t vc_target_console
; /* arg (1..n) */
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
))
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
))
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
);
131 static uint_t
vt_minor2arg(minor_t minor
);
132 static minor_t
vt_arg2minor(uint_t arg
);
135 * If the system console is directed to tipline, consider /dev/vt/0 as
137 * For other VT, if it is opened and tty is initialized, consider it
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()))
146 * the vt switching message is encoded as:
148 * -------------------------------------------------------------
149 * | \033 | 'Q' | vtno + 'A' | opcode | 'z' | '\0' |
150 * -------------------------------------------------------------
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')
158 #define VT_MSG_VTNO(mp) (*((mp)->b_rptr + 2) - 'A')
159 #define VT_MSG_OPCODE(mp) (*((mp)->b_rptr + 3))
161 #define VT_DOORCALL_MAX_RETRY 3
164 vt_init_ttycommon(tty_common_t
*pcommon
)
166 struct termios
*termiosp
;
169 mutex_init(&pcommon
->t_excl
, NULL
, MUTEX_DEFAULT
, NULL
);
170 pcommon
->t_iflag
= 0;
173 * Get the default termios settings (cflag).
174 * These are stored as a property in the
177 if (ddi_getlongprop(DDI_DEV_T_ANY
,
178 ddi_root_node(), 0, "ttymodes",
179 (caddr_t
)&termiosp
, &len
) == DDI_PROP_SUCCESS
) {
181 if (len
== sizeof (struct termios
))
182 pcommon
->t_cflag
= termiosp
->c_cflag
;
185 "wc: Couldn't get ttymodes property!");
187 kmem_free(termiosp
, len
);
190 * Gack! Whine about it.
193 "wc: Couldn't get ttymodes property!");
196 pcommon
->t_iocpending
= NULL
;
200 vt_config(uint_t count
)
202 if (consmode
!= CONS_KFB
)
205 /* one for system console, one for vtdaemon */
210 * Shouldn't allow to shrink the max vt minor to be smaller than
211 * the max in used minor.
213 if (count
<= vc_inuse_max_minor
)
216 mutex_enter(&vc_lock
);
218 mutex_exit(&vc_lock
);
224 vt_clean(queue_t
*q
, vc_state_t
*pvc
)
226 ASSERT(MUTEX_HELD(&pvc
->vc_state_lock
));
228 if (pvc
->vc_bufcallid
!= 0) {
229 qunbufcall(q
, pvc
->vc_bufcallid
);
230 pvc
->vc_bufcallid
= 0;
232 if (pvc
->vc_timeoutid
!= 0) {
233 (void) quntimeout(q
, pvc
->vc_timeoutid
);
234 pvc
->vc_timeoutid
= 0;
236 ttycommon_close(&pvc
->vc_ttycommon
);
238 pvc
->vc_flags
&= ~WCS_INIT
;
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.
248 vc_waitactive_reply(int minor
, boolean_t close
)
250 vc_waitactive_msg_t
*index
, *tmp
;
253 index
= list_head(&vc_waitactive_list
);
255 while (index
!= NULL
) {
257 index
= list_next(&vc_waitactive_list
, index
);
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
);
265 vt_iocnak(pvc
->vc_wq
, tmp
->wa_mp
, ENXIO
);
267 vt_iocack(pvc
->vc_wq
, tmp
->wa_mp
);
269 kmem_free(tmp
, sizeof (vc_waitactive_msg_t
));
275 vt_close(queue_t
*q
, vc_state_t
*pvc
, cred_t
*credp
)
279 mutex_enter(&pvc
->vc_state_lock
);
281 pvc
->vc_flags
&= ~WCS_ISOPEN
;
282 mutex_exit(&pvc
->vc_state_lock
);
284 tem_destroy(pvc
->vc_tem
, credp
);
287 index
= pvc
->vc_minor
;
288 if (index
== vc_inuse_max_minor
) {
289 while ((--index
> 0) && !VT_IS_INUSE(index
))
291 vc_inuse_max_minor
= index
;
294 vc_waitactive_reply(pvc
->vc_minor
, B_TRUE
);
298 vt_init_tty(vc_state_t
*pvc
)
300 ASSERT(MUTEX_HELD(&pvc
->vc_state_lock
));
302 pvc
->vc_flags
|= WCS_INIT
;
303 vt_init_ttycommon(&pvc
->vc_ttycommon
);
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)
312 * minor n: /dev/vt/n (index = n)
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.
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'.
323 * minor 1 (/dev/vt/1) is reserved for vtdaemon special console, and it's
324 * not available to end users.
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.
335 vt_open(minor_t minor
, queue_t
*rq
, cred_t
*crp
)
339 if (!vt_minor_valid(minor
))
342 pvc
= vt_minor2vc(minor
);
346 mutex_enter(&vc_lock
);
347 mutex_enter(&pvc
->vc_state_lock
);
349 if (!(pvc
->vc_flags
& WCS_ISOPEN
)) {
351 * vc_tem might not be intialized if !tems.ts_initialized,
352 * and this only happens during console configuration.
354 pvc
->vc_tem
= tem_init(crp
);
357 if (!(pvc
->vc_flags
& WCS_INIT
))
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.
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
;
368 vc_active_console
= minor
;
369 vc_last_console
= minor
;
372 * If we are not opening the system console
373 * as the first console, clear the phyical
379 tem_activate(pvc
->vc_tem
, unblank
, crp
);
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
);
389 if (minor
> vc_inuse_max_minor
)
390 vc_inuse_max_minor
= minor
;
392 pvc
->vc_flags
|= WCS_ISOPEN
;
393 pvc
->vc_ttycommon
.t_readq
= rq
;
394 pvc
->vc_ttycommon
.t_writeq
= WR(rq
);
396 mutex_exit(&pvc
->vc_state_lock
);
397 mutex_exit(&vc_lock
);
408 vt_find_prev(minor_t cur
)
412 ASSERT(vc_active_console
!= VT_MINOR_INVALID
);
414 max
= VC_INSTANCES_COUNT
;
416 for (i
= cur
- 1; (t
= (i
+ max
) % max
) != cur
; i
--)
417 if (!VT_IS_DAEMON(t
) && VT_IS_INUSE(t
))
420 return (VT_MINOR_INVALID
);
424 vt_find_next(minor_t cur
)
428 ASSERT(vc_active_console
!= VT_MINOR_INVALID
);
430 max
= VC_INSTANCES_COUNT
;
432 for (i
= cur
+ 1; (t
= (i
+ max
) % max
) != cur
; i
++)
433 if (!VT_IS_DAEMON(t
) && VT_IS_INUSE(t
))
436 return (VT_MINOR_INVALID
);
441 vt_send_hotkeys(void *timeout_arg
)
449 arg
.vt_ev
= VT_EV_HOTKEYS
;
451 mutex_enter(&vt_pending_vtno_lock
);
452 arg
.vt_num
= vt_pending_vtno
;
453 mutex_exit(&vt_pending_vtno_lock
);
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
);
463 door_arg
.rbuf
= NULL
;
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;
473 while ((error
= door_ki_upcall(door
, &door_arg
)) != 0 &&
474 retries
< VT_DOORCALL_MAX_RETRY
)
475 if (error
== EAGAIN
|| error
== EINTR
)
482 mutex_enter(&vt_pending_vtno_lock
);
483 vt_pending_vtno
= -1;
484 mutex_exit(&vt_pending_vtno_lock
);
488 vt_validate_hotkeys(int minor
)
491 * minor should not succeed the existing minor numbers range.
493 if (!vt_minor_valid(minor
))
497 * Shouldn't switch to /dev/vt/1 or an unused vt.
499 if (!VT_IS_DAEMON(minor
) && VT_IS_INUSE(minor
))
506 vt_trigger_hotkeys(int vtno
)
508 mutex_enter(&vt_pending_vtno_lock
);
510 if (vt_pending_vtno
!= -1) {
511 mutex_exit(&vt_pending_vtno_lock
);
515 vt_pending_vtno
= vtno
;
516 mutex_exit(&vt_pending_vtno_lock
);
517 (void) timeout(vt_send_hotkeys
, NULL
, 1);
522 * 0: non msg of vt hotkeys
523 * 1: msg of vt hotkeys
526 vt_check_hotkeys(mblk_t
*mp
)
531 /* LINTED E_PTRDIFF_OVERFLOW */
532 if (!VT_MSG_SWITCH(mp
))
535 switch (VT_MSG_OPCODE(mp
)) {
537 /* find out the previous vt */
538 if (vc_active_console
== VT_MINOR_INVALID
)
541 if (VT_IS_DAEMON(vc_active_console
)) {
542 minor
= vt_find_prev(vt_arg2minor(vc_target_console
));
546 minor
= vt_find_prev(vc_active_console
);
549 /* find out the next vt */
550 if (vc_active_console
== VT_MINOR_INVALID
)
553 if (VT_IS_DAEMON(vc_active_console
)) {
554 minor
= vt_find_next(vt_arg2minor(vc_target_console
));
558 minor
= vt_find_next(vc_active_console
);
561 /* find out the specified vt */
562 minor
= VT_MSG_VTNO(mp
);
564 /* check for system console, Alt + F1 */
569 /* find out the last vt */
570 if ((minor
= vc_last_console
) == VT_MINOR_INVALID
)
577 if (!vt_validate_hotkeys(minor
))
581 * for system console, the argument of vtno for
582 * vt_activate is 1, though its minor is 0
585 vtno
= 1; /* for system console */
589 vt_trigger_hotkeys(vtno
);
594 vt_proc_sendsig(pid_t pid
, int sig
)
601 mutex_enter(&pidlock
);
602 if ((p
= prfind(pid
)) == NULL
|| p
->p_stat
== SIDL
) {
603 mutex_exit(&pidlock
);
608 mutex_exit(&pidlock
);
612 vt_proc_exists(pid_t pid
)
619 mutex_enter(&pidlock
);
620 if ((p
= prfind(pid
)) == NULL
|| p
->p_stat
== SIDL
) {
621 mutex_exit(&pidlock
);
624 mutex_exit(&pidlock
);
629 #define SIG_VALID(x) (((x) > 0) && ((x) <= MAXSIG) && \
630 ((x) != SIGKILL) && ((x) != SIGSTOP))
633 vt_setmode(vc_state_t
*pvc
, struct vt_mode
*pmode
)
635 if ((pmode
->mode
!= VT_PROCESS
) && (pmode
->mode
!= VT_AUTO
))
638 if (!SIG_VALID(pmode
->relsig
) || !SIG_VALID(pmode
->acqsig
))
641 if (pmode
->mode
== VT_PROCESS
) {
642 pvc
->vc_pid
= curproc
->p_pid
;
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
;
657 vt_reset(vc_state_t
*pvc
)
659 pvc
->vc_switch_mode
= VT_AUTO
;
663 pvc
->vc_switchto
= VT_MINOR_INVALID
;
667 * switch to vt_no from vc_active_console
670 vt_switch(uint_t vt_no
, cred_t
*credp
)
672 vc_state_t
*pvc_active
= vt_minor2vc(vc_active_console
);
673 vc_state_t
*pvc
= vt_minor2vc(vt_no
);
676 ASSERT(pvc_active
&& pvc
);
678 /* sanity test for the target VT and the active VT */
679 if (!((pvc
->vc_flags
& WCS_ISOPEN
) && (pvc
->vc_flags
& WCS_INIT
)))
682 if (!((pvc_active
->vc_flags
& WCS_ISOPEN
) &&
683 (pvc_active
->vc_flags
& WCS_INIT
)))
686 mutex_enter(&vc_lock
);
688 tem_switch(pvc_active
->vc_tem
, pvc
->vc_tem
, credp
);
690 if (!VT_IS_DAEMON(vc_active_console
))
691 vc_last_console
= vc_active_console
;
693 vc_last_console
= vt_arg2minor(vc_target_console
);
695 vc_active_console
= pvc
->vc_minor
;
697 if (pvc
->vc_switch_mode
== VT_PROCESS
) {
698 pvc
->vc_switchto
= pvc
->vc_minor
;
700 /* send it an acquired signal */
701 vt_proc_sendsig(pvc
->vc_pid
, pvc
->vc_acqsig
);
704 vc_waitactive_reply(vc_active_console
, B_FALSE
);
706 mutex_exit(&vc_lock
);
708 if (!VT_IS_DAEMON(vt_no
)) {
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.
716 for (index
= 0; index
< VC_INSTANCES_COUNT
; index
++) {
717 vc_state_t
*tmp_pvc
= vt_minor2vc(index
);
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
);
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
742 * 2 for Virtual Console #2
743 * n for Virtual Console #n
746 vt_arg2minor(uint_t arg
)
758 vt_minor2arg(minor_t minor
)
763 if (VT_IS_DAEMON(minor
)) {
764 /* here it should be the real console */
765 return (vc_target_console
);
772 vt_activate(uint_t vt_no
, cred_t
*credp
)
777 minor
= vt_arg2minor(vt_no
);
778 if (!vt_minor_valid(minor
))
780 if (minor
== vc_active_console
) {
781 if (VT_IS_DAEMON(minor
)) {
783 * vtdaemon is reactivating itself to do locking
784 * on behalf of another console, so record current
785 * target console as the last console.
787 vc_last_console
= vt_arg2minor(vc_target_console
);
794 * In tipline case, the system console is redirected to tipline
795 * and thus is always available.
797 if (minor
== 0 && consconfig_console_is_tipline())
800 if (!VT_IS_INUSE(minor
))
803 pvc
= vt_minor2vc(minor
);
806 if (pvc
->vc_tem
== NULL
)
809 pvc
= vt_minor2vc(vc_active_console
);
812 if (pvc
->vc_switch_mode
!= VT_PROCESS
)
813 return (vt_switch(minor
, credp
));
816 * Validate the process, reset the
817 * vt to auto mode if failed.
819 if (pvc
->vc_pid
== -1 || vt_proc_exists(pvc
->vc_pid
) != 0) {
821 * Xserver has not started up yet,
822 * or it dose not exist.
829 * Send the release signal to the process,
830 * and wait VT_RELDISP ioctl from Xserver
831 * after its leaving VT.
833 vt_proc_sendsig(pvc
->vc_pid
, pvc
->vc_relsig
);
834 pvc
->vc_switchto
= minor
;
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
847 vt_reldisp(vc_state_t
*pvc
, int arg
, cred_t
*credp
)
849 minor_t target_vtno
= pvc
->vc_switchto
;
851 if ((pvc
->vc_switch_mode
!= VT_PROCESS
) ||
852 (pvc
->vc_minor
!= vc_active_console
))
855 if (target_vtno
== VT_MINOR_INVALID
)
858 pvc
->vc_switchto
= VT_MINOR_INVALID
;
860 if (arg
== VT_ACKACQ
)
864 return (0); /* refuse to release */
866 /* Xserver has left VT */
867 return (vt_switch(target_vtno
, credp
));
871 vt_ioctl(queue_t
*q
, mblk_t
*mp
)
873 vc_state_t
*pvc
= (vc_state_t
*)q
->q_ptr
;
875 struct vt_mode vtmode
;
876 struct vt_stat vtinfo
;
877 struct vt_dispinfo vtdisp
;
882 vc_waitactive_msg_t
*wait_msg
;
884 iocp
= (struct iocblk
*)(void *)mp
->b_rptr
;
885 if (consmode
!= CONS_KFB
&& iocp
->ioc_cmd
!= VT_ENABLED
) {
886 vt_iocnak(q
, mp
, EINVAL
);
890 switch (iocp
->ioc_cmd
) {
892 if (!(tmp
= allocb(sizeof (int), BPRI_MED
))) {
896 *(int *)(void *)tmp
->b_rptr
= consmode
;
897 tmp
->b_wptr
+= sizeof (int);
898 vt_copyout(q
, mp
, tmp
, sizeof (int));
902 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
903 if (arg
!= KD_TEXT
&& arg
!= KD_GRAPHICS
) {
907 if (tem_get_fbmode(pvc
->vc_tem
) == arg
)
910 tem_set_fbmode(pvc
->vc_tem
, (uchar_t
)arg
, iocp
->ioc_cr
);
915 if (!(tmp
= allocb(sizeof (int), BPRI_MED
))) {
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));
924 case VT_OPENQRY
: /* return number of first free VT */
925 if (!(tmp
= allocb(sizeof (int), BPRI_MED
))) {
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
))
935 if (!vt_minor_valid(minor
))
937 *(int *)(void *)tmp
->b_rptr
= minor
; /* /dev/vt/minor */
938 tmp
->b_wptr
+= sizeof (int);
939 vt_copyout(q
, mp
, tmp
, sizeof (int));
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
;
948 if (!(tmp
= allocb(sizeof (struct vt_mode
), BPRI_MED
))) {
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
));
958 vt_copyin(q
, mp
, sizeof (struct vt_mode
));
962 /* always enforce sys_devices privilege for setdispinfo */
963 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
966 pvc
->vc_dispnum
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
969 case VT_SETDISPLOGIN
:
970 pvc
->vc_login
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
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
))) {
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
));
987 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
988 error
= vt_reldisp(pvc
, arg
, iocp
->ioc_cr
);
992 /* always enforce sys_devices privilege for config */
993 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
996 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
997 error
= vt_config(arg
);
1001 /* always enforce sys_devices privilege for secure switch */
1002 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
1005 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
1006 error
= vt_activate(arg
, iocp
->ioc_cr
);
1010 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
1011 arg
= vt_arg2minor(arg
);
1012 if (!vt_minor_valid(arg
)) {
1016 if (arg
== vc_active_console
)
1019 wait_msg
= kmem_zalloc(sizeof (vc_waitactive_msg_t
),
1021 if (wait_msg
== NULL
) {
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
);
1035 * Here v_active is the argument for vt_activate,
1038 vtinfo
.v_active
= vt_minor2arg(vc_active_console
);
1039 vtinfo
.v_state
= 3; /* system console and vtdaemon */
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
);
1046 if (VT_IS_INUSE(minor
))
1047 vtinfo
.v_state
|= (1 << pvc
->vc_minor
);
1050 if (!(tmp
= allocb(sizeof (struct vt_stat
), BPRI_MED
))) {
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
));
1060 /* always enforce sys_devices privilege */
1061 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
1064 arg
= *(intptr_t *)(void *)mp
->b_cont
->b_rptr
;
1066 /* vtdaemon is doing authentication for this target console */
1067 vc_target_console
= arg
;
1070 case VT_GETACTIVE
: /* get real active console (minor) */
1071 if (!(tmp
= allocb(sizeof (int), BPRI_MED
))) {
1075 *(int *)(void *)tmp
->b_rptr
= vc_active_console
;
1076 tmp
->b_wptr
+= sizeof (int);
1077 vt_copyout(q
, mp
, tmp
, sizeof (int));
1080 case VT_GET_CONSUSER
:
1081 if (!(tmp
= allocb(sizeof (int), BPRI_MED
))) {
1086 if (vc_cons_user
== VT_MINOR_INVALID
) {
1088 * Return -1 if console user link points to
1091 *(int *)(void *)tmp
->b_rptr
= -1;
1093 *(int *)(void *)tmp
->b_rptr
= vc_cons_user
;
1096 tmp
->b_wptr
+= sizeof (int);
1097 vt_copyout(q
, mp
, tmp
, sizeof (int));
1100 case VT_RESET_CONSUSER
:
1101 /* always enforce sys_devices privilege */
1102 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
1105 /* Ensure it comes from /dev/console */
1106 if (pvc
->vc_minor
!= 0) {
1111 mutex_enter(&vc_lock
);
1112 vc_cons_user
= VT_MINOR_INVALID
;
1113 mutex_exit(&vc_lock
);
1116 case VT_SET_CONSUSER
:
1117 /* always enforce sys_devices privilege */
1118 if ((error
= secpolicy_console(iocp
->ioc_cr
)) != 0)
1121 mutex_enter(&vc_lock
);
1122 vc_cons_user
= pvc
->vc_minor
;
1123 mutex_exit(&vc_lock
);
1132 vt_iocnak(q
, mp
, error
);
1138 vt_miocdata(queue_t
*qp
, mblk_t
*mp
)
1140 vc_state_t
*pvc
= (vc_state_t
*)qp
->q_ptr
;
1141 struct copyresp
*copyresp
;
1142 struct vt_mode
*pmode
;
1145 copyresp
= (struct copyresp
*)(void *)mp
->b_rptr
;
1146 if (copyresp
->cp_rval
) {
1147 vt_iocnak(qp
, mp
, EAGAIN
);
1151 switch (copyresp
->cp_cmd
) {
1153 pmode
= (struct vt_mode
*)(void *)mp
->b_cont
->b_rptr
;
1154 error
= vt_setmode(pvc
, pmode
);
1160 case VT_GETDISPINFO
:
1172 vt_iocnak(qp
, mp
, error
);
1178 vt_iocack(queue_t
*qp
, mblk_t
*mp
)
1180 struct iocblk
*iocbp
= (struct iocblk
*)(void *)mp
->b_rptr
;
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
);
1195 vt_iocnak(queue_t
*qp
, mblk_t
*mp
, int error
)
1197 struct iocblk
*iocp
= (struct iocblk
*)(void *)mp
->b_rptr
;
1199 mp
->b_datap
->db_type
= M_IOCNAK
;
1201 iocp
->ioc_count
= 0;
1202 iocp
->ioc_error
= error
;
1203 if (mp
->b_cont
!= NULL
) {
1204 freemsg(mp
->b_cont
);
1211 vt_copyin(queue_t
*qp
, mblk_t
*mp
, uint_t size
)
1213 struct copyreq
*cqp
;
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
;
1219 cqp
->cq_private
= NULL
;
1220 mp
->b_wptr
= mp
->b_rptr
+ sizeof (struct copyreq
);
1221 mp
->b_datap
->db_type
= M_COPYIN
;
1223 freemsg(mp
->b_cont
);
1229 vt_copyout(queue_t
*qp
, mblk_t
*mp
, mblk_t
*tmp
, uint_t size
)
1231 struct copyreq
*cqp
;
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
);
1237 cqp
->cq_private
= NULL
;
1238 mp
->b_wptr
= mp
->b_rptr
+ sizeof (struct copyreq
);
1239 mp
->b_datap
->db_type
= M_COPYOUT
;
1241 freemsg(mp
->b_cont
);
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
1254 vt_minor2vc(minor_t minor
)
1259 if (minor
!= VT_ACTIVE
) {
1260 target
.vc_minor
= minor
;
1261 return (avl_find(&vc_avl_root
, &target
, &where
));
1264 if (vc_active_console
== VT_MINOR_INVALID
)
1265 target
.vc_minor
= 0;
1267 target
.vc_minor
= vc_active_console
;
1269 return (avl_find(&vc_avl_root
, &target
, &where
));
1273 vt_state_init(vc_state_t
*vcptr
, minor_t minor
)
1275 mutex_init(&vcptr
->vc_state_lock
, NULL
, MUTEX_DRIVER
, NULL
);
1277 mutex_enter(&vcptr
->vc_state_lock
);
1278 vcptr
->vc_flags
= 0;
1279 mutex_exit(&vcptr
->vc_state_lock
);
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
;
1296 vt_resize(uint_t count
)
1300 ASSERT(MUTEX_HELD(&vc_lock
));
1302 vc_num
= VC_INSTANCES_COUNT
;
1304 if (count
== vc_num
)
1307 if (count
> vc_num
) {
1308 for (i
= vc_num
; i
< count
; i
++) {
1309 vc_state_t
*vcptr
= kmem_zalloc(sizeof (vc_state_t
),
1311 vt_state_init(vcptr
, i
);
1312 avl_add(&vc_avl_root
, vcptr
);
1317 for (i
= vc_num
; i
> count
; i
--) {
1319 vc_state_t target
, *found
;
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
));
1330 vc_avl_compare(const void *first
, const void *second
)
1332 const vc_state_t
*vcptr1
= first
;
1333 const vc_state_t
*vcptr2
= second
;
1335 if (vcptr1
->vc_minor
< vcptr2
->vc_minor
)
1338 if (vcptr1
->vc_minor
== vcptr2
->vc_minor
)
1345 * Only called from wc init().
1351 ASSERT(NO_COMPETING_THREADS
);
1354 avl_create(&vc_avl_root
, vc_avl_compare
, sizeof (vc_state_t
),
1355 offsetof(vc_state_t
, vc_avl_node
));
1357 list_create(&vc_waitactive_list
, sizeof (vc_waitactive_msg_t
),
1358 offsetof(vc_waitactive_msg_t
, wa_list_node
));
1360 mutex_init(&vc_lock
, NULL
, MUTEX_DRIVER
, NULL
);
1361 mutex_init(&vt_pending_vtno_lock
, NULL
, MUTEX_DRIVER
, NULL
);