xref: /onnv-gate/usr/src/uts/common/xen/io/xencons.c (revision 7656)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
235084Sjohnlev /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
245084Sjohnlev /*	  All Rights Reserved					*/
255084Sjohnlev 
265084Sjohnlev /*
27*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
285084Sjohnlev  * Use is subject to license terms.
295084Sjohnlev  */
305084Sjohnlev 
315084Sjohnlev 
325084Sjohnlev /*
335084Sjohnlev  *
345084Sjohnlev  * Copyright (c) 2004 Christian Limpach.
355084Sjohnlev  * All rights reserved.
365084Sjohnlev  *
375084Sjohnlev  * Redistribution and use in source and binary forms, with or without
385084Sjohnlev  * modification, are permitted provided that the following conditions
395084Sjohnlev  * are met:
405084Sjohnlev  * 1. Redistributions of source code must retain the above copyright
415084Sjohnlev  *    notice, this list of conditions and the following disclaimer.
425084Sjohnlev  * 2. Redistributions in binary form must reproduce the above copyright
435084Sjohnlev  *    notice, this list of conditions and the following disclaimer in the
445084Sjohnlev  *    documentation and/or other materials provided with the distribution.
455084Sjohnlev  * 3. This section intentionally left blank.
465084Sjohnlev  * 4. The name of the author may not be used to endorse or promote products
475084Sjohnlev  *    derived from this software without specific prior written permission.
485084Sjohnlev  *
495084Sjohnlev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
505084Sjohnlev  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
515084Sjohnlev  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
525084Sjohnlev  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
535084Sjohnlev  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
545084Sjohnlev  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
555084Sjohnlev  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
565084Sjohnlev  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
575084Sjohnlev  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
585084Sjohnlev  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
595084Sjohnlev  */
605084Sjohnlev /*
615084Sjohnlev  * Section 3 of the above license was updated in response to bug 6379571.
625084Sjohnlev  */
635084Sjohnlev 
645084Sjohnlev /*
655084Sjohnlev  * Hypervisor virtual console driver
665084Sjohnlev  */
675084Sjohnlev 
685084Sjohnlev #include <sys/param.h>
695084Sjohnlev #include <sys/types.h>
705084Sjohnlev #include <sys/signal.h>
715084Sjohnlev #include <sys/stream.h>
725084Sjohnlev #include <sys/termio.h>
735084Sjohnlev #include <sys/errno.h>
745084Sjohnlev #include <sys/file.h>
755084Sjohnlev #include <sys/cmn_err.h>
765084Sjohnlev #include <sys/stropts.h>
775084Sjohnlev #include <sys/strsubr.h>
785084Sjohnlev #include <sys/strtty.h>
795084Sjohnlev #include <sys/debug.h>
805084Sjohnlev #include <sys/kbio.h>
815084Sjohnlev #include <sys/cred.h>
825084Sjohnlev #include <sys/stat.h>
835084Sjohnlev #include <sys/consdev.h>
845084Sjohnlev #include <sys/mkdev.h>
855084Sjohnlev #include <sys/kmem.h>
865084Sjohnlev #include <sys/cred.h>
875084Sjohnlev #include <sys/strsun.h>
885084Sjohnlev #ifdef DEBUG
895084Sjohnlev #include <sys/promif.h>
905084Sjohnlev #endif
915084Sjohnlev #include <sys/modctl.h>
925084Sjohnlev #include <sys/ddi.h>
935084Sjohnlev #include <sys/sunddi.h>
945084Sjohnlev #include <sys/sunndi.h>
955084Sjohnlev #include <sys/policy.h>
965084Sjohnlev #include <sys/atomic.h>
975084Sjohnlev #include <sys/psm.h>
985084Sjohnlev #include <xen/public/io/console.h>
995084Sjohnlev 
1005084Sjohnlev #include "xencons.h"
1015084Sjohnlev 
1025084Sjohnlev #include <sys/hypervisor.h>
1035084Sjohnlev #include <sys/evtchn_impl.h>
1045084Sjohnlev #include <xen/sys/xenbus_impl.h>
1055084Sjohnlev #include <xen/sys/xendev.h>
1065084Sjohnlev 
1075084Sjohnlev #ifdef DEBUG
1085084Sjohnlev #define	XENCONS_DEBUG_INIT	0x0001	/* msgs during driver initialization. */
1095084Sjohnlev #define	XENCONS_DEBUG_INPUT	0x0002	/* characters received during int. */
1105084Sjohnlev #define	XENCONS_DEBUG_EOT	0x0004	/* msgs when wait for xmit to finish. */
1115084Sjohnlev #define	XENCONS_DEBUG_CLOSE	0x0008	/* msgs when driver open/close called */
1125084Sjohnlev #define	XENCONS_DEBUG_PROCS	0x0020	/* each proc name as it is entered. */
1135084Sjohnlev #define	XENCONS_DEBUG_OUT	0x0100	/* msgs about output events. */
1145084Sjohnlev #define	XENCONS_DEBUG_BUSY	0x0200	/* msgs when xmit is enabled/disabled */
1155084Sjohnlev #define	XENCONS_DEBUG_MODEM	0x0400	/* msgs about modem status & control. */
1165084Sjohnlev #define	XENCONS_DEBUG_MODM2	0x0800	/* msgs about modem status & control. */
1175084Sjohnlev #define	XENCONS_DEBUG_IOCTL	0x1000	/* Output msgs about ioctl messages. */
1185084Sjohnlev #define	XENCONS_DEBUG_CHIP	0x2000	/* msgs about chip identification. */
1195084Sjohnlev #define	XENCONS_DEBUG_SFLOW	0x4000	/* msgs when S/W flowcontrol active */
1205084Sjohnlev #define	XENCONS_DEBUG(x) (debug & (x))
1215084Sjohnlev static int debug  = 0;
1225084Sjohnlev #else
1235084Sjohnlev #define	XENCONS_DEBUG(x) B_FALSE
1245084Sjohnlev #endif
1255084Sjohnlev 
1265084Sjohnlev #define	XENCONS_WBUFSIZE	4096
1275084Sjohnlev 
1285084Sjohnlev static boolean_t abort_charseq_recognize(uchar_t);
1295084Sjohnlev 
1305084Sjohnlev /* The async interrupt entry points */
1315084Sjohnlev static void	xcasync_ioctl(struct asyncline *, queue_t *, mblk_t *);
1325084Sjohnlev static void	xcasync_reioctl(void *);
1335084Sjohnlev static void	xcasync_start(struct asyncline *);
1345084Sjohnlev static void	xenconsputchar(cons_polledio_arg_t, uchar_t);
1355084Sjohnlev static int	xenconsgetchar(cons_polledio_arg_t);
1365084Sjohnlev static boolean_t	xenconsischar(cons_polledio_arg_t);
1375084Sjohnlev 
1385084Sjohnlev static uint_t	xenconsintr(caddr_t);
1395084Sjohnlev static uint_t	xenconsintr_priv(caddr_t);
1405084Sjohnlev /*PRINTFLIKE2*/
1415084Sjohnlev static void	xenconserror(int, const char *, ...) __KPRINTFLIKE(2);
1425084Sjohnlev static void	xencons_soft_state_free(struct xencons *);
1435084Sjohnlev static boolean_t
1445084Sjohnlev xcasync_flowcontrol_sw_input(struct xencons *, async_flowc_action, int);
1455084Sjohnlev static void
1465084Sjohnlev xcasync_flowcontrol_sw_output(struct xencons *, async_flowc_action);
1475084Sjohnlev 
1485084Sjohnlev void		*xencons_soft_state;
1495084Sjohnlev char		*xencons_wbuf;
1505084Sjohnlev struct xencons	*xencons_console;
1515084Sjohnlev 
1525084Sjohnlev static void
1535084Sjohnlev xenconssetup_avintr(struct xencons *xcp, int attach)
1545084Sjohnlev {
1555084Sjohnlev 	/*
1565084Sjohnlev 	 * On xen, CPU 0 always exists and can't be taken offline,
1575084Sjohnlev 	 * so binding this thread to it should always succeed.
1585084Sjohnlev 	 */
1595084Sjohnlev 	mutex_enter(&cpu_lock);
1605084Sjohnlev 	thread_affinity_set(curthread, 0);
1615084Sjohnlev 	mutex_exit(&cpu_lock);
1625084Sjohnlev 
1635084Sjohnlev 	if (attach) {
1645084Sjohnlev 		/* Setup our interrupt binding. */
1655084Sjohnlev 		(void) add_avintr(NULL, IPL_CONS, (avfunc)xenconsintr_priv,
1665084Sjohnlev 		    "xencons", xcp->console_irq, (caddr_t)xcp, NULL, NULL,
1675084Sjohnlev 		    xcp->dip);
1685084Sjohnlev 	} else {
1695084Sjohnlev 		/*
1705084Sjohnlev 		 * Cleanup interrupt configuration.  Note that the framework
1715084Sjohnlev 		 * _should_ ensure that when rem_avintr() returns the interrupt
1725084Sjohnlev 		 * service routine is not currently executing and that it won't
1735084Sjohnlev 		 * be invoked again.
1745084Sjohnlev 		 */
1755084Sjohnlev 		(void) rem_avintr(NULL, IPL_CONS, (avfunc)xenconsintr_priv,
1765084Sjohnlev 		    xcp->console_irq);
1775084Sjohnlev 	}
1785084Sjohnlev 
1795084Sjohnlev 	/* Notify our caller that we're done. */
1805084Sjohnlev 	mutex_enter(&xcp->excl);
1815084Sjohnlev 	cv_signal(&xcp->excl_cv);
1825084Sjohnlev 	mutex_exit(&xcp->excl);
1835084Sjohnlev 
1845084Sjohnlev 	/* Clear our binding to CPU 0 */
1855084Sjohnlev 	thread_affinity_clear(curthread);
1865084Sjohnlev 
1875084Sjohnlev }
1885084Sjohnlev 
1895084Sjohnlev static void
1905084Sjohnlev xenconssetup_add_avintr(struct xencons *xcp)
1915084Sjohnlev {
1925084Sjohnlev 	xenconssetup_avintr(xcp, B_TRUE);
1935084Sjohnlev }
1945084Sjohnlev 
1955084Sjohnlev static void
1965084Sjohnlev xenconssetup_rem_avintr(struct xencons *xcp)
1975084Sjohnlev {
1985084Sjohnlev 	xenconssetup_avintr(xcp, B_FALSE);
1995084Sjohnlev }
2005084Sjohnlev 
2015084Sjohnlev static int
2025084Sjohnlev xenconsdetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
2035084Sjohnlev {
2045084Sjohnlev 	int instance;
2055084Sjohnlev 	struct xencons *xcp;
2065084Sjohnlev 
2075084Sjohnlev 	if (cmd != DDI_DETACH && cmd != DDI_SUSPEND)
2085084Sjohnlev 		return (DDI_FAILURE);
2095084Sjohnlev 
2105084Sjohnlev 	if (cmd == DDI_SUSPEND) {
2115084Sjohnlev 		ddi_remove_intr(devi, 0, NULL);
2125084Sjohnlev 		return (DDI_SUCCESS);
2135084Sjohnlev 	}
2145084Sjohnlev 
2155084Sjohnlev 	/*
2165084Sjohnlev 	 * We should never try to detach the console driver on a domU
2175084Sjohnlev 	 * because it should always be held open
2185084Sjohnlev 	 */
2195084Sjohnlev 	ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
2205084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
2215084Sjohnlev 		return (DDI_FAILURE);
2225084Sjohnlev 
2235084Sjohnlev 	instance = ddi_get_instance(devi);	/* find out which unit */
2245084Sjohnlev 
2255084Sjohnlev 	xcp = ddi_get_soft_state(xencons_soft_state, instance);
2265084Sjohnlev 	if (xcp == NULL)
2275084Sjohnlev 		return (DDI_FAILURE);
2285084Sjohnlev 
2295084Sjohnlev 	/*
2305084Sjohnlev 	 * Cleanup our interrupt bindings.  For more info on why we
2315084Sjohnlev 	 * do this in a seperate thread, see the comments for when we
2325084Sjohnlev 	 * setup the interrupt bindings.
2335084Sjohnlev 	 */
2345084Sjohnlev 	xencons_console = NULL;
2355084Sjohnlev 	mutex_enter(&xcp->excl);
2365084Sjohnlev 	(void) taskq_dispatch(system_taskq,
2375084Sjohnlev 	    (void (*)(void *))xenconssetup_rem_avintr, xcp, TQ_SLEEP);
2385084Sjohnlev 	cv_wait(&xcp->excl_cv, &xcp->excl);
2395084Sjohnlev 	mutex_exit(&xcp->excl);
2405084Sjohnlev 
2415084Sjohnlev 	/* remove all minor device node(s) for this device */
2425084Sjohnlev 	ddi_remove_minor_node(devi, NULL);
2435084Sjohnlev 
2445084Sjohnlev 	/* free up state */
2455084Sjohnlev 	xencons_soft_state_free(xcp);
2465084Sjohnlev 	kmem_free(xencons_wbuf, XENCONS_WBUFSIZE);
2475084Sjohnlev 
2485084Sjohnlev 	DEBUGNOTE1(XENCONS_DEBUG_INIT, "xencons%d: shutdown complete",
2495084Sjohnlev 	    instance);
2505084Sjohnlev 	return (DDI_SUCCESS);
2515084Sjohnlev }
2525084Sjohnlev 
2535084Sjohnlev static void
2545084Sjohnlev xenconssetup(struct xencons *xcp)
2555084Sjohnlev {
2565084Sjohnlev 	xcp->ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
2575084Sjohnlev 
2585084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
2595084Sjohnlev 		xencons_wbuf = kmem_alloc(XENCONS_WBUFSIZE, KM_SLEEP);
2605084Sjohnlev 
2615084Sjohnlev 		/*
2625084Sjohnlev 		 * Activate the xen console virq.  Note that xen requires
2635084Sjohnlev 		 * that VIRQs be bound to CPU 0 when first created.
2645084Sjohnlev 		 */
2655084Sjohnlev 		xcp->console_irq = ec_bind_virq_to_irq(VIRQ_CONSOLE, 0);
2665084Sjohnlev 
2675084Sjohnlev 		/*
2685084Sjohnlev 		 * Ok.  This is kinda ugly.  We want to register an
2695084Sjohnlev 		 * interrupt handler for the xen console virq, but
2705084Sjohnlev 		 * virq's are xen sepcific and currently the DDI doesn't
2715084Sjohnlev 		 * support binding to them.  So instead we need to use
2725084Sjohnlev 		 * add_avintr().  So to make things more complicated,
2735084Sjohnlev 		 * we already had to bind the xen console VIRQ to CPU 0,
2745084Sjohnlev 		 * and add_avintr() needs to be invoked on the same CPU
2755084Sjohnlev 		 * where the VIRQ is bound, in this case on CPU 0.  We
2765084Sjohnlev 		 * could just temporarily bind ourselves to CPU 0, but
2775084Sjohnlev 		 * we don't want to do that since this attach thread
2785084Sjohnlev 		 * could have been invoked in a user thread context,
2795084Sjohnlev 		 * in which case this thread could already have some
2805084Sjohnlev 		 * pre-existing cpu binding.  So to avoid changing our
2815084Sjohnlev 		 * cpu binding we're going to use a taskq thread that
2825084Sjohnlev 		 * will bind to CPU 0 and register our interrupts
2835084Sjohnlev 		 * handler for us.
2845084Sjohnlev 		 */
2855084Sjohnlev 		mutex_enter(&xcp->excl);
2865084Sjohnlev 		(void) taskq_dispatch(system_taskq,
2875084Sjohnlev 		    (void (*)(void *))xenconssetup_add_avintr, xcp, TQ_SLEEP);
2885084Sjohnlev 		cv_wait(&xcp->excl_cv, &xcp->excl);
2895084Sjohnlev 		mutex_exit(&xcp->excl);
2905084Sjohnlev 	} else {
2915084Sjohnlev 		(void) xvdi_alloc_evtchn(xcp->dip);
2925741Smrj 		xcp->evtchn = xvdi_get_evtchn(xcp->dip);
2935084Sjohnlev 		(void) ddi_add_intr(xcp->dip, 0, NULL, NULL, xenconsintr,
2945084Sjohnlev 		    (caddr_t)xcp);
2955084Sjohnlev 	}
2965084Sjohnlev }
2975084Sjohnlev 
2985084Sjohnlev static int
2995084Sjohnlev xenconsattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3005084Sjohnlev {
3015084Sjohnlev 	int instance = ddi_get_instance(devi);
3025084Sjohnlev 	struct xencons *xcp;
3035084Sjohnlev 	int ret;
3045084Sjohnlev 
3055084Sjohnlev 	/* There can be only one. */
3065084Sjohnlev 	if (instance != 0)
3075084Sjohnlev 		return (DDI_FAILURE);
3085084Sjohnlev 
3095084Sjohnlev 	switch (cmd) {
3105084Sjohnlev 	case DDI_RESUME:
3115084Sjohnlev 		xcp = xencons_console;
3125084Sjohnlev 		xenconssetup(xcp);
3135084Sjohnlev 		return (DDI_SUCCESS);
3145084Sjohnlev 	case DDI_ATTACH:
3155084Sjohnlev 		break;
3165084Sjohnlev 	default:
3175084Sjohnlev 		return (DDI_FAILURE);
3185084Sjohnlev 	}
3195084Sjohnlev 
3205084Sjohnlev 	ret = ddi_soft_state_zalloc(xencons_soft_state, instance);
3215084Sjohnlev 	if (ret != DDI_SUCCESS)
3225084Sjohnlev 		return (DDI_FAILURE);
3235084Sjohnlev 	xcp = ddi_get_soft_state(xencons_soft_state, instance);
3245084Sjohnlev 	ASSERT(xcp != NULL);	/* can't fail - we only just allocated it */
3255084Sjohnlev 
3265084Sjohnlev 	/*
3275084Sjohnlev 	 * Set up the other components of the xencons structure for this port.
3285084Sjohnlev 	 */
3295084Sjohnlev 	xcp->unit = instance;
3305084Sjohnlev 	xcp->dip = devi;
3315084Sjohnlev 
3325084Sjohnlev 	/* Fill in the polled I/O structure. */
3335084Sjohnlev 	xcp->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
3345084Sjohnlev 	xcp->polledio.cons_polledio_argument = (cons_polledio_arg_t)xcp;
3355084Sjohnlev 	xcp->polledio.cons_polledio_putchar = xenconsputchar;
3365084Sjohnlev 	xcp->polledio.cons_polledio_getchar = xenconsgetchar;
3375084Sjohnlev 	xcp->polledio.cons_polledio_ischar = xenconsischar;
3385084Sjohnlev 	xcp->polledio.cons_polledio_enter = NULL;
3395084Sjohnlev 	xcp->polledio.cons_polledio_exit = NULL;
3405084Sjohnlev 
3415084Sjohnlev 	/*
3425084Sjohnlev 	 * Initializes the asyncline structure which has TTY protocol-private
3435084Sjohnlev 	 * data before enabling interrupts.
3445084Sjohnlev 	 */
3455084Sjohnlev 	xcp->priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP);
3465084Sjohnlev 	xcp->priv->async_common = xcp;
3475084Sjohnlev 	cv_init(&xcp->priv->async_flags_cv, NULL, CV_DRIVER, NULL);
3485084Sjohnlev 
3495084Sjohnlev 	/* Initialize mutexes before accessing the interface. */
3505084Sjohnlev 	mutex_init(&xcp->excl, NULL, MUTEX_DRIVER, NULL);
3515084Sjohnlev 	cv_init(&xcp->excl_cv, NULL, CV_DEFAULT, NULL);
3525084Sjohnlev 
3535084Sjohnlev 	/* create minor device node for this device */
3545084Sjohnlev 	ret = ddi_create_minor_node(devi, "xencons", S_IFCHR, instance,
3555084Sjohnlev 	    DDI_NT_SERIAL, NULL);
3565084Sjohnlev 	if (ret != DDI_SUCCESS) {
3575084Sjohnlev 		ddi_remove_minor_node(devi, NULL);
3585084Sjohnlev 		xencons_soft_state_free(xcp);
3595084Sjohnlev 		return (DDI_FAILURE);
3605084Sjohnlev 	}
3615084Sjohnlev 
3625084Sjohnlev 	ddi_report_dev(devi);
3635084Sjohnlev 	xencons_console = xcp;
3645084Sjohnlev 	xenconssetup(xcp);
3655084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_INIT, "xencons%dattach: done\n", instance);
3665084Sjohnlev 	return (DDI_SUCCESS);
3675084Sjohnlev }
3685084Sjohnlev 
3695084Sjohnlev /*ARGSUSED*/
3705084Sjohnlev static int
3715084Sjohnlev xenconsinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
3725084Sjohnlev 	void **result)
3735084Sjohnlev {
3745084Sjohnlev 	dev_t dev = (dev_t)arg;
3755084Sjohnlev 	int instance, error;
3765084Sjohnlev 	struct xencons *xcp;
3775084Sjohnlev 
3785084Sjohnlev 	instance = getminor(dev);
3795084Sjohnlev 	xcp = ddi_get_soft_state(xencons_soft_state, instance);
3805084Sjohnlev 	if (xcp == NULL)
3815084Sjohnlev 		return (DDI_FAILURE);
3825084Sjohnlev 
3835084Sjohnlev 	switch (infocmd) {
3845084Sjohnlev 	case DDI_INFO_DEVT2DEVINFO:
3855084Sjohnlev 		if (xcp->dip == NULL)
3865084Sjohnlev 			error = DDI_FAILURE;
3875084Sjohnlev 		else {
3885084Sjohnlev 			*result = (void *) xcp->dip;
3895084Sjohnlev 			error = DDI_SUCCESS;
3905084Sjohnlev 		}
3915084Sjohnlev 		break;
3925084Sjohnlev 	case DDI_INFO_DEVT2INSTANCE:
3935084Sjohnlev 		*result = (void *)(intptr_t)instance;
3945084Sjohnlev 		error = DDI_SUCCESS;
3955084Sjohnlev 		break;
3965084Sjohnlev 	default:
3975084Sjohnlev 		error = DDI_FAILURE;
3985084Sjohnlev 	}
3995084Sjohnlev 	return (error);
4005084Sjohnlev }
4015084Sjohnlev 
4025084Sjohnlev /* xencons_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
4035084Sjohnlev 
4045084Sjohnlev static void
4055084Sjohnlev xencons_soft_state_free(struct xencons *xcp)
4065084Sjohnlev {
4075084Sjohnlev 	mutex_destroy(&xcp->excl);
4085084Sjohnlev 	cv_destroy(&xcp->excl_cv);
4095084Sjohnlev 	kmem_free(xcp->priv, sizeof (struct asyncline));
4105084Sjohnlev 	ddi_soft_state_free(xencons_soft_state, xcp->unit);
4115084Sjohnlev }
4125084Sjohnlev 
4135084Sjohnlev /*ARGSUSED*/
4145084Sjohnlev static int
4155084Sjohnlev xenconsopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
4165084Sjohnlev {
4175084Sjohnlev 	struct xencons	*xcp;
4185084Sjohnlev 	struct asyncline *async;
4195084Sjohnlev 	int		unit;
4205084Sjohnlev 
4215084Sjohnlev 	unit = getminor(*dev);
4225084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dopen\n", unit);
4235084Sjohnlev 	xcp = ddi_get_soft_state(xencons_soft_state, unit);
4245084Sjohnlev 	if (xcp == NULL)
4255084Sjohnlev 		return (ENXIO);		/* unit not configured */
4265084Sjohnlev 	async = xcp->priv;
4275084Sjohnlev 	mutex_enter(&xcp->excl);
4285084Sjohnlev 
4295084Sjohnlev again:
4305084Sjohnlev 
4315084Sjohnlev 	if ((async->async_flags & ASYNC_ISOPEN) == 0) {
4325084Sjohnlev 		async->async_ttycommon.t_iflag = 0;
4335084Sjohnlev 		async->async_ttycommon.t_iocpending = NULL;
4345084Sjohnlev 		async->async_ttycommon.t_size.ws_row = 0;
4355084Sjohnlev 		async->async_ttycommon.t_size.ws_col = 0;
4365084Sjohnlev 		async->async_ttycommon.t_size.ws_xpixel = 0;
4375084Sjohnlev 		async->async_ttycommon.t_size.ws_ypixel = 0;
4385084Sjohnlev 		async->async_dev = *dev;
4395084Sjohnlev 		async->async_wbufcid = 0;
4405084Sjohnlev 
4415084Sjohnlev 		async->async_startc = CSTART;
4425084Sjohnlev 		async->async_stopc = CSTOP;
4435084Sjohnlev 	} else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
4445084Sjohnlev 	    secpolicy_excl_open(cr) != 0) {
4455084Sjohnlev 		mutex_exit(&xcp->excl);
4465084Sjohnlev 		return (EBUSY);
4475084Sjohnlev 	}
4485084Sjohnlev 
4495084Sjohnlev 	async->async_ttycommon.t_flags |= TS_SOFTCAR;
4505084Sjohnlev 
4515084Sjohnlev 	async->async_ttycommon.t_readq = rq;
4525084Sjohnlev 	async->async_ttycommon.t_writeq = WR(rq);
4535084Sjohnlev 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
4545084Sjohnlev 	mutex_exit(&xcp->excl);
4555084Sjohnlev 	/*
4565084Sjohnlev 	 * Caution here -- qprocson sets the pointers that are used by canput
4575084Sjohnlev 	 * called by xencons_rxint.  ASYNC_ISOPEN must *not* be set until those
4585084Sjohnlev 	 * pointers are valid.
4595084Sjohnlev 	 */
4605084Sjohnlev 	qprocson(rq);
4615084Sjohnlev 	async->async_flags |= ASYNC_ISOPEN;
4625084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_INIT, "asy%dopen: done\n", unit);
4635084Sjohnlev 	return (0);
4645084Sjohnlev }
4655084Sjohnlev 
4665084Sjohnlev 
4675084Sjohnlev /*
4685084Sjohnlev  * Close routine.
4695084Sjohnlev  */
4705084Sjohnlev /*ARGSUSED*/
4715084Sjohnlev static int
4725084Sjohnlev xenconsclose(queue_t *q, int flag, cred_t *credp)
4735084Sjohnlev {
4745084Sjohnlev 	struct asyncline *async;
4755084Sjohnlev 	struct xencons	 *xcp;
4765084Sjohnlev #ifdef DEBUG
4775084Sjohnlev 	int instance;
4785084Sjohnlev #endif
4795084Sjohnlev 
4805084Sjohnlev 	async = (struct asyncline *)q->q_ptr;
4815084Sjohnlev 	ASSERT(async != NULL);
4825084Sjohnlev 	xcp = async->async_common;
4835084Sjohnlev #ifdef DEBUG
4845084Sjohnlev 	instance = xcp->unit;
4855084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dclose\n", instance);
4865084Sjohnlev #endif
4875084Sjohnlev 
4885084Sjohnlev 	mutex_enter(&xcp->excl);
4895084Sjohnlev 	async->async_flags |= ASYNC_CLOSING;
4905084Sjohnlev 
4915084Sjohnlev 	async->async_ocnt = 0;
4925084Sjohnlev 	if (async->async_xmitblk != NULL)
4935084Sjohnlev 		freeb(async->async_xmitblk);
4945084Sjohnlev 	async->async_xmitblk = NULL;
4955084Sjohnlev 
4965084Sjohnlev out:
4975084Sjohnlev 	ttycommon_close(&async->async_ttycommon);
4985084Sjohnlev 
4995084Sjohnlev 	/*
5005084Sjohnlev 	 * Cancel outstanding "bufcall" request.
5015084Sjohnlev 	 */
5025084Sjohnlev 	if (async->async_wbufcid != 0) {
5035084Sjohnlev 		unbufcall(async->async_wbufcid);
5045084Sjohnlev 		async->async_wbufcid = 0;
5055084Sjohnlev 	}
5065084Sjohnlev 
5075084Sjohnlev 	/* Note that qprocsoff can't be done until after interrupts are off */
5085084Sjohnlev 	qprocsoff(q);
5095084Sjohnlev 	q->q_ptr = WR(q)->q_ptr = NULL;
5105084Sjohnlev 	async->async_ttycommon.t_readq = NULL;
5115084Sjohnlev 	async->async_ttycommon.t_writeq = NULL;
5125084Sjohnlev 
5135084Sjohnlev 	/*
5145084Sjohnlev 	 * Clear out device state, except persistant device property flags.
5155084Sjohnlev 	 */
5165084Sjohnlev 	async->async_flags = 0;
5175084Sjohnlev 	cv_broadcast(&async->async_flags_cv);
5185084Sjohnlev 	mutex_exit(&xcp->excl);
5195084Sjohnlev 
5205084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dclose: done\n", instance);
5215084Sjohnlev 	return (0);
5225084Sjohnlev }
5235084Sjohnlev 
5245084Sjohnlev #define	INBUF_IX(ix, ifp)	(DOMAIN_IS_INITDOMAIN(xen_info) ? \
5255084Sjohnlev 	(ix) : MASK_XENCONS_IDX((ix), (ifp)->in))
5265084Sjohnlev 
5275084Sjohnlev /*
5285084Sjohnlev  * Handle a xen console rx interrupt.
5295084Sjohnlev  */
5305084Sjohnlev /*ARGSUSED*/
5315084Sjohnlev static void
5325084Sjohnlev xencons_rxint(struct xencons *xcp)
5335084Sjohnlev {
5345084Sjohnlev 	struct asyncline *async;
5355084Sjohnlev 	short	cc;
5365084Sjohnlev 	mblk_t	*bp;
5375084Sjohnlev 	queue_t	*q;
5385084Sjohnlev 	uchar_t	c, buf[16];
5395084Sjohnlev 	uchar_t	*cp;
5405084Sjohnlev 	tty_common_t	*tp;
5415084Sjohnlev 	int instance;
5425084Sjohnlev 	volatile struct xencons_interface *ifp;
5435084Sjohnlev 	XENCONS_RING_IDX cons, prod;
5445084Sjohnlev 
5455084Sjohnlev 	DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_rxint\n");
5465084Sjohnlev 
5475084Sjohnlev loop:
5485084Sjohnlev 	mutex_enter(&xcp->excl);
5495084Sjohnlev 
5505084Sjohnlev 	/* sanity check if we should bail */
5515084Sjohnlev 	if (xencons_console == NULL) {
5525084Sjohnlev 		mutex_exit(&xcp->excl);
5535084Sjohnlev 		goto out;
5545084Sjohnlev 	}
5555084Sjohnlev 
5565084Sjohnlev 	async = xcp->priv;
5575084Sjohnlev 	instance = xcp->unit;
5585084Sjohnlev 	ifp = xcp->ifp;
5595084Sjohnlev 	tp = &async->async_ttycommon;
5605084Sjohnlev 	q = tp->t_readq;
5615084Sjohnlev 
5625084Sjohnlev 	if (async->async_flags & ASYNC_OUT_FLW_RESUME) {
5635084Sjohnlev 		xcasync_start(async);
5645084Sjohnlev 		async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
5655084Sjohnlev 	}
5665084Sjohnlev 
5675084Sjohnlev 	/*
5685084Sjohnlev 	 * If data is available, send it up the stream if there's
5695084Sjohnlev 	 * somebody listening.
5705084Sjohnlev 	 */
5715084Sjohnlev 	if (!(async->async_flags & ASYNC_ISOPEN)) {
5725084Sjohnlev 		mutex_exit(&xcp->excl);
5735084Sjohnlev 		goto out;
5745084Sjohnlev 	}
5755084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
5765084Sjohnlev 		cc = HYPERVISOR_console_io(CONSOLEIO_read, 16, (char *)buf);
5775084Sjohnlev 		cp = buf;
5785084Sjohnlev 		cons = 0;
5795084Sjohnlev 	} else {
5805084Sjohnlev 		cons = ifp->in_cons;
5815084Sjohnlev 		prod = ifp->in_prod;
5825084Sjohnlev 
5835084Sjohnlev 		cc = prod - cons;
5845084Sjohnlev 		cp = (uchar_t *)ifp->in;
5855084Sjohnlev 	}
5865084Sjohnlev 	if (cc <= 0) {
5875084Sjohnlev 		mutex_exit(&xcp->excl);
5885084Sjohnlev 		goto out;
5895084Sjohnlev 	}
5905084Sjohnlev 
5915084Sjohnlev 	/*
5925084Sjohnlev 	 * Check for character break sequence.
5935084Sjohnlev 	 *
5945084Sjohnlev 	 * Note that normally asy drivers only check for a character sequence
5955084Sjohnlev 	 * if abort_enable == KIOCABORTALTERNATE and otherwise use a break
5965084Sjohnlev 	 * sensed on the line to do an abort_sequence_enter.  Since the
5975084Sjohnlev 	 * hypervisor does not use a real chip for the console we default to
5985084Sjohnlev 	 * using the alternate sequence.
5995084Sjohnlev 	 */
6005084Sjohnlev 	if ((abort_enable == KIOCABORTENABLE) && (xcp->flags & ASY_CONSOLE)) {
6015084Sjohnlev 		XENCONS_RING_IDX i;
6025084Sjohnlev 
6035084Sjohnlev 		for (i = 0; i < cc; i++) {
6045084Sjohnlev 			c = cp[INBUF_IX(cons + i, ifp)];
6055084Sjohnlev 			if (abort_charseq_recognize(c)) {
6065084Sjohnlev 				/*
6075084Sjohnlev 				 * Eat abort seg, it's not a valid debugger
6085084Sjohnlev 				 * command.
6095084Sjohnlev 				 */
6105084Sjohnlev 				if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
6115084Sjohnlev 					membar_producer();
6125084Sjohnlev 					ifp->in_cons = cons + i;
6135084Sjohnlev 				} else {
6145084Sjohnlev 					cons += i;
6155084Sjohnlev 				}
6165084Sjohnlev 				abort_sequence_enter((char *)NULL);
6175084Sjohnlev 				/*
6185084Sjohnlev 				 * Back from debugger, resume normal processing
6195084Sjohnlev 				 */
6205084Sjohnlev 				mutex_exit(&xcp->excl);
6215084Sjohnlev 				goto loop;
6225084Sjohnlev 			}
6235084Sjohnlev 		}
6245084Sjohnlev 	}
6255084Sjohnlev 
6265084Sjohnlev 	if (!canput(q)) {
6275084Sjohnlev 		if (!(async->async_inflow_source & IN_FLOW_STREAMS)) {
6285084Sjohnlev 			(void) xcasync_flowcontrol_sw_input(xcp, FLOW_STOP,
6295084Sjohnlev 			    IN_FLOW_STREAMS);
6305084Sjohnlev 		}
6315084Sjohnlev 		mutex_exit(&xcp->excl);
6325084Sjohnlev 		goto out;
6335084Sjohnlev 	}
6345084Sjohnlev 	if (async->async_inflow_source & IN_FLOW_STREAMS) {
6355084Sjohnlev 		(void) xcasync_flowcontrol_sw_input(xcp, FLOW_START,
6365084Sjohnlev 		    IN_FLOW_STREAMS);
6375084Sjohnlev 	}
6385084Sjohnlev 	DEBUGCONT2(XENCONS_DEBUG_INPUT,
6395084Sjohnlev 	    "xencons%d_rxint: %d char(s) in queue.\n", instance, cc);
6405084Sjohnlev 	if (!(bp = allocb(cc, BPRI_MED))) {
6415084Sjohnlev 		mutex_exit(&xcp->excl);
6425084Sjohnlev 		ttycommon_qfull(&async->async_ttycommon, q);
6435084Sjohnlev 		goto out;
6445084Sjohnlev 	}
6455084Sjohnlev 	do {
6465084Sjohnlev 		c = cp[INBUF_IX(cons++, ifp)];
6475084Sjohnlev 		/*
6485084Sjohnlev 		 * We handle XON/XOFF char if IXON is set,
6495084Sjohnlev 		 * but if received char is _POSIX_VDISABLE,
6505084Sjohnlev 		 * we left it to the up level module.
6515084Sjohnlev 		 */
6525084Sjohnlev 		if (tp->t_iflag & IXON) {
6535084Sjohnlev 			if ((c == async->async_stopc) &&
6545084Sjohnlev 			    (c != _POSIX_VDISABLE)) {
6555084Sjohnlev 				xcasync_flowcontrol_sw_output(xcp, FLOW_STOP);
6565084Sjohnlev 				continue;
6575084Sjohnlev 			} else if ((c == async->async_startc) &&
6585084Sjohnlev 			    (c != _POSIX_VDISABLE)) {
6595084Sjohnlev 				xcasync_flowcontrol_sw_output(xcp, FLOW_START);
6605084Sjohnlev 				continue;
6615084Sjohnlev 			}
6625084Sjohnlev 			if ((tp->t_iflag & IXANY) &&
6635084Sjohnlev 			    (async->async_flags & ASYNC_SW_OUT_FLW)) {
6645084Sjohnlev 				xcasync_flowcontrol_sw_output(xcp, FLOW_START);
6655084Sjohnlev 			}
6665084Sjohnlev 		}
6675084Sjohnlev 		*bp->b_wptr++ = c;
6685084Sjohnlev 	} while (--cc);
6695084Sjohnlev 	membar_producer();
6705084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
6715084Sjohnlev 		ifp->in_cons = cons;
6725084Sjohnlev 	mutex_exit(&xcp->excl);
6735084Sjohnlev 	if (bp->b_wptr > bp->b_rptr) {
6745084Sjohnlev 		if (!canput(q)) {
6755084Sjohnlev 			xenconserror(CE_NOTE, "xencons%d: local queue full",
6765084Sjohnlev 			    instance);
6775084Sjohnlev 			freemsg(bp);
6785084Sjohnlev 		} else
6795084Sjohnlev 			(void) putq(q, bp);
6805084Sjohnlev 	} else
6815084Sjohnlev 		freemsg(bp);
6825084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info))
6835084Sjohnlev 		goto loop;
6845084Sjohnlev out:
6855084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_PROCS, "xencons%d_rxint: done\n", instance);
6865084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
6875084Sjohnlev 		ec_notify_via_evtchn(xcp->evtchn);
6885084Sjohnlev }
6895084Sjohnlev 
6905084Sjohnlev 
6915084Sjohnlev /*
6925084Sjohnlev  * Handle a xen console tx interrupt.
6935084Sjohnlev  */
6945084Sjohnlev /*ARGSUSED*/
6955084Sjohnlev static void
6965084Sjohnlev xencons_txint(struct xencons *xcp)
6975084Sjohnlev {
6985084Sjohnlev 	struct asyncline *async;
6995084Sjohnlev 
7005084Sjohnlev 	DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_txint\n");
7015084Sjohnlev 
7025084Sjohnlev 	/*
7035084Sjohnlev 	 * prevent recursive entry
7045084Sjohnlev 	 */
7055084Sjohnlev 	if (mutex_owner(&xcp->excl) == curthread) {
7065084Sjohnlev 		goto out;
7075084Sjohnlev 	}
7085084Sjohnlev 
7095084Sjohnlev 	mutex_enter(&xcp->excl);
7105084Sjohnlev 	if (xencons_console == NULL) {
7115084Sjohnlev 		mutex_exit(&xcp->excl);
7125084Sjohnlev 		goto out;
7135084Sjohnlev 	}
7145084Sjohnlev 
7155084Sjohnlev 	/* make sure the device is open */
7165084Sjohnlev 	async = xcp->priv;
7175084Sjohnlev 	if ((async->async_flags & ASYNC_ISOPEN) != 0)
7185084Sjohnlev 		xcasync_start(async);
7195084Sjohnlev 
7205084Sjohnlev 	mutex_exit(&xcp->excl);
7215084Sjohnlev out:
7225084Sjohnlev 	DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_txint: done\n");
7235084Sjohnlev }
7245084Sjohnlev 
7255084Sjohnlev 
7265084Sjohnlev /*
7275084Sjohnlev  * Get an event when input ring becomes not empty or output ring becomes not
7285084Sjohnlev  * full.
7295084Sjohnlev  */
7305084Sjohnlev static uint_t
7315084Sjohnlev xenconsintr(caddr_t arg)
7325084Sjohnlev {
7335084Sjohnlev 	struct xencons *xcp = (struct xencons *)arg;
7345084Sjohnlev 	volatile struct xencons_interface *ifp = xcp->ifp;
7355084Sjohnlev 
7365084Sjohnlev 	if (ifp->in_prod != ifp->in_cons)
7375084Sjohnlev 		xencons_rxint(xcp);
7385084Sjohnlev 	if (ifp->out_prod - ifp->out_cons < sizeof (ifp->out))
7395084Sjohnlev 		xencons_txint(xcp);
7405084Sjohnlev 	return (DDI_INTR_CLAIMED);
7415084Sjohnlev }
7425084Sjohnlev 
7435084Sjohnlev /*
7445084Sjohnlev  * Console interrupt routine for priviliged domains
7455084Sjohnlev  */
7465084Sjohnlev static uint_t
7475084Sjohnlev xenconsintr_priv(caddr_t arg)
7485084Sjohnlev {
7495084Sjohnlev 	struct xencons *xcp = (struct xencons *)arg;
7505084Sjohnlev 
7515084Sjohnlev 	xencons_rxint(xcp);
7525084Sjohnlev 	xencons_txint(xcp);
7535084Sjohnlev 	return (DDI_INTR_CLAIMED);
7545084Sjohnlev }
7555084Sjohnlev 
7565084Sjohnlev /*
7575084Sjohnlev  * Start output on a line, unless it's busy, frozen, or otherwise.
7585084Sjohnlev  */
7595084Sjohnlev /*ARGSUSED*/
7605084Sjohnlev static void
7615084Sjohnlev xcasync_start(struct asyncline *async)
7625084Sjohnlev {
7635084Sjohnlev 	struct xencons *xcp = async->async_common;
7645084Sjohnlev 	int cc;
7655084Sjohnlev 	queue_t *q;
7665084Sjohnlev 	mblk_t *bp;
7675084Sjohnlev 	int	len, space, blen;
7685084Sjohnlev 	mblk_t *nbp;
7695084Sjohnlev 
7705084Sjohnlev #ifdef DEBUG
7715084Sjohnlev 	int instance = xcp->unit;
7725084Sjohnlev 
7735084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_nstart\n", instance);
7745084Sjohnlev #endif
7755084Sjohnlev 	ASSERT(mutex_owned(&xcp->excl));
7765084Sjohnlev 
7775084Sjohnlev 	/*
7785084Sjohnlev 	 * Check only pended sw input flow control.
7795084Sjohnlev 	 */
7805084Sjohnlev domore:
7815084Sjohnlev 	(void) xcasync_flowcontrol_sw_input(xcp, FLOW_CHECK, IN_FLOW_NULL);
7825084Sjohnlev 
7835084Sjohnlev 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
7845084Sjohnlev 		return;	/* not attached to a stream */
7855084Sjohnlev 	}
7865084Sjohnlev 
7875084Sjohnlev 	for (;;) {
7885084Sjohnlev 		if ((bp = getq(q)) == NULL)
7895084Sjohnlev 			return;	/* no data to transmit */
7905084Sjohnlev 
7915084Sjohnlev 		/*
7925084Sjohnlev 		 * We have a message block to work on.
7935084Sjohnlev 		 * Check whether it's a break, a delay, or an ioctl (the latter
7945084Sjohnlev 		 * occurs if the ioctl in question was waiting for the output
7955084Sjohnlev 		 * to drain).  If it's one of those, process it immediately.
7965084Sjohnlev 		 */
7975084Sjohnlev 		switch (bp->b_datap->db_type) {
7985084Sjohnlev 
7995084Sjohnlev 		case M_IOCTL:
8005084Sjohnlev 			/*
8015084Sjohnlev 			 * This ioctl was waiting for the output ahead of
8025084Sjohnlev 			 * it to drain; obviously, it has.  Do it, and
8035084Sjohnlev 			 * then grab the next message after it.
8045084Sjohnlev 			 */
8055084Sjohnlev 			mutex_exit(&xcp->excl);
8065084Sjohnlev 			xcasync_ioctl(async, q, bp);
8075084Sjohnlev 			mutex_enter(&xcp->excl);
8085084Sjohnlev 			continue;
8095084Sjohnlev 		}
8105084Sjohnlev 
8115084Sjohnlev 		while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
8125084Sjohnlev 			nbp = bp->b_cont;
8135084Sjohnlev 			freeb(bp);
8145084Sjohnlev 			bp = nbp;
8155084Sjohnlev 		}
8165084Sjohnlev 		if (bp != NULL)
8175084Sjohnlev 			break;
8185084Sjohnlev 	}
8195084Sjohnlev 
8205084Sjohnlev 	/*
8215084Sjohnlev 	 * We have data to transmit.  If output is stopped, put
8225084Sjohnlev 	 * it back and try again later.
8235084Sjohnlev 	 */
8245084Sjohnlev 	if (async->async_flags & (ASYNC_SW_OUT_FLW | ASYNC_STOPPED)) {
8255084Sjohnlev 		(void) putbq(q, bp);
8265084Sjohnlev 		return;
8275084Sjohnlev 	}
8285084Sjohnlev 
8295084Sjohnlev 
8305084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
8315084Sjohnlev 		len = 0;
8325084Sjohnlev 		space = XENCONS_WBUFSIZE;
8335084Sjohnlev 		while (bp != NULL && space) {
8345084Sjohnlev 			blen = bp->b_wptr - bp->b_rptr;
8355084Sjohnlev 			cc = min(blen, space);
8365084Sjohnlev 			bcopy(bp->b_rptr, &xencons_wbuf[len], cc);
8375084Sjohnlev 			bp->b_rptr += cc;
8385084Sjohnlev 			if (cc == blen) {
8395084Sjohnlev 				nbp = bp->b_cont;
8405084Sjohnlev 				freeb(bp);
8415084Sjohnlev 				bp = nbp;
8425084Sjohnlev 			}
8435084Sjohnlev 			space -= cc;
8445084Sjohnlev 			len += cc;
8455084Sjohnlev 		}
8465084Sjohnlev 		mutex_exit(&xcp->excl);
8475084Sjohnlev 		(void) HYPERVISOR_console_io(CONSOLEIO_write, len,
8485084Sjohnlev 		    xencons_wbuf);
8495084Sjohnlev 		mutex_enter(&xcp->excl);
8505084Sjohnlev 		if (bp != NULL)
8515084Sjohnlev 			(void) putbq(q, bp); /* not done with this msg yet */
8525084Sjohnlev 		/*
8535084Sjohnlev 		 * There are no completion interrupts when using the
8545084Sjohnlev 		 * HYPERVISOR_console_io call to write console data
8555084Sjohnlev 		 * so we loop here till we have sent all the data to the
8565084Sjohnlev 		 * hypervisor.
8575084Sjohnlev 		 */
8585084Sjohnlev 		goto domore;
8595084Sjohnlev 	} else {
8605084Sjohnlev 		volatile struct xencons_interface *ifp = xcp->ifp;
8615084Sjohnlev 		XENCONS_RING_IDX cons, prod;
8625084Sjohnlev 
8635084Sjohnlev 		cons = ifp->out_cons;
8645084Sjohnlev 		prod = ifp->out_prod;
8655084Sjohnlev 		membar_enter();
8665084Sjohnlev 		while (bp != NULL && ((prod - cons) < sizeof (ifp->out))) {
8675084Sjohnlev 			ifp->out[MASK_XENCONS_IDX(prod++, ifp->out)] =
8685084Sjohnlev 			    *bp->b_rptr++;
8695084Sjohnlev 			if (bp->b_rptr == bp->b_wptr) {
8705084Sjohnlev 				nbp = bp->b_cont;
8715084Sjohnlev 				freeb(bp);
8725084Sjohnlev 				bp = nbp;
8735084Sjohnlev 			}
8745084Sjohnlev 		}
8755084Sjohnlev 		membar_producer();
8765084Sjohnlev 		ifp->out_prod = prod;
8775084Sjohnlev 		ec_notify_via_evtchn(xcp->evtchn);
8785084Sjohnlev 		if (bp != NULL)
8795084Sjohnlev 			(void) putbq(q, bp); /* not done with this msg yet */
8805084Sjohnlev 	}
8815084Sjohnlev }
8825084Sjohnlev 
8835084Sjohnlev 
8845084Sjohnlev /*
8855084Sjohnlev  * Process an "ioctl" message sent down to us.
8865084Sjohnlev  * Note that we don't need to get any locks until we are ready to access
8875084Sjohnlev  * the hardware.  Nothing we access until then is going to be altered
8885084Sjohnlev  * outside of the STREAMS framework, so we should be safe.
8895084Sjohnlev  */
8905084Sjohnlev static void
8915084Sjohnlev xcasync_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
8925084Sjohnlev {
8935084Sjohnlev 	struct xencons *xcp = async->async_common;
8945084Sjohnlev 	tty_common_t  *tp = &async->async_ttycommon;
8955084Sjohnlev 	struct iocblk *iocp;
8965084Sjohnlev 	unsigned datasize;
8975084Sjohnlev 	int error = 0;
8985084Sjohnlev 
8995084Sjohnlev #ifdef DEBUG
9005084Sjohnlev 	int instance = xcp->unit;
9015084Sjohnlev 
9025084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_ioctl\n", instance);
9035084Sjohnlev #endif
9045084Sjohnlev 
9055084Sjohnlev 	if (tp->t_iocpending != NULL) {
9065084Sjohnlev 		/*
9075084Sjohnlev 		 * We were holding an "ioctl" response pending the
9085084Sjohnlev 		 * availability of an "mblk" to hold data to be passed up;
9095084Sjohnlev 		 * another "ioctl" came through, which means that "ioctl"
9105084Sjohnlev 		 * must have timed out or been aborted.
9115084Sjohnlev 		 */
9125084Sjohnlev 		freemsg(async->async_ttycommon.t_iocpending);
9135084Sjohnlev 		async->async_ttycommon.t_iocpending = NULL;
9145084Sjohnlev 	}
9155084Sjohnlev 
9165084Sjohnlev 	iocp = (struct iocblk *)mp->b_rptr;
9175084Sjohnlev 
9185084Sjohnlev 	/*
9195084Sjohnlev 	 * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
9205084Sjohnlev 	 * because this function frees up the message block (mp->b_cont) that
9215084Sjohnlev 	 * contains the user location where we pass back the results.
9225084Sjohnlev 	 *
9235084Sjohnlev 	 * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
9245084Sjohnlev 	 * zaps.  We know that ttycommon_ioctl doesn't know any CONS*
9255084Sjohnlev 	 * ioctls, so keep the others safe too.
9265084Sjohnlev 	 */
9275084Sjohnlev 	DEBUGCONT2(XENCONS_DEBUG_IOCTL, "async%d_ioctl: %s\n",
9285084Sjohnlev 	    instance,
9295084Sjohnlev 	    iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
9305084Sjohnlev 	    iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
9315084Sjohnlev 	    iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
9325084Sjohnlev 	    iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : "other");
9335084Sjohnlev 
9345084Sjohnlev 	switch (iocp->ioc_cmd) {
9355084Sjohnlev 	case TIOCMGET:
9365084Sjohnlev 	case TIOCGPPS:
9375084Sjohnlev 	case TIOCSPPS:
9385084Sjohnlev 	case TIOCGPPSEV:
9395084Sjohnlev 	case CONSOPENPOLLEDIO:
9405084Sjohnlev 	case CONSCLOSEPOLLEDIO:
9415084Sjohnlev 	case CONSSETABORTENABLE:
9425084Sjohnlev 	case CONSGETABORTENABLE:
9435084Sjohnlev 		error = -1; /* Do Nothing */
9445084Sjohnlev 		break;
9455084Sjohnlev 	default:
9465084Sjohnlev 
9475084Sjohnlev 		/*
9485084Sjohnlev 		 * The only way in which "ttycommon_ioctl" can fail is if the
9495084Sjohnlev 		 * "ioctl" requires a response containing data to be returned
9505084Sjohnlev 		 * to the user, and no mblk could be allocated for the data.
9515084Sjohnlev 		 * No such "ioctl" alters our state.  Thus, we always go ahead
9525084Sjohnlev 		 * and do any state-changes the "ioctl" calls for.  If we
9535084Sjohnlev 		 * couldn't allocate the data, "ttycommon_ioctl" has stashed
9545084Sjohnlev 		 * the "ioctl" away safely, so we just call "bufcall" to
9555084Sjohnlev 		 * request that we be called back when we stand a better
9565084Sjohnlev 		 * chance of allocating the data.
9575084Sjohnlev 		 */
9585084Sjohnlev 		if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
9595084Sjohnlev 			if (async->async_wbufcid)
9605084Sjohnlev 				unbufcall(async->async_wbufcid);
9615084Sjohnlev 			async->async_wbufcid = bufcall(datasize, BPRI_HI,
9625084Sjohnlev 			    (void (*)(void *)) xcasync_reioctl,
9635084Sjohnlev 			    (void *)(intptr_t)async->async_common->unit);
9645084Sjohnlev 			return;
9655084Sjohnlev 		}
9665084Sjohnlev 	}
9675084Sjohnlev 
9685084Sjohnlev 	mutex_enter(&xcp->excl);
9695084Sjohnlev 
9705084Sjohnlev 	if (error == 0) {
9715084Sjohnlev 		/*
9725084Sjohnlev 		 * "ttycommon_ioctl" did most of the work; we just use the
9735084Sjohnlev 		 * data it set up.
9745084Sjohnlev 		 */
9755084Sjohnlev 		switch (iocp->ioc_cmd) {
9765084Sjohnlev 
9775084Sjohnlev 		case TCSETS:
9785084Sjohnlev 		case TCSETSF:
9795084Sjohnlev 		case TCSETSW:
9805084Sjohnlev 		case TCSETA:
9815084Sjohnlev 		case TCSETAW:
9825084Sjohnlev 		case TCSETAF:
9835084Sjohnlev 			break;
9845084Sjohnlev 		}
9855084Sjohnlev 	} else if (error < 0) {
9865084Sjohnlev 		/*
9875084Sjohnlev 		 * "ttycommon_ioctl" didn't do anything; we process it here.
9885084Sjohnlev 		 */
9895084Sjohnlev 		error = 0;
9905084Sjohnlev 		switch (iocp->ioc_cmd) {
9915084Sjohnlev 
9925084Sjohnlev 		case TCSBRK:
9935084Sjohnlev 			error = miocpullup(mp, sizeof (int));
9945084Sjohnlev 			break;
9955084Sjohnlev 
9965084Sjohnlev 		case TIOCSBRK:
9975084Sjohnlev 			mioc2ack(mp, NULL, 0, 0);
9985084Sjohnlev 			break;
9995084Sjohnlev 
10005084Sjohnlev 		case TIOCCBRK:
10015084Sjohnlev 			mioc2ack(mp, NULL, 0, 0);
10025084Sjohnlev 			break;
10035084Sjohnlev 
10045084Sjohnlev 		case CONSOPENPOLLEDIO:
10055084Sjohnlev 			error = miocpullup(mp, sizeof (cons_polledio_arg_t));
10065084Sjohnlev 			if (error != 0)
10075084Sjohnlev 				break;
10085084Sjohnlev 
10095084Sjohnlev 			*(cons_polledio_arg_t *)mp->b_cont->b_rptr =
10105084Sjohnlev 			    (cons_polledio_arg_t)&xcp->polledio;
10115084Sjohnlev 
10125084Sjohnlev 			mp->b_datap->db_type = M_IOCACK;
10135084Sjohnlev 			break;
10145084Sjohnlev 
10155084Sjohnlev 		case CONSCLOSEPOLLEDIO:
10165084Sjohnlev 			mp->b_datap->db_type = M_IOCACK;
10175084Sjohnlev 			iocp->ioc_error = 0;
10185084Sjohnlev 			iocp->ioc_rval = 0;
10195084Sjohnlev 			break;
10205084Sjohnlev 
10215084Sjohnlev 		case CONSSETABORTENABLE:
10225084Sjohnlev 			error = secpolicy_console(iocp->ioc_cr);
10235084Sjohnlev 			if (error != 0)
10245084Sjohnlev 				break;
10255084Sjohnlev 
10265084Sjohnlev 			if (iocp->ioc_count != TRANSPARENT) {
10275084Sjohnlev 				error = EINVAL;
10285084Sjohnlev 				break;
10295084Sjohnlev 			}
10305084Sjohnlev 
10315084Sjohnlev 			if (*(intptr_t *)mp->b_cont->b_rptr)
10325084Sjohnlev 				xcp->flags |= ASY_CONSOLE;
10335084Sjohnlev 			else
10345084Sjohnlev 				xcp->flags &= ~ASY_CONSOLE;
10355084Sjohnlev 
10365084Sjohnlev 			mp->b_datap->db_type = M_IOCACK;
10375084Sjohnlev 			iocp->ioc_error = 0;
10385084Sjohnlev 			iocp->ioc_rval = 0;
10395084Sjohnlev 			break;
10405084Sjohnlev 
10415084Sjohnlev 		case CONSGETABORTENABLE:
10425084Sjohnlev 			/*CONSTANTCONDITION*/
10435084Sjohnlev 			ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
10445084Sjohnlev 			/*
10455084Sjohnlev 			 * Store the return value right in the payload
10465084Sjohnlev 			 * we were passed.  Crude.
10475084Sjohnlev 			 */
10485084Sjohnlev 			mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
10495084Sjohnlev 			*(boolean_t *)mp->b_cont->b_rptr =
10505084Sjohnlev 			    (xcp->flags & ASY_CONSOLE) != 0;
10515084Sjohnlev 			break;
10525084Sjohnlev 
10535084Sjohnlev 		default:
10545084Sjohnlev 			/*
10555084Sjohnlev 			 * If we don't understand it, it's an error.  NAK it.
10565084Sjohnlev 			 */
10575084Sjohnlev 			error = EINVAL;
10585084Sjohnlev 			break;
10595084Sjohnlev 		}
10605084Sjohnlev 	}
10615084Sjohnlev 	if (error != 0) {
10625084Sjohnlev 		iocp->ioc_error = error;
10635084Sjohnlev 		mp->b_datap->db_type = M_IOCNAK;
10645084Sjohnlev 	}
10655084Sjohnlev 	mutex_exit(&xcp->excl);
10665084Sjohnlev 	qreply(wq, mp);
10675084Sjohnlev 	DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_ioctl: done\n", instance);
10685084Sjohnlev }
10695084Sjohnlev 
10705084Sjohnlev static int
10715084Sjohnlev xenconsrsrv(queue_t *q)
10725084Sjohnlev {
10735084Sjohnlev 	mblk_t *bp;
10745084Sjohnlev 
10755084Sjohnlev 	while (canputnext(q) && (bp = getq(q)))
10765084Sjohnlev 		putnext(q, bp);
10775084Sjohnlev 	return (0);
10785084Sjohnlev }
10795084Sjohnlev 
10805084Sjohnlev /*
10815084Sjohnlev  * Put procedure for write queue.
10825084Sjohnlev  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
10835084Sjohnlev  * set the flow control character for M_STOPI and M_STARTI messages;
10845084Sjohnlev  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
10855084Sjohnlev  * by the start routine, and then call the start routine; discard
10865084Sjohnlev  * everything else.  Note that this driver does not incorporate any
10875084Sjohnlev  * mechanism to negotiate to handle the canonicalization process.
10885084Sjohnlev  * It expects that these functions are handled in upper module(s),
10895084Sjohnlev  * as we do in ldterm.
10905084Sjohnlev  */
10915084Sjohnlev static int
10925084Sjohnlev xenconswput(queue_t *q, mblk_t *mp)
10935084Sjohnlev {
10945084Sjohnlev 	struct asyncline *async;
10955084Sjohnlev 	struct xencons *xcp;
10965084Sjohnlev 
10975084Sjohnlev 	async = (struct asyncline *)q->q_ptr;
10985084Sjohnlev 	xcp = async->async_common;
10995084Sjohnlev 
11005084Sjohnlev 	switch (mp->b_datap->db_type) {
11015084Sjohnlev 
11025084Sjohnlev 	case M_STOP:
11035084Sjohnlev 		mutex_enter(&xcp->excl);
11045084Sjohnlev 		async->async_flags |= ASYNC_STOPPED;
11055084Sjohnlev 		mutex_exit(&xcp->excl);
11065084Sjohnlev 		freemsg(mp);
11075084Sjohnlev 		break;
11085084Sjohnlev 
11095084Sjohnlev 	case M_START:
11105084Sjohnlev 		mutex_enter(&xcp->excl);
11115084Sjohnlev 		if (async->async_flags & ASYNC_STOPPED) {
11125084Sjohnlev 			async->async_flags &= ~ASYNC_STOPPED;
11135084Sjohnlev 			xcasync_start(async);
11145084Sjohnlev 		}
11155084Sjohnlev 		mutex_exit(&xcp->excl);
11165084Sjohnlev 		freemsg(mp);
11175084Sjohnlev 		break;
11185084Sjohnlev 
11195084Sjohnlev 	case M_IOCTL:
11205084Sjohnlev 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
11215084Sjohnlev 
11225084Sjohnlev 		case TCSETSW:
11235084Sjohnlev 		case TCSETSF:
11245084Sjohnlev 		case TCSETAW:
11255084Sjohnlev 		case TCSETAF:
11265084Sjohnlev 			/*
11275084Sjohnlev 			 * The changes do not take effect until all
11285084Sjohnlev 			 * output queued before them is drained.
11295084Sjohnlev 			 * Put this message on the queue, so that
11305084Sjohnlev 			 * "xcasync_start" will see it when it's done
11315084Sjohnlev 			 * with the output before it.  Poke the
11325084Sjohnlev 			 * start routine, just in case.
11335084Sjohnlev 			 */
11345084Sjohnlev 			(void) putq(q, mp);
11355084Sjohnlev 			mutex_enter(&xcp->excl);
11365084Sjohnlev 			xcasync_start(async);
11375084Sjohnlev 			mutex_exit(&xcp->excl);
11385084Sjohnlev 			break;
11395084Sjohnlev 
11405084Sjohnlev 		default:
11415084Sjohnlev 			/*
11425084Sjohnlev 			 * Do it now.
11435084Sjohnlev 			 */
11445084Sjohnlev 			xcasync_ioctl(async, q, mp);
11455084Sjohnlev 			break;
11465084Sjohnlev 		}
11475084Sjohnlev 		break;
11485084Sjohnlev 
11495084Sjohnlev 	case M_FLUSH:
11505084Sjohnlev 		if (*mp->b_rptr & FLUSHW) {
11515084Sjohnlev 			mutex_enter(&xcp->excl);
11525084Sjohnlev 			/*
11535084Sjohnlev 			 * Flush our write queue.
11545084Sjohnlev 			 */
11555084Sjohnlev 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
11565084Sjohnlev 			if (async->async_xmitblk != NULL) {
11575084Sjohnlev 				freeb(async->async_xmitblk);
11585084Sjohnlev 				async->async_xmitblk = NULL;
11595084Sjohnlev 			}
11605084Sjohnlev 			mutex_exit(&xcp->excl);
11615084Sjohnlev 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
11625084Sjohnlev 		}
11635084Sjohnlev 		if (*mp->b_rptr & FLUSHR) {
11645084Sjohnlev 			flushq(RD(q), FLUSHDATA);
11655084Sjohnlev 			qreply(q, mp);	/* give the read queues a crack at it */
11665084Sjohnlev 		} else {
11675084Sjohnlev 			freemsg(mp);
11685084Sjohnlev 		}
11695084Sjohnlev 
11705084Sjohnlev 		/*
11715084Sjohnlev 		 * We must make sure we process messages that survive the
11725084Sjohnlev 		 * write-side flush.
11735084Sjohnlev 		 */
11745084Sjohnlev 		mutex_enter(&xcp->excl);
11755084Sjohnlev 		xcasync_start(async);
11765084Sjohnlev 		mutex_exit(&xcp->excl);
11775084Sjohnlev 		break;
11785084Sjohnlev 
11795084Sjohnlev 	case M_BREAK:
11805084Sjohnlev 	case M_DELAY:
11815084Sjohnlev 	case M_DATA:
11825084Sjohnlev 		/*
11835084Sjohnlev 		 * Queue the message up to be transmitted,
11845084Sjohnlev 		 * and poke the start routine.
11855084Sjohnlev 		 */
11865084Sjohnlev 		(void) putq(q, mp);
11875084Sjohnlev 		mutex_enter(&xcp->excl);
11885084Sjohnlev 		xcasync_start(async);
11895084Sjohnlev 		mutex_exit(&xcp->excl);
11905084Sjohnlev 		break;
11915084Sjohnlev 
11925084Sjohnlev 	case M_STOPI:
11935084Sjohnlev 		mutex_enter(&xcp->excl);
11945084Sjohnlev 		mutex_enter(&xcp->excl);
11955084Sjohnlev 		if (!(async->async_inflow_source & IN_FLOW_USER)) {
11965084Sjohnlev 			(void) xcasync_flowcontrol_sw_input(xcp, FLOW_STOP,
11975084Sjohnlev 			    IN_FLOW_USER);
11985084Sjohnlev 		}
11995084Sjohnlev 		mutex_exit(&xcp->excl);
12005084Sjohnlev 		mutex_exit(&xcp->excl);
12015084Sjohnlev 		freemsg(mp);
12025084Sjohnlev 		break;
12035084Sjohnlev 
12045084Sjohnlev 	case M_STARTI:
12055084Sjohnlev 		mutex_enter(&xcp->excl);
12065084Sjohnlev 		mutex_enter(&xcp->excl);
12075084Sjohnlev 		if (async->async_inflow_source & IN_FLOW_USER) {
12085084Sjohnlev 			(void) xcasync_flowcontrol_sw_input(xcp, FLOW_START,
12095084Sjohnlev 			    IN_FLOW_USER);
12105084Sjohnlev 		}
12115084Sjohnlev 		mutex_exit(&xcp->excl);
12125084Sjohnlev 		mutex_exit(&xcp->excl);
12135084Sjohnlev 		freemsg(mp);
12145084Sjohnlev 		break;
12155084Sjohnlev 
12165084Sjohnlev 	case M_CTL:
12175084Sjohnlev 		if (MBLKL(mp) >= sizeof (struct iocblk) &&
12185084Sjohnlev 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
12195084Sjohnlev 			((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
12205084Sjohnlev 			qreply(q, mp);
12215084Sjohnlev 		} else {
12225084Sjohnlev 			freemsg(mp);
12235084Sjohnlev 		}
12245084Sjohnlev 		break;
12255084Sjohnlev 
12265084Sjohnlev 	default:
12275084Sjohnlev 		freemsg(mp);
12285084Sjohnlev 		break;
12295084Sjohnlev 	}
12305084Sjohnlev 	return (0);
12315084Sjohnlev }
12325084Sjohnlev 
12335084Sjohnlev /*
12345084Sjohnlev  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
12355084Sjohnlev  * the buffer we need.
12365084Sjohnlev  */
12375084Sjohnlev static void
12385084Sjohnlev xcasync_reioctl(void *unit)
12395084Sjohnlev {
12405084Sjohnlev 	int instance = (uintptr_t)unit;
12415084Sjohnlev 	struct asyncline *async;
12425084Sjohnlev 	struct xencons *xcp;
12435084Sjohnlev 	queue_t	*q;
12445084Sjohnlev 	mblk_t	*mp;
12455084Sjohnlev 
12465084Sjohnlev 	xcp = ddi_get_soft_state(xencons_soft_state, instance);
12475084Sjohnlev 	ASSERT(xcp != NULL);
12485084Sjohnlev 	async = xcp->priv;
12495084Sjohnlev 
12505084Sjohnlev 	/*
12515084Sjohnlev 	 * The bufcall is no longer pending.
12525084Sjohnlev 	 */
12535084Sjohnlev 	mutex_enter(&xcp->excl);
12545084Sjohnlev 	async->async_wbufcid = 0;
12555084Sjohnlev 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
12565084Sjohnlev 		mutex_exit(&xcp->excl);
12575084Sjohnlev 		return;
12585084Sjohnlev 	}
12595084Sjohnlev 	if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
12605084Sjohnlev 		/* not pending any more */
12615084Sjohnlev 		async->async_ttycommon.t_iocpending = NULL;
12625084Sjohnlev 		mutex_exit(&xcp->excl);
12635084Sjohnlev 		xcasync_ioctl(async, q, mp);
12645084Sjohnlev 	} else
12655084Sjohnlev 		mutex_exit(&xcp->excl);
12665084Sjohnlev }
12675084Sjohnlev 
12685084Sjohnlev 
12695084Sjohnlev /*
12705084Sjohnlev  * debugger/console support routines.
12715084Sjohnlev  */
12725084Sjohnlev 
12735084Sjohnlev /*
12745084Sjohnlev  * put a character out
12755084Sjohnlev  * Do not use interrupts.  If char is LF, put out CR, LF.
12765084Sjohnlev  */
12775084Sjohnlev /*ARGSUSED*/
12785084Sjohnlev static void
12795084Sjohnlev xenconsputchar(cons_polledio_arg_t arg, uchar_t c)
12805084Sjohnlev {
12815084Sjohnlev 	struct xencons *xcp = xencons_console;
12825084Sjohnlev 	volatile struct xencons_interface *ifp = xcp->ifp;
12835084Sjohnlev 	XENCONS_RING_IDX prod;
12845084Sjohnlev 
12855084Sjohnlev 	if (c == '\n')
12865084Sjohnlev 		xenconsputchar(arg, '\r');
12875084Sjohnlev 
12885084Sjohnlev 	/*
12895084Sjohnlev 	 * domain 0 can use the console I/O...
12905084Sjohnlev 	 */
12915084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
12925084Sjohnlev 		char	buffer[1];
12935084Sjohnlev 
12945084Sjohnlev 		buffer[0] = c;
12955084Sjohnlev 		(void) HYPERVISOR_console_io(CONSOLEIO_write, 1, buffer);
12965084Sjohnlev 		return;
12975084Sjohnlev 	}
12985084Sjohnlev 
12995084Sjohnlev 	/*
13005084Sjohnlev 	 * domU has to go through dom0 virtual console.
13015084Sjohnlev 	 */
13025084Sjohnlev 	while (ifp->out_prod - ifp->out_cons == sizeof (ifp->out))
13035084Sjohnlev 		(void) HYPERVISOR_yield();
13045084Sjohnlev 
13055084Sjohnlev 	prod = ifp->out_prod;
13065084Sjohnlev 	ifp->out[MASK_XENCONS_IDX(prod++, ifp->out)] = c;
13075084Sjohnlev 	membar_producer();
13085084Sjohnlev 	ifp->out_prod = prod;
13095084Sjohnlev 	ec_notify_via_evtchn(xcp->evtchn);
13105084Sjohnlev }
13115084Sjohnlev 
13125084Sjohnlev /*
13135084Sjohnlev  * See if there's a character available. If no character is
13145084Sjohnlev  * available, return 0. Run in polled mode, no interrupts.
13155084Sjohnlev  */
13165084Sjohnlev static boolean_t
13175084Sjohnlev xenconsischar(cons_polledio_arg_t arg)
13185084Sjohnlev {
13195084Sjohnlev 	struct xencons *xcp = (struct xencons *)arg;
13205084Sjohnlev 	volatile struct xencons_interface *ifp = xcp->ifp;
13215084Sjohnlev 
13225084Sjohnlev 	if (xcp->polldix < xcp->polllen)
13235084Sjohnlev 		return (B_TRUE);
13245084Sjohnlev 	/*
13255084Sjohnlev 	 * domain 0 can use the console I/O...
13265084Sjohnlev 	 */
13275084Sjohnlev 	xcp->polldix = 0;
13285084Sjohnlev 	xcp->polllen = 0;
13295084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
13305084Sjohnlev 		xcp->polllen = HYPERVISOR_console_io(CONSOLEIO_read, 1,
13315084Sjohnlev 		    (char *)xcp->pollbuf);
13325084Sjohnlev 		return (xcp->polllen != 0);
13335084Sjohnlev 	}
13345084Sjohnlev 
13355084Sjohnlev 	/*
13365084Sjohnlev 	 * domU has to go through virtual console device.
13375084Sjohnlev 	 */
13385084Sjohnlev 	if (ifp->in_prod != ifp->in_cons) {
13395084Sjohnlev 		XENCONS_RING_IDX cons;
13405084Sjohnlev 
13415084Sjohnlev 		cons = ifp->in_cons;
13425084Sjohnlev 		membar_enter();
13435084Sjohnlev 		xcp->pollbuf[0] = ifp->in[MASK_XENCONS_IDX(cons++, ifp->in)];
13445084Sjohnlev 		membar_producer();
13455084Sjohnlev 		ifp->in_cons = cons;
13465084Sjohnlev 		xcp->polllen = 1;
13475084Sjohnlev 	}
13485084Sjohnlev 	return (xcp->polllen != 0);
13495084Sjohnlev }
13505084Sjohnlev 
13515084Sjohnlev /*
13525084Sjohnlev  * Get a character. Run in polled mode, no interrupts.
13535084Sjohnlev  */
13545084Sjohnlev static int
13555084Sjohnlev xenconsgetchar(cons_polledio_arg_t arg)
13565084Sjohnlev {
13575084Sjohnlev 	struct xencons *xcp = (struct xencons *)arg;
13585084Sjohnlev 
13595084Sjohnlev 	ec_wait_on_evtchn(xcp->evtchn, (int (*)(void *))xenconsischar, arg);
13605084Sjohnlev 
13615084Sjohnlev 	return (xcp->pollbuf[xcp->polldix++]);
13625084Sjohnlev }
13635084Sjohnlev 
13645084Sjohnlev static void
13655084Sjohnlev xenconserror(int level, const char *fmt, ...)
13665084Sjohnlev {
13675084Sjohnlev 	va_list adx;
13685084Sjohnlev 	static time_t	last;
13695084Sjohnlev 	static const char *lastfmt;
13705084Sjohnlev 	time_t now;
13715084Sjohnlev 
13725084Sjohnlev 	/*
13735084Sjohnlev 	 * Don't print the same error message too often.
13745084Sjohnlev 	 * Print the message only if we have not printed the
13755084Sjohnlev 	 * message within the last second.
13765084Sjohnlev 	 * Note: that fmt cannot be a pointer to a string
13775084Sjohnlev 	 * stored on the stack. The fmt pointer
13785084Sjohnlev 	 * must be in the data segment otherwise lastfmt would point
13795084Sjohnlev 	 * to non-sense.
13805084Sjohnlev 	 */
13815084Sjohnlev 	now = gethrestime_sec();
13825084Sjohnlev 	if (last == now && lastfmt == fmt)
13835084Sjohnlev 		return;
13845084Sjohnlev 
13855084Sjohnlev 	last = now;
13865084Sjohnlev 	lastfmt = fmt;
13875084Sjohnlev 
13885084Sjohnlev 	va_start(adx, fmt);
13895084Sjohnlev 	vcmn_err(level, fmt, adx);
13905084Sjohnlev 	va_end(adx);
13915084Sjohnlev }
13925084Sjohnlev 
13935084Sjohnlev 
13945084Sjohnlev /*
13955084Sjohnlev  * Check for abort character sequence
13965084Sjohnlev  */
13975084Sjohnlev static boolean_t
13985084Sjohnlev abort_charseq_recognize(uchar_t ch)
13995084Sjohnlev {
14005084Sjohnlev 	static int state = 0;
14015084Sjohnlev #define	CNTRL(c) ((c)&037)
14025084Sjohnlev 	static char sequence[] = { '\r', '~', CNTRL('b') };
14035084Sjohnlev 
14045084Sjohnlev 	if (ch == sequence[state]) {
14055084Sjohnlev 		if (++state >= sizeof (sequence)) {
14065084Sjohnlev 			state = 0;
14075084Sjohnlev 			return (B_TRUE);
14085084Sjohnlev 		}
14095084Sjohnlev 	} else {
14105084Sjohnlev 		state = (ch == sequence[0]) ? 1 : 0;
14115084Sjohnlev 	}
14125084Sjohnlev 	return (B_FALSE);
14135084Sjohnlev }
14145084Sjohnlev 
14155084Sjohnlev /*
14165084Sjohnlev  * Flow control functions
14175084Sjohnlev  */
14185084Sjohnlev 
14195084Sjohnlev /*
14205084Sjohnlev  * Software output flow control
14215084Sjohnlev  * This function can be executed sucessfully at any situation.
14225084Sjohnlev  * It does not handle HW, and just change the SW output flow control flag.
14235084Sjohnlev  * INPUT VALUE of onoff:
14245084Sjohnlev  *                 FLOW_START means to clear SW output flow control flag,
14255084Sjohnlev  *			also set ASYNC_OUT_FLW_RESUME.
14265084Sjohnlev  *                 FLOW_STOP means to set SW output flow control flag,
14275084Sjohnlev  *			also clear ASYNC_OUT_FLW_RESUME.
14285084Sjohnlev  */
14295084Sjohnlev static void
14305084Sjohnlev xcasync_flowcontrol_sw_output(struct xencons *xcp, async_flowc_action onoff)
14315084Sjohnlev {
14325084Sjohnlev 	struct asyncline *async = xcp->priv;
14335084Sjohnlev 	int instance = xcp->unit;
14345084Sjohnlev 
14355084Sjohnlev 	ASSERT(mutex_owned(&xcp->excl));
14365084Sjohnlev 
14375084Sjohnlev 	if (!(async->async_ttycommon.t_iflag & IXON))
14385084Sjohnlev 		return;
14395084Sjohnlev 
14405084Sjohnlev 	switch (onoff) {
14415084Sjohnlev 	case FLOW_STOP:
14425084Sjohnlev 		async->async_flags |= ASYNC_SW_OUT_FLW;
14435084Sjohnlev 		async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
14445084Sjohnlev 		DEBUGCONT1(XENCONS_DEBUG_SFLOW,
14455084Sjohnlev 		    "xencons%d: output sflow stop\n", instance);
14465084Sjohnlev 		break;
14475084Sjohnlev 	case FLOW_START:
14485084Sjohnlev 		async->async_flags &= ~ASYNC_SW_OUT_FLW;
14495084Sjohnlev 		async->async_flags |= ASYNC_OUT_FLW_RESUME;
14505084Sjohnlev 		DEBUGCONT1(XENCONS_DEBUG_SFLOW,
14515084Sjohnlev 		    "xencons%d: output sflow start\n", instance);
14525084Sjohnlev 		break;
14535084Sjohnlev 	default:
14545084Sjohnlev 		break;
14555084Sjohnlev 	}
14565084Sjohnlev }
14575084Sjohnlev 
14585084Sjohnlev /*
14595084Sjohnlev  * Software input flow control
14605084Sjohnlev  * This function can execute software input flow control
14615084Sjohnlev  * INPUT VALUE of onoff:
14625084Sjohnlev  *               FLOW_START means to send out a XON char
14635084Sjohnlev  *                          and clear SW input flow control flag.
14645084Sjohnlev  *               FLOW_STOP means to send out a XOFF char
14655084Sjohnlev  *                          and set SW input flow control flag.
14665084Sjohnlev  *               FLOW_CHECK means to check whether there is pending XON/XOFF
14675084Sjohnlev  *                          if it is true, send it out.
14685084Sjohnlev  * INPUT VALUE of type:
14695084Sjohnlev  *		 IN_FLOW_STREAMS means flow control is due to STREAMS
14705084Sjohnlev  *		 IN_FLOW_USER means flow control is due to user's commands
14715084Sjohnlev  * RETURN VALUE: B_FALSE means no flow control char is sent
14725084Sjohnlev  *               B_TRUE means one flow control char is sent
14735084Sjohnlev  */
14745084Sjohnlev static boolean_t
14755084Sjohnlev xcasync_flowcontrol_sw_input(struct xencons *xcp, async_flowc_action onoff,
14765084Sjohnlev     int type)
14775084Sjohnlev {
14785084Sjohnlev 	struct asyncline *async = xcp->priv;
14795084Sjohnlev 	int instance = xcp->unit;
14805084Sjohnlev 	int rval = B_FALSE;
14815084Sjohnlev 
14825084Sjohnlev 	ASSERT(mutex_owned(&xcp->excl));
14835084Sjohnlev 
14845084Sjohnlev 	if (!(async->async_ttycommon.t_iflag & IXOFF))
14855084Sjohnlev 		return (rval);
14865084Sjohnlev 
14875084Sjohnlev 	/*
14885084Sjohnlev 	 * If we get this far, then we know IXOFF is set.
14895084Sjohnlev 	 */
14905084Sjohnlev 	switch (onoff) {
14915084Sjohnlev 	case FLOW_STOP:
14925084Sjohnlev 		async->async_inflow_source |= type;
14935084Sjohnlev 
14945084Sjohnlev 		/*
14955084Sjohnlev 		 * We'll send an XOFF character for each of up to
14965084Sjohnlev 		 * three different input flow control attempts to stop input.
14975084Sjohnlev 		 * If we already send out one XOFF, but FLOW_STOP comes again,
14985084Sjohnlev 		 * it seems that input flow control becomes more serious,
14995084Sjohnlev 		 * then send XOFF again.
15005084Sjohnlev 		 */
15015084Sjohnlev 		if (async->async_inflow_source & (IN_FLOW_STREAMS |
15025084Sjohnlev 		    IN_FLOW_USER))
15035084Sjohnlev 			async->async_flags |= ASYNC_SW_IN_FLOW |
15045084Sjohnlev 			    ASYNC_SW_IN_NEEDED;
15055084Sjohnlev 		DEBUGCONT2(XENCONS_DEBUG_SFLOW, "xencons%d: input sflow stop, "
15065084Sjohnlev 		    "type = %x\n", instance, async->async_inflow_source);
15075084Sjohnlev 		break;
15085084Sjohnlev 	case FLOW_START:
15095084Sjohnlev 		async->async_inflow_source &= ~type;
15105084Sjohnlev 		if (async->async_inflow_source == 0) {
15115084Sjohnlev 			async->async_flags = (async->async_flags &
15125084Sjohnlev 			    ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED;
15135084Sjohnlev 			DEBUGCONT1(XENCONS_DEBUG_SFLOW, "xencons%d: "
15145084Sjohnlev 			    "input sflow start\n", instance);
15155084Sjohnlev 		}
15165084Sjohnlev 		break;
15175084Sjohnlev 	default:
15185084Sjohnlev 		break;
15195084Sjohnlev 	}
15205084Sjohnlev 
15215084Sjohnlev 	if (async->async_flags & ASYNC_SW_IN_NEEDED) {
15225084Sjohnlev 		/*
15235084Sjohnlev 		 * If we get this far, then we know we need to send out
15245084Sjohnlev 		 * XON or XOFF char.
15255084Sjohnlev 		 */
15265084Sjohnlev 		char c;
15275084Sjohnlev 
15285084Sjohnlev 		rval = B_TRUE;
15295084Sjohnlev 		c = (async->async_flags & ASYNC_SW_IN_FLOW) ?
15305084Sjohnlev 		    async->async_stopc : async->async_startc;
15315084Sjohnlev 		if (DOMAIN_IS_INITDOMAIN(xen_info)) {
15325084Sjohnlev 			(void) HYPERVISOR_console_io(CONSOLEIO_write, 1, &c);
15335084Sjohnlev 			async->async_flags &= ~ASYNC_SW_IN_NEEDED;
15345084Sjohnlev 			return (rval);
15355084Sjohnlev 		} else {
15365084Sjohnlev 			xenconsputchar(NULL, c);
15375084Sjohnlev 		}
15385084Sjohnlev 	}
15395084Sjohnlev 	return (rval);
15405084Sjohnlev }
15415084Sjohnlev 
15425084Sjohnlev struct module_info xencons_info = {
15435084Sjohnlev 	0,
15445084Sjohnlev 	"xencons",
15455084Sjohnlev 	0,
15465084Sjohnlev 	INFPSZ,
15475084Sjohnlev 	4096,
15485084Sjohnlev 	128
15495084Sjohnlev };
15505084Sjohnlev 
15515084Sjohnlev static struct qinit xencons_rint = {
15525084Sjohnlev 	putq,
15535084Sjohnlev 	xenconsrsrv,
15545084Sjohnlev 	xenconsopen,
15555084Sjohnlev 	xenconsclose,
15565084Sjohnlev 	NULL,
15575084Sjohnlev 	&xencons_info,
15585084Sjohnlev 	NULL
15595084Sjohnlev };
15605084Sjohnlev 
15615084Sjohnlev static struct qinit xencons_wint = {
15625084Sjohnlev 	xenconswput,
15635084Sjohnlev 	NULL,
15645084Sjohnlev 	NULL,
15655084Sjohnlev 	NULL,
15665084Sjohnlev 	NULL,
15675084Sjohnlev 	&xencons_info,
15685084Sjohnlev 	NULL
15695084Sjohnlev };
15705084Sjohnlev 
15715084Sjohnlev struct streamtab xencons_str_info = {
15725084Sjohnlev 	&xencons_rint,
15735084Sjohnlev 	&xencons_wint,
15745084Sjohnlev 	NULL,
15755084Sjohnlev 	NULL
15765084Sjohnlev };
15775084Sjohnlev 
15785084Sjohnlev static struct cb_ops cb_xencons_ops = {
15795084Sjohnlev 	nodev,			/* cb_open */
15805084Sjohnlev 	nodev,			/* cb_close */
15815084Sjohnlev 	nodev,			/* cb_strategy */
15825084Sjohnlev 	nodev,			/* cb_print */
15835084Sjohnlev 	nodev,			/* cb_dump */
15845084Sjohnlev 	nodev,			/* cb_read */
15855084Sjohnlev 	nodev,			/* cb_write */
15865084Sjohnlev 	nodev,			/* cb_ioctl */
15875084Sjohnlev 	nodev,			/* cb_devmap */
15885084Sjohnlev 	nodev,			/* cb_mmap */
15895084Sjohnlev 	nodev,			/* cb_segmap */
15905084Sjohnlev 	nochpoll,		/* cb_chpoll */
15915084Sjohnlev 	ddi_prop_op,		/* cb_prop_op */
15925084Sjohnlev 	&xencons_str_info,		/* cb_stream */
15935084Sjohnlev 	D_MP			/* cb_flag */
15945084Sjohnlev };
15955084Sjohnlev 
15965084Sjohnlev struct dev_ops xencons_ops = {
15975084Sjohnlev 	DEVO_REV,		/* devo_rev */
15985084Sjohnlev 	0,			/* devo_refcnt */
15995084Sjohnlev 	xenconsinfo,		/* devo_getinfo */
16005084Sjohnlev 	nulldev,		/* devo_identify */
16015084Sjohnlev 	nulldev,		/* devo_probe */
16025084Sjohnlev 	xenconsattach,		/* devo_attach */
16035084Sjohnlev 	xenconsdetach,		/* devo_detach */
16045084Sjohnlev 	nodev,			/* devo_reset */
1605*7656SSherry.Moore@Sun.COM 	&cb_xencons_ops,	/* devo_cb_ops */
1606*7656SSherry.Moore@Sun.COM 	NULL,			/* devo_bus_ops */
1607*7656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
1608*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
16095084Sjohnlev };
16105084Sjohnlev 
16115084Sjohnlev static struct modldrv modldrv = {
16125084Sjohnlev 	&mod_driverops, /* Type of module.  This one is a driver */
1613*7656SSherry.Moore@Sun.COM 	"virtual console driver",
16145084Sjohnlev 	&xencons_ops,	/* driver ops */
16155084Sjohnlev };
16165084Sjohnlev 
16175084Sjohnlev static struct modlinkage modlinkage = {
16185084Sjohnlev 	MODREV_1,
16195084Sjohnlev 	(void *)&modldrv,
16205084Sjohnlev 	NULL
16215084Sjohnlev };
16225084Sjohnlev 
16235084Sjohnlev int
16245084Sjohnlev _init(void)
16255084Sjohnlev {
16265084Sjohnlev 	int rv;
16275084Sjohnlev 
16285084Sjohnlev 	if ((rv = ddi_soft_state_init(&xencons_soft_state,
16295084Sjohnlev 	    sizeof (struct xencons), 1)) != 0)
16305084Sjohnlev 		return (rv);
16315084Sjohnlev 	if ((rv = mod_install(&modlinkage)) != 0) {
16325084Sjohnlev 		ddi_soft_state_fini(&xencons_soft_state);
16335084Sjohnlev 		return (rv);
16345084Sjohnlev 	}
16355084Sjohnlev 	DEBUGCONT2(XENCONS_DEBUG_INIT, "%s, debug = %x\n",
16365084Sjohnlev 	    modldrv.drv_linkinfo, debug);
16375084Sjohnlev 	return (0);
16385084Sjohnlev }
16395084Sjohnlev 
16405084Sjohnlev int
16415084Sjohnlev _fini(void)
16425084Sjohnlev {
16435084Sjohnlev 	int rv;
16445084Sjohnlev 
16455084Sjohnlev 	if ((rv = mod_remove(&modlinkage)) != 0)
16465084Sjohnlev 		return (rv);
16475084Sjohnlev 
16485084Sjohnlev 	ddi_soft_state_fini(&xencons_soft_state);
16495084Sjohnlev 	return (0);
16505084Sjohnlev }
16515084Sjohnlev 
16525084Sjohnlev int
16535084Sjohnlev _info(struct modinfo *modinfop)
16545084Sjohnlev {
16555084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
16565084Sjohnlev }
1657