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