1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 23843e1988Sjohnlev /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 24843e1988Sjohnlev /* All Rights Reserved */ 25843e1988Sjohnlev 26843e1988Sjohnlev /* 27*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 28843e1988Sjohnlev * Use is subject to license terms. 29843e1988Sjohnlev */ 30843e1988Sjohnlev 31843e1988Sjohnlev 32843e1988Sjohnlev /* 33843e1988Sjohnlev * 34843e1988Sjohnlev * Copyright (c) 2004 Christian Limpach. 35843e1988Sjohnlev * All rights reserved. 36843e1988Sjohnlev * 37843e1988Sjohnlev * Redistribution and use in source and binary forms, with or without 38843e1988Sjohnlev * modification, are permitted provided that the following conditions 39843e1988Sjohnlev * are met: 40843e1988Sjohnlev * 1. Redistributions of source code must retain the above copyright 41843e1988Sjohnlev * notice, this list of conditions and the following disclaimer. 42843e1988Sjohnlev * 2. Redistributions in binary form must reproduce the above copyright 43843e1988Sjohnlev * notice, this list of conditions and the following disclaimer in the 44843e1988Sjohnlev * documentation and/or other materials provided with the distribution. 45843e1988Sjohnlev * 3. This section intentionally left blank. 46843e1988Sjohnlev * 4. The name of the author may not be used to endorse or promote products 47843e1988Sjohnlev * derived from this software without specific prior written permission. 48843e1988Sjohnlev * 49843e1988Sjohnlev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 50843e1988Sjohnlev * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 51843e1988Sjohnlev * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 52843e1988Sjohnlev * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 53843e1988Sjohnlev * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 54843e1988Sjohnlev * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 55843e1988Sjohnlev * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 56843e1988Sjohnlev * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 57843e1988Sjohnlev * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 58843e1988Sjohnlev * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59843e1988Sjohnlev */ 60843e1988Sjohnlev /* 61843e1988Sjohnlev * Section 3 of the above license was updated in response to bug 6379571. 62843e1988Sjohnlev */ 63843e1988Sjohnlev 64843e1988Sjohnlev /* 65843e1988Sjohnlev * Hypervisor virtual console driver 66843e1988Sjohnlev */ 67843e1988Sjohnlev 68843e1988Sjohnlev #include <sys/param.h> 69843e1988Sjohnlev #include <sys/types.h> 70843e1988Sjohnlev #include <sys/signal.h> 71843e1988Sjohnlev #include <sys/stream.h> 72843e1988Sjohnlev #include <sys/termio.h> 73843e1988Sjohnlev #include <sys/errno.h> 74843e1988Sjohnlev #include <sys/file.h> 75843e1988Sjohnlev #include <sys/cmn_err.h> 76843e1988Sjohnlev #include <sys/stropts.h> 77843e1988Sjohnlev #include <sys/strsubr.h> 78843e1988Sjohnlev #include <sys/strtty.h> 79843e1988Sjohnlev #include <sys/debug.h> 80843e1988Sjohnlev #include <sys/kbio.h> 81843e1988Sjohnlev #include <sys/cred.h> 82843e1988Sjohnlev #include <sys/stat.h> 83843e1988Sjohnlev #include <sys/consdev.h> 84843e1988Sjohnlev #include <sys/mkdev.h> 85843e1988Sjohnlev #include <sys/kmem.h> 86843e1988Sjohnlev #include <sys/cred.h> 87843e1988Sjohnlev #include <sys/strsun.h> 88843e1988Sjohnlev #ifdef DEBUG 89843e1988Sjohnlev #include <sys/promif.h> 90843e1988Sjohnlev #endif 91843e1988Sjohnlev #include <sys/modctl.h> 92843e1988Sjohnlev #include <sys/ddi.h> 93843e1988Sjohnlev #include <sys/sunddi.h> 94843e1988Sjohnlev #include <sys/sunndi.h> 95843e1988Sjohnlev #include <sys/policy.h> 96843e1988Sjohnlev #include <sys/atomic.h> 97843e1988Sjohnlev #include <sys/psm.h> 98843e1988Sjohnlev #include <xen/public/io/console.h> 99843e1988Sjohnlev 100843e1988Sjohnlev #include "xencons.h" 101843e1988Sjohnlev 102843e1988Sjohnlev #include <sys/hypervisor.h> 103843e1988Sjohnlev #include <sys/evtchn_impl.h> 104843e1988Sjohnlev #include <xen/sys/xenbus_impl.h> 105843e1988Sjohnlev #include <xen/sys/xendev.h> 106843e1988Sjohnlev 107843e1988Sjohnlev #ifdef DEBUG 108843e1988Sjohnlev #define XENCONS_DEBUG_INIT 0x0001 /* msgs during driver initialization. */ 109843e1988Sjohnlev #define XENCONS_DEBUG_INPUT 0x0002 /* characters received during int. */ 110843e1988Sjohnlev #define XENCONS_DEBUG_EOT 0x0004 /* msgs when wait for xmit to finish. */ 111843e1988Sjohnlev #define XENCONS_DEBUG_CLOSE 0x0008 /* msgs when driver open/close called */ 112843e1988Sjohnlev #define XENCONS_DEBUG_PROCS 0x0020 /* each proc name as it is entered. */ 113843e1988Sjohnlev #define XENCONS_DEBUG_OUT 0x0100 /* msgs about output events. */ 114843e1988Sjohnlev #define XENCONS_DEBUG_BUSY 0x0200 /* msgs when xmit is enabled/disabled */ 115843e1988Sjohnlev #define XENCONS_DEBUG_MODEM 0x0400 /* msgs about modem status & control. */ 116843e1988Sjohnlev #define XENCONS_DEBUG_MODM2 0x0800 /* msgs about modem status & control. */ 117843e1988Sjohnlev #define XENCONS_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */ 118843e1988Sjohnlev #define XENCONS_DEBUG_CHIP 0x2000 /* msgs about chip identification. */ 119843e1988Sjohnlev #define XENCONS_DEBUG_SFLOW 0x4000 /* msgs when S/W flowcontrol active */ 120843e1988Sjohnlev #define XENCONS_DEBUG(x) (debug & (x)) 121843e1988Sjohnlev static int debug = 0; 122843e1988Sjohnlev #else 123843e1988Sjohnlev #define XENCONS_DEBUG(x) B_FALSE 124843e1988Sjohnlev #endif 125843e1988Sjohnlev 126843e1988Sjohnlev #define XENCONS_WBUFSIZE 4096 127843e1988Sjohnlev 128843e1988Sjohnlev static boolean_t abort_charseq_recognize(uchar_t); 129843e1988Sjohnlev 130843e1988Sjohnlev /* The async interrupt entry points */ 131843e1988Sjohnlev static void xcasync_ioctl(struct asyncline *, queue_t *, mblk_t *); 132843e1988Sjohnlev static void xcasync_reioctl(void *); 133843e1988Sjohnlev static void xcasync_start(struct asyncline *); 134843e1988Sjohnlev static void xenconsputchar(cons_polledio_arg_t, uchar_t); 135843e1988Sjohnlev static int xenconsgetchar(cons_polledio_arg_t); 136843e1988Sjohnlev static boolean_t xenconsischar(cons_polledio_arg_t); 137843e1988Sjohnlev 138843e1988Sjohnlev static uint_t xenconsintr(caddr_t); 139843e1988Sjohnlev static uint_t xenconsintr_priv(caddr_t); 140843e1988Sjohnlev /*PRINTFLIKE2*/ 141843e1988Sjohnlev static void xenconserror(int, const char *, ...) __KPRINTFLIKE(2); 142843e1988Sjohnlev static void xencons_soft_state_free(struct xencons *); 143843e1988Sjohnlev static boolean_t 144843e1988Sjohnlev xcasync_flowcontrol_sw_input(struct xencons *, async_flowc_action, int); 145843e1988Sjohnlev static void 146843e1988Sjohnlev xcasync_flowcontrol_sw_output(struct xencons *, async_flowc_action); 147843e1988Sjohnlev 148843e1988Sjohnlev void *xencons_soft_state; 149843e1988Sjohnlev char *xencons_wbuf; 150843e1988Sjohnlev struct xencons *xencons_console; 151843e1988Sjohnlev 152843e1988Sjohnlev static void 153843e1988Sjohnlev xenconssetup_avintr(struct xencons *xcp, int attach) 154843e1988Sjohnlev { 155843e1988Sjohnlev /* 156843e1988Sjohnlev * On xen, CPU 0 always exists and can't be taken offline, 157843e1988Sjohnlev * so binding this thread to it should always succeed. 158843e1988Sjohnlev */ 159843e1988Sjohnlev mutex_enter(&cpu_lock); 160843e1988Sjohnlev thread_affinity_set(curthread, 0); 161843e1988Sjohnlev mutex_exit(&cpu_lock); 162843e1988Sjohnlev 163843e1988Sjohnlev if (attach) { 164843e1988Sjohnlev /* Setup our interrupt binding. */ 165843e1988Sjohnlev (void) add_avintr(NULL, IPL_CONS, (avfunc)xenconsintr_priv, 166843e1988Sjohnlev "xencons", xcp->console_irq, (caddr_t)xcp, NULL, NULL, 167843e1988Sjohnlev xcp->dip); 168843e1988Sjohnlev } else { 169843e1988Sjohnlev /* 170843e1988Sjohnlev * Cleanup interrupt configuration. Note that the framework 171843e1988Sjohnlev * _should_ ensure that when rem_avintr() returns the interrupt 172843e1988Sjohnlev * service routine is not currently executing and that it won't 173843e1988Sjohnlev * be invoked again. 174843e1988Sjohnlev */ 175843e1988Sjohnlev (void) rem_avintr(NULL, IPL_CONS, (avfunc)xenconsintr_priv, 176843e1988Sjohnlev xcp->console_irq); 177843e1988Sjohnlev } 178843e1988Sjohnlev 179843e1988Sjohnlev /* Notify our caller that we're done. */ 180843e1988Sjohnlev mutex_enter(&xcp->excl); 181843e1988Sjohnlev cv_signal(&xcp->excl_cv); 182843e1988Sjohnlev mutex_exit(&xcp->excl); 183843e1988Sjohnlev 184843e1988Sjohnlev /* Clear our binding to CPU 0 */ 185843e1988Sjohnlev thread_affinity_clear(curthread); 186843e1988Sjohnlev 187843e1988Sjohnlev } 188843e1988Sjohnlev 189843e1988Sjohnlev static void 190843e1988Sjohnlev xenconssetup_add_avintr(struct xencons *xcp) 191843e1988Sjohnlev { 192843e1988Sjohnlev xenconssetup_avintr(xcp, B_TRUE); 193843e1988Sjohnlev } 194843e1988Sjohnlev 195843e1988Sjohnlev static void 196843e1988Sjohnlev xenconssetup_rem_avintr(struct xencons *xcp) 197843e1988Sjohnlev { 198843e1988Sjohnlev xenconssetup_avintr(xcp, B_FALSE); 199843e1988Sjohnlev } 200843e1988Sjohnlev 201843e1988Sjohnlev static int 202843e1988Sjohnlev xenconsdetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 203843e1988Sjohnlev { 204843e1988Sjohnlev int instance; 205843e1988Sjohnlev struct xencons *xcp; 206843e1988Sjohnlev 207843e1988Sjohnlev if (cmd != DDI_DETACH && cmd != DDI_SUSPEND) 208843e1988Sjohnlev return (DDI_FAILURE); 209843e1988Sjohnlev 210843e1988Sjohnlev if (cmd == DDI_SUSPEND) { 211843e1988Sjohnlev ddi_remove_intr(devi, 0, NULL); 212843e1988Sjohnlev return (DDI_SUCCESS); 213843e1988Sjohnlev } 214843e1988Sjohnlev 215843e1988Sjohnlev /* 216843e1988Sjohnlev * We should never try to detach the console driver on a domU 217843e1988Sjohnlev * because it should always be held open 218843e1988Sjohnlev */ 219843e1988Sjohnlev ASSERT(DOMAIN_IS_INITDOMAIN(xen_info)); 220843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) 221843e1988Sjohnlev return (DDI_FAILURE); 222843e1988Sjohnlev 223843e1988Sjohnlev instance = ddi_get_instance(devi); /* find out which unit */ 224843e1988Sjohnlev 225843e1988Sjohnlev xcp = ddi_get_soft_state(xencons_soft_state, instance); 226843e1988Sjohnlev if (xcp == NULL) 227843e1988Sjohnlev return (DDI_FAILURE); 228843e1988Sjohnlev 229843e1988Sjohnlev /* 230843e1988Sjohnlev * Cleanup our interrupt bindings. For more info on why we 231843e1988Sjohnlev * do this in a seperate thread, see the comments for when we 232843e1988Sjohnlev * setup the interrupt bindings. 233843e1988Sjohnlev */ 234843e1988Sjohnlev xencons_console = NULL; 235843e1988Sjohnlev mutex_enter(&xcp->excl); 236843e1988Sjohnlev (void) taskq_dispatch(system_taskq, 237843e1988Sjohnlev (void (*)(void *))xenconssetup_rem_avintr, xcp, TQ_SLEEP); 238843e1988Sjohnlev cv_wait(&xcp->excl_cv, &xcp->excl); 239843e1988Sjohnlev mutex_exit(&xcp->excl); 240843e1988Sjohnlev 241843e1988Sjohnlev /* remove all minor device node(s) for this device */ 242843e1988Sjohnlev ddi_remove_minor_node(devi, NULL); 243843e1988Sjohnlev 244843e1988Sjohnlev /* free up state */ 245843e1988Sjohnlev xencons_soft_state_free(xcp); 246843e1988Sjohnlev kmem_free(xencons_wbuf, XENCONS_WBUFSIZE); 247843e1988Sjohnlev 248843e1988Sjohnlev DEBUGNOTE1(XENCONS_DEBUG_INIT, "xencons%d: shutdown complete", 249843e1988Sjohnlev instance); 250843e1988Sjohnlev return (DDI_SUCCESS); 251843e1988Sjohnlev } 252843e1988Sjohnlev 253843e1988Sjohnlev static void 254843e1988Sjohnlev xenconssetup(struct xencons *xcp) 255843e1988Sjohnlev { 256843e1988Sjohnlev xcp->ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page; 257843e1988Sjohnlev 258843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 259843e1988Sjohnlev xencons_wbuf = kmem_alloc(XENCONS_WBUFSIZE, KM_SLEEP); 260843e1988Sjohnlev 261843e1988Sjohnlev /* 262843e1988Sjohnlev * Activate the xen console virq. Note that xen requires 263843e1988Sjohnlev * that VIRQs be bound to CPU 0 when first created. 264843e1988Sjohnlev */ 265843e1988Sjohnlev xcp->console_irq = ec_bind_virq_to_irq(VIRQ_CONSOLE, 0); 266843e1988Sjohnlev 267843e1988Sjohnlev /* 268843e1988Sjohnlev * Ok. This is kinda ugly. We want to register an 269843e1988Sjohnlev * interrupt handler for the xen console virq, but 270843e1988Sjohnlev * virq's are xen sepcific and currently the DDI doesn't 271843e1988Sjohnlev * support binding to them. So instead we need to use 272843e1988Sjohnlev * add_avintr(). So to make things more complicated, 273843e1988Sjohnlev * we already had to bind the xen console VIRQ to CPU 0, 274843e1988Sjohnlev * and add_avintr() needs to be invoked on the same CPU 275843e1988Sjohnlev * where the VIRQ is bound, in this case on CPU 0. We 276843e1988Sjohnlev * could just temporarily bind ourselves to CPU 0, but 277843e1988Sjohnlev * we don't want to do that since this attach thread 278843e1988Sjohnlev * could have been invoked in a user thread context, 279843e1988Sjohnlev * in which case this thread could already have some 280843e1988Sjohnlev * pre-existing cpu binding. So to avoid changing our 281843e1988Sjohnlev * cpu binding we're going to use a taskq thread that 282843e1988Sjohnlev * will bind to CPU 0 and register our interrupts 283843e1988Sjohnlev * handler for us. 284843e1988Sjohnlev */ 285843e1988Sjohnlev mutex_enter(&xcp->excl); 286843e1988Sjohnlev (void) taskq_dispatch(system_taskq, 287843e1988Sjohnlev (void (*)(void *))xenconssetup_add_avintr, xcp, TQ_SLEEP); 288843e1988Sjohnlev cv_wait(&xcp->excl_cv, &xcp->excl); 289843e1988Sjohnlev mutex_exit(&xcp->excl); 290843e1988Sjohnlev } else { 291843e1988Sjohnlev (void) xvdi_alloc_evtchn(xcp->dip); 292551bc2a6Smrj xcp->evtchn = xvdi_get_evtchn(xcp->dip); 293843e1988Sjohnlev (void) ddi_add_intr(xcp->dip, 0, NULL, NULL, xenconsintr, 294843e1988Sjohnlev (caddr_t)xcp); 295843e1988Sjohnlev } 296843e1988Sjohnlev } 297843e1988Sjohnlev 298843e1988Sjohnlev static int 299843e1988Sjohnlev xenconsattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 300843e1988Sjohnlev { 301843e1988Sjohnlev int instance = ddi_get_instance(devi); 302843e1988Sjohnlev struct xencons *xcp; 303843e1988Sjohnlev int ret; 304843e1988Sjohnlev 305843e1988Sjohnlev /* There can be only one. */ 306843e1988Sjohnlev if (instance != 0) 307843e1988Sjohnlev return (DDI_FAILURE); 308843e1988Sjohnlev 309843e1988Sjohnlev switch (cmd) { 310843e1988Sjohnlev case DDI_RESUME: 311843e1988Sjohnlev xcp = xencons_console; 312843e1988Sjohnlev xenconssetup(xcp); 313843e1988Sjohnlev return (DDI_SUCCESS); 314843e1988Sjohnlev case DDI_ATTACH: 315843e1988Sjohnlev break; 316843e1988Sjohnlev default: 317843e1988Sjohnlev return (DDI_FAILURE); 318843e1988Sjohnlev } 319843e1988Sjohnlev 320843e1988Sjohnlev ret = ddi_soft_state_zalloc(xencons_soft_state, instance); 321843e1988Sjohnlev if (ret != DDI_SUCCESS) 322843e1988Sjohnlev return (DDI_FAILURE); 323843e1988Sjohnlev xcp = ddi_get_soft_state(xencons_soft_state, instance); 324843e1988Sjohnlev ASSERT(xcp != NULL); /* can't fail - we only just allocated it */ 325843e1988Sjohnlev 326843e1988Sjohnlev /* 327843e1988Sjohnlev * Set up the other components of the xencons structure for this port. 328843e1988Sjohnlev */ 329843e1988Sjohnlev xcp->unit = instance; 330843e1988Sjohnlev xcp->dip = devi; 331843e1988Sjohnlev 332843e1988Sjohnlev /* Fill in the polled I/O structure. */ 333843e1988Sjohnlev xcp->polledio.cons_polledio_version = CONSPOLLEDIO_V0; 334843e1988Sjohnlev xcp->polledio.cons_polledio_argument = (cons_polledio_arg_t)xcp; 335843e1988Sjohnlev xcp->polledio.cons_polledio_putchar = xenconsputchar; 336843e1988Sjohnlev xcp->polledio.cons_polledio_getchar = xenconsgetchar; 337843e1988Sjohnlev xcp->polledio.cons_polledio_ischar = xenconsischar; 338843e1988Sjohnlev xcp->polledio.cons_polledio_enter = NULL; 339843e1988Sjohnlev xcp->polledio.cons_polledio_exit = NULL; 340843e1988Sjohnlev 341843e1988Sjohnlev /* 342843e1988Sjohnlev * Initializes the asyncline structure which has TTY protocol-private 343843e1988Sjohnlev * data before enabling interrupts. 344843e1988Sjohnlev */ 345843e1988Sjohnlev xcp->priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP); 346843e1988Sjohnlev xcp->priv->async_common = xcp; 347843e1988Sjohnlev cv_init(&xcp->priv->async_flags_cv, NULL, CV_DRIVER, NULL); 348843e1988Sjohnlev 349843e1988Sjohnlev /* Initialize mutexes before accessing the interface. */ 350843e1988Sjohnlev mutex_init(&xcp->excl, NULL, MUTEX_DRIVER, NULL); 351843e1988Sjohnlev cv_init(&xcp->excl_cv, NULL, CV_DEFAULT, NULL); 352843e1988Sjohnlev 353843e1988Sjohnlev /* create minor device node for this device */ 354843e1988Sjohnlev ret = ddi_create_minor_node(devi, "xencons", S_IFCHR, instance, 355843e1988Sjohnlev DDI_NT_SERIAL, NULL); 356843e1988Sjohnlev if (ret != DDI_SUCCESS) { 357843e1988Sjohnlev ddi_remove_minor_node(devi, NULL); 358843e1988Sjohnlev xencons_soft_state_free(xcp); 359843e1988Sjohnlev return (DDI_FAILURE); 360843e1988Sjohnlev } 361843e1988Sjohnlev 362843e1988Sjohnlev ddi_report_dev(devi); 363843e1988Sjohnlev xencons_console = xcp; 364843e1988Sjohnlev xenconssetup(xcp); 365843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_INIT, "xencons%dattach: done\n", instance); 366843e1988Sjohnlev return (DDI_SUCCESS); 367843e1988Sjohnlev } 368843e1988Sjohnlev 369843e1988Sjohnlev /*ARGSUSED*/ 370843e1988Sjohnlev static int 371843e1988Sjohnlev xenconsinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 372843e1988Sjohnlev void **result) 373843e1988Sjohnlev { 374843e1988Sjohnlev dev_t dev = (dev_t)arg; 375843e1988Sjohnlev int instance, error; 376843e1988Sjohnlev struct xencons *xcp; 377843e1988Sjohnlev 378843e1988Sjohnlev instance = getminor(dev); 379843e1988Sjohnlev xcp = ddi_get_soft_state(xencons_soft_state, instance); 380843e1988Sjohnlev if (xcp == NULL) 381843e1988Sjohnlev return (DDI_FAILURE); 382843e1988Sjohnlev 383843e1988Sjohnlev switch (infocmd) { 384843e1988Sjohnlev case DDI_INFO_DEVT2DEVINFO: 385843e1988Sjohnlev if (xcp->dip == NULL) 386843e1988Sjohnlev error = DDI_FAILURE; 387843e1988Sjohnlev else { 388843e1988Sjohnlev *result = (void *) xcp->dip; 389843e1988Sjohnlev error = DDI_SUCCESS; 390843e1988Sjohnlev } 391843e1988Sjohnlev break; 392843e1988Sjohnlev case DDI_INFO_DEVT2INSTANCE: 393843e1988Sjohnlev *result = (void *)(intptr_t)instance; 394843e1988Sjohnlev error = DDI_SUCCESS; 395843e1988Sjohnlev break; 396843e1988Sjohnlev default: 397843e1988Sjohnlev error = DDI_FAILURE; 398843e1988Sjohnlev } 399843e1988Sjohnlev return (error); 400843e1988Sjohnlev } 401843e1988Sjohnlev 402843e1988Sjohnlev /* xencons_soft_state_free - local wrapper for ddi_soft_state_free(9F) */ 403843e1988Sjohnlev 404843e1988Sjohnlev static void 405843e1988Sjohnlev xencons_soft_state_free(struct xencons *xcp) 406843e1988Sjohnlev { 407843e1988Sjohnlev mutex_destroy(&xcp->excl); 408843e1988Sjohnlev cv_destroy(&xcp->excl_cv); 409843e1988Sjohnlev kmem_free(xcp->priv, sizeof (struct asyncline)); 410843e1988Sjohnlev ddi_soft_state_free(xencons_soft_state, xcp->unit); 411843e1988Sjohnlev } 412843e1988Sjohnlev 413843e1988Sjohnlev /*ARGSUSED*/ 414843e1988Sjohnlev static int 415843e1988Sjohnlev xenconsopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 416843e1988Sjohnlev { 417843e1988Sjohnlev struct xencons *xcp; 418843e1988Sjohnlev struct asyncline *async; 419843e1988Sjohnlev int unit; 420843e1988Sjohnlev 421843e1988Sjohnlev unit = getminor(*dev); 422843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dopen\n", unit); 423843e1988Sjohnlev xcp = ddi_get_soft_state(xencons_soft_state, unit); 424843e1988Sjohnlev if (xcp == NULL) 425843e1988Sjohnlev return (ENXIO); /* unit not configured */ 426843e1988Sjohnlev async = xcp->priv; 427843e1988Sjohnlev mutex_enter(&xcp->excl); 428843e1988Sjohnlev 429843e1988Sjohnlev again: 430843e1988Sjohnlev 431843e1988Sjohnlev if ((async->async_flags & ASYNC_ISOPEN) == 0) { 432843e1988Sjohnlev async->async_ttycommon.t_iflag = 0; 433843e1988Sjohnlev async->async_ttycommon.t_iocpending = NULL; 434843e1988Sjohnlev async->async_ttycommon.t_size.ws_row = 0; 435843e1988Sjohnlev async->async_ttycommon.t_size.ws_col = 0; 436843e1988Sjohnlev async->async_ttycommon.t_size.ws_xpixel = 0; 437843e1988Sjohnlev async->async_ttycommon.t_size.ws_ypixel = 0; 438843e1988Sjohnlev async->async_dev = *dev; 439843e1988Sjohnlev async->async_wbufcid = 0; 440843e1988Sjohnlev 441843e1988Sjohnlev async->async_startc = CSTART; 442843e1988Sjohnlev async->async_stopc = CSTOP; 443843e1988Sjohnlev } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 444843e1988Sjohnlev secpolicy_excl_open(cr) != 0) { 445843e1988Sjohnlev mutex_exit(&xcp->excl); 446843e1988Sjohnlev return (EBUSY); 447843e1988Sjohnlev } 448843e1988Sjohnlev 449843e1988Sjohnlev async->async_ttycommon.t_flags |= TS_SOFTCAR; 450843e1988Sjohnlev 451843e1988Sjohnlev async->async_ttycommon.t_readq = rq; 452843e1988Sjohnlev async->async_ttycommon.t_writeq = WR(rq); 453843e1988Sjohnlev rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 454843e1988Sjohnlev mutex_exit(&xcp->excl); 455843e1988Sjohnlev /* 456843e1988Sjohnlev * Caution here -- qprocson sets the pointers that are used by canput 457843e1988Sjohnlev * called by xencons_rxint. ASYNC_ISOPEN must *not* be set until those 458843e1988Sjohnlev * pointers are valid. 459843e1988Sjohnlev */ 460843e1988Sjohnlev qprocson(rq); 461843e1988Sjohnlev async->async_flags |= ASYNC_ISOPEN; 462843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_INIT, "asy%dopen: done\n", unit); 463843e1988Sjohnlev return (0); 464843e1988Sjohnlev } 465843e1988Sjohnlev 466843e1988Sjohnlev 467843e1988Sjohnlev /* 468843e1988Sjohnlev * Close routine. 469843e1988Sjohnlev */ 470843e1988Sjohnlev /*ARGSUSED*/ 471843e1988Sjohnlev static int 472843e1988Sjohnlev xenconsclose(queue_t *q, int flag, cred_t *credp) 473843e1988Sjohnlev { 474843e1988Sjohnlev struct asyncline *async; 475843e1988Sjohnlev struct xencons *xcp; 476843e1988Sjohnlev #ifdef DEBUG 477843e1988Sjohnlev int instance; 478843e1988Sjohnlev #endif 479843e1988Sjohnlev 480843e1988Sjohnlev async = (struct asyncline *)q->q_ptr; 481843e1988Sjohnlev ASSERT(async != NULL); 482843e1988Sjohnlev xcp = async->async_common; 483843e1988Sjohnlev #ifdef DEBUG 484843e1988Sjohnlev instance = xcp->unit; 485843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dclose\n", instance); 486843e1988Sjohnlev #endif 487843e1988Sjohnlev 488843e1988Sjohnlev mutex_enter(&xcp->excl); 489843e1988Sjohnlev async->async_flags |= ASYNC_CLOSING; 490843e1988Sjohnlev 491843e1988Sjohnlev async->async_ocnt = 0; 492843e1988Sjohnlev if (async->async_xmitblk != NULL) 493843e1988Sjohnlev freeb(async->async_xmitblk); 494843e1988Sjohnlev async->async_xmitblk = NULL; 495843e1988Sjohnlev 496843e1988Sjohnlev out: 497843e1988Sjohnlev ttycommon_close(&async->async_ttycommon); 498843e1988Sjohnlev 499843e1988Sjohnlev /* 500843e1988Sjohnlev * Cancel outstanding "bufcall" request. 501843e1988Sjohnlev */ 502843e1988Sjohnlev if (async->async_wbufcid != 0) { 503843e1988Sjohnlev unbufcall(async->async_wbufcid); 504843e1988Sjohnlev async->async_wbufcid = 0; 505843e1988Sjohnlev } 506843e1988Sjohnlev 507843e1988Sjohnlev /* Note that qprocsoff can't be done until after interrupts are off */ 508843e1988Sjohnlev qprocsoff(q); 509843e1988Sjohnlev q->q_ptr = WR(q)->q_ptr = NULL; 510843e1988Sjohnlev async->async_ttycommon.t_readq = NULL; 511843e1988Sjohnlev async->async_ttycommon.t_writeq = NULL; 512843e1988Sjohnlev 513843e1988Sjohnlev /* 514843e1988Sjohnlev * Clear out device state, except persistant device property flags. 515843e1988Sjohnlev */ 516843e1988Sjohnlev async->async_flags = 0; 517843e1988Sjohnlev cv_broadcast(&async->async_flags_cv); 518843e1988Sjohnlev mutex_exit(&xcp->excl); 519843e1988Sjohnlev 520843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_CLOSE, "xencons%dclose: done\n", instance); 521843e1988Sjohnlev return (0); 522843e1988Sjohnlev } 523843e1988Sjohnlev 524843e1988Sjohnlev #define INBUF_IX(ix, ifp) (DOMAIN_IS_INITDOMAIN(xen_info) ? \ 525843e1988Sjohnlev (ix) : MASK_XENCONS_IDX((ix), (ifp)->in)) 526843e1988Sjohnlev 527843e1988Sjohnlev /* 528843e1988Sjohnlev * Handle a xen console rx interrupt. 529843e1988Sjohnlev */ 530843e1988Sjohnlev /*ARGSUSED*/ 531843e1988Sjohnlev static void 532843e1988Sjohnlev xencons_rxint(struct xencons *xcp) 533843e1988Sjohnlev { 534843e1988Sjohnlev struct asyncline *async; 535843e1988Sjohnlev short cc; 536843e1988Sjohnlev mblk_t *bp; 537843e1988Sjohnlev queue_t *q; 538843e1988Sjohnlev uchar_t c, buf[16]; 539843e1988Sjohnlev uchar_t *cp; 540843e1988Sjohnlev tty_common_t *tp; 541843e1988Sjohnlev int instance; 542843e1988Sjohnlev volatile struct xencons_interface *ifp; 543843e1988Sjohnlev XENCONS_RING_IDX cons, prod; 544843e1988Sjohnlev 545843e1988Sjohnlev DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_rxint\n"); 546843e1988Sjohnlev 547843e1988Sjohnlev loop: 548843e1988Sjohnlev mutex_enter(&xcp->excl); 549843e1988Sjohnlev 550843e1988Sjohnlev /* sanity check if we should bail */ 551843e1988Sjohnlev if (xencons_console == NULL) { 552843e1988Sjohnlev mutex_exit(&xcp->excl); 553843e1988Sjohnlev goto out; 554843e1988Sjohnlev } 555843e1988Sjohnlev 556843e1988Sjohnlev async = xcp->priv; 557843e1988Sjohnlev instance = xcp->unit; 558843e1988Sjohnlev ifp = xcp->ifp; 559843e1988Sjohnlev tp = &async->async_ttycommon; 560843e1988Sjohnlev q = tp->t_readq; 561843e1988Sjohnlev 562843e1988Sjohnlev if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 563843e1988Sjohnlev xcasync_start(async); 564843e1988Sjohnlev async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 565843e1988Sjohnlev } 566843e1988Sjohnlev 567843e1988Sjohnlev /* 568843e1988Sjohnlev * If data is available, send it up the stream if there's 569843e1988Sjohnlev * somebody listening. 570843e1988Sjohnlev */ 571843e1988Sjohnlev if (!(async->async_flags & ASYNC_ISOPEN)) { 572843e1988Sjohnlev mutex_exit(&xcp->excl); 573843e1988Sjohnlev goto out; 574843e1988Sjohnlev } 575843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 576843e1988Sjohnlev cc = HYPERVISOR_console_io(CONSOLEIO_read, 16, (char *)buf); 577843e1988Sjohnlev cp = buf; 578843e1988Sjohnlev cons = 0; 579843e1988Sjohnlev } else { 580843e1988Sjohnlev cons = ifp->in_cons; 581843e1988Sjohnlev prod = ifp->in_prod; 582843e1988Sjohnlev 583843e1988Sjohnlev cc = prod - cons; 584843e1988Sjohnlev cp = (uchar_t *)ifp->in; 585843e1988Sjohnlev } 586843e1988Sjohnlev if (cc <= 0) { 587843e1988Sjohnlev mutex_exit(&xcp->excl); 588843e1988Sjohnlev goto out; 589843e1988Sjohnlev } 590843e1988Sjohnlev 591843e1988Sjohnlev /* 592843e1988Sjohnlev * Check for character break sequence. 593843e1988Sjohnlev * 594843e1988Sjohnlev * Note that normally asy drivers only check for a character sequence 595843e1988Sjohnlev * if abort_enable == KIOCABORTALTERNATE and otherwise use a break 596843e1988Sjohnlev * sensed on the line to do an abort_sequence_enter. Since the 597843e1988Sjohnlev * hypervisor does not use a real chip for the console we default to 598843e1988Sjohnlev * using the alternate sequence. 599843e1988Sjohnlev */ 600843e1988Sjohnlev if ((abort_enable == KIOCABORTENABLE) && (xcp->flags & ASY_CONSOLE)) { 601843e1988Sjohnlev XENCONS_RING_IDX i; 602843e1988Sjohnlev 603843e1988Sjohnlev for (i = 0; i < cc; i++) { 604843e1988Sjohnlev c = cp[INBUF_IX(cons + i, ifp)]; 605843e1988Sjohnlev if (abort_charseq_recognize(c)) { 606843e1988Sjohnlev /* 607843e1988Sjohnlev * Eat abort seg, it's not a valid debugger 608843e1988Sjohnlev * command. 609843e1988Sjohnlev */ 610843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 611843e1988Sjohnlev membar_producer(); 612843e1988Sjohnlev ifp->in_cons = cons + i; 613843e1988Sjohnlev } else { 614843e1988Sjohnlev cons += i; 615843e1988Sjohnlev } 616843e1988Sjohnlev abort_sequence_enter((char *)NULL); 617843e1988Sjohnlev /* 618843e1988Sjohnlev * Back from debugger, resume normal processing 619843e1988Sjohnlev */ 620843e1988Sjohnlev mutex_exit(&xcp->excl); 621843e1988Sjohnlev goto loop; 622843e1988Sjohnlev } 623843e1988Sjohnlev } 624843e1988Sjohnlev } 625843e1988Sjohnlev 626843e1988Sjohnlev if (!canput(q)) { 627843e1988Sjohnlev if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 628843e1988Sjohnlev (void) xcasync_flowcontrol_sw_input(xcp, FLOW_STOP, 629843e1988Sjohnlev IN_FLOW_STREAMS); 630843e1988Sjohnlev } 631843e1988Sjohnlev mutex_exit(&xcp->excl); 632843e1988Sjohnlev goto out; 633843e1988Sjohnlev } 634843e1988Sjohnlev if (async->async_inflow_source & IN_FLOW_STREAMS) { 635843e1988Sjohnlev (void) xcasync_flowcontrol_sw_input(xcp, FLOW_START, 636843e1988Sjohnlev IN_FLOW_STREAMS); 637843e1988Sjohnlev } 638843e1988Sjohnlev DEBUGCONT2(XENCONS_DEBUG_INPUT, 639843e1988Sjohnlev "xencons%d_rxint: %d char(s) in queue.\n", instance, cc); 640843e1988Sjohnlev if (!(bp = allocb(cc, BPRI_MED))) { 641843e1988Sjohnlev mutex_exit(&xcp->excl); 642843e1988Sjohnlev ttycommon_qfull(&async->async_ttycommon, q); 643843e1988Sjohnlev goto out; 644843e1988Sjohnlev } 645843e1988Sjohnlev do { 646843e1988Sjohnlev c = cp[INBUF_IX(cons++, ifp)]; 647843e1988Sjohnlev /* 648843e1988Sjohnlev * We handle XON/XOFF char if IXON is set, 649843e1988Sjohnlev * but if received char is _POSIX_VDISABLE, 650843e1988Sjohnlev * we left it to the up level module. 651843e1988Sjohnlev */ 652843e1988Sjohnlev if (tp->t_iflag & IXON) { 653843e1988Sjohnlev if ((c == async->async_stopc) && 654843e1988Sjohnlev (c != _POSIX_VDISABLE)) { 655843e1988Sjohnlev xcasync_flowcontrol_sw_output(xcp, FLOW_STOP); 656843e1988Sjohnlev continue; 657843e1988Sjohnlev } else if ((c == async->async_startc) && 658843e1988Sjohnlev (c != _POSIX_VDISABLE)) { 659843e1988Sjohnlev xcasync_flowcontrol_sw_output(xcp, FLOW_START); 660843e1988Sjohnlev continue; 661843e1988Sjohnlev } 662843e1988Sjohnlev if ((tp->t_iflag & IXANY) && 663843e1988Sjohnlev (async->async_flags & ASYNC_SW_OUT_FLW)) { 664843e1988Sjohnlev xcasync_flowcontrol_sw_output(xcp, FLOW_START); 665843e1988Sjohnlev } 666843e1988Sjohnlev } 667843e1988Sjohnlev *bp->b_wptr++ = c; 668843e1988Sjohnlev } while (--cc); 669843e1988Sjohnlev membar_producer(); 670843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) 671843e1988Sjohnlev ifp->in_cons = cons; 672843e1988Sjohnlev mutex_exit(&xcp->excl); 673843e1988Sjohnlev if (bp->b_wptr > bp->b_rptr) { 674843e1988Sjohnlev if (!canput(q)) { 675843e1988Sjohnlev xenconserror(CE_NOTE, "xencons%d: local queue full", 676843e1988Sjohnlev instance); 677843e1988Sjohnlev freemsg(bp); 678843e1988Sjohnlev } else 679843e1988Sjohnlev (void) putq(q, bp); 680843e1988Sjohnlev } else 681843e1988Sjohnlev freemsg(bp); 682843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) 683843e1988Sjohnlev goto loop; 684843e1988Sjohnlev out: 685843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_PROCS, "xencons%d_rxint: done\n", instance); 686843e1988Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) 687843e1988Sjohnlev ec_notify_via_evtchn(xcp->evtchn); 688843e1988Sjohnlev } 689843e1988Sjohnlev 690843e1988Sjohnlev 691843e1988Sjohnlev /* 692843e1988Sjohnlev * Handle a xen console tx interrupt. 693843e1988Sjohnlev */ 694843e1988Sjohnlev /*ARGSUSED*/ 695843e1988Sjohnlev static void 696843e1988Sjohnlev xencons_txint(struct xencons *xcp) 697843e1988Sjohnlev { 698843e1988Sjohnlev struct asyncline *async; 699843e1988Sjohnlev 700843e1988Sjohnlev DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_txint\n"); 701843e1988Sjohnlev 702843e1988Sjohnlev /* 703843e1988Sjohnlev * prevent recursive entry 704843e1988Sjohnlev */ 705843e1988Sjohnlev if (mutex_owner(&xcp->excl) == curthread) { 706843e1988Sjohnlev goto out; 707843e1988Sjohnlev } 708843e1988Sjohnlev 709843e1988Sjohnlev mutex_enter(&xcp->excl); 710843e1988Sjohnlev if (xencons_console == NULL) { 711843e1988Sjohnlev mutex_exit(&xcp->excl); 712843e1988Sjohnlev goto out; 713843e1988Sjohnlev } 714843e1988Sjohnlev 715843e1988Sjohnlev /* make sure the device is open */ 716843e1988Sjohnlev async = xcp->priv; 717843e1988Sjohnlev if ((async->async_flags & ASYNC_ISOPEN) != 0) 718843e1988Sjohnlev xcasync_start(async); 719843e1988Sjohnlev 720843e1988Sjohnlev mutex_exit(&xcp->excl); 721843e1988Sjohnlev out: 722843e1988Sjohnlev DEBUGCONT0(XENCONS_DEBUG_PROCS, "xencons_txint: done\n"); 723843e1988Sjohnlev } 724843e1988Sjohnlev 725843e1988Sjohnlev 726843e1988Sjohnlev /* 727843e1988Sjohnlev * Get an event when input ring becomes not empty or output ring becomes not 728843e1988Sjohnlev * full. 729843e1988Sjohnlev */ 730843e1988Sjohnlev static uint_t 731843e1988Sjohnlev xenconsintr(caddr_t arg) 732843e1988Sjohnlev { 733843e1988Sjohnlev struct xencons *xcp = (struct xencons *)arg; 734843e1988Sjohnlev volatile struct xencons_interface *ifp = xcp->ifp; 735843e1988Sjohnlev 736843e1988Sjohnlev if (ifp->in_prod != ifp->in_cons) 737843e1988Sjohnlev xencons_rxint(xcp); 738843e1988Sjohnlev if (ifp->out_prod - ifp->out_cons < sizeof (ifp->out)) 739843e1988Sjohnlev xencons_txint(xcp); 740843e1988Sjohnlev return (DDI_INTR_CLAIMED); 741843e1988Sjohnlev } 742843e1988Sjohnlev 743843e1988Sjohnlev /* 744843e1988Sjohnlev * Console interrupt routine for priviliged domains 745843e1988Sjohnlev */ 746843e1988Sjohnlev static uint_t 747843e1988Sjohnlev xenconsintr_priv(caddr_t arg) 748843e1988Sjohnlev { 749843e1988Sjohnlev struct xencons *xcp = (struct xencons *)arg; 750843e1988Sjohnlev 751843e1988Sjohnlev xencons_rxint(xcp); 752843e1988Sjohnlev xencons_txint(xcp); 753843e1988Sjohnlev return (DDI_INTR_CLAIMED); 754843e1988Sjohnlev } 755843e1988Sjohnlev 756843e1988Sjohnlev /* 757843e1988Sjohnlev * Start output on a line, unless it's busy, frozen, or otherwise. 758843e1988Sjohnlev */ 759843e1988Sjohnlev /*ARGSUSED*/ 760843e1988Sjohnlev static void 761843e1988Sjohnlev xcasync_start(struct asyncline *async) 762843e1988Sjohnlev { 763843e1988Sjohnlev struct xencons *xcp = async->async_common; 764843e1988Sjohnlev int cc; 765843e1988Sjohnlev queue_t *q; 766843e1988Sjohnlev mblk_t *bp; 767843e1988Sjohnlev int len, space, blen; 768843e1988Sjohnlev mblk_t *nbp; 769843e1988Sjohnlev 770843e1988Sjohnlev #ifdef DEBUG 771843e1988Sjohnlev int instance = xcp->unit; 772843e1988Sjohnlev 773843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_nstart\n", instance); 774843e1988Sjohnlev #endif 775843e1988Sjohnlev ASSERT(mutex_owned(&xcp->excl)); 776843e1988Sjohnlev 777843e1988Sjohnlev /* 778843e1988Sjohnlev * Check only pended sw input flow control. 779843e1988Sjohnlev */ 780843e1988Sjohnlev domore: 781843e1988Sjohnlev (void) xcasync_flowcontrol_sw_input(xcp, FLOW_CHECK, IN_FLOW_NULL); 782843e1988Sjohnlev 783843e1988Sjohnlev if ((q = async->async_ttycommon.t_writeq) == NULL) { 784843e1988Sjohnlev return; /* not attached to a stream */ 785843e1988Sjohnlev } 786843e1988Sjohnlev 787843e1988Sjohnlev for (;;) { 788843e1988Sjohnlev if ((bp = getq(q)) == NULL) 789843e1988Sjohnlev return; /* no data to transmit */ 790843e1988Sjohnlev 791843e1988Sjohnlev /* 792843e1988Sjohnlev * We have a message block to work on. 793843e1988Sjohnlev * Check whether it's a break, a delay, or an ioctl (the latter 794843e1988Sjohnlev * occurs if the ioctl in question was waiting for the output 795843e1988Sjohnlev * to drain). If it's one of those, process it immediately. 796843e1988Sjohnlev */ 797843e1988Sjohnlev switch (bp->b_datap->db_type) { 798843e1988Sjohnlev 799843e1988Sjohnlev case M_IOCTL: 800843e1988Sjohnlev /* 801843e1988Sjohnlev * This ioctl was waiting for the output ahead of 802843e1988Sjohnlev * it to drain; obviously, it has. Do it, and 803843e1988Sjohnlev * then grab the next message after it. 804843e1988Sjohnlev */ 805843e1988Sjohnlev mutex_exit(&xcp->excl); 806843e1988Sjohnlev xcasync_ioctl(async, q, bp); 807843e1988Sjohnlev mutex_enter(&xcp->excl); 808843e1988Sjohnlev continue; 809843e1988Sjohnlev } 810843e1988Sjohnlev 811843e1988Sjohnlev while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { 812843e1988Sjohnlev nbp = bp->b_cont; 813843e1988Sjohnlev freeb(bp); 814843e1988Sjohnlev bp = nbp; 815843e1988Sjohnlev } 816843e1988Sjohnlev if (bp != NULL) 817843e1988Sjohnlev break; 818843e1988Sjohnlev } 819843e1988Sjohnlev 820843e1988Sjohnlev /* 821843e1988Sjohnlev * We have data to transmit. If output is stopped, put 822843e1988Sjohnlev * it back and try again later. 823843e1988Sjohnlev */ 824843e1988Sjohnlev if (async->async_flags & (ASYNC_SW_OUT_FLW | ASYNC_STOPPED)) { 825843e1988Sjohnlev (void) putbq(q, bp); 826843e1988Sjohnlev return; 827843e1988Sjohnlev } 828843e1988Sjohnlev 829843e1988Sjohnlev 830843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 831843e1988Sjohnlev len = 0; 832843e1988Sjohnlev space = XENCONS_WBUFSIZE; 833843e1988Sjohnlev while (bp != NULL && space) { 834843e1988Sjohnlev blen = bp->b_wptr - bp->b_rptr; 835843e1988Sjohnlev cc = min(blen, space); 836843e1988Sjohnlev bcopy(bp->b_rptr, &xencons_wbuf[len], cc); 837843e1988Sjohnlev bp->b_rptr += cc; 838843e1988Sjohnlev if (cc == blen) { 839843e1988Sjohnlev nbp = bp->b_cont; 840843e1988Sjohnlev freeb(bp); 841843e1988Sjohnlev bp = nbp; 842843e1988Sjohnlev } 843843e1988Sjohnlev space -= cc; 844843e1988Sjohnlev len += cc; 845843e1988Sjohnlev } 846843e1988Sjohnlev mutex_exit(&xcp->excl); 847843e1988Sjohnlev (void) HYPERVISOR_console_io(CONSOLEIO_write, len, 848843e1988Sjohnlev xencons_wbuf); 849843e1988Sjohnlev mutex_enter(&xcp->excl); 850843e1988Sjohnlev if (bp != NULL) 851843e1988Sjohnlev (void) putbq(q, bp); /* not done with this msg yet */ 852843e1988Sjohnlev /* 853843e1988Sjohnlev * There are no completion interrupts when using the 854843e1988Sjohnlev * HYPERVISOR_console_io call to write console data 855843e1988Sjohnlev * so we loop here till we have sent all the data to the 856843e1988Sjohnlev * hypervisor. 857843e1988Sjohnlev */ 858843e1988Sjohnlev goto domore; 859843e1988Sjohnlev } else { 860843e1988Sjohnlev volatile struct xencons_interface *ifp = xcp->ifp; 861843e1988Sjohnlev XENCONS_RING_IDX cons, prod; 862843e1988Sjohnlev 863843e1988Sjohnlev cons = ifp->out_cons; 864843e1988Sjohnlev prod = ifp->out_prod; 865843e1988Sjohnlev membar_enter(); 866843e1988Sjohnlev while (bp != NULL && ((prod - cons) < sizeof (ifp->out))) { 867843e1988Sjohnlev ifp->out[MASK_XENCONS_IDX(prod++, ifp->out)] = 868843e1988Sjohnlev *bp->b_rptr++; 869843e1988Sjohnlev if (bp->b_rptr == bp->b_wptr) { 870843e1988Sjohnlev nbp = bp->b_cont; 871843e1988Sjohnlev freeb(bp); 872843e1988Sjohnlev bp = nbp; 873843e1988Sjohnlev } 874843e1988Sjohnlev } 875843e1988Sjohnlev membar_producer(); 876843e1988Sjohnlev ifp->out_prod = prod; 877843e1988Sjohnlev ec_notify_via_evtchn(xcp->evtchn); 878843e1988Sjohnlev if (bp != NULL) 879843e1988Sjohnlev (void) putbq(q, bp); /* not done with this msg yet */ 880843e1988Sjohnlev } 881843e1988Sjohnlev } 882843e1988Sjohnlev 883843e1988Sjohnlev 884843e1988Sjohnlev /* 885843e1988Sjohnlev * Process an "ioctl" message sent down to us. 886843e1988Sjohnlev * Note that we don't need to get any locks until we are ready to access 887843e1988Sjohnlev * the hardware. Nothing we access until then is going to be altered 888843e1988Sjohnlev * outside of the STREAMS framework, so we should be safe. 889843e1988Sjohnlev */ 890843e1988Sjohnlev static void 891843e1988Sjohnlev xcasync_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 892843e1988Sjohnlev { 893843e1988Sjohnlev struct xencons *xcp = async->async_common; 894843e1988Sjohnlev tty_common_t *tp = &async->async_ttycommon; 895843e1988Sjohnlev struct iocblk *iocp; 896843e1988Sjohnlev unsigned datasize; 897843e1988Sjohnlev int error = 0; 898843e1988Sjohnlev 899843e1988Sjohnlev #ifdef DEBUG 900843e1988Sjohnlev int instance = xcp->unit; 901843e1988Sjohnlev 902843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_ioctl\n", instance); 903843e1988Sjohnlev #endif 904843e1988Sjohnlev 905843e1988Sjohnlev if (tp->t_iocpending != NULL) { 906843e1988Sjohnlev /* 907843e1988Sjohnlev * We were holding an "ioctl" response pending the 908843e1988Sjohnlev * availability of an "mblk" to hold data to be passed up; 909843e1988Sjohnlev * another "ioctl" came through, which means that "ioctl" 910843e1988Sjohnlev * must have timed out or been aborted. 911843e1988Sjohnlev */ 912843e1988Sjohnlev freemsg(async->async_ttycommon.t_iocpending); 913843e1988Sjohnlev async->async_ttycommon.t_iocpending = NULL; 914843e1988Sjohnlev } 915843e1988Sjohnlev 916843e1988Sjohnlev iocp = (struct iocblk *)mp->b_rptr; 917843e1988Sjohnlev 918843e1988Sjohnlev /* 919843e1988Sjohnlev * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 920843e1988Sjohnlev * because this function frees up the message block (mp->b_cont) that 921843e1988Sjohnlev * contains the user location where we pass back the results. 922843e1988Sjohnlev * 923843e1988Sjohnlev * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 924843e1988Sjohnlev * zaps. We know that ttycommon_ioctl doesn't know any CONS* 925843e1988Sjohnlev * ioctls, so keep the others safe too. 926843e1988Sjohnlev */ 927843e1988Sjohnlev DEBUGCONT2(XENCONS_DEBUG_IOCTL, "async%d_ioctl: %s\n", 928843e1988Sjohnlev instance, 929843e1988Sjohnlev iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 930843e1988Sjohnlev iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 931843e1988Sjohnlev iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 932843e1988Sjohnlev iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : "other"); 933843e1988Sjohnlev 934843e1988Sjohnlev switch (iocp->ioc_cmd) { 935843e1988Sjohnlev case TIOCMGET: 936843e1988Sjohnlev case TIOCGPPS: 937843e1988Sjohnlev case TIOCSPPS: 938843e1988Sjohnlev case TIOCGPPSEV: 939843e1988Sjohnlev case CONSOPENPOLLEDIO: 940843e1988Sjohnlev case CONSCLOSEPOLLEDIO: 941843e1988Sjohnlev case CONSSETABORTENABLE: 942843e1988Sjohnlev case CONSGETABORTENABLE: 943843e1988Sjohnlev error = -1; /* Do Nothing */ 944843e1988Sjohnlev break; 945843e1988Sjohnlev default: 946843e1988Sjohnlev 947843e1988Sjohnlev /* 948843e1988Sjohnlev * The only way in which "ttycommon_ioctl" can fail is if the 949843e1988Sjohnlev * "ioctl" requires a response containing data to be returned 950843e1988Sjohnlev * to the user, and no mblk could be allocated for the data. 951843e1988Sjohnlev * No such "ioctl" alters our state. Thus, we always go ahead 952843e1988Sjohnlev * and do any state-changes the "ioctl" calls for. If we 953843e1988Sjohnlev * couldn't allocate the data, "ttycommon_ioctl" has stashed 954843e1988Sjohnlev * the "ioctl" away safely, so we just call "bufcall" to 955843e1988Sjohnlev * request that we be called back when we stand a better 956843e1988Sjohnlev * chance of allocating the data. 957843e1988Sjohnlev */ 958843e1988Sjohnlev if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 959843e1988Sjohnlev if (async->async_wbufcid) 960843e1988Sjohnlev unbufcall(async->async_wbufcid); 961843e1988Sjohnlev async->async_wbufcid = bufcall(datasize, BPRI_HI, 962843e1988Sjohnlev (void (*)(void *)) xcasync_reioctl, 963843e1988Sjohnlev (void *)(intptr_t)async->async_common->unit); 964843e1988Sjohnlev return; 965843e1988Sjohnlev } 966843e1988Sjohnlev } 967843e1988Sjohnlev 968843e1988Sjohnlev mutex_enter(&xcp->excl); 969843e1988Sjohnlev 970843e1988Sjohnlev if (error == 0) { 971843e1988Sjohnlev /* 972843e1988Sjohnlev * "ttycommon_ioctl" did most of the work; we just use the 973843e1988Sjohnlev * data it set up. 974843e1988Sjohnlev */ 975843e1988Sjohnlev switch (iocp->ioc_cmd) { 976843e1988Sjohnlev 977843e1988Sjohnlev case TCSETS: 978843e1988Sjohnlev case TCSETSF: 979843e1988Sjohnlev case TCSETSW: 980843e1988Sjohnlev case TCSETA: 981843e1988Sjohnlev case TCSETAW: 982843e1988Sjohnlev case TCSETAF: 983843e1988Sjohnlev break; 984843e1988Sjohnlev } 985843e1988Sjohnlev } else if (error < 0) { 986843e1988Sjohnlev /* 987843e1988Sjohnlev * "ttycommon_ioctl" didn't do anything; we process it here. 988843e1988Sjohnlev */ 989843e1988Sjohnlev error = 0; 990843e1988Sjohnlev switch (iocp->ioc_cmd) { 991843e1988Sjohnlev 992843e1988Sjohnlev case TCSBRK: 993843e1988Sjohnlev error = miocpullup(mp, sizeof (int)); 994843e1988Sjohnlev break; 995843e1988Sjohnlev 996843e1988Sjohnlev case TIOCSBRK: 997843e1988Sjohnlev mioc2ack(mp, NULL, 0, 0); 998843e1988Sjohnlev break; 999843e1988Sjohnlev 1000843e1988Sjohnlev case TIOCCBRK: 1001843e1988Sjohnlev mioc2ack(mp, NULL, 0, 0); 1002843e1988Sjohnlev break; 1003843e1988Sjohnlev 1004843e1988Sjohnlev case CONSOPENPOLLEDIO: 1005843e1988Sjohnlev error = miocpullup(mp, sizeof (cons_polledio_arg_t)); 1006843e1988Sjohnlev if (error != 0) 1007843e1988Sjohnlev break; 1008843e1988Sjohnlev 1009843e1988Sjohnlev *(cons_polledio_arg_t *)mp->b_cont->b_rptr = 1010843e1988Sjohnlev (cons_polledio_arg_t)&xcp->polledio; 1011843e1988Sjohnlev 1012843e1988Sjohnlev mp->b_datap->db_type = M_IOCACK; 1013843e1988Sjohnlev break; 1014843e1988Sjohnlev 1015843e1988Sjohnlev case CONSCLOSEPOLLEDIO: 1016843e1988Sjohnlev mp->b_datap->db_type = M_IOCACK; 1017843e1988Sjohnlev iocp->ioc_error = 0; 1018843e1988Sjohnlev iocp->ioc_rval = 0; 1019843e1988Sjohnlev break; 1020843e1988Sjohnlev 1021843e1988Sjohnlev case CONSSETABORTENABLE: 1022843e1988Sjohnlev error = secpolicy_console(iocp->ioc_cr); 1023843e1988Sjohnlev if (error != 0) 1024843e1988Sjohnlev break; 1025843e1988Sjohnlev 1026843e1988Sjohnlev if (iocp->ioc_count != TRANSPARENT) { 1027843e1988Sjohnlev error = EINVAL; 1028843e1988Sjohnlev break; 1029843e1988Sjohnlev } 1030843e1988Sjohnlev 1031843e1988Sjohnlev if (*(intptr_t *)mp->b_cont->b_rptr) 1032843e1988Sjohnlev xcp->flags |= ASY_CONSOLE; 1033843e1988Sjohnlev else 1034843e1988Sjohnlev xcp->flags &= ~ASY_CONSOLE; 1035843e1988Sjohnlev 1036843e1988Sjohnlev mp->b_datap->db_type = M_IOCACK; 1037843e1988Sjohnlev iocp->ioc_error = 0; 1038843e1988Sjohnlev iocp->ioc_rval = 0; 1039843e1988Sjohnlev break; 1040843e1988Sjohnlev 1041843e1988Sjohnlev case CONSGETABORTENABLE: 1042843e1988Sjohnlev /*CONSTANTCONDITION*/ 1043843e1988Sjohnlev ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 1044843e1988Sjohnlev /* 1045843e1988Sjohnlev * Store the return value right in the payload 1046843e1988Sjohnlev * we were passed. Crude. 1047843e1988Sjohnlev */ 1048843e1988Sjohnlev mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 1049843e1988Sjohnlev *(boolean_t *)mp->b_cont->b_rptr = 1050843e1988Sjohnlev (xcp->flags & ASY_CONSOLE) != 0; 1051843e1988Sjohnlev break; 1052843e1988Sjohnlev 1053843e1988Sjohnlev default: 1054843e1988Sjohnlev /* 1055843e1988Sjohnlev * If we don't understand it, it's an error. NAK it. 1056843e1988Sjohnlev */ 1057843e1988Sjohnlev error = EINVAL; 1058843e1988Sjohnlev break; 1059843e1988Sjohnlev } 1060843e1988Sjohnlev } 1061843e1988Sjohnlev if (error != 0) { 1062843e1988Sjohnlev iocp->ioc_error = error; 1063843e1988Sjohnlev mp->b_datap->db_type = M_IOCNAK; 1064843e1988Sjohnlev } 1065843e1988Sjohnlev mutex_exit(&xcp->excl); 1066843e1988Sjohnlev qreply(wq, mp); 1067843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_PROCS, "async%d_ioctl: done\n", instance); 1068843e1988Sjohnlev } 1069843e1988Sjohnlev 1070843e1988Sjohnlev static int 1071843e1988Sjohnlev xenconsrsrv(queue_t *q) 1072843e1988Sjohnlev { 1073843e1988Sjohnlev mblk_t *bp; 1074843e1988Sjohnlev 1075843e1988Sjohnlev while (canputnext(q) && (bp = getq(q))) 1076843e1988Sjohnlev putnext(q, bp); 1077843e1988Sjohnlev return (0); 1078843e1988Sjohnlev } 1079843e1988Sjohnlev 1080843e1988Sjohnlev /* 1081843e1988Sjohnlev * Put procedure for write queue. 1082843e1988Sjohnlev * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 1083843e1988Sjohnlev * set the flow control character for M_STOPI and M_STARTI messages; 1084843e1988Sjohnlev * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 1085843e1988Sjohnlev * by the start routine, and then call the start routine; discard 1086843e1988Sjohnlev * everything else. Note that this driver does not incorporate any 1087843e1988Sjohnlev * mechanism to negotiate to handle the canonicalization process. 1088843e1988Sjohnlev * It expects that these functions are handled in upper module(s), 1089843e1988Sjohnlev * as we do in ldterm. 1090843e1988Sjohnlev */ 1091843e1988Sjohnlev static int 1092843e1988Sjohnlev xenconswput(queue_t *q, mblk_t *mp) 1093843e1988Sjohnlev { 1094843e1988Sjohnlev struct asyncline *async; 1095843e1988Sjohnlev struct xencons *xcp; 1096843e1988Sjohnlev 1097843e1988Sjohnlev async = (struct asyncline *)q->q_ptr; 1098843e1988Sjohnlev xcp = async->async_common; 1099843e1988Sjohnlev 1100843e1988Sjohnlev switch (mp->b_datap->db_type) { 1101843e1988Sjohnlev 1102843e1988Sjohnlev case M_STOP: 1103843e1988Sjohnlev mutex_enter(&xcp->excl); 1104843e1988Sjohnlev async->async_flags |= ASYNC_STOPPED; 1105843e1988Sjohnlev mutex_exit(&xcp->excl); 1106843e1988Sjohnlev freemsg(mp); 1107843e1988Sjohnlev break; 1108843e1988Sjohnlev 1109843e1988Sjohnlev case M_START: 1110843e1988Sjohnlev mutex_enter(&xcp->excl); 1111843e1988Sjohnlev if (async->async_flags & ASYNC_STOPPED) { 1112843e1988Sjohnlev async->async_flags &= ~ASYNC_STOPPED; 1113843e1988Sjohnlev xcasync_start(async); 1114843e1988Sjohnlev } 1115843e1988Sjohnlev mutex_exit(&xcp->excl); 1116843e1988Sjohnlev freemsg(mp); 1117843e1988Sjohnlev break; 1118843e1988Sjohnlev 1119843e1988Sjohnlev case M_IOCTL: 1120843e1988Sjohnlev switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 1121843e1988Sjohnlev 1122843e1988Sjohnlev case TCSETSW: 1123843e1988Sjohnlev case TCSETSF: 1124843e1988Sjohnlev case TCSETAW: 1125843e1988Sjohnlev case TCSETAF: 1126843e1988Sjohnlev /* 1127843e1988Sjohnlev * The changes do not take effect until all 1128843e1988Sjohnlev * output queued before them is drained. 1129843e1988Sjohnlev * Put this message on the queue, so that 1130843e1988Sjohnlev * "xcasync_start" will see it when it's done 1131843e1988Sjohnlev * with the output before it. Poke the 1132843e1988Sjohnlev * start routine, just in case. 1133843e1988Sjohnlev */ 1134843e1988Sjohnlev (void) putq(q, mp); 1135843e1988Sjohnlev mutex_enter(&xcp->excl); 1136843e1988Sjohnlev xcasync_start(async); 1137843e1988Sjohnlev mutex_exit(&xcp->excl); 1138843e1988Sjohnlev break; 1139843e1988Sjohnlev 1140843e1988Sjohnlev default: 1141843e1988Sjohnlev /* 1142843e1988Sjohnlev * Do it now. 1143843e1988Sjohnlev */ 1144843e1988Sjohnlev xcasync_ioctl(async, q, mp); 1145843e1988Sjohnlev break; 1146843e1988Sjohnlev } 1147843e1988Sjohnlev break; 1148843e1988Sjohnlev 1149843e1988Sjohnlev case M_FLUSH: 1150843e1988Sjohnlev if (*mp->b_rptr & FLUSHW) { 1151843e1988Sjohnlev mutex_enter(&xcp->excl); 1152843e1988Sjohnlev /* 1153843e1988Sjohnlev * Flush our write queue. 1154843e1988Sjohnlev */ 1155843e1988Sjohnlev flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 1156843e1988Sjohnlev if (async->async_xmitblk != NULL) { 1157843e1988Sjohnlev freeb(async->async_xmitblk); 1158843e1988Sjohnlev async->async_xmitblk = NULL; 1159843e1988Sjohnlev } 1160843e1988Sjohnlev mutex_exit(&xcp->excl); 1161843e1988Sjohnlev *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 1162843e1988Sjohnlev } 1163843e1988Sjohnlev if (*mp->b_rptr & FLUSHR) { 1164843e1988Sjohnlev flushq(RD(q), FLUSHDATA); 1165843e1988Sjohnlev qreply(q, mp); /* give the read queues a crack at it */ 1166843e1988Sjohnlev } else { 1167843e1988Sjohnlev freemsg(mp); 1168843e1988Sjohnlev } 1169843e1988Sjohnlev 1170843e1988Sjohnlev /* 1171843e1988Sjohnlev * We must make sure we process messages that survive the 1172843e1988Sjohnlev * write-side flush. 1173843e1988Sjohnlev */ 1174843e1988Sjohnlev mutex_enter(&xcp->excl); 1175843e1988Sjohnlev xcasync_start(async); 1176843e1988Sjohnlev mutex_exit(&xcp->excl); 1177843e1988Sjohnlev break; 1178843e1988Sjohnlev 1179843e1988Sjohnlev case M_BREAK: 1180843e1988Sjohnlev case M_DELAY: 1181843e1988Sjohnlev case M_DATA: 1182843e1988Sjohnlev /* 1183843e1988Sjohnlev * Queue the message up to be transmitted, 1184843e1988Sjohnlev * and poke the start routine. 1185843e1988Sjohnlev */ 1186843e1988Sjohnlev (void) putq(q, mp); 1187843e1988Sjohnlev mutex_enter(&xcp->excl); 1188843e1988Sjohnlev xcasync_start(async); 1189843e1988Sjohnlev mutex_exit(&xcp->excl); 1190843e1988Sjohnlev break; 1191843e1988Sjohnlev 1192843e1988Sjohnlev case M_STOPI: 1193843e1988Sjohnlev mutex_enter(&xcp->excl); 1194843e1988Sjohnlev mutex_enter(&xcp->excl); 1195843e1988Sjohnlev if (!(async->async_inflow_source & IN_FLOW_USER)) { 1196843e1988Sjohnlev (void) xcasync_flowcontrol_sw_input(xcp, FLOW_STOP, 1197843e1988Sjohnlev IN_FLOW_USER); 1198843e1988Sjohnlev } 1199843e1988Sjohnlev mutex_exit(&xcp->excl); 1200843e1988Sjohnlev mutex_exit(&xcp->excl); 1201843e1988Sjohnlev freemsg(mp); 1202843e1988Sjohnlev break; 1203843e1988Sjohnlev 1204843e1988Sjohnlev case M_STARTI: 1205843e1988Sjohnlev mutex_enter(&xcp->excl); 1206843e1988Sjohnlev mutex_enter(&xcp->excl); 1207843e1988Sjohnlev if (async->async_inflow_source & IN_FLOW_USER) { 1208843e1988Sjohnlev (void) xcasync_flowcontrol_sw_input(xcp, FLOW_START, 1209843e1988Sjohnlev IN_FLOW_USER); 1210843e1988Sjohnlev } 1211843e1988Sjohnlev mutex_exit(&xcp->excl); 1212843e1988Sjohnlev mutex_exit(&xcp->excl); 1213843e1988Sjohnlev freemsg(mp); 1214843e1988Sjohnlev break; 1215843e1988Sjohnlev 1216843e1988Sjohnlev case M_CTL: 1217843e1988Sjohnlev if (MBLKL(mp) >= sizeof (struct iocblk) && 1218843e1988Sjohnlev ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 1219843e1988Sjohnlev ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; 1220843e1988Sjohnlev qreply(q, mp); 1221843e1988Sjohnlev } else { 1222843e1988Sjohnlev freemsg(mp); 1223843e1988Sjohnlev } 1224843e1988Sjohnlev break; 1225843e1988Sjohnlev 1226843e1988Sjohnlev default: 1227843e1988Sjohnlev freemsg(mp); 1228843e1988Sjohnlev break; 1229843e1988Sjohnlev } 1230843e1988Sjohnlev return (0); 1231843e1988Sjohnlev } 1232843e1988Sjohnlev 1233843e1988Sjohnlev /* 1234843e1988Sjohnlev * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 1235843e1988Sjohnlev * the buffer we need. 1236843e1988Sjohnlev */ 1237843e1988Sjohnlev static void 1238843e1988Sjohnlev xcasync_reioctl(void *unit) 1239843e1988Sjohnlev { 1240843e1988Sjohnlev int instance = (uintptr_t)unit; 1241843e1988Sjohnlev struct asyncline *async; 1242843e1988Sjohnlev struct xencons *xcp; 1243843e1988Sjohnlev queue_t *q; 1244843e1988Sjohnlev mblk_t *mp; 1245843e1988Sjohnlev 1246843e1988Sjohnlev xcp = ddi_get_soft_state(xencons_soft_state, instance); 1247843e1988Sjohnlev ASSERT(xcp != NULL); 1248843e1988Sjohnlev async = xcp->priv; 1249843e1988Sjohnlev 1250843e1988Sjohnlev /* 1251843e1988Sjohnlev * The bufcall is no longer pending. 1252843e1988Sjohnlev */ 1253843e1988Sjohnlev mutex_enter(&xcp->excl); 1254843e1988Sjohnlev async->async_wbufcid = 0; 1255843e1988Sjohnlev if ((q = async->async_ttycommon.t_writeq) == NULL) { 1256843e1988Sjohnlev mutex_exit(&xcp->excl); 1257843e1988Sjohnlev return; 1258843e1988Sjohnlev } 1259843e1988Sjohnlev if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 1260843e1988Sjohnlev /* not pending any more */ 1261843e1988Sjohnlev async->async_ttycommon.t_iocpending = NULL; 1262843e1988Sjohnlev mutex_exit(&xcp->excl); 1263843e1988Sjohnlev xcasync_ioctl(async, q, mp); 1264843e1988Sjohnlev } else 1265843e1988Sjohnlev mutex_exit(&xcp->excl); 1266843e1988Sjohnlev } 1267843e1988Sjohnlev 1268843e1988Sjohnlev 1269843e1988Sjohnlev /* 1270843e1988Sjohnlev * debugger/console support routines. 1271843e1988Sjohnlev */ 1272843e1988Sjohnlev 1273843e1988Sjohnlev /* 1274843e1988Sjohnlev * put a character out 1275843e1988Sjohnlev * Do not use interrupts. If char is LF, put out CR, LF. 1276843e1988Sjohnlev */ 1277843e1988Sjohnlev /*ARGSUSED*/ 1278843e1988Sjohnlev static void 1279843e1988Sjohnlev xenconsputchar(cons_polledio_arg_t arg, uchar_t c) 1280843e1988Sjohnlev { 1281843e1988Sjohnlev struct xencons *xcp = xencons_console; 1282843e1988Sjohnlev volatile struct xencons_interface *ifp = xcp->ifp; 1283843e1988Sjohnlev XENCONS_RING_IDX prod; 1284843e1988Sjohnlev 1285843e1988Sjohnlev if (c == '\n') 1286843e1988Sjohnlev xenconsputchar(arg, '\r'); 1287843e1988Sjohnlev 1288843e1988Sjohnlev /* 1289843e1988Sjohnlev * domain 0 can use the console I/O... 1290843e1988Sjohnlev */ 1291843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 1292843e1988Sjohnlev char buffer[1]; 1293843e1988Sjohnlev 1294843e1988Sjohnlev buffer[0] = c; 1295843e1988Sjohnlev (void) HYPERVISOR_console_io(CONSOLEIO_write, 1, buffer); 1296843e1988Sjohnlev return; 1297843e1988Sjohnlev } 1298843e1988Sjohnlev 1299843e1988Sjohnlev /* 1300843e1988Sjohnlev * domU has to go through dom0 virtual console. 1301843e1988Sjohnlev */ 1302843e1988Sjohnlev while (ifp->out_prod - ifp->out_cons == sizeof (ifp->out)) 1303843e1988Sjohnlev (void) HYPERVISOR_yield(); 1304843e1988Sjohnlev 1305843e1988Sjohnlev prod = ifp->out_prod; 1306843e1988Sjohnlev ifp->out[MASK_XENCONS_IDX(prod++, ifp->out)] = c; 1307843e1988Sjohnlev membar_producer(); 1308843e1988Sjohnlev ifp->out_prod = prod; 1309843e1988Sjohnlev ec_notify_via_evtchn(xcp->evtchn); 1310843e1988Sjohnlev } 1311843e1988Sjohnlev 1312843e1988Sjohnlev /* 1313843e1988Sjohnlev * See if there's a character available. If no character is 1314843e1988Sjohnlev * available, return 0. Run in polled mode, no interrupts. 1315843e1988Sjohnlev */ 1316843e1988Sjohnlev static boolean_t 1317843e1988Sjohnlev xenconsischar(cons_polledio_arg_t arg) 1318843e1988Sjohnlev { 1319843e1988Sjohnlev struct xencons *xcp = (struct xencons *)arg; 1320843e1988Sjohnlev volatile struct xencons_interface *ifp = xcp->ifp; 1321843e1988Sjohnlev 1322843e1988Sjohnlev if (xcp->polldix < xcp->polllen) 1323843e1988Sjohnlev return (B_TRUE); 1324843e1988Sjohnlev /* 1325843e1988Sjohnlev * domain 0 can use the console I/O... 1326843e1988Sjohnlev */ 1327843e1988Sjohnlev xcp->polldix = 0; 1328843e1988Sjohnlev xcp->polllen = 0; 1329843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 1330843e1988Sjohnlev xcp->polllen = HYPERVISOR_console_io(CONSOLEIO_read, 1, 1331843e1988Sjohnlev (char *)xcp->pollbuf); 1332843e1988Sjohnlev return (xcp->polllen != 0); 1333843e1988Sjohnlev } 1334843e1988Sjohnlev 1335843e1988Sjohnlev /* 1336843e1988Sjohnlev * domU has to go through virtual console device. 1337843e1988Sjohnlev */ 1338843e1988Sjohnlev if (ifp->in_prod != ifp->in_cons) { 1339843e1988Sjohnlev XENCONS_RING_IDX cons; 1340843e1988Sjohnlev 1341843e1988Sjohnlev cons = ifp->in_cons; 1342843e1988Sjohnlev membar_enter(); 1343843e1988Sjohnlev xcp->pollbuf[0] = ifp->in[MASK_XENCONS_IDX(cons++, ifp->in)]; 1344843e1988Sjohnlev membar_producer(); 1345843e1988Sjohnlev ifp->in_cons = cons; 1346843e1988Sjohnlev xcp->polllen = 1; 1347843e1988Sjohnlev } 1348843e1988Sjohnlev return (xcp->polllen != 0); 1349843e1988Sjohnlev } 1350843e1988Sjohnlev 1351843e1988Sjohnlev /* 1352843e1988Sjohnlev * Get a character. Run in polled mode, no interrupts. 1353843e1988Sjohnlev */ 1354843e1988Sjohnlev static int 1355843e1988Sjohnlev xenconsgetchar(cons_polledio_arg_t arg) 1356843e1988Sjohnlev { 1357843e1988Sjohnlev struct xencons *xcp = (struct xencons *)arg; 1358843e1988Sjohnlev 1359843e1988Sjohnlev ec_wait_on_evtchn(xcp->evtchn, (int (*)(void *))xenconsischar, arg); 1360843e1988Sjohnlev 1361843e1988Sjohnlev return (xcp->pollbuf[xcp->polldix++]); 1362843e1988Sjohnlev } 1363843e1988Sjohnlev 1364843e1988Sjohnlev static void 1365843e1988Sjohnlev xenconserror(int level, const char *fmt, ...) 1366843e1988Sjohnlev { 1367843e1988Sjohnlev va_list adx; 1368843e1988Sjohnlev static time_t last; 1369843e1988Sjohnlev static const char *lastfmt; 1370843e1988Sjohnlev time_t now; 1371843e1988Sjohnlev 1372843e1988Sjohnlev /* 1373843e1988Sjohnlev * Don't print the same error message too often. 1374843e1988Sjohnlev * Print the message only if we have not printed the 1375843e1988Sjohnlev * message within the last second. 1376843e1988Sjohnlev * Note: that fmt cannot be a pointer to a string 1377843e1988Sjohnlev * stored on the stack. The fmt pointer 1378843e1988Sjohnlev * must be in the data segment otherwise lastfmt would point 1379843e1988Sjohnlev * to non-sense. 1380843e1988Sjohnlev */ 1381843e1988Sjohnlev now = gethrestime_sec(); 1382843e1988Sjohnlev if (last == now && lastfmt == fmt) 1383843e1988Sjohnlev return; 1384843e1988Sjohnlev 1385843e1988Sjohnlev last = now; 1386843e1988Sjohnlev lastfmt = fmt; 1387843e1988Sjohnlev 1388843e1988Sjohnlev va_start(adx, fmt); 1389843e1988Sjohnlev vcmn_err(level, fmt, adx); 1390843e1988Sjohnlev va_end(adx); 1391843e1988Sjohnlev } 1392843e1988Sjohnlev 1393843e1988Sjohnlev 1394843e1988Sjohnlev /* 1395843e1988Sjohnlev * Check for abort character sequence 1396843e1988Sjohnlev */ 1397843e1988Sjohnlev static boolean_t 1398843e1988Sjohnlev abort_charseq_recognize(uchar_t ch) 1399843e1988Sjohnlev { 1400843e1988Sjohnlev static int state = 0; 1401843e1988Sjohnlev #define CNTRL(c) ((c)&037) 1402843e1988Sjohnlev static char sequence[] = { '\r', '~', CNTRL('b') }; 1403843e1988Sjohnlev 1404843e1988Sjohnlev if (ch == sequence[state]) { 1405843e1988Sjohnlev if (++state >= sizeof (sequence)) { 1406843e1988Sjohnlev state = 0; 1407843e1988Sjohnlev return (B_TRUE); 1408843e1988Sjohnlev } 1409843e1988Sjohnlev } else { 1410843e1988Sjohnlev state = (ch == sequence[0]) ? 1 : 0; 1411843e1988Sjohnlev } 1412843e1988Sjohnlev return (B_FALSE); 1413843e1988Sjohnlev } 1414843e1988Sjohnlev 1415843e1988Sjohnlev /* 1416843e1988Sjohnlev * Flow control functions 1417843e1988Sjohnlev */ 1418843e1988Sjohnlev 1419843e1988Sjohnlev /* 1420843e1988Sjohnlev * Software output flow control 1421843e1988Sjohnlev * This function can be executed sucessfully at any situation. 1422843e1988Sjohnlev * It does not handle HW, and just change the SW output flow control flag. 1423843e1988Sjohnlev * INPUT VALUE of onoff: 1424843e1988Sjohnlev * FLOW_START means to clear SW output flow control flag, 1425843e1988Sjohnlev * also set ASYNC_OUT_FLW_RESUME. 1426843e1988Sjohnlev * FLOW_STOP means to set SW output flow control flag, 1427843e1988Sjohnlev * also clear ASYNC_OUT_FLW_RESUME. 1428843e1988Sjohnlev */ 1429843e1988Sjohnlev static void 1430843e1988Sjohnlev xcasync_flowcontrol_sw_output(struct xencons *xcp, async_flowc_action onoff) 1431843e1988Sjohnlev { 1432843e1988Sjohnlev struct asyncline *async = xcp->priv; 1433843e1988Sjohnlev int instance = xcp->unit; 1434843e1988Sjohnlev 1435843e1988Sjohnlev ASSERT(mutex_owned(&xcp->excl)); 1436843e1988Sjohnlev 1437843e1988Sjohnlev if (!(async->async_ttycommon.t_iflag & IXON)) 1438843e1988Sjohnlev return; 1439843e1988Sjohnlev 1440843e1988Sjohnlev switch (onoff) { 1441843e1988Sjohnlev case FLOW_STOP: 1442843e1988Sjohnlev async->async_flags |= ASYNC_SW_OUT_FLW; 1443843e1988Sjohnlev async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 1444843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_SFLOW, 1445843e1988Sjohnlev "xencons%d: output sflow stop\n", instance); 1446843e1988Sjohnlev break; 1447843e1988Sjohnlev case FLOW_START: 1448843e1988Sjohnlev async->async_flags &= ~ASYNC_SW_OUT_FLW; 1449843e1988Sjohnlev async->async_flags |= ASYNC_OUT_FLW_RESUME; 1450843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_SFLOW, 1451843e1988Sjohnlev "xencons%d: output sflow start\n", instance); 1452843e1988Sjohnlev break; 1453843e1988Sjohnlev default: 1454843e1988Sjohnlev break; 1455843e1988Sjohnlev } 1456843e1988Sjohnlev } 1457843e1988Sjohnlev 1458843e1988Sjohnlev /* 1459843e1988Sjohnlev * Software input flow control 1460843e1988Sjohnlev * This function can execute software input flow control 1461843e1988Sjohnlev * INPUT VALUE of onoff: 1462843e1988Sjohnlev * FLOW_START means to send out a XON char 1463843e1988Sjohnlev * and clear SW input flow control flag. 1464843e1988Sjohnlev * FLOW_STOP means to send out a XOFF char 1465843e1988Sjohnlev * and set SW input flow control flag. 1466843e1988Sjohnlev * FLOW_CHECK means to check whether there is pending XON/XOFF 1467843e1988Sjohnlev * if it is true, send it out. 1468843e1988Sjohnlev * INPUT VALUE of type: 1469843e1988Sjohnlev * IN_FLOW_STREAMS means flow control is due to STREAMS 1470843e1988Sjohnlev * IN_FLOW_USER means flow control is due to user's commands 1471843e1988Sjohnlev * RETURN VALUE: B_FALSE means no flow control char is sent 1472843e1988Sjohnlev * B_TRUE means one flow control char is sent 1473843e1988Sjohnlev */ 1474843e1988Sjohnlev static boolean_t 1475843e1988Sjohnlev xcasync_flowcontrol_sw_input(struct xencons *xcp, async_flowc_action onoff, 1476843e1988Sjohnlev int type) 1477843e1988Sjohnlev { 1478843e1988Sjohnlev struct asyncline *async = xcp->priv; 1479843e1988Sjohnlev int instance = xcp->unit; 1480843e1988Sjohnlev int rval = B_FALSE; 1481843e1988Sjohnlev 1482843e1988Sjohnlev ASSERT(mutex_owned(&xcp->excl)); 1483843e1988Sjohnlev 1484843e1988Sjohnlev if (!(async->async_ttycommon.t_iflag & IXOFF)) 1485843e1988Sjohnlev return (rval); 1486843e1988Sjohnlev 1487843e1988Sjohnlev /* 1488843e1988Sjohnlev * If we get this far, then we know IXOFF is set. 1489843e1988Sjohnlev */ 1490843e1988Sjohnlev switch (onoff) { 1491843e1988Sjohnlev case FLOW_STOP: 1492843e1988Sjohnlev async->async_inflow_source |= type; 1493843e1988Sjohnlev 1494843e1988Sjohnlev /* 1495843e1988Sjohnlev * We'll send an XOFF character for each of up to 1496843e1988Sjohnlev * three different input flow control attempts to stop input. 1497843e1988Sjohnlev * If we already send out one XOFF, but FLOW_STOP comes again, 1498843e1988Sjohnlev * it seems that input flow control becomes more serious, 1499843e1988Sjohnlev * then send XOFF again. 1500843e1988Sjohnlev */ 1501843e1988Sjohnlev if (async->async_inflow_source & (IN_FLOW_STREAMS | 1502843e1988Sjohnlev IN_FLOW_USER)) 1503843e1988Sjohnlev async->async_flags |= ASYNC_SW_IN_FLOW | 1504843e1988Sjohnlev ASYNC_SW_IN_NEEDED; 1505843e1988Sjohnlev DEBUGCONT2(XENCONS_DEBUG_SFLOW, "xencons%d: input sflow stop, " 1506843e1988Sjohnlev "type = %x\n", instance, async->async_inflow_source); 1507843e1988Sjohnlev break; 1508843e1988Sjohnlev case FLOW_START: 1509843e1988Sjohnlev async->async_inflow_source &= ~type; 1510843e1988Sjohnlev if (async->async_inflow_source == 0) { 1511843e1988Sjohnlev async->async_flags = (async->async_flags & 1512843e1988Sjohnlev ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 1513843e1988Sjohnlev DEBUGCONT1(XENCONS_DEBUG_SFLOW, "xencons%d: " 1514843e1988Sjohnlev "input sflow start\n", instance); 1515843e1988Sjohnlev } 1516843e1988Sjohnlev break; 1517843e1988Sjohnlev default: 1518843e1988Sjohnlev break; 1519843e1988Sjohnlev } 1520843e1988Sjohnlev 1521843e1988Sjohnlev if (async->async_flags & ASYNC_SW_IN_NEEDED) { 1522843e1988Sjohnlev /* 1523843e1988Sjohnlev * If we get this far, then we know we need to send out 1524843e1988Sjohnlev * XON or XOFF char. 1525843e1988Sjohnlev */ 1526843e1988Sjohnlev char c; 1527843e1988Sjohnlev 1528843e1988Sjohnlev rval = B_TRUE; 1529843e1988Sjohnlev c = (async->async_flags & ASYNC_SW_IN_FLOW) ? 1530843e1988Sjohnlev async->async_stopc : async->async_startc; 1531843e1988Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) { 1532843e1988Sjohnlev (void) HYPERVISOR_console_io(CONSOLEIO_write, 1, &c); 1533843e1988Sjohnlev async->async_flags &= ~ASYNC_SW_IN_NEEDED; 1534843e1988Sjohnlev return (rval); 1535843e1988Sjohnlev } else { 1536843e1988Sjohnlev xenconsputchar(NULL, c); 1537843e1988Sjohnlev } 1538843e1988Sjohnlev } 1539843e1988Sjohnlev return (rval); 1540843e1988Sjohnlev } 1541843e1988Sjohnlev 1542843e1988Sjohnlev struct module_info xencons_info = { 1543843e1988Sjohnlev 0, 1544843e1988Sjohnlev "xencons", 1545843e1988Sjohnlev 0, 1546843e1988Sjohnlev INFPSZ, 1547843e1988Sjohnlev 4096, 1548843e1988Sjohnlev 128 1549843e1988Sjohnlev }; 1550843e1988Sjohnlev 1551843e1988Sjohnlev static struct qinit xencons_rint = { 1552843e1988Sjohnlev putq, 1553843e1988Sjohnlev xenconsrsrv, 1554843e1988Sjohnlev xenconsopen, 1555843e1988Sjohnlev xenconsclose, 1556843e1988Sjohnlev NULL, 1557843e1988Sjohnlev &xencons_info, 1558843e1988Sjohnlev NULL 1559843e1988Sjohnlev }; 1560843e1988Sjohnlev 1561843e1988Sjohnlev static struct qinit xencons_wint = { 1562843e1988Sjohnlev xenconswput, 1563843e1988Sjohnlev NULL, 1564843e1988Sjohnlev NULL, 1565843e1988Sjohnlev NULL, 1566843e1988Sjohnlev NULL, 1567843e1988Sjohnlev &xencons_info, 1568843e1988Sjohnlev NULL 1569843e1988Sjohnlev }; 1570843e1988Sjohnlev 1571843e1988Sjohnlev struct streamtab xencons_str_info = { 1572843e1988Sjohnlev &xencons_rint, 1573843e1988Sjohnlev &xencons_wint, 1574843e1988Sjohnlev NULL, 1575843e1988Sjohnlev NULL 1576843e1988Sjohnlev }; 1577843e1988Sjohnlev 1578843e1988Sjohnlev static struct cb_ops cb_xencons_ops = { 1579843e1988Sjohnlev nodev, /* cb_open */ 1580843e1988Sjohnlev nodev, /* cb_close */ 1581843e1988Sjohnlev nodev, /* cb_strategy */ 1582843e1988Sjohnlev nodev, /* cb_print */ 1583843e1988Sjohnlev nodev, /* cb_dump */ 1584843e1988Sjohnlev nodev, /* cb_read */ 1585843e1988Sjohnlev nodev, /* cb_write */ 1586843e1988Sjohnlev nodev, /* cb_ioctl */ 1587843e1988Sjohnlev nodev, /* cb_devmap */ 1588843e1988Sjohnlev nodev, /* cb_mmap */ 1589843e1988Sjohnlev nodev, /* cb_segmap */ 1590843e1988Sjohnlev nochpoll, /* cb_chpoll */ 1591843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */ 1592843e1988Sjohnlev &xencons_str_info, /* cb_stream */ 1593843e1988Sjohnlev D_MP /* cb_flag */ 1594843e1988Sjohnlev }; 1595843e1988Sjohnlev 1596843e1988Sjohnlev struct dev_ops xencons_ops = { 1597843e1988Sjohnlev DEVO_REV, /* devo_rev */ 1598843e1988Sjohnlev 0, /* devo_refcnt */ 1599843e1988Sjohnlev xenconsinfo, /* devo_getinfo */ 1600843e1988Sjohnlev nulldev, /* devo_identify */ 1601843e1988Sjohnlev nulldev, /* devo_probe */ 1602843e1988Sjohnlev xenconsattach, /* devo_attach */ 1603843e1988Sjohnlev xenconsdetach, /* devo_detach */ 1604843e1988Sjohnlev nodev, /* devo_reset */ 1605843e1988Sjohnlev &cb_xencons_ops, /* devo_cb_ops */ 1606*19397407SSherry Moore NULL, /* devo_bus_ops */ 1607*19397407SSherry Moore NULL, /* devo_power */ 1608*19397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 1609843e1988Sjohnlev }; 1610843e1988Sjohnlev 1611843e1988Sjohnlev static struct modldrv modldrv = { 1612843e1988Sjohnlev &mod_driverops, /* Type of module. This one is a driver */ 1613*19397407SSherry Moore "virtual console driver", 1614843e1988Sjohnlev &xencons_ops, /* driver ops */ 1615843e1988Sjohnlev }; 1616843e1988Sjohnlev 1617843e1988Sjohnlev static struct modlinkage modlinkage = { 1618843e1988Sjohnlev MODREV_1, 1619843e1988Sjohnlev (void *)&modldrv, 1620843e1988Sjohnlev NULL 1621843e1988Sjohnlev }; 1622843e1988Sjohnlev 1623843e1988Sjohnlev int 1624843e1988Sjohnlev _init(void) 1625843e1988Sjohnlev { 1626843e1988Sjohnlev int rv; 1627843e1988Sjohnlev 1628843e1988Sjohnlev if ((rv = ddi_soft_state_init(&xencons_soft_state, 1629843e1988Sjohnlev sizeof (struct xencons), 1)) != 0) 1630843e1988Sjohnlev return (rv); 1631843e1988Sjohnlev if ((rv = mod_install(&modlinkage)) != 0) { 1632843e1988Sjohnlev ddi_soft_state_fini(&xencons_soft_state); 1633843e1988Sjohnlev return (rv); 1634843e1988Sjohnlev } 1635843e1988Sjohnlev DEBUGCONT2(XENCONS_DEBUG_INIT, "%s, debug = %x\n", 1636843e1988Sjohnlev modldrv.drv_linkinfo, debug); 1637843e1988Sjohnlev return (0); 1638843e1988Sjohnlev } 1639843e1988Sjohnlev 1640843e1988Sjohnlev int 1641843e1988Sjohnlev _fini(void) 1642843e1988Sjohnlev { 1643843e1988Sjohnlev int rv; 1644843e1988Sjohnlev 1645843e1988Sjohnlev if ((rv = mod_remove(&modlinkage)) != 0) 1646843e1988Sjohnlev return (rv); 1647843e1988Sjohnlev 1648843e1988Sjohnlev ddi_soft_state_fini(&xencons_soft_state); 1649843e1988Sjohnlev return (0); 1650843e1988Sjohnlev } 1651843e1988Sjohnlev 1652843e1988Sjohnlev int 1653843e1988Sjohnlev _info(struct modinfo *modinfop) 1654843e1988Sjohnlev { 1655843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 1656843e1988Sjohnlev } 1657