xref: /onnv-gate/usr/src/uts/common/xen/io/evtchn_dev.c (revision 10175:dd9708d1f561)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
23*10175SStuart.Maybee@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev 
285084Sjohnlev /*
295084Sjohnlev  * evtchn.c
305084Sjohnlev  *
315084Sjohnlev  * Driver for receiving and demuxing event-channel signals.
325084Sjohnlev  *
335084Sjohnlev  * Copyright (c) 2004-2005, K A Fraser
345084Sjohnlev  * Multi-process extensions Copyright (c) 2004, Steven Smith
355084Sjohnlev  *
365084Sjohnlev  * This file may be distributed separately from the Linux kernel, or
375084Sjohnlev  * incorporated into other software packages, subject to the following license:
385084Sjohnlev  *
395084Sjohnlev  * Permission is hereby granted, free of charge, to any person obtaining a copy
405084Sjohnlev  * of this source file (the "Software"), to deal in the Software without
415084Sjohnlev  * restriction, including without limitation the rights to use, copy, modify,
425084Sjohnlev  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
435084Sjohnlev  * and to permit persons to whom the Software is furnished to do so, subject to
445084Sjohnlev  * the following conditions:
455084Sjohnlev  *
465084Sjohnlev  * The above copyright notice and this permission notice shall be included in
475084Sjohnlev  * all copies or substantial portions of the Software.
485084Sjohnlev  *
495084Sjohnlev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
505084Sjohnlev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
515084Sjohnlev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
525084Sjohnlev  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
535084Sjohnlev  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
545084Sjohnlev  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
555084Sjohnlev  * IN THE SOFTWARE.
565084Sjohnlev  */
575084Sjohnlev 
585084Sjohnlev #include <sys/types.h>
595084Sjohnlev #include <sys/hypervisor.h>
605084Sjohnlev #include <sys/machsystm.h>
615084Sjohnlev #include <sys/mutex.h>
625084Sjohnlev #include <sys/evtchn_impl.h>
635084Sjohnlev #include <sys/ddi_impldefs.h>
645084Sjohnlev #include <sys/avintr.h>
655084Sjohnlev #include <sys/cpuvar.h>
665084Sjohnlev #include <sys/smp_impldefs.h>
675084Sjohnlev #include <sys/archsystm.h>
685084Sjohnlev #include <sys/sysmacros.h>
695084Sjohnlev #include <sys/fcntl.h>
705084Sjohnlev #include <sys/open.h>
715084Sjohnlev #include <sys/stat.h>
725084Sjohnlev #include <sys/psm.h>
735084Sjohnlev #include <sys/cpu.h>
745084Sjohnlev #include <sys/cmn_err.h>
755084Sjohnlev #include <sys/xen_errno.h>
766784Sjohnlev #include <sys/policy.h>
775084Sjohnlev #include <xen/sys/evtchn.h>
785084Sjohnlev 
795084Sjohnlev /* Some handy macros */
805084Sjohnlev #define	EVTCHNDRV_MINOR2INST(minor)	((int)(minor))
815084Sjohnlev #define	EVTCHNDRV_DEFAULT_NCLONES 	256
825084Sjohnlev #define	EVTCHNDRV_INST2SOFTS(inst)	\
835084Sjohnlev 	(ddi_get_soft_state(evtchndrv_statep, (inst)))
845084Sjohnlev 
855084Sjohnlev /* Soft state data structure for evtchn driver */
865084Sjohnlev struct evtsoftdata {
875084Sjohnlev 	dev_info_t *dip;
885084Sjohnlev 	/* Notification ring, accessed via /dev/xen/evtchn. */
895084Sjohnlev #define	EVTCHN_RING_SIZE	(PAGESIZE / sizeof (evtchn_port_t))
905084Sjohnlev #define	EVTCHN_RING_MASK(_i)	((_i) & (EVTCHN_RING_SIZE - 1))
915084Sjohnlev 	evtchn_port_t *ring;
925084Sjohnlev 	unsigned int ring_cons, ring_prod, ring_overflow;
935084Sjohnlev 
94*10175SStuart.Maybee@Sun.COM 	kcondvar_t evtchn_wait; /* Processes wait on this when ring is empty. */
955084Sjohnlev 	kmutex_t evtchn_lock;
965084Sjohnlev 	struct pollhead evtchn_pollhead;
975084Sjohnlev 
98*10175SStuart.Maybee@Sun.COM 	pid_t pid;		/* last pid to bind to this event channel. */
99*10175SStuart.Maybee@Sun.COM 	processorid_t cpu;	/* cpu thread/evtchn is bound to */
1005084Sjohnlev };
1015084Sjohnlev 
1025084Sjohnlev static void *evtchndrv_statep;
1035084Sjohnlev int evtchndrv_nclones = EVTCHNDRV_DEFAULT_NCLONES;
1045084Sjohnlev static int *evtchndrv_clone_tab;
1055084Sjohnlev static dev_info_t *evtchndrv_dip;
1065084Sjohnlev static kmutex_t evtchndrv_clone_tab_mutex;
1075084Sjohnlev 
1085084Sjohnlev static int evtchndrv_detach(dev_info_t *, ddi_detach_cmd_t);
1095084Sjohnlev 
1105084Sjohnlev /* Who's bound to each port? */
1115084Sjohnlev static struct evtsoftdata *port_user[NR_EVENT_CHANNELS];
1125084Sjohnlev static kmutex_t port_user_lock;
1135084Sjohnlev 
1145084Sjohnlev void
evtchn_device_upcall()1155084Sjohnlev evtchn_device_upcall()
1165084Sjohnlev {
1175084Sjohnlev 	struct evtsoftdata *ep;
1185084Sjohnlev 	int port;
1195084Sjohnlev 
1205084Sjohnlev 	/*
1215084Sjohnlev 	 * This is quite gross, we had to leave the evtchn that led to this
122*10175SStuart.Maybee@Sun.COM 	 * invocation in a per-cpu mailbox, retrieve it now.
1235084Sjohnlev 	 * We do this because the interface doesn't offer us a way to pass
1245084Sjohnlev 	 * a dynamic argument up through the generic interrupt service layer.
1255084Sjohnlev 	 * The mailbox is safe since we either run with interrupts disabled or
1265084Sjohnlev 	 * non-preemptable till we reach here.
1275084Sjohnlev 	 */
128*10175SStuart.Maybee@Sun.COM 	port = CPU->cpu_m.mcpu_ec_mbox;
1295084Sjohnlev 	ASSERT(port != 0);
130*10175SStuart.Maybee@Sun.COM 	CPU->cpu_m.mcpu_ec_mbox = 0;
1315084Sjohnlev 	ec_clear_evtchn(port);
1325084Sjohnlev 	mutex_enter(&port_user_lock);
1335084Sjohnlev 
1345084Sjohnlev 	if ((ep = port_user[port]) != NULL) {
1355084Sjohnlev 		mutex_enter(&ep->evtchn_lock);
1365084Sjohnlev 		if ((ep->ring_prod - ep->ring_cons) < EVTCHN_RING_SIZE) {
1375084Sjohnlev 			ep->ring[EVTCHN_RING_MASK(ep->ring_prod)] = port;
1385084Sjohnlev 			/*
1395084Sjohnlev 			 * Wake up reader when ring goes non-empty
1405084Sjohnlev 			 */
1415084Sjohnlev 			if (ep->ring_cons == ep->ring_prod++) {
1425084Sjohnlev 				cv_signal(&ep->evtchn_wait);
1435084Sjohnlev 				mutex_exit(&ep->evtchn_lock);
1445084Sjohnlev 				pollwakeup(&ep->evtchn_pollhead,
1455084Sjohnlev 				    POLLIN | POLLRDNORM);
1465084Sjohnlev 				goto done;
1475084Sjohnlev 			}
1485084Sjohnlev 		} else {
1495084Sjohnlev 			ep->ring_overflow = 1;
1505084Sjohnlev 		}
1515084Sjohnlev 		mutex_exit(&ep->evtchn_lock);
1525084Sjohnlev 	}
1535084Sjohnlev 
1545084Sjohnlev done:
1555084Sjohnlev 	mutex_exit(&port_user_lock);
1565084Sjohnlev }
1575084Sjohnlev 
1585084Sjohnlev /* ARGSUSED */
1595084Sjohnlev static int
evtchndrv_read(dev_t dev,struct uio * uio,cred_t * cr)1606784Sjohnlev evtchndrv_read(dev_t dev, struct uio *uio, cred_t *cr)
1615084Sjohnlev {
1625084Sjohnlev 	int rc = 0;
1635084Sjohnlev 	ssize_t count;
1645084Sjohnlev 	unsigned int c, p, bytes1 = 0, bytes2 = 0;
1655084Sjohnlev 	struct evtsoftdata *ep;
1665084Sjohnlev 	minor_t minor = getminor(dev);
1675084Sjohnlev 
1686784Sjohnlev 	if (secpolicy_xvm_control(cr))
1696784Sjohnlev 		return (EPERM);
1706784Sjohnlev 
1715084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
1725084Sjohnlev 
1735084Sjohnlev 	/* Whole number of ports. */
1745084Sjohnlev 	count = uio->uio_resid;
1755084Sjohnlev 	count &= ~(sizeof (evtchn_port_t) - 1);
1765084Sjohnlev 
1775084Sjohnlev 	if (count == 0)
1785084Sjohnlev 		return (0);
1795084Sjohnlev 
1805084Sjohnlev 	if (count > PAGESIZE)
1815084Sjohnlev 		count = PAGESIZE;
1825084Sjohnlev 
1835084Sjohnlev 	mutex_enter(&ep->evtchn_lock);
1845084Sjohnlev 	for (;;) {
1855084Sjohnlev 		if (ep->ring_overflow) {
1865084Sjohnlev 			rc = EFBIG;
1875084Sjohnlev 			goto done;
1885084Sjohnlev 		}
1895084Sjohnlev 
1905084Sjohnlev 		if ((c = ep->ring_cons) != (p = ep->ring_prod))
1915084Sjohnlev 			break;
1925084Sjohnlev 
1935084Sjohnlev 		if (uio->uio_fmode & O_NONBLOCK) {
1945084Sjohnlev 			rc = EAGAIN;
1955084Sjohnlev 			goto done;
1965084Sjohnlev 		}
1975084Sjohnlev 
1985084Sjohnlev 		if (cv_wait_sig(&ep->evtchn_wait, &ep->evtchn_lock) == 0) {
1995084Sjohnlev 			rc = EINTR;
2005084Sjohnlev 			goto done;
2015084Sjohnlev 		}
2025084Sjohnlev 	}
2035084Sjohnlev 
2045084Sjohnlev 	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
2055084Sjohnlev 	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
2065084Sjohnlev 		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
2075084Sjohnlev 		    sizeof (evtchn_port_t);
2085084Sjohnlev 		bytes2 = EVTCHN_RING_MASK(p) * sizeof (evtchn_port_t);
2095084Sjohnlev 	} else {
2105084Sjohnlev 		bytes1 = (p - c) * sizeof (evtchn_port_t);
2115084Sjohnlev 		bytes2 = 0;
2125084Sjohnlev 	}
2135084Sjohnlev 
2145084Sjohnlev 	/* Truncate chunks according to caller's maximum byte count. */
2155084Sjohnlev 	if (bytes1 > count) {
2165084Sjohnlev 		bytes1 = count;
2175084Sjohnlev 		bytes2 = 0;
2185084Sjohnlev 	} else if ((bytes1 + bytes2) > count) {
2195084Sjohnlev 		bytes2 = count - bytes1;
2205084Sjohnlev 	}
2215084Sjohnlev 
2225084Sjohnlev 	if (uiomove(&ep->ring[EVTCHN_RING_MASK(c)], bytes1, UIO_READ, uio) ||
2235084Sjohnlev 	    ((bytes2 != 0) && uiomove(&ep->ring[0], bytes2, UIO_READ, uio))) {
2245084Sjohnlev 		rc = EFAULT;
2255084Sjohnlev 		goto done;
2265084Sjohnlev 	}
2275084Sjohnlev 
2285084Sjohnlev 	ep->ring_cons += (bytes1 + bytes2) / sizeof (evtchn_port_t);
2295084Sjohnlev done:
2305084Sjohnlev 	mutex_exit(&ep->evtchn_lock);
2315084Sjohnlev 	return (rc);
2325084Sjohnlev }
2335084Sjohnlev 
2345084Sjohnlev /* ARGSUSED */
2355084Sjohnlev static int
evtchndrv_write(dev_t dev,struct uio * uio,cred_t * cr)2366784Sjohnlev evtchndrv_write(dev_t dev, struct uio *uio, cred_t *cr)
2375084Sjohnlev {
2385084Sjohnlev 	int  rc, i;
2395084Sjohnlev 	ssize_t count;
2405084Sjohnlev 	evtchn_port_t *kbuf;
2415084Sjohnlev 	struct evtsoftdata *ep;
2425084Sjohnlev 	ulong_t flags;
2435084Sjohnlev 	minor_t minor = getminor(dev);
244*10175SStuart.Maybee@Sun.COM 	evtchn_port_t sbuf[32];
2455084Sjohnlev 
2466784Sjohnlev 	if (secpolicy_xvm_control(cr))
2476784Sjohnlev 		return (EPERM);
2486784Sjohnlev 
2495084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
2505084Sjohnlev 
2515084Sjohnlev 
2525084Sjohnlev 	/* Whole number of ports. */
2535084Sjohnlev 	count = uio->uio_resid;
2545084Sjohnlev 	count &= ~(sizeof (evtchn_port_t) - 1);
2555084Sjohnlev 
256*10175SStuart.Maybee@Sun.COM 	if (count == 0)
257*10175SStuart.Maybee@Sun.COM 		return (0);
2585084Sjohnlev 
2595084Sjohnlev 	if (count > PAGESIZE)
2605084Sjohnlev 		count = PAGESIZE;
2615084Sjohnlev 
262*10175SStuart.Maybee@Sun.COM 	if (count <= sizeof (sbuf))
263*10175SStuart.Maybee@Sun.COM 		kbuf = sbuf;
264*10175SStuart.Maybee@Sun.COM 	else
265*10175SStuart.Maybee@Sun.COM 		kbuf = kmem_alloc(PAGESIZE, KM_SLEEP);
2665084Sjohnlev 	if ((rc = uiomove(kbuf, count, UIO_WRITE, uio)) != 0)
2675084Sjohnlev 		goto out;
2685084Sjohnlev 
2695084Sjohnlev 	mutex_enter(&port_user_lock);
2705084Sjohnlev 	for (i = 0; i < (count / sizeof (evtchn_port_t)); i++)
2715084Sjohnlev 		if ((kbuf[i] < NR_EVENT_CHANNELS) &&
2725084Sjohnlev 		    (port_user[kbuf[i]] == ep)) {
2735084Sjohnlev 			flags = intr_clear();
2745084Sjohnlev 			ec_unmask_evtchn(kbuf[i]);
2755084Sjohnlev 			intr_restore(flags);
2765084Sjohnlev 		}
2775084Sjohnlev 	mutex_exit(&port_user_lock);
2785084Sjohnlev 
2795084Sjohnlev out:
280*10175SStuart.Maybee@Sun.COM 	if (kbuf != sbuf)
281*10175SStuart.Maybee@Sun.COM 		kmem_free(kbuf, PAGESIZE);
2825084Sjohnlev 	return (rc);
2835084Sjohnlev }
2845084Sjohnlev 
2855084Sjohnlev static void
evtchn_bind_to_user(struct evtsoftdata * u,int port)2865084Sjohnlev evtchn_bind_to_user(struct evtsoftdata *u, int port)
2875084Sjohnlev {
2885084Sjohnlev 	ulong_t flags;
2895084Sjohnlev 
2905084Sjohnlev 	/*
2915084Sjohnlev 	 * save away the PID of the last process to bind to this event channel.
2925084Sjohnlev 	 * Useful for debugging.
2935084Sjohnlev 	 */
2945084Sjohnlev 	u->pid = ddi_get_pid();
2955084Sjohnlev 
2965084Sjohnlev 	mutex_enter(&port_user_lock);
2975084Sjohnlev 	ASSERT(port_user[port] == NULL);
2985084Sjohnlev 	port_user[port] = u;
2995084Sjohnlev 	ec_irq_add_evtchn(ec_dev_irq, port);
3005084Sjohnlev 	flags = intr_clear();
3015084Sjohnlev 	ec_unmask_evtchn(port);
3025084Sjohnlev 	intr_restore(flags);
3035084Sjohnlev 	mutex_exit(&port_user_lock);
3045084Sjohnlev }
3055084Sjohnlev 
3065084Sjohnlev static void
evtchndrv_close_evtchn(int port)3075084Sjohnlev evtchndrv_close_evtchn(int port)
3085084Sjohnlev {
3095084Sjohnlev 	struct evtsoftdata *ep;
3105084Sjohnlev 
3115084Sjohnlev 	ASSERT(MUTEX_HELD(&port_user_lock));
3125084Sjohnlev 	ep = port_user[port];
3135084Sjohnlev 	ASSERT(ep != NULL);
3145084Sjohnlev 	(void) ec_mask_evtchn(port);
3155084Sjohnlev 	/*
3165084Sjohnlev 	 * It is possible the event is in transit to us.
3175084Sjohnlev 	 * If it is already in the ring buffer, then a client may
3185084Sjohnlev 	 * get a spurious event notification on the next read of
3195084Sjohnlev 	 * of the evtchn device.  Clients will need to be able to
3205084Sjohnlev 	 * handle getting a spurious event notification.
3215084Sjohnlev 	 */
3225084Sjohnlev 	port_user[port] = NULL;
3235084Sjohnlev 	/*
3245084Sjohnlev 	 * The event is masked and should stay so, clean it up.
3255084Sjohnlev 	 */
3265084Sjohnlev 	ec_irq_rm_evtchn(ec_dev_irq, port);
3275084Sjohnlev }
3285084Sjohnlev 
3295084Sjohnlev /* ARGSUSED */
3305084Sjohnlev static int
evtchndrv_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cr,int * rvalp)3316784Sjohnlev evtchndrv_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr,
3325084Sjohnlev     int *rvalp)
3335084Sjohnlev {
3345084Sjohnlev 	int err = 0;
3355084Sjohnlev 	struct evtsoftdata *ep;
3365084Sjohnlev 	minor_t minor = getminor(dev);
3375084Sjohnlev 
3386784Sjohnlev 	if (secpolicy_xvm_control(cr))
3396784Sjohnlev 		return (EPERM);
3406784Sjohnlev 
3415084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
3425084Sjohnlev 
3435084Sjohnlev 	*rvalp = 0;
3445084Sjohnlev 
3455084Sjohnlev 	switch (cmd) {
3465084Sjohnlev 	case IOCTL_EVTCHN_BIND_VIRQ: {
3475084Sjohnlev 		struct ioctl_evtchn_bind_virq bind;
3485084Sjohnlev 
3495084Sjohnlev 		if (copyin((void *)data, &bind, sizeof (bind))) {
3505084Sjohnlev 			err = EFAULT;
3515084Sjohnlev 			break;
3525084Sjohnlev 		}
3535084Sjohnlev 
3545084Sjohnlev 		if ((err = xen_bind_virq(bind.virq, 0, rvalp)) != 0)
3555084Sjohnlev 			break;
3565084Sjohnlev 
3575084Sjohnlev 		evtchn_bind_to_user(ep, *rvalp);
3585084Sjohnlev 		break;
3595084Sjohnlev 	}
3605084Sjohnlev 
3615084Sjohnlev 	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
3625084Sjohnlev 		struct ioctl_evtchn_bind_interdomain bind;
3635084Sjohnlev 
3645084Sjohnlev 		if (copyin((void *)data, &bind, sizeof (bind))) {
3655084Sjohnlev 			err = EFAULT;
3665084Sjohnlev 			break;
3675084Sjohnlev 		}
3685084Sjohnlev 
3695084Sjohnlev 		if ((err = xen_bind_interdomain(bind.remote_domain,
3705084Sjohnlev 		    bind.remote_port, rvalp)) != 0)
3715084Sjohnlev 			break;
3725084Sjohnlev 
3735084Sjohnlev 		ec_bind_vcpu(*rvalp, 0);
3745084Sjohnlev 		evtchn_bind_to_user(ep, *rvalp);
3755084Sjohnlev 		break;
3765084Sjohnlev 	}
3775084Sjohnlev 
3785084Sjohnlev 	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
3795084Sjohnlev 		struct ioctl_evtchn_bind_unbound_port bind;
3805084Sjohnlev 
3815084Sjohnlev 		if (copyin((void *)data, &bind, sizeof (bind))) {
3825084Sjohnlev 			err = EFAULT;
3835084Sjohnlev 			break;
3845084Sjohnlev 		}
3855084Sjohnlev 
3865084Sjohnlev 		if ((err = xen_alloc_unbound_evtchn(bind.remote_domain,
3875084Sjohnlev 		    rvalp)) != 0)
3885084Sjohnlev 			break;
3895084Sjohnlev 
3905084Sjohnlev 		evtchn_bind_to_user(ep, *rvalp);
3915084Sjohnlev 		break;
3925084Sjohnlev 	}
3935084Sjohnlev 
3945084Sjohnlev 	case IOCTL_EVTCHN_UNBIND: {
3955084Sjohnlev 		struct ioctl_evtchn_unbind unbind;
3965084Sjohnlev 
3975084Sjohnlev 		if (copyin((void *)data, &unbind, sizeof (unbind))) {
3985084Sjohnlev 			err = EFAULT;
3995084Sjohnlev 			break;
4005084Sjohnlev 		}
4015084Sjohnlev 
4025084Sjohnlev 		if (unbind.port >= NR_EVENT_CHANNELS) {
4035084Sjohnlev 			err = EFAULT;
4045084Sjohnlev 			break;
4055084Sjohnlev 		}
4065084Sjohnlev 
4075084Sjohnlev 		mutex_enter(&port_user_lock);
4085084Sjohnlev 
4095084Sjohnlev 		if (port_user[unbind.port] != ep) {
4105084Sjohnlev 			mutex_exit(&port_user_lock);
4115084Sjohnlev 			err = ENOTCONN;
4125084Sjohnlev 			break;
4135084Sjohnlev 		}
4145084Sjohnlev 
4155084Sjohnlev 		evtchndrv_close_evtchn(unbind.port);
4165084Sjohnlev 		mutex_exit(&port_user_lock);
4175084Sjohnlev 		break;
4185084Sjohnlev 	}
4195084Sjohnlev 
4205084Sjohnlev 	case IOCTL_EVTCHN_NOTIFY: {
4215084Sjohnlev 		struct ioctl_evtchn_notify notify;
4225084Sjohnlev 
4235084Sjohnlev 		if (copyin((void *)data, &notify, sizeof (notify))) {
4245084Sjohnlev 			err = EFAULT;
4255084Sjohnlev 			break;
4265084Sjohnlev 		}
4275084Sjohnlev 
4285084Sjohnlev 		if (notify.port >= NR_EVENT_CHANNELS) {
4295084Sjohnlev 			err = EINVAL;
4305084Sjohnlev 		} else if (port_user[notify.port] != ep) {
4315084Sjohnlev 			err = ENOTCONN;
4325084Sjohnlev 		} else {
4335084Sjohnlev 			ec_notify_via_evtchn(notify.port);
4345084Sjohnlev 		}
4355084Sjohnlev 		break;
4365084Sjohnlev 	}
4375084Sjohnlev 
4385084Sjohnlev 	default:
4395084Sjohnlev 		err = ENOSYS;
4405084Sjohnlev 	}
4415084Sjohnlev 
4425084Sjohnlev 	return (err);
4435084Sjohnlev }
4445084Sjohnlev 
4455084Sjohnlev static int
evtchndrv_poll(dev_t dev,short ev,int anyyet,short * revp,pollhead_t ** phpp)4465084Sjohnlev evtchndrv_poll(dev_t dev, short ev, int anyyet, short *revp, pollhead_t **phpp)
4475084Sjohnlev {
4485084Sjohnlev 	struct evtsoftdata *ep;
4495084Sjohnlev 	minor_t minor = getminor(dev);
4505084Sjohnlev 	short mask = 0;
4515084Sjohnlev 
4525084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
4535084Sjohnlev 	*phpp = (struct pollhead *)NULL;
4545084Sjohnlev 
4555084Sjohnlev 	if (ev & POLLOUT)
4565084Sjohnlev 		mask |= POLLOUT;
4575084Sjohnlev 	if (ep->ring_overflow)
4585084Sjohnlev 		mask |= POLLERR;
4595084Sjohnlev 	if (ev & (POLLIN | POLLRDNORM)) {
4605084Sjohnlev 		mutex_enter(&ep->evtchn_lock);
4615084Sjohnlev 		if (ep->ring_cons != ep->ring_prod)
4625084Sjohnlev 			mask |= (POLLIN | POLLRDNORM) & ev;
4635084Sjohnlev 		else
4645084Sjohnlev 			if (mask == 0 && !anyyet)
4655084Sjohnlev 				*phpp = &ep->evtchn_pollhead;
4665084Sjohnlev 		mutex_exit(&ep->evtchn_lock);
4675084Sjohnlev 	}
4685084Sjohnlev 	*revp = mask;
4695084Sjohnlev 	return (0);
4705084Sjohnlev }
4715084Sjohnlev 
4725084Sjohnlev 
4735084Sjohnlev /* ARGSUSED */
4745084Sjohnlev static int
evtchndrv_open(dev_t * devp,int flag,int otyp,cred_t * credp)4755084Sjohnlev evtchndrv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4765084Sjohnlev {
4775084Sjohnlev 	struct evtsoftdata *ep;
4785084Sjohnlev 	minor_t minor = getminor(*devp);
4795084Sjohnlev 
4805084Sjohnlev 	if (otyp == OTYP_BLK)
4815084Sjohnlev 		return (ENXIO);
4825084Sjohnlev 
4835084Sjohnlev 	/*
4845084Sjohnlev 	 * only allow open on minor = 0 - the clone device
4855084Sjohnlev 	 */
4865084Sjohnlev 	if (minor != 0)
4875084Sjohnlev 		return (ENXIO);
4885084Sjohnlev 
4895084Sjohnlev 	/*
4905084Sjohnlev 	 * find a free slot and grab it
4915084Sjohnlev 	 */
4925084Sjohnlev 	mutex_enter(&evtchndrv_clone_tab_mutex);
4935084Sjohnlev 	for (minor = 1; minor < evtchndrv_nclones; minor++) {
4945084Sjohnlev 		if (evtchndrv_clone_tab[minor] == 0) {
4955084Sjohnlev 			evtchndrv_clone_tab[minor] = 1;
4965084Sjohnlev 			break;
4975084Sjohnlev 		}
4985084Sjohnlev 	}
4995084Sjohnlev 	mutex_exit(&evtchndrv_clone_tab_mutex);
5005084Sjohnlev 	if (minor == evtchndrv_nclones)
5015084Sjohnlev 		return (EAGAIN);
5025084Sjohnlev 
5035084Sjohnlev 	/* Allocate softstate structure */
5045084Sjohnlev 	if (ddi_soft_state_zalloc(evtchndrv_statep,
5055084Sjohnlev 	    EVTCHNDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
5065084Sjohnlev 		mutex_enter(&evtchndrv_clone_tab_mutex);
5075084Sjohnlev 		evtchndrv_clone_tab[minor] = 0;
5085084Sjohnlev 		mutex_exit(&evtchndrv_clone_tab_mutex);
5095084Sjohnlev 		return (EAGAIN);
5105084Sjohnlev 	}
5115084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
5125084Sjohnlev 
5135084Sjohnlev 	/* ... and init it */
5145084Sjohnlev 	ep->dip = evtchndrv_dip;
5155084Sjohnlev 
5165084Sjohnlev 	cv_init(&ep->evtchn_wait, NULL, CV_DEFAULT, NULL);
5175084Sjohnlev 	mutex_init(&ep->evtchn_lock, NULL, MUTEX_DEFAULT, NULL);
5185084Sjohnlev 
5195084Sjohnlev 	ep->ring = kmem_alloc(PAGESIZE, KM_SLEEP);
5205084Sjohnlev 
5215084Sjohnlev 	/* clone driver */
5225084Sjohnlev 	*devp = makedevice(getmajor(*devp), minor);
5235084Sjohnlev 
5245084Sjohnlev 	return (0);
5255084Sjohnlev }
5265084Sjohnlev 
5275084Sjohnlev /* ARGSUSED */
5285084Sjohnlev static int
evtchndrv_close(dev_t dev,int flag,int otyp,struct cred * credp)5295084Sjohnlev evtchndrv_close(dev_t dev, int flag, int otyp, struct cred *credp)
5305084Sjohnlev {
5315084Sjohnlev 	struct evtsoftdata *ep;
5325084Sjohnlev 	minor_t minor = getminor(dev);
5335084Sjohnlev 	int i;
5345084Sjohnlev 
5355084Sjohnlev 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
5365084Sjohnlev 	if (ep == NULL)
5375084Sjohnlev 		return (ENXIO);
5385084Sjohnlev 
5395084Sjohnlev 	mutex_enter(&port_user_lock);
5405084Sjohnlev 
5415084Sjohnlev 
5425084Sjohnlev 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
5435084Sjohnlev 		if (port_user[i] != ep)
5445084Sjohnlev 			continue;
5455084Sjohnlev 
5465084Sjohnlev 		evtchndrv_close_evtchn(i);
5475084Sjohnlev 	}
5485084Sjohnlev 
5495084Sjohnlev 	mutex_exit(&port_user_lock);
5505084Sjohnlev 
5515084Sjohnlev 	kmem_free(ep->ring, PAGESIZE);
5525084Sjohnlev 	ddi_soft_state_free(evtchndrv_statep, EVTCHNDRV_MINOR2INST(minor));
5535084Sjohnlev 
5545084Sjohnlev 	/*
5555084Sjohnlev 	 * free clone tab slot
5565084Sjohnlev 	 */
5575084Sjohnlev 	mutex_enter(&evtchndrv_clone_tab_mutex);
5585084Sjohnlev 	evtchndrv_clone_tab[minor] = 0;
5595084Sjohnlev 	mutex_exit(&evtchndrv_clone_tab_mutex);
5605084Sjohnlev 
5615084Sjohnlev 	return (0);
5625084Sjohnlev }
5635084Sjohnlev 
5645084Sjohnlev /* ARGSUSED */
5655084Sjohnlev static int
evtchndrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)5665084Sjohnlev evtchndrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
5675084Sjohnlev {
5685084Sjohnlev 	dev_t	dev = (dev_t)arg;
5695084Sjohnlev 	minor_t	minor = getminor(dev);
5705084Sjohnlev 	int	retval;
5715084Sjohnlev 
5725084Sjohnlev 	switch (cmd) {
5735084Sjohnlev 	case DDI_INFO_DEVT2DEVINFO:
5745084Sjohnlev 		if (minor != 0 || evtchndrv_dip == NULL) {
5755084Sjohnlev 			*result = (void *)NULL;
5765084Sjohnlev 			retval = DDI_FAILURE;
5775084Sjohnlev 		} else {
5785084Sjohnlev 			*result = (void *)evtchndrv_dip;
5795084Sjohnlev 			retval = DDI_SUCCESS;
5805084Sjohnlev 		}
5815084Sjohnlev 		break;
5825084Sjohnlev 	case DDI_INFO_DEVT2INSTANCE:
5835084Sjohnlev 		*result = (void *)0;
5845084Sjohnlev 		retval = DDI_SUCCESS;
5855084Sjohnlev 		break;
5865084Sjohnlev 	default:
5875084Sjohnlev 		retval = DDI_FAILURE;
5885084Sjohnlev 	}
5895084Sjohnlev 	return (retval);
5905084Sjohnlev }
5915084Sjohnlev 
5925084Sjohnlev 
5935084Sjohnlev static int
evtchndrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5945084Sjohnlev evtchndrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5955084Sjohnlev {
5965084Sjohnlev 	int	error;
5975084Sjohnlev 	int	unit = ddi_get_instance(dip);
5985084Sjohnlev 
5995084Sjohnlev 
6005084Sjohnlev 	switch (cmd) {
6015084Sjohnlev 	case DDI_ATTACH:
6025084Sjohnlev 		break;
6035084Sjohnlev 	case DDI_RESUME:
6045084Sjohnlev 		return (DDI_SUCCESS);
6055084Sjohnlev 	default:
6065084Sjohnlev 		cmn_err(CE_WARN, "evtchn_attach: unknown cmd 0x%x\n", cmd);
6075084Sjohnlev 		return (DDI_FAILURE);
6085084Sjohnlev 	}
6095084Sjohnlev 
6105084Sjohnlev 	/* DDI_ATTACH */
6115084Sjohnlev 
6125084Sjohnlev 	/*
6135084Sjohnlev 	 * only one instance - but we clone using the open routine
6145084Sjohnlev 	 */
6155084Sjohnlev 	if (ddi_get_instance(dip) > 0)
6165084Sjohnlev 		return (DDI_FAILURE);
6175084Sjohnlev 
6185084Sjohnlev 	mutex_init(&evtchndrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
6195084Sjohnlev 	    NULL);
6205084Sjohnlev 
6215084Sjohnlev 	error = ddi_create_minor_node(dip, "evtchn", S_IFCHR, unit,
6225084Sjohnlev 	    DDI_PSEUDO, NULL);
6235084Sjohnlev 	if (error != DDI_SUCCESS)
6245084Sjohnlev 		goto fail;
6255084Sjohnlev 
6265084Sjohnlev 	/*
6275084Sjohnlev 	 * save dip for getinfo
6285084Sjohnlev 	 */
6295084Sjohnlev 	evtchndrv_dip = dip;
6305084Sjohnlev 	ddi_report_dev(dip);
6315084Sjohnlev 
6325084Sjohnlev 	mutex_init(&port_user_lock, NULL, MUTEX_DRIVER, NULL);
6335084Sjohnlev 	(void) memset(port_user, 0, sizeof (port_user));
6345084Sjohnlev 
6355084Sjohnlev 	ec_dev_irq = ec_dev_alloc_irq();
6365084Sjohnlev 	(void) add_avintr(NULL, IPL_EVTCHN, (avfunc)evtchn_device_upcall,
6375084Sjohnlev 	    "evtchn_driver", ec_dev_irq, NULL, NULL, NULL, dip);
6385084Sjohnlev 
6395084Sjohnlev 	return (DDI_SUCCESS);
6405084Sjohnlev 
6415084Sjohnlev fail:
6425084Sjohnlev 	(void) evtchndrv_detach(dip, DDI_DETACH);
6435084Sjohnlev 	return (error);
6445084Sjohnlev }
6455084Sjohnlev 
6465084Sjohnlev /*ARGSUSED*/
6475084Sjohnlev static int
evtchndrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6485084Sjohnlev evtchndrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6495084Sjohnlev {
6505084Sjohnlev 	/*
6515084Sjohnlev 	 * Don't allow detach for now.
6525084Sjohnlev 	 */
6535084Sjohnlev 	return (DDI_FAILURE);
6545084Sjohnlev }
6555084Sjohnlev 
6565084Sjohnlev /* Solaris driver framework */
6575084Sjohnlev 
6585084Sjohnlev static 	struct cb_ops evtchndrv_cb_ops = {
6595084Sjohnlev 	evtchndrv_open,		/* cb_open */
6605084Sjohnlev 	evtchndrv_close,	/* cb_close */
6615084Sjohnlev 	nodev,			/* cb_strategy */
6625084Sjohnlev 	nodev,			/* cb_print */
6635084Sjohnlev 	nodev,			/* cb_dump */
6645084Sjohnlev 	evtchndrv_read,		/* cb_read */
6655084Sjohnlev 	evtchndrv_write,	/* cb_write */
6665084Sjohnlev 	evtchndrv_ioctl,	/* cb_ioctl */
6675084Sjohnlev 	nodev,			/* cb_devmap */
6685084Sjohnlev 	nodev,			/* cb_mmap */
6695084Sjohnlev 	nodev,			/* cb_segmap */
6705084Sjohnlev 	evtchndrv_poll,		/* cb_chpoll */
6715084Sjohnlev 	ddi_prop_op,		/* cb_prop_op */
6725084Sjohnlev 	0,			/* cb_stream */
6735084Sjohnlev 	D_NEW | D_MP | D_64BIT	/* cb_flag */
6745084Sjohnlev };
6755084Sjohnlev 
6765084Sjohnlev static struct dev_ops evtchndrv_dev_ops = {
6775084Sjohnlev 	DEVO_REV,		/* devo_rev */
6785084Sjohnlev 	0,			/* devo_refcnt */
6795084Sjohnlev 	evtchndrv_info,		/* devo_getinfo */
6805084Sjohnlev 	nulldev,		/* devo_identify */
6815084Sjohnlev 	nulldev,		/* devo_probe */
6825084Sjohnlev 	evtchndrv_attach,	/* devo_attach */
6835084Sjohnlev 	evtchndrv_detach,	/* devo_detach */
6845084Sjohnlev 	nodev,			/* devo_reset */
6855084Sjohnlev 	&evtchndrv_cb_ops,	/* devo_cb_ops */
6865084Sjohnlev 	NULL,			/* devo_bus_ops */
6877656SSherry.Moore@Sun.COM 	NULL,			/* power */
6887656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
6895084Sjohnlev };
6905084Sjohnlev 
6915084Sjohnlev static struct modldrv modldrv = {
6925084Sjohnlev 	&mod_driverops,		/* Type of module.  This one is a driver */
6937656SSherry.Moore@Sun.COM 	"Evtchn driver",	/* Name of the module. */
6945084Sjohnlev 	&evtchndrv_dev_ops	/* driver ops */
6955084Sjohnlev };
6965084Sjohnlev 
6975084Sjohnlev static struct modlinkage modlinkage = {
6985084Sjohnlev 	MODREV_1,
6995084Sjohnlev 	&modldrv,
7005084Sjohnlev 	NULL
7015084Sjohnlev };
7025084Sjohnlev 
7035084Sjohnlev int
_init(void)7045084Sjohnlev _init(void)
7055084Sjohnlev {
7065084Sjohnlev 	int err;
7075084Sjohnlev 
7085084Sjohnlev 	err = ddi_soft_state_init(&evtchndrv_statep,
7095084Sjohnlev 	    sizeof (struct evtsoftdata), 1);
7105084Sjohnlev 	if (err)
7115084Sjohnlev 		return (err);
7125084Sjohnlev 
7135084Sjohnlev 	err = mod_install(&modlinkage);
7145084Sjohnlev 	if (err)
7155084Sjohnlev 		ddi_soft_state_fini(&evtchndrv_statep);
7165084Sjohnlev 	else
7175084Sjohnlev 		evtchndrv_clone_tab = kmem_zalloc(
7185084Sjohnlev 		    sizeof (int) * evtchndrv_nclones, KM_SLEEP);
7195084Sjohnlev 	return (err);
7205084Sjohnlev }
7215084Sjohnlev 
7225084Sjohnlev int
_fini(void)7235084Sjohnlev _fini(void)
7245084Sjohnlev {
7255084Sjohnlev 	int e;
7265084Sjohnlev 
7275084Sjohnlev 	e = mod_remove(&modlinkage);
7285084Sjohnlev 	if (e)
7295084Sjohnlev 		return (e);
7305084Sjohnlev 
7315084Sjohnlev 	ddi_soft_state_fini(&evtchndrv_statep);
7325084Sjohnlev 
7335084Sjohnlev 	return (0);
7345084Sjohnlev }
7355084Sjohnlev 
7365084Sjohnlev int
_info(struct modinfo * modinfop)7375084Sjohnlev _info(struct modinfo *modinfop)
7385084Sjohnlev {
7395084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
7405084Sjohnlev }
741