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