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