/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* Copyright (c) 2004 Christian Limpach.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. This section intentionally left blank.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Section 3 of the above license was updated in response to bug 6379571.
*/
/*
* Hypervisor virtual console driver
*/
#ifdef DEBUG
#endif
#include "xencons.h"
#include <sys/hypervisor.h>
#include <sys/evtchn_impl.h>
#ifdef DEBUG
static int debug = 0;
#else
#endif
/* The async interrupt entry points */
static void xcasync_reioctl(void *);
static void xcasync_start(struct asyncline *);
static int xenconsgetchar(cons_polledio_arg_t);
/*PRINTFLIKE2*/
static void xencons_soft_state_free(struct xencons *);
static boolean_t
static void
void *xencons_soft_state;
char *xencons_wbuf;
static void
{
/*
* On xen, CPU 0 always exists and can't be taken offline,
* so binding this thread to it should always succeed.
*/
if (attach) {
/* Setup our interrupt binding. */
} else {
/*
* Cleanup interrupt configuration. Note that the framework
* _should_ ensure that when rem_avintr() returns the interrupt
* service routine is not currently executing and that it won't
* be invoked again.
*/
xcp->console_irq);
}
/* Notify our caller that we're done. */
/* Clear our binding to CPU 0 */
}
static void
{
}
static void
{
}
static int
{
int instance;
return (DDI_FAILURE);
if (cmd == DDI_SUSPEND) {
return (DDI_SUCCESS);
}
/*
* We should never try to detach the console driver on a domU
* because it should always be held open
*/
if (!DOMAIN_IS_INITDOMAIN(xen_info))
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* Cleanup our interrupt bindings. For more info on why we
* do this in a seperate thread, see the comments for when we
* setup the interrupt bindings.
*/
(void) taskq_dispatch(system_taskq,
/* remove all minor device node(s) for this device */
/* free up state */
instance);
return (DDI_SUCCESS);
}
static void
{
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
/*
* Activate the xen console virq. Note that xen requires
* that VIRQs be bound to CPU 0 when first created.
*/
/*
* Ok. This is kinda ugly. We want to register an
* interrupt handler for the xen console virq, but
* virq's are xen sepcific and currently the DDI doesn't
* support binding to them. So instead we need to use
* add_avintr(). So to make things more complicated,
* we already had to bind the xen console VIRQ to CPU 0,
* and add_avintr() needs to be invoked on the same CPU
* where the VIRQ is bound, in this case on CPU 0. We
* could just temporarily bind ourselves to CPU 0, but
* we don't want to do that since this attach thread
* could have been invoked in a user thread context,
* in which case this thread could already have some
* pre-existing cpu binding. So to avoid changing our
* cpu binding we're going to use a taskq thread that
* will bind to CPU 0 and register our interrupts
* handler for us.
*/
(void) taskq_dispatch(system_taskq,
} else {
}
}
static int
{
int ret;
/* There can be only one. */
if (instance != 0)
return (DDI_FAILURE);
switch (cmd) {
case DDI_RESUME:
return (DDI_SUCCESS);
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
if (ret != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* Set up the other components of the xencons structure for this port.
*/
/* Fill in the polled I/O structure. */
/*
* Initializes the asyncline structure which has TTY protocol-private
* data before enabling interrupts.
*/
/* Initialize mutexes before accessing the interface. */
/* create minor device node for this device */
if (ret != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
void **result)
{
return (DDI_FAILURE);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/* xencons_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
static void
{
}
/*ARGSUSED*/
static int
{
int unit;
return (ENXIO); /* unit not configured */
async->async_wbufcid = 0;
secpolicy_excl_open(cr) != 0) {
return (EBUSY);
}
/*
* Caution here -- qprocson sets the pointers that are used by canput
* called by xencons_rxint. ASYNC_ISOPEN must *not* be set until those
* pointers are valid.
*/
return (0);
}
/*
* Close routine.
*/
/*ARGSUSED*/
static int
{
#ifdef DEBUG
int instance;
#endif
#ifdef DEBUG
#endif
async->async_ocnt = 0;
out:
/*
* Cancel outstanding "bufcall" request.
*/
if (async->async_wbufcid != 0) {
async->async_wbufcid = 0;
}
/* Note that qprocsoff can't be done until after interrupts are off */
qprocsoff(q);
/*
* Clear out device state, except persistant device property flags.
*/
async->async_flags = 0;
return (0);
}
/*
* Handle a xen console rx interrupt.
*/
/*ARGSUSED*/
static void
{
short cc;
queue_t *q;
int instance;
loop:
/* sanity check if we should bail */
if (xencons_console == NULL) {
goto out;
}
}
/*
* If data is available, send it up the stream if there's
* somebody listening.
*/
goto out;
}
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
cons = 0;
} else {
}
if (cc <= 0) {
goto out;
}
/*
* Check for character break sequence.
*
* Note that normally asy drivers only check for a character sequence
* if abort_enable == KIOCABORTALTERNATE and otherwise use a break
* sensed on the line to do an abort_sequence_enter. Since the
* hypervisor does not use a real chip for the console we default to
* using the alternate sequence.
*/
for (i = 0; i < cc; i++) {
if (abort_charseq_recognize(c)) {
/*
* Eat abort seg, it's not a valid debugger
* command.
*/
if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
} else {
cons += i;
}
abort_sequence_enter((char *)NULL);
/*
* Back from debugger, resume normal processing
*/
goto loop;
}
}
}
if (!canput(q)) {
}
goto out;
}
}
goto out;
}
do {
/*
* but if received char is _POSIX_VDISABLE,
* we left it to the up level module.
*/
if ((c == async->async_stopc) &&
(c != _POSIX_VDISABLE)) {
continue;
} else if ((c == async->async_startc) &&
(c != _POSIX_VDISABLE)) {
continue;
}
}
}
} while (--cc);
if (!DOMAIN_IS_INITDOMAIN(xen_info))
if (!canput(q)) {
instance);
} else
} else
if (DOMAIN_IS_INITDOMAIN(xen_info))
goto loop;
out:
if (!DOMAIN_IS_INITDOMAIN(xen_info))
}
/*
* Handle a xen console tx interrupt.
*/
/*ARGSUSED*/
static void
{
/*
* prevent recursive entry
*/
goto out;
}
if (xencons_console == NULL) {
goto out;
}
/* make sure the device is open */
out:
}
/*
* Get an event when input ring becomes not empty or output ring becomes not
* full.
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* Console interrupt routine for priviliged domains
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* Start output on a line, unless it's busy, frozen, or otherwise.
*/
/*ARGSUSED*/
static void
{
int cc;
queue_t *q;
#ifdef DEBUG
#endif
/*
* Check only pended sw input flow control.
*/
return; /* not attached to a stream */
}
for (;;) {
return; /* no data to transmit */
/*
* We have a message block to work on.
* Check whether it's a break, a delay, or an ioctl (the latter
* occurs if the ioctl in question was waiting for the output
* to drain). If it's one of those, process it immediately.
*/
case M_IOCTL:
/*
* This ioctl was waiting for the output ahead of
* it to drain; obviously, it has. Do it, and
* then grab the next message after it.
*/
continue;
}
}
break;
}
/*
* We have data to transmit. If output is stopped, put
* it back and try again later.
*/
return;
}
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
len = 0;
}
}
/*
* There are no completion interrupts when using the
* HYPERVISOR_console_io call to write console data
* so we loop here till we have sent all the data to the
* hypervisor.
*/
goto domore;
} else {
membar_enter();
}
}
}
}
/*
* Process an "ioctl" message sent down to us.
* Note that we don't need to get any locks until we are ready to access
* the hardware. Nothing we access until then is going to be altered
* outside of the STREAMS framework, so we should be safe.
*/
static void
{
unsigned datasize;
int error = 0;
#ifdef DEBUG
#endif
/*
* We were holding an "ioctl" response pending the
* availability of an "mblk" to hold data to be passed up;
* another "ioctl" came through, which means that "ioctl"
* must have timed out or been aborted.
*/
}
/*
* For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
* because this function frees up the message block (mp->b_cont) that
* contains the user location where we pass back the results.
*
* Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
* zaps. We know that ttycommon_ioctl doesn't know any CONS*
* ioctls, so keep the others safe too.
*/
case TIOCMGET:
case TIOCGPPS:
case TIOCSPPS:
case TIOCGPPSEV:
case CONSOPENPOLLEDIO:
case CONSCLOSEPOLLEDIO:
case CONSSETABORTENABLE:
case CONSGETABORTENABLE:
break;
default:
/*
* The only way in which "ttycommon_ioctl" can fail is if the
* "ioctl" requires a response containing data to be returned
* to the user, and no mblk could be allocated for the data.
* No such "ioctl" alters our state. Thus, we always go ahead
* and do any state-changes the "ioctl" calls for. If we
* couldn't allocate the data, "ttycommon_ioctl" has stashed
* the "ioctl" away safely, so we just call "bufcall" to
* request that we be called back when we stand a better
* chance of allocating the data.
*/
if (async->async_wbufcid)
(void (*)(void *)) xcasync_reioctl,
return;
}
}
if (error == 0) {
/*
* "ttycommon_ioctl" did most of the work; we just use the
* data it set up.
*/
case TCSETS:
case TCSETSF:
case TCSETSW:
case TCSETA:
case TCSETAW:
case TCSETAF:
break;
}
} else if (error < 0) {
/*
* "ttycommon_ioctl" didn't do anything; we process it here.
*/
error = 0;
case TCSBRK:
break;
case TIOCSBRK:
break;
case TIOCCBRK:
break;
case CONSOPENPOLLEDIO:
if (error != 0)
break;
break;
case CONSCLOSEPOLLEDIO:
break;
case CONSSETABORTENABLE:
if (error != 0)
break;
break;
}
else
break;
case CONSGETABORTENABLE:
/*CONSTANTCONDITION*/
/*
* Store the return value right in the payload
* we were passed. Crude.
*/
break;
default:
/*
* If we don't understand it, it's an error. NAK it.
*/
break;
}
}
if (error != 0) {
}
}
static int
{
return (0);
}
/*
* Put procedure for write queue.
* Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
* set the flow control character for M_STOPI and M_STARTI messages;
* queue up M_BREAK, M_DELAY, and M_DATA messages for processing
* by the start routine, and then call the start routine; discard
* everything else. Note that this driver does not incorporate any
* mechanism to negotiate to handle the canonicalization process.
* It expects that these functions are handled in upper module(s),
* as we do in ldterm.
*/
static int
{
case M_STOP:
break;
case M_START:
}
break;
case M_IOCTL:
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
/*
* The changes do not take effect until all
* output queued before them is drained.
* Put this message on the queue, so that
* "xcasync_start" will see it when it's done
* with the output before it. Poke the
* start routine, just in case.
*/
break;
default:
/*
* Do it now.
*/
break;
}
break;
case M_FLUSH:
/*
* Flush our write queue.
*/
}
}
} else {
}
/*
* We must make sure we process messages that survive the
* write-side flush.
*/
break;
case M_BREAK:
case M_DELAY:
case M_DATA:
/*
* Queue the message up to be transmitted,
* and poke the start routine.
*/
break;
case M_STOPI:
}
break;
case M_STARTI:
}
break;
case M_CTL:
} else {
}
break;
default:
break;
}
return (0);
}
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
*/
static void
{
queue_t *q;
/*
* The bufcall is no longer pending.
*/
async->async_wbufcid = 0;
return;
}
/* not pending any more */
} else
}
/*
*/
/*
* put a character out
* Do not use interrupts. If char is LF, put out CR, LF.
*/
/*ARGSUSED*/
static void
{
if (c == '\n')
/*
* domain 0 can use the console I/O...
*/
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
buffer[0] = c;
return;
}
/*
* domU has to go through dom0 virtual console.
*/
(void) HYPERVISOR_yield();
}
/*
* See if there's a character available. If no character is
* available, return 0. Run in polled mode, no interrupts.
*/
static boolean_t
{
return (B_TRUE);
/*
* domain 0 can use the console I/O...
*/
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
}
/*
* domU has to go through virtual console device.
*/
membar_enter();
}
}
/*
* Get a character. Run in polled mode, no interrupts.
*/
static int
{
}
static void
{
static const char *lastfmt;
/*
* Don't print the same error message too often.
* Print the message only if we have not printed the
* message within the last second.
* Note: that fmt cannot be a pointer to a string
* stored on the stack. The fmt pointer
* must be in the data segment otherwise lastfmt would point
* to non-sense.
*/
now = gethrestime_sec();
return;
}
/*
* Check for abort character sequence
*/
static boolean_t
{
static int state = 0;
state = 0;
return (B_TRUE);
}
} else {
}
return (B_FALSE);
}
/*
* Flow control functions
*/
/*
* Software output flow control
* This function can be executed sucessfully at any situation.
* It does not handle HW, and just change the SW output flow control flag.
* INPUT VALUE of onoff:
* FLOW_START means to clear SW output flow control flag,
* also set ASYNC_OUT_FLW_RESUME.
* FLOW_STOP means to set SW output flow control flag,
* also clear ASYNC_OUT_FLW_RESUME.
*/
static void
{
return;
switch (onoff) {
case FLOW_STOP:
"xencons%d: output sflow stop\n", instance);
break;
case FLOW_START:
"xencons%d: output sflow start\n", instance);
break;
default:
break;
}
}
/*
* Software input flow control
* This function can execute software input flow control
* INPUT VALUE of onoff:
* FLOW_START means to send out a XON char
* and clear SW input flow control flag.
* FLOW_STOP means to send out a XOFF char
* and set SW input flow control flag.
* if it is true, send it out.
* INPUT VALUE of type:
* IN_FLOW_STREAMS means flow control is due to STREAMS
* IN_FLOW_USER means flow control is due to user's commands
* RETURN VALUE: B_FALSE means no flow control char is sent
* B_TRUE means one flow control char is sent
*/
static boolean_t
int type)
{
return (rval);
/*
* If we get this far, then we know IXOFF is set.
*/
switch (onoff) {
case FLOW_STOP:
/*
* We'll send an XOFF character for each of up to
* three different input flow control attempts to stop input.
* If we already send out one XOFF, but FLOW_STOP comes again,
* it seems that input flow control becomes more serious,
* then send XOFF again.
*/
break;
case FLOW_START:
if (async->async_inflow_source == 0) {
"input sflow start\n", instance);
}
break;
default:
break;
}
/*
* If we get this far, then we know we need to send out
* XON or XOFF char.
*/
char c;
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
return (rval);
} else {
xenconsputchar(NULL, c);
}
}
return (rval);
}
0,
"xencons",
0,
4096,
128
};
putq,
NULL,
};
NULL,
NULL,
NULL,
NULL,
};
NULL,
};
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&xencons_str_info, /* cb_stream */
D_MP /* cb_flag */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
xenconsinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
xenconsattach, /* devo_attach */
xenconsdetach, /* devo_detach */
nodev, /* devo_reset */
&cb_xencons_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
&mod_driverops, /* Type of module. This one is a driver */
"virtual console driver",
&xencons_ops, /* driver ops */
};
(void *)&modldrv,
};
int
_init(void)
{
int rv;
sizeof (struct xencons), 1)) != 0)
return (rv);
return (rv);
}
return (0);
}
int
_fini(void)
{
int rv;
return (rv);
return (0);
}
int
{
}