diff options
Diffstat (limited to 'usr/src')
76 files changed, 7227 insertions, 1660 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 018eec7115..0b0b657e11 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -281,6 +281,7 @@ COMMON_SUBDIRS = \ cmd/utmpd \ cmd/valtools \ cmd/vna \ + cmd/vt \ cmd/wall \ cmd/wbem \ cmd/wc \ diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index 31245d9ef6..15c4693142 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -71,6 +71,7 @@ ROOT.SYS= \ /dev/pts \ /dev/sad \ /dev/swap \ + /dev/vt \ /dev/zcons \ /devices \ /devices/pseudo \ diff --git a/usr/src/cmd/Adm/ttysrch b/usr/src/cmd/Adm/ttysrch index ef3bd2e4ed..b4e2195503 100644 --- a/usr/src/cmd/Adm/ttysrch +++ b/usr/src/cmd/Adm/ttysrch @@ -1,13 +1,12 @@ # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -22,8 +21,6 @@ # # CDDL HEADER END # -#ident "%Z%%M% %I% %E% SMI" -# # This file is used by ttyname(3C) to minimize search time # during attempts to determine the name of a terminal device. # @@ -56,5 +53,6 @@ # in the /dev/slan directory. # /dev/pts +/dev/vt /dev/term /dev/zcons diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index c9b15faf8d..dbabc8ac14 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -417,6 +417,7 @@ COMMON_SUBDIRS= \ volcheck \ volrmmount \ vscan \ + vt \ w \ wall \ wbem \ diff --git a/usr/src/cmd/login/login.c b/usr/src/cmd/login/login.c index 16a9a633e0..98e42468e2 100644 --- a/usr/src/cmd/login/login.c +++ b/usr/src/cmd/login/login.c @@ -18,6 +18,7 @@ * * CDDL HEADER END */ + /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -1177,22 +1178,42 @@ logins_disabled(char *user_name) return (FALSE); } +#define DEFAULT_CONSOLE "/dev/console" + /* * check_for_console - Checks if we're getting a root login on the - * console, or a login from the global zone. Exits if not. + * console, or a login from the global zone. Exits if not. * + * If CONSOLE is set to /dev/console in /etc/default/login, then root logins + * on /dev/vt/# are permitted as well. /dev/vt/# does not exist in non-global + * zones, but checking them does no harm. */ static void check_for_console(void) { - if (pwd != NULL && pwd->pw_uid == 0 && zflag == B_FALSE) { - if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) { - (void) printf("Not on system console\n"); + const char *consoles[] = { "/dev/console", "/dev/vt/", NULL }; + int i; + + if (pwd == NULL || pwd->pw_uid != 0 || zflag != B_FALSE || + Console == NULL) + return; - audit_error = ADT_FAIL_VALUE_CONSOLE; - login_exit(10); + if (strcmp(Console, DEFAULT_CONSOLE) == 0) { + for (i = 0; consoles[i] != NULL; i ++) { + if (strncmp(ttyn, consoles[i], + strlen(consoles[i])) == 0) + return; } + } else { + if (strcmp(ttyn, Console) == 0) + return; } + + (void) printf("Not on system console\n"); + + audit_error = ADT_FAIL_VALUE_CONSOLE; + login_exit(10); + } /* @@ -2017,10 +2038,10 @@ update_utmpx_entry(int sublogin) char *user; static char *errmsg = "No utmpx entry. " "You must exec \"login\" from the lowest level \"shell\"."; - int tmplen; + int tmplen; struct utmpx *u = (struct utmpx *)0; struct utmpx utmpx; - char *ttyntail; + char *ttyntail; /* * If we're not a sublogin then diff --git a/usr/src/cmd/login/login.dfl b/usr/src/cmd/login/login.dfl index 03cd4d14f2..4b11830230 100644 --- a/usr/src/cmd/login/login.dfl +++ b/usr/src/cmd/login/login.dfl @@ -1,14 +1,12 @@ -#ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -34,6 +32,8 @@ #ULIMIT=0 # If CONSOLE is set, root can only login on that device. +# If the specified device is /dev/console, then root can also log into +# any of the currently enabled /dev/vt/# virtual terminal devices. # Comment this line out to allow remote login by root. # CONSOLE=/dev/console diff --git a/usr/src/cmd/login/logindevperm.sh b/usr/src/cmd/login/logindevperm.sh index c6e0a09933..7c5025212b 100644 --- a/usr/src/cmd/login/logindevperm.sh +++ b/usr/src/cmd/login/logindevperm.sh @@ -20,10 +20,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # # This is the script that generates the logindevperm file. It is # architecture-aware, and dumps different stuff for x86 and sparc. @@ -35,11 +34,9 @@ cat <<EOM # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%logindevperm %I% %E% SMI" -# # /etc/logindevperm - login-based device permissions # # If the user is logging in on a device specified in the "console" field @@ -47,6 +44,11 @@ cat <<EOM # "devices" field will be set to that of the user. Similarly, the mode # will be set to the mode specified in the "mode" field. # +# If the "console" is "/dev/vt/active" which is a symlink to the current +# active virtual console (/dev/console, or /dev/vt/#), then the first +# user to log into any virtual console will get ownership of all the +# devices until they log out. +# # "devices" is a colon-separated list of device names. A device name # ending in "/*", such as "/dev/fbs/*", specifies all entries (except "." # and "..") in a directory. A '#' begins a comment and may appear @@ -58,17 +60,17 @@ cat <<EOM # # console mode devices # -/dev/console 0600 /dev/mouse:/dev/kbd -/dev/console 0600 /dev/sound/* # audio devices -/dev/console 0600 /dev/fbs/* # frame buffers -/dev/console 0600 /dev/dri/* # dri devices -/dev/console 0400 /dev/removable-media/dsk/* # removable media -/dev/console 0400 /dev/removable-media/rdsk/* # removable media -/dev/console 0400 /dev/hotpluggable/dsk/* # hotpluggable storage -/dev/console 0400 /dev/hotpluggable/rdsk/* # hotpluggable storage -/dev/console 0600 /dev/video[0-9]+ # video devices -/dev/console 0600 /dev/usb/hid[0-9]+ # hid devices should have the same permission with conskbd and consms -/dev/console 0600 /dev/usb/[0-9a-f]+[.][0-9a-f]+/[0-9]+/* driver=scsa2usb,usb_mid,usbprn,ugen #libusb/ugen devices +/dev/vt/active 0600 /dev/mouse:/dev/kbd +/dev/vt/active 0600 /dev/sound/* # audio devices +/dev/vt/active 0600 /dev/fbs/* # frame buffers +/dev/vt/active 0600 /dev/dri/* # dri devices +/dev/vt/active 0400 /dev/removable-media/dsk/* # removable media +/dev/vt/active 0400 /dev/removable-media/rdsk/* # removable media +/dev/vt/active 0400 /dev/hotpluggable/dsk/* # hotpluggable storage +/dev/vt/active 0400 /dev/hotpluggable/rdsk/* # hotpluggable storage +/dev/vt/active 0600 /dev/video[0-9]+ # video devices +/dev/vt/active 0600 /dev/usb/hid[0-9]+ # hid devices should have the same permission with conskbd and consms +/dev/vt/active 0600 /dev/usb/[0-9a-f]+[.][0-9a-f]+/[0-9]+/* driver=scsa2usb,usb_mid,usbprn,ugen #libusb/ugen devices EOM case "$MACH" in diff --git a/usr/src/cmd/netadm/iu.ap.sh b/usr/src/cmd/netadm/iu.ap.sh index abf930cade..c16b5a0b69 100644 --- a/usr/src/cmd/netadm/iu.ap.sh +++ b/usr/src/cmd/netadm/iu.ap.sh @@ -20,22 +20,20 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved -#ident "%Z%%M% %I% %E% SMI" - case "$MACH" in "i386" ) echo "# /dev/console and /dev/contty autopush setup # # major minor lastminor modules - wc 0 0 ldterm ttcompat + wc -1 0 ldterm ttcompat asy -1 0 ldterm ttcompat xencons 0 0 ldterm ttcompat ptsl 0 47 ldterm ttcompat @@ -52,7 +50,7 @@ case "$MACH" in # # major minor lastminor modules - wc 0 0 ldterm ttcompat + wc -1 0 ldterm ttcompat qcn 0 255 ldterm ttcompat sgcn 0 0 ldterm ttcompat zs 0 63 ldterm ttcompat diff --git a/usr/src/cmd/sulogin/sulogin.c b/usr/src/cmd/sulogin/sulogin.c index a5bf3c4b99..101f6da055 100644 --- a/usr/src/cmd/sulogin/sulogin.c +++ b/usr/src/cmd/sulogin/sulogin.c @@ -74,6 +74,7 @@ #include <auth_list.h> #include <nss_dbdefs.h> #include <user_attr.h> +#include <sys/vt.h> /* * Intervals to sleep after failed login @@ -458,6 +459,16 @@ main_loop(char *devname, boolean_t cttyflag) } if ((fd = open(devname, O_RDWR)) < 0) exit(EXIT_FAILURE); + + /* + * In system maintenance mode, all virtual console instances + * of the svc:/system/console-login service are not available + * any more, and only the system console is available. So here + * we always switch to the system console in case at the moment + * the active console isn't it. + */ + (void) ioctl(fd, VT_ACTIVATE, 1); + if (fd != 0) (void) dup2(fd, STDIN_FILENO); if (fd != 1) diff --git a/usr/src/cmd/svc/milestone/Makefile b/usr/src/cmd/svc/milestone/Makefile index b084a04e44..fdcff57933 100644 --- a/usr/src/cmd/svc/milestone/Makefile +++ b/usr/src/cmd/svc/milestone/Makefile @@ -19,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" include ../../Makefile.cmd @@ -70,7 +69,8 @@ SYSTEMSVCS= \ console-login.xml \ identity.xml \ manifest-import.xml \ - rmtmpfiles.xml + rmtmpfiles.xml \ + vtdaemon.xml SYSTEMMANIFESTS = $(SYSTEMSVCS:%=$(ROOTSVCSYSTEM)/%) @@ -107,7 +107,8 @@ SVCMETHOD=\ net-physical \ net-routing-setup \ net-svc \ - rmtmpfiles + rmtmpfiles \ + vtdaemon $(ROOTSVCMETHOD) := FILEMODE = 0555 diff --git a/usr/src/cmd/svc/milestone/console-login b/usr/src/cmd/svc/milestone/console-login index 6e4a733d87..c7003b103b 100644 --- a/usr/src/cmd/svc/milestone/console-login +++ b/usr/src/cmd/svc/milestone/console-login @@ -21,10 +21,11 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +# This method script manages all vt logins including system +# console login. # # For modifying parameters passed to ttymon, do not edit # this script. Instead use svccfg(1m) to modify the SMF @@ -35,6 +36,15 @@ # svc:/system/console-login> setprop ttymon/terminal_type = "xterm" # svc:/system/console-login> exit +. /lib/svc/share/smf_include.sh + +if [ "$1" != "default" ]; then + if smf_dont_configure_vt; then + /usr/sbin/svcadm disable $SMF_FMRI + exit $SMF_EXIT_OK + fi +fi + getproparg() { val=`svcprop -p $2 $SMF_FMRI` [ -n "$val" ] && [ "$val" != "\"\"" ] && echo $1 $val @@ -44,7 +54,13 @@ args="-g" val=`svcprop -p ttymon/device $SMF_FMRI` # if this isn't set, recover a little -[ -z "$val" ] && val=/dev/console +[ -z "$val" ] && val="/dev/console" + +if [ "$val" = "/dev/vt/1" ]; then + echo "ERROR: ttymon/device cannot be configured to /dev/vt/1." + exit $SMF_EXIT_ERR_CONFIG +fi + args="$args -d $val" args="$args `getproparg -l ttymon/label`" diff --git a/usr/src/cmd/svc/milestone/make-console-login-xml b/usr/src/cmd/svc/milestone/make-console-login-xml index 07f238ba4d..75948cde6f 100644 --- a/usr/src/cmd/svc/milestone/make-console-login-xml +++ b/usr/src/cmd/svc/milestone/make-console-login-xml @@ -21,10 +21,9 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" cat >console-login.xml <<EOF <?xml version="1.0"?> @@ -32,8 +31,6 @@ cat >console-login.xml <<EOF Copyright 2006 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. - ident "%Z%%M% %I% %E% SMI" - NOTE: This service manifest is not editable; its contents will be overwritten by package or patch operations, including operating system upgrade. Make customizations in a different @@ -49,10 +46,6 @@ cat >console-login.xml <<EOF type='service' version='1'> - <create_default_instance enabled='true' /> - - <single_instance/> - <dependency name='fs' grouping='require_all' @@ -93,14 +86,23 @@ cat >console-login.xml <<EOF <exec_method type='method' name='start' - exec='/lib/svc/method/console-login' - timeout_seconds='0' /> + exec='/lib/svc/method/console-login %i' + timeout_seconds='0'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> <exec_method type='method' name='stop' exec=':kill -9' - timeout_seconds='3' /> + timeout_seconds='3'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + <property_group name='startd' type='framework'> <propval name='duration' type='astring' value='child' /> @@ -109,8 +111,20 @@ cat >console-login.xml <<EOF <propval name='utmpx_prefix' type='astring' value='co' /> </property_group> - <!-- these are passed to ttymon in the method script --> + <property_group name='general' type='framework'> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.vt' /> + <propval name='value_authorization' type='astring' + value='solaris.smf.manage.vt' /> + </property_group> + + <!-- these are passed to ttymon in the method script. + note that value_authorization is not passed to ttymon + and it's for smf_security(5). + --> <property_group name='ttymon' type='application'> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.vt' /> <propval name='device' type='astring' value='/dev/console' /> <propval name='label' type='astring' value='console' /> <propval name='timeout' type='count' value='0' /> @@ -123,6 +137,64 @@ cat >console-login.xml <<EOF value='' /> </property_group> + +<instance name='default' enabled='true'> +</instance> + +EOF + +# Note that this script file is normally parsed during build by sh(1). +# When the parser encounters an EOF token (like the one above), it +# will fork off and pipe all the text after the EOF above to the shell +# for execution. +# +# one system console (/dev/console) plus five virtual consoles +# (/dev/vt/#, # is from 2 to 6). + +for num in 2 3 4 5 6; do + cat >>console-login.xml <<EOF + +<instance name='vt$num' enabled='false'> + + <dependency + name='system-console' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/console-login:default' /> + </dependency> + + <dependency + name='vtdaemon' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/vtdaemon:default' /> + </dependency> + + <!-- these are passed to ttymon in the method script --> + <property_group name='ttymon' type='application'> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.vt' /> + <propval name='device' type='astring' value='/dev/vt/$num' /> + <propval name='label' type='astring' value='console' /> + <propval name='timeout' type='count' value='0' /> + <propval name='nohangup' type='boolean' value='true' /> + <propval name='modules' type='astring' + value='ldterm,ttcompat' /> + <propval name='prompt' type='astring' + value='\`uname -n\` vt$num login:' /> + <propval name='terminal_type' type='astring' + value='' /> + </property_group> + +</instance> + +EOF +done + +cat >>console-login.xml <<EOF + <stability value='Evolving' /> <template> diff --git a/usr/src/cmd/svc/milestone/vtdaemon b/usr/src/cmd/svc/milestone/vtdaemon new file mode 100644 index 0000000000..3d039f8dbb --- /dev/null +++ b/usr/src/cmd/svc/milestone/vtdaemon @@ -0,0 +1,45 @@ +#!/sbin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +. /lib/svc/share/smf_include.sh + +if smf_dont_configure_vt; then + /usr/sbin/svcadm disable $SMF_FMRI + exit $SMF_EXIT_OK +fi + +args="" + +val=`svcprop -p options/hotkeys $SMF_FMRI` +[ "$val" = "false" ] && args="$args -k" + +val=`svcprop -p options/secure $SMF_FMRI` +[ "$val" = "false" ] && args="$args -s" + +val=`svcprop -p options/nodecount $SMF_FMRI` +[ -n "$val" -a "$val" != "0" ] && args="$args -c $val" + +exec /usr/lib/vtdaemon $args diff --git a/usr/src/cmd/svc/milestone/vtdaemon.xml b/usr/src/cmd/svc/milestone/vtdaemon.xml new file mode 100644 index 0000000000..920792bc5b --- /dev/null +++ b/usr/src/cmd/svc/milestone/vtdaemon.xml @@ -0,0 +1,110 @@ +<?xml version="1.0"?> +<!-- + Copyright 2008 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> + +<service_bundle type='manifest' name='SUNWcsr:vtdaemon'> + +<service + name='system/vtdaemon' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <single_instance /> + + <dependency + name='console-login' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/console-login:default' /> + </dependency> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/vtdaemon' + timeout_seconds='0'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <exec_method + type='method' + name='stop' + exec=':kill -9' + timeout_seconds='3'> + <method_context> + <method_credential user='root' group='root' /> + </method_context> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='child' /> + <propval name='ignore_error' type='astring' + value='core,signal' /> + <propval name='utmpx_prefix' type='astring' value='co' /> + </property_group> + + <property_group name='general' type='framework'> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.vt' /> + <propval name='value_authorization' type='astring' + value='solaris.smf.manage.vt' /> + </property_group> + + <property_group name='options' type='application'> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.vt' /> + <propval name='hotkeys' type='boolean' value='false' /> + <propval name='secure' type='boolean' value='true' /> + <propval name='nodecount' type='count' value='16' /> + </property_group> + + + <stability value='Evolving' /> + + <template> + <common_name> + <loctext xml:lang='C'> +vtdaemon for virtual console secure switch + </loctext> + </common_name> + <documentation> + <manpage title='vtdaemon' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/profile/generic_limited_net.xml b/usr/src/cmd/svc/profile/generic_limited_net.xml index acb430bf66..6fbd62f051 100644 --- a/usr/src/cmd/svc/profile/generic_limited_net.xml +++ b/usr/src/cmd/svc/profile/generic_limited_net.xml @@ -23,8 +23,6 @@ Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. - ident "%Z%%M% %I% %E% SMI" - The purpose of the limited_net profile is to provide a set of active services that allow one to connect to the machine via ssh (requires sshd). The services which are deactivated here are those @@ -48,6 +46,13 @@ <!-- svc.startd(1M) services --> + <service name='system/console-login' version='1' type='service'> + <instance name='vt2' enabled='true'/> + <instance name='vt3' enabled='true'/> + <instance name='vt4' enabled='true'/> + <instance name='vt5' enabled='true'/> + <instance name='vt6' enabled='true'/> + </service> <service name='system/coreadm' version='1' type='service'> <instance name='default' enabled='true'/> </service> @@ -92,6 +97,9 @@ <service name='system/utmp' version='1' type='service'> <instance name='default' enabled='true'/> </service> + <service name='system/vtdaemon' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> <service name='system/zones' version='1' type='service'> <instance name='default' enabled='true'/> </service> diff --git a/usr/src/cmd/svc/profile/generic_open.xml b/usr/src/cmd/svc/profile/generic_open.xml index cad0b821cc..e2aa1b6b53 100644 --- a/usr/src/cmd/svc/profile/generic_open.xml +++ b/usr/src/cmd/svc/profile/generic_open.xml @@ -23,8 +23,6 @@ Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. - ident "%Z%%M% %I% %E% SMI" - Default service profile, containing a typical set of active service instances. @@ -45,6 +43,13 @@ <!-- svc.startd(1M) services --> + <service name='system/console-login' version='1' type='service'> + <instance name='vt2' enabled='true'/> + <instance name='vt3' enabled='true'/> + <instance name='vt4' enabled='true'/> + <instance name='vt5' enabled='true'/> + <instance name='vt6' enabled='true'/> + </service> <service name='system/coreadm' version='1' type='service'> <instance name='default' enabled='true'/> </service> @@ -89,6 +94,9 @@ <service name='system/utmp' version='1' type='service'> <instance name='default' enabled='true'/> </service> + <service name='system/vtdaemon' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> <service name='system/zones' version='1' type='service'> <instance name='default' enabled='true'/> </service> diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh index 9357c1bfbc..28aa22d701 100644 --- a/usr/src/cmd/svc/shell/smf_include.sh +++ b/usr/src/cmd/svc/shell/smf_include.sh @@ -20,10 +20,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" smf_present () { [ -r /etc/svc/volatile/repository_door ] && \ @@ -95,6 +94,17 @@ smf_dont_configure_ip() { return 1 } +# smf_dont_configure_vt +# +# Returns zero (success) if vt functionality is not to be configured, +# 1 otherwise. +# +smf_dont_configure_vt() { + [ "${SMF_ZONENAME:=`/sbin/zonename`}" != "global" ] && return 0 + /usr/lib/vtinfo > /dev/null 2>&1 + return $? +} + # smf_is_system_labeled # # Returns zero (success) if system is labeled (aka Trusted Extensions). diff --git a/usr/src/cmd/ttymon/tmexpress.c b/usr/src/cmd/ttymon/tmexpress.c index 79c140c2cb..c01a67142e 100644 --- a/usr/src/cmd/ttymon/tmexpress.c +++ b/usr/src/cmd/ttymon/tmexpress.c @@ -20,15 +20,12 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ - -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -109,11 +106,9 @@ ttymon_express(int argc, char **argv) read_ttydefs(NULL, FALSE); - if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0') && - strcmp(pmtab->p_device, "/dev/console") == 0) { + if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) while (checkut_line(pmtab->p_device)) sleep(15); - } if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) { devname = find_ttyname(0); diff --git a/usr/src/cmd/vt/Makefile b/usr/src/cmd/vt/Makefile new file mode 100644 index 0000000000..432e832888 --- /dev/null +++ b/usr/src/cmd/vt/Makefile @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG= vtdaemon vtinfo vtxlock + +VTDAEMON_SRC= vtdaemon.c +VTINFO_SRC= vtinfo.c + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) + +LDLIBS += -ldoor -lpam -lbsm + +.KEEP_STATE: + +all: $(PROG) + +vtdaemon: $(VTDAEMON_SRC) + $(LINK.c) -o $@ $(VTDAEMON_SRC) $(LDLIBS) + $(POST_PROCESS) + +vtinfo: $(VTINFO_SRC) + $(LINK.c) -o $@ $(VTINFO_SRC) + $(POST_PROCESS) + +install: all $(ROOTLIBPROG) + +clean: + +lint: + $(LINT.c) $(VTDAEMON_SRC) $(LDLIBS) + $(LINT.c) $(VTINFO_SRC) + +include ../Makefile.targ diff --git a/usr/src/cmd/vt/vtdaemon.c b/usr/src/cmd/vt/vtdaemon.c new file mode 100644 index 0000000000..08d100594f --- /dev/null +++ b/usr/src/cmd/vt/vtdaemon.c @@ -0,0 +1,1400 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * vtdaemon is responsible for the session secure switch via hotkeys. + * + * vtdaemon itself, like ttymon(1M), is also running on a virtual + * console device (/dev/vt/1), and provides a text console session + * for password input and authentication. The /dev/vt/1 special text + * console is reserved and end users cannot switch to it via hotkeys. + * + * + * The hotkey event request can come from either kernel or Xserver, + * and a door server is setup to handle the request: + * + * 1) All text console hotkeys (e.g. "Alt + F#") are intercepted by + * the kernel console driver which sends a door upcall to the + * vtdaemon via door_upcall (target_vt). + * + * 2) All Xserver hotkeys ("Alt + Ctrl + F#") are intercepted by + * Xserver which sends a door call to the vtdaemon via + * door_call (target_vt). + * + * + * server_for_door receives and handles any door server requests: + * + * Firstly, check source session: + * + * . If it's from kernel for a text console source session, + * then directly go to check the target session. + * + * . If it's from Xserver for a graphical source session and the vt + * associated with the Xserver is currently active: + * check if a user has logged in, if true, issue an internal + * VT_EV_LOCK event to the main thread to request lock for + * the graphical source session; else, directly go to check + * the target session. + * + * . otherwise, discard this request. + * + * + * Secondly, check the target session + * + * . if the target session is a text one that no one has logged in + * or a graphical one, issue an internal VT_EV_ACTIVATE event to + * the main thread to request the actual VT switch. + * + * . otherwise, the target session is a text one that someone has + * logged in, issue an internal VT_EV_AUTH event to the main + * thread to request authentication for the target session. + * + * + * The main thread of vtdaemon is a loop waiting for internal events + * which come from door call threads: + * + * 1) VT_EV_AUTH to authenticate for target session: + * + * firstly switch to the vtdaemon special text console; + * then prompt for password (target_owner on target_vt), + * e.g. "User Bob's password on vt/#: ". + * + * if the password is correct (authentication succeeds), + * then actually issue the VT switch; otherwise, ignore + * the request. + * + * 2) VT_EV_LOCK to lock the graphical source session: + * + * activate screenlock for this graphical session. + * vtdaemon just invokes existing front-end command line + * tools (e.g. xscreensaver-command -lock for JDS) to + * lock the display. + * + * 3) VT_EV_ACTIVATE to directly switch to the target session + * + * + * There is a system/vtdaemon:default SMF service for vtdaemon. + * + * There's a "hotkeys" property (BOOLEAN) in the + * system/vtdaemon:default SMF service, which allows authorized + * users to dynamically enable or disable VT switch via hotkeys. + * Its default value is TRUE (enabled). + * + * There's a "secure" property (BOOLEAN) in the + * system/vtdaemon:default SMF service, which allows authorized + * users to dynamically enable or disable hotkeys are secure. + * If disabled, the user can freely switch to any session without + * authentication. Its default value is TRUE (enabled). + * + * + * By default, there's only 16 virtual console device nodes (from + * /dev/vt/0 to /dev/vt/15). There's a property "nodecount" + * (default value is 16) in the system/vtdaemon:default SMF + * service, so authorized users can configure it to have more + * or less virtual console device nodes. + * + * Xserver needs to switch back to previous active vt via VT_EV_X_EXIT + * door event request when it's exiting, so vtdaemon always needs to + * be there even if the hotkeys switch is disabled, otherwise the screen + * will be just blank when Xserver exits. + */ + +#include <sys/param.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <syslog.h> +#include <deflt.h> + +#include <bsm/adt.h> +#include <bsm/adt_event.h> + +#include <alloca.h> +#include <assert.h> +#include <errno.h> +#include <door.h> +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <synch.h> +#include <thread.h> +#include <unistd.h> +#include <wait.h> +#include <limits.h> +#include <zone.h> +#include <priv.h> +#include <pwd.h> +#include <utmpx.h> +#include <procfs.h> +#include <poll.h> +#include <termio.h> +#include <security/pam_appl.h> +#include <time.h> +#include <sys/console.h> +#include <assert.h> +#include <syslog.h> + +#include <sys/vt.h> +#include <sys/vtdaemon.h> + +/* + * The door file /var/run/vt/vtdaemon_door + */ +#define VT_TMPDIR "/var/run/vt" + +#define VT_DAEMON_ARG 0 +#define VT_DAEMON_CONSOLE_FILE "/dev/vt/1" + +#define VT_IS_SYSTEM_CONSOLE(vtno) ((vtno) == 1) + +/* Defaults for updating expired passwords */ +#define DEF_ATTEMPTS 3 + +int daemonfd; + +static boolean_t vt_hotkeys = B_TRUE; /* '-k' option to disable */ +static boolean_t vt_secure = B_TRUE; /* '-s' option to disable */ + +static char vt_door_path[MAXPATHLEN]; +static int vt_door = -1; + +/* protecting vt_hotkeys_pending and vt_auth_doing */ +static mutex_t vt_mutex = DEFAULTMUTEX; + +static boolean_t vt_hotkeys_pending = B_FALSE; +static boolean_t vt_auth_doing = B_FALSE; + +static adt_session_data_t **vt_ah_array = NULL; +static int vtnodecount = 0; + +static int vt_audit_start(adt_session_data_t **, pid_t); +static void vt_audit_event(adt_session_data_t *, au_event_t, int); +static void vt_check_source_audit(void); + +static int +vt_setup_signal(int signo, int mask) +{ + sigset_t set; + + (void) sigemptyset(&set); + (void) sigaddset(&set, signo); + + if (mask) + return (sigprocmask(SIG_BLOCK, &set, NULL)); + else + return (sigprocmask(SIG_UNBLOCK, &set, NULL)); +} + +static void +do_activate_screenlock(int display_num) +{ + char dpy[16]; + + (void) snprintf(dpy, sizeof (dpy), "%d", display_num); + (void) execl("/usr/lib/vtxlock", "vtxlock", dpy, NULL); +} + +static void +vt_activate_screenlock(int display) +{ + pid_t pid; + + if ((pid = fork()) == -1) + return; + + if (pid == 0) { /* child */ + do_activate_screenlock(display); + exit(0); + } + + /* parent */ + while (waitpid(pid, (int *)0, 0) != pid) + continue; +} + +/* + * Find the login process and user logged in on the target vt. + */ +static void +vt_read_utx(int target_vt, pid_t *pid, char name[]) +{ + struct utmpx *u; + char ttyntail[sizeof (u->ut_line)]; + + *pid = (pid_t)-1; + + if (VT_IS_SYSTEM_CONSOLE(target_vt)) /* system console */ + (void) snprintf(ttyntail, sizeof (ttyntail), + "%s", "console"); + else + (void) snprintf(ttyntail, sizeof (ttyntail), + "%s%d", "vt/", target_vt); + + setutxent(); + while ((u = getutxent()) != NULL) + /* see if this is the entry we want */ + if ((u->ut_type == USER_PROCESS) && + (!nonuserx(*u)) && + (u->ut_host[0] == '\0') && + (strncmp(u->ut_line, ttyntail, sizeof (u->ut_line)) == 0)) { + + *pid = u->ut_pid; + if (name != NULL) { + (void) strncpy(name, u->ut_user, + sizeof (u->ut_user)); + name[sizeof (u->ut_user)] = '\0'; + } + break; + } + + endutxent(); +} + +static boolean_t +vt_is_tipline(void) +{ + static int is_tipline = 0; + int fd; + static char termbuf[MAX_TERM_TYPE_LEN]; + static struct cons_getterm cons_term = { sizeof (termbuf), termbuf}; + + if (is_tipline != 0) + return (is_tipline == 1); + + if ((fd = open("/dev/console", O_RDONLY)) < 0) + return (B_FALSE); + + if (ioctl(fd, CONS_GETTERM, &cons_term) != 0 && + errno == ENODEV) { + is_tipline = 1; + } else { + is_tipline = -1; + } + + (void) close(fd); + return (is_tipline == 1); +} + +static int +validate_target_vt(int target_vt) +{ + int fd; + struct vt_stat state; + + if (target_vt < 1) + return (-1); + + if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_WRONLY)) < 0) + return (-1); + + if (ioctl(fd, VT_GETSTATE, &state) != 0) { + (void) close(fd); + return (-1); + } + + (void) close(fd); + + if (state.v_active == target_vt) { + return (1); /* it's current active vt */ + } + + if (target_vt == 1) { + /* + * In tipline case, the system console is always + * available, so ignore this request. + */ + if (vt_is_tipline()) + return (-1); + + target_vt = 0; + } + + /* + * The hotkey request and corresponding target_vt number can come + * from either kernel or Xserver (or other user applications). + * In kernel we've validated the hotkey request, but Xserver (or + * other user applications) cannot do it, so here we still try + * to validate it. + * + * VT_GETSTATE is only valid for first 16 VTs for historical reasons. + * Fortunately, in practice, Xserver can only send the hotkey + * request of target_vt number from 1 to 12 (Ctrl + Alt + F1 to F2). + */ + if (target_vt < 8 * sizeof (state.v_state)) { + if ((state.v_state & (1 << target_vt)) != 0) { + return (0); + } else { + return (-1); + } + } + + return (0); +} + +static void +vt_do_activate(int target_vt) +{ + (void) ioctl(daemonfd, VT_ACTIVATE, target_vt); + (void) mutex_lock(&vt_mutex); + vt_hotkeys_pending = B_FALSE; + (void) mutex_unlock(&vt_mutex); +} + +/* events written to fd 0 and read from fd 1 */ +#define VT_EV_AUTH 1 +#define VT_EV_LOCK 2 +#define VT_EV_ACTIVATE 3 + +/* events written to fd 1 and read from fd 0 */ +#define VT_EV_TERMINATE_AUTH 4 + +typedef struct vt_evt { + int ve_cmd; + int ve_info; /* vtno or display num */ +} vt_evt_t; + +static int eventstream[2]; + +boolean_t +eventstream_init(void) +{ + if (pipe(eventstream) == -1) + return (B_FALSE); + return (B_TRUE); +} + +void +eventstream_write(int channel, vt_evt_t *pevt) +{ + (void) write(eventstream[channel], pevt, sizeof (vt_evt_t)); +} + +static boolean_t +eventstream_read(int channel, vt_evt_t *pevt) +{ + ssize_t rval; + + rval = read(eventstream[channel], pevt, sizeof (vt_evt_t)); + return (rval > 0); +} + +static void +vt_ev_request(int cmd, int info) +{ + int channel; + vt_evt_t ve; + + ve.ve_cmd = cmd; + ve.ve_info = info; + + channel = (cmd == VT_EV_TERMINATE_AUTH) ? 1 : 0; + eventstream_write(channel, &ve); +} + +static void +vt_clear_events(void) +{ + int rval = 0; + struct stat buf; + vt_evt_t evt; + + while (rval == 0) { + rval = fstat(eventstream[0], &buf); + if (rval != -1 && buf.st_size > 0) + (void) eventstream_read(0, &evt); + else + break; + } +} + +static int vt_conv(int, struct pam_message **, + struct pam_response **, void *); + +/*ARGSUSED*/ +static void +catch(int x) +{ + (void) signal(SIGINT, catch); +} + +/* + * The SIGINT (ctl_c) will restart the authentication, and re-prompt + * the end user to input the password. + */ +static int +vt_poll() +{ + struct pollfd pollfds[2]; + vt_evt_t ve; + int ret; + + pollfds[0].fd = eventstream[0]; + pollfds[1].fd = daemonfd; + pollfds[0].events = pollfds[1].events = + POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + for (;;) { + pollfds[0].revents = pollfds[1].revents = 0; + + ret = poll(pollfds, + sizeof (pollfds) / sizeof (struct pollfd), -1); + if (ret == -1 && errno != EINTR) { + continue; + } + + if (ret == -1 && errno == EINTR) + return (-1); + + if (pollfds[0].revents) { + (void) eventstream_read(0, &ve); + return (0); + } + + if (pollfds[1].revents) + return (1); + + return (0); + + } +} + +static char +vt_getchar(int fd) +{ + char c; + int cnt; + + cnt = read(fd, &c, 1); + if (cnt > 0) { + return (c); + } + + return (EOF); +} + +static char * +vt_getinput(int noecho) +{ + int c; + int i = 0; + struct termio tty; + tcflag_t tty_flags; + char input[PAM_MAX_RESP_SIZE]; + + if (noecho) { + (void) ioctl(daemonfd, TCGETA, &tty); + tty_flags = tty.c_lflag; + tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + (void) ioctl(daemonfd, TCSETAF, &tty); + } + + while ((vt_poll()) == 1) { + if ((c = vt_getchar(daemonfd)) != '\n' && c != '\r' && + c != EOF && (i < PAM_MAX_RESP_SIZE)) + input[i++] = (char)c; + else + break; + } + + input[i] = '\0'; + + if (noecho) { + tty.c_lflag = tty_flags; + (void) ioctl(daemonfd, TCSETAW, &tty); + (void) fputc('\n', stdout); + } + + return (strdup(input)); +} + +/* + * vt_conv: vtdaemon PAM conversation function. + * SIGINT/EINTR is handled in vt_getinput()/vt_poll(). + */ + +/*ARGSUSED*/ +static int +vt_conv(int num_msg, struct pam_message **msg, + struct pam_response **response, void *appdata_ptr) +{ + struct pam_message *m; + struct pam_response *r; + int i, k; + + if (num_msg >= PAM_MAX_NUM_MSG) { + syslog(LOG_ERR, "too many messages %d >= %d", + num_msg, PAM_MAX_NUM_MSG); + *response = NULL; + return (PAM_CONV_ERR); + } + + *response = calloc(num_msg, sizeof (struct pam_response)); + if (*response == NULL) + return (PAM_BUF_ERR); + + m = *msg; + r = *response; + for (i = 0; i < num_msg; i++) { + int echo_off = 0; + + /* Bad message */ + if (m->msg == NULL) { + syslog(LOG_ERR, "message[%d]: %d/NULL\n", + i, m->msg_style); + goto err; + } + + /* + * Fix up final newline: + * remove from prompts, add back for messages. + */ + if (m->msg[strlen(m->msg)] == '\n') + m->msg[strlen(m->msg)] = '\0'; + + r->resp = NULL; + r->resp_retcode = 0; + + switch (m->msg_style) { + + case PAM_PROMPT_ECHO_OFF: + echo_off = 1; + /* FALLTHROUGH */ + + case PAM_PROMPT_ECHO_ON: + (void) fputs(m->msg, stdout); + + r->resp = vt_getinput(echo_off); + break; + + case PAM_ERROR_MSG: + /* the user may want to see this */ + (void) fputs(m->msg, stdout); + (void) fputs("\n", stdout); + break; + + case PAM_TEXT_INFO: + (void) fputs(m->msg, stdout); + (void) fputs("\n", stdout); + break; + + default: + syslog(LOG_ERR, "message[%d]: unknown type" + "%d/val=\"%s\"", i, m->msg_style, m->msg); + + /* error, service module won't clean up */ + goto err; + } + + /* Next message/response */ + m++; + r++; + + } + return (PAM_SUCCESS); + +err: + /* + * Service modules don't clean up responses if an error is returned. + * Free responses here. + */ + r = *response; + for (k = 0; k < i; k++, r++) { + if (r->resp) { + /* Clear before freeing -- maybe a password */ + bzero(r->resp, strlen(r->resp)); + free(r->resp); + r->resp = NULL; + } + } + + free(*response); + *response = NULL; + return (PAM_CONV_ERR); +} + +#define DEF_FILE "/etc/default/login" + +/* Get PASSREQ from default file */ +static boolean_t +vt_default(void) +{ + int flags; + char *ptr; + boolean_t retval = B_FALSE; + + if ((defopen(DEF_FILE)) == 0) { + /* ignore case */ + flags = defcntl(DC_GETFLAGS, 0); + TURNOFF(flags, DC_CASE); + (void) defcntl(DC_SETFLAGS, flags); + + if ((ptr = defread("PASSREQ=")) != NULL && + strcasecmp("YES", ptr) == 0) + retval = B_TRUE; + + (void) defopen(NULL); + } + + return (retval); +} + +/* + * VT_CLEAR_SCREEN_STR is the console terminal escape sequence used to + * clear the current screen. The vt special console (/dev/vt/1) is + * just reserved for vtdaemon, and the TERM/termcap of it is always + * the local sun-color, which is always supported by our kernel terminal + * emulator. + */ +#define VT_CLEAR_SCREEN_STR "\033[2J\033[1;1H" + +static void +vt_do_auth(int target_vt) +{ + char user_name[sizeof (((struct utmpx *)0)->ut_line) + 1] = {'\0'}; + pam_handle_t *vt_pamh; + int err; + int pam_flag = 0; + int chpasswd_tries; + struct pam_conv pam_conv = {vt_conv, NULL}; + pid_t pid; + adt_session_data_t *ah; + + vt_read_utx(target_vt, &pid, user_name); + + if (pid == (pid_t)-1 || user_name[0] == '\0') + return; + + if ((err = pam_start("vtdaemon", user_name, &pam_conv, + &vt_pamh)) != PAM_SUCCESS) + return; + + /* + * firstly switch to the vtdaemon special console + * and clear the current screen + */ + (void) ioctl(daemonfd, VT_ACTIVATE, VT_DAEMON_ARG); + (void) write(daemonfd, VT_CLEAR_SCREEN_STR, + strlen(VT_CLEAR_SCREEN_STR)); + (void) ioctl(daemonfd, VT_SET_TARGET, target_vt); + + (void) mutex_lock(&vt_mutex); + vt_auth_doing = B_TRUE; + vt_hotkeys_pending = B_FALSE; + (void) mutex_unlock(&vt_mutex); + + /* + * Fetch audit handle. + */ + ah = vt_ah_array[target_vt - 1]; + + if (vt_default()) + pam_flag = PAM_DISALLOW_NULL_AUTHTOK; + + do { + if (VT_IS_SYSTEM_CONSOLE(target_vt)) + (void) fprintf(stdout, + "\nUnlock user %s on the system console\n", + user_name); + else + (void) fprintf(stdout, + "\nUnlock user %s on vt/%d\n", user_name, + target_vt); + + err = pam_authenticate(vt_pamh, pam_flag); + + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + break; + } + (void) mutex_unlock(&vt_mutex); + + if (err == PAM_SUCCESS) { + err = pam_acct_mgmt(vt_pamh, pam_flag); + + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + break; + } + (void) mutex_unlock(&vt_mutex); + + if (err == PAM_NEW_AUTHTOK_REQD) { + chpasswd_tries = 0; + + do { + err = pam_chauthtok(vt_pamh, + PAM_CHANGE_EXPIRED_AUTHTOK); + chpasswd_tries++; + + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + break; + } + (void) mutex_unlock(&vt_mutex); + + } while ((err == PAM_AUTHTOK_ERR || + err == PAM_TRY_AGAIN) && + chpasswd_tries < DEF_ATTEMPTS); + + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + break; + } + (void) mutex_unlock(&vt_mutex); + + vt_audit_event(ah, ADT_passwd, err); + } + } + + /* + * Only audit failed unlock here, successful unlock + * will be audited after switching to target vt. + */ + if (err != PAM_SUCCESS) { + (void) fprintf(stdout, "%s", + pam_strerror(vt_pamh, err)); + + vt_audit_event(ah, ADT_screenunlock, err); + } + + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + break; + } + (void) mutex_unlock(&vt_mutex); + + } while (err != PAM_SUCCESS); + + (void) mutex_lock(&vt_mutex); + if (!vt_hotkeys_pending) { + /* + * Should be PAM_SUCCESS to reach here. + */ + (void) ioctl(daemonfd, VT_ACTIVATE, target_vt); + + vt_audit_event(ah, ADT_screenunlock, err); + + /* + * Free audit handle. + */ + (void) adt_end_session(ah); + vt_ah_array[target_vt - 1] = NULL; + } + (void) mutex_unlock(&vt_mutex); + + (void) pam_end(vt_pamh, err); + + if (user_name != NULL) + free(user_name); + + (void) mutex_lock(&vt_mutex); + vt_auth_doing = B_FALSE; + vt_clear_events(); + (void) mutex_unlock(&vt_mutex); +} + +/* main thread (lock and auth) */ +static void +vt_serve_events(void) +{ + struct pollfd pollfds[1]; + int ret; + vt_evt_t ve; + + pollfds[0].fd = eventstream[1]; + pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + for (;;) { + pollfds[0].revents = 0; + ret = poll(pollfds, + sizeof (pollfds) / sizeof (struct pollfd), -1); + if (ret == -1 && errno == EINTR) { + continue; + } + + if (pollfds[0].revents && eventstream_read(1, &ve)) { + /* new request */ + switch (ve.ve_cmd) { + case VT_EV_AUTH: + vt_do_auth(ve.ve_info); + break; + + case VT_EV_LOCK: + vt_activate_screenlock(ve.ve_info); + break; + + case VT_EV_ACTIVATE: + /* directly activate target vt */ + vt_do_activate(ve.ve_info); + break; + } + } + } +} + +static void +vt_check_target_session(uint32_t target_vt) +{ + pid_t pid = (pid_t)-1; + + if (!vt_secure) { + vt_ev_request(VT_EV_ACTIVATE, target_vt); + return; + } + + /* check the target session */ + vt_read_utx(target_vt, &pid, NULL); + if (pid == (pid_t)-1) { + vt_ev_request(VT_EV_ACTIVATE, target_vt); + return; + } + + vt_ev_request(VT_EV_AUTH, target_vt); +} + +static boolean_t +vt_get_active_disp_info(struct vt_dispinfo *vd) +{ + int fd; + struct vt_stat state; + char vtname[16]; + + if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_RDONLY)) < 0) + return (B_FALSE); + + if (ioctl(fd, VT_GETSTATE, &state) != 0) { + (void) close(fd); + return (B_FALSE); + } + (void) close(fd); + + (void) snprintf(vtname, sizeof (vtname), "/dev/vt/%d", state.v_active); + if ((fd = open(vtname, O_RDONLY)) < 0) + return (B_FALSE); + + if (ioctl(fd, VT_GETDISPINFO, vd) != 0) { + (void) close(fd); + return (B_FALSE); + } + + (void) close(fd); + return (B_TRUE); +} + +/* + * Xserver registers its pid into kernel to associate it with + * its vt upon startup for each graphical display. So here we can + * check if the pid is of the Xserver for the current active + * display when we receive a special VT_EV_X_EXIT request from + * a process. If the request does not come from the current + * active Xserver, it is discarded. + */ +static boolean_t +vt_check_disp_active(pid_t x_pid) +{ + struct vt_dispinfo vd; + + if (vt_get_active_disp_info(&vd) && + vd.v_pid == x_pid) + return (B_TRUE); + + return (B_FALSE); +} + +/* + * check if the pid is of the Xserver for the current active display, + * return true when it is, and then also return other associated + * information with the Xserver. + */ +static boolean_t +vt_get_disp_info(pid_t x_pid, int *logged_in, int *display_num) +{ + struct vt_dispinfo vd; + + if (!vt_get_active_disp_info(&vd) || + vd.v_pid != x_pid) + return (B_FALSE); + + *logged_in = vd.v_login; + *display_num = vd.v_dispnum; + return (B_TRUE); +} + +static void +vt_terminate_auth(void) +{ + struct timespec sleeptime; + + sleeptime.tv_sec = 0; + sleeptime.tv_nsec = 1000000; /* 1ms */ + + (void) mutex_lock(&vt_mutex); + while (vt_auth_doing) { + vt_ev_request(VT_EV_TERMINATE_AUTH, 0); + + if (vt_auth_doing) { + (void) mutex_unlock(&vt_mutex); + (void) nanosleep(&sleeptime, NULL); + sleeptime.tv_nsec *= 2; + (void) mutex_lock(&vt_mutex); + } + } + (void) mutex_unlock(&vt_mutex); +} + +static void +vt_do_hotkeys(pid_t pid, uint32_t target_vt) +{ + int logged_in; + int display_num; + + if (validate_target_vt(target_vt) != 0) + return; + + /* + * Maybe last switch action is being taken and the lock is ongoing, + * here we must reject the newly request. + */ + (void) mutex_lock(&vt_mutex); + if (vt_hotkeys_pending) { + (void) mutex_unlock(&vt_mutex); + return; + } + + /* cleared in vt_do_active and vt_do_auth */ + vt_hotkeys_pending = B_TRUE; + (void) mutex_unlock(&vt_mutex); + + vt_terminate_auth(); + + /* check source session for this hotkeys request */ + if (pid == 0) { + /* ok, it comes from kernel. */ + if (vt_secure) + vt_check_source_audit(); + + /* then only need to check target session */ + vt_check_target_session(target_vt); + return; + } + + /* + * check if it comes from current active X graphical session, + * if not, ignore this request. + */ + if (!vt_get_disp_info(pid, &logged_in, &display_num)) { + (void) mutex_lock(&vt_mutex); + vt_hotkeys_pending = B_FALSE; + (void) mutex_unlock(&vt_mutex); + return; + } + + if (logged_in && vt_secure) + vt_ev_request(VT_EV_LOCK, display_num); + + vt_check_target_session(target_vt); +} + +/* + * The main routine for the door server that deals with secure hotkeys + */ +/* ARGSUSED */ +static void +server_for_door(void *cookie, char *args, size_t alen, door_desc_t *dp, + uint_t n_desc) +{ + ucred_t *uc = NULL; + vt_cmd_arg_t *vtargp; + + /* LINTED E_BAD_PTR_CAST_ALIGN */ + vtargp = (vt_cmd_arg_t *)args; + + if (vtargp == NULL || + alen != sizeof (vt_cmd_arg_t) || + door_ucred(&uc) != 0) { + (void) door_return(NULL, 0, NULL, 0); + return; + } + + switch (vtargp->vt_ev) { + case VT_EV_X_EXIT: + /* + * Xserver will issue this event requesting to switch back + * to previous active vt when it's exiting and the associated + * vt is currently active. + */ + if (vt_check_disp_active(ucred_getpid(uc))) + vt_do_hotkeys(0, vtargp->vt_num); + break; + + case VT_EV_HOTKEYS: + if (!vt_hotkeys) /* hotkeys are disabled? */ + break; + + vt_do_hotkeys(ucred_getpid(uc), vtargp->vt_num); + break; + + default: + break; + } + + ucred_free(uc); + (void) door_return(NULL, 0, NULL, 0); +} + +static boolean_t +setup_door(void) +{ + if ((vt_door = door_create(server_for_door, NULL, + DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) { + syslog(LOG_ERR, "door_create failed: %s", strerror(errno)); + return (B_FALSE); + } + + (void) fdetach(vt_door_path); + + if (fattach(vt_door, vt_door_path) != 0) { + syslog(LOG_ERR, "fattach to %s failed: %s", + vt_door_path, strerror(errno)); + (void) door_revoke(vt_door); + (void) fdetach(vt_door_path); + vt_door = -1; + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * check to see if vtdaemon is already running. + * + * The idea here is that we want to open the path to which we will + * attach our door, lock it, and then make sure that no-one has beat us + * to fattach(3c)ing onto it. + * + * fattach(3c) is really a mount, so there are actually two possible + * vnodes we could be dealing with. Our strategy is as follows: + * + * - If the file we opened is a regular file (common case): + * There is no fattach(3c)ed door, so we have a chance of becoming + * the running vtdaemon. We attempt to lock the file: if it is + * already locked, that means someone else raced us here, so we + * lose and give up. + * + * - If the file we opened is a namefs file: + * This means there is already an established door fattach(3c)'ed + * to the rendezvous path. We've lost the race, so we give up. + * Note that in this case we also try to grab the file lock, and + * will succeed in acquiring it since the vnode locked by the + * "winning" vtdaemon was a regular one, and the one we locked was + * the fattach(3c)'ed door node. At any rate, no harm is done. + */ +static boolean_t +make_daemon_exclusive(void) +{ + int doorfd = -1; + boolean_t ret = B_FALSE; + struct stat st; + struct flock flock; + +top: + if ((doorfd = open(vt_door_path, O_CREAT|O_RDWR, + S_IREAD|S_IWRITE|S_IRGRP|S_IROTH)) < 0) { + syslog(LOG_ERR, "failed to open %s", vt_door_path); + goto out; + } + if (fstat(doorfd, &st) < 0) { + syslog(LOG_ERR, "failed to stat %s", vt_door_path); + goto out; + } + /* + * Lock the file to synchronize + */ + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = (off_t)0; + flock.l_len = (off_t)0; + if (fcntl(doorfd, F_SETLK, &flock) < 0) { + /* + * Someone else raced us here and grabbed the lock file + * first. A warning here and exit. + */ + syslog(LOG_ERR, "vtdaemon is already running!"); + goto out; + } + + if (strcmp(st.st_fstype, "namefs") == 0) { + struct door_info info; + + /* + * There is already something fattach()'ed to this file. + * Lets see what the door is up to. + */ + if (door_info(doorfd, &info) == 0 && info.di_target != -1) { + syslog(LOG_ERR, "vtdaemon is already running!"); + goto out; + } + + (void) fdetach(vt_door_path); + (void) close(doorfd); + goto top; + } + + ret = setup_door(); + +out: + (void) close(doorfd); + return (ret); +} + +static boolean_t +mkvtdir(void) +{ + struct stat st; + /* + * We must create and lock everyone but root out of VT_TMPDIR + * since anyone can open any UNIX domain socket, regardless of + * its file system permissions. + */ + if (mkdir(VT_TMPDIR, S_IRWXU|S_IROTH|S_IXOTH|S_IRGRP|S_IXGRP) < 0 && + errno != EEXIST) { + syslog(LOG_ERR, "could not mkdir '%s'", VT_TMPDIR); + return (B_FALSE); + } + /* paranoia */ + if ((stat(VT_TMPDIR, &st) < 0) || !S_ISDIR(st.st_mode)) { + syslog(LOG_ERR, "'%s' is not a directory", VT_TMPDIR); + return (B_FALSE); + } + (void) chmod(VT_TMPDIR, S_IRWXU|S_IROTH|S_IXOTH|S_IRGRP|S_IXGRP); + return (B_TRUE); +} + +int +main(int argc, char *argv[]) +{ + int i; + int opt; + priv_set_t *privset; + int active; + + openlog("vtdaemon", LOG_PID | LOG_CONS, 0); + + /* + * Check that we have all privileges. It would be nice to pare + * this down, but this is at least a first cut. + */ + if ((privset = priv_allocset()) == NULL) { + syslog(LOG_ERR, "priv_allocset failed"); + return (1); + } + + if (getppriv(PRIV_EFFECTIVE, privset) != 0) { + syslog(LOG_ERR, "getppriv failed", "getppriv"); + priv_freeset(privset); + return (1); + } + + if (priv_isfullset(privset) == B_FALSE) { + syslog(LOG_ERR, "You lack sufficient privilege " + "to run this command (all privs required)"); + priv_freeset(privset); + return (1); + } + priv_freeset(privset); + + while ((opt = getopt(argc, argv, "ksrc:")) != EOF) { + switch (opt) { + case 'k': + vt_hotkeys = B_FALSE; + break; + case 's': + vt_secure = B_FALSE; + break; + case 'c': + vtnodecount = atoi(optarg); + break; + default: + break; + } + } + + (void) vt_setup_signal(SIGINT, 1); + + if (!mkvtdir()) + return (1); + + if (!eventstream_init()) + return (1); + + (void) snprintf(vt_door_path, sizeof (vt_door_path), + VT_TMPDIR "/vtdaemon_door"); + + if (!make_daemon_exclusive()) + return (1); + + /* only the main thread accepts SIGINT */ + (void) vt_setup_signal(SIGINT, 0); + (void) sigset(SIGPIPE, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, catch); + + for (i = 0; i < 3; i++) + (void) close(i); + (void) setsid(); + + if ((daemonfd = open(VT_DAEMON_CONSOLE_FILE, O_RDWR)) < 0) { + return (1); + } + + if (daemonfd != 0) + (void) dup2(daemonfd, STDIN_FILENO); + if (daemonfd != 1) + (void) dup2(daemonfd, STDOUT_FILENO); + + if (vtnodecount >= 2) + (void) ioctl(daemonfd, VT_CONFIG, vtnodecount); + + if ((vt_ah_array = calloc(vtnodecount - 1, + sizeof (adt_session_data_t *))) == NULL) + return (1); + + (void) ioctl(daemonfd, VT_GETACTIVE, &active); + + if (active == 1) { + /* + * This is for someone who restarts vtdaemon while vtdaemon + * is doing authentication on /dev/vt/1. + * A better way is to continue the authentication, but there + * are chances that the status of the target VT has changed. + * So we just clear the screen here. + */ + (void) write(daemonfd, VT_CLEAR_SCREEN_STR, + strlen(VT_CLEAR_SCREEN_STR)); + } + + vt_serve_events(); + /*NOTREACHED*/ +} + +static int +vt_audit_start(adt_session_data_t **ah, pid_t pid) +{ + ucred_t *uc; + + if (adt_start_session(ah, NULL, 0)) + return (-1); + + if ((uc = ucred_get(pid)) == NULL) { + (void) adt_end_session(*ah); + return (-1); + } + + if (adt_set_from_ucred(*ah, uc, ADT_NEW)) { + ucred_free(uc); + (void) adt_end_session(*ah); + return (-1); + } + + ucred_free(uc); + return (0); +} + +/* + * Write audit event + */ +static void +vt_audit_event(adt_session_data_t *ah, au_event_t event_id, int status) +{ + adt_event_data_t *event; + + + if ((event = adt_alloc_event(ah, event_id)) == NULL) { + return; + } + + (void) adt_put_event(event, + status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE, + status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM + status); + + adt_free_event(event); +} + +static void +vt_check_source_audit(void) +{ + int fd; + int source_vt; + int real_vt; + struct vt_stat state; + pid_t pid; + adt_session_data_t *ah; + + if ((fd = open(VT_DAEMON_CONSOLE_FILE, O_WRONLY)) < 0) + return; + + if (ioctl(fd, VT_GETSTATE, &state) != 0 || + ioctl(fd, VT_GETACTIVE, &real_vt) != 0) { + (void) close(fd); + return; + } + + source_vt = state.v_active; /* 1..n */ + (void) close(fd); + + /* check if it's already locked */ + if (real_vt == 1) /* vtdaemon is taking over the screen */ + return; + + vt_read_utx(source_vt, &pid, NULL); + if (pid == (pid_t)-1) + return; + + if (vt_audit_start(&ah, pid) != 0) { + syslog(LOG_ERR, "audit start failed "); + return; + } + + /* + * In case the previous session terminated abnormally. + */ + if (vt_ah_array[source_vt - 1] != NULL) + (void) adt_end_session(vt_ah_array[source_vt - 1]); + + vt_ah_array[source_vt - 1] = ah; + + vt_audit_event(ah, ADT_screenlock, PAM_SUCCESS); +} diff --git a/usr/src/cmd/vt/vtinfo.c b/usr/src/cmd/vt/vtinfo.c new file mode 100644 index 0000000000..b9af7c3cc3 --- /dev/null +++ b/usr/src/cmd/vt/vtinfo.c @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stropts.h> +#include <sys/vt.h> + +/* + * return value: + * 0: disabled + * 1: enabled + * 2: An error occured + */ +int +main(void) +{ + int result; + int fd; + + if ((fd = open("/dev/vt/0", O_RDONLY)) < 0) + return (2); + + if (ioctl(fd, VT_ENABLED, &result) != 0) { + (void) close(fd); + return (2); + } + + (void) close(fd); + + if (result == 0) + return (0); /* disabled */ + + return (1); /* enabled */ +} diff --git a/usr/src/cmd/vt/vtxlock.sh b/usr/src/cmd/vt/vtxlock.sh new file mode 100644 index 0000000000..4ed6e55c86 --- /dev/null +++ b/usr/src/cmd/vt/vtxlock.sh @@ -0,0 +1,85 @@ +#!/sbin/sh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# $1 is the display to be locked. +# /var/xauth/$1 is a symbolic link to the actual xauth file. +# + +XSCREENSAVER_LOCK=/usr/openwin/bin/xscreensaver-command +XSCREENSAVER_LOCKARGS="-lock" +XSCREENSAVER_CHECKARGS="-time" +XSCREENSAVER_LOCKED="locked" + +OTHER_LOCK=/usr/openwin/bin/xlock +OTHER_LOCKARGS="-mode blank" + +XLSATOMS="/usr/openwin/bin/xlsatoms" +XLSATOMS_ARGS="-name" +CDE_ATOM=_DT_SM_PREFERENCES +GNOME_ATOM=GNOME_SM_DESKTOP +XSCREENSAVER_ATOM=SCREENSAVER + +DISPLAY=:$1; export DISPLAY +XAUTHORITY=/var/xauth/$1; export XAUTHORITY + +# +# Note that these text strings we're greping are not localized. +# + +# +# Is it GNOME? +# +if ${XLSATOMS} ${XLSATOMS_ARGS} ${GNOME_ATOM} 2>/dev/null \ + | grep -w ${GNOME_ATOM} >/dev/null; then + # + # Is it xscreensaver? + # + # xscreensaver + if [ -x ${XSCREENSAVER_LOCK} ]; then + ${XSCREENSAVER_LOCK} ${XSCREENSAVER_CHECKARGS} 2>/dev/null \ + | grep -w ${XSCREENSAVER_LOCKED} >/dev/null && exit 0 + + ${XSCREENSAVER_LOCK} ${XSCREENSAVER_LOCKARGS} >/dev/null 2>&1 & + exit 0 + fi +fi + +# +# Is it CDE? +# +if ${XLSATOMS} ${XLSATOMS_ARGS} ${CDE_ATOM} 2>/dev/null \ + | grep -w ${CDE_ATOM} >/dev/null; then + exit 0 +fi + +# In other situations, use xlock as default. +if [ -x ${OTHER_LOCK} ]; then + ${OTHER_LOCK} ${OTHER_LOCKARGS} & + exit 0 +fi + +exit 0 diff --git a/usr/src/lib/libc/port/gen/ttyname.c b/usr/src/lib/libc/port/gen/ttyname.c index 24f2725199..01a4ea8cb7 100644 --- a/usr/src/lib/libc/port/gen/ttyname.c +++ b/usr/src/lib/libc/port/gen/ttyname.c @@ -124,6 +124,7 @@ static const entry_t dev_dir = static const entry_t def_srch_dirs[] = { /* default search list */ { "/dev/pts", MATCH_ALL }, + { "/dev/vt", MATCH_ALL }, { "/dev/term", MATCH_ALL }, { "/dev/zcons", MATCH_ALL }, { NULL, 0 } @@ -537,6 +538,9 @@ srch_dir(const entry_t path, /* current path */ if (stat64(file_name, &tsb) < 0) continue; + if (strcmp(file_name, "/dev/vt/active") == 0) + continue; + /* * skip "/dev/syscon" because it may be an invalid link after * single user mode. diff --git a/usr/src/lib/libdevinfo/devinfo_devperm.c b/usr/src/lib/libdevinfo/devinfo_devperm.c index 3f7380a2de..e2716af8d3 100644 --- a/usr/src/lib/libdevinfo/devinfo_devperm.c +++ b/usr/src/lib/libdevinfo/devinfo_devperm.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #define _POSIX_PTHREAD_SEMANTICS /* for getgrnam_r */ #ifdef lint #define _REENTRANT /* for strtok_r */ @@ -56,9 +54,12 @@ #include <strings.h> #include <libdevinfo.h> #include <zone.h> +#include <fcntl.h> +#include <utmpx.h> extern int is_minor_node(const char *, const char **); +static int is_login_user(uid_t); static int logindevperm(const char *, uid_t, gid_t, void (*)()); static int dir_dev_acc(char *, char *, uid_t, gid_t, mode_t, char *line, void (*)()); @@ -192,8 +193,21 @@ logindevperm(const char *ttyn, uid_t uid, gid_t gid, void (*errmsg)(char *)) if (console == NULL) continue; /* ignore blank lines */ - if (strcmp(console, ttyn) != 0) - continue; /* not our tty, skip */ + /* + * If "console" read from /dev/logindevperm is + * "/dev/vt/active", then the first user who logged into + * consoles (/dev/vt/# or /dev/console) takes ownership. + * Otherwise the first user who logged into "console" + * takes owership. + */ + if (strcmp(console, ttyn) != 0) { + if (strcmp(console, "/dev/vt/active") != 0) + continue; /* not our tty, skip */ + if (strncmp(ttyn, "/dev/vt/", + strlen("/dev/vt/")) != 0 && strcmp(ttyn, + "/dev/console") != 0) + continue; /* not our tty, skip */ + } mode_str = strtok_r(last, field_delims, &last); if (mode_str == NULL) { @@ -372,6 +386,38 @@ check_driver_match(char *path, char *line) } /* + * Check whether the user has logged onto "/dev/console" or "/dev/vt/#". + */ +static int +is_login_user(uid_t uid) +{ + int changed = 0; + struct passwd pwd, *ppwd; + char pwd_buf[NSS_BUFLEN_PASSWD]; + struct utmpx *utx; + + if ((getpwuid_r(uid, &pwd, pwd_buf, NSS_BUFLEN_PASSWD, &ppwd))) { + return (0); + } + + setutxent(); + while ((utx = getutxent()) != NULL) { + if (utx->ut_type == USER_PROCESS && + strncmp(utx->ut_user, ppwd->pw_name, + strlen(ppwd->pw_name)) == 0 && (strncmp(utx->ut_line, + "console", strlen("console")) == 0 || strncmp(utx->ut_line, + "vt", strlen("vt")) == 0)) { + + changed = 1; + break; + } + } + endutxent(); + + return (changed); +} + +/* * Apply owner/group/perms to all files (except "." and "..") * in a directory. * This function is recursive. We start with "/" and the rest of the pathname @@ -423,6 +469,17 @@ dir_dev_acc(char *path, char *left_to_do, uid_t uid, gid_t gid, mode_t mode, if (strlen(left_to_do) == 0) { /* finally check the driver matches */ if (check_driver_match(path, line) == 0) { + /* + * if the owner of device has been + * login, the ownership and mode + * should be set already. in + * this case, do not set the + * permissions. + */ + if (is_login_user(stat_buf.st_uid)) { + + return (0); + } /* we are done, set the permissions */ if (setdevaccess(path, uid, gid, mode, errmsg)) { diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt index 6c671a5d75..42ec91a1b1 100644 --- a/usr/src/lib/libsecdb/auth_attr.txt +++ b/usr/src/lib/libsecdb/auth_attr.txt @@ -141,6 +141,7 @@ solaris.smf.manage.system-log:::Manage Syslog Service States::help=SmfSyslogStat solaris.smf.manage.tnctl:::Manage Refresh of Trusted Network Parameters::help=TNctl.html solaris.smf.manage.tnd:::Manage Trusted Network Daemon::help=TNDaemon.html solaris.smf.manage.vscan:::Manage VSCAN Service States::help=SmfVscanStates.html +solaris.smf.manage.vt:::Manage Virtual Console Service States::help=SmfVtStates.html solaris.smf.manage.wpa:::Manage WPA Service States::help=SmfWpaStates.html solaris.smf.manage.ndmp:::Manage NDMP Service States::help=SmfNDMPStates.html solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeader.html @@ -158,6 +159,7 @@ solaris.smf.read.smb:::Read permission for protected SMF SMB Service Properties: solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html solaris.smf.value.vscan:::Change Values of VSCAN Properties::help=SmfValueVscan.html +solaris.smf.value.vt:::Change Values of Virtual Console Service Properties::help=SmfValueVt.html solaris.smf.value.ndmp:::Change Values of SMF NDMP Service Properties::help=SmfValueNDMP.html solaris.smf.read.ndmp:::Read permission for protected SMF NDMP Service Properties::help=AuthReadNDMP.html # diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile index f279119a4d..af16336769 100644 --- a/usr/src/lib/libsecdb/help/auths/Makefile +++ b/usr/src/lib/libsecdb/help/auths/Makefile @@ -91,6 +91,7 @@ HTMLENTS = \ SmfValueExAcctFlow.html \ SmfValueExAcctProcess.html \ SmfValueExAcctTask.html \ + SmfVtStates.html \ SmfValueHeader.html \ SmfValueInetd.html \ SmfValueIPsec.html \ @@ -107,6 +108,7 @@ HTMLENTS = \ SmfSMBStates.html \ SmfValueVscan.html \ SmfVscanStates.html \ + SmfValueVt.html \ SmfWpaStates.html \ NetworkAutoconf.html \ NetworkHeader.html \ diff --git a/usr/src/lib/libsecdb/help/auths/SmfValueVt.html b/usr/src/lib/libsecdb/help/auths/SmfValueVt.html new file mode 100644 index 0000000000..8ff346cbbc --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfValueVt.html @@ -0,0 +1,37 @@ +<HTML> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + +Copyright 2008 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. +--> +<!-- + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +--> +<BODY> +When Value Virtual Console Properties is in the Authorizations Include +column, it grants the the authorization to change virtual console service +property values. +<P> +If Value Virtual Console Properties is grayed, then you are not entitled to +Add or Remove this authorization. +<BR> +</BODY> +</HTML> diff --git a/usr/src/lib/libsecdb/help/auths/SmfVtStates.html b/usr/src/lib/libsecdb/help/auths/SmfVtStates.html new file mode 100644 index 0000000000..33b660289c --- /dev/null +++ b/usr/src/lib/libsecdb/help/auths/SmfVtStates.html @@ -0,0 +1,37 @@ +<HTML> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + +Copyright 2008 Sun Microsystems, Inc. All rights reserved. +Use is subject to license terms. +--> +<!-- + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> +--> +<BODY> +When Manage Virtual Console Service States is in the Authorizations Include +column, it grants the authorization to enable, disable, or +restart the virtual console daemon. +<p> +If Manage Virtual Console Service States is grayed, then you are not entitled +to Add or Remove this authorization. +<BR> +</BODY> +</HTML> diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt index 4c09ff6b49..2fb7504a67 100644 --- a/usr/src/lib/libsecdb/prof_attr.txt +++ b/usr/src/lib/libsecdb/prof_attr.txt @@ -39,7 +39,7 @@ Printer Management:::Manage printers, daemons, spooling:auths=solaris.print.*,so Cron Management:::Manage at and cron jobs:auths=solaris.jobs.*,solaris.smf.manage.cron;help=RtCronMngmnt.html Log Management:::Manage log files:help=RtLogMngmnt.html Basic Solaris User:::Automatically assigned rights:auths=solaris.profmgr.read,solaris.jobs.user,solaris.mail.mailq,solaris.device.mount.removable;profiles=All;help=RtDefault.html -Device Security:::Manage devices and Volume Manager:auths=solaris.device.*;help=RtDeviceSecurity.html +Device Security:::Manage devices and Volume Manager:auths=solaris.device.*,solaris.smf.manage.vt;help=RtDeviceSecurity.html DHCP Management:::Manage the DHCP service:auths=solaris.dhcpmgr.*;help=RtDHCPMngmnt.html Extended Accounting Flow Management:::Manage the Flow Extended Accounting service:auths=solaris.smf.manage.extended-accounting.flow,solaris.smf.value.extended-accounting.flow;profiles=acctadm;help=RtExActtFlow.html Extended Accounting Process Management:::Manage the Process Extended Accounting service:auths=solaris.smf.manage.extended-accounting.process,solaris.smf.value.extended-accounting.process;profiles=acctadm;hep=RtExAcctProcess.html diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com index 387eeaab53..a8ec534d70 100644 --- a/usr/src/pkgdefs/SUNW0on/prototype_com +++ b/usr/src/pkgdefs/SUNW0on/prototype_com @@ -265,6 +265,7 @@ f none usr/lib/help/auths/locale/SmfVscanStates.html 444 root bin f none usr/lib/help/auths/locale/SmfValueExAcctFlow.html 444 root bin f none usr/lib/help/auths/locale/SmfValueExAcctProcess.html 444 root bin f none usr/lib/help/auths/locale/SmfValueExAcctTask.html 444 root bin +f none usr/lib/help/auths/locale/SmfVtStates.html 444 root bin f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin f none usr/lib/help/auths/locale/SmfValueInetd.html 444 root bin f none usr/lib/help/auths/locale/SmfValueIPsec.html 444 root bin @@ -279,6 +280,7 @@ f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin f none usr/lib/help/auths/locale/SmfValueSMB.html 444 root bin f none usr/lib/help/auths/locale/AuthReadSMB.html 444 root bin f none usr/lib/help/auths/locale/SmfValueVscan.html 444 root bin +f none usr/lib/help/auths/locale/SmfValueVt.html 444 root bin f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin f none usr/lib/help/auths/locale/NetworkAutoconf.html 444 root bin f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin diff --git a/usr/src/pkgdefs/SUNWcsd/prototype_com b/usr/src/pkgdefs/SUNWcsd/prototype_com index 1c036a6c15..9ff03705c9 100644 --- a/usr/src/pkgdefs/SUNWcsd/prototype_com +++ b/usr/src/pkgdefs/SUNWcsd/prototype_com @@ -22,8 +22,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -82,6 +80,7 @@ s none dev/stdin=./fd/0 s none dev/stdout=./fd/1 d none dev/swap 755 root sys d none dev/term 755 root root +d none dev/vt 755 root sys d none dev/zcons 755 root sys d none devices 755 root sys d none devices/pseudo 755 root sys diff --git a/usr/src/pkgdefs/SUNWcsr/postinstall b/usr/src/pkgdefs/SUNWcsr/postinstall index a97a62f4f6..fb42157c67 100644 --- a/usr/src/pkgdefs/SUNWcsr/postinstall +++ b/usr/src/pkgdefs/SUNWcsr/postinstall @@ -21,8 +21,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# BASEPREFIX=`echo $BASEDIR | sed "s/\//_/g"` # # Clear sysidtool which may have gone into maintenance due to a dependency @@ -408,4 +406,29 @@ if [ -f /etc/acctadm.conf ]; then fi _ACCTADM +# Preinstall script will create this file if vtdaemon service was +# already installed, in which case we preserve current service state, +# be it enabled or disabled. +if [ -f $PKG_INSTALL_ROOT/var/tmp/vtdaemon_installed.tmp ]; then + rm -f $PKG_INSTALL_ROOT/var/tmp/vtdaemon_installed.tmp +elif [ "${PKG_INSTALL_ROOT:-/}" = "/" ]; then + # live system + /usr/sbin/svcadm enable svc:/system/vtdaemon:default + /usr/sbin/svcadm enable svc:/system/console-login:vt2 + /usr/sbin/svcadm enable svc:/system/console-login:vt3 + /usr/sbin/svcadm enable svc:/system/console-login:vt4 + /usr/sbin/svcadm enable svc:/system/console-login:vt5 + /usr/sbin/svcadm enable svc:/system/console-login:vt6 +else + # upgrade + cat >> ${PKG_INSTALL_ROOT}/var/svc/profile/upgrade <<-EOF + /usr/sbin/svcadm enable svc:/system/vtdaemon:default + /usr/sbin/svcadm enable svc:/system/console-login:vt2 + /usr/sbin/svcadm enable svc:/system/console-login:vt3 + /usr/sbin/svcadm enable svc:/system/console-login:vt4 + /usr/sbin/svcadm enable svc:/system/console-login:vt5 + /usr/sbin/svcadm enable svc:/system/console-login:vt6 + EOF +fi + exit 0 diff --git a/usr/src/pkgdefs/SUNWcsr/preinstall b/usr/src/pkgdefs/SUNWcsr/preinstall index 53fe22b5ec..ad54851d7d 100644 --- a/usr/src/pkgdefs/SUNWcsr/preinstall +++ b/usr/src/pkgdefs/SUNWcsr/preinstall @@ -18,10 +18,9 @@ # # CDDL HEADER END # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" # # When first introduced the /etc/inet/ipnodes file was seperate from @@ -127,4 +126,14 @@ if [ "$UPDATE" = yes ]; then fi fi +# Presence of this temp file will tell postinstall script +# that the vtdaemon service is already installed, in which case +# the current service state will be preserved, be it enabled +# or disabled. +rm -f $PKG_INSTALL_ROOT/var/tmp/vtdaemon_installed.tmp > /dev/null 2>&1 + +if [ -f $PKG_INSTALL_ROOT/var/svc/manifest/system/vtdaemon.xml ]; then + touch $PKG_INSTALL_ROOT/var/tmp/vtdaemon_installed.tmp +fi + exit 0 diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com index 737e3b4757..a806ca1425 100644 --- a/usr/src/pkgdefs/SUNWcsr/prototype_com +++ b/usr/src/pkgdefs/SUNWcsr/prototype_com @@ -375,6 +375,7 @@ f none lib/svc/method/svc-dlmgmtd 0555 root bin f none lib/svc/method/svc-nscd 0555 root bin f none lib/svc/method/svc-utmpd 0555 root bin f none lib/svc/method/system-log 0555 root bin +f none lib/svc/method/vtdaemon 0555 root bin f none lib/svc/method/yp 0555 root bin d none lib/svc/monitor 0755 root bin d none lib/svc/seed 0755 root bin @@ -547,6 +548,7 @@ f manifest var/svc/manifest/system/rmtmpfiles.xml 0444 root sys f manifest var/svc/manifest/system/sac.xml 0444 root sys f manifest var/svc/manifest/system/system-log.xml 0444 root sys f manifest var/svc/manifest/system/utmp.xml 0444 root sys +f manifest var/svc/manifest/system/vtdaemon.xml 0444 root sys d none var/svc/manifest/system/svc 755 root sys f manifest var/svc/manifest/system/svc/restarter.xml 0444 root sys d none var/svc/profile 755 root sys diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com index 213f55d4a3..e01cfff87c 100644 --- a/usr/src/pkgdefs/SUNWcsu/prototype_com +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com @@ -505,6 +505,7 @@ f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueExAcctFlow.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueExAcctProcess.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueExAcctTask.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfVtStates.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueInetd.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueIPsec.html 444 root bin @@ -519,6 +520,7 @@ f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin f none usr/lib/help/auths/locale/C/SmfValueSMB.html 444 root bin f none usr/lib/help/auths/locale/C/AuthReadSMB.html 444 root bin f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin +f none usr/lib/help/auths/locale/C/SmfValueVt.html 444 root bin f none usr/lib/help/auths/locale/C/SysDate.html 444 root bin f none usr/lib/help/auths/locale/C/SysHeader.html 444 root bin f none usr/lib/help/auths/locale/C/SysMaintenance.html 444 root bin @@ -697,6 +699,9 @@ f none usr/lib/utmp_update 4555 root bin f none usr/lib/utmpd 555 root bin f none usr/lib/vna 555 root bin f none usr/lib/vplot 555 root bin +f none usr/lib/vtdaemon 555 root bin +f none usr/lib/vtinfo 555 root bin +f none usr/lib/vtxlock 555 root bin s none usr/mail=../var/mail d none usr/net 755 root sys d none usr/net/nls 755 root sys diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com index 91e68ba64f..6a9a3b1db8 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_com +++ b/usr/src/pkgdefs/SUNWhea/prototype_com @@ -957,6 +957,7 @@ f none usr/include/sys/kbd.h 644 root bin f none usr/include/sys/kbdreg.h 644 root bin f none usr/include/sys/kbio.h 644 root bin f none usr/include/sys/kcpc.h 644 root bin +f none usr/include/sys/kd.h 644 root bin f none usr/include/sys/kdi.h 644 root bin f none usr/include/sys/kdi_impl.h 644 root bin f none usr/include/sys/kdi_machimpl.h 644 root bin @@ -1354,6 +1355,8 @@ f none usr/include/sys/vmmeter.h 644 root bin f none usr/include/sys/vmparam.h 644 root bin f none usr/include/sys/vmsystm.h 644 root bin f none usr/include/sys/vnode.h 644 root bin +f none usr/include/sys/vt.h 644 root bin +f none usr/include/sys/vtdaemon.h 644 root bin f none usr/include/sys/vtoc.h 644 root bin f none usr/include/sys/vtrace.h 644 root bin f none usr/include/sys/vuid_event.h 644 root bin diff --git a/usr/src/pkgdefs/SUNWhea/prototype_i386 b/usr/src/pkgdefs/SUNWhea/prototype_i386 index 824c5d690c..c5769ec020 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_i386 +++ b/usr/src/pkgdefs/SUNWhea/prototype_i386 @@ -22,7 +22,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -89,7 +88,6 @@ f none usr/include/sys/fp.h 644 root bin f none usr/include/sys/hypervisor.h 644 root bin f none usr/include/sys/i8272A.h 644 root bin f none usr/include/sys/iommulib.h 644 root bin -f none usr/include/sys/kd.h 644 root bin f none usr/include/sys/mc.h 644 root bin f none usr/include/sys/mc_amd.h 644 root bin f none usr/include/sys/mca_amd.h 644 root bin diff --git a/usr/src/pkgdefs/common_files/i.logindevperm b/usr/src/pkgdefs/common_files/i.logindevperm index e05cbe9823..ea511184d8 100644 --- a/usr/src/pkgdefs/common_files/i.logindevperm +++ b/usr/src/pkgdefs/common_files/i.logindevperm @@ -19,10 +19,7 @@ # # CDDL HEADER END # -# -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -62,9 +59,13 @@ while read src dest; do devices=`echo "$devices" | sed 's/\[/\\\[/g' | sed 's/\]/\\\]/g' | sed 's/\+/\\\+/g' | sed 's/\*/\\\*/g'` - # if dest has an entry for these devices, preserve that + # if dest has an entry for these devices and the difference + # between dest entry and src entry is not only + # "/dev/console" vs. "/dev/vt/active", preserve dest entry dest_entry=`egrep "^[# ]*/[a-zA-Z0-9\/]+[ ]+[0-9]+[ ]+$devices[ ]*$drivers" $dest | tail -1 2>/dev/null` - if [ -n "$dest_entry" ]; then + tmp_line=`echo "$line" | sed 's:/dev/vt/active:/dev/console:'` + + if [ -n "$dest_entry" ] && [ "$dest_entry" != "$tmp_line" ]; then echo "$dest_entry" else echo "$line" diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index 5350bc4e40..5076f9f718 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -247,6 +247,7 @@ superfluous_nonglobal_zone_files=" dev/stdout dev/swap dev/term + dev/vt devices etc/dacf.conf etc/dat @@ -321,6 +322,7 @@ superfluous_nonglobal_zone_files=" lib/svc/method/svc-tnd lib/svc/method/svc-vntsd lib/svc/method/svc-zones + lib/svc/method/vtdaemon platform/*/kernel platform/SUNW,Sun-Fire-15000/lib/cvcd platform/SUNW,Ultra-Enterprise-10000/lib/cvcd @@ -379,6 +381,7 @@ superfluous_nonglobal_zone_files=" var/svc/manifest/system/resource-mgmt.xml var/svc/manifest/system/scheduler.xml var/svc/manifest/system/sysevent.xml + var/svc/manifest/system/vtdaemon.xml var/svc/manifest/system/zones.xml var/svc/manifest/system/filesystem/rmvolmgr.xml " @@ -1557,6 +1560,20 @@ smf_cleanup_dlmgmtd() { ) } +smf_cleanup_vt() { + ( + smf_delete_manifest var/src/manifest/system/vtdaemon.xml + cd $root + rm -f lib/svc/method/vtdaemon + + vt_conslogin_instances=`/usr/bin/svcs -o FMRI | \ + grep console-login:vt` + for i in $vt_conslogin_instances; do + /usr/sbin/svccfg delete -f $i + done + ) +} + old_mfst_dir="var/svc/manifest.orig" new_mfst_dir="var/svc/manifest" @@ -1864,6 +1881,17 @@ smf_apply_conf () { smf_cleanup_dlmgmtd fi + # + # When doing backwards BFU, if the target does not contain + # vtdaemon manifest, delete it and delete all the additional + # console-login service instances which were used to provide + # additional console sessions. + # + if ((! $ZCAT $cpiodir/generic.root$ZFIX | cpio -it 2>/dev/null | \ + grep vtdaemon.xml > /dev/null 2>&1) && [ $zone = global ]); then + smf_cleanup_vt + fi + print "Disabling unneeded inetd.conf entries ..." smf_inetd_disable smf_tftp_reinstall @@ -6029,6 +6057,7 @@ mondo_loop() { rm -f \ $usr/include/table.h \ $usr/include/libgenIO.h \ + $usr/include/sys/kd.h \ $usr/lib/llib-lTL \ $usr/lib/llib-lTL.ln diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 5da8fe4faa..7affe7dc8e 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -666,7 +666,9 @@ USBS49_FW_OBJS += keyspan_49fw.o USBSPRL_OBJS += usbser_pl2303.o pl2303_dsd.o -WC_OBJS += wscons.o +WC_OBJS += wscons.o vcons.o + +VCONS_CONF_OBJS += vcons_conf.o SCSI_OBJS += scsi_capabilities.o scsi_confsubr.o scsi_control.o \ scsi_data.o scsi_fm.o scsi_hba.o scsi_reset_notify.o \ @@ -857,7 +859,7 @@ DEVFS_OBJS += devfs_subr.o devfs_vfsops.o devfs_vnops.o DEV_OBJS += sdev_subr.o sdev_vfsops.o sdev_vnops.o \ sdev_ptsops.o sdev_comm.o sdev_profile.o \ - sdev_ncache.o sdev_netops.o + sdev_ncache.o sdev_netops.o sdev_vtops.o CTFS_OBJS += ctfs_all.o ctfs_cdir.o ctfs_ctl.o ctfs_event.o \ ctfs_latest.o ctfs_root.o ctfs_sym.o ctfs_tdir.o ctfs_tmpl.o diff --git a/usr/src/uts/common/fs/dev/sdev_subr.c b/usr/src/uts/common/fs/dev/sdev_subr.c index 40d2b7962e..7a926cab67 100644 --- a/usr/src/uts/common/fs/dev/sdev_subr.c +++ b/usr/src/uts/common/fs/dev/sdev_subr.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * utility routines for the /dev fs */ @@ -608,6 +606,9 @@ static struct sdev_vop_table vtab[] = { "pts", devpts_vnodeops_tbl, NULL, &devpts_vnodeops, devpts_validate, SDEV_DYNAMIC | SDEV_VTOR }, + { "vt", devvt_vnodeops_tbl, NULL, &devvt_vnodeops, devvt_validate, + SDEV_DYNAMIC | SDEV_VTOR }, + { "zcons", NULL, NULL, NULL, NULL, SDEV_NO_NCACHE }, { "net", devnet_vnodeops_tbl, NULL, &devnet_vnodeops, devnet_validate, @@ -1949,7 +1950,6 @@ sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, return (-1); } - ASSERT(physpath); rvp = devname_configure_by_path(physpath, NULL); if (rvp == NULL) { sdcmn_err3(("devname_configure_by_path: " @@ -1990,6 +1990,34 @@ sdev_call_dircallback(struct sdev_node *ddv, struct sdev_node **dvp, char *nm, return (0); } } + } else if (flags & SDEV_VLINK) { + physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + rv = callback(ddv, nm, (void *)&physpath, kcred, NULL, + NULL); + if (rv) { + kmem_free(physpath, MAXPATHLEN); + return (-1); + } + + vap = sdev_getdefault_attr(VLNK); + vap->va_size = strlen(physpath); + ASSERT(RW_READ_HELD(&ddv->sdev_contents)); + + if (!rw_tryupgrade(&ddv->sdev_contents)) { + rw_exit(&ddv->sdev_contents); + rw_enter(&ddv->sdev_contents, RW_WRITER); + } + rv = sdev_mknode(ddv, nm, &dv, vap, NULL, + (void *)physpath, cred, SDEV_READY); + rw_downgrade(&ddv->sdev_contents); + kmem_free(physpath, MAXPATHLEN); + if (rv) + return (rv); + + mutex_enter(&dv->sdev_lookup_lock); + SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP); + mutex_exit(&dv->sdev_lookup_lock); + return (0); } else if (flags & SDEV_VNODE) { /* * DBNR has its own way to create the device diff --git a/usr/src/uts/common/fs/dev/sdev_vtops.c b/usr/src/uts/common/fs/dev/sdev_vtops.c new file mode 100644 index 0000000000..11ceaadd26 --- /dev/null +++ b/usr/src/uts/common/fs/dev/sdev_vtops.c @@ -0,0 +1,442 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * vnode ops for the /dev/vt directory + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysmacros.h> +#include <sys/sunndi.h> +#include <fs/fs_subr.h> +#include <sys/fs/dv_node.h> +#include <sys/fs/sdev_impl.h> +#include <sys/policy.h> +#include <sys/stat.h> +#include <sys/vfs_opreg.h> +#include <sys/tty.h> +#include <sys/vt_impl.h> +#include <sys/note.h> + +/* warlock in this file only cares about variables shared by vt and devfs */ +_NOTE(SCHEME_PROTECTS_DATA("Do not care", sdev_node vattr vnode)) + +#define DEVVT_UID_DEFAULT SDEV_UID_DEFAULT +#define DEVVT_GID_DEFAULT (0) +#define DEVVT_DEVMODE_DEFAULT (0600) +#define DEVVT_ACTIVE_NAME "active" + +#define isdigit(ch) ((ch) >= '0' && (ch) <= '9') + +/* attributes for VT nodes */ +static vattr_t devvt_vattr = { + AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ + VCHR, /* va_type */ + S_IFCHR | DEVVT_DEVMODE_DEFAULT, /* va_mode */ + DEVVT_UID_DEFAULT, /* va_uid */ + DEVVT_GID_DEFAULT, /* va_gid */ + 0 /* 0 hereafter */ +}; + +struct vnodeops *devvt_vnodeops; + +struct vnodeops * +devvt_getvnodeops(void) +{ + return (devvt_vnodeops); +} + +static int +devvt_str2minor(const char *nm, minor_t *mp) +{ + long uminor = 0; + char *endptr = NULL; + + if (nm == NULL || !isdigit(*nm)) + return (EINVAL); + + *mp = 0; + if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || + *endptr != '\0' || uminor < 0) { + return (EINVAL); + } + + *mp = (minor_t)uminor; + return (0); +} + +/*ARGSUSED*/ +int +devvt_validate(struct sdev_node *dv) +{ + minor_t min; + char *nm = dv->sdev_name; + + ASSERT(!(dv->sdev_flags & SDEV_STALE)); + ASSERT(dv->sdev_state == SDEV_READY); + + /* validate only READY nodes */ + if (dv->sdev_state != SDEV_READY) { + sdcmn_err(("dev fs: skipping: node not ready %s(%p)", + nm, (void *)dv)); + return (SDEV_VTOR_SKIP); + } + + if (vt_wc_attached() == (major_t)-1) + return (SDEV_VTOR_INVALID); + + if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { + char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + + (void) vt_getactive(link, MAXPATHLEN); + if (strcmp(link, dv->sdev_symlink) != 0) { + kmem_free(dv->sdev_symlink, + strlen(dv->sdev_symlink) + 1); + dv->sdev_symlink = i_ddi_strdup(link, KM_SLEEP); + dv->sdev_attr->va_size = strlen(link); + } + kmem_free(link, MAXPATHLEN); + return (SDEV_VTOR_VALID); + } else if (devvt_str2minor(nm, &min) != 0) { + return (SDEV_VTOR_INVALID); + } + + if (vt_minor_valid(min) == B_FALSE) + return (SDEV_VTOR_INVALID); + + return (SDEV_VTOR_VALID); +} + +/* + * This callback is invoked from devname_lookup_func() to create + * a entry when the node is not found in the cache. + */ +/*ARGSUSED*/ +static int +devvt_create_rvp(struct sdev_node *ddv, char *nm, + void **arg, cred_t *cred, void *whatever, char *whichever) +{ + minor_t min; + major_t maj; + struct vattr *vap = (struct vattr *)arg; + + if ((maj = vt_wc_attached()) == (major_t)-1) + return (SDEV_VTOR_INVALID); + + if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { + (void) vt_getactive((char *)*arg, MAXPATHLEN); + return (0); + } + + if (devvt_str2minor(nm, &min) != 0) + return (-1); + + if (vt_minor_valid(min) == B_FALSE) + return (-1); + + *vap = devvt_vattr; + vap->va_rdev = makedevice(maj, min); + + return (0); +} + +/*ARGSUSED3*/ +static int +devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, + struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, + caller_context_t *ct, int *direntflags, pathname_t *realpnp) +{ + struct sdev_node *sdvp = VTOSDEV(dvp); + struct sdev_node *dv; + struct vnode *rvp = NULL; + int type, error; + + if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { + type = SDEV_VLINK; + } else { + type = SDEV_VATTR; + } + +/* Give warlock a more clear call graph */ +#ifndef __lock_lint + error = devname_lookup_func(sdvp, nm, vpp, cred, + devvt_create_rvp, type); +#else + devvt_create_rvp(0, 0, 0, 0, 0, 0); +#endif + + if (error == 0) { + switch ((*vpp)->v_type) { + case VCHR: + dv = VTOSDEV(VTOS(*vpp)->s_realvp); + ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); + break; + case VDIR: + case VLNK: + dv = VTOSDEV(*vpp); + break; + default: + cmn_err(CE_PANIC, "devvt_lookup: Unsupported node " + "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); + break; + } + ASSERT(SDEV_HELD(dv)); + } + + return (error); +} + +static void +devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type) +{ + int error; + struct sdev_node *sdv = NULL; + struct vattr *vap = NULL; + major_t maj; + minor_t min; + + if ((maj = vt_wc_attached()) == (major_t)-1) + return; + + if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 && + devvt_str2minor(nm, &min) != 0) + return; + + error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT); + if (error || !sdv) { + return; + } + + mutex_enter(&sdv->sdev_lookup_lock); + SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP); + mutex_exit(&sdv->sdev_lookup_lock); + + if (type & SDEV_VATTR) { + vap = &devvt_vattr; + vap->va_rdev = makedevice(maj, min); + error = sdev_mknode(ddv, nm, &sdv, vap, NULL, + NULL, cred, SDEV_READY); + } else if (type & SDEV_VLINK) { + char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + + (void) vt_getactive(link, MAXPATHLEN); + vap = &sdev_vattr_lnk; + vap->va_size = strlen(link); + error = sdev_mknode(ddv, nm, &sdv, vap, NULL, + (void *)link, cred, SDEV_READY); + + kmem_free(link, MAXPATHLEN); + } + + mutex_enter(&sdv->sdev_lookup_lock); + SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP); + mutex_exit(&sdv->sdev_lookup_lock); + +} + +static void +devvt_prunedir(struct sdev_node *ddv) +{ + struct vnode *vp; + struct sdev_node *dv, *next = NULL; + int (*vtor)(struct sdev_node *) = NULL; + + ASSERT(ddv->sdev_flags & SDEV_VTOR); + + vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); + ASSERT(vtor); + + for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { + next = SDEV_NEXT_ENTRY(ddv, dv); + + /* skip stale nodes */ + if (dv->sdev_flags & SDEV_STALE) + continue; + + /* validate and prune only ready nodes */ + if (dv->sdev_state != SDEV_READY) + continue; + + switch (vtor(dv)) { + case SDEV_VTOR_VALID: + case SDEV_VTOR_SKIP: + continue; + case SDEV_VTOR_INVALID: + sdcmn_err7(("destroy invalid " + "node: %s(%p)\n", dv->sdev_name, (void *)dv)); + break; + } + vp = SDEVTOV(dv); + if (vp->v_count > 0) + continue; + SDEV_HOLD(dv); + /* remove the cache node */ + (void) sdev_cache_update(ddv, &dv, dv->sdev_name, + SDEV_CACHE_DELETE); + } +} + +static void +devvt_cleandir(struct vnode *dvp, struct cred *cred) +{ + struct sdev_node *sdvp = VTOSDEV(dvp); + struct sdev_node *dv, *next = NULL; + int min, cnt; + int found = 0; + + mutex_enter(&vc_lock); + cnt = VC_INSTANCES_COUNT; + mutex_exit(&vc_lock); + +/* We have to fool warlock this way, otherwise it will complain */ +#ifndef __lock_lint + if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) { + rw_exit(&sdvp->sdev_contents); + rw_enter(&sdvp->sdev_contents, RW_WRITER); + } +#else + rw_enter(&sdvp->sdev_contents, RW_WRITER); +#endif + + /* 1. create missed nodes */ + for (min = 0; min < cnt; min++) { + char nm[16]; + + if (vt_minor_valid(min) == B_FALSE) + continue; + + (void) snprintf(nm, sizeof (nm), "%d", min); + found = 0; + for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { + next = SDEV_NEXT_ENTRY(sdvp, dv); + + /* skip stale nodes */ + if (dv->sdev_flags & SDEV_STALE) + continue; + /* validate and prune only ready nodes */ + if (dv->sdev_state != SDEV_READY) + continue; + if (strcmp(nm, dv->sdev_name) == 0) { + found = 1; + break; + } + } + if (!found) { + devvt_create_snode(sdvp, nm, cred, SDEV_VATTR); + } + } + + /* 2. create active link node */ + found = 0; + for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { + next = SDEV_NEXT_ENTRY(sdvp, dv); + + /* skip stale nodes */ + if (dv->sdev_flags & SDEV_STALE) + continue; + /* validate and prune only ready nodes */ + if (dv->sdev_state != SDEV_READY) + continue; + if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) { + found = 1; + break; + } + } + if (!found) + devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK); + + /* 3. cleanup invalid nodes */ + devvt_prunedir(sdvp); + +#ifndef __lock_lint + rw_downgrade(&sdvp->sdev_contents); +#else + rw_exit(&sdvp->sdev_contents); +#endif +} + +/*ARGSUSED4*/ +static int +devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, + int *eofp, caller_context_t *ct, int flags) +{ + if (uiop->uio_offset == 0) { + devvt_cleandir(dvp, cred); + } + + return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); +} + +/* + * We allow create to find existing nodes + * - if the node doesn't exist - EROFS + * - creating an existing dir read-only succeeds, otherwise EISDIR + * - exclusive creates fail - EEXIST + */ +/*ARGSUSED2*/ +static int +devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, + int mode, struct vnode **vpp, struct cred *cred, int flag, + caller_context_t *ct, vsecattr_t *vsecp) +{ + int error; + struct vnode *vp; + + *vpp = NULL; + + if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, + NULL)) != 0) { + if (error == ENOENT) + error = EROFS; + return (error); + } + + if (excl == EXCL) + error = EEXIST; + else if (vp->v_type == VDIR && (mode & VWRITE)) + error = EISDIR; + else + error = VOP_ACCESS(vp, mode, 0, cred, ct); + + if (error) { + VN_RELE(vp); + } else + *vpp = vp; + + return (error); +} + +const fs_operation_def_t devvt_vnodeops_tbl[] = { + VOPNAME_READDIR, { .vop_readdir = devvt_readdir }, + VOPNAME_LOOKUP, { .vop_lookup = devvt_lookup }, + VOPNAME_CREATE, { .vop_create = devvt_create }, + VOPNAME_REMOVE, { .error = fs_nosys }, + VOPNAME_MKDIR, { .error = fs_nosys }, + VOPNAME_RMDIR, { .error = fs_nosys }, + VOPNAME_SYMLINK, { .error = fs_nosys }, + VOPNAME_SETSECATTR, { .error = fs_nosys }, + NULL, NULL +}; diff --git a/usr/src/uts/common/fs/namefs/namevfs.c b/usr/src/uts/common/fs/namefs/namevfs.c index fa245b1ece..a6670d9cd6 100644 --- a/usr/src/uts/common/fs/namefs/namevfs.c +++ b/usr/src/uts/common/fs/namefs/namevfs.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,8 +27,6 @@ /* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" /* from S5R4 1.28 */ - /* * This file supports the vfs operations for the NAMEFS file system. */ @@ -356,13 +354,14 @@ nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp) /* * Cannot allow users to fattach() in /dev/pts. * First, there is no need for doing so and secondly - * we cannot allow arbitrary users to park on a - * /dev/pts node. + * we cannot allow arbitrary users to park on a node in + * /dev/pts or /dev/vt. */ rvp = NULLVP; if (vn_matchops(mvp, spec_getvnodeops()) && VOP_REALVP(mvp, &rvp, NULL) == 0 && rvp && - vn_matchops(rvp, devpts_getvnodeops())) { + (vn_matchops(rvp, devpts_getvnodeops()) || + vn_matchops(rvp, devvt_getvnodeops()))) { releasef(namefdp.fd); return (ENOTSUP); } diff --git a/usr/src/uts/common/io/cons.c b/usr/src/uts/common/io/cons.c index 9174f48db9..a44c5c7a9f 100644 --- a/usr/src/uts/common/io/cons.c +++ b/usr/src/uts/common/io/cons.c @@ -24,7 +24,6 @@ * Use is subject to license terms. */ - /* * Indirect console driver for Sun. * @@ -221,6 +220,7 @@ cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } + cn_dip = devi; return (DDI_SUCCESS); } @@ -357,7 +357,6 @@ cnclose(dev_t dev, int flag, int state, struct cred *cred) while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) { err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); if (!err) { - vp->v_stream = NULL; rconsopen--; } } diff --git a/usr/src/uts/common/io/consconfig_dacf.c b/usr/src/uts/common/io/consconfig_dacf.c index fbc71cb239..4226766769 100644 --- a/usr/src/uts/common/io/consconfig_dacf.c +++ b/usr/src/uts/common/io/consconfig_dacf.c @@ -496,6 +496,9 @@ consconfig_tem_supported(cons_state_t *sp) uint_t nint; int rv = 0; + if (sp->cons_fb_path == NULL) + return (0); + if ((dev = ddi_pathname_to_dev_t(sp->cons_fb_path)) == NODEV) return (0); /* warning printed later by common code */ @@ -722,6 +725,9 @@ consconfig_state_init(void) #endif /* _HAVE_TEM_FIRMWARE */ } else { sp->cons_fb_path = plat_fbpath(); +#ifdef _HAVE_TEM_FIRMWARE + sp->cons_tem_supported = consconfig_tem_supported(sp); +#endif /* _HAVE_TEM_FIRMWARE */ } sp->cons_li = ldi_ident_from_anon(); @@ -2109,3 +2115,17 @@ flush_usb_serial_buf(void) kmem_free(usbser_kern_buf, MMU_PAGESIZE); } + +boolean_t +consconfig_console_is_tipline(void) +{ + cons_state_t *sp; + + if ((sp = (cons_state_t *)space_fetch("consconfig")) == NULL) + return (B_FALSE); + + if (sp->cons_input_type == CONSOLE_TIP) + return (B_TRUE); + + return (B_FALSE); +} diff --git a/usr/src/uts/common/io/kbtrans/kbtrans_streams.c b/usr/src/uts/common/io/kbtrans/kbtrans_streams.c index f5b6a4a1d9..fa99e5caac 100644 --- a/usr/src/uts/common/io/kbtrans/kbtrans_streams.c +++ b/usr/src/uts/common/io/kbtrans/kbtrans_streams.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Generic keyboard support: streams and administration. */ @@ -56,6 +54,8 @@ int kbtrans_errmask; int kbtrans_errlevel; #endif +#define KB_NR_FUNCKEYS 12 + /* * Repeat rates set in static variables so they can be tweeked with * debugger. @@ -78,7 +78,7 @@ extern struct mod_ops mod_miscops; static struct modlmisc modlmisc = { &mod_miscops, /* Type of module */ - "kbtrans (key translation) 1.32" + "kbtrans (key translation)" }; static struct modlinkage modlinkage = { @@ -242,7 +242,7 @@ kbtrans_streams_init( case CLONEOPEN: DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (NULL, - "kbtrans_streams_init: Clone open not supported")); + "kbtrans_streams_init: Clone open not supported")); return (EINVAL); } @@ -308,15 +308,15 @@ kbtrans_streams_init( /* Allocate dynamic memory for downs table */ upper->kbtrans_streams_num_downs_entries = kbtrans_downs_size; upper->kbtrans_streams_downs_bytes = - (uint32_t)(kbtrans_downs_size * sizeof (Key_event)); + (uint32_t)(kbtrans_downs_size * sizeof (Key_event)); upper->kbtrans_streams_downs = - kmem_zalloc(upper->kbtrans_streams_downs_bytes, KM_SLEEP); + kmem_zalloc(upper->kbtrans_streams_downs_bytes, KM_SLEEP); upper->kbtrans_streams_abortable = B_FALSE; upper->kbtrans_streams_flags = KBTRANS_STREAMS_OPEN; DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (upper, "kbtrans_streams_init " - "exiting")); + "exiting")); return (0); } @@ -338,18 +338,18 @@ kbtrans_streams_fini(struct kbtrans *upper) /* clear all timeouts */ if (upper->kbtrans_streams_bufcallid) { qunbufcall(upper->kbtrans_streams_readq, - upper->kbtrans_streams_bufcallid); + upper->kbtrans_streams_bufcallid); } if (upper->kbtrans_streams_rptid) { (void) quntimeout(upper->kbtrans_streams_readq, - upper->kbtrans_streams_rptid); + upper->kbtrans_streams_rptid); } kmem_free(upper->kbtrans_streams_downs, - upper->kbtrans_streams_downs_bytes); + upper->kbtrans_streams_downs_bytes); kmem_free(upper, sizeof (struct kbtrans)); DPRINTF(PRINT_L1, PRINT_MASK_CLOSE, (upper, "kbtrans_streams_fini " - "exiting")); + "exiting")); return (0); } @@ -396,7 +396,7 @@ kbtrans_streams_message(struct kbtrans *upper, register mblk_t *mp) enum kbtrans_message_response ret; DPRINTF(PRINT_L1, PRINT_MASK_ALL, (upper, - "kbtrans_streams_message entering")); + "kbtrans_streams_message entering")); /* * Process M_FLUSH, and some M_IOCTL, messages here; pass * everything else down. @@ -425,7 +425,7 @@ kbtrans_streams_message(struct kbtrans *upper, register mblk_t *mp) } DPRINTF(PRINT_L1, PRINT_MASK_ALL, (upper, - "kbtrans_streams_message exiting\n")); + "kbtrans_streams_message exiting\n")); return (ret); } @@ -459,7 +459,7 @@ kbtrans_streams_key( if (key == (kbtrans_key_t)kp->k_abort1 || key == (kbtrans_key_t)kp->k_abort1a) { upper->kbtrans_streams_abort_state = - ABORT_ABORT1_RECEIVED; + ABORT_ABORT1_RECEIVED; upper->kbtrans_streams_abort1_key = key; return; } @@ -467,7 +467,7 @@ kbtrans_streams_key( if (key == (kbtrans_key_t)kp->k_newabort1 || key == (kbtrans_key_t)kp->k_newabort1a) { upper->kbtrans_streams_abort_state = - NEW_ABORT_ABORT1_RECEIVED; + NEW_ABORT_ABORT1_RECEIVED; upper->kbtrans_streams_new_abort1_key = key; } break; @@ -479,9 +479,9 @@ kbtrans_streams_key( return; } else { kbtrans_processkey(lower, - upper->kbtrans_streams_callback, - upper->kbtrans_streams_abort1_key, - KEY_PRESSED); + upper->kbtrans_streams_callback, + upper->kbtrans_streams_abort1_key, + KEY_PRESSED); } break; case NEW_ABORT_ABORT1_RECEIVED: @@ -490,9 +490,9 @@ kbtrans_streams_key( key == (kbtrans_key_t)kp->k_newabort2) { abort_sequence_enter((char *)NULL); kbtrans_processkey(lower, - upper->kbtrans_streams_callback, - upper->kbtrans_streams_new_abort1_key, - KEY_RELEASED); + upper->kbtrans_streams_callback, + upper->kbtrans_streams_new_abort1_key, + KEY_RELEASED); return; } } @@ -605,12 +605,12 @@ kbtrans_streams_untimeout(struct kbtrans *upper) /* clear all timeouts */ if (upper->kbtrans_streams_bufcallid) { qunbufcall(upper->kbtrans_streams_readq, - upper->kbtrans_streams_bufcallid); + upper->kbtrans_streams_bufcallid); upper->kbtrans_streams_bufcallid = 0; } if (upper->kbtrans_streams_rptid) { (void) quntimeout(upper->kbtrans_streams_readq, - upper->kbtrans_streams_rptid); + upper->kbtrans_streams_rptid); upper->kbtrans_streams_rptid = 0; } } @@ -659,7 +659,7 @@ kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp) iocp = (struct iocblk *)mp->b_rptr; DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, - "kbtrans_ioctl: ioc_cmd 0x%x - ", iocp->ioc_cmd)); + "kbtrans_ioctl: ioc_cmd 0x%x - ", iocp->ioc_cmd)); switch (iocp->ioc_cmd) { case VUIDSFORMAT: @@ -729,7 +729,7 @@ kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp) *(int *)datap->b_wptr = (upper->kbtrans_streams_translate_mode == TR_EVENT || upper->kbtrans_streams_translate_mode == TR_UNTRANS_EVENT) ? - VUID_FIRM_EVENT: VUID_NATIVE; + VUID_FIRM_EVENT: VUID_NATIVE; datap->b_wptr += sizeof (int); if (mp->b_cont) /* free msg to prevent memory leak */ freemsg(mp->b_cont); @@ -793,17 +793,17 @@ kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp) case ASCII_FIRST: addr_probe->data.current = - upper->kbtrans_streams_vuid_addr.ascii; + upper->kbtrans_streams_vuid_addr.ascii; break; case TOP_FIRST: addr_probe->data.current = - upper->kbtrans_streams_vuid_addr.top; + upper->kbtrans_streams_vuid_addr.top; break; case VKEY_FIRST: addr_probe->data.current = - upper->kbtrans_streams_vuid_addr.vkey; + upper->kbtrans_streams_vuid_addr.vkey; break; default: @@ -870,7 +870,7 @@ kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp) case KIOCSETKEY: DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSETKEY %d\n", - kiocsetkey++)); + kiocsetkey++)); err = miocpullup(mp, sizeof (struct kiockey)); if (err != 0) break; @@ -885,7 +885,7 @@ kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp) case KIOCGETKEY: DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGETKEY %d\n", - kiocgetkey++)); + kiocgetkey++)); err = miocpullup(mp, sizeof (struct kiockey)); if (err != 0) break; @@ -1073,11 +1073,11 @@ allocfailure: upper->kbtrans_streams_iocpending = mp; if (upper->kbtrans_streams_bufcallid) { qunbufcall(upper->kbtrans_streams_readq, - upper->kbtrans_streams_bufcallid); + upper->kbtrans_streams_bufcallid); } upper->kbtrans_streams_bufcallid = - qbufcall(upper->kbtrans_streams_readq, ioctlrespsize, BPRI_HI, - kbtrans_reioctl, upper); + qbufcall(upper->kbtrans_streams_readq, ioctlrespsize, BPRI_HI, + kbtrans_reioctl, upper); /* * This is a white lie... we *will* handle it, eventually. */ @@ -1111,8 +1111,8 @@ static void kbtrans_setled(struct kbtrans *upper) { upper->kbtrans_streams_hw_callbacks->kbtrans_streams_setled( - upper->kbtrans_streams_hw, - upper->kbtrans_lower.kbtrans_led_state); + upper->kbtrans_streams_hw, + upper->kbtrans_lower.kbtrans_led_state); } /* @@ -1127,8 +1127,8 @@ kbtrans_rpt(void *arg) struct kbtrans_lower *lower = &upper->kbtrans_lower; DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, - "kbtrans_rpt: repeat key %X\n", - lower->kbtrans_repeatkey)); + "kbtrans_rpt: repeat key %X\n", + lower->kbtrans_repeatkey)); upper->kbtrans_streams_rptid = 0; @@ -1140,13 +1140,13 @@ kbtrans_rpt(void *arg) kbtrans_keyreleased(upper, lower->kbtrans_repeatkey); kbtrans_processkey(lower, - upper->kbtrans_streams_callback, - lower->kbtrans_repeatkey, - KEY_PRESSED); + upper->kbtrans_streams_callback, + lower->kbtrans_repeatkey, + KEY_PRESSED); upper->kbtrans_streams_rptid = - qtimeout(upper->kbtrans_streams_readq, kbtrans_rpt, - (caddr_t)upper, kbtrans_repeat_rate); + qtimeout(upper->kbtrans_streams_readq, kbtrans_rpt, + (caddr_t)upper, kbtrans_repeat_rate); } } @@ -1161,7 +1161,7 @@ kbtrans_cancelrpt(struct kbtrans *upper) if (upper->kbtrans_streams_rptid != 0) { (void) quntimeout(upper->kbtrans_streams_readq, - upper->kbtrans_streams_rptid); + upper->kbtrans_streams_rptid); upper->kbtrans_streams_rptid = 0; } } @@ -1236,7 +1236,7 @@ kbtrans_keypressed(struct kbtrans *upper, uchar_t key_station, unsigned int mask; mask = lower->kbtrans_shiftmask & - ~(CTRLMASK | CTLSMASK | UPMASK); + ~(CTRLMASK | CTLSMASK | UPMASK); ke = kbtrans_find_entry(lower, mask, key_station); if (ke == NULL) @@ -1287,7 +1287,7 @@ kbtrans_queuepress(struct kbtrans *upper, register int i; DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, "kbtrans_queuepress:" - " key=%d", key_station)); + " key=%d", key_station)); ke_free = 0; @@ -1300,9 +1300,9 @@ kbtrans_queuepress(struct kbtrans *upper, if (ke->key_station == key_station) { DPRINTF(PRINT_L0, PRINT_MASK_ALL, - (NULL, "kbtrans: Double " - "entry in downs table (%d,%d)!\n", - key_station, i)); + (NULL, "kbtrans: Double " + "entry in downs table (%d,%d)!\n", + key_station, i)); goto add_event; } @@ -1335,7 +1335,7 @@ kbtrans_keyreleased(register struct kbtrans *upper, uchar_t key_station) register int i; DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, "RELEASE key=%d\n", - key_station)); + key_station)); if (upper->kbtrans_streams_translate_mode != TR_EVENT && upper->kbtrans_streams_translate_mode != TR_UNTRANS_EVENT) { @@ -1446,7 +1446,7 @@ kbtrans_queueevent(struct kbtrans *upper, Firm_event *fe) if (!canputnext(q)) { if (kbtrans_overflow_msg) { DPRINTF(PRINT_L2, PRINT_MASK_ALL, (NULL, - "kbtrans: Buffer flushed when overflowed.")); + "kbtrans: Buffer flushed when overflowed.")); } kbtrans_flush(upper); @@ -1457,7 +1457,7 @@ kbtrans_queueevent(struct kbtrans *upper, Firm_event *fe) block for event."); } else { uniqtime32(&fe->time); - *(Firm_event *)bp->b_wptr = *fe; + *(Firm_event *)bp->b_wptr = *fe; bp->b_wptr += sizeof (Firm_event); (void) putnext(q, bp); @@ -1478,6 +1478,14 @@ kbtrans_set_translation_callback(register struct kbtrans *upper) default: case TR_ASCII: + upper->vt_switch_keystate = VT_SWITCH_KEY_NONE; + + /* Discard any obsolete CTRL/ALT/SHIFT keys */ + upper->kbtrans_lower.kbtrans_shiftmask &= + ~(CTRLMASK | ALTMASK | SHIFTMASK); + upper->kbtrans_lower.kbtrans_togglemask &= + ~(CTRLMASK | ALTMASK | SHIFTMASK); + upper->kbtrans_streams_callback = &ascii_callback; break; @@ -1535,6 +1543,75 @@ kbtrans_untrans_keyreleased_raw(struct kbtrans *upper, kbtrans_key_t key) } /* + * kbtrans_vt_compose: + * To compose the key sequences for virtual terminal switching. + * + * 'ALTL + F#' for 1-12 terminals + * 'ALTGR + F#' for 13-24 terminals + * 'ALT + UPARROW' for last terminal + * 'ALT + LEFTARROW' for previous terminal + * 'ALT + RIGHTARROW' for next terminal + * + * the vt switching message is encoded as: + * + * ------------------------------------------------------------- + * | \033 | 'Q' | vtno + 'A' | opcode | 'z' | '\0' | + * ------------------------------------------------------------- + * + * opcode: + * 'B' to switch to previous terminal + * 'F' to switch to next terminal + * 'L' to switch to last terminal + * 'H' to switch to the terminal as specified by vtno, + * which is from 1 to 24. + * + * Here keyid is the keycode of UPARROW, LEFTARROW, or RIGHTARROW + * when it is a kind of arrow key as indicated by is_arrow_key, + * otherwise it indicates a function key and keyid is the number + * corresponding to that function key. + */ +static void +kbtrans_vt_compose(struct kbtrans *upper, unsigned short keyid, + boolean_t is_arrow_key, char *buf) +{ + char *bufp; + + bufp = buf; + *bufp++ = '\033'; /* Escape */ + *bufp++ = 'Q'; + if (is_arrow_key) { + *bufp++ = 'A'; + switch (keyid) { + case UPARROW: /* last vt */ + *bufp++ = 'L'; + break; + case LEFTARROW: /* previous vt */ + *bufp++ = 'B'; + break; + case RIGHTARROW: /* next vt */ + *bufp++ = 'F'; + break; + default: + break; + } + } else { + /* this is funckey specifying vtno for switch */ + *bufp++ = keyid + + (upper->vt_switch_keystate - VT_SWITCH_KEY_ALT) * + KB_NR_FUNCKEYS + 'A'; + *bufp++ = 'H'; + } + *bufp++ = 'z'; + *bufp = '\0'; + + /* + * Send the result upstream. + */ + kbtrans_putbuf(buf, upper->kbtrans_streams_readq); + +} + +/* * kbtrans_ascii_keypressed: * This is the code if we are in TR_ASCII mode and a key * is pressed. This is where we will do any special processing that @@ -1551,16 +1628,29 @@ kbtrans_ascii_keypressed( register char *cp; register char *bufp; char buf[14]; + unsigned short keyid; struct kbtrans_lower *lower = &upper->kbtrans_lower; /* * Based on the type of key, we may need to do some ASCII - * specific post processing. + * specific post processing. Note that the translated entry + * is constructed as the actual keycode plus entrytype. see + * sys/kbd.h for details of each entrytype. */ switch (entrytype) { case BUCKYBITS: + return; + case SHIFTKEYS: + keyid = entry & 0xFF; + if (keyid == ALT) { + upper->vt_switch_keystate = VT_SWITCH_KEY_ALT; + } else if (keyid == ALTGRAPH) { + upper->vt_switch_keystate = VT_SWITCH_KEY_ALTGR; + } + return; + case FUNNY: /* * There is no ascii equivalent. We will ignore these @@ -1569,14 +1659,29 @@ kbtrans_ascii_keypressed( return; case FUNCKEYS: + if (upper->vt_switch_keystate > VT_SWITCH_KEY_NONE) { + if (entry >= TOPFUNC && + entry < (TOPFUNC + KB_NR_FUNCKEYS)) { + + /* + * keyid is the number correspoding to F# + * and its value is from 1 to 12. + */ + keyid = (entry & 0xF) + 1; + + kbtrans_vt_compose(upper, keyid, B_FALSE, buf); + return; + } + } + /* * We need to expand this key to get the ascii * equivalent. These are the function keys (F1, F2 ...) */ bufp = buf; cp = kbtrans_strsetwithdecimal(bufp + 2, - (uint_t)((entry & 0x003F) + 192), - sizeof (buf) - 5); + (uint_t)((entry & 0x003F) + 192), + sizeof (buf) - 5); *bufp++ = '\033'; /* Escape */ *bufp++ = '['; while (*cp != '\0') @@ -1592,6 +1697,17 @@ kbtrans_ascii_keypressed( return; case STRING: + if (upper->vt_switch_keystate > VT_SWITCH_KEY_NONE) { + keyid = entry & 0xFF; + if (keyid == UPARROW || + keyid == RIGHTARROW || + keyid == LEFTARROW) { + + kbtrans_vt_compose(upper, keyid, B_TRUE, buf); + return; + } + } + /* * These are the multi byte keys (Home, Up, Down ...) */ @@ -1616,7 +1732,7 @@ kbtrans_ascii_keypressed( * answer in the kb_numlock_table and send it upstream. */ kbtrans_putcode(upper, - lower->kbtrans_numlock_table[entry&0x1F]); + lower->kbtrans_numlock_table[entry&0x1F]); return; @@ -1632,17 +1748,24 @@ kbtrans_ascii_keypressed( } +#define KB_SCANCODE_ALT 0xe2 +#define KB_SCANCODE_ALTGRAPH 0xe6 + /* * kbtrans_ascii_keyreleased: * This is the function if we are in TR_ASCII mode and a key * is released. ASCII doesn't have the concept of released keys, - * or make/break codes. So there is nothing for us to do. + * or make/break codes. So there is nothing for us to do except + * checking 'Alt/AltGraph' release key in order to reset the state + * of vt switch key sequence. */ /* ARGSUSED */ static void kbtrans_ascii_keyreleased(struct kbtrans *upper, kbtrans_key_t key) { - /* Nothing to do ... for now */ + if (key == KB_SCANCODE_ALT || key == KB_SCANCODE_ALTGRAPH) { + upper->vt_switch_keystate = VT_SWITCH_KEY_NONE; + } } /* @@ -1675,7 +1798,7 @@ kbtrans_ascii_setup_repeat( * be called to repeat the key. */ upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq, - kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay); + kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay); } /* @@ -1890,7 +2013,7 @@ kbtrans_trans_event_setup_repeat( * be called to repeat the key. */ upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq, - kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay); + kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay); } /* @@ -1990,7 +2113,7 @@ kbtrans_setkey(struct kbtrans_lower *lower, struct kiockey *key, cred_t *cr) key->kio_entry <= (uchar_t)(OLD_STRING + 15)) { strtabindex = key->kio_entry - OLD_STRING; bcopy(key->kio_string, - lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN); + lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN); lower->kbtrans_keystringtab[strtabindex][KTAB_STRLEN-1] = '\0'; } @@ -2064,7 +2187,7 @@ kbtrans_getkey(struct kbtrans_lower *lower, struct kiockey *key) } ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask, - key->kio_station); + key->kio_station); if (ke == NULL) return (EINVAL); @@ -2084,7 +2207,7 @@ kbtrans_getkey(struct kbtrans_lower *lower, struct kiockey *key) if (entry >= STRING && entry <= (uchar_t)(STRING + 15)) { strtabindex = entry - STRING; bcopy(lower->kbtrans_keystringtab[strtabindex], - key->kio_string, KTAB_STRLEN); + key->kio_string, KTAB_STRLEN); } return (0); } @@ -2134,7 +2257,7 @@ kbtrans_skey(struct kbtrans_lower *lower, struct kiockeymap *key, cred_t *cr) } ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask, - key->kio_station); + key->kio_station); if (ke == NULL) return (EINVAL); @@ -2142,7 +2265,7 @@ kbtrans_skey(struct kbtrans_lower *lower, struct kiockeymap *key, cred_t *cr) key->kio_entry <= (ushort_t)(STRING + 15)) { strtabindex = key->kio_entry-STRING; bcopy(key->kio_string, - lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN); + lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN); lower->kbtrans_keystringtab[strtabindex][KTAB_STRLEN-1] = '\0'; } @@ -2184,7 +2307,7 @@ kbtrans_gkey(struct kbtrans_lower *lower, struct kiockeymap *key) } ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask, - key->kio_station); + key->kio_station); if (ke == NULL) return (EINVAL); @@ -2194,7 +2317,7 @@ kbtrans_gkey(struct kbtrans_lower *lower, struct kiockeymap *key) key->kio_entry <= (ushort_t)(STRING + 15)) { strtabindex = key->kio_entry-STRING; bcopy(lower->kbtrans_keystringtab[strtabindex], - key->kio_string, KTAB_STRLEN); + key->kio_string, KTAB_STRLEN); } return (0); } diff --git a/usr/src/uts/common/io/kbtrans/kbtrans_streams.h b/usr/src/uts/common/io/kbtrans/kbtrans_streams.h index 68f23580d5..d6870e0864 100644 --- a/usr/src/uts/common/io/kbtrans/kbtrans_streams.h +++ b/usr/src/uts/common/io/kbtrans/kbtrans_streams.h @@ -18,16 +18,15 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KBTRANS_STREAMS_H #define _KBTRANS_STREAMS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -132,6 +131,13 @@ struct kbtrans { /* Buffers to hold characters during the polled mode */ char *kbtrans_polled_pending_chars; char kbtrans_polled_buf[KBTRANS_POLLED_BUF_SIZE+1]; + + /* vt switch key sequence state */ + enum { + VT_SWITCH_KEY_NONE = 0, + VT_SWITCH_KEY_ALT, /* left Alt key is pressed */ + VT_SWITCH_KEY_ALTGR /* right Alt key is pressed */ + } vt_switch_keystate; }; #ifdef __cplusplus diff --git a/usr/src/uts/common/io/tem.c b/usr/src/uts/common/io/tem.c index 3cc0df18c3..33e42fd982 100644 --- a/usr/src/uts/common/io/tem.c +++ b/usr/src/uts/common/io/tem.c @@ -27,8 +27,39 @@ /* * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and * the like. + * + * How Virtual Terminal Emulator Works: + * + * Every virtual terminal is associated with a tem_vt_state structure + * and maintains a virtual screen buffer in tvs_screen_buf, which contains + * all the characters which should be shown on the physical screen when + * the terminal is activated. There are also two other buffers, tvs_fg_buf + * and tvs_bg_buf, which track the foreground and background colors of the + * on screen characters + * + * Data written to a virtual terminal is composed of characters which + * should be displayed on the screen when this virtual terminal is + * activated, fg/bg colors of these characters, and other control + * information (escape sequence, etc). + * + * When data is passed to a virtual terminal it first is parsed for + * control information by tem_safe_parse(). Subsequently the character + * and color data are written to tvs_screen_buf, tvs_fg_buf, and + * tvs_bg_buf. They are saved in these buffers in order to refresh + * the screen when this terminal is activated. If the terminal is + * currently active, the data (characters and colors) are also written + * to the physical screen by invoking a callback function, + * tem_safe_text_callbacks() or tem_safe_pix_callbacks(). + * + * When rendering data to the framebuffer, if the framebuffer is in + * VIS_PIXEL mode, the character data will first be converted to pixel + * data using tem_safe_pix_bit2pix(), and then the pixels get displayed + * on the physical screen. We only store the character and color data in + * tem_vt_state since the bit2pix conversion only happens when actually + * rendering to the physical framebuffer. */ + #include <sys/types.h> #include <sys/file.h> #include <sys/conf.h> @@ -54,26 +85,33 @@ #include <sys/sunddi.h> #include <sys/sunldi.h> #include <sys/tem_impl.h> -#include <sys/tem.h> #ifdef _HAVE_TEM_FIRMWARE #include <sys/promif.h> #endif /* _HAVE_TEM_FIRMWARE */ #include <sys/consplat.h> +#include <sys/kd.h> +#include <sys/sysmacros.h> +#include <sys/note.h> +#include <sys/t_lock.h> + +/* Terminal emulator internal helper functions */ +static void tems_setup_terminal(struct vis_devinit *, size_t, size_t); +static void tems_modechange_callback(struct vis_modechg_arg *, + struct vis_devinit *); -/* Terminal emulator functions */ -static int tem_setup_terminal(struct vis_devinit *, tem_t *, - size_t, size_t); -static void tem_modechange_callback(tem_t *, struct vis_devinit *); -static void tem_free(tem_t *); -static void tem_get_inverses(boolean_t *, boolean_t *); -static void tem_get_initial_color(tem_t *); -static int tem_adjust_row(tem_t *, int, cred_t *); +static void tems_reset_colormap(cred_t *, enum called_from); + +static void tem_free_buf(struct tem_vt_state *); +static void tem_internal_init(struct tem_vt_state *, cred_t *, boolean_t, + boolean_t); +static void tems_get_initial_color(tem_color_t *pcolor); /* * Globals */ -ldi_ident_t term_li = NULL; - +static ldi_ident_t term_li = NULL; +tem_state_t tems; /* common term info */ +_NOTE(MUTEX_PROTECTS_DATA(tems.ts_lock, tems)) extern struct mod_ops mod_miscops; @@ -98,6 +136,12 @@ _init(void) (void) mod_remove(&modlinkage); return (ret); } + + mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL); + list_create(&tems.ts_list, sizeof (struct tem_vt_state), + offsetof(struct tem_vt_state, tvs_list_node)); + tems.ts_active = NULL; + return (0); } @@ -120,114 +164,238 @@ _info(struct modinfo *modinfop) return (mod_info(&modlinkage, modinfop)); } -int -tem_fini(tem_t *tem) +static void +tem_add(struct tem_vt_state *tem) { - int lyr_rval; + ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); - mutex_enter(&tem->lock); + list_insert_head(&tems.ts_list, tem); +} - ASSERT(tem->hdl != NULL); +static void +tem_rm(struct tem_vt_state *tem) +{ + ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); - /* - * Allow layered on driver to clean up console private - * data. - */ - (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, - 0, FKIOCTL, kcred, &lyr_rval); + list_remove(&tems.ts_list, tem); +} - /* - * Close layered on driver - */ - (void) ldi_close(tem->hdl, NULL, kcred); - tem->hdl = NULL; +/* + * This is the main entry point to the module. It handles output requests + * during normal system operation, when (e.g.) mutexes are available. + */ +void +tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp) +{ + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; - mutex_exit(&tem->lock); + mutex_enter(&tems.ts_lock); + mutex_enter(&tem->tvs_lock); - tem_free(tem); + if (!tem->tvs_initialized) { + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); + return; + } - return (0); + tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL); + tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); + + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); } -static int -tem_init_failed(tem_t *tem, cred_t *credp, boolean_t finish_ioctl) +static void +tem_internal_init(struct tem_vt_state *ptem, cred_t *credp, + boolean_t init_color, boolean_t clear_screen) { - int lyr_rval; + int i, j; + int width, height; + int total; + text_color_t fg; + text_color_t bg; + size_t tc_size = sizeof (text_color_t); + + ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock)); + + if (tems.ts_display_mode == VIS_PIXEL) { + ptem->tvs_pix_data_size = tems.ts_pix_data_size; + ptem->tvs_pix_data = + kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP); + } - if (finish_ioctl) - (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, - credp, &lyr_rval); + ptem->tvs_outbuf_size = tems.ts_c_dimension.width; + ptem->tvs_outbuf = + (unsigned char *)kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP); - (void) ldi_close(tem->hdl, NULL, credp); - tem_free(tem); - return (ENXIO); + width = tems.ts_c_dimension.width; + height = tems.ts_c_dimension.height; + ptem->tvs_screen_buf_size = width * height; + ptem->tvs_screen_buf = + (unsigned char *)kmem_alloc(width * height, KM_SLEEP); + + total = width * height * tc_size; + ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP); + ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP); + ptem->tvs_color_buf_size = total; + + tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL, + clear_screen, init_color); + + tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE); + for (i = 0; i < height; i++) + for (j = 0; j < width; j++) { + ptem->tvs_screen_buf[i * width + j] = ' '; + ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg; + ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg; + + } + + ptem->tvs_initialized = 1; } -static void -tem_free_state(struct tem_state *tems) +int +tem_initialized(tem_vt_state_t tem_arg) { - ASSERT(tems != NULL); + struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg; + int ret; + + mutex_enter(&ptem->tvs_lock); + ret = ptem->tvs_initialized; + mutex_exit(&ptem->tvs_lock); - if (tems->a_outbuf != NULL) - kmem_free(tems->a_outbuf, - tems->a_c_dimension.width); - if (tems->a_blank_line != NULL) - kmem_free(tems->a_blank_line, - tems->a_c_dimension.width); - if (tems->a_pix_data != NULL) - kmem_free(tems->a_pix_data, - tems->a_pix_data_size); - kmem_free(tems, sizeof (struct tem_state)); + return (ret); } -static void -tem_free(tem_t *tem) +tem_vt_state_t +tem_init(cred_t *credp) { - ASSERT(tem != NULL); + struct tem_vt_state *ptem; + + ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP); + mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL); + + mutex_enter(&tems.ts_lock); + mutex_enter(&ptem->tvs_lock); + + ptem->tvs_isactive = B_FALSE; + ptem->tvs_fbmode = KD_TEXT; + + /* + * A tem is regarded as initialized only after tem_internal_init(), + * will be set at the end of tem_internal_init(). + */ + ptem->tvs_initialized = 0; + - if (tem->state != NULL) - tem_free_state(tem->state); + if (!tems.ts_initialized) { + /* + * Only happens during early console configuration. + */ + tem_add(ptem); + mutex_exit(&ptem->tvs_lock); + mutex_exit(&tems.ts_lock); + return ((tem_vt_state_t)ptem); + } - kmem_free(tem, sizeof (struct tem)); + tem_internal_init(ptem, credp, B_TRUE, B_FALSE); + tem_add(ptem); + mutex_exit(&ptem->tvs_lock); + mutex_exit(&tems.ts_lock); + + return ((tem_vt_state_t)ptem); } /* - * This is the main entry point to the module. It handles output requests - * during normal system operation, when (e.g.) mutexes are available. + * re-init the tem after video mode has changed and tems_info has + * been re-inited. The lock is already held. */ +static void +tem_reinit(struct tem_vt_state *tem, boolean_t reset_display) +{ + ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); + + tem_free_buf(tem); /* only free virtual buffers */ + + /* reserve color */ + tem_internal_init(tem, kcred, B_FALSE, reset_display); +} + +static void +tem_free_buf(struct tem_vt_state *tem) +{ + ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock)); + + if (tem->tvs_outbuf != NULL) + kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size); + if (tem->tvs_pix_data != NULL) + kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size); + if (tem->tvs_screen_buf != NULL) + kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size); + if (tem->tvs_fg_buf != NULL) + kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size); + if (tem->tvs_bg_buf != NULL) + kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size); +} + void -tem_write(tem_t *tem, uchar_t *buf, ssize_t len, cred_t *credp) +tem_destroy(tem_vt_state_t tem_arg, cred_t *credp) { - mutex_enter(&tem->lock); + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; - ASSERT(tem->hdl != NULL); + mutex_enter(&tems.ts_lock); + mutex_enter(&tem->tvs_lock); - tem_check_first_time(tem, credp, CALLED_FROM_NORMAL); - tem_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); + if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT) + tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL); - mutex_exit(&tem->lock); + tem_free_buf(tem); + tem_rm(tem); + + if (tems.ts_active == tem) + tems.ts_active = NULL; + + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); + + kmem_free(tem, sizeof (struct tem_vt_state)); +} + +static int +tems_failed(cred_t *credp, boolean_t finish_ioctl) +{ + int lyr_rval; + + ASSERT(MUTEX_HELD(&tems.ts_lock)); + + if (finish_ioctl) + (void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0, + FWRITE|FKIOCTL, credp, &lyr_rval); + + (void) ldi_close(tems.ts_hdl, NULL, credp); + tems.ts_hdl = NULL; + return (ENXIO); } +/* + * only called once during boot + */ int -tem_init(tem_t **ptem, char *pathname, cred_t *credp) +tem_info_init(char *pathname, cred_t *credp) { - struct vis_devinit devinit; - tem_t *tem; + int lyr_rval, ret; + struct vis_devinit temargs; + char *pathbuf; size_t height = 0; size_t width = 0; - uint32_t row = 0; - uint32_t col = 0; - char *pathbuf; - int err = 0; - int lyr_rval; + struct tem_vt_state *p; - tem = kmem_zalloc(sizeof (struct tem), KM_SLEEP); + mutex_enter(&tems.ts_lock); - mutex_init(&tem->lock, (char *)NULL, MUTEX_DRIVER, NULL); - -#ifdef _HAVE_TEM_FIRMWARE - tem->cons_wrtvec = tem_write; -#endif /* _HAVE_TEM_FIRMWARE */ + if (tems.ts_initialized) { + mutex_exit(&tems.ts_lock); + return (0); + } /* * Open the layered device using the devfs physical device name @@ -237,220 +405,143 @@ tem_init(tem_t **ptem, char *pathname, cred_t *credp) (void) strcpy(pathbuf, "/devices"); if (i_ddi_prompath_to_devfspath(pathname, pathbuf + strlen("/devices")) != DDI_SUCCESS) { - cmn_err(CE_WARN, "terminal emulator: Path conversion error"); + cmn_err(CE_WARN, "terminal-emulator: path conversion error"); kmem_free(pathbuf, MAXPATHLEN); - tem_free(tem); + + mutex_exit(&tems.ts_lock); return (ENXIO); } - if (ldi_open_by_name(pathbuf, FWRITE, credp, &tem->hdl, term_li) != 0) { - cmn_err(CE_WARN, "terminal emulator: Device path open error"); + if (ldi_open_by_name(pathbuf, FWRITE, credp, + &tems.ts_hdl, term_li) != 0) { + cmn_err(CE_WARN, "terminal-emulator: device path open error"); kmem_free(pathbuf, MAXPATHLEN); - tem_free(tem); + + mutex_exit(&tems.ts_lock); return (ENXIO); } kmem_free(pathbuf, MAXPATHLEN); - devinit.modechg_cb = (vis_modechg_cb_t)tem_modechange_callback; - devinit.modechg_arg = (struct vis_modechg_arg *)tem; + temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback; + temargs.modechg_arg = NULL; /* * Initialize the console and get the device parameters */ - if ((err = ldi_ioctl(tem->hdl, VIS_DEVINIT, - (intptr_t)&devinit, FWRITE|FKIOCTL, credp, &lyr_rval)) != 0) { + if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT, + (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) { cmn_err(CE_WARN, "terminal emulator: Compatible fb not found"); - return (tem_init_failed(tem, credp, B_FALSE)); + ret = tems_failed(credp, B_FALSE); + mutex_exit(&tems.ts_lock); + return (ret); } /* Make sure the fb driver and terminal emulator versions match */ - if (devinit.version != VIS_CONS_REV) { + if (temargs.version != VIS_CONS_REV) { cmn_err(CE_WARN, "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " - "of console fb driver not supported", devinit.version); - return (tem_init_failed(tem, credp, B_TRUE)); + "of console fb driver not supported", temargs.version); + ret = tems_failed(credp, B_TRUE); + mutex_exit(&tems.ts_lock); + return (ret); } - if ((tem->fb_polledio = devinit.polledio) == NULL) { + if ((tems.ts_fb_polledio = temargs.polledio) == NULL) { cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled " "I/O"); - return (tem_init_failed(tem, credp, B_TRUE)); + ret = tems_failed(credp, B_TRUE); + mutex_exit(&tems.ts_lock); + return (ret); } /* other sanity checks */ - if (!((devinit.depth == 4) || (devinit.depth == 8) || - (devinit.depth == 24) || (devinit.depth == 32))) { + if (!((temargs.depth == 4) || (temargs.depth == 8) || + (temargs.depth == 24) || (temargs.depth == 32))) { cmn_err(CE_WARN, "terminal emulator: unsupported depth"); - return (tem_init_failed(tem, credp, B_TRUE)); + ret = tems_failed(credp, B_TRUE); + mutex_exit(&tems.ts_lock); + return (ret); } - if ((devinit.mode != VIS_TEXT) && (devinit.mode != VIS_PIXEL)) { + if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) { cmn_err(CE_WARN, "terminal emulator: unsupported mode"); - return (tem_init_failed(tem, credp, B_TRUE)); + ret = tems_failed(credp, B_TRUE); + mutex_exit(&tems.ts_lock); + return (ret); } - if ((devinit.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) { + if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) plat_tem_get_prom_size(&height, &width); - } /* - * Initialize the terminal emulator + * Initialize the common terminal emulator info */ - mutex_enter(&tem->lock); - if ((err = tem_setup_terminal(&devinit, tem, height, width)) != 0) { - cmn_err(CE_WARN, "terminal emulator: Init failed"); - (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, - credp, &lyr_rval); - (void) ldi_close(tem->hdl, NULL, credp); - mutex_exit(&tem->lock); - tem_free(tem); - return (err); - } + tems_setup_terminal(&temargs, height, width); - /* - * make our kernel console keep compatibility with OBP. - */ - tem_get_initial_color(tem); + tems_reset_colormap(credp, CALLED_FROM_NORMAL); + tems_get_initial_color(&tems.ts_init_color); - /* - * On SPARC don't clear the screen if the console is the framebuffer. - * Otherwise it needs to be cleared to get rid of junk that may be - * in frameuffer memory, since the screen isn't cleared when - * boot messages are directed elsewhere. - */ - if (devinit.mode == VIS_TEXT) { - /* - * The old getting current cursor position code, which - * is not needed here, has been in tem_write/tem_polled_write. - */ - tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); - } else if (plat_stdout_is_framebuffer()) { - ASSERT(devinit.mode == VIS_PIXEL); - plat_tem_hide_prom_cursor(); - tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); - - /* - * We are getting the current cursor position in pixel - * mode so that we don't over-write the console output - * during boot. - */ - plat_tem_get_prom_pos(&row, &col); - - /* - * Adjust the row if necessary when the font of our - * kernel console tem is different with that of prom - * tem. - */ - row = tem_adjust_row(tem, row, credp); + tems.ts_initialized = 1; /* initialization flag */ - /* first line of our kernel console output */ - tem->state->first_line = row + 1; - - /* re-set and align cusror position */ - tem->state->a_c_cursor.row = row; - tem->state->a_c_cursor.col = 0; - tem_align_cursor(tem); - } else { - tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 1, NULL); + for (p = list_head(&tems.ts_list); p != NULL; + p = list_next(&tems.ts_list, p)) { + mutex_enter(&p->tvs_lock); + tem_internal_init(p, credp, B_TRUE, B_FALSE); + if (temargs.mode == VIS_PIXEL) + tem_pix_align(p, credp, CALLED_FROM_NORMAL); + mutex_exit(&p->tvs_lock); } -#ifdef _HAVE_TEM_FIRMWARE - if (plat_stdout_is_framebuffer()) { - /* - * Drivers in the console stream may emit additional - * messages before we are ready. This causes text - * overwrite on the screen. So we set the redirection - * here. It is safe because the ioctl in consconfig_dacf - * will succeed and consmode will be set to CONS_KFB. - */ - prom_set_stdout_redirect(console_prom_write_cb, - (promif_redir_arg_t)tem); - - } -#endif /* _HAVE_TEM_FIRMWARE */ - - mutex_exit(&tem->lock); - *ptem = tem; /* Return tem to caller only upon success */ + mutex_exit(&tems.ts_lock); return (0); } -/* - * This is a callback function that we register with the frame - * buffer driver layered underneath. It gets invoked from - * the underlying frame buffer driver to reconfigure the terminal - * emulator to a new screen size and depth in conjunction with - * framebuffer videomode changes. - * Here we keep the foreground/background color and attributes, - * which may be different with the initial settings, so that - * the color won't change while the framebuffer videomode changes. - * And we also reset the kernel terminal emulator and clear the - * whole screen. - */ -void -tem_modechange_callback(tem_t *tem, struct vis_devinit *devinit) -{ - tem_color_t tc; +#define TEMS_DEPTH_DIFF 0x01 +#define TEMS_DIMENSION_DIFF 0x02 - mutex_enter(&tem->lock); - - ASSERT(tem->hdl != NULL); - - tc.fg_color = tem->state->fg_color; - tc.bg_color = tem->state->bg_color; - tc.a_flags = tem->state->a_flags; - - (void) tem_setup_terminal(devinit, tem, - tem->state->a_c_dimension.height, - tem->state->a_c_dimension.width); +static uchar_t +tems_check_videomode(struct vis_devinit *tp) +{ + uchar_t result = 0; - tem_reset_display(tem, kcred, CALLED_FROM_NORMAL, 1, &tc); + if (tems.ts_pdepth != tp->depth) + result |= TEMS_DEPTH_DIFF; - mutex_exit(&tem->lock); + if (tp->mode == VIS_TEXT) { + if (tems.ts_c_dimension.width != tp->width || + tems.ts_c_dimension.height != tp->height) + result |= TEMS_DIMENSION_DIFF; + } else { + if (tems.ts_p_dimension.width != tp->width || + tems.ts_p_dimension.height != tp->height) + result |= TEMS_DIMENSION_DIFF; + } - if (tem->modechg_cb != NULL) - tem->modechg_cb(tem->modechg_arg); + return (result); } -static int -tem_setup_terminal( - struct vis_devinit *devinit, - tem_t *tem, - size_t height, size_t width) +static void +tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width) { int i; - struct tem_state *new_state, *prev_state; - - ASSERT(MUTEX_HELD(&tem->lock)); + int old_blank_buf_size = tems.ts_c_dimension.width; - prev_state = tem->state; + ASSERT(MUTEX_HELD(&tems.ts_lock)); - new_state = kmem_zalloc(sizeof (struct tem_state), KM_SLEEP); + tems.ts_pdepth = tp->depth; + tems.ts_linebytes = tp->linebytes; + tems.ts_display_mode = tp->mode; - new_state->a_pdepth = devinit->depth; - new_state->display_mode = devinit->mode; - new_state->linebytes = devinit->linebytes; - - switch (devinit->mode) { + switch (tp->mode) { case VIS_TEXT: - new_state->a_p_dimension.width = 0; - new_state->a_p_dimension.height = 0; - new_state->a_c_dimension.width = devinit->width; - new_state->a_c_dimension.height = devinit->height; - - new_state->in_fp.f_display = tem_text_display; - new_state->in_fp.f_copy = tem_text_copy; - new_state->in_fp.f_cursor = tem_text_cursor; - new_state->in_fp.f_cls = tem_text_cls; - new_state->in_fp.f_bit2pix = NULL; - - new_state->a_blank_line = - kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); - - for (i = 0; i < new_state->a_c_dimension.width; i++) - new_state->a_blank_line[i] = ' '; + tems.ts_p_dimension.width = 0; + tems.ts_p_dimension.height = 0; + tems.ts_c_dimension.width = tp->width; + tems.ts_c_dimension.height = tp->height; + tems.ts_callbacks = &tem_safe_text_callbacks; break; - case VIS_PIXEL: + case VIS_PIXEL: /* * First check to see if the user has specified a screen size. * If so, use those values. Else use 34x80 as the default. @@ -459,18 +550,13 @@ tem_setup_terminal( width = TEM_DEFAULT_COLS; height = TEM_DEFAULT_ROWS; } - new_state->a_c_dimension.height = height; - new_state->a_c_dimension.width = width; - - new_state->a_p_dimension.height = devinit->height; - new_state->a_p_dimension.width = devinit->width; + tems.ts_c_dimension.height = (screen_size_t)height; + tems.ts_c_dimension.width = (screen_size_t)width; - new_state->in_fp.f_display = tem_pix_display; - new_state->in_fp.f_copy = tem_pix_copy; - new_state->in_fp.f_cursor = tem_pix_cursor; - new_state->in_fp.f_cls = tem_pix_cls; + tems.ts_p_dimension.height = tp->height; + tems.ts_p_dimension.width = tp->width; - new_state->a_blank_line = NULL; + tems.ts_callbacks = &tem_safe_pix_callbacks; /* * set_font() will select a appropriate sized font for @@ -479,75 +565,114 @@ tem_setup_terminal( * default builtin font and adjust the rows and columns * to fit on the screen. */ - set_font(&new_state->a_font, - &new_state->a_c_dimension.height, - &new_state->a_c_dimension.width, - new_state->a_p_dimension.height, - new_state->a_p_dimension.width); - - new_state->a_p_offset.y = - (new_state->a_p_dimension.height - - (new_state->a_c_dimension.height * - new_state->a_font.height)) / 2; - - new_state->a_p_offset.x = - (new_state->a_p_dimension.width - - (new_state->a_c_dimension.width * - new_state->a_font.width)) / 2; - - switch (devinit->depth) { - case 4: - new_state->in_fp.f_bit2pix = bit_to_pix4; - new_state->a_pix_data_size = - (((new_state->a_font.width * 4) + - NBBY - 1) / NBBY) * new_state->a_font.height; - break; - case 8: - new_state->in_fp.f_bit2pix = bit_to_pix8; - new_state->a_pix_data_size = - new_state->a_font.width * - new_state->a_font.height; - break; - case 24: - case 32: - new_state->in_fp.f_bit2pix = bit_to_pix24; - new_state->a_pix_data_size = - new_state->a_font.width * - new_state->a_font.height; - new_state->a_pix_data_size *= 4; - break; - } + set_font(&tems.ts_font, + &tems.ts_c_dimension.height, + &tems.ts_c_dimension.width, + tems.ts_p_dimension.height, + tems.ts_p_dimension.width); + + tems.ts_p_offset.y = (tems.ts_p_dimension.height - + (tems.ts_c_dimension.height * tems.ts_font.height)) / 2; + tems.ts_p_offset.x = (tems.ts_p_dimension.width - + (tems.ts_c_dimension.width * tems.ts_font.width)) / 2; - new_state->a_pix_data = - kmem_alloc(new_state->a_pix_data_size, KM_SLEEP); + tems.ts_pix_data_size = + tems.ts_font.width * tems.ts_font.height; + + tems.ts_pix_data_size *= 4; + + tems.ts_pdepth = tp->depth; break; + } + + /* Now virtual cls also uses the blank_line buffer */ + if (tems.ts_blank_line) + kmem_free(tems.ts_blank_line, old_blank_buf_size); - default: + tems.ts_blank_line = (unsigned char *) + kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP); + for (i = 0; i < tems.ts_c_dimension.width; i++) + tems.ts_blank_line[i] = ' '; +} + +/* + * This is a callback function that we register with the frame + * buffer driver layered underneath. It gets invoked from + * the underlying frame buffer driver to reconfigure the terminal + * emulator to a new screen size and depth in conjunction with + * framebuffer videomode changes. + * Here we keep the foreground/background color and attributes, + * which may be different with the initial settings, so that + * the color won't change while the framebuffer videomode changes. + * And we also reset the kernel terminal emulator and clear the + * whole screen. + */ +/* ARGSUSED */ +void +tems_modechange_callback(struct vis_modechg_arg *arg, + struct vis_devinit *devinit) +{ + uchar_t diff; + struct tem_vt_state *p; + tem_modechg_cb_t cb; + tem_modechg_cb_arg_t cb_arg; + + ASSERT(!(list_is_empty(&tems.ts_list))); + + mutex_enter(&tems.ts_lock); + + /* + * currently only for pixel mode + */ + diff = tems_check_videomode(devinit); + if (diff == 0) { + mutex_exit(&tems.ts_lock); + return; + } + + diff = diff & TEMS_DIMENSION_DIFF; + + if (diff == 0) { /* - * The layered fb driver conveyed an unrecognized rendering - * mode. We cannot proceed with tem initialization. + * Only need to reinit the active tem. */ - kmem_free(new_state, sizeof (struct tem_state)); - return (ENXIO); + struct tem_vt_state *active = tems.ts_active; + tems.ts_pdepth = devinit->depth; + + mutex_enter(&active->tvs_lock); + ASSERT(active->tvs_isactive); + tem_reinit(active, B_TRUE); + mutex_exit(&active->tvs_lock); + + mutex_exit(&tems.ts_lock); + return; } - new_state->a_outbuf = - kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); + tems_setup_terminal(devinit, tems.ts_c_dimension.height, + tems.ts_c_dimension.width); - /* - * Change state atomically so that polled I/O requests - * can be safely and reliably serviced anytime after the terminal - * emulator is originally initialized and the console mode has been - * switched over from the PROM, even while a videomode change - * callback is being processed. - */ - tem->state = new_state; + for (p = list_head(&tems.ts_list); p != NULL; + p = list_next(&tems.ts_list, p)) { + mutex_enter(&p->tvs_lock); + tem_reinit(p, p->tvs_isactive); + mutex_exit(&p->tvs_lock); + } - if (prev_state != NULL) - tem_free_state(prev_state); - return (0); + if (tems.ts_modechg_cb == NULL) { + mutex_exit(&tems.ts_lock); + return; + } + + cb = tems.ts_modechg_cb; + cb_arg = tems.ts_modechg_arg; + + /* + * Release the lock while doing callback. + */ + mutex_exit(&tems.ts_lock); + cb(cb_arg); } /* @@ -556,14 +681,13 @@ tem_setup_terminal( * The blit can be as small as a pixel or as large as the screen. */ void -tem_display_layered( - tem_t *tem, +tems_display_layered( struct vis_consdisplay *pda, cred_t *credp) { int rval; - (void) ldi_ioctl(tem->hdl, VIS_CONSDISPLAY, + (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, (intptr_t)pda, FKIOCTL, credp, &rval); } @@ -574,14 +698,13 @@ tem_display_layered( * such as from vi when deleting characters and words. */ void -tem_copy_layered( - tem_t *tem, +tems_copy_layered( struct vis_conscopy *pma, cred_t *credp) { int rval; - (void) ldi_ioctl(tem->hdl, VIS_CONSCOPY, + (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY, (intptr_t)pma, FKIOCTL, credp, &rval); } @@ -590,22 +713,28 @@ tem_copy_layered( * pixel inverting, text block cursor via the underlying framebuffer. */ void -tem_cursor_layered( - tem_t *tem, +tems_cursor_layered( struct vis_conscursor *pca, cred_t *credp) { int rval; - (void) ldi_ioctl(tem->hdl, VIS_CONSCURSOR, + (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR, (intptr_t)pca, FKIOCTL, credp, &rval); } -void -tem_reset_colormap( - tem_t *tem, - cred_t *credp, - enum called_from called_from) +static void +tem_kdsetmode(int mode, cred_t *credp) +{ + int rval; + + (void) ldi_ioctl(tems.ts_hdl, KDSETMODE, + (intptr_t)mode, FKIOCTL, credp, &rval); + +} + +static void +tems_reset_colormap(cred_t *credp, enum called_from called_from) { struct vis_cmap cm; int rval; @@ -613,35 +742,41 @@ tem_reset_colormap( if (called_from == CALLED_FROM_STANDALONE) return; - switch (tem->state->a_pdepth) { + switch (tems.ts_pdepth) { case 8: cm.index = 0; cm.count = 16; cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */ cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */ cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */ - (void) ldi_ioctl(tem->hdl, VIS_PUTCMAP, (intptr_t)&cm, + (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm, FKIOCTL, credp, &rval); break; } } void -tem_get_size(tem_t *tem, ushort_t *r, ushort_t *c, +tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y) { - *r = (ushort_t)tem->state->a_c_dimension.height; - *c = (ushort_t)tem->state->a_c_dimension.width; - *x = (ushort_t)tem->state->a_p_dimension.width; - *y = (ushort_t)tem->state->a_p_dimension.height; + mutex_enter(&tems.ts_lock); + *r = (ushort_t)tems.ts_c_dimension.height; + *c = (ushort_t)tems.ts_c_dimension.width; + *x = (ushort_t)tems.ts_p_dimension.width; + *y = (ushort_t)tems.ts_p_dimension.height; + mutex_exit(&tems.ts_lock); } void -tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func, +tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg) { - tem->modechg_cb = func; - tem->modechg_arg = arg; + mutex_enter(&tems.ts_lock); + + tems.ts_modechg_cb = func; + tems.ts_modechg_arg = arg; + + mutex_exit(&tems.ts_lock); } /* @@ -649,43 +784,41 @@ tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func, * different screen height and width with our kernel console. */ static void -tem_prom_scroll_up(struct tem *tem, int nrows, cred_t *credp) +tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp, + enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_conscopy ma; int ncols, width; /* copy */ - ma.s_row = nrows * tems->a_font.height; - ma.e_row = tems->a_p_dimension.height - 1; + ma.s_row = nrows * tems.ts_font.height; + ma.e_row = tems.ts_p_dimension.height - 1; ma.t_row = 0; ma.s_col = 0; - ma.e_col = tems->a_p_dimension.width - 1; + ma.e_col = tems.ts_p_dimension.width - 1; ma.t_col = 0; - tem_copy(tem, &ma, credp, CALLED_FROM_NORMAL); + tems_safe_copy(&ma, credp, called_from); /* clear */ - width = tems->a_font.width; - ncols = (tems->a_p_dimension.width + - (width - 1))/ width; + width = tems.ts_font.width; + ncols = (tems.ts_p_dimension.width + (width - 1))/ width; - tem_pix_cls_range(tem, - 0, nrows, tems->a_p_offset.y, - 0, ncols, 0, - B_TRUE, credp, CALLED_FROM_NORMAL); + tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, + 0, ncols, 0, B_TRUE, credp, called_from); } #define PROM_DEFAULT_FONT_HEIGHT 22 -#define PROM_DEFAULT_WINDOW_TOP 0x8a +#define PROM_DEFAULT_WINDOW_TOP 0x8a /* * This function is to compute the starting row of the console, according to * PROM cursor's position. Here we have to take different fonts into account. */ static int -tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp) +tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp, + enum called_from called_from) { int tem_row; int tem_y; @@ -700,28 +833,64 @@ tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp) prom_window_top = PROM_DEFAULT_WINDOW_TOP; tem_y = (prom_row + 1) * prom_charheight + prom_window_top - - tem->state->a_p_offset.y; - tem_row = (tem_y + tem->state->a_font.height - 1) / - tem->state->a_font.height - 1; + tems.ts_p_offset.y; + tem_row = (tem_y + tems.ts_font.height - 1) / + tems.ts_font.height - 1; if (tem_row < 0) { tem_row = 0; - } else if (tem_row >= (tem->state->a_c_dimension.height - 1)) { + } else if (tem_row >= (tems.ts_c_dimension.height - 1)) { /* * Scroll up the prom outputs if the PROM cursor's position is * below our tem's lower boundary. */ scroll_up_lines = tem_row - - (tem->state->a_c_dimension.height - 1); - tem_prom_scroll_up(tem, scroll_up_lines, credp); - tem_row = tem->state->a_c_dimension.height - 1; + (tems.ts_c_dimension.height - 1); + tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from); + tem_row = tems.ts_c_dimension.height - 1; } return (tem_row); } +void +tem_pix_align(struct tem_vt_state *tem, cred_t *credp, + enum called_from called_from) +{ + uint32_t row = 0; + uint32_t col = 0; + + if (plat_stdout_is_framebuffer()) { + plat_tem_hide_prom_cursor(); + + /* + * We are getting the current cursor position in pixel + * mode so that we don't over-write the console output + * during boot. + */ + plat_tem_get_prom_pos(&row, &col); + + /* + * Adjust the row if necessary when the font of our + * kernel console tem is different with that of prom + * tem. + */ + row = tem_adjust_row(tem, row, credp, called_from); + + /* first line of our kernel console output */ + tem->tvs_first_line = row + 1; + + /* re-set and align cusror position */ + tem->tvs_s_cursor.row = tem->tvs_c_cursor.row = + (screen_pos_t)row; + tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0; + } else { + tem_safe_reset_display(tem, credp, called_from, B_TRUE, B_TRUE); + } +} + static void -tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) +tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) { int i_inverse = 0; int i_inverse_screen = 0; @@ -737,16 +906,16 @@ tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) * PROM, so that our kernel console can keep the same visual behaviour. */ static void -tem_get_initial_color(tem_t *tem) +tems_get_initial_color(tem_color_t *pcolor) { boolean_t inverse, inverse_screen; unsigned short flags = 0; - tem->init_color.fg_color = DEFAULT_ANSI_FOREGROUND; - tem->init_color.bg_color = DEFAULT_ANSI_BACKGROUND; + pcolor->fg_color = DEFAULT_ANSI_FOREGROUND; + pcolor->bg_color = DEFAULT_ANSI_BACKGROUND; if (plat_stdout_is_framebuffer()) { - tem_get_inverses(&inverse, &inverse_screen); + tems_get_inverses(&inverse, &inverse_screen); if (inverse) flags |= TEM_ATTR_REVERSE; if (inverse_screen) @@ -755,5 +924,91 @@ tem_get_initial_color(tem_t *tem) flags |= TEM_ATTR_BOLD; } - tem->init_color.a_flags = flags; + pcolor->a_flags = flags; +} + +uchar_t +tem_get_fbmode(tem_vt_state_t tem_arg) +{ + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; + + uchar_t fbmode; + + mutex_enter(&tem->tvs_lock); + fbmode = tem->tvs_fbmode; + mutex_exit(&tem->tvs_lock); + + return (fbmode); +} + +void +tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp) +{ + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; + + mutex_enter(&tems.ts_lock); + mutex_enter(&tem->tvs_lock); + + if (fbmode == tem->tvs_fbmode) { + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); + return; + } + + tem->tvs_fbmode = fbmode; + + if (tem->tvs_isactive) { + tem_kdsetmode(tem->tvs_fbmode, credp); + if (fbmode == KD_TEXT) + tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); + } + + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); +} + +void +tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp) +{ + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; + + mutex_enter(&tems.ts_lock); + tems.ts_active = tem; + + mutex_enter(&tem->tvs_lock); + tem->tvs_isactive = B_TRUE; + + tem_kdsetmode(tem->tvs_fbmode, credp); + + if (unblank) + tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); + + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); +} + +void +tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp) +{ + struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1; + struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2; + + mutex_enter(&tems.ts_lock); + mutex_enter(&tobe->tvs_lock); + mutex_enter(&cur->tvs_lock); + + tems.ts_active = tobe; + cur->tvs_isactive = B_FALSE; + tobe->tvs_isactive = B_TRUE; + + mutex_exit(&cur->tvs_lock); + + if (cur->tvs_fbmode != tobe->tvs_fbmode) + tem_kdsetmode(tobe->tvs_fbmode, credp); + + if (tobe->tvs_fbmode == KD_TEXT) + tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL); + + mutex_exit(&tobe->tvs_lock); + mutex_exit(&tems.ts_lock); } diff --git a/usr/src/uts/common/io/tem_safe.c b/usr/src/uts/common/io/tem_safe.c index 10df667057..fa2e4916cd 100644 --- a/usr/src/uts/common/io/tem_safe.c +++ b/usr/src/uts/common/io/tem_safe.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Polled I/O safe ANSI terminal emulator module; * Supporting TERM types 'sun' and 'sun-color, parsing @@ -48,6 +46,14 @@ * - CANNOT wait for interrupts * - CANNOT allocate memory * + * All non-static functions in this file which: + * - Operates on tems and tem_vt_state + * - Not only called from standalone mode, i.e. has + * a "calledfrom" argument + * should assert this at the beginning: + * + * ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + * called_from == CALLED_FROM_STANDALONE); */ #include <sys/types.h> @@ -59,65 +65,91 @@ #include <sys/ksynch.h> #include <sys/sysmacros.h> #include <sys/mutex.h> +#include <sys/note.h> +#include <sys/t_lock.h> + +tem_safe_callbacks_t tem_safe_text_callbacks = { + &tem_safe_text_display, + &tem_safe_text_copy, + &tem_safe_text_cursor, + NULL, + &tem_safe_text_cls +}; +tem_safe_callbacks_t tem_safe_pix_callbacks = { + &tem_safe_pix_display, + &tem_safe_pix_copy, + &tem_safe_pix_cursor, + &tem_safe_pix_bit2pix, + &tem_safe_pix_cls +}; -static void tem_display(struct tem *, - struct vis_consdisplay *, - cred_t *, enum called_from); -static void tem_cursor(struct tem *, - struct vis_conscursor *, - cred_t *, enum called_from); -static void tem_control(struct tem *, uchar_t, + +static void tem_safe_control(struct tem_vt_state *, uchar_t, cred_t *, enum called_from); -static void tem_setparam(struct tem *, int, int); -static void tem_selgraph(struct tem *); -static void tem_chkparam(struct tem *, uchar_t, +static void tem_safe_setparam(struct tem_vt_state *, int, int); +static void tem_safe_selgraph(struct tem_vt_state *); +static void tem_safe_chkparam(struct tem_vt_state *, uchar_t, cred_t *, enum called_from); -static void tem_getparams(struct tem *, uchar_t, +static void tem_safe_getparams(struct tem_vt_state *, uchar_t, cred_t *, enum called_from); -static void tem_outch(struct tem *, uchar_t, +static void tem_safe_outch(struct tem_vt_state *, uchar_t, cred_t *, enum called_from); -static void tem_parse(struct tem *, uchar_t, +static void tem_safe_parse(struct tem_vt_state *, uchar_t, cred_t *, enum called_from); -static void tem_new_line(struct tem *, +static void tem_safe_new_line(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_cr(struct tem *); -static void tem_lf(struct tem *, +static void tem_safe_cr(struct tem_vt_state *); +static void tem_safe_lf(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_send_data(struct tem *, cred_t *, +static void tem_safe_send_data(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_cls(struct tem *, +static void tem_safe_cls(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_tab(struct tem *, +static void tem_safe_tab(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_back_tab(struct tem *, +static void tem_safe_back_tab(struct tem_vt_state *, cred_t *, enum called_from); -static void tem_clear_tabs(struct tem *, int); -static void tem_set_tab(struct tem *); -static void tem_mv_cursor(struct tem *, int, int, +static void tem_safe_clear_tabs(struct tem_vt_state *, int); +static void tem_safe_set_tab(struct tem_vt_state *); +static void tem_safe_mv_cursor(struct tem_vt_state *, int, int, cred_t *, enum called_from); -static void tem_shift(struct tem *, int, int, +static void tem_safe_shift(struct tem_vt_state *, int, int, cred_t *, enum called_from); -static void tem_scroll(struct tem *, int, int, +static void tem_safe_scroll(struct tem_vt_state *, int, int, int, int, cred_t *, enum called_from); -static void tem_clear_chars(struct tem *tem, +static void tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row, screen_pos_t col, cred_t *credp, enum called_from called_from); -static void tem_copy_area(struct tem *tem, +static void tem_safe_copy_area(struct tem_vt_state *tem, screen_pos_t s_col, screen_pos_t s_row, screen_pos_t e_col, screen_pos_t e_row, screen_pos_t t_col, screen_pos_t t_row, cred_t *credp, enum called_from called_from); -static void tem_image_display(struct tem *, uchar_t *, +static void tem_safe_image_display(struct tem_vt_state *, uchar_t *, int, int, screen_pos_t, screen_pos_t, cred_t *, enum called_from); -static void tem_bell(struct tem *tem, +static void tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from); -static void tem_get_color(struct tem *tem, - text_color_t *fg, text_color_t *bg); -static void tem_pix_clear_prom_output(struct tem *tem, +static void tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from); +static void tem_safe_virtual_cls(struct tem_vt_state *, int, screen_pos_t, + screen_pos_t); +static void tem_safe_virtual_display(struct tem_vt_state *, + unsigned char *, int, screen_pos_t, screen_pos_t, + text_color_t, text_color_t); +static void tem_safe_virtual_copy(struct tem_vt_state *, screen_pos_t, + screen_pos_t, screen_pos_t, screen_pos_t, + screen_pos_t, screen_pos_t); +static void tem_safe_align_cursor(struct tem_vt_state *tem); +static void bit_to_pix4(struct tem_vt_state *tem, uchar_t c, + text_color_t fg_color, text_color_t bg_color); +static void bit_to_pix8(struct tem_vt_state *tem, uchar_t c, + text_color_t fg_color, text_color_t bg_color); +static void bit_to_pix24(struct tem_vt_state *tem, uchar_t c, + text_color_t fg_color, text_color_t bg_color); + /* BEGIN CSTYLED */ /* Bk Rd Gr Br Bl Mg Cy Wh */ static text_color_t fg_dim_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 8 }; @@ -168,31 +200,40 @@ struct fontlist fonts[] = { #define INVERSE(ch) (ch ^ 0xff) -#define BIT_TO_PIX(tem, c, fg, bg) { \ - ASSERT((tem)->state->in_fp.f_bit2pix != NULL); \ - (void) (*(tem)->state->in_fp.f_bit2pix)((tem), (c), (fg), (bg));\ +#define tem_safe_callback_display (*tems.ts_callbacks->tsc_display) +#define tem_safe_callback_copy (*tems.ts_callbacks->tsc_copy) +#define tem_safe_callback_cursor (*tems.ts_callbacks->tsc_cursor) +#define tem_safe_callback_cls (*tems.ts_callbacks->tsc_cls) +#define tem_safe_callback_bit2pix(tem, c, fg, bg) { \ + ASSERT(tems.ts_callbacks->tsc_bit2pix != NULL); \ + (void) (*tems.ts_callbacks->tsc_bit2pix)((tem), (c), (fg), (bg));\ } void -tem_check_first_time( - struct tem *tem, +tem_safe_check_first_time( + struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { static int first_time = 1; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + /* * Realign the console cursor. We did this in tem_init(). * However, drivers in the console stream may emit additional * messages before we are ready. This causes text overwrite * on the screen. This is a workaround. */ - if (first_time && tem->state->display_mode == VIS_TEXT) { - tem_text_cursor(tem, VIS_GET_CURSOR, credp, called_from); - tem_align_cursor(tem); - } - first_time = 0; + if (!first_time) + return; + first_time = 0; + if (tems.ts_display_mode == VIS_TEXT) { + tem_safe_text_cursor(tem, VIS_GET_CURSOR, credp, called_from); + tem_safe_align_cursor(tem); + } } /* @@ -205,16 +246,24 @@ tem_check_first_time( * It is also entered when the kernel is panicing. */ void -tem_polled_write( - struct tem *tem, +tem_safe_polled_write( + tem_vt_state_t tem_arg, uchar_t *buf, int len) { + struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; + +#ifdef __lock_lint + _NOTE(NO_COMPETING_THREADS_NOW) + _NOTE(NO_COMPETING_THREADS_AS_SIDE_EFFECT) +#endif - ASSERT(tem->hdl != NULL); + if (!tem->tvs_initialized) { + return; + } - tem_check_first_time(tem, kcred, CALLED_FROM_STANDALONE); - tem_terminal_emulate(tem, buf, len, NULL, CALLED_FROM_STANDALONE); + tem_safe_check_first_time(tem, kcred, CALLED_FROM_STANDALONE); + tem_safe_terminal_emulate(tem, buf, len, NULL, CALLED_FROM_STANDALONE); } @@ -227,32 +276,32 @@ tem_polled_write( * no enqueing. */ void -tem_terminal_emulate( - struct tem *tem, +tem_safe_terminal_emulate( + struct tem_vt_state *tem, uchar_t *buf, int len, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - (*tems->in_fp.f_cursor) - (tem, VIS_HIDE_CURSOR, credp, called_from); + if (tem->tvs_isactive) + tem_safe_callback_cursor(tem, + VIS_HIDE_CURSOR, credp, called_from); - for (; len > 0; len--, buf++) { - tem_parse(tem, *buf, credp, called_from); - } + for (; len > 0; len--, buf++) + tem_safe_parse(tem, *buf, credp, called_from); /* * Send the data we just got to the framebuffer. */ - tem_send_data(tem, credp, called_from); + tem_safe_send_data(tem, credp, called_from); - (*tems->in_fp.f_cursor) - (tem, VIS_DISPLAY_CURSOR, credp, called_from); + if (tem->tvs_isactive) + tem_safe_callback_cursor(tem, + VIS_DISPLAY_CURSOR, credp, called_from); } /* @@ -261,16 +310,15 @@ tem_terminal_emulate( * from quiesced or normal (ie. use polled I/O vs. layered ioctls) */ static void -tem_display( - struct tem *tem, +tems_safe_display( struct vis_consdisplay *pda, cred_t *credp, enum called_from called_from) { if (called_from == CALLED_FROM_STANDALONE) - tem->fb_polledio->display(tem->fb_polledio->arg, pda); + tems.ts_fb_polledio->display(tems.ts_fb_polledio->arg, pda); else - tem_display_layered(tem, pda, credp); + tems_display_layered(pda, credp); } /* @@ -279,16 +327,15 @@ tem_display( * from, quiesced or normal (ie. use polled I/O vs. layered ioctls) */ void -tem_copy( - struct tem *tem, +tems_safe_copy( struct vis_conscopy *pca, cred_t *credp, enum called_from called_from) { if (called_from == CALLED_FROM_STANDALONE) - tem->fb_polledio->copy(tem->fb_polledio->arg, pca); + tems.ts_fb_polledio->copy(tems.ts_fb_polledio->arg, pca); else - tem_copy_layered(tem, pca, credp); + tems_copy_layered(pca, credp); } /* @@ -298,16 +345,15 @@ tem_copy( * normal (ie. use polled I/O vs. layered ioctls). */ static void -tem_cursor( - struct tem *tem, +tems_safe_cursor( struct vis_conscursor *pca, cred_t *credp, enum called_from called_from) { if (called_from == CALLED_FROM_STANDALONE) - tem->fb_polledio->cursor(tem->fb_polledio->arg, pca); + tems.ts_fb_polledio->cursor(tems.ts_fb_polledio->arg, pca); else - tem_cursor_layered(tem, pca, credp); + tems_cursor_layered(pca, credp); } /* @@ -316,75 +362,70 @@ tem_cursor( */ static void -tem_control( - struct tem *tem, +tem_safe_control( + struct tem_vt_state *tem, uchar_t ch, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; - - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); - - tems->a_state = A_STATE_START; + tem->tvs_state = A_STATE_START; switch (ch) { case A_BEL: - tem_bell(tem, called_from); + tem_safe_bell(tem, called_from); break; case A_BS: - tem_mv_cursor(tem, - tems->a_c_cursor.row, - tems->a_c_cursor.col - 1, + tem_safe_mv_cursor(tem, + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col - 1, credp, called_from); break; case A_HT: - tem_tab(tem, credp, called_from); + tem_safe_tab(tem, credp, called_from); break; case A_NL: /* - * tem_send_data(tem, credp, called_from); - * tem_new_line(tem, credp, called_from); + * tem_safe_send_data(tem, credp, called_from); + * tem_safe_new_line(tem, credp, called_from); * break; */ case A_VT: - tem_send_data(tem, credp, called_from); - tem_lf(tem, credp, called_from); + tem_safe_send_data(tem, credp, called_from); + tem_safe_lf(tem, credp, called_from); break; case A_FF: - tem_send_data(tem, credp, called_from); - tem_cls(tem, credp, called_from); + tem_safe_send_data(tem, credp, called_from); + tem_safe_cls(tem, credp, called_from); break; case A_CR: - tem_send_data(tem, credp, called_from); - tem_cr(tem); + tem_safe_send_data(tem, credp, called_from); + tem_safe_cr(tem); break; case A_ESC: - tems->a_state = A_STATE_ESC; + tem->tvs_state = A_STATE_ESC; break; case A_CSI: { int i; - tems->a_curparam = 0; - tems->a_paramval = 0; - tems->a_gotparam = B_FALSE; + tem->tvs_curparam = 0; + tem->tvs_paramval = 0; + tem->tvs_gotparam = B_FALSE; /* clear the parameters */ for (i = 0; i < TEM_MAXPARAMS; i++) - tems->a_params[i] = -1; - tems->a_state = A_STATE_CSI; + tem->tvs_params[i] = -1; + tem->tvs_state = A_STATE_CSI; } break; case A_GS: - tem_back_tab(tem, credp, called_from); + tem_safe_back_tab(tem, credp, called_from); break; default: @@ -399,13 +440,13 @@ tem_control( */ static void -tem_setparam(struct tem *tem, int count, int newparam) +tem_safe_setparam(struct tem_vt_state *tem, int count, int newparam) { int i; for (i = 0; i < count; i++) { - if (tem->state->a_params[i] == -1) - tem->state->a_params[i] = newparam; + if (tem->tvs_params[i] == -1) + tem->tvs_params[i] = newparam; } } @@ -414,47 +455,44 @@ tem_setparam(struct tem *tem, int count, int newparam) * select graphics mode based on the param vals stored in a_params */ static void -tem_selgraph(struct tem *tem) +tem_safe_selgraph(struct tem_vt_state *tem) { - struct tem_state *tems = tem->state; int curparam; int count = 0; int param; - curparam = tems->a_curparam; + tem->tvs_state = A_STATE_START; + + curparam = tem->tvs_curparam; do { - param = tems->a_params[count]; + param = tem->tvs_params[count]; switch (param) { case -1: case 0: - if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { - tems->a_flags |= TEM_ATTR_REVERSE; - } else { - tems->a_flags &= ~TEM_ATTR_REVERSE; - } - tems->a_flags = tem->init_color.a_flags; - tems->fg_color = tem->init_color.fg_color; - tems->bg_color = tem->init_color.bg_color; + /* reset to initial normal settings */ + tem->tvs_fg_color = tems.ts_init_color.fg_color; + tem->tvs_bg_color = tems.ts_init_color.bg_color; + tem->tvs_flags = tems.ts_init_color.a_flags; break; case 1: /* Bold Intense */ - tems->a_flags |= TEM_ATTR_BOLD; + tem->tvs_flags |= TEM_ATTR_BOLD; break; case 2: /* Faint Intense */ - tems->a_flags &= ~TEM_ATTR_BOLD; + tem->tvs_flags &= ~TEM_ATTR_BOLD; break; case 5: /* Blink */ - tems->a_flags |= TEM_ATTR_BLINK; + tem->tvs_flags |= TEM_ATTR_BLINK; break; case 7: /* Reverse video */ - if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { - tems->a_flags &= ~TEM_ATTR_REVERSE; + if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) { + tem->tvs_flags &= ~TEM_ATTR_REVERSE; } else { - tems->a_flags |= TEM_ATTR_REVERSE; + tem->tvs_flags |= TEM_ATTR_REVERSE; } break; @@ -466,7 +504,7 @@ tem_selgraph(struct tem *tem) case 35: /* magenta (light magenta) foreground */ case 36: /* cyan (light cyan) foreground */ case 37: /* white (bright white) foreground */ - tems->fg_color = param - 30; + tem->tvs_fg_color = param - 30; break; case 40: /* black (grey) background */ @@ -477,7 +515,7 @@ tem_selgraph(struct tem *tem) case 45: /* magenta (light magenta) background */ case 46: /* cyan (light cyan) background */ case 47: /* white (bright white) background */ - tems->bg_color = param - 40; + tem->tvs_bg_color = param - 40; break; default: @@ -487,9 +525,6 @@ tem_selgraph(struct tem *tem) curparam--; } while (curparam > 0); - - - tems->a_state = A_STATE_START; } /* @@ -499,103 +534,102 @@ tem_selgraph(struct tem *tem) * It assumes that the next lower level will do so. */ static void -tem_chkparam( - struct tem *tem, +tem_safe_chkparam( + struct tem_vt_state *tem, uchar_t ch, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int i; int row; int col; ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + MUTEX_HELD(&tem->tvs_lock)); - row = tems->a_c_cursor.row; - col = tems->a_c_cursor.col; + row = tem->tvs_c_cursor.row; + col = tem->tvs_c_cursor.col; switch (ch) { case 'm': /* select terminal graphics mode */ - tem_send_data(tem, credp, called_from); - tem_selgraph(tem); + tem_safe_send_data(tem, credp, called_from); + tem_safe_selgraph(tem); break; case '@': /* insert char */ - tem_setparam(tem, 1, 1); - tem_shift(tem, tems->a_params[0], TEM_SHIFT_RIGHT, + tem_safe_setparam(tem, 1, 1); + tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT, credp, called_from); break; case 'A': /* cursor up */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row - tems->a_params[0], col, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row - tem->tvs_params[0], col, credp, called_from); break; case 'd': /* VPA - vertical position absolute */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, tems->a_params[0] - 1, col, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, tem->tvs_params[0] - 1, col, credp, called_from); break; case 'e': /* VPR - vertical position relative */ case 'B': /* cursor down */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row + tems->a_params[0], col, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row + tem->tvs_params[0], col, credp, called_from); break; case 'a': /* HPR - horizontal position relative */ case 'C': /* cursor right */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row, col + tems->a_params[0], + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row, col + tem->tvs_params[0], credp, called_from); break; case '`': /* HPA - horizontal position absolute */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row, tems->a_params[0] - 1, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1, credp, called_from); break; case 'D': /* cursor left */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row, col - tems->a_params[0], + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row, col - tem->tvs_params[0], credp, called_from); break; case 'E': /* CNL cursor next line */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row + tems->a_params[0], 0, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row + tem->tvs_params[0], 0, credp, called_from); break; case 'F': /* CPL cursor previous line */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row - tems->a_params[0], 0, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row - tem->tvs_params[0], 0, credp, called_from); break; case 'G': /* cursor horizontal position */ - tem_setparam(tem, 1, 1); - tem_mv_cursor(tem, row, tems->a_params[0] - 1, + tem_safe_setparam(tem, 1, 1); + tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1, credp, called_from); break; case 'g': /* clear tabs */ - tem_setparam(tem, 1, 0); - tem_clear_tabs(tem, tems->a_params[0]); + tem_safe_setparam(tem, 1, 0); + tem_safe_clear_tabs(tem, tem->tvs_params[0]); break; case 'f': /* HVP Horizontal and Vertical Position */ case 'H': /* CUP position cursor */ - tem_setparam(tem, 2, 1); - tem_mv_cursor(tem, - tems->a_params[0] - 1, - tems->a_params[1] - 1, + tem_safe_setparam(tem, 2, 1); + tem_safe_mv_cursor(tem, + tem->tvs_params[0] - 1, + tem->tvs_params[1] - 1, credp, called_from); break; @@ -604,24 +638,24 @@ tem_chkparam( break; case 'J': /* ED - Erase in Display */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 0); - switch (tems->a_params[0]) { + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 0); + switch (tem->tvs_params[0]) { case 0: /* erase cursor to end of screen */ /* FIRST erase cursor to end of line */ - tem_clear_chars(tem, - tems->a_c_dimension.width - - tems->a_c_cursor.col, - tems->a_c_cursor.row, - tems->a_c_cursor.col, credp, called_from); + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width - + tem->tvs_c_cursor.col, + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, credp, called_from); /* THEN erase lines below the cursor */ - for (row = tems->a_c_cursor.row + 1; - row < tems->a_c_dimension.height; + for (row = tem->tvs_c_cursor.row + 1; + row < tems.ts_c_dimension.height; row++) { - tem_clear_chars(tem, - tems->a_c_dimension.width, + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width, row, 0, credp, called_from); } break; @@ -630,26 +664,26 @@ tem_chkparam( /* erase beginning of screen to cursor */ /* FIRST erase lines above the cursor */ for (row = 0; - row < tems->a_c_cursor.row; + row < tem->tvs_c_cursor.row; row++) { - tem_clear_chars(tem, - tems->a_c_dimension.width, + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width, row, 0, credp, called_from); } /* THEN erase beginning of line to cursor */ - tem_clear_chars(tem, - tems->a_c_cursor.col + 1, - tems->a_c_cursor.row, + tem_safe_clear_chars(tem, + tem->tvs_c_cursor.col + 1, + tem->tvs_c_cursor.row, 0, credp, called_from); break; case 2: /* erase whole screen */ for (row = 0; - row < tems->a_c_dimension.height; + row < tems.ts_c_dimension.height; row++) { - tem_clear_chars(tem, - tems->a_c_dimension.width, + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width, row, 0, credp, called_from); } break; @@ -657,92 +691,92 @@ tem_chkparam( break; case 'K': /* EL - Erase in Line */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 0); - switch (tems->a_params[0]) { + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 0); + switch (tem->tvs_params[0]) { case 0: /* erase cursor to end of line */ - tem_clear_chars(tem, - (tems->a_c_dimension.width - - tems->a_c_cursor.col), - tems->a_c_cursor.row, - tems->a_c_cursor.col, + tem_safe_clear_chars(tem, + (tems.ts_c_dimension.width - + tem->tvs_c_cursor.col), + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, credp, called_from); break; case 1: /* erase beginning of line to cursor */ - tem_clear_chars(tem, - tems->a_c_cursor.col + 1, - tems->a_c_cursor.row, + tem_safe_clear_chars(tem, + tem->tvs_c_cursor.col + 1, + tem->tvs_c_cursor.row, 0, credp, called_from); break; case 2: /* erase whole line */ - tem_clear_chars(tem, - tems->a_c_dimension.width, - tems->a_c_cursor.row, + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width, + tem->tvs_c_cursor.row, 0, credp, called_from); break; } break; case 'L': /* insert line */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 1); - tem_scroll(tem, - tems->a_c_cursor.row, - tems->a_c_dimension.height - 1, - tems->a_params[0], TEM_SCROLL_DOWN, + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 1); + tem_safe_scroll(tem, + tem->tvs_c_cursor.row, + tems.ts_c_dimension.height - 1, + tem->tvs_params[0], TEM_SCROLL_DOWN, credp, called_from); break; case 'M': /* delete line */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 1); - tem_scroll(tem, - tems->a_c_cursor.row, - tems->a_c_dimension.height - 1, - tems->a_params[0], TEM_SCROLL_UP, + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 1); + tem_safe_scroll(tem, + tem->tvs_c_cursor.row, + tems.ts_c_dimension.height - 1, + tem->tvs_params[0], TEM_SCROLL_UP, credp, called_from); break; case 'P': /* DCH - delete char */ - tem_setparam(tem, 1, 1); - tem_shift(tem, tems->a_params[0], TEM_SHIFT_LEFT, + tem_safe_setparam(tem, 1, 1); + tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT, credp, called_from); break; case 'S': /* scroll up */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 1); - tem_scroll(tem, 0, - tems->a_c_dimension.height - 1, - tems->a_params[0], TEM_SCROLL_UP, + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 1); + tem_safe_scroll(tem, 0, + tems.ts_c_dimension.height - 1, + tem->tvs_params[0], TEM_SCROLL_UP, credp, called_from); break; case 'T': /* scroll down */ - tem_send_data(tem, credp, called_from); - tem_setparam(tem, 1, 1); - tem_scroll(tem, 0, - tems->a_c_dimension.height - 1, - tems->a_params[0], TEM_SCROLL_DOWN, + tem_safe_send_data(tem, credp, called_from); + tem_safe_setparam(tem, 1, 1); + tem_safe_scroll(tem, 0, + tems.ts_c_dimension.height - 1, + tem->tvs_params[0], TEM_SCROLL_DOWN, credp, called_from); break; case 'X': /* erase char */ - tem_setparam(tem, 1, 1); - tem_clear_chars(tem, - tems->a_params[0], - tems->a_c_cursor.row, - tems->a_c_cursor.col, + tem_safe_setparam(tem, 1, 1); + tem_safe_clear_chars(tem, + tem->tvs_params[0], + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, credp, called_from); break; case 'Z': /* cursor backward tabulation */ - tem_setparam(tem, 1, 1); + tem_safe_setparam(tem, 1, 1); /* * Rule exception - We do sanity checking here. @@ -751,14 +785,14 @@ tem_chkparam( * looping for a long time. There can't be more than one * tab stop per column, so use that as a limit. */ - if (tems->a_params[0] > tems->a_c_dimension.width) - tems->a_params[0] = tems->a_c_dimension.width; + if (tem->tvs_params[0] > tems.ts_c_dimension.width) + tem->tvs_params[0] = tems.ts_c_dimension.width; - for (i = 0; i < tems->a_params[0]; i++) - tem_back_tab(tem, credp, called_from); + for (i = 0; i < tem->tvs_params[0]; i++) + tem_safe_back_tab(tem, credp, called_from); break; } - tems->a_state = A_STATE_START; + tem->tvs_state = A_STATE_START; } @@ -766,38 +800,36 @@ tem_chkparam( * Gather the parameters of an ANSI escape sequence */ static void -tem_getparams(struct tem *tem, uchar_t ch, +tem_safe_getparams(struct tem_vt_state *tem, uchar_t ch, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + MUTEX_HELD(&tem->tvs_lock)); if (ch >= '0' && ch <= '9') { - tems->a_paramval = ((tems->a_paramval * 10) + (ch - '0')); - tems->a_gotparam = B_TRUE; /* Remember got parameter */ + tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0')); + tem->tvs_gotparam = B_TRUE; /* Remember got parameter */ return; /* Return immediately */ - } else if (tems->a_state == A_STATE_CSI_EQUAL || - tems->a_state == A_STATE_CSI_QMARK) { - tems->a_state = A_STATE_START; + } else if (tem->tvs_state == A_STATE_CSI_EQUAL || + tem->tvs_state == A_STATE_CSI_QMARK) { + tem->tvs_state = A_STATE_START; } else { - if (tems->a_curparam < TEM_MAXPARAMS) { - if (tems->a_gotparam) { + if (tem->tvs_curparam < TEM_MAXPARAMS) { + if (tem->tvs_gotparam) { /* get the parameter value */ - tems->a_params[tems->a_curparam] = - tems->a_paramval; + tem->tvs_params[tem->tvs_curparam] = + tem->tvs_paramval; } - tems->a_curparam++; + tem->tvs_curparam++; } if (ch == ';') { /* Restart parameter search */ - tems->a_gotparam = B_FALSE; - tems->a_paramval = 0; /* No parame value yet */ + tem->tvs_gotparam = B_FALSE; + tem->tvs_paramval = 0; /* No parame value yet */ } else { /* Handle escape sequence */ - tem_chkparam(tem, ch, credp, called_from); + tem_safe_chkparam(tem, ch, credp, called_from); } } } @@ -808,64 +840,63 @@ tem_getparams(struct tem *tem, uchar_t ch, */ static void -tem_outch(struct tem *tem, uchar_t ch, +tem_safe_outch(struct tem_vt_state *tem, uchar_t ch, cred_t *credp, enum called_from called_from) { - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); /* buffer up the character until later */ - tem->state->a_outbuf[tem->state->a_outindex++] = ch; - tem->state->a_c_cursor.col++; - if (tem->state->a_c_cursor.col >= tem->state->a_c_dimension.width) { - tem_send_data(tem, credp, called_from); - tem_new_line(tem, credp, called_from); + tem->tvs_outbuf[tem->tvs_outindex++] = ch; + tem->tvs_c_cursor.col++; + if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) { + tem_safe_send_data(tem, credp, called_from); + tem_safe_new_line(tem, credp, called_from); } } static void -tem_new_line(struct tem *tem, +tem_safe_new_line(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - tem_cr(tem); - tem_lf(tem, credp, called_from); + tem_safe_cr(tem); + tem_safe_lf(tem, credp, called_from); } static void -tem_cr(struct tem *tem) +tem_safe_cr(struct tem_vt_state *tem) { - tem->state->a_c_cursor.col = 0; - tem_align_cursor(tem); + tem->tvs_c_cursor.col = 0; + tem_safe_align_cursor(tem); } static void -tem_lf(struct tem *tem, +tem_safe_lf(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int row; ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + MUTEX_HELD(&tem->tvs_lock)); /* * Sanity checking notes: * . a_nscroll was validated when it was set. - * . Regardless of that, tem_scroll and tem_mv_cursor will prevent - * anything bad from happening. + * . Regardless of that, tem_safe_scroll and tem_safe_mv_cursor + * will prevent anything bad from happening. */ - row = tems->a_c_cursor.row + 1; + row = tem->tvs_c_cursor.row + 1; - if (row >= tems->a_c_dimension.height) { - if (tems->a_nscroll != 0) { - tem_scroll(tem, 0, - tems->a_c_dimension.height - 1, - tems->a_nscroll, TEM_SCROLL_UP, + if (row >= tems.ts_c_dimension.height) { + if (tem->tvs_nscroll != 0) { + tem_safe_scroll(tem, 0, + tems.ts_c_dimension.height - 1, + tem->tvs_nscroll, TEM_SCROLL_UP, credp, called_from); - row = tems->a_c_dimension.height - - tems->a_nscroll; + row = tems.ts_c_dimension.height - + tem->tvs_nscroll; } else { /* no scroll */ /* * implement Esc[#r when # is zero. This means no @@ -876,61 +907,58 @@ tem_lf(struct tem *tem, } } - tem_mv_cursor(tem, row, tems->a_c_cursor.col, + tem_safe_mv_cursor(tem, row, tem->tvs_c_cursor.col, credp, called_from); - if (tems->a_nscroll == 0) { + if (tem->tvs_nscroll == 0) { /* erase rest of cursor line */ - tem_clear_chars(tem, - tems->a_c_dimension.width - - tems->a_c_cursor.col, - tems->a_c_cursor.row, - tems->a_c_cursor.col, + tem_safe_clear_chars(tem, + tems.ts_c_dimension.width - + tem->tvs_c_cursor.col, + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, credp, called_from); } - tem_align_cursor(tem); + tem_safe_align_cursor(tem); } static void -tem_send_data(struct tem *tem, cred_t *credp, +tem_safe_send_data(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; text_color_t fg_color; text_color_t bg_color; ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + MUTEX_HELD(&tem->tvs_lock)); - if (tems->a_outindex != 0) { + if (tem->tvs_outindex == 0) { + tem_safe_align_cursor(tem); + return; + } - if (tems->a_flags & TEM_ATTR_REVERSE) { - fg_color = ansi_fg_to_solaris(tem, - tems->bg_color); - bg_color = ansi_bg_to_solaris(tem, - tems->fg_color); - } else { - fg_color = ansi_fg_to_solaris(tem, - tems->fg_color); - bg_color = ansi_bg_to_solaris(tem, - tems->bg_color); - } + tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_REVERSE); + tem_safe_virtual_display(tem, + tem->tvs_outbuf, tem->tvs_outindex, + tem->tvs_s_cursor.row, tem->tvs_s_cursor.col, + fg_color, bg_color); + if (tem->tvs_isactive) { /* * Call the primitive to render this data. */ - (*tems->in_fp.f_display)(tem, - tems->a_outbuf, - tems->a_outindex, - tems->a_s_cursor.row, - tems->a_s_cursor.col, + tem_safe_callback_display(tem, + tem->tvs_outbuf, tem->tvs_outindex, + tem->tvs_s_cursor.row, tem->tvs_s_cursor.col, fg_color, bg_color, credp, called_from); - tems->a_outindex = 0; } - tem_align_cursor(tem); + + tem->tvs_outindex = 0; + + tem_safe_align_cursor(tem); } @@ -939,52 +967,51 @@ tem_send_data(struct tem *tem, cred_t *credp, * point for the buffered data in a_outbuf. There shouldn't be any data * buffered yet. */ -void -tem_align_cursor(struct tem *tem) +static void +tem_safe_align_cursor(struct tem_vt_state *tem) { - tem->state->a_s_cursor.row = tem->state->a_c_cursor.row; - tem->state->a_s_cursor.col = tem->state->a_c_cursor.col; + tem->tvs_s_cursor.row = tem->tvs_c_cursor.row; + tem->tvs_s_cursor.col = tem->tvs_c_cursor.col; } - - /* * State machine parser based on the current state and character input * major terminations are to control character or normal character */ static void -tem_parse(struct tem *tem, uchar_t ch, +tem_safe_parse(struct tem_vt_state *tem, uchar_t ch, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int i; ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + MUTEX_HELD(&tem->tvs_lock)); - if (tems->a_state == A_STATE_START) { /* Normal state? */ - if (ch == A_CSI || ch == A_ESC || ch < ' ') /* Control? */ - tem_control(tem, ch, credp, called_from); - else + if (tem->tvs_state == A_STATE_START) { /* Normal state? */ + if (ch == A_CSI || ch == A_ESC || ch < ' ') { + /* Control */ + tem_safe_control(tem, ch, credp, called_from); + } else { /* Display */ - tem_outch(tem, ch, credp, called_from); + tem_safe_outch(tem, ch, credp, called_from); + } return; } /* In <ESC> sequence */ - if (tems->a_state != A_STATE_ESC) { /* Need to get parameters? */ - if (tems->a_state != A_STATE_CSI) { - tem_getparams(tem, ch, credp, called_from); + if (tem->tvs_state != A_STATE_ESC) { /* Need to get parameters? */ + if (tem->tvs_state != A_STATE_CSI) { + tem_safe_getparams(tem, ch, credp, called_from); return; } switch (ch) { case '?': - tems->a_state = A_STATE_CSI_QMARK; + tem->tvs_state = A_STATE_CSI_QMARK; return; case '=': - tems->a_state = A_STATE_CSI_EQUAL; + tem->tvs_state = A_STATE_CSI_EQUAL; return; case 's': /* @@ -1003,120 +1030,120 @@ tem_parse(struct tem *tem, uchar_t ch, /* * Original code - * tems->a_r_cursor.row = tems->a_c_cursor.row; - * tems->a_r_cursor.col = tems->a_c_cursor.col; - * tems->a_state = A_STATE_START; + * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row; + * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col; + * tem->tvs_state = A_STATE_START; */ - tems->a_state = A_STATE_START; + tem->tvs_state = A_STATE_START; return; case 'u': - tem_mv_cursor(tem, tems->a_r_cursor.row, - tems->a_r_cursor.col, credp, called_from); - tems->a_state = A_STATE_START; + tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row, + tem->tvs_r_cursor.col, credp, called_from); + tem->tvs_state = A_STATE_START; return; case 'p': /* sunbow */ - tem_send_data(tem, credp, called_from); + tem_safe_send_data(tem, credp, called_from); /* * Don't set anything if we are * already as we want to be. */ - if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { - tems->a_flags &= ~TEM_ATTR_SCREEN_REVERSE; + if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) { + tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE; /* * If we have switched the characters to be the * inverse from the screen, then switch them as * well to keep them the inverse of the screen. */ - if (tems->a_flags & TEM_ATTR_REVERSE) { - tems->a_flags &= ~TEM_ATTR_REVERSE; - } else { - tems->a_flags |= TEM_ATTR_REVERSE; - } + if (tem->tvs_flags & TEM_ATTR_REVERSE) + tem->tvs_flags &= ~TEM_ATTR_REVERSE; + else + tem->tvs_flags |= TEM_ATTR_REVERSE; } - tem_cls(tem, credp, called_from); - tems->a_state = A_STATE_START; + tem_safe_cls(tem, credp, called_from); + tem->tvs_state = A_STATE_START; return; case 'q': /* sunwob */ - tem_send_data(tem, credp, called_from); + tem_safe_send_data(tem, credp, called_from); /* * Don't set anything if we are * already where as we want to be. */ - if (!(tems->a_flags & TEM_ATTR_SCREEN_REVERSE)) { - tems->a_flags |= TEM_ATTR_SCREEN_REVERSE; + if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) { + tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE; /* * If we have switched the characters to be the * inverse from the screen, then switch them as * well to keep them the inverse of the screen. */ - if (!(tems->a_flags & TEM_ATTR_REVERSE)) { - tems->a_flags |= TEM_ATTR_REVERSE; - } else { - tems->a_flags &= ~TEM_ATTR_REVERSE; - } + if (!(tem->tvs_flags & TEM_ATTR_REVERSE)) + tem->tvs_flags |= TEM_ATTR_REVERSE; + else + tem->tvs_flags &= ~TEM_ATTR_REVERSE; } - tem_cls(tem, credp, called_from); - tems->a_state = A_STATE_START; + tem_safe_cls(tem, credp, called_from); + tem->tvs_state = A_STATE_START; return; case 'r': /* sunscrl */ /* * Rule exception: check for validity here. */ - tems->a_nscroll = tems->a_paramval; - if (tems->a_nscroll > tems->a_c_dimension.height) - tems->a_nscroll = tems->a_c_dimension.height; - if (tems->a_nscroll < 0) - tems->a_nscroll = 1; - tems->a_state = A_STATE_START; + tem->tvs_nscroll = tem->tvs_paramval; + if (tem->tvs_nscroll > tems.ts_c_dimension.height) + tem->tvs_nscroll = tems.ts_c_dimension.height; + if (tem->tvs_nscroll < 0) + tem->tvs_nscroll = 1; + tem->tvs_state = A_STATE_START; return; default: - tem_getparams(tem, ch, credp, called_from); + tem_safe_getparams(tem, ch, credp, called_from); return; } } /* Previous char was <ESC> */ if (ch == '[') { - tems->a_curparam = 0; - tems->a_paramval = 0; - tems->a_gotparam = B_FALSE; + tem->tvs_curparam = 0; + tem->tvs_paramval = 0; + tem->tvs_gotparam = B_FALSE; /* clear the parameters */ for (i = 0; i < TEM_MAXPARAMS; i++) - tems->a_params[i] = -1; - tems->a_state = A_STATE_CSI; + tem->tvs_params[i] = -1; + tem->tvs_state = A_STATE_CSI; } else if (ch == 'Q') { /* <ESC>Q ? */ - tems->a_state = A_STATE_START; + tem->tvs_state = A_STATE_START; } else if (ch == 'C') { /* <ESC>C ? */ - tems->a_state = A_STATE_START; + tem->tvs_state = A_STATE_START; } else { - tems->a_state = A_STATE_START; - if (ch == 'c') + tem->tvs_state = A_STATE_START; + if (ch == 'c') { /* ESC c resets display */ - tem_reset_display(tem, credp, called_from, 1, NULL); - else if (ch == 'H') + tem_safe_reset_display(tem, credp, called_from, + B_TRUE, B_TRUE); + } else if (ch == 'H') { /* ESC H sets a tab */ - tem_set_tab(tem); - else if (ch == '7') { + tem_safe_set_tab(tem); + } else if (ch == '7') { /* ESC 7 Save Cursor position */ - tems->a_r_cursor.row = tems->a_c_cursor.row; - tems->a_r_cursor.col = tems->a_c_cursor.col; - } else if (ch == '8') + tem->tvs_r_cursor.row = tem->tvs_c_cursor.row; + tem->tvs_r_cursor.col = tem->tvs_c_cursor.col; + } else if (ch == '8') { /* ESC 8 Restore Cursor position */ - tem_mv_cursor(tem, tems->a_r_cursor.row, - tems->a_r_cursor.col, credp, called_from); + tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row, + tem->tvs_r_cursor.col, credp, called_from); /* check for control chars */ - else if (ch < ' ') - tem_control(tem, ch, credp, called_from); - else - tem_outch(tem, ch, credp, called_from); + } else if (ch < ' ') { + tem_safe_control(tem, ch, credp, called_from); + } else { + tem_safe_outch(tem, ch, credp, called_from); + } } } /* ARGSUSED */ static void -tem_bell(struct tem *tem, enum called_from called_from) +tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from) { if (called_from == CALLED_FROM_STANDALONE) (void) beep_polled(BEEP_CONSOLE); @@ -1126,15 +1153,15 @@ tem_bell(struct tem *tem, enum called_from called_from) static void -tem_scroll(tem_t *tem, int start, int end, int count, int direction, +tem_safe_scroll(struct tem_vt_state *tem, int start, int end, int count, + int direction, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int row; int lines_affected; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); lines_affected = end - start + 1; if (count > lines_affected) @@ -1145,25 +1172,25 @@ tem_scroll(tem_t *tem, int start, int end, int count, int direction, switch (direction) { case TEM_SCROLL_UP: if (count < lines_affected) { - tem_copy_area(tem, 0, start + count, - tems->a_c_dimension.width - 1, end, + tem_safe_copy_area(tem, 0, start + count, + tems.ts_c_dimension.width - 1, end, 0, start, credp, called_from); } for (row = (end - count) + 1; row <= end; row++) { - tem_clear_chars(tem, tems->a_c_dimension.width, + tem_safe_clear_chars(tem, tems.ts_c_dimension.width, row, 0, credp, called_from); } break; case TEM_SCROLL_DOWN: if (count < lines_affected) { - tem_copy_area(tem, 0, start, - tems->a_c_dimension.width - 1, + tem_safe_copy_area(tem, 0, start, + tems.ts_c_dimension.width - 1, end - count, 0, start + count, credp, called_from); } for (row = start; row < start + count; row++) { - tem_clear_chars(tem, tems->a_c_dimension.width, + tem_safe_clear_chars(tem, tems.ts_c_dimension.width, row, 0, credp, called_from); } break; @@ -1171,28 +1198,27 @@ tem_scroll(tem_t *tem, int start, int end, int count, int direction, } static void -tem_copy_area(struct tem *tem, +tem_safe_copy_area(struct tem_vt_state *tem, screen_pos_t s_col, screen_pos_t s_row, screen_pos_t e_col, screen_pos_t e_row, screen_pos_t t_col, screen_pos_t t_row, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int rows; int cols; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); if (s_col < 0 || s_row < 0 || e_col < 0 || e_row < 0 || t_col < 0 || t_row < 0 || - s_col >= tems->a_c_dimension.width || - e_col >= tems->a_c_dimension.width || - t_col >= tems->a_c_dimension.width || - s_row >= tems->a_c_dimension.height || - e_row >= tems->a_c_dimension.height || - t_row >= tems->a_c_dimension.height) + s_col >= tems.ts_c_dimension.width || + e_col >= tems.ts_c_dimension.width || + t_col >= tems.ts_c_dimension.width || + s_row >= tems.ts_c_dimension.height || + e_row >= tems.ts_c_dimension.height || + t_row >= tems.ts_c_dimension.height) return; if (s_row > e_row || s_col > e_col) @@ -1200,25 +1226,31 @@ tem_copy_area(struct tem *tem, rows = e_row - s_row + 1; cols = e_col - s_col + 1; - if (t_row + rows > tems->a_c_dimension.height || - t_col + cols > tems->a_c_dimension.width) + if (t_row + rows > tems.ts_c_dimension.height || + t_col + cols > tems.ts_c_dimension.width) + return; + + tem_safe_virtual_copy(tem, + s_col, s_row, + e_col, e_row, + t_col, t_row); + + if (!tem->tvs_isactive) return; - (*tems->in_fp.f_copy)(tem, s_col, s_row, + tem_safe_callback_copy(tem, s_col, s_row, e_col, e_row, t_col, t_row, credp, called_from); } static void -tem_clear_chars(struct tem *tem, int count, screen_pos_t row, +tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row, screen_pos_t col, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); - - if (row < 0 || row >= tems->a_c_dimension.height || - col < 0 || col >= tems->a_c_dimension.width || + if (row < 0 || row >= tems.ts_c_dimension.height || + col < 0 || col >= tems.ts_c_dimension.width || count < 0) return; @@ -1226,30 +1258,39 @@ tem_clear_chars(struct tem *tem, int count, screen_pos_t row, * Note that very large values of "count" could cause col+count * to overflow, so we check "count" independently. */ - if (count > tems->a_c_dimension.width || - col + count > tems->a_c_dimension.width) - count = tems->a_c_dimension.width - col; + if (count > tems.ts_c_dimension.width || + col + count > tems.ts_c_dimension.width) + count = tems.ts_c_dimension.width - col; - (*tems->in_fp.f_cls)(tem, count, row, col, credp, called_from); + tem_safe_virtual_cls(tem, count, row, col); + + if (!tem->tvs_isactive) + return; + + tem_safe_callback_cls(tem, count, row, col, credp, called_from); } +/*ARGSUSED*/ void -tem_text_display(struct tem *tem, uchar_t *string, +tem_safe_text_display(struct tem_vt_state *tem, uchar_t *string, int count, screen_pos_t row, screen_pos_t col, text_color_t fg_color, text_color_t bg_color, cred_t *credp, enum called_from called_from) { struct vis_consdisplay da; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + da.data = string; - da.width = count; + da.width = (screen_size_t)count; da.row = row; da.col = col; da.fg_color = fg_color; da.bg_color = bg_color; - tem_display(tem, &da, credp, called_from); + tems_safe_display(&da, credp, called_from); } /* @@ -1262,25 +1303,33 @@ tem_text_display(struct tem *tem, uchar_t *string, * * This function is unused now. */ -void -tem_image_display(struct tem *tem, uchar_t *image, +/*ARGSUSED*/ +static void +tem_safe_image_display(struct tem_vt_state *tem, uchar_t *image, int height, int width, screen_pos_t row, screen_pos_t col, cred_t *credp, enum called_from called_from) { struct vis_consdisplay da; + mutex_enter(&tems.ts_lock); + mutex_enter(&tem->tvs_lock); + da.data = image; - da.width = width; - da.height = height; + da.width = (screen_size_t)width; + da.height = (screen_size_t)height; da.row = row; da.col = col; - tem_display(tem, &da, credp, called_from); + tems_safe_display(&da, credp, called_from); + + mutex_exit(&tem->tvs_lock); + mutex_exit(&tems.ts_lock); } +/*ARGSUSED*/ void -tem_text_copy(struct tem *tem, +tem_safe_text_copy(struct tem_vt_state *tem, screen_pos_t s_col, screen_pos_t s_row, screen_pos_t e_col, screen_pos_t e_row, screen_pos_t t_col, screen_pos_t t_row, @@ -1288,6 +1337,9 @@ tem_text_copy(struct tem *tem, { struct vis_conscopy da; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + da.s_row = s_row; da.s_col = s_col; da.e_row = e_row; @@ -1295,131 +1347,158 @@ tem_text_copy(struct tem *tem, da.t_row = t_row; da.t_col = t_col; - tem_copy(tem, &da, credp, called_from); + tems_safe_copy(&da, credp, called_from); } void -tem_text_cls(struct tem *tem, +tem_safe_text_cls(struct tem_vt_state *tem, int count, screen_pos_t row, screen_pos_t col, cred_t *credp, enum called_from called_from) { struct vis_consdisplay da; - da.data = tem->state->a_blank_line; - da.width = count; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + da.data = tems.ts_blank_line; + da.width = (screen_size_t)count; da.row = row; da.col = col; - tem_get_color(tem, &da.fg_color, &da.bg_color); - tem_display(tem, &da, credp, called_from); + tem_safe_get_color(tem, &da.fg_color, &da.bg_color, + TEM_ATTR_SCREEN_REVERSE); + tems_safe_display(&da, credp, called_from); } void -tem_pix_display(struct tem *tem, +tem_safe_pix_display(struct tem_vt_state *tem, uchar_t *string, int count, screen_pos_t row, screen_pos_t col, text_color_t fg_color, text_color_t bg_color, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_consdisplay da; int i; - da.data = (uchar_t *)tems->a_pix_data; - da.width = tems->a_font.width; - da.height = tems->a_font.height; - da.row = (row * da.height) + tems->a_p_offset.y; - da.col = (col * da.width) + tems->a_p_offset.x; + + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + da.data = (uchar_t *)tem->tvs_pix_data; + da.width = tems.ts_font.width; + da.height = tems.ts_font.height; + da.row = (row * da.height) + tems.ts_p_offset.y; + da.col = (col * da.width) + tems.ts_p_offset.x; for (i = 0; i < count; i++) { - BIT_TO_PIX(tem, string[i], fg_color, bg_color); - tem_display(tem, &da, credp, called_from); + tem_safe_callback_bit2pix(tem, string[i], fg_color, bg_color); + tems_safe_display(&da, credp, called_from); da.col += da.width; } } void -tem_pix_copy(struct tem *tem, +tem_safe_pix_copy(struct tem_vt_state *tem, screen_pos_t s_col, screen_pos_t s_row, screen_pos_t e_col, screen_pos_t e_row, screen_pos_t t_col, screen_pos_t t_row, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_conscopy ma; static boolean_t need_clear = B_TRUE; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - if (need_clear && tems->first_line > 0) { + if (need_clear && tem->tvs_first_line > 0) { /* * Clear OBP output above our kernel console term * when our kernel console term begins to scroll up, * we hope it is user friendly. - * (Also see comments on tem_pix_clear_prom_output) + * (Also see comments on tem_safe_pix_clear_prom_output) * * This is only one time call. */ - tem_pix_clear_prom_output(tem, credp, called_from); + tem_safe_pix_clear_prom_output(tem, credp, called_from); } need_clear = B_FALSE; - ma.s_row = s_row * tems->a_font.height + tems->a_p_offset.y; - ma.e_row = (e_row + 1) * tems->a_font.height + tems->a_p_offset.y - 1; - ma.t_row = t_row * tems->a_font.height + tems->a_p_offset.y; + ma.s_row = s_row * tems.ts_font.height + tems.ts_p_offset.y; + ma.e_row = (e_row + 1) * tems.ts_font.height + tems.ts_p_offset.y - 1; + ma.t_row = t_row * tems.ts_font.height + tems.ts_p_offset.y; /* * Check if we're in process of clearing OBP's columns area, * which only happens when term scrolls up a whole line. */ - if (tems->first_line > 0 && t_row < s_row && t_col == 0 && - e_col == tems->a_c_dimension.width - 1) { + if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 && + e_col == tems.ts_c_dimension.width - 1) { /* * We need to clear OBP's columns area outside our kernel * console term. So that we set ma.e_col to entire row here. */ - ma.s_col = s_col * tems->a_font.width; - ma.e_col = tems->a_p_dimension.width - 1; + ma.s_col = s_col * tems.ts_font.width; + ma.e_col = tems.ts_p_dimension.width - 1; - ma.t_col = t_col * tems->a_font.width; + ma.t_col = t_col * tems.ts_font.width; } else { - ma.s_col = s_col * tems->a_font.width + tems->a_p_offset.x; - ma.e_col = (e_col + 1) * tems->a_font.width + - tems->a_p_offset.x - 1; - ma.t_col = t_col * tems->a_font.width + tems->a_p_offset.x; + ma.s_col = s_col * tems.ts_font.width + tems.ts_p_offset.x; + ma.e_col = (e_col + 1) * tems.ts_font.width + + tems.ts_p_offset.x - 1; + ma.t_col = t_col * tems.ts_font.width + tems.ts_p_offset.x; } - tem_copy(tem, &ma, credp, called_from); + tems_safe_copy(&ma, credp, called_from); - if (tems->first_line > 0 && t_row < s_row) { + if (tem->tvs_first_line > 0 && t_row < s_row) { /* We have scrolled up (s_row - t_row) rows. */ - tems->first_line -= (s_row - t_row); - if (tems->first_line <= 0) { + tem->tvs_first_line -= (s_row - t_row); + if (tem->tvs_first_line <= 0) { /* All OBP rows have been cleared. */ - tems->first_line = 0; + tem->tvs_first_line = 0; } } } +void +tem_safe_pix_bit2pix(struct tem_vt_state *tem, unsigned char c, + unsigned char fg, unsigned char bg) +{ + void (*fp)(struct tem_vt_state *, unsigned char, + unsigned char, unsigned char); + + switch (tems.ts_pdepth) { + case 4: + fp = bit_to_pix4; + break; + case 8: + fp = bit_to_pix8; + break; + case 24: + case 32: + fp = bit_to_pix24; + } + + fp(tem, c, fg, bg); +} + + /* * This function only clears count of columns in one row */ void -tem_pix_cls(struct tem *tem, int count, +tem_safe_pix_cls(struct tem_vt_state *tem, int count, screen_pos_t row, screen_pos_t col, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); - - tem_pix_cls_range(tem, row, 1, tems->a_p_offset.y, - col, count, tems->a_p_offset.x, B_FALSE, credp, called_from); + tem_safe_pix_cls_range(tem, row, 1, tems.ts_p_offset.y, + col, count, tems.ts_p_offset.x, B_FALSE, credp, called_from); } /* @@ -1446,182 +1525,182 @@ tem_pix_cls(struct tem *tem, int count, * console term. */ static void -tem_pix_clear_prom_output(struct tem *tem, cred_t *credp, +tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int nrows, ncols, width, height; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - width = tems->a_font.width; - height = tems->a_font.height; + width = tems.ts_font.width; + height = tems.ts_font.height; - nrows = (tems->a_p_offset.y + (height - 1))/ height; - ncols = (tems->a_p_dimension.width + (width - 1))/ width; + nrows = (tems.ts_p_offset.y + (height - 1))/ height; + ncols = (tems.ts_p_dimension.width + (width - 1))/ width; - tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, + tem_safe_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, B_FALSE, credp, called_from); } /* - * clear the whole screen for pixel mode + * clear the whole screen for pixel mode, just clear the + * physical screen. */ -static void -tem_pix_clear_entire_screen(struct tem *tem, cred_t *credp, +void +tem_safe_pix_clear_entire_screen(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int nrows, ncols, width, height; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - width = tems->a_font.width; - height = tems->a_font.height; + width = tems.ts_font.width; + height = tems.ts_font.height; - nrows = (tems->a_p_dimension.height + (height - 1))/ height; - ncols = (tems->a_p_dimension.width + (width - 1))/ width; + nrows = (tems.ts_p_dimension.height + (height - 1))/ height; + ncols = (tems.ts_p_dimension.width + (width - 1))/ width; - tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, + tem_safe_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, B_FALSE, credp, called_from); - tems->a_c_cursor.row = 0; - tems->a_c_cursor.col = 0; - tem_align_cursor(tem); - /* * Since the whole screen is cleared, we don't need * to clear OBP output later. */ - if (tems->first_line > 0) { - tems->first_line = 0; - } + if (tem->tvs_first_line > 0) + tem->tvs_first_line = 0; } /* - * clear the whole screen + * clear the whole screen, including the virtual screen buffer, + * and reset the cursor to start point. */ static void -tem_cls(struct tem *tem, +tem_safe_cls(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int row; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - if (tems->display_mode == VIS_TEXT) { - for (row = 0; row < tems->a_c_dimension.height; row++) { - tem_clear_chars(tem, tems->a_c_dimension.width, + if (tems.ts_display_mode == VIS_TEXT) { + for (row = 0; row < tems.ts_c_dimension.height; row++) { + tem_safe_clear_chars(tem, tems.ts_c_dimension.width, row, 0, credp, called_from); } - tems->a_c_cursor.row = 0; - tems->a_c_cursor.col = 0; - tem_align_cursor(tem); + tem->tvs_c_cursor.row = 0; + tem->tvs_c_cursor.col = 0; + tem_safe_align_cursor(tem); return; } - ASSERT(tems->display_mode == VIS_PIXEL); + ASSERT(tems.ts_display_mode == VIS_PIXEL); + + for (row = 0; row < tems.ts_c_dimension.height; row++) { + tem_safe_virtual_cls(tem, tems.ts_c_dimension.width, row, 0); + } + tem->tvs_c_cursor.row = 0; + tem->tvs_c_cursor.col = 0; + tem_safe_align_cursor(tem); + + if (!tem->tvs_isactive) + return; - tem_pix_clear_entire_screen(tem, credp, called_from); + tem_safe_pix_clear_entire_screen(tem, credp, called_from); } static void -tem_back_tab(struct tem *tem, +tem_safe_back_tab(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int i; screen_pos_t tabstop; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); tabstop = 0; - for (i = tems->a_ntabs - 1; i >= 0; i--) { - if (tems->a_tabs[i] < tems->a_c_cursor.col) { - tabstop = tems->a_tabs[i]; + for (i = tem->tvs_ntabs - 1; i >= 0; i--) { + if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) { + tabstop = tem->tvs_tabs[i]; break; } } - tem_mv_cursor(tem, tems->a_c_cursor.row, + tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop, credp, called_from); } static void -tem_tab(struct tem *tem, +tem_safe_tab(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int i; screen_pos_t tabstop; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - tabstop = tems->a_c_dimension.width - 1; + tabstop = tems.ts_c_dimension.width - 1; - for (i = 0; i < tems->a_ntabs; i++) { - if (tems->a_tabs[i] > tems->a_c_cursor.col) { - tabstop = tems->a_tabs[i]; + for (i = 0; i < tem->tvs_ntabs; i++) { + if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) { + tabstop = tem->tvs_tabs[i]; break; } } - tem_mv_cursor(tem, tems->a_c_cursor.row, + tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop, credp, called_from); } static void -tem_set_tab(struct tem *tem) +tem_safe_set_tab(struct tem_vt_state *tem) { - struct tem_state *tems = tem->state; int i; int j; - if (tems->a_ntabs == TEM_MAXTAB) - return; - if (tems->a_ntabs == 0 || - tems->a_tabs[tems->a_ntabs] < tems->a_c_cursor.col) { - tems->a_tabs[tems->a_ntabs++] = tems->a_c_cursor.col; + if (tem->tvs_ntabs == TEM_MAXTAB) return; + if (tem->tvs_ntabs == 0 || + tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) { + tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col; + return; } - for (i = 0; i < tems->a_ntabs; i++) { - if (tems->a_tabs[i] == tems->a_c_cursor.col) + for (i = 0; i < tem->tvs_ntabs; i++) { + if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) return; - if (tems->a_tabs[i] > tems->a_c_cursor.col) { - for (j = tems->a_ntabs - 1; j >= i; j--) - tems->a_tabs[j+ 1] = tems->a_tabs[j]; - tems->a_tabs[i] = tems->a_c_cursor.col; - tems->a_ntabs++; + if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) { + for (j = tem->tvs_ntabs - 1; j >= i; j--) + tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j]; + tem->tvs_tabs[i] = tem->tvs_c_cursor.col; + tem->tvs_ntabs++; return; } } } static void -tem_clear_tabs(struct tem *tem, int action) +tem_safe_clear_tabs(struct tem_vt_state *tem, int action) { - struct tem_state *tems = tem->state; int i; int j; switch (action) { case 3: /* clear all tabs */ - tems->a_ntabs = 0; + tem->tvs_ntabs = 0; break; case 0: /* clr tab at cursor */ - for (i = 0; i < tems->a_ntabs; i++) { - if (tems->a_tabs[i] == tems->a_c_cursor.col) { - tems->a_ntabs--; - for (j = i; j < tems->a_ntabs; j++) - tems->a_tabs[j] = tems->a_tabs[j + 1]; + for (i = 0; i < tem->tvs_ntabs; i++) { + if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) { + tem->tvs_ntabs--; + for (j = i; j < tem->tvs_ntabs; j++) + tem->tvs_tabs[j] = tem->tvs_tabs[j + 1]; return; } } @@ -1630,13 +1709,11 @@ tem_clear_tabs(struct tem *tem, int action) } static void -tem_mv_cursor(struct tem *tem, int row, int col, +tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; - - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); /* * Sanity check and bounds enforcement. Out of bounds requests are @@ -1645,109 +1722,98 @@ tem_mv_cursor(struct tem *tem, int row, int col, */ if (row < 0) row = 0; - if (row >= tems->a_c_dimension.height) - row = tems->a_c_dimension.height - 1; + if (row >= tems.ts_c_dimension.height) + row = tems.ts_c_dimension.height - 1; if (col < 0) col = 0; - if (col >= tems->a_c_dimension.width) - col = tems->a_c_dimension.width - 1; + if (col >= tems.ts_c_dimension.width) + col = tems.ts_c_dimension.width - 1; - tem_send_data(tem, credp, called_from); - tems->a_c_cursor.row = row; - tems->a_c_cursor.col = col; - tem_align_cursor(tem); + tem_safe_send_data(tem, credp, called_from); + tem->tvs_c_cursor.row = (screen_pos_t)row; + tem->tvs_c_cursor.col = (screen_pos_t)col; + tem_safe_align_cursor(tem); } /* ARGSUSED */ void -tem_reset_emulator(struct tem *tem, +tem_safe_reset_emulator(struct tem_vt_state *tem, cred_t *credp, enum called_from called_from, - tem_color_t *pcolor) + boolean_t init_color) { - struct tem_state *tems = tem->state; int j; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); - - tems->a_c_cursor.row = 0; - tems->a_c_cursor.col = 0; - tems->a_r_cursor.row = 0; - tems->a_r_cursor.col = 0; - tems->a_s_cursor.row = 0; - tems->a_s_cursor.col = 0; - tems->a_outindex = 0; - tems->a_state = A_STATE_START; - tems->a_gotparam = B_FALSE; - tems->a_curparam = 0; - tems->a_paramval = 0; - tems->a_nscroll = 1; - - if (pcolor != NULL) { - /* use customized settings */ - tems->fg_color = pcolor->fg_color; - tems->bg_color = pcolor->bg_color; - tems->a_flags = pcolor->a_flags; - } else { + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + tem->tvs_c_cursor.row = 0; + tem->tvs_c_cursor.col = 0; + tem->tvs_r_cursor.row = 0; + tem->tvs_r_cursor.col = 0; + tem->tvs_s_cursor.row = 0; + tem->tvs_s_cursor.col = 0; + tem->tvs_outindex = 0; + tem->tvs_state = A_STATE_START; + tem->tvs_gotparam = B_FALSE; + tem->tvs_curparam = 0; + tem->tvs_paramval = 0; + tem->tvs_nscroll = 1; + + if (init_color) { /* use initial settings */ - tems->fg_color = tem->init_color.fg_color; - tems->bg_color = tem->init_color.bg_color; - tems->a_flags = tem->init_color.a_flags; + tem->tvs_fg_color = tems.ts_init_color.fg_color; + tem->tvs_bg_color = tems.ts_init_color.bg_color; + tem->tvs_flags = tems.ts_init_color.a_flags; } /* * set up the initial tab stops */ - tems->a_ntabs = 0; - for (j = 8; j < tems->a_c_dimension.width; j += 8) - tems->a_tabs[tems->a_ntabs++] = (screen_pos_t)j; + tem->tvs_ntabs = 0; + for (j = 8; j < tems.ts_c_dimension.width; j += 8) + tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j; for (j = 0; j < TEM_MAXPARAMS; j++) - tems->a_params[j] = 0; + tem->tvs_params[j] = 0; } void -tem_reset_display(struct tem *tem, - cred_t *credp, enum called_from called_from, int clear_txt, - tem_color_t *pcolor) +tem_safe_reset_display(struct tem_vt_state *tem, + cred_t *credp, enum called_from called_from, + boolean_t clear_txt, boolean_t init_color) { - struct tem_state *tems = tem->state; - - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - tem_reset_emulator(tem, credp, called_from, pcolor); - tem_reset_colormap(tem, credp, called_from); + tem_safe_reset_emulator(tem, credp, called_from, init_color); if (clear_txt) { - (*tems->in_fp.f_cursor)(tem, - VIS_HIDE_CURSOR, credp, called_from); + if (tem->tvs_isactive) + tem_safe_callback_cursor(tem, + VIS_HIDE_CURSOR, credp, called_from); - tem_cls(tem, credp, called_from); + tem_safe_cls(tem, credp, called_from); - (*tems->in_fp.f_cursor)(tem, - VIS_DISPLAY_CURSOR, credp, called_from); + if (tem->tvs_isactive) + tem_safe_callback_cursor(tem, + VIS_DISPLAY_CURSOR, credp, called_from); } - - tems->a_initialized = 1; } - static void -tem_shift( - struct tem *tem, +tem_safe_shift( + struct tem_vt_state *tem, int count, int direction, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; int rest_of_line; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - rest_of_line = tems->a_c_dimension.width - tems->a_c_cursor.col; + rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col; if (count > rest_of_line) count = rest_of_line; @@ -1757,87 +1823,84 @@ tem_shift( switch (direction) { case TEM_SHIFT_LEFT: if (count < rest_of_line) { - tem_copy_area(tem, - tems->a_c_cursor.col + count, - tems->a_c_cursor.row, - tems->a_c_dimension.width - 1, - tems->a_c_cursor.row, - tems->a_c_cursor.col, - tems->a_c_cursor.row, + tem_safe_copy_area(tem, + tem->tvs_c_cursor.col + count, + tem->tvs_c_cursor.row, + tems.ts_c_dimension.width - 1, + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, + tem->tvs_c_cursor.row, credp, called_from); } - tem_clear_chars(tem, count, tems->a_c_cursor.row, - (tems->a_c_dimension.width - count), credp, + tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row, + (tems.ts_c_dimension.width - count), credp, called_from); break; case TEM_SHIFT_RIGHT: if (count < rest_of_line) { - tem_copy_area(tem, - tems->a_c_cursor.col, - tems->a_c_cursor.row, - tems->a_c_dimension.width - count - 1, - tems->a_c_cursor.row, - tems->a_c_cursor.col + count, - tems->a_c_cursor.row, + tem_safe_copy_area(tem, + tem->tvs_c_cursor.col, + tem->tvs_c_cursor.row, + tems.ts_c_dimension.width - count - 1, + tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col + count, + tem->tvs_c_cursor.row, credp, called_from); } - tem_clear_chars(tem, count, tems->a_c_cursor.row, - tems->a_c_cursor.col, credp, called_from); + tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row, + tem->tvs_c_cursor.col, credp, called_from); break; } } void -tem_text_cursor(struct tem *tem, short action, +tem_safe_text_cursor(struct tem_vt_state *tem, short action, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_conscursor ca; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); - ca.row = tems->a_c_cursor.row; - ca.col = tems->a_c_cursor.col; + ca.row = tem->tvs_c_cursor.row; + ca.col = tem->tvs_c_cursor.col; ca.action = action; - tem_cursor(tem, &ca, credp, called_from); + tems_safe_cursor(&ca, credp, called_from); if (action == VIS_GET_CURSOR) { - tems->a_c_cursor.row = ca.row; - tems->a_c_cursor.col = ca.col; + tem->tvs_c_cursor.row = ca.row; + tem->tvs_c_cursor.col = ca.col; } } - void -tem_pix_cursor(struct tem *tem, short action, +tem_safe_pix_cursor(struct tem_vt_state *tem, short action, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_conscursor ca; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); - - ca.row = tems->a_c_cursor.row * tems->a_font.height + - tems->a_p_offset.y; - ca.col = tems->a_c_cursor.col * tems->a_font.width + - tems->a_p_offset.x; - ca.width = tems->a_font.width; - ca.height = tems->a_font.height; - if (tems->a_pdepth == 8 || tems->a_pdepth == 4) { - if (tems->a_flags & TEM_ATTR_REVERSE) { + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + ca.row = tem->tvs_c_cursor.row * tems.ts_font.height + + tems.ts_p_offset.y; + ca.col = tem->tvs_c_cursor.col * tems.ts_font.width + + tems.ts_p_offset.x; + ca.width = tems.ts_font.width; + ca.height = tems.ts_font.height; + if (tems.ts_pdepth == 8 || tems.ts_pdepth == 4) { + if (tem->tvs_flags & TEM_ATTR_REVERSE) { ca.fg_color.mono = TEM_TEXT_WHITE; ca.bg_color.mono = TEM_TEXT_BLACK; } else { ca.fg_color.mono = TEM_TEXT_BLACK; ca.bg_color.mono = TEM_TEXT_WHITE; } - } else if (tems->a_pdepth == 24 || tems->a_pdepth == 32) { - if (tems->a_flags & TEM_ATTR_REVERSE) { + } else if (tems.ts_pdepth == 24 || tems.ts_pdepth == 32) { + if (tem->tvs_flags & TEM_ATTR_REVERSE) { ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED; ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN; ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE; @@ -1858,7 +1921,7 @@ tem_pix_cursor(struct tem *tem, short action, ca.action = action; - tem_cursor(tem, &ca, credp, called_from); + tems_safe_cursor(&ca, credp, called_from); } #define BORDER_PIXELS 10 @@ -1916,14 +1979,13 @@ set_font(struct font *f, short *rows, short *cols, short height, short width) * 00000001 00000001 00000000 00010001. */ -void +static void bit_to_pix4( - struct tem *tem, + struct tem_vt_state *tem, uchar_t c, text_color_t fg_color, text_color_t bg_color) { - struct tem_state *tems = tem->state; int row; int byte; int i; @@ -1933,12 +1995,12 @@ bit_to_pix4( int bytes_wide; uint8_t *dest; - dest = (uint8_t *)tems->a_pix_data; + dest = (uint8_t *)tem->tvs_pix_data; - cp = tems->a_font.char_ptr[c]; - bytes_wide = (tems->a_font.width + 7) / 8; + cp = tems.ts_font.char_ptr[c]; + bytes_wide = (tems.ts_font.width + 7) / 8; - for (row = 0; row < tems->a_font.height; row++) { + for (row = 0; row < tems.ts_font.height; row++) { for (byte = 0; byte < bytes_wide; byte++) { data = *cp++; for (i = 0; i < 4; i++) { @@ -1972,14 +2034,13 @@ bit_to_pix4( * 0000000 000000001 00000000 00000001 00000000 00000000 00000001 00000001. */ -void +static void bit_to_pix8( - struct tem *tem, + struct tem_vt_state *tem, uchar_t c, text_color_t fg_color, text_color_t bg_color) { - struct tem_state *tems = tem->state; int row; int byte; int i; @@ -1990,13 +2051,13 @@ bit_to_pix8( int bitsleft, nbits; uint8_t *dest; - dest = (uint8_t *)tems->a_pix_data; + dest = (uint8_t *)tem->tvs_pix_data; - cp = tems->a_font.char_ptr[c]; - bytes_wide = (tems->a_font.width + 7) / 8; + cp = tems.ts_font.char_ptr[c]; + bytes_wide = (tems.ts_font.width + 7) / 8; - for (row = 0; row < tems->a_font.height; row++) { - bitsleft = tems->a_font.width; + for (row = 0; row < tems.ts_font.height; row++) { + bitsleft = tems.ts_font.width; for (byte = 0; byte < bytes_wide; byte++) { data = *cp++; mask = 0x80; @@ -2033,14 +2094,13 @@ bit_to_pix8( */ typedef uint32_t pixel32_t; -void +static void bit_to_pix24( - struct tem *tem, + struct tem_vt_state *tem, uchar_t c, text_color_t fg_color4, text_color_t bg_color4) { - struct tem_state *tems = tem->state; int row; int byte; int i; @@ -2056,12 +2116,12 @@ bit_to_pix24( fg_color32 = PIX4TO32(fg_color4); bg_color32 = PIX4TO32(bg_color4); - destp = (pixel32_t *)tems->a_pix_data; - cp = tems->a_font.char_ptr[c]; - bytes_wide = (tems->a_font.width + 7) / 8; + destp = (pixel32_t *)tem->tvs_pix_data; + cp = tems.ts_font.char_ptr[c]; + bytes_wide = (tems.ts_font.width + 7) / 8; - for (row = 0; row < tems->a_font.height; row++) { - bitsleft = tems->a_font.width; + for (row = 0; row < tems.ts_font.height; row++) { + bitsleft = tems.ts_font.width; for (byte = 0; byte < bytes_wide; byte++) { data = *cp++; nbits = MIN(8, bitsleft); @@ -2075,34 +2135,38 @@ bit_to_pix24( } /* ARGSUSED */ -text_color_t -ansi_bg_to_solaris(struct tem *tem, int ansi) +static text_color_t +ansi_bg_to_solaris(struct tem_vt_state *tem, int ansi) { return (bg_xlate[ansi]); } -text_color_t -ansi_fg_to_solaris(struct tem *tem, int ansi) +static text_color_t +ansi_fg_to_solaris(struct tem_vt_state *tem, int ansi) { - if (tem->state->a_flags & TEM_ATTR_BOLD) + if (tem->tvs_flags & TEM_ATTR_BOLD) return (fg_brt_xlate[ansi]); else return (fg_dim_xlate[ansi]); } -static void -tem_get_color(struct tem *tem, text_color_t *fg, text_color_t *bg) +/* + * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE + */ +void +tem_safe_get_color(struct tem_vt_state *tem, text_color_t *fg, + text_color_t *bg, uint8_t flag) { - if (tem->state->a_flags & TEM_ATTR_SCREEN_REVERSE) { + if (tem->tvs_flags & flag) { *fg = ansi_fg_to_solaris(tem, - DEFAULT_ANSI_BACKGROUND); + tem->tvs_bg_color); *bg = ansi_bg_to_solaris(tem, - DEFAULT_ANSI_FOREGROUND); + tem->tvs_fg_color); } else { *fg = ansi_fg_to_solaris(tem, - DEFAULT_ANSI_FOREGROUND); + tem->tvs_fg_color); *bg = ansi_bg_to_solaris(tem, - DEFAULT_ANSI_BACKGROUND); + tem->tvs_bg_color); } } @@ -2120,39 +2184,266 @@ tem_get_color(struct tem *tem, text_color_t *fg, text_color_t *bg) * which is called only once. */ void -tem_pix_cls_range(tem_t *tem, +tem_safe_pix_cls_range(struct tem_vt_state *tem, screen_pos_t row, int nrows, int offset_y, screen_pos_t col, int ncols, int offset_x, boolean_t sroll_up, cred_t *credp, enum called_from called_from) { - struct tem_state *tems = tem->state; struct vis_consdisplay da; int i, j; int row_add = 0; text_color_t fg_color; text_color_t bg_color; - ASSERT((called_from == CALLED_FROM_STANDALONE) || - MUTEX_HELD(&tem->lock)); + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); if (sroll_up) - row_add = tems->a_c_dimension.height - 1; + row_add = tems.ts_c_dimension.height - 1; - da.width = tems->a_font.width; - da.height = tems->a_font.height; + da.width = tems.ts_font.width; + da.height = tems.ts_font.height; - tem_get_color(tem, &fg_color, &bg_color); + tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE); - BIT_TO_PIX(tem, ' ', fg_color, bg_color); - da.data = (uchar_t *)tems->a_pix_data; + tem_safe_callback_bit2pix(tem, ' ', fg_color, bg_color); + da.data = (uchar_t *)tem->tvs_pix_data; for (i = 0; i < nrows; i++, row++) { da.row = (row + row_add) * da.height + offset_y; da.col = col * da.width + offset_x; for (j = 0; j < ncols; j++) { - tem_display(tem, &da, credp, called_from); + tems_safe_display(&da, credp, called_from); da.col += da.width; } } } + +/* + * virtual screen operations + */ +static void +tem_safe_virtual_display(struct tem_vt_state *tem, unsigned char *string, + int count, screen_pos_t row, screen_pos_t col, + text_color_t fg_color, text_color_t bg_color) +{ + int i, width; + unsigned char *addr; + text_color_t *pfgcolor; + text_color_t *pbgcolor; + + if (row < 0 || row >= tems.ts_c_dimension.height || + col < 0 || col >= tems.ts_c_dimension.width || + col + count > tems.ts_c_dimension.width) + return; + + width = tems.ts_c_dimension.width; + addr = tem->tvs_screen_buf + (row * width + col); + pfgcolor = tem->tvs_fg_buf + (row * width + col); + pbgcolor = tem->tvs_bg_buf + (row * width + col); + for (i = 0; i < count; i++) { + *addr++ = string[i]; + *pfgcolor++ = fg_color; + *pbgcolor++ = bg_color; + } +} + +static void +i_virtual_copy(unsigned char *base, + screen_pos_t s_col, screen_pos_t s_row, + screen_pos_t e_col, screen_pos_t e_row, + screen_pos_t t_col, screen_pos_t t_row) +{ + unsigned char *from; + unsigned char *to; + int cnt; + screen_size_t chars_per_row; + unsigned char *to_row_start; + unsigned char *from_row_start; + screen_size_t rows_to_move; + int cols = tems.ts_c_dimension.width; + + chars_per_row = e_col - s_col + 1; + rows_to_move = e_row - s_row + 1; + + to_row_start = base + ((t_row * cols) + t_col); + from_row_start = base + ((s_row * cols) + s_col); + + if (to_row_start < from_row_start) { + while (rows_to_move-- > 0) { + to = to_row_start; + from = from_row_start; + to_row_start += cols; + from_row_start += cols; + for (cnt = chars_per_row; cnt-- > 0; ) + *to++ = *from++; + } + } else { + /* + * Offset to the end of the region and copy backwards. + */ + cnt = rows_to_move * cols + chars_per_row; + to_row_start += cnt; + from_row_start += cnt; + + while (rows_to_move-- > 0) { + to_row_start -= cols; + from_row_start -= cols; + to = to_row_start; + from = from_row_start; + for (cnt = chars_per_row; cnt-- > 0; ) + *--to = *--from; + } + } +} + +static void +tem_safe_virtual_copy(struct tem_vt_state *tem, + screen_pos_t s_col, screen_pos_t s_row, + screen_pos_t e_col, screen_pos_t e_row, + screen_pos_t t_col, screen_pos_t t_row) +{ + screen_size_t chars_per_row; + screen_size_t rows_to_move; + int rows = tems.ts_c_dimension.height; + int cols = tems.ts_c_dimension.width; + + if (s_col < 0 || s_col >= cols || + s_row < 0 || s_row >= rows || + e_col < 0 || e_col >= cols || + e_row < 0 || e_row >= rows || + t_col < 0 || t_col >= cols || + t_row < 0 || t_row >= rows || + s_col > e_col || + s_row > e_row) + return; + + chars_per_row = e_col - s_col + 1; + rows_to_move = e_row - s_row + 1; + + /* More sanity checks. */ + if (t_row + rows_to_move > rows || + t_col + chars_per_row > cols) + return; + + i_virtual_copy(tem->tvs_screen_buf, s_col, s_row, + e_col, e_row, t_col, t_row); + + /* text_color_t is the same size as char */ + i_virtual_copy((unsigned char *)tem->tvs_fg_buf, + s_col, s_row, e_col, e_row, t_col, t_row); + i_virtual_copy((unsigned char *)tem->tvs_bg_buf, + s_col, s_row, e_col, e_row, t_col, t_row); + +} + +static void +tem_safe_virtual_cls(struct tem_vt_state *tem, + int count, screen_pos_t row, screen_pos_t col) +{ + text_color_t fg_color; + text_color_t bg_color; + + tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE); + tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col, + fg_color, bg_color); +} + +/* + * only blank screen, not clear our screen buffer + */ +void +tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp, + enum called_from called_from) +{ + int row; + + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + if (tems.ts_display_mode == VIS_PIXEL) { + tem_safe_pix_clear_entire_screen(tem, credp, called_from); + return; + } + + for (row = 0; row < tems.ts_c_dimension.height; row++) { + tem_safe_callback_cls(tem, + tems.ts_c_dimension.width, + row, 0, credp, called_from); + } +} + +/* + * unblank screen with associated tem from its screen buffer + */ +void +tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp, + enum called_from called_from) +{ + text_color_t fg_color, fg_last; + text_color_t bg_color, bg_last; + size_t tc_size = sizeof (text_color_t); + int row, col, count, col_start; + int width; + unsigned char *buf; + + ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) || + called_from == CALLED_FROM_STANDALONE); + + if (tems.ts_display_mode == VIS_PIXEL) + tem_safe_pix_clear_entire_screen(tem, credp, called_from); + + tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from); + + width = tems.ts_c_dimension.width; + + /* + * Display data in tvs_screen_buf to the actual framebuffer in a + * row by row way. + * When dealing with one row, output data with the same foreground + * and background color all together. + */ + for (row = 0; row < tems.ts_c_dimension.height; row++) { + buf = tem->tvs_screen_buf + (row * width); + count = col_start = 0; + for (col = 0; col < width; col++) { + fg_color = + tem->tvs_fg_buf[(row * width + col) * tc_size]; + bg_color = + tem->tvs_bg_buf[(row * width + col) * tc_size]; + if (col == 0) { + fg_last = fg_color; + bg_last = bg_color; + } + + if ((fg_color != fg_last) || (bg_color != bg_last)) { + /* + * Call the primitive to render this data. + */ + tem_safe_callback_display(tem, + buf, count, row, col_start, + fg_last, bg_last, credp, called_from); + buf += count; + count = 1; + col_start = col; + fg_last = fg_color; + bg_last = bg_color; + } else { + count++; + } + } + + if (col_start == (width - 1)) + continue; + + /* + * Call the primitive to render this data. + */ + tem_safe_callback_display(tem, + buf, count, row, col_start, + fg_last, bg_last, credp, called_from); + } + + tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from); +} diff --git a/usr/src/uts/common/io/vcons.c b/usr/src/uts/common/io/vcons.c new file mode 100644 index 0000000000..3646057465 --- /dev/null +++ b/usr/src/uts/common/io/vcons.c @@ -0,0 +1,1308 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <sys/cred.h> +#include <sys/vnode.h> +#include <sys/termios.h> +#include <sys/termio.h> +#include <sys/ttold.h> +#include <sys/stropts.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/tty.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/procset.h> +#include <sys/fault.h> +#include <sys/siginfo.h> +#include <sys/debug.h> +#include <sys/kd.h> +#include <sys/vt.h> +#include <sys/vtdaemon.h> +#include <sys/session.h> +#include <sys/door.h> +#include <sys/kmem.h> +#include <sys/cpuvar.h> +#include <sys/kbio.h> +#include <sys/strredir.h> +#include <sys/fs/snode.h> +#include <sys/consdev.h> +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/console.h> +#include <sys/promif.h> +#include <sys/note.h> +#include <sys/polled_io.h> +#include <sys/systm.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/esunddi.h> +#include <sys/sunldi.h> +#include <sys/debug.h> +#include <sys/console.h> +#include <sys/ddi_impldefs.h> +#include <sys/policy.h> +#include <sys/tem.h> +#include <sys/wscons.h> +#include <sys/systm.h> +#include <sys/modctl.h> +#include <sys/vt_impl.h> +#include <sys/consconfig_dacf.h> + +/* + * This file belongs to wc STREAMS module which has a D_MTPERMODE + * inner perimeter. See "Locking Policy" comment in wscons.c for + * more information. + */ + +/* + * Minor name device file Hotkeys + * + * 0 the system console /dev/console Alt + F1 + * 0: virtual console #1 /dev/vt/0 Alt + F1 + * + * 2: virtual console #2 /dev/vt/2 Alt + F2 + * 3: virtual console #3 /dev/vt/3 Alt + F3 + * ...... + * n: virtual console #n /dev/vt/n Alt + Fn + * + * Note that vtdaemon is running on /dev/vt/1 (minor=1), + * which is not available to end users. + * + */ + +#define VT_DAEMON_MINOR 1 +#define VT_IS_DAEMON(minor) ((minor) == VT_DAEMON_MINOR) + +extern void wc_get_size(vc_state_t *pvc); +extern boolean_t consconfig_console_is_tipline(void); + + +minor_t vc_last_console = VT_MINOR_INVALID; /* the last used console */ +volatile uint_t vc_target_console; /* arg (1..n) */ + +static volatile minor_t vc_inuse_max_minor = 0; +static list_t vc_waitactive_list; +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_target_console)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_last_console)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_inuse_max_minor)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_waitactive_list)) + +static int vt_pending_vtno = -1; +kmutex_t vt_pending_vtno_lock; +_NOTE(MUTEX_PROTECTS_DATA(vt_pending_vtno_lock, vt_pending_vtno)) + +static int vt_activate(uint_t vt_no, cred_t *credp); +static void vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size); +static void vt_copyin(queue_t *qp, mblk_t *mp, uint_t size); +static void vt_iocnak(queue_t *qp, mblk_t *mp, int error); +static void vt_iocack(queue_t *qp, mblk_t *mp); + +static uint_t vt_minor2arg(minor_t minor); +static minor_t vt_arg2minor(uint_t arg); + +/* + * If the system console is directed to tipline, consider /dev/vt/0 as + * not being used. + * For other VT, if it is opened and tty is initialized, consider it + * as being used. + */ +#define VT_IS_INUSE(id) \ + (((vt_minor2vc(id))->vc_flags & WCS_ISOPEN) && \ + ((vt_minor2vc(id))->vc_flags & WCS_INIT) && \ + (id != 0 || !consconfig_console_is_tipline())) + +/* + * the vt switching message is encoded as: + * + * ------------------------------------------------------------- + * | \033 | 'Q' | vtno + 'A' | opcode | 'z' | '\0' | + * ------------------------------------------------------------- + */ +#define VT_MSG_SWITCH(mp) \ + ((int)((mp)->b_wptr - (mp)->b_rptr) >= 5 && \ + *((mp)->b_rptr) == '\033' && \ + *((mp)->b_rptr + 1) == 'Q' && \ + *((mp)->b_rptr + 4) == 'z') + +#define VT_MSG_VTNO(mp) (*((mp)->b_rptr + 2) - 'A') +#define VT_MSG_OPCODE(mp) (*((mp)->b_rptr + 3)) + +#define VT_DOORCALL_MAX_RETRY 3 + +static void +vt_init_ttycommon(tty_common_t *pcommon) +{ + struct termios *termiosp; + int len; + + mutex_init(&pcommon->t_excl, NULL, MUTEX_DEFAULT, NULL); + pcommon->t_iflag = 0; + + /* + * Get the default termios settings (cflag). + * These are stored as a property in the + * "options" node. + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, + ddi_root_node(), 0, "ttymodes", + (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS) { + + if (len == sizeof (struct termios)) + pcommon->t_cflag = termiosp->c_cflag; + else + cmn_err(CE_WARN, + "wc: Couldn't get ttymodes property!"); + + kmem_free(termiosp, len); + } else { + /* + * Gack! Whine about it. + */ + cmn_err(CE_WARN, + "wc: Couldn't get ttymodes property!"); + } + + pcommon->t_iocpending = NULL; +} + +static int +vt_config(uint_t count) +{ + if (consmode != CONS_KFB) + return (ENOTSUP); + + /* one for system console, one for vtdaemon */ + if (count < 2) + return (ENXIO); + + /* + * Shouldn't allow to shrink the max vt minor to be smaller than + * the max in used minor. + */ + if (count <= vc_inuse_max_minor) + return (EBUSY); + + mutex_enter(&vc_lock); + vt_resize(count); + mutex_exit(&vc_lock); + + return (0); +} + +void +vt_clean(queue_t *q, vc_state_t *pvc) +{ + ASSERT(MUTEX_HELD(&pvc->vc_state_lock)); + + if (pvc->vc_bufcallid != 0) { + qunbufcall(q, pvc->vc_bufcallid); + pvc->vc_bufcallid = 0; + } + if (pvc->vc_timeoutid != 0) { + (void) quntimeout(q, pvc->vc_timeoutid); + pvc->vc_timeoutid = 0; + } + ttycommon_close(&pvc->vc_ttycommon); + + pvc->vc_flags &= ~WCS_INIT; +} + +/* + * Reply the VT_WAITACTIVE ioctl. + * Argument 'close' usage: + * B_TRUE: the vt designated by argument 'minor' is being closed. + * B_FALSE: the vt designated by argument 'minor' has been activated just now. + */ +static void +vc_waitactive_reply(int minor, boolean_t close) +{ + vc_waitactive_msg_t *index, *tmp; + vc_state_t *pvc; + + index = list_head(&vc_waitactive_list); + + while (index != NULL) { + tmp = index; + index = list_next(&vc_waitactive_list, index); + + if ((close && tmp->wa_msg_minor == minor) || + (!close && tmp->wa_wait_minor == minor)) { + list_remove(&vc_waitactive_list, tmp); + pvc = vt_minor2vc(tmp->wa_msg_minor); + + if (close) + vt_iocnak(pvc->vc_wq, tmp->wa_mp, ENXIO); + else + vt_iocack(pvc->vc_wq, tmp->wa_mp); + + kmem_free(tmp, sizeof (vc_waitactive_msg_t)); + } + } +} + +void +vt_close(queue_t *q, vc_state_t *pvc, cred_t *credp) +{ + minor_t index; + + mutex_enter(&pvc->vc_state_lock); + vt_clean(q, pvc); + pvc->vc_flags &= ~WCS_ISOPEN; + mutex_exit(&pvc->vc_state_lock); + + tem_destroy(pvc->vc_tem, credp); + pvc->vc_tem = NULL; + + index = pvc->vc_minor; + if (index == vc_inuse_max_minor) { + while ((--index > 0) && !VT_IS_INUSE(index)) + ; + vc_inuse_max_minor = index; + } + + vc_waitactive_reply(pvc->vc_minor, B_TRUE); +} + +static void +vt_init_tty(vc_state_t *pvc) +{ + ASSERT(MUTEX_HELD(&pvc->vc_state_lock)); + + pvc->vc_flags |= WCS_INIT; + vt_init_ttycommon(&pvc->vc_ttycommon); + wc_get_size(pvc); +} + +/* + * minor 0: /dev/vt/0 (index = 0, indicating the system console) + * minor 1: /dev/vt/1 (index = 1, vtdaemon special console) + * minor 2: /dev/vt/2 (index = 2, virtual consoles) + * ...... + * minor n: /dev/vt/n (index = n) + * + * + * The system console (minor 0), is opened firstly and used during console + * configuration. It also acts as the system hard console even when all + * virtual consoles go off. + * + * In tipline case, minor 0 (/dev/vt/0) is reserved, and cannot be switched to. + * And the system console is redirected to the tipline. During normal cases, + * we can switch from virtual consoles to it by pressing 'Alt + F1'. + * + * minor 1 (/dev/vt/1) is reserved for vtdaemon special console, and it's + * not available to end users. + * + * During early console configuration, consconfig_dacf opens wscons and then + * issue a WC_OPEN_FB ioctl to kick off terminal init process. So during + * consconfig_dacf first opening of wscons, tems (of type tem_state_t) is + * not initialized. We do not initialize the tem_vt_state_t instance returned + * by tem_init() for this open, since we do not have enough info to handle + * normal terminal operation at this moment. This tem_vt_state_t instance + * will get initialized when handling WC_OPEN_FB. + */ +int +vt_open(minor_t minor, queue_t *rq, cred_t *crp) +{ + vc_state_t *pvc; + + if (!vt_minor_valid(minor)) + return (ENXIO); + + pvc = vt_minor2vc(minor); + if (pvc == NULL) + return (ENXIO); + + mutex_enter(&vc_lock); + mutex_enter(&pvc->vc_state_lock); + + if (!(pvc->vc_flags & WCS_ISOPEN)) { + /* + * vc_tem might not be intialized if !tems.ts_initialized, + * and this only happens during console configuration. + */ + pvc->vc_tem = tem_init(crp); + } + + if (!(pvc->vc_flags & WCS_INIT)) + vt_init_tty(pvc); + + /* + * In normal case, the first screen is the system console; + * In tipline case, the first screen is the first VT that gets started. + */ + if (vc_active_console == VT_MINOR_INVALID && minor != VT_DAEMON_MINOR) + if (minor == 0 || consmode == CONS_KFB) { + boolean_t unblank = B_FALSE; + + vc_active_console = minor; + vc_last_console = minor; + if (minor != 0) { + /* + * If we are not opening the system console + * as the first console, clear the phyical + * screen. + */ + unblank = B_TRUE; + } + + tem_activate(pvc->vc_tem, unblank, crp); + } + + if ((pvc->vc_ttycommon.t_flags & TS_XCLUDE) && + (secpolicy_excl_open(crp) != 0)) { + mutex_exit(&pvc->vc_state_lock); + mutex_exit(&vc_lock); + return (EBUSY); + } + + if (minor > vc_inuse_max_minor) + vc_inuse_max_minor = minor; + + pvc->vc_flags |= WCS_ISOPEN; + pvc->vc_ttycommon.t_readq = rq; + pvc->vc_ttycommon.t_writeq = WR(rq); + + mutex_exit(&pvc->vc_state_lock); + mutex_exit(&vc_lock); + + rq->q_ptr = pvc; + WR(rq)->q_ptr = pvc; + pvc->vc_wq = WR(rq); + + qprocson(rq); + return (0); +} + +static minor_t +vt_find_prev(minor_t cur) +{ + minor_t i, t, max; + + ASSERT(vc_active_console != VT_MINOR_INVALID); + + max = VC_INSTANCES_COUNT; + + for (i = cur - 1; (t = (i + max) % max) != cur; i--) + if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t)) + return (t); + + return (VT_MINOR_INVALID); +} + +static minor_t +vt_find_next(minor_t cur) +{ + minor_t i, t, max; + + ASSERT(vc_active_console != VT_MINOR_INVALID); + + max = VC_INSTANCES_COUNT; + + for (i = cur + 1; (t = (i + max) % max) != cur; i++) + if (!VT_IS_DAEMON(t) && VT_IS_INUSE(t)) + return (t); + + return (VT_MINOR_INVALID); +} + +/* ARGSUSED */ +void +vt_send_hotkeys(void *timeout_arg) +{ + door_handle_t door; + vt_cmd_arg_t arg; + int error = 0; + int retries = 0; + door_arg_t door_arg; + + mutex_enter(&vt_pending_vtno_lock); + + arg.vt_ev = VT_EV_HOTKEYS; + arg.vt_num = vt_pending_vtno; + + /* only available in kernel context or user context */ + if (door_ki_open(VT_DAEMON_DOOR_FILE, &door) != 0) { + vt_pending_vtno = -1; + mutex_exit(&vt_pending_vtno_lock); + return; + } + + door_arg.rbuf = NULL; + door_arg.rsize = 0; + door_arg.data_ptr = (void *)&arg; + door_arg.data_size = sizeof (arg); + door_arg.desc_ptr = NULL; + door_arg.desc_num = 0; + + /* + * Make door upcall + */ + while ((error = door_ki_upcall(door, &door_arg)) != 0 && + retries < VT_DOORCALL_MAX_RETRY) + if (error == EAGAIN || error == EINTR) + retries++; + else + break; + + door_ki_rele(door); + + vt_pending_vtno = -1; + + mutex_exit(&vt_pending_vtno_lock); +} + +static boolean_t +vt_validate_hotkeys(int minor) +{ + /* + * minor should not succeed the existing minor numbers range. + */ + if (!vt_minor_valid(minor)) + return (B_FALSE); + + /* + * Shouldn't switch to /dev/vt/1 or an unused vt. + */ + if (!VT_IS_DAEMON(minor) && VT_IS_INUSE(minor)) + return (B_TRUE); + + return (B_FALSE); +} + +static void +vt_trigger_hotkeys(int vtno) +{ + mutex_enter(&vt_pending_vtno_lock); + + if (vt_pending_vtno != -1) { + mutex_exit(&vt_pending_vtno_lock); + return; + } + + vt_pending_vtno = vtno; + mutex_exit(&vt_pending_vtno_lock); + (void) timeout(vt_send_hotkeys, NULL, 1); +} + +/* + * return value: + * 0: non msg of vt hotkeys + * 1: msg of vt hotkeys + */ +int +vt_check_hotkeys(mblk_t *mp) +{ + int vtno = 0; + minor_t minor = 0; + + /* LINTED E_PTRDIFF_OVERFLOW */ + if (!VT_MSG_SWITCH(mp)) + return (0); + + switch (VT_MSG_OPCODE(mp)) { + case 'B': + /* find out the previous vt */ + if (vc_active_console == VT_MINOR_INVALID) + return (1); + + if (VT_IS_DAEMON(vc_active_console)) { + minor = vt_find_prev(vt_arg2minor(vc_target_console)); + break; + } + + minor = vt_find_prev(vc_active_console); + break; + case 'F': + /* find out the next vt */ + if (vc_active_console == VT_MINOR_INVALID) + return (1); + + if (VT_IS_DAEMON(vc_active_console)) { + minor = vt_find_next(vt_arg2minor(vc_target_console)); + break; + } + + minor = vt_find_next(vc_active_console); + break; + case 'H': + /* find out the specified vt */ + minor = VT_MSG_VTNO(mp); + + /* check for system console, Alt + F1 */ + if (minor == 1) + minor = 0; + break; + case 'L': + /* find out the last vt */ + if ((minor = vc_last_console) == VT_MINOR_INVALID) + return (1); + break; + default: + return (1); + } + + if (!vt_validate_hotkeys(minor)) + return (1); + + /* + * for system console, the argument of vtno for + * vt_activate is 1, though its minor is 0 + */ + if (minor == 0) + vtno = 1; /* for system console */ + else + vtno = minor; + + vt_trigger_hotkeys(vtno); + return (1); +} + +static void +vt_proc_sendsig(pid_t pid, int sig) +{ + register proc_t *p; + + if (pid <= 0) + return; + + mutex_enter(&pidlock); + if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { + mutex_exit(&pidlock); + return; + } + + psignal(p, sig); + mutex_exit(&pidlock); +} + +static int +vt_proc_exists(pid_t pid) +{ + register proc_t *p; + + if (pid <= 0) + return (EINVAL); + + mutex_enter(&pidlock); + if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { + mutex_exit(&pidlock); + return (ESRCH); + } + mutex_exit(&pidlock); + + return (0); +} + +#define SIG_VALID(x) (((x) > 0) && ((x) < _SIGRTMAX) && \ + ((x) != SIGKILL) && ((x) != SIGSTOP)) + +static int +vt_setmode(vc_state_t *pvc, struct vt_mode *pmode) +{ + if ((pmode->mode != VT_PROCESS) && (pmode->mode != VT_AUTO)) + return (EINVAL); + + if (!SIG_VALID(pmode->relsig) || !SIG_VALID(pmode->acqsig)) + return (EINVAL); + + if (pmode->mode == VT_PROCESS) { + pvc->vc_pid = curproc->p_pid; + } else { + pvc->vc_dispnum = 0; + pvc->vc_login = 0; + } + + pvc->vc_switch_mode = pmode->mode; + pvc->vc_waitv = pmode->waitv; + pvc->vc_relsig = pmode->relsig; + pvc->vc_acqsig = pmode->acqsig; + + return (0); +} + +static void +vt_reset(vc_state_t *pvc) +{ + pvc->vc_switch_mode = VT_AUTO; + pvc->vc_pid = -1; + pvc->vc_dispnum = 0; + pvc->vc_login = 0; + pvc->vc_switchto = VT_MINOR_INVALID; +} + +/* + * switch to vt_no from vc_active_console + */ +static void +vt_switch(uint_t vt_no, cred_t *credp) +{ + vc_state_t *pvc_active = vt_minor2vc(vc_active_console); + vc_state_t *pvc = vt_minor2vc(vt_no); + minor_t index; + + ASSERT(pvc_active && pvc); + + mutex_enter(&vc_lock); + + tem_switch(pvc_active->vc_tem, pvc->vc_tem, credp); + + if (!VT_IS_DAEMON(vc_active_console)) + vc_last_console = vc_active_console; + else + vc_last_console = vt_arg2minor(vc_target_console); + + vc_active_console = pvc->vc_minor; + + if (pvc->vc_switch_mode == VT_PROCESS) { + pvc->vc_switchto = pvc->vc_minor; + + /* send it an acquired signal */ + vt_proc_sendsig(pvc->vc_pid, pvc->vc_acqsig); + } + + vc_waitactive_reply(vc_active_console, B_FALSE); + + mutex_exit(&vc_lock); + + if (!VT_IS_DAEMON(vt_no)) { + /* + * Applications that open the virtual console device may request + * asynchronous notification of VT switching from a previous VT + * to another one by setting the S_MSG flag in an I_SETSIG + * STREAMS ioctl. Such processes receive a SIGPOLL signal when + * a VT switching succeeds. + */ + for (index = 0; index < VC_INSTANCES_COUNT; index++) { + vc_state_t *tmp_pvc = vt_minor2vc(index); + mblk_t *mp; + + if ((tmp_pvc->vc_flags & WCS_ISOPEN) && + (tmp_pvc->vc_flags & WCS_INIT) && + (mp = allocb(sizeof (unsigned char), BPRI_HI))) { + mp->b_datap->db_type = M_PCSIG; + *mp->b_wptr = SIGPOLL; + mp->b_wptr += sizeof (unsigned char); + putnext(RD(tmp_pvc->vc_wq), mp); + } + } + } + +} + +/* + * vt_no from 0 to n + * + * 0 for the vtdaemon sepcial console (only vtdaemon will use it) + * 1 for the system console (Alt + F1, or Alt + Ctrl + F1), + * aka Virtual Console #1 + * + * 2 for Virtual Console #2 + * n for Virtual Console #n + */ +static minor_t +vt_arg2minor(uint_t arg) +{ + if (arg == 0) + return (1); + + if (arg == 1) + return (0); + + return (arg); +} + +static uint_t +vt_minor2arg(minor_t minor) +{ + if (minor == 0) + return (1); + + if (VT_IS_DAEMON(minor)) { + /* here it should be the real console */ + return (vc_target_console); + } + + return (minor); +} + +static int +vt_activate(uint_t vt_no, cred_t *credp) +{ + vc_state_t *pvc; + minor_t minor; + + minor = vt_arg2minor(vt_no); + if (!vt_minor_valid(minor)) + return (ENXIO); + if (minor == vc_active_console) { + if (VT_IS_DAEMON(minor)) { + /* + * vtdaemon is reactivating itself to do locking + * on behalf of another console, so record current + * target console as the last console. + */ + vc_last_console = vt_arg2minor(vc_target_console); + } + + return (0); + } + + /* + * In tipline case, the system console is redirected to tipline + * and thus is always available. + */ + if (minor == 0 && consconfig_console_is_tipline()) + return (0); + + if (!VT_IS_INUSE(minor)) + return (ENXIO); + + pvc = vt_minor2vc(minor); + if (pvc == NULL) + return (ENXIO); + if (pvc->vc_tem == NULL) + return (ENXIO); + + pvc = vt_minor2vc(vc_active_console); + if (pvc == NULL) + return (ENXIO); + if (pvc->vc_switch_mode != VT_PROCESS) { + vt_switch(minor, credp); + return (0); + } + + /* + * Validate the process, reset the + * vt to auto mode if failed. + */ + if (pvc->vc_pid == -1 || vt_proc_exists(pvc->vc_pid) != 0) { + /* + * Xserver has not started up yet, + * or it dose not exist. + */ + vt_reset(pvc); + return (0); + } + + /* + * Send the release signal to the process, + * and wait VT_RELDISP ioctl from Xserver + * after its leaving VT. + */ + vt_proc_sendsig(pvc->vc_pid, pvc->vc_relsig); + pvc->vc_switchto = minor; + + /* + * We don't need a timeout here, for if Xserver refuses + * or fails to respond to release signal using VT_RELDISP, + * we cannot successfully switch to our text mode. Actually + * users can try again. At present we don't support force + * switch. + */ + return (0); +} + +static int +vt_reldisp(vc_state_t *pvc, int arg, cred_t *credp) +{ + minor_t target_vtno = pvc->vc_switchto; + + if ((pvc->vc_switch_mode != VT_PROCESS) || + (pvc->vc_minor != vc_active_console)) + return (EACCES); + + if (target_vtno == VT_MINOR_INVALID) + return (EINVAL); + + pvc->vc_switchto = VT_MINOR_INVALID; + + if (arg == VT_ACKACQ) + return (0); + + if (arg == 0) + return (0); /* refuse to release */ + + /* Xserver has left VT */ + vt_switch(target_vtno, credp); + return (0); +} + +void +vt_ioctl(queue_t *q, mblk_t *mp) +{ + vc_state_t *pvc = (vc_state_t *)q->q_ptr; + struct iocblk *iocp; + struct vt_mode vtmode; + struct vt_stat vtinfo; + struct vt_dispinfo vtdisp; + mblk_t *tmp; + int minor; + int arg; + int error = 0; + vc_waitactive_msg_t *wait_msg; + + iocp = (struct iocblk *)(void *)mp->b_rptr; + if (consmode != CONS_KFB && iocp->ioc_cmd != VT_ENABLED) { + vt_iocnak(q, mp, EINVAL); + return; + } + + switch (iocp->ioc_cmd) { + case VT_ENABLED: + if (!(tmp = allocb(sizeof (int), BPRI_MED))) { + error = ENOMEM; + break; + } + *(int *)(void *)tmp->b_rptr = consmode; + tmp->b_wptr += sizeof (int); + vt_copyout(q, mp, tmp, sizeof (int)); + return; + + case KDSETMODE: + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + if (arg != KD_TEXT && arg != KD_GRAPHICS) { + error = EINVAL; + break; + } + if (tem_get_fbmode(pvc->vc_tem) == arg) + break; + + tem_set_fbmode(pvc->vc_tem, (uchar_t)arg, iocp->ioc_cr); + + break; + + case KDGETMODE: + if (!(tmp = allocb(sizeof (int), BPRI_MED))) { + error = ENOMEM; + break; + } + *(int *)(void *)tmp->b_rptr = tem_get_fbmode(pvc->vc_tem); + tmp->b_wptr += sizeof (int); + vt_copyout(q, mp, tmp, sizeof (int)); + return; + + case VT_OPENQRY: /* return number of first free VT */ + if (!(tmp = allocb(sizeof (int), BPRI_MED))) { + error = ENOMEM; + break; + } + + /* minors of 0 and 1 are not available to end users */ + for (minor = 2; vt_minor_valid(minor); minor++) + if (!VT_IS_INUSE(minor)) + break; + + if (!vt_minor_valid(minor)) + minor = -1; + *(int *)(void *)tmp->b_rptr = minor; /* /dev/vt/minor */ + tmp->b_wptr += sizeof (int); + vt_copyout(q, mp, tmp, sizeof (int)); + return; + + case VT_GETMODE: + vtmode.mode = pvc->vc_switch_mode; + vtmode.waitv = pvc->vc_waitv; + vtmode.relsig = pvc->vc_relsig; + vtmode.acqsig = pvc->vc_acqsig; + vtmode.frsig = 0; + if (!(tmp = allocb(sizeof (struct vt_mode), BPRI_MED))) { + error = ENOMEM; + break; + } + *(struct vt_mode *)(void *)tmp->b_rptr = vtmode; + tmp->b_wptr += sizeof (struct vt_mode); + vt_copyout(q, mp, tmp, sizeof (struct vt_mode)); + return; + + case VT_SETMODE: + vt_copyin(q, mp, sizeof (struct vt_mode)); + return; + + case VT_SETDISPINFO: + /* always enforce sys_devices privilege for setdispinfo */ + if ((error = secpolicy_console(iocp->ioc_cr)) != 0) + break; + + pvc->vc_dispnum = *(intptr_t *)(void *)mp->b_cont->b_rptr; + break; + + case VT_SETDISPLOGIN: + pvc->vc_login = *(intptr_t *)(void *)mp->b_cont->b_rptr; + break; + + case VT_GETDISPINFO: + vtdisp.v_pid = pvc->vc_pid; + vtdisp.v_dispnum = pvc->vc_dispnum; + vtdisp.v_login = pvc->vc_login; + if (!(tmp = allocb(sizeof (struct vt_dispinfo), BPRI_MED))) { + error = ENOMEM; + break; + } + *(struct vt_dispinfo *)(void *)tmp->b_rptr = vtdisp; + tmp->b_wptr += sizeof (struct vt_dispinfo); + vt_copyout(q, mp, tmp, sizeof (struct vt_dispinfo)); + return; + + case VT_RELDISP: + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + error = vt_reldisp(pvc, arg, iocp->ioc_cr); + break; + + case VT_CONFIG: + /* always enforce sys_devices privilege for config */ + if ((error = secpolicy_console(iocp->ioc_cr)) != 0) + break; + + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + error = vt_config(arg); + break; + + case VT_ACTIVATE: + /* always enforce sys_devices privilege for secure switch */ + if ((error = secpolicy_console(iocp->ioc_cr)) != 0) + break; + + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + error = vt_activate(arg, iocp->ioc_cr); + break; + + case VT_WAITACTIVE: + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + arg = vt_arg2minor(arg); + if (!vt_minor_valid(arg)) { + error = ENXIO; + break; + } + if (arg == vc_active_console) + break; + + wait_msg = kmem_zalloc(sizeof (vc_waitactive_msg_t), + KM_NOSLEEP); + if (wait_msg == NULL) { + error = ENXIO; + break; + } + + wait_msg->wa_mp = mp; + wait_msg->wa_msg_minor = pvc->vc_minor; + wait_msg->wa_wait_minor = arg; + list_insert_head(&vc_waitactive_list, wait_msg); + + return; + + case VT_GETSTATE: + /* + * Here v_active is the argument for vt_activate, + * not minor. + */ + vtinfo.v_active = vt_minor2arg(vc_active_console); + vtinfo.v_state = 3; /* system console and vtdaemon */ + + /* we only support 16 vt states since the v_state is short */ + for (minor = 2; minor < 16; minor++) { + pvc = vt_minor2vc(minor); + if (pvc == NULL) + break; + if (VT_IS_INUSE(minor)) + vtinfo.v_state |= (1 << pvc->vc_minor); + } + + if (!(tmp = allocb(sizeof (struct vt_stat), BPRI_MED))) { + error = ENOMEM; + break; + } + *(struct vt_stat *)(void *)tmp->b_rptr = vtinfo; + tmp->b_wptr += sizeof (struct vt_stat); + vt_copyout(q, mp, tmp, sizeof (struct vt_stat)); + return; + + case VT_SET_TARGET: + /* always enforce sys_devices privilege */ + if ((error = secpolicy_console(iocp->ioc_cr)) != 0) + break; + + arg = *(intptr_t *)(void *)mp->b_cont->b_rptr; + + /* vtdaemon is doing authentication for this target console */ + vc_target_console = arg; + break; + + case VT_GETACTIVE: /* get real active console (minor) */ + if (!(tmp = allocb(sizeof (int), BPRI_MED))) { + error = ENOMEM; + break; + } + *(int *)(void *)tmp->b_rptr = vc_active_console; + tmp->b_wptr += sizeof (int); + vt_copyout(q, mp, tmp, sizeof (int)); + return; + + default: + error = ENXIO; + break; + } + + if (error != 0) + vt_iocnak(q, mp, error); + else + vt_iocack(q, mp); +} + +void +vt_miocdata(queue_t *qp, mblk_t *mp) +{ + vc_state_t *pvc = (vc_state_t *)qp->q_ptr; + struct copyresp *copyresp; + struct vt_mode *pmode; + int error = 0; + + copyresp = (struct copyresp *)(void *)mp->b_rptr; + if (copyresp->cp_rval) { + vt_iocnak(qp, mp, EAGAIN); + return; + } + + switch (copyresp->cp_cmd) { + case VT_SETMODE: + pmode = (struct vt_mode *)(void *)mp->b_cont->b_rptr; + error = vt_setmode(pvc, pmode); + break; + + case KDGETMODE: + case VT_OPENQRY: + case VT_GETMODE: + case VT_GETDISPINFO: + case VT_GETSTATE: + case VT_ENABLED: + case VT_GETACTIVE: + break; + + default: + error = ENXIO; + break; + } + + if (error != 0) + vt_iocnak(qp, mp, error); + else + vt_iocack(qp, mp); +} + +static void +vt_iocack(queue_t *qp, mblk_t *mp) +{ + struct iocblk *iocbp = (struct iocblk *)(void *)mp->b_rptr; + + mp->b_datap->db_type = M_IOCACK; + mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); + iocbp->ioc_error = 0; + iocbp->ioc_count = 0; + iocbp->ioc_rval = 0; + if (mp->b_cont != NULL) { + freemsg(mp->b_cont); + mp->b_cont = NULL; + } + qreply(qp, mp); +} + +static void +vt_iocnak(queue_t *qp, mblk_t *mp, int error) +{ + struct iocblk *iocp = (struct iocblk *)(void *)mp->b_rptr; + + mp->b_datap->db_type = M_IOCNAK; + iocp->ioc_rval = 0; + iocp->ioc_count = 0; + iocp->ioc_error = error; + if (mp->b_cont != NULL) { + freemsg(mp->b_cont); + mp->b_cont = NULL; + } + qreply(qp, mp); +} + +static void +vt_copyin(queue_t *qp, mblk_t *mp, uint_t size) +{ + struct copyreq *cqp; + + cqp = (struct copyreq *)(void *)mp->b_rptr; + cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr); + cqp->cq_size = size; + cqp->cq_flag = 0; + cqp->cq_private = (mblk_t *)NULL; + mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); + mp->b_datap->db_type = M_COPYIN; + if (mp->b_cont) + freemsg(mp->b_cont); + mp->b_cont = (mblk_t *)NULL; + qreply(qp, mp); +} + +static void +vt_copyout(queue_t *qp, mblk_t *mp, mblk_t *tmp, uint_t size) +{ + struct copyreq *cqp; + + cqp = (struct copyreq *)(void *)mp->b_rptr; + cqp->cq_size = size; + cqp->cq_addr = *((caddr_t *)(void *)mp->b_cont->b_rptr); + cqp->cq_flag = 0; + cqp->cq_private = (mblk_t *)NULL; + mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); + mp->b_datap->db_type = M_COPYOUT; + if (mp->b_cont) + freemsg(mp->b_cont); + mp->b_cont = tmp; + qreply(qp, mp); +} + +/* + * Get vc state from minor. + * Once a caller gets a vc_state_t from this function, + * the vc_state_t is guaranteed not being freed before + * the caller leaves this STREAMS module by the D_MTPERMOD + * perimeter. + */ +vc_state_t * +vt_minor2vc(minor_t minor) +{ + avl_index_t where; + vc_state_t target; + + if (minor != VT_ACTIVE) { + target.vc_minor = minor; + return (avl_find(&vc_avl_root, &target, &where)); + } + + if (vc_active_console == VT_MINOR_INVALID) + target.vc_minor = 0; + else + target.vc_minor = vc_active_console; + + return (avl_find(&vc_avl_root, &target, &where)); +} + +static void +vt_state_init(vc_state_t *vcptr, minor_t minor) +{ + mutex_init(&vcptr->vc_state_lock, NULL, MUTEX_DRIVER, NULL); + + mutex_enter(&vcptr->vc_state_lock); + vcptr->vc_flags = 0; + mutex_exit(&vcptr->vc_state_lock); + + vcptr->vc_pid = -1; + vcptr->vc_dispnum = 0; + vcptr->vc_login = 0; + vcptr->vc_switchto = VT_MINOR_INVALID; + vcptr->vc_switch_mode = VT_AUTO; + vcptr->vc_relsig = SIGUSR1; + vcptr->vc_acqsig = SIGUSR1; + vcptr->vc_tem = NULL; + vcptr->vc_bufcallid = 0; + vcptr->vc_timeoutid = 0; + vcptr->vc_wq = NULL; + vcptr->vc_minor = minor; +} + +void +vt_resize(uint_t count) +{ + uint_t vc_num, i; + + ASSERT(MUTEX_HELD(&vc_lock)); + + vc_num = VC_INSTANCES_COUNT; + + if (count == vc_num) + return; + + if (count > vc_num) { + for (i = vc_num; i < count; i++) { + vc_state_t *vcptr = kmem_zalloc(sizeof (vc_state_t), + KM_SLEEP); + vt_state_init(vcptr, i); + avl_add(&vc_avl_root, vcptr); + } + return; + } + + for (i = vc_num; i > count; i--) { + avl_index_t where; + vc_state_t target, *found; + + target.vc_minor = i - 1; + found = avl_find(&vc_avl_root, &target, &where); + ASSERT(found != NULL && found->vc_flags == 0); + avl_remove(&vc_avl_root, found); + kmem_free(found, sizeof (vc_state_t)); + } +} + +static int +vc_avl_compare(const void *first, const void *second) +{ + const vc_state_t *vcptr1 = first; + const vc_state_t *vcptr2 = second; + + if (vcptr1->vc_minor < vcptr2->vc_minor) + return (-1); + + if (vcptr1->vc_minor == vcptr2->vc_minor) + return (0); + + return (1); +} + +/* + * Only called from wc init(). + */ +void +vt_init(void) +{ +#ifdef __lock_lint + ASSERT(NO_COMPETING_THREADS); +#endif + + avl_create(&vc_avl_root, vc_avl_compare, sizeof (vc_state_t), + offsetof(vc_state_t, vc_avl_node)); + + list_create(&vc_waitactive_list, sizeof (vc_waitactive_msg_t), + offsetof(vc_waitactive_msg_t, wa_list_node)); + + mutex_init(&vc_lock, NULL, MUTEX_DRIVER, NULL); + mutex_init(&vt_pending_vtno_lock, NULL, MUTEX_DRIVER, NULL); +} diff --git a/usr/src/uts/common/io/vcons_conf.c b/usr/src/uts/common/io/vcons_conf.c new file mode 100644 index 0000000000..c4b9a9db3b --- /dev/null +++ b/usr/src/uts/common/io/vcons_conf.c @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/termios.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/kmem.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/ddi.h> +#include <sys/bitmap.h> +#include <sys/sysmacros.h> +#include <sys/ddi_impldefs.h> +#include <sys/zone.h> +#include <sys/thread.h> +#ifdef DEBUG +#include <sys/strlog.h> +#endif + +#include <sys/consdev.h> +#include <sys/console.h> +#include <sys/wscons.h> +#include <sys/vt_impl.h> +#include <sys/note.h> +#include <sys/avl.h> + +/* set if console driver is attached */ +dev_info_t *wc_dip = NULL; +/* active virtual console minor number */ +minor_t vc_active_console = VT_MINOR_INVALID; +/* vc_state_t AVL tree */ +avl_tree_t vc_avl_root; +/* virtual console global lock */ +kmutex_t vc_lock; + +_NOTE(MUTEX_PROTECTS_DATA(vc_lock, wc_dip vc_avl_root vc_active_console)) + +/* + * Called from vt devname part. Checks if dip is attached. If it is, + * return its major number. + */ +major_t +vt_wc_attached(void) +{ + major_t maj = (major_t)-1; + + mutex_enter(&vc_lock); + + if (wc_dip) + maj = ddi_driver_major(wc_dip); + + mutex_exit(&vc_lock); + + return (maj); +} + +void +vt_getactive(char *buf, int buflen) +{ + ASSERT(buf); + ASSERT(buflen != 0); + + mutex_enter(&vc_lock); + + if (vc_active_console == 0 || vc_active_console == VT_MINOR_INVALID) + (void) snprintf(buf, buflen, "/dev/console"); + else + (void) snprintf(buf, buflen, "%d", vc_active_console); + + mutex_exit(&vc_lock); +} + +boolean_t +vt_minor_valid(minor_t minor) +{ + if (consmode == CONS_FW) { + if (minor == 0) + return (B_TRUE); + + return (B_FALSE); + } + + mutex_enter(&vc_lock); + if (minor < VC_INSTANCES_COUNT) { + mutex_exit(&vc_lock); + return (B_TRUE); + } + + mutex_exit(&vc_lock); + return (B_FALSE); + +} diff --git a/usr/src/uts/common/io/warlock/tem.wlcmd b/usr/src/uts/common/io/warlock/tem.wlcmd new file mode 100644 index 0000000000..488aef0ed3 --- /dev/null +++ b/usr/src/uts/common/io/warlock/tem.wlcmd @@ -0,0 +1,61 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +add bus_ops::bus_add_eventcall target warlock_dummy +add bus_ops::bus_config target warlock_dummy +add bus_ops::bus_get_eventcookie target warlock_dummy +add bus_ops::bus_intr_ctl target warlock_dummy +add bus_ops::bus_post_event target warlock_dummy +add bus_ops::bus_remove_eventcall target warlock_dummy +add bus_ops::bus_unconfig target warlock_dummy +add tems_modechange_callback/cb target warlock_dummy +add vis_polledio::copy target warlock_dummy +add vis_polledio::cursor target warlock_dummy +add vis_polledio::display target warlock_dummy +add tem_safe_callbacks::tsc_display target tem_safe_text_display +add tem_safe_callbacks::tsc_display target tem_safe_pix_display +add tem_safe_callbacks::tsc_copy target tem_safe_text_copy +add tem_safe_callbacks::tsc_copy target tem_safe_pix_copy +add tem_safe_callbacks::tsc_cursor target tem_safe_text_cursor +add tem_safe_callbacks::tsc_cursor target tem_safe_pix_cursor +add tem_safe_callbacks::tsc_bit2pix target tem_safe_pix_bit2pix +add tem_safe_callbacks::tsc_cls target tem_safe_text_cls +add tem_safe_callbacks::tsc_cls target tem_safe_pix_cls + +root tem_initialized +root tem_get_size +root tem_info_init +root tem_init +root tem_register_modechg_cb +root tem_write +root tems_modechange_callback +root tem_safe_polled_write +root tem_activate +root tem_destroy +root tem_safe_image_display +root tem_switch +root tem_get_fbmode +root tem_set_fbmode diff --git a/usr/src/uts/common/io/warlock/wc.wlcmd b/usr/src/uts/common/io/warlock/wc.wlcmd new file mode 100644 index 0000000000..4283c1ce03 --- /dev/null +++ b/usr/src/uts/common/io/warlock/wc.wlcmd @@ -0,0 +1,88 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +add bus_ops::bus_add_eventcall target warlock_dummy +add bus_ops::bus_config target warlock_dummy +add bus_ops::bus_get_eventcookie target warlock_dummy +add bus_ops::bus_intr_ctl target warlock_dummy +add bus_ops::bus_post_event target warlock_dummy +add bus_ops::bus_remove_eventcall target warlock_dummy +add bus_ops::bus_unconfig target warlock_dummy + +root wcuwput +root wcopen +root wclrput +root wc_polled_enter +root wc_polled_exit +root wc_polled_getchar +root wc_polled_ischar +root wc_polled_putchar +root wcclose +root wcreioctl +root wcrstrt +root wc_modechg_cb +root vc_avl_compare + +if test `uname -p` = "sparc"; then +root wc_cons_wrtvec +root wconsout +root wcopoll +fi + +# +# The devfs part of virtual console only reads these three variables, +# so we only have to hold the lock when writing to these variables. +# +readable wc_dip +readable vc_active_console +readable vc_state::vc_flags + +# +# Only called from sdev_vtops.c, will be checked in wc_devfs.wlcmd +# +ignore vt_getactive +ignore vt_wc_attached + +# +# Protected by D_MTPERMODE +# +ignore vc_state::vc_acqsig +ignore vc_state::vc_bufcallid +ignore vc_state::vc_dispnum +ignore vc_state::vc_fb_mode +ignore vc_state::vc_login +ignore vc_state::vc_minor +ignore vc_state::vc_pid +ignore vc_state::vc_relsig +ignore vc_state::vc_switch_mode +ignore vc_state::vc_switchto +ignore vc_state::vc_tem +ignore vc_state::vc_timeoutid +ignore vc_state::vc_ttycommon.t_iocpending +ignore vc_state::vc_ttycommon.t_readq +ignore vc_state::vc_ttycommon.t_writeq +ignore vc_state::vc_waitv +ignore vc_state::vc_wq diff --git a/usr/src/uts/common/io/warlock/wc_devfs.wlcmd b/usr/src/uts/common/io/warlock/wc_devfs.wlcmd new file mode 100644 index 0000000000..baca059abe --- /dev/null +++ b/usr/src/uts/common/io/warlock/wc_devfs.wlcmd @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This file only checks those virtual console variables +# which are shared with devfs. +# + + +add bus_ops::bus_add_eventcall target warlock_dummy +add bus_ops::bus_config target warlock_dummy +add bus_ops::bus_get_eventcookie target warlock_dummy +add bus_ops::bus_intr_ctl target warlock_dummy +add bus_ops::bus_post_event target warlock_dummy +add bus_ops::bus_remove_eventcall target warlock_dummy +add bus_ops::bus_unconfig target warlock_dummy + +root devvt_getvnodeops +root devvt_validate +root devvt_readdir +root devvt_lookup +root devvt_create diff --git a/usr/src/uts/common/io/wscons.c b/usr/src/uts/common/io/wscons.c index 21b4b3fdd7..21ea9bf2a6 100644 --- a/usr/src/uts/common/io/wscons.c +++ b/usr/src/uts/common/io/wscons.c @@ -24,7 +24,6 @@ * Use is subject to license terms. */ - /* * "Workstation console" multiplexor driver for Sun. * @@ -33,6 +32,54 @@ * driver", below which is linked the primary keyboard. */ +/* + * Locking Policy: + * This module has a D_MTPERMOD inner perimeter which means STREAMS + * only allows one thread to enter this module through STREAMS entry + * points each time -- open() close() put() srv() qtimeout(). + * So for the most time we do not need locking in this module, but with + * the following exceptions: + * + * - wc shares three global variables (wc_dip, vc_active_consle, vc_avl_root) + * with virtual console devname part (fs/dev/sdev_vtops.c) which get + * compiled into genunix. + * + * - wc_modechg_cb() is a callback function which will triggered when + * framebuffer display mode is changed. + * + * - vt_send_hotkeys() is triggered by timeout() which is not STREAMS MT + * safe. + * + * Based on the fact that virtual console devname part and wc_modechg_cb() + * only do read access to the above mentioned shared three global variables, + * It is safe to do locking this way: + * 1) all read access to the three global variables in THIS WC MODULE do not + * need locking; + * 2) all write access to the three global variables in THIS WC MODULE must + * hold vc_lock; + * 3) any access to the three global variables in either DEVNAME PART or the + * CALLBACK must hold vc_lock; + * 4) other global variables which are only shared in this wc module and only + * accessible through STREAMS entry points such as "vc_last_console", + * "vc_inuse_max_minor", "vc_target_console" and "vc_waitactive_list" + * do not need explict locking. + * + * wc_modechg_cb() does read access to vc_state_t::vc_flags, + * vc_state_t::vc_state_lock is used to protect concurrently accesses to + * vc_state_t::vc_flags which may happen from both through STREAMS entry + * points and wc_modechg_cb(). + * Since wc_modechg_cb() only does read access to vc_state_t::vc_flags, + * The other parts of wc module (except wc_modechg_cb()) only has to hold + * vc_state_t::vc_flags when writing to vc_state_t::vc_flags. + * + * vt_send_hotkeys() could access vt_pending_vtno at the same time with + * the rest of wc module, vt_pending_vtno_lock is used to protect + * vt_pending_vtno. + * + * Lock order: vc_lock -> vc_state_t::vc_state_lock. + * No overlap between vc_lock and vt_pending_vtno_lock. + */ + #include <sys/types.h> #include <sys/param.h> #include <sys/signal.h> @@ -48,6 +95,14 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/errno.h> +#include <sys/proc.h> +#include <sys/procset.h> +#include <sys/fault.h> +#include <sys/siginfo.h> +#include <sys/debug.h> +#include <sys/session.h> #include <sys/kmem.h> #include <sys/cpuvar.h> #include <sys/kbio.h> @@ -55,15 +110,33 @@ #include <sys/fs/snode.h> #include <sys/consdev.h> #include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/console.h> +#include <sys/promif.h> +#include <sys/note.h> +#include <sys/polled_io.h> +#include <sys/systm.h> #include <sys/ddi.h> #include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/esunddi.h> +#include <sys/sunldi.h> #include <sys/debug.h> #include <sys/console.h> #include <sys/ddi_impldefs.h> -#include <sys/promif.h> #include <sys/policy.h> +#include <sys/modctl.h> #include <sys/tem.h> #include <sys/wscons.h> +#include <sys/vt_impl.h> + +/* streams stuff */ +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyreq)) +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyresp)) +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", datab)) +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", iocblk)) +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", msgb)) +_NOTE(SCHEME_PROTECTS_DATA("Unshared data", queue)) #define MINLINES 10 #define MAXLINES 48 @@ -75,30 +148,37 @@ #define LOSCREENCOLS 80 #define HISCREENCOLS 120 -struct wscons { - struct tem *wc_tem; /* Terminal emulator state */ - int wc_flags; /* random flags (protected by */ - /* write-side exclusion lock */ +struct wscons_state { dev_t wc_dev; /* major/minor for this device */ - tty_common_t wc_ttycommon; /* data common to all tty drivers */ #ifdef _HAVE_TEM_FIRMWARE - int wc_pendc; /* pending output character */ int wc_defer_output; /* set if output device is "slow" */ #endif /* _HAVE_TEM_FIRMWARE */ queue_t *wc_kbdqueue; /* "console keyboard" device queue */ /* below us */ - bufcall_id_t wc_bufcallid; /* id returned by qbufcall */ - timeout_id_t wc_timeoutid; /* id returned by qtimeout */ cons_polledio_t wc_polledio; /* polled I/O function pointers */ cons_polledio_t *wc_kb_polledio; /* keyboard's polledio */ unsigned int wc_kb_getpolledio_id; /* id for kb CONSOPENPOLLEDIO */ + queue_t *wc_pending_wq; mblk_t *wc_pending_link; /* I_PLINK pending for kb polledio */ } wscons; -#define WCS_ISOPEN 0x00000001 /* open is complete */ -#define WCS_STOPPED 0x00000002 /* output is stopped */ -#define WCS_DELAY 0x00000004 /* waiting for delay to finish */ -#define WCS_BUSY 0x00000008 /* waiting for transmission to finish */ +/* + * This module has a D_MTPERMOD inner perimeter, so we don't need to protect + * the variables only shared within this module + */ +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons_state)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_stat)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_waitactive_msg)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", tty_common)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_mode)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_dispinfo)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", winsize)) +_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_last_console)) + +#ifdef _HAVE_TEM_FIRMWARE +ssize_t wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n); +#endif /* _HAVE_TEM_FIRMWARE */ static int wcopen(queue_t *, dev_t *, int, int, cred_t *); static int wcclose(queue_t *, int, cred_t *); @@ -166,7 +246,6 @@ static struct streamtab wcinfo = { static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result); static int wc_attach(dev_info_t *, ddi_attach_cmd_t); -static dev_info_t *wc_dip; DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev, wc_info, D_MTPERMOD | D_MP, &wcinfo, ddi_quiesce_not_supported); @@ -178,24 +257,20 @@ static void wcopoll(void *); static void wconsout(void *); #endif /* _HAVE_TEM_FIRMWARE */ static void wcrstrt(void *); -static void wcstart(void); -static void wc_open_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp); -static void wc_close_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp); -static void wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c); +static void wcstart(void *); +static void wc_open_kb_polledio(struct wscons_state *wc, queue_t *q, + mblk_t *mp); +static void wc_close_kb_polledio(struct wscons_state *wc, queue_t *q, + mblk_t *mp); +static void wc_polled_putchar(cons_polledio_arg_t arg, + unsigned char c); static boolean_t wc_polled_ischar(cons_polledio_arg_t arg); static int wc_polled_getchar(cons_polledio_arg_t arg); static void wc_polled_enter(cons_polledio_arg_t arg); static void wc_polled_exit(cons_polledio_arg_t arg); -static void wc_get_size(struct wscons *wscons); +void wc_get_size(vc_state_t *pvc); static void wc_modechg_cb(tem_modechg_cb_arg_t arg); -#include <sys/types.h> -#include <sys/conf.h> -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/errno.h> -#include <sys/modctl.h> - static struct dev_ops wc_ops; /* @@ -209,7 +284,6 @@ static void wc_dprintf(const char *fmt, ...) __KPRINTFLIKE(1); (((l) >= wc_errlevel) && ((m) & wc_errmask) ? \ wc_dprintf args : \ (void) 0) - /* * Severity levels for printing */ @@ -232,7 +306,6 @@ uint_t wc_errlevel = PRINT_L2; /* * Module linkage information for the kernel. */ - static struct modldrv modldrv = { &mod_driverops, /* Type of module. This one is a pseudo driver */ "Workstation multiplexer Driver 'wc'", @@ -248,7 +321,10 @@ static struct modlinkage modlinkage = { int _init(void) { - return (mod_install(&modlinkage)); + int rc; + if ((rc = mod_install(&modlinkage)) == 0) + vt_init(); + return (rc); } int @@ -267,15 +343,23 @@ _info(struct modinfo *modinfop) static int wc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { + /* create minor node for workstation hard console */ if (ddi_create_minor_node(devi, "wscons", S_IFCHR, 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); - return (-1); + return (DDI_FAILURE); } + + mutex_enter(&vc_lock); + wc_dip = devi; bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio)); + vt_resize(VC_DEFAULT_COUNT); + + mutex_exit(&vc_lock); + return (DDI_SUCCESS); } @@ -313,104 +397,95 @@ wc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, static char obuf[MAXHIWAT]; #endif /* _HAVE_TEM_FIRMWARE */ -/*ARGSUSED*/ -static int -wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp) +static void +wc_init_polledio(void) { static boolean_t polledio_inited = B_FALSE; - struct termios *termiosp; - int len; - - if (getminor(*devp) != 0) - return (ENXIO); /* sorry, only one per customer */ + _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", + polledio_inited)) - if (!(wscons.wc_flags & WCS_ISOPEN)) { - mutex_init(&wscons.wc_ttycommon.t_excl, NULL, MUTEX_DEFAULT, - NULL); - wscons.wc_ttycommon.t_iflag = 0; - /* - * Get the default termios settings (cflag). - * These are stored as a property in the - * "options" node. - */ - if (ddi_getlongprop(DDI_DEV_T_ANY, - ddi_root_node(), 0, "ttymodes", - (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS && - len == sizeof (struct termios)) { - - wscons.wc_ttycommon.t_cflag = termiosp->c_cflag; - kmem_free(termiosp, len); - } else { - /* - * Gack! Whine about it. - */ - cmn_err(CE_WARN, - "wc: Couldn't get ttymodes property!\n"); - } - wscons.wc_ttycommon.t_iocpending = NULL; - wscons.wc_flags = WCS_ISOPEN; - - wscons.wc_dev = *devp; - wc_get_size(&wscons); + if (polledio_inited) + return; - if (!polledio_inited) { - polledio_inited = B_TRUE; + polledio_inited = B_TRUE; - /* - * Initialize the parts of the polled I/O struct that - * are common to both input and output modes, but which - * don't flag to the upper layers, which if any of the - * two modes are available. We don't know at this point - * if system is configured CONS_KFB, but we will when - * consconfig_dacf asks us with CONSOPENPOLLED I/O. - */ - wscons.wc_polledio.cons_polledio_version = - CONSPOLLEDIO_V0; - wscons.wc_polledio.cons_polledio_argument = - (cons_polledio_arg_t)&wscons; - wscons.wc_polledio.cons_polledio_enter = - wc_polled_enter; - wscons.wc_polledio.cons_polledio_exit = - wc_polled_exit; + /* + * Initialize the parts of the polled I/O struct that + * are common to both input and output modes, but which + * don't flag to the upper layers, which if any of the + * two modes are available. We don't know at this point + * if system is configured CONS_KFB, but we will when + * consconfig_dacf asks us with CONSOPENPOLLED I/O. + */ + bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio)); + wscons.wc_polledio.cons_polledio_version = + CONSPOLLEDIO_V0; + wscons.wc_polledio.cons_polledio_argument = + (cons_polledio_arg_t)&wscons; + wscons.wc_polledio.cons_polledio_enter = + wc_polled_enter; + wscons.wc_polledio.cons_polledio_exit = + wc_polled_exit; #ifdef _HAVE_TEM_FIRMWARE - /* - * If we're talking directly to a framebuffer, we assume - * that it's a "slow" device, so that rendering should - * be deferred to a timeout or softcall so that we write - * a bunch of characters at once. - */ - wscons.wc_defer_output = prom_stdout_is_framebuffer(); + /* + * If we're talking directly to a framebuffer, we assume + * that it's a "slow" device, so that rendering should + * be deferred to a timeout or softcall so that we write + * a bunch of characters at once. + */ + wscons.wc_defer_output = prom_stdout_is_framebuffer(); #endif /* _HAVE_TEM_FIRMWARE */ - } - } +} - if (wscons.wc_ttycommon.t_flags & TS_XCLUDE) { - if (secpolicy_excl_open(crp) != 0) { - return (EBUSY); - } - } - wscons.wc_ttycommon.t_readq = q; - wscons.wc_ttycommon.t_writeq = WR(q); - qprocson(q); - return (0); +/*ARGSUSED*/ +static int +wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp) +{ + int minor; + + wc_init_polledio(); + minor = (int)getminor(*devp); + return (vt_open(minor, q, crp)); } /*ARGSUSED*/ static int wcclose(queue_t *q, int flag, cred_t *crp) { + vc_state_t *pvc = (vc_state_t *)q->q_ptr; + qprocsoff(q); - if (wscons.wc_bufcallid != 0) { - qunbufcall(q, wscons.wc_bufcallid); - wscons.wc_bufcallid = 0; - } - if (wscons.wc_timeoutid != 0) { - (void) quntimeout(q, wscons.wc_timeoutid); - wscons.wc_timeoutid = 0; + + mutex_enter(&vc_lock); + + if (pvc->vc_minor == 0 || pvc->vc_minor == vc_active_console) { + + /* + * If we lose the system console, + * no any other active consoles. + */ + if (pvc->vc_minor == 0 && pvc->vc_minor == vc_active_console) { + vc_active_console = VT_MINOR_INVALID; + vc_last_console = VT_MINOR_INVALID; + } + + /* + * just clean for our primary console + * and active console + */ + mutex_enter(&pvc->vc_state_lock); + vt_clean(q, pvc); + mutex_exit(&pvc->vc_state_lock); + + mutex_exit(&vc_lock); + + return (0); } - ttycommon_close(&wscons.wc_ttycommon); - wscons.wc_flags = 0; + vt_close(q, pvc, crp); + + mutex_exit(&vc_lock); + return (0); } @@ -424,16 +499,24 @@ wcclose(queue_t *q, int flag, cred_t *crp) static int wcuwput(queue_t *q, mblk_t *mp) { + vc_state_t *pvc = (vc_state_t *)q->q_ptr; + switch (mp->b_datap->db_type) { case M_STOP: - wscons.wc_flags |= WCS_STOPPED; + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags |= WCS_STOPPED; + mutex_exit(&pvc->vc_state_lock); + freemsg(mp); break; case M_START: - wscons.wc_flags &= ~WCS_STOPPED; - wcstart(); + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags &= ~WCS_STOPPED; + mutex_exit(&pvc->vc_state_lock); + + wcstart(pvc); freemsg(mp); break; @@ -441,7 +524,7 @@ wcuwput(queue_t *q, mblk_t *mp) struct iocblk *iocp; struct linkblk *linkp; - iocp = (void *)mp->b_rptr; + iocp = (struct iocblk *)(void *)mp->b_rptr; switch (iocp->ioc_cmd) { case I_LINK: /* stupid, but permitted */ @@ -451,7 +534,7 @@ wcuwput(queue_t *q, mblk_t *mp) miocnak(q, mp, 0, EINVAL); return (0); } - linkp = (void *)mp->b_cont->b_rptr; + linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr; wscons.wc_kbdqueue = WR(linkp->l_qbot); mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = 0; @@ -460,7 +543,7 @@ wcuwput(queue_t *q, mblk_t *mp) case I_UNLINK: /* stupid, but permitted */ case I_PUNLINK: - linkp = (void *)mp->b_cont->b_rptr; + linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr; if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) { /* not us */ miocnak(q, mp, 0, EINVAL); @@ -486,13 +569,14 @@ wcuwput(queue_t *q, mblk_t *mp) * start routine, just in case. */ (void) putq(q, mp); - wcstart(); + wcstart(pvc); break; case CONSSETABORTENABLE: case CONSGETABORTENABLE: case KIOCSDIRECT: if (wscons.wc_kbdqueue != NULL) { + wscons.wc_pending_wq = q; (void) putnext(wscons.wc_kbdqueue, mp); break; } @@ -537,7 +621,11 @@ wcuwput(queue_t *q, mblk_t *mp) * and poke the start routine. */ (void) putq(q, mp); - wcstart(); + wcstart(pvc); + break; + + case M_IOCDATA: + vt_miocdata(q, mp); break; default: @@ -560,14 +648,15 @@ wcuwput(queue_t *q, mblk_t *mp) static void wcreioctl(void *arg) { + vc_state_t *pvc = (vc_state_t *)arg; queue_t *q; mblk_t *mp; - wscons.wc_bufcallid = 0; - q = wscons.wc_ttycommon.t_writeq; - if ((mp = wscons.wc_ttycommon.t_iocpending) != NULL) { + pvc->vc_bufcallid = 0; + q = pvc->vc_ttycommon.t_writeq; + if ((mp = pvc->vc_ttycommon.t_iocpending) != NULL) { /* not pending any more */ - wscons.wc_ttycommon.t_iocpending = NULL; + pvc->vc_ttycommon.t_iocpending = NULL; wcioctl(q, mp); } } @@ -618,12 +707,19 @@ wc_getterm(mblk_t *mp) static void wcioctl(queue_t *q, mblk_t *mp) { + vc_state_t *pvc = (vc_state_t *)q->q_ptr; struct iocblk *iocp; size_t datasize; int error; long len; - iocp = (void *)mp->b_rptr; + iocp = (struct iocblk *)(void *)mp->b_rptr; + + if ((iocp->ioc_cmd & VTIOC) == VTIOC || + (iocp->ioc_cmd & KDIOC) == KDIOC) { + vt_ioctl(q, mp); + return; + } switch (iocp->ioc_cmd) { case TIOCSWINSZ: @@ -698,21 +794,17 @@ wcioctl(queue_t *q, mblk_t *mp) iocp->ioc_error = EINVAL; /* - * If we're already open, fail. - */ - if (wscons.wc_tem != NULL) - goto open_fail; - - /* * If we don't have exactly one continuation block, fail. */ - if (mp->b_cont == NULL || mp->b_cont->b_cont != NULL) + if (mp->b_cont == NULL || + mp->b_cont->b_cont != NULL) goto open_fail; /* * If there's no null terminator in the string, fail. */ - len = MBLKL(mp->b_cont); + /* LINTED E_PTRDIFF_OVERFLOW */ + len = mp->b_cont->b_wptr - mp->b_cont->b_rptr; if (memchr(mp->b_cont->b_rptr, 0, len) == NULL) goto open_fail; @@ -720,8 +812,8 @@ wcioctl(queue_t *q, mblk_t *mp) * NOTE: should eventually get default * dimensions from a property, e.g. screen-#rows. */ - iocp->ioc_error = tem_init(&wscons.wc_tem, - (char *)mp->b_cont->b_rptr, iocp->ioc_cr); + iocp->ioc_error = tem_info_init((char *)mp->b_cont->b_rptr, + iocp->ioc_cr); /* * Of course, if the terminal emulator initialization * failed, fail. @@ -729,13 +821,23 @@ wcioctl(queue_t *q, mblk_t *mp) if (iocp->ioc_error != 0) goto open_fail; - tem_register_modechg_cb(wscons.wc_tem, wc_modechg_cb, - (tem_modechg_cb_arg_t)&wscons); +#ifdef _HAVE_TEM_FIRMWARE + if (prom_stdout_is_framebuffer()) { + /* + * Drivers in the console stream may emit additional + * messages before we are ready. This causes text + * overwrite on the screen. So we set the redirection + * here. It is safe because the ioctl in consconfig_dacf + * will succeed and consmode will be set to CONS_KFB. + */ + prom_set_stdout_redirect(wc_cons_wrtvec, + (promif_redir_arg_t)NULL); - /* - * Refresh terminal size with info from terminal emulator. - */ - wc_get_size(&wscons); + } +#endif /* _HAVE_TEM_FIRMWARE */ + + tem_register_modechg_cb(wc_modechg_cb, + (tem_modechg_cb_arg_t)&wscons); /* * ... and succeed. @@ -779,12 +881,12 @@ close_fail: * request that we be called back when we stand a * better chance of allocating the data. */ - datasize = ttycommon_ioctl(&wscons.wc_ttycommon, q, mp, &error); + datasize = ttycommon_ioctl(&pvc->vc_ttycommon, q, mp, &error); if (datasize != 0) { - if (wscons.wc_bufcallid != 0) - qunbufcall(q, wscons.wc_bufcallid); - wscons.wc_bufcallid = qbufcall(q, datasize, BPRI_HI, - wcreioctl, NULL); + if (pvc->vc_bufcallid != 0) + qunbufcall(q, pvc->vc_bufcallid); + pvc->vc_bufcallid = qbufcall(q, datasize, BPRI_HI, + wcreioctl, pvc); return; } @@ -810,7 +912,7 @@ close_fail: * the lower driver services this message. */ static void -wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) +wc_open_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp) { mblk_t *mp2; struct iocblk *iocp; @@ -838,12 +940,13 @@ wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) goto nomem; } - iocp = (void *)mp2->b_rptr; + iocp = (struct iocblk *)(void *)mp2->b_rptr; iocp->ioc_count = sizeof (struct cons_polledio *); mp2->b_cont->b_wptr = mp2->b_cont->b_rptr + sizeof (struct cons_polledio *); + wscons->wc_pending_wq = q; wscons->wc_pending_link = mp; wscons->wc_kb_getpolledio_id = iocp->ioc_id; @@ -852,7 +955,7 @@ wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) return; nomem: - iocp = (void *)mp->b_rptr; + iocp = (struct iocblk *)(void *)mp->b_rptr; iocp->ioc_error = ENOMEM; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); @@ -865,7 +968,7 @@ nomem: * driver services this message. */ static void -wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) +wc_close_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp) { mblk_t *mp2; struct iocblk *iocp; @@ -894,10 +997,11 @@ wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) goto nomem; } - iocp = (void *)mp2->b_rptr; + iocp = (struct iocblk *)(void *)mp2->b_rptr; iocp->ioc_count = 0; + wscons->wc_pending_wq = q; wscons->wc_pending_link = mp; wscons->wc_kb_getpolledio_id = iocp->ioc_id; @@ -906,7 +1010,7 @@ wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) return; nomem: - iocp = (void *)mp->b_rptr; + iocp = (struct iocblk *)(void *)mp->b_rptr; iocp->ioc_error = ENOMEM; mp->b_datap->db_type = M_IOCNAK; qreply(q, mp); @@ -917,20 +1021,26 @@ nomem: static void wcopoll(void *arg) { + vc_state_t *pvc = (vc_state_t *)arg; queue_t *q; - q = wscons.wc_ttycommon.t_writeq; - wscons.wc_timeoutid = 0; + q = pvc->vc_ttycommon.t_writeq; + pvc->vc_timeoutid = 0; + + mutex_enter(&pvc->vc_state_lock); + /* See if we can continue output */ - if ((wscons.wc_flags & WCS_BUSY) && wscons.wc_pendc != -1) { - if (prom_mayput((char)wscons.wc_pendc) == 0) { - wscons.wc_pendc = -1; - wscons.wc_flags &= ~WCS_BUSY; - if (!(wscons.wc_flags&(WCS_DELAY|WCS_STOPPED))) - wcstart(); + if ((pvc->vc_flags & WCS_BUSY) && pvc->vc_pendc != -1) { + if (prom_mayput((char)pvc->vc_pendc) == 0) { + pvc->vc_pendc = -1; + pvc->vc_flags &= ~WCS_BUSY; + if (!(pvc->vc_flags&(WCS_DELAY|WCS_STOPPED))) + wcstart(pvc); } else - wscons.wc_timeoutid = qtimeout(q, wcopoll, NULL, 1); + pvc->vc_timeoutid = qtimeout(q, wcopoll, pvc, 1); } + + mutex_exit(&pvc->vc_state_lock); } #endif /* _HAVE_TEM_FIRMWARE */ @@ -941,17 +1051,38 @@ wcopoll(void *arg) static void wcrstrt(void *arg) { - ASSERT(wscons.wc_ttycommon.t_writeq != NULL); - wscons.wc_flags &= ~WCS_DELAY; - wcstart(); + vc_state_t *pvc = (vc_state_t *)arg; + + ASSERT(pvc->vc_ttycommon.t_writeq != NULL); + + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags &= ~WCS_DELAY; + mutex_exit(&pvc->vc_state_lock); + + wcstart(pvc); +} + +/* + * get screen terminal for current output + */ +static tem_vt_state_t +wc_get_screen_tem(vc_state_t *pvc) +{ + if (!tem_initialized(pvc->vc_tem) || + tem_get_fbmode(pvc->vc_tem) != KD_TEXT) + return (NULL); + + return (pvc->vc_tem); } /* * Start console output */ static void -wcstart(void) +wcstart(void *arg) { + vc_state_t *pvc = (vc_state_t *)arg; + tem_vt_state_t ptem = NULL; #ifdef _HAVE_TEM_FIRMWARE int c; ssize_t cc; @@ -966,14 +1097,14 @@ wcstart(void) * restarted, output to finish draining), don't grab anything * new. */ - if (wscons.wc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED)) + if (pvc->vc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED)) return; - q = wscons.wc_ttycommon.t_writeq; + q = pvc->vc_ttycommon.t_writeq; /* * assumes that we have been called by whoever holds the * exclusionary lock on the write-side queue (protects - * wc_flags and wc_pendc). + * vc_flags and vc_pendc). */ for (;;) { if ((bp = getq(q)) == NULL) @@ -993,11 +1124,15 @@ wcstart(void) * delay expires; it will turn WCS_DELAY off, * and call "wcstart" to grab the next message. */ - if (wscons.wc_timeoutid != 0) - (void) quntimeout(q, wscons.wc_timeoutid); - wscons.wc_timeoutid = qtimeout(q, wcrstrt, NULL, + if (pvc->vc_timeoutid != 0) + (void) quntimeout(q, pvc->vc_timeoutid); + pvc->vc_timeoutid = qtimeout(q, wcrstrt, pvc, (clock_t)(*(unsigned char *)bp->b_rptr + 6)); - wscons.wc_flags |= WCS_DELAY; + + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags |= WCS_DELAY; + mutex_exit(&pvc->vc_state_lock); + freemsg(bp); return; /* wait for this to finish */ @@ -1014,23 +1149,34 @@ wcstart(void) #ifdef _HAVE_TEM_FIRMWARE if (consmode == CONS_KFB) { #endif /* _HAVE_TEM_FIRMWARE */ - if (wscons.wc_tem != NULL) { + if ((ptem = wc_get_screen_tem(pvc)) != NULL) { + for (nbp = bp; nbp != NULL; nbp = nbp->b_cont) { if (nbp->b_wptr > nbp->b_rptr) { - (void) tem_write(wscons.wc_tem, + (void) tem_write(ptem, nbp->b_rptr, - MBLKL(nbp), + /* LINTED */ + nbp->b_wptr - nbp->b_rptr, kcred); } } - freemsg(bp); + } + + freemsg(bp); + #ifdef _HAVE_TEM_FIRMWARE continue; } /* consmode = CONS_FW */ - if ((cc = MBLKL(bp)) == 0) { + if (pvc->vc_minor != 0) { + freemsg(bp); + continue; + } + + /* LINTED E_PTRDIFF_OVERFLOW */ + if ((cc = bp->b_wptr - bp->b_rptr) == 0) { freemsg(bp); continue; } @@ -1043,17 +1189,20 @@ wcstart(void) * Never do output here; * it takes forever. */ - wscons.wc_flags |= WCS_BUSY; - wscons.wc_pendc = -1; + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags |= WCS_BUSY; + mutex_exit(&pvc->vc_state_lock); + + pvc->vc_pendc = -1; (void) putbq(q, bp); if (q->q_count > 128) { /* do it soon */ - softcall(wconsout, NULL); + softcall(wconsout, pvc); } else { /* wait a bit */ - if (wscons.wc_timeoutid != 0) + if (pvc->vc_timeoutid != 0) (void) quntimeout(q, - wscons.wc_timeoutid); - wscons.wc_timeoutid = qtimeout(q, wconsout, - NULL, hz / 30); + pvc->vc_timeoutid); + pvc->vc_timeoutid = qtimeout(q, wconsout, + pvc, hz / 30); } return; } @@ -1061,13 +1210,17 @@ wcstart(void) c = *bp->b_rptr++; cc--; if (prom_mayput((char)c) != 0) { - wscons.wc_flags |= WCS_BUSY; - wscons.wc_pendc = c; - if (wscons.wc_timeoutid != 0) + + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags |= WCS_BUSY; + mutex_exit(&pvc->vc_state_lock); + + pvc->vc_pendc = c; + if (pvc->vc_timeoutid != 0) (void) quntimeout(q, - wscons.wc_timeoutid); - wscons.wc_timeoutid = qtimeout(q, wcopoll, - NULL, 1); + pvc->vc_timeoutid); + pvc->vc_timeoutid = qtimeout(q, wcopoll, + pvc, 1); if (bp != NULL) /* not done with this message yet */ (void) putbq(q, bp); @@ -1079,7 +1232,8 @@ wcstart(void) freeb(nbp); if (bp == NULL) return; - cc = MBLKL(bp); + /* LINTED E_PTRDIFF_OVERFLOW */ + cc = bp->b_wptr - bp->b_rptr; } } #endif /* _HAVE_TEM_FIRMWARE */ @@ -1093,8 +1247,9 @@ wcstart(void) */ /* ARGSUSED */ static void -wconsout(void *dummy) +wconsout(void *arg) { + vc_state_t *pvc = (vc_state_t *)arg; uchar_t *cp; ssize_t cc; queue_t *q; @@ -1103,7 +1258,7 @@ wconsout(void *dummy) char *current_position; ssize_t bytes_left; - if ((q = wscons.wc_ttycommon.t_writeq) == NULL) { + if ((q = pvc->vc_ttycommon.t_writeq) == NULL) { return; /* not attached to a stream */ } @@ -1126,7 +1281,8 @@ wconsout(void *dummy) do { cp = bp->b_rptr; - cc = MBLKL(bp); + /* LINTED E_PTRDIFF_OVERFLOW */ + cc = bp->b_wptr - cp; while (cc != 0) { if (bytes_left == 0) { /* @@ -1152,8 +1308,11 @@ transmit: if ((cc = MAXHIWAT - bytes_left) != 0) console_puts(obuf, cc); - wscons.wc_flags &= ~WCS_BUSY; - wcstart(); + mutex_enter(&pvc->vc_state_lock); + pvc->vc_flags &= ~WCS_BUSY; + mutex_exit(&pvc->vc_state_lock); + + wcstart(pvc); } #endif /* _HAVE_TEM_FIRMWARE */ @@ -1164,9 +1323,12 @@ transmit: static int wclrput(queue_t *q, mblk_t *mp) { + vc_state_t *pvc; queue_t *upq; struct iocblk *iocp; + pvc = vt_minor2vc(VT_ACTIVE); + DPRINTF(PRINT_L1, PRINT_MASK_ALL, ("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type)); @@ -1190,20 +1352,26 @@ wclrput(queue_t *q, mblk_t *mp) break; case M_DATA: - if ((upq = wscons.wc_ttycommon.t_readq) != NULL) { + if (consmode == CONS_KFB && vt_check_hotkeys(mp)) { + freemsg(mp); + break; + } + + if ((upq = pvc->vc_ttycommon.t_readq) != NULL) { if (!canput(upq->q_next)) { - ttycommon_qfull(&wscons.wc_ttycommon, upq); - wcstart(); + ttycommon_qfull(&pvc->vc_ttycommon, upq); + wcstart(pvc); freemsg(mp); - } else + } else { putnext(upq, mp); + } } else freemsg(mp); break; case M_IOCACK: case M_IOCNAK: - iocp = (void *)mp->b_rptr; + iocp = (struct iocblk *)(void *)mp->b_rptr; if (wscons.wc_pending_link != NULL && iocp->ioc_id == wscons.wc_kb_getpolledio_id) { switch (mp->b_datap->db_type) { @@ -1211,14 +1379,13 @@ wclrput(queue_t *q, mblk_t *mp) case M_IOCACK: switch (iocp->ioc_cmd) { - case CONSOPENPOLLEDIO: DPRINTF(PRINT_L1, PRINT_MASK_ALL, ("wclrput: " "ACK CONSOPENPOLLEDIO\n")); wscons.wc_kb_polledio = - *(struct cons_polledio **)(void *) - mp->b_cont->b_rptr; + *(struct cons_polledio **) + (void *)mp->b_cont->b_rptr; wscons.wc_polledio. cons_polledio_getchar = wc_polled_getchar; @@ -1240,7 +1407,8 @@ wclrput(queue_t *q, mblk_t *mp) break; default: DPRINTF(PRINT_L1, PRINT_MASK_ALL, - ("wclrput: ACK UNKNOWN\n")); + ("wclrput: " + "ACK UNKNOWN\n")); } break; @@ -1282,11 +1450,17 @@ wclrput(queue_t *q, mblk_t *mp) /* FALLTHROUGH */ default: /* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */ - DPRINTF(PRINT_L1, PRINT_MASK_ALL, - ("wclrput: Message DISCARDED\n")); - if ((upq = wscons.wc_ttycommon.t_readq) != NULL) { + if (wscons.wc_pending_wq != NULL) { + qreply(wscons.wc_pending_wq, mp); + wscons.wc_pending_wq = NULL; + break; + } + + if ((upq = pvc->vc_ttycommon.t_readq) != NULL) { putnext(upq, mp); } else { + DPRINTF(PRINT_L1, PRINT_MASK_ALL, + ("wclrput: Message DISCARDED\n")); freemsg(mp); } break; @@ -1295,6 +1469,38 @@ wclrput(queue_t *q, mblk_t *mp) return (0); } +#ifdef _HAVE_TEM_FIRMWARE +/* + * This routine exists so that prom_write() can redirect writes + * to the framebuffer through the kernel terminal emulator, if + * that configuration is selected during consconfig. + * When the kernel terminal emulator is enabled, consconfig_dacf + * sets up the PROM output redirect vector to enter this function. + * During panic the console will already be powered up as part of + * calling into the prom_*() layer. + */ +/* ARGSUSED */ +ssize_t +wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n) +{ + vc_state_t *pvc; + + pvc = vt_minor2vc(VT_ACTIVE); + + if (pvc->vc_tem == NULL) + return (0); + + ASSERT(consmode == CONS_KFB); + + if (panicstr) + polled_io_cons_write(s, n); + else + (void) tem_write(pvc->vc_tem, s, n, kcred); + + return (n); +} +#endif /* _HAVE_TEM_FIRMWARE */ + /* * These are for systems without OBP, and for devices that cannot be * shared between Solaris and the OBP. @@ -1302,10 +1508,14 @@ wclrput(queue_t *q, mblk_t *mp) static void wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c) { + vc_state_t *pvc; + + pvc = vt_minor2vc(VT_ACTIVE); + if (c == '\n') wc_polled_putchar(arg, '\r'); - if (wscons.wc_tem == NULL) { + if (pvc->vc_tem == NULL) { /* * We have no terminal emulator configured. We have no * recourse but to drop the output on the floor. @@ -1313,7 +1523,7 @@ wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c) return; } - tem_polled_write(wscons.wc_tem, &c, 1); + tem_safe_polled_write(pvc->vc_tem, &c, 1); } /* @@ -1323,7 +1533,7 @@ wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c) static int wc_polled_getchar(cons_polledio_arg_t arg) { - struct wscons *wscons = (struct wscons *)arg; + struct wscons_state *wscons = (struct wscons_state *)arg; if (wscons->wc_kb_polledio == NULL) { prom_printf("wscons: getchar with no keyboard support"); @@ -1339,7 +1549,7 @@ wc_polled_getchar(cons_polledio_arg_t arg) static boolean_t wc_polled_ischar(cons_polledio_arg_t arg) { - struct wscons *wscons = (struct wscons *)arg; + struct wscons_state *wscons = (struct wscons_state *)arg; if (wscons->wc_kb_polledio == NULL) return (B_FALSE); @@ -1351,7 +1561,7 @@ wc_polled_ischar(cons_polledio_arg_t arg) static void wc_polled_enter(cons_polledio_arg_t arg) { - struct wscons *wscons = (struct wscons *)arg; + struct wscons_state *wscons = (struct wscons_state *)arg; if (wscons->wc_kb_polledio == NULL) return; @@ -1365,7 +1575,7 @@ wc_polled_enter(cons_polledio_arg_t arg) static void wc_polled_exit(cons_polledio_arg_t arg) { - struct wscons *wscons = (struct wscons *)arg; + struct wscons_state *wscons = (struct wscons_state *)arg; if (wscons->wc_kb_polledio == NULL) return; @@ -1392,41 +1602,69 @@ wc_dprintf(const char *fmt, ...) } #endif +/*ARGSUSED*/ static void -update_property(struct wscons *wscons, char *name, ushort_t value) +update_property(vc_state_t *pvc, char *name, ushort_t value) { char data[8]; (void) snprintf(data, sizeof (data), "%u", value); - (void) ddi_prop_update_string(wscons->wc_dev, wc_dip, name, data); + + (void) ddi_prop_update_string(wscons.wc_dev, wc_dip, name, data); } /* * Gets the number of text rows and columns and the * width and height (in pixels) of the console. */ -static void -wc_get_size(struct wscons *wscons) +void +wc_get_size(vc_state_t *pvc) { - struct winsize *t = &wscons->wc_ttycommon.t_size; + struct winsize *t = &pvc->vc_ttycommon.t_size; ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0; - if (wscons->wc_tem != NULL) - tem_get_size(wscons->wc_tem, &r, &c, &x, &y); + if (pvc->vc_tem != NULL) + tem_get_size(&r, &c, &x, &y); #ifdef _HAVE_TEM_FIRMWARE - else { + else console_get_size(&r, &c, &x, &y); - } #endif /* _HAVE_TEM_FIRMWARE */ - update_property(wscons, "screen-#cols", t->ws_col = c); - update_property(wscons, "screen-#rows", t->ws_row = r); - update_property(wscons, "screen-width", t->ws_xpixel = x); - update_property(wscons, "screen-height", t->ws_ypixel = y); + mutex_enter(&pvc->vc_ttycommon.t_excl); + t->ws_col = c; + t->ws_row = r; + t->ws_xpixel = x; + t->ws_ypixel = y; + mutex_exit(&pvc->vc_ttycommon.t_excl); + + if (pvc->vc_minor != 0) + return; + + /* only for the wscons:0 */ + update_property(pvc, "screen-#cols", c); + update_property(pvc, "screen-#rows", r); + update_property(pvc, "screen-width", x); + update_property(pvc, "screen-height", y); } +/*ARGSUSED*/ static void wc_modechg_cb(tem_modechg_cb_arg_t arg) { - wc_get_size((struct wscons *)arg); + minor_t index; + vc_state_t *pvc; + + mutex_enter(&vc_lock); + for (index = 0; index < VC_INSTANCES_COUNT; index++) { + pvc = vt_minor2vc(index); + + mutex_enter(&pvc->vc_state_lock); + + if ((pvc->vc_flags & WCS_ISOPEN) && + (pvc->vc_flags & WCS_INIT)) + wc_get_size(pvc); + + mutex_exit(&pvc->vc_state_lock); + } + mutex_exit(&vc_lock); } diff --git a/usr/src/uts/common/os/console.c b/usr/src/uts/common/os/console.c index 91f5de218d..9145eaf808 100644 --- a/usr/src/uts/common/os/console.c +++ b/usr/src/uts/common/os/console.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/varargs.h> #include <sys/modctl.h> @@ -168,32 +166,6 @@ console_rele(void) rw_exit(&console_lock); } -#ifdef _HAVE_TEM_FIRMWARE -/* - * This routine exists so that prom_write() can redirect writes - * to the framebuffer through the kernel terminal emulator, if - * that configuration is selected during consconfig. - * When the kernel terminal emulator is enabled, consconfig_dacf - * sets up the PROM output redirect vector to enter this function. - * During panic the console will already be powered up as part of - * calling into the prom_*() layer. - */ -ssize_t -console_prom_write_cb(promif_redir_arg_t arg, uchar_t *s, size_t n) -{ - struct tem *tem = (struct tem *)arg; - - ASSERT(consmode == CONS_KFB); - - if (panicstr) - polled_io_cons_write(s, n); - else - tem->cons_wrtvec(tem, s, n, kcred); - - return (n); -} -#endif /* _HAVE_TEM_FIRMWARE */ - static void console_getprop(dev_t dev, dev_info_t *dip, char *name, ushort_t *sp) { diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index 966e485e43..367e2c0211 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -126,6 +126,9 @@ CHKHDRS= \ consdev.h \ console.h \ consplat.h \ + vt.h \ + vtdaemon.h \ + kd.h \ contract.h \ contract_impl.h \ copyops.h \ diff --git a/usr/src/uts/common/sys/console.h b/usr/src/uts/common/sys/console.h index d0b25941db..9f60764092 100644 --- a/usr/src/uts/common/sys/console.h +++ b/usr/src/uts/common/sys/console.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_CONSOLE_H #define _SYS_CONSOLE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -77,14 +75,6 @@ extern void console_exit(int, int); extern vnode_t *console_vnode; extern taskq_t *console_taskq; -/* - * PROM interface callback routine - */ -#ifdef _HAVE_TEM_FIRMWARE -#include <sys/promif.h> -extern ssize_t console_prom_write_cb(promif_redir_arg_t, uchar_t *, size_t); -#endif /* _HAVE_TEM_FIRMWARE */ - #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/sys/fs/sdev_impl.h b/usr/src/uts/common/sys/fs/sdev_impl.h index ce86e0d5c0..7bf24fb96a 100644 --- a/usr/src/uts/common/sys/fs/sdev_impl.h +++ b/usr/src/uts/common/sys/fs/sdev_impl.h @@ -26,8 +26,6 @@ #ifndef _SYS_SDEV_IMPL_H #define _SYS_SDEV_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -333,6 +331,7 @@ extern int devname_lookup_func(struct sdev_node *, char *, struct vnode **, #define SDEV_PATH 0x1 /* callback returning /devices physical path */ #define SDEV_VNODE 0x2 /* callback returning backing store vnode */ #define SDEV_VATTR 0x4 /* callback returning node vattr */ +#define SDEV_VLINK 0x8 /* callback returning /dev link */ /* * devname_readdir_func() @@ -429,6 +428,7 @@ extern int devname_profile_update(char *, size_t); extern struct sdev_data *sdev_find_mntinfo(char *); void sdev_mntinfo_rele(struct sdev_data *); extern struct vnodeops *devpts_getvnodeops(void); +extern struct vnodeops *devvt_getvnodeops(void); /* * Directory Based Device Naming (DBNR) defines @@ -626,6 +626,7 @@ extern int prof_lookup(); extern void prof_filldir(struct sdev_node *); extern int devpts_validate(struct sdev_node *dv); extern int devnet_validate(struct sdev_node *dv); +extern int devvt_validate(struct sdev_node *dv); extern void *sdev_get_vtor(struct sdev_node *dv); /* @@ -656,10 +657,12 @@ extern kmem_cache_t *sdev_node_cache; extern struct vnodeops *sdev_vnodeops; extern struct vnodeops *devpts_vnodeops; extern struct vnodeops *devnet_vnodeops; +extern struct vnodeops *devvt_vnodeops; extern struct sdev_data *sdev_origins; /* mount info for global /dev instance */ extern const fs_operation_def_t sdev_vnodeops_tbl[]; extern const fs_operation_def_t devpts_vnodeops_tbl[]; extern const fs_operation_def_t devnet_vnodeops_tbl[]; +extern const fs_operation_def_t devvt_vnodeops_tbl[]; extern const fs_operation_def_t devsys_vnodeops_tbl[]; extern const fs_operation_def_t devpseudo_vnodeops_tbl[]; diff --git a/usr/src/uts/intel/sys/kd.h b/usr/src/uts/common/sys/kd.h index 4daffa1e4f..b34b326a31 100644 --- a/usr/src/uts/intel/sys/kd.h +++ b/usr/src/uts/common/sys/kd.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1999 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,8 +31,6 @@ #ifndef _SYS_KD_H #define _SYS_KD_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Minimal compatibility support for "kd" ioctls. * diff --git a/usr/src/uts/common/sys/tem.h b/usr/src/uts/common/sys/tem.h index 202e692b52..0b11b4ff4c 100644 --- a/usr/src/uts/common/sys/tem.h +++ b/usr/src/uts/common/sys/tem.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_TEM_H #define _SYS_TEM_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -42,19 +40,31 @@ extern "C" { typedef struct __tem_modechg_cb_arg *tem_modechg_cb_arg_t; typedef void (*tem_modechg_cb_t) (tem_modechg_cb_arg_t arg); -struct tem; -int tem_init(struct tem **, - char *, cred_t *); -void tem_write(struct tem *, - uchar_t *, ssize_t, cred_t *); -void tem_polled_write(struct tem *, - unsigned char *, int); -void tem_get_size(struct tem *, ushort_t *, ushort_t *, - ushort_t *, ushort_t *); -int tem_fini(struct tem *); - -void tem_register_modechg_cb(struct tem *, tem_modechg_cb_t, - tem_modechg_cb_arg_t); +typedef struct __tem_vt_state *tem_vt_state_t; + +int tem_initialized(tem_vt_state_t); + +tem_vt_state_t tem_init(cred_t *); + +void tem_destroy(tem_vt_state_t, cred_t *); + +int tem_info_init(char *, cred_t *); + +void tem_write(tem_vt_state_t, uchar_t *, ssize_t, cred_t *); + +void tem_safe_polled_write(tem_vt_state_t, unsigned char *, int); + +void tem_get_size(ushort_t *, ushort_t *, ushort_t *, ushort_t *); + +void tem_register_modechg_cb(tem_modechg_cb_t, tem_modechg_cb_arg_t); + +void tem_activate(tem_vt_state_t, boolean_t, cred_t *); + +void tem_switch(tem_vt_state_t, tem_vt_state_t, cred_t *); + +uchar_t tem_get_fbmode(tem_vt_state_t); + +void tem_set_fbmode(tem_vt_state_t, uchar_t, cred_t *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/tem_impl.h b/usr/src/uts/common/sys/tem_impl.h index 804caf4c85..31e5187cea 100644 --- a/usr/src/uts/common/sys/tem_impl.h +++ b/usr/src/uts/common/sys/tem_impl.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,8 +32,6 @@ #ifndef _SYS_TEM_IMPL_H #define _SYS_TEM_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -43,7 +41,9 @@ extern "C" { #include <sys/sunldi.h> #include <sys/visual_io.h> #include <sys/font.h> +#include <sys/list.h> #include <sys/tem.h> +#include <sys/note.h> /* * definitions for ANSI x3.64 terminal control language parser @@ -93,6 +93,7 @@ extern "C" { /* * Default foreground/background color */ + #ifdef _HAVE_TEM_FIRMWARE #define DEFAULT_ANSI_FOREGROUND ANSI_COLOR_BLACK #define DEFAULT_ANSI_BACKGROUND ANSI_COLOR_WHITE @@ -101,6 +102,7 @@ extern "C" { #define DEFAULT_ANSI_BACKGROUND ANSI_COLOR_BLACK #endif + #define BUF_LEN 160 /* Two lines of data can be processed at a time */ typedef uint8_t text_color_t; @@ -111,6 +113,8 @@ typedef struct tem_color { unsigned short a_flags; } tem_color_t; +enum called_from { CALLED_FROM_NORMAL, CALLED_FROM_STANDALONE }; + struct tem_pix_pos { screen_pos_t x; screen_pos_t y; @@ -134,131 +138,165 @@ typedef struct { extern text_cmap_t cmap4_to_24; -struct tem; /* Forward declare */ - -enum called_from { CALLED_FROM_NORMAL, CALLED_FROM_STANDALONE }; +/* + * State structure for each virtual terminal emulator + */ +struct tem_vt_state { + kmutex_t tvs_lock; + uchar_t tvs_fbmode; /* framebuffer mode */ + unsigned short tvs_flags; /* flags for this x3.64 terminal */ + int tvs_state; /* state in output esc seq processing */ + boolean_t tvs_gotparam; /* does output esc seq have a param */ + + int tvs_curparam; /* current param # of output esc seq */ + int tvs_paramval; /* value of current param */ + int tvs_params[TEM_MAXPARAMS]; /* parameters of output esc seq */ + screen_pos_t tvs_tabs[TEM_MAXTAB]; /* tab stops */ + int tvs_ntabs; /* number of tabs used */ + int tvs_nscroll; /* number of lines to scroll */ + + struct tem_char_pos tvs_s_cursor; /* start cursor position */ + struct tem_char_pos tvs_c_cursor; /* current cursor position */ + struct tem_char_pos tvs_r_cursor; /* remembered cursor position */ + + unsigned char *tvs_outbuf; /* place to keep incomplete lines */ + int tvs_outbuf_size; + int tvs_outindex; /* index into a_outbuf */ + void *tvs_pix_data; /* pointer to tmp bitmap area */ + int tvs_pix_data_size; + text_color_t tvs_fg_color; + text_color_t tvs_bg_color; + int tvs_first_line; /* kernel console output begins */ + + unsigned char *tvs_screen_buf; /* whole screen buffer */ + int tvs_screen_buf_size; + text_color_t *tvs_fg_buf; /* fg_color attribute cache */ + text_color_t *tvs_bg_buf; /* bg_color attribute cache */ + int tvs_color_buf_size; + + boolean_t tvs_isactive; + int tvs_initialized; /* initialization flag */ + + list_node_t tvs_list_node; +}; +_NOTE(MUTEX_PROTECTS_DATA(tem_vt_state::tvs_lock, tem_vt_state)) -struct in_func_ptrs { - void (*f_display)(struct tem *, unsigned char *, int, +typedef struct tem_safe_callbacks { + void (*tsc_display)(struct tem_vt_state *, unsigned char *, int, screen_pos_t, screen_pos_t, unsigned char, unsigned char, cred_t *, enum called_from); - void (*f_copy)(struct tem *, + void (*tsc_copy)(struct tem_vt_state *, screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t, cred_t *, enum called_from); - void (*f_cursor)(struct tem *, short, cred_t *, + void (*tsc_cursor)(struct tem_vt_state *, short, cred_t *, enum called_from); - void (*f_bit2pix)(struct tem *, unsigned char, + void (*tsc_bit2pix)(struct tem_vt_state *, unsigned char, unsigned char, unsigned char); - void (*f_cls)(struct tem *, int, + void (*tsc_cls)(struct tem_vt_state *, int, screen_pos_t, screen_pos_t, cred_t *, enum called_from); -}; +} tem_safe_callbacks_t; /* - * State structure for terminal emulator + * common term soft state structure shared by all virtual terminal emulators */ -typedef struct tem_state { /* state for tem x3.64 emulator */ - int display_mode; /* What mode we are in */ - screen_size_t linebytes; /* Layered on bytes per scan line */ - unsigned short a_flags; /* flags for this x3.64 terminal */ - int a_state; /* state in output esc seq processing */ - boolean_t a_gotparam; /* does output esc seq have a param */ - int a_curparam; /* current param # of output esc seq */ - int a_paramval; /* value of current param */ - int a_params[TEM_MAXPARAMS]; /* parameters of output esc seq */ - screen_pos_t a_tabs[TEM_MAXTAB]; /* tab stops */ - int a_ntabs; /* number of tabs used */ - int a_nscroll; /* number of lines to scroll */ - struct tem_char_pos a_s_cursor; /* start cursor position */ - struct tem_char_pos a_c_cursor; /* current cursor position */ - struct tem_char_pos a_r_cursor; /* remembered cursor position */ - struct tem_size a_c_dimension; /* window dimensions in characters */ - struct tem_size a_p_dimension; /* screen dimensions in pixels */ - struct tem_pix_pos a_p_offset; /* pix offset to center the display */ - unsigned char *a_outbuf; /* place to keep incomplete lines */ - unsigned char *a_blank_line; /* a blank line for scrolling */ - int a_outindex; /* index into a_outbuf */ - struct in_func_ptrs in_fp; /* internal output functions */ - struct font a_font; /* font table */ - int a_pdepth; /* pixel depth */ - int a_initialized; /* initialization flag */ - void *a_pix_data; /* pointer to tmp bitmap area */ - int a_pix_data_size; /* size of bitmap data areas */ - text_color_t fg_color; - text_color_t bg_color; - int first_line; /* kernel console output begins */ +typedef struct tem_state { + ldi_handle_t ts_hdl; /* Framework handle for layered on dev */ + screen_size_t ts_linebytes; /* Layered on bytes per scan line */ + + int ts_display_mode; /* What mode we are in */ + struct vis_polledio *ts_fb_polledio; + + struct tem_size ts_c_dimension; /* window dimensions in characters */ + struct tem_size ts_p_dimension; /* screen dimensions in pixels */ + struct tem_pix_pos ts_p_offset; /* pix offset to center the display */ + + int ts_pix_data_size; /* size of bitmap data areas */ + int ts_pdepth; /* pixel depth */ + struct font ts_font; /* font table */ + + unsigned char *ts_blank_line; /* a blank line for scrolling */ + tem_safe_callbacks_t *ts_callbacks; /* internal output functions */ + + int ts_initialized; /* initialization flag */ + + tem_modechg_cb_t ts_modechg_cb; + tem_modechg_cb_arg_t ts_modechg_arg; + + tem_color_t ts_init_color; /* initial color and attributes */ + + struct tem_vt_state *ts_active; + kmutex_t ts_lock; + list_t ts_list; /* chain of all tems */ } tem_state_t; +extern tem_state_t tems; +extern tem_safe_callbacks_t tem_safe_text_callbacks; +extern tem_safe_callbacks_t tem_safe_pix_callbacks; + + /* - * State structure for terminal emulator + * tems_* fuctions mean that they just operate on the common soft state + * (tem_state_t), and tem_* functions mean that they operate on the + * per-tem structure (tem_vt_state). All "safe" interfaces are in tem_safe.c. */ -typedef struct tem { -#ifdef _HAVE_TEM_FIRMWARE - void (*cons_wrtvec) /* PROM output gets redirected thru this vec. */ - (struct tem *, uchar_t *, ssize_t, cred_t *); -#endif /* _HAVE_TEM_FIRMWARE */ - ldi_handle_t hdl; /* Framework handle for layered on dev */ - dev_info_t *dip; /* Our dip */ - kmutex_t lock; - struct vis_polledio *fb_polledio; - tem_state_t *state; - tem_modechg_cb_t modechg_cb; - tem_modechg_cb_arg_t modechg_arg; - tem_color_t init_color; /* initial color and attributes */ -} tem_t; - -void tem_check_first_time(tem_t *tem, cred_t *, enum called_from); -void tem_reset_colormap(tem_t *, cred_t *, enum called_from); -void tem_align_cursor(tem_t *); -void tem_reset_emulator(tem_t *, cred_t *, enum called_from, tem_color_t *); -void tem_reset_display(tem_t *, cred_t *, enum called_from, int, - tem_color_t *); -void tem_display_layered(tem_t *, struct vis_consdisplay *, cred_t *); -void tem_copy_layered(tem_t *, struct vis_conscopy *, cred_t *); -void tem_cursor_layered(tem_t *, struct vis_conscursor *, cred_t *); -void tem_terminal_emulate(tem_t *, uchar_t *, int, cred_t *, - enum called_from); -void tem_text_display(tem_t *, uchar_t *, - int, screen_pos_t, screen_pos_t, - text_color_t, text_color_t, - cred_t *, enum called_from); -void tem_text_copy(tem_t *, - screen_pos_t, screen_pos_t, - screen_pos_t, screen_pos_t, - screen_pos_t, screen_pos_t, - cred_t *, enum called_from); -void tem_text_cursor(tem_t *, short, cred_t *, enum called_from); -void tem_text_cls(tem_t *, - int count, screen_pos_t row, screen_pos_t col, - cred_t *credp, enum called_from called_from); -void tem_pix_display(tem_t *, uchar_t *, - int, screen_pos_t, screen_pos_t, - text_color_t, text_color_t, - cred_t *, enum called_from); -void tem_pix_copy(tem_t *, - screen_pos_t, screen_pos_t, - screen_pos_t, screen_pos_t, - screen_pos_t, screen_pos_t, - cred_t *, enum called_from); -void tem_copy(tem_t *, - struct vis_conscopy *, - cred_t *, enum called_from); -void tem_pix_cursor(tem_t *, short, cred_t *, enum called_from); -void tem_pix_cls(tem_t *, int, screen_pos_t, screen_pos_t, - cred_t *, enum called_from); -void tem_pix_cls_range(tem_t *, - screen_pos_t, int, int, - screen_pos_t, int, int, - boolean_t, cred_t *, enum called_from); - -void bit_to_pix24(tem_t *, uchar_t, text_color_t, text_color_t); -void bit_to_pix8(tem_t *, uchar_t, text_color_t, text_color_t); -void bit_to_pix4(tem_t *, uchar_t, text_color_t, text_color_t); - -text_color_t ansi_bg_to_solaris(tem_t *, int); -text_color_t ansi_fg_to_solaris(tem_t *, int); +void tems_display_layered(struct vis_consdisplay *, cred_t *); +void tems_copy_layered(struct vis_conscopy *, cred_t *); +void tems_cursor_layered(struct vis_conscursor *, cred_t *); +void tems_safe_copy(struct vis_conscopy *, cred_t *, enum called_from); + +void tem_pix_align(struct tem_vt_state *, cred_t *, enum called_from); +void tem_safe_check_first_time(struct tem_vt_state *tem, cred_t *, + enum called_from); +void tem_safe_reset_display(struct tem_vt_state *, cred_t *, + enum called_from, boolean_t, boolean_t); +void tem_safe_terminal_emulate(struct tem_vt_state *, uchar_t *, int, + cred_t *, enum called_from); +void tem_safe_text_display(struct tem_vt_state *, uchar_t *, + int, screen_pos_t, screen_pos_t, + text_color_t, text_color_t, + cred_t *, enum called_from); +void tem_safe_text_copy(struct tem_vt_state *, + screen_pos_t, screen_pos_t, + screen_pos_t, screen_pos_t, + screen_pos_t, screen_pos_t, + cred_t *, enum called_from); +void tem_safe_text_cursor(struct tem_vt_state *, short, cred_t *, + enum called_from); +void tem_safe_text_cls(struct tem_vt_state *, + int count, screen_pos_t row, screen_pos_t col, + cred_t *credp, enum called_from called_from); +void tem_safe_pix_display(struct tem_vt_state *, uchar_t *, + int, screen_pos_t, screen_pos_t, + text_color_t, text_color_t, + cred_t *, enum called_from); +void tem_safe_pix_copy(struct tem_vt_state *, + screen_pos_t, screen_pos_t, + screen_pos_t, screen_pos_t, + screen_pos_t, screen_pos_t, + cred_t *, enum called_from); +void tem_safe_pix_cursor(struct tem_vt_state *, short, cred_t *, + enum called_from); +void tem_safe_pix_bit2pix(struct tem_vt_state *, unsigned char, + unsigned char, unsigned char); +void tem_safe_pix_cls(struct tem_vt_state *, int, screen_pos_t, screen_pos_t, + cred_t *, enum called_from); +void tem_safe_pix_cls_range(struct tem_vt_state *, + screen_pos_t, int, int, + screen_pos_t, int, int, + boolean_t, cred_t *, enum called_from); +void tem_safe_pix_clear_entire_screen(struct tem_vt_state *, + cred_t *, enum called_from); + +void tem_safe_get_color(struct tem_vt_state *, text_color_t *, + text_color_t *, uint8_t); void set_font(struct font *, short *, short *, short, short); +void tem_safe_blank_screen(struct tem_vt_state *, cred_t *, + enum called_from); +void tem_safe_unblank_screen(struct tem_vt_state *, cred_t *, + enum called_from); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/vt.h b/usr/src/uts/common/sys/vt.h new file mode 100644 index 0000000000..dd0c2144dc --- /dev/null +++ b/usr/src/uts/common/sys/vt.h @@ -0,0 +1,112 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_VT_H +#define _SYS_VT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* + * Public IOCTLs supported by the VT, which are shared with + * other operating systems. + */ +#define VTIOC ('V'<<8) +#define VT_OPENQRY (VTIOC|1) /* inquires if this vt already open */ +#define VT_SETMODE (VTIOC|2) /* set vt into auto or process mode */ + +#define VT_GETMODE (VTIOC|3) /* returns mode vt is currently in */ +#define VT_RELDISP (VTIOC|4) /* tells vt when display released */ +#define VT_ACTIVATE (VTIOC|5) /* activates specified vt */ +#define VT_WAITACTIVE (VTIOC|6) /* wait for vt to be activated */ +#define VT_GETSTATE (VTIOC|100) /* returns active and open vts */ + +/* + * Solaris specific public IOCTL. + * Inquires if the vt functionality is available. + */ +#define VT_ENABLED (VTIOC|101) + +struct vt_mode { + char mode; /* mode to set vt into, VT_AUTO or VT_PROCESS */ + char waitv; /* if != 0, vt hangs on writes when not active */ + short relsig; /* signal to use for release request */ + short acqsig; /* signal to use for display acquired */ + short frsig; /* signal to use for forced release */ +}; + +/* vt switching mode */ +enum { + VT_AUTO = 0, /* this vt switching is automatic */ + VT_PROCESS /* this vt switching controlled by process */ +}; + +#define VT_ACKACQ 2 /* ack from v86 acquire routine */ + +/* + * structure used by VT_GETSTATE ioctl + */ + +struct vt_stat { + unsigned short v_active; + unsigned short v_signal; + unsigned short v_state; +}; + +/* project private IOCTLs */ +#define VT_CONFIG (VTIOC|102) /* config virtual console number */ +#define VT_SETDISPINFO (VTIOC|103) /* set display number */ +#define VT_SETDISPLOGIN (VTIOC|104) /* set display login */ +#define VT_GETDISPINFO (VTIOC|105) /* get display info */ + +/* + * setting target console is only used by vtdaemon + * to set target console while vtdaemon is authenticating + * for it, which is returned in VT_GETSTATE. At that + * time, the real active console is the vtdaemon special console, + * but VT_GETSTATE should not be aware of it. Instead, VT_GETACTIVE + * is used to get the real active console for vtdaemon. + */ +#define VT_SET_TARGET (VTIOC|106) +#define VT_GETACTIVE (VTIOC|107) + +/* + * structure used by VT_GETDISPINFO + */ +struct vt_dispinfo { + pid_t v_pid; /* -1 if no display info (auto mode) */ + int v_dispnum; /* display number associated with vt */ + int v_login; /* if the user logged in the display */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_VT_H */ diff --git a/usr/src/uts/common/sys/vt_impl.h b/usr/src/uts/common/sys/vt_impl.h new file mode 100644 index 0000000000..7bd41615f3 --- /dev/null +++ b/usr/src/uts/common/sys/vt_impl.h @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_VT_IMPL_H +#define _SYS_VT_IMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <sys/stream.h> +#include <sys/vt.h> +#include <sys/kd.h> +#include <sys/tem.h> +#include <sys/tty.h> +#include <sys/cred.h> +#include <sys/list.h> +#include <sys/avl.h> +#include <sys/note.h> + +#define WCS_INIT 0x00000001 /* tty is init */ +#define WCS_ISOPEN 0x00000002 /* open is complete */ +#define WCS_STOPPED 0x00000004 /* output is stopped */ +#define WCS_DELAY 0x00000008 /* waiting for delay to finish */ +#define WCS_BUSY 0x00000010 /* waiting for transmission to finish */ + +typedef struct vc_waitactive_msg { + list_node_t wa_list_node; + int wa_msg_minor; /* minor number from which msg comes */ + int wa_wait_minor; /* which node we are waiting for */ + mblk_t *wa_mp; +} vc_waitactive_msg_t; + +/* virtual console soft state associated with each vt */ +typedef struct vc_state { + minor_t vc_minor; + avl_node_t vc_avl_node; + uchar_t vc_switch_mode; /* VT_AUTO or VT_PROCESS */ + char vc_waitv; + int vc_relsig; + int vc_acqsig; + pid_t vc_pid; + minor_t vc_switchto; + int vc_flags; + + int vc_dispnum; + int vc_login; + + tem_vt_state_t vc_tem; /* Terminal emulator state */ + tty_common_t vc_ttycommon; /* data common to all tty drivers */ + bufcall_id_t vc_bufcallid; /* id returned by qbufcall */ + timeout_id_t vc_timeoutid; /* id returned by qtimeout */ + + queue_t *vc_wq; /* write queue */ + +#ifdef _HAVE_TEM_FIRMWARE + int vc_pendc; /* pending output character */ +#endif /* _HAVE_TEM_FIRMWARE */ + + /* + * vc_state_lock is currently only used to protect vc_flags, + * more precisely, the state change of vc_state_t. + * The existence of this lock is because wc_modechg_cb(). + * wc_modechg_cb() is a callback function which may result in + * multiple threads accessing vc_flags regardless the STREAMS + * periemters of wc module. + * Since wc_modechg_cb() only conducts read access to vc_flags, + * we only need to hold this lock when writing to vc_flags in + * wc module (except wc_modechg_cb()). + * See locking policy in wscons.c for more info. + */ + kmutex_t vc_state_lock; +} vc_state_t; +_NOTE(MUTEX_PROTECTS_DATA(vc_state_t::vc_state_lock, vc_state_t::vc_flags)) + +#define VC_DEFAULT_COUNT 16 + +/* Invalid VT minor number */ +#define VT_MINOR_INVALID ((minor_t)-1) +/* Argument to vt_minor2vc to get the softstate of the active VT */ +#define VT_ACTIVE VT_MINOR_INVALID + +/* + * VC_INSTANCES_COUNT should be regarded as reading access to vc_avl_root + */ +#define VC_INSTANCES_COUNT (avl_numnodes(&vc_avl_root)) + +void vt_ioctl(queue_t *q, mblk_t *mp); +void vt_miocdata(queue_t *qp, mblk_t *mp); +void vt_clean(queue_t *q, vc_state_t *pvc); +void vt_close(queue_t *q, vc_state_t *pvc, cred_t *crp); +int vt_open(minor_t minor, queue_t *rq, cred_t *crp); +int vt_check_hotkeys(mblk_t *mp); +vc_state_t *vt_minor2vc(minor_t); + +extern dev_info_t *wc_dip; +extern avl_tree_t vc_avl_root; +extern minor_t vc_active_console; +extern kmutex_t vc_lock; +extern minor_t vc_last_console; + +major_t vt_wc_attached(void); +void vt_getactive(char *, int); +boolean_t vt_minor_valid(minor_t minor); +void vt_resize(uint_t); +void vt_attach(void); +void vt_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_VT_IMPL_H */ diff --git a/usr/src/uts/common/sys/vtdaemon.h b/usr/src/uts/common/sys/vtdaemon.h new file mode 100644 index 0000000000..95d512efda --- /dev/null +++ b/usr/src/uts/common/sys/vtdaemon.h @@ -0,0 +1,51 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_VTDAEMON_H +#define _SYS_VTDAEMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define VT_DAEMON_DOOR_FILE "/var/run/vt/vtdaemon_door" + +#define VT_EV_X_EXIT 0 /* <vt_num> */ +#define VT_EV_HOTKEYS 1 /* <vt_num> */ + +/* + * The structure of a request to vtdaemon. + */ +typedef struct vt_cmd_arg { + uchar_t vt_ev; + uint32_t vt_num; +} vt_cmd_arg_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_VTDAEMON_H */ diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 280b7b5c07..185eb386ea 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -82,7 +82,7 @@ include $(UTSTREE)/common/Makefile.files # defined before we include Makefile.uts, or else genunix's build # won't be as parallel as we might like. # -NOT_YET_KMODS = $(OLDPTY_OBJS) $(PTY_OBJS) $(MOD_OBJS) +NOT_YET_KMODS = $(OLDPTY_OBJS) $(PTY_OBJS) $(VCONS_CONF_OBJS) $(MOD_OBJS) # # ----- END OF TRANSITIONAL SECTION ------------------------------------------- diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s index bec4b20e5d..a1352a260b 100644 --- a/usr/src/uts/intel/ia32/ml/modstubs.s +++ b/usr/src/uts/intel/ia32/ml/modstubs.s @@ -428,6 +428,7 @@ fcnname/**/_info: \ NO_UNLOAD_STUB(dev, devname_profile_update, nomod_minus_one); NO_UNLOAD_STUB(dev, sdev_module_register, nomod_minus_one); NO_UNLOAD_STUB(dev, sdev_devstate_change, nomod_minus_one); + NO_UNLOAD_STUB(dev, devvt_getvnodeops, nomod_minus_one); NO_UNLOAD_STUB(dev, devpts_getvnodeops, nomod_zero); END_MODULE(dev); #endif diff --git a/usr/src/uts/intel/sys/Makefile b/usr/src/uts/intel/sys/Makefile index 8ac0deba85..2b242ba979 100644 --- a/usr/src/uts/intel/sys/Makefile +++ b/usr/src/uts/intel/sys/Makefile @@ -22,7 +22,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# include ../../../Makefile.master @@ -48,7 +47,6 @@ HDRS = \ inline.h \ iommulib.h \ hypervisor.h \ - kd.h \ kdi_machimpl.h \ kdi_regs.h \ machlock.h \ diff --git a/usr/src/uts/intel/tem/Makefile b/usr/src/uts/intel/tem/Makefile index ac45575c5e..9962a28f30 100644 --- a/usr/src/uts/intel/tem/Makefile +++ b/usr/src/uts/intel/tem/Makefile @@ -20,11 +20,9 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This makefile drives the production of the tem module # # intel implementation architecture dependent @@ -42,6 +40,9 @@ MODULE = tem OBJECTS = $(TEM_OBJS:%=$(OBJS_DIR)/%) LINTS = $(TEM_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) +WARLOCK_OUT = $(TEM_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock # # Include common rules. @@ -63,7 +64,6 @@ LDFLAGS += -dy -Ndacf/consconfig_dacf # Please do not carry these forward to new Makefiles. # LINTTAGS += -erroff=E_STATIC_UNUSED -LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV # # Default build targets. @@ -90,3 +90,26 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/intel/Makefile.targ + +# +# Defines for local commands +# +TEST = test +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock + +# +# warlock targets +# + +warlock: $(WARLOCK_OUT) warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/tem.wlcmd $(WARLOCK_OUT) \ + -l ../../intel/warlock/ddi_dki_impl.ll + $(TOUCH) $(WARLOCK_OK) + +%.ll: $(UTSBASE)/common/io/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +warlock_ddi.files: + @cd ../../intel/warlock; pwd; $(MAKE) warlock diff --git a/usr/src/uts/intel/warlock/Makefile b/usr/src/uts/intel/warlock/Makefile index 028c88d4a4..fbefb9b95d 100644 --- a/usr/src/uts/intel/warlock/Makefile +++ b/usr/src/uts/intel/warlock/Makefile @@ -52,7 +52,7 @@ include $(UTSBASE)/intel/Makefile.intel # lock_lint rules # all: warlock warlock.1394 warlock.audio warlock.ecpp warlock.scsi \ - warlock.usb warlock.ib warlock.sata warlock.sdcard + warlock.usb warlock.ib warlock.sata warlock.sdcard warlock.wc warlock: $(MODULE).ok @@ -137,3 +137,6 @@ $(CLOSED_BUILD) @cd $(CLOSED)/uts/intel/marvell88sx; \ warlock.sdcard: @cd ../sda; $(MAKE) clean; $(MAKE) warlock @cd ../sdhost; $(MAKE) clean; $(MAKE) warlock + +warlock.wc: + @cd ../wc; $(MAKE) clean; $(MAKE) warlock diff --git a/usr/src/uts/intel/wc/Makefile b/usr/src/uts/intel/wc/Makefile index efdad561f7..b7461a4bed 100644 --- a/usr/src/uts/intel/wc/Makefile +++ b/usr/src/uts/intel/wc/Makefile @@ -23,7 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of the wc driver # kernel module. @@ -44,6 +43,9 @@ OBJECTS = $(WC_OBJS:%=$(OBJS_DIR)/%) LINTS = $(WC_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) CONF_SRCDIR = $(UTSBASE)/common/io +WARLOCK_OUT = $(WC_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock # # Include common rules. @@ -60,7 +62,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) # # Overrides. # -LDFLAGS += -dy -Nmisc/tem +LDFLAGS += -dy -Nmisc/tem -Ndacf/consconfig_dacf # # Default build targets. @@ -87,3 +89,37 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/intel/Makefile.targ + +# +# Defines for local commands +# +TEST = test +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock + +# +# warlock targets +# + +# +# vcons_conf.c is compile to genunix, add it to WARLOCK_OUT +# +WARLOCK_OUT += vcons_conf.ll + +warlock: $(WARLOCK_OUT) sdev_vtops.file warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/wc.wlcmd $(WARLOCK_OUT) \ + -l ../../intel/warlock/ddi_dki_impl.ll + $(WARLOCK) -c $(WLCMD_DIR)/wc_devfs.wlcmd sdev_vtops.ll vcons_conf.ll \ + -l ../../intel/warlock/ddi_dki_impl.ll + $(TOUCH) $(WARLOCK_OK) + +%.ll: $(UTSBASE)/common/io/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +sdev_vtops.file: + $(WLCC) $(CPPFLAGS) -DDEBUG -o sdev_vtops.ll -c \ + ../../common/fs/dev/sdev_vtops.c + +warlock_ddi.files: + @cd ../../intel/warlock; pwd; $(MAKE) warlock diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 1ef0190d3f..6644b73a2b 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -95,7 +95,7 @@ include $(UTSTREE)/common/Makefile.files # defined before we include Makefile.uts, or else genunix's build # won't be as parallel as we might like. # -NOT_YET_KMODS = $(OLDPTY_OBJS) $(PTY_OBJS) $(MOD_OBJS) +NOT_YET_KMODS = $(OLDPTY_OBJS) $(PTY_OBJS) $(VCONS_CONF_OBJS) $(MOD_OBJS) # # ----- END OF TRANSITIONAL SECTION ------------------------------------------- diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s index 142cd0567c..15377f736c 100644 --- a/usr/src/uts/sparc/ml/modstubs.s +++ b/usr/src/uts/sparc/ml/modstubs.s @@ -316,6 +316,7 @@ stubs_base: NO_UNLOAD_STUB(dev, devname_profile_update, nomod_minus_one); NO_UNLOAD_STUB(dev, sdev_module_register, nomod_minus_one); NO_UNLOAD_STUB(dev, sdev_devstate_change, nomod_minus_one); + NO_UNLOAD_STUB(dev, devvt_getvnodeops, nomod_minus_one); NO_UNLOAD_STUB(dev, devpts_getvnodeops, nomod_zero); END_MODULE(dev); #endif diff --git a/usr/src/uts/sparc/tem/Makefile b/usr/src/uts/sparc/tem/Makefile index 74cfc7091d..cd6a9c6d34 100644 --- a/usr/src/uts/sparc/tem/Makefile +++ b/usr/src/uts/sparc/tem/Makefile @@ -20,10 +20,9 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of the tem module @@ -44,6 +43,9 @@ OBJECTS = $(TEM_OBJS:%=$(OBJS_DIR)/%) LINTS = $(TEM_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) +WARLOCK_OUT = $(TEM_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock # # Include common rules. @@ -66,7 +68,6 @@ LDFLAGS += -dy -Ndacf/consconfig_dacf # to investigate and remove these for maximum lint coverage. # Please do not carry these forward to new Makefiles. # -LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV LINTTAGS += -erroff=E_STATIC_UNUSED # @@ -94,3 +95,26 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/sparc/Makefile.targ + +# +# Defines for local commands +# +TEST = test +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock + +# +# warlock targets +# + +warlock: $(WARLOCK_OUT) warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/tem.wlcmd $(WARLOCK_OUT) \ + -l ../../sparc/warlock/ddi_dki_impl.ll + $(TOUCH) $(WARLOCK_OK) + +%.ll: $(UTSBASE)/common/io/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +warlock_ddi.files: + @cd ../../sparc/warlock; pwd; $(MAKE) warlock diff --git a/usr/src/uts/sparc/warlock/Makefile b/usr/src/uts/sparc/warlock/Makefile index 68a9256259..746c15d646 100644 --- a/usr/src/uts/sparc/warlock/Makefile +++ b/usr/src/uts/sparc/warlock/Makefile @@ -52,7 +52,7 @@ include $(UTSBASE)/sparc/Makefile.sparc # lock_lint rules # all: warlock warlock.1394 warlock.audio warlock.ecpp warlock.scsi \ - warlock.smartcard warlock.usb warlock.ib warlock.sata + warlock.smartcard warlock.usb warlock.ib warlock.sata warlock.wc warlock: $(MODULE).ok @@ -152,3 +152,6 @@ warlock.sdcard: @cd ../sda; $(MAKE) clean; $(MAKE) warlock @cd ../sdhost; $(MAKE) clean; $(MAKE) warlock @cd ../wbsd; $(MAKE) clean; $(MAKE) warlock + +warlock.wc: + @cd ../wc; $(MAKE) clean; $(MAKE) warlock diff --git a/usr/src/uts/sparc/wc/Makefile b/usr/src/uts/sparc/wc/Makefile index 39e210e09c..37a0e94a97 100644 --- a/usr/src/uts/sparc/wc/Makefile +++ b/usr/src/uts/sparc/wc/Makefile @@ -23,8 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# # This makefile drives the production of the wc driver # # sparc architecture dependent @@ -43,6 +41,9 @@ OBJECTS = $(WC_OBJS:%=$(OBJS_DIR)/%) LINTS = $(WC_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) CONF_SRCDIR = $(UTSBASE)/sun/io +WARLOCK_OUT = $(WC_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock # # Include common rules. @@ -67,7 +68,7 @@ CLEANFILES += $(MODSTUBS_O) # lint pass one enforcement # CFLAGS += $(CCVERBOSE) -LDFLAGS += -dy -Nmisc/tem +LDFLAGS += -dy -Nmisc/tem -Ndacf/consconfig_dacf # # Default build targets. @@ -94,3 +95,37 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/sparc/Makefile.targ + +# +# Defines for local commands +# +TEST = test +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock + +# +# warlock targets +# + +# +# vcons_conf.c is compiled to genunix, add it to WARLOCK_OUT +# +WARLOCK_OUT += vcons_conf.ll + +warlock: $(WARLOCK_OUT) sdev_vtops.file warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/wc.wlcmd $(WARLOCK_OUT) \ + -l ../../sparc/warlock/ddi_dki_impl.ll + $(WARLOCK) -c $(WLCMD_DIR)/wc_devfs.wlcmd sdev_vtops.ll vcons_conf.ll \ + -l ../../sparc/warlock/ddi_dki_impl.ll + $(TOUCH) $(WARLOCK_OK) + +%.ll: $(UTSBASE)/common/io/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +sdev_vtops.file: + $(WLCC) $(CPPFLAGS) -DDEBUG -o sdev_vtops.ll -c \ + ../../common/fs/dev/sdev_vtops.c + +warlock_ddi.files: + @cd ../../sparc/warlock; pwd; $(MAKE) warlock |