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
xenconssetup_avintr(struct xencons * xcp,int attach)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
xenconssetup_add_avintr(struct xencons * xcp)1905084Sjohnlev xenconssetup_add_avintr(struct xencons *xcp)
1915084Sjohnlev {
1925084Sjohnlev xenconssetup_avintr(xcp, B_TRUE);
1935084Sjohnlev }
1945084Sjohnlev
1955084Sjohnlev static void
xenconssetup_rem_avintr(struct xencons * xcp)1965084Sjohnlev xenconssetup_rem_avintr(struct xencons *xcp)
1975084Sjohnlev {
1985084Sjohnlev xenconssetup_avintr(xcp, B_FALSE);
1995084Sjohnlev }
2005084Sjohnlev
2015084Sjohnlev static int
xenconsdetach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
xenconssetup(struct xencons * xcp)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
xenconsattach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
xenconsinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
xencons_soft_state_free(struct xencons * xcp)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
xenconsopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)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
xenconsclose(queue_t * q,int flag,cred_t * credp)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
xencons_rxint(struct xencons * xcp)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
xencons_txint(struct xencons * xcp)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
xenconsintr(caddr_t arg)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
xenconsintr_priv(caddr_t arg)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
xcasync_start(struct asyncline * async)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
xcasync_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp)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
xenconsrsrv(queue_t * q)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
xenconswput(queue_t * q,mblk_t * mp)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
xcasync_reioctl(void * unit)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
xenconsputchar(cons_polledio_arg_t arg,uchar_t c)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
xenconsischar(cons_polledio_arg_t arg)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
xenconsgetchar(cons_polledio_arg_t arg)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
xenconserror(int level,const char * fmt,...)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
abort_charseq_recognize(uchar_t ch)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
xcasync_flowcontrol_sw_output(struct xencons * xcp,async_flowc_action onoff)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
xcasync_flowcontrol_sw_input(struct xencons * xcp,async_flowc_action onoff,int type)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)16535084Sjohnlev _info(struct modinfo *modinfop)
16545084Sjohnlev {
16555084Sjohnlev return (mod_info(&modlinkage, modinfop));
16565084Sjohnlev }
1657