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