evtchn_dev.c revision 843e19887f64dde75055cf8842fc4db2171eff45
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* evtchn.c
*
* Driver for receiving and demuxing event-channel signals.
*
* Copyright (c) 2004-2005, K A Fraser
* Multi-process extensions Copyright (c) 2004, Steven Smith
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <sys/hypervisor.h>
#include <sys/machsystm.h>
#include <sys/evtchn_impl.h>
#include <sys/ddi_impldefs.h>
#include <sys/smp_impldefs.h>
#include <sys/archsystm.h>
#include <sys/sysmacros.h>
#include <sys/xen_errno.h>
/* Some handy macros */
#define EVTCHNDRV_DEFAULT_NCLONES 256
#define EVTCHNDRV_INST2SOFTS(inst) \
/* Soft state data structure for evtchn driver */
struct evtsoftdata {
/* Processes wait on this queue when ring is empty. */
struct pollhead evtchn_pollhead;
/* last pid to bind to this event channel. debug aid. */
};
static void *evtchndrv_statep;
static int *evtchndrv_clone_tab;
static dev_info_t *evtchndrv_dip;
static kmutex_t evtchndrv_clone_tab_mutex;
/* Who's bound to each port? */
static kmutex_t port_user_lock;
void
{
struct evtsoftdata *ep;
int port;
/*
* This is quite gross, we had to leave the evtchn that led to this
* invocation in a global mailbox, retrieve it now.
* We do this because the interface doesn't offer us a way to pass
* a dynamic argument up through the generic interrupt service layer.
* The mailbox is safe since we either run with interrupts disabled or
* non-preemptable till we reach here.
*/
port = ec_dev_mbox;
ec_dev_mbox = 0;
/*
* Wake up reader when ring goes non-empty
*/
POLLIN | POLLRDNORM);
goto done;
}
} else {
}
}
done:
}
/* ARGSUSED */
static int
{
int rc = 0;
struct evtsoftdata *ep;
/* Whole number of ports. */
if (count == 0)
return (0);
for (;;) {
if (ep->ring_overflow) {
goto done;
}
break;
goto done;
}
goto done;
}
}
/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
sizeof (evtchn_port_t);
} else {
bytes1 = (p - c) * sizeof (evtchn_port_t);
bytes2 = 0;
}
/* Truncate chunks according to caller's maximum byte count. */
bytes2 = 0;
}
goto done;
}
done:
return (rc);
}
/* ARGSUSED */
static int
{
int rc, i;
struct evtsoftdata *ep;
/* Whole number of ports. */
if (count == 0) {
rc = 0;
goto out;
}
goto out;
for (i = 0; i < (count / sizeof (evtchn_port_t)); i++)
if ((kbuf[i] < NR_EVENT_CHANNELS) &&
flags = intr_clear();
ec_unmask_evtchn(kbuf[i]);
}
out:
return (rc);
}
static void
{
/*
* save away the PID of the last process to bind to this event channel.
* Useful for debugging.
*/
u->pid = ddi_get_pid();
flags = intr_clear();
}
static void
{
struct evtsoftdata *ep;
(void) ec_mask_evtchn(port);
/*
* It is possible the event is in transit to us.
* If it is already in the ring buffer, then a client may
* get a spurious event notification on the next read of
* of the evtchn device. Clients will need to be able to
* handle getting a spurious event notification.
*/
/*
* The event is masked and should stay so, clean it up.
*/
}
/* ARGSUSED */
static int
int *rvalp)
{
int err = 0;
struct evtsoftdata *ep;
*rvalp = 0;
switch (cmd) {
case IOCTL_EVTCHN_BIND_VIRQ: {
struct ioctl_evtchn_bind_virq bind;
break;
}
break;
break;
}
case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
struct ioctl_evtchn_bind_interdomain bind;
break;
}
break;
ec_bind_vcpu(*rvalp, 0);
break;
}
case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
struct ioctl_evtchn_bind_unbound_port bind;
break;
}
rvalp)) != 0)
break;
break;
}
case IOCTL_EVTCHN_UNBIND: {
struct ioctl_evtchn_unbind unbind;
break;
}
break;
}
break;
}
break;
}
case IOCTL_EVTCHN_NOTIFY: {
struct ioctl_evtchn_notify notify;
break;
}
} else {
}
break;
}
default:
}
return (err);
}
static int
{
struct evtsoftdata *ep;
short mask = 0;
if (ep->ring_overflow)
else
}
return (0);
}
/* ARGSUSED */
static int
{
struct evtsoftdata *ep;
return (ENXIO);
/*
* only allow open on minor = 0 - the clone device
*/
if (minor != 0)
return (ENXIO);
/*
* find a free slot and grab it
*/
if (evtchndrv_clone_tab[minor] == 0) {
break;
}
}
if (minor == evtchndrv_nclones)
return (EAGAIN);
/* Allocate softstate structure */
evtchndrv_clone_tab[minor] = 0;
return (EAGAIN);
}
/* ... and init it */
/* clone driver */
return (0);
}
/* ARGSUSED */
static int
{
struct evtsoftdata *ep;
int i;
return (ENXIO);
for (i = 0; i < NR_EVENT_CHANNELS; i++) {
continue;
}
/*
* free clone tab slot
*/
evtchndrv_clone_tab[minor] = 0;
return (0);
}
/* ARGSUSED */
static int
{
int retval;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
} else {
*result = (void *)evtchndrv_dip;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
break;
default:
}
return (retval);
}
static int
{
int error;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* DDI_ATTACH */
/*
* only one instance - but we clone using the open routine
*/
if (ddi_get_instance(dip) > 0)
return (DDI_FAILURE);
NULL);
DDI_PSEUDO, NULL);
if (error != DDI_SUCCESS)
goto fail;
/*
* save dip for getinfo
*/
evtchndrv_dip = dip;
return (DDI_SUCCESS);
fail:
return (error);
}
/*ARGSUSED*/
static int
{
/*
* Don't allow detach for now.
*/
return (DDI_FAILURE);
}
/* Solaris driver framework */
static struct cb_ops evtchndrv_cb_ops = {
evtchndrv_open, /* cb_open */
evtchndrv_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
evtchndrv_read, /* cb_read */
evtchndrv_write, /* cb_write */
evtchndrv_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
evtchndrv_poll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
0, /* cb_stream */
};
static struct dev_ops evtchndrv_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
evtchndrv_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
evtchndrv_attach, /* devo_attach */
evtchndrv_detach, /* devo_detach */
nodev, /* devo_reset */
&evtchndrv_cb_ops, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL /* power */
};
&mod_driverops, /* Type of module. This one is a driver */
"Evtchn driver v1.0", /* Name of the module. */
&evtchndrv_dev_ops /* driver ops */
};
static struct modlinkage modlinkage = {
&modldrv,
};
int
_init(void)
{
int err;
sizeof (struct evtsoftdata), 1);
if (err)
return (err);
if (err)
else
sizeof (int) * evtchndrv_nclones, KM_SLEEP);
return (err);
}
int
_fini(void)
{
int e;
e = mod_remove(&modlinkage);
if (e)
return (e);
return (0);
}
int
{
}