diff options
Diffstat (limited to 'usr/src/uts/common')
26 files changed, 4935 insertions, 1544 deletions
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/common/sys/kd.h b/usr/src/uts/common/sys/kd.h new file mode 100644 index 0000000000..b34b326a31 --- /dev/null +++ b/usr/src/uts/common/sys/kd.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ +/* All Rights Reserved */ + +#ifndef _SYS_KD_H +#define _SYS_KD_H + +/* + * Minimal compatibility support for "kd" ioctls. + * + * This file may be deleted or changed without notice. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KDIOC ('K'<<8) +#define KDGETMODE (KDIOC|9) /* get text/graphics mode */ +#define KDSETMODE (KDIOC|10) /* set text/graphics mode */ +#define KD_TEXT 0 +#define KD_GRAPHICS 1 + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_KD_H */ 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 */ |