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