xref: /freebsd/sys/dev/xen/evtchn/evtchn_dev.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
15779d8adSRoger Pau Monné /******************************************************************************
25779d8adSRoger Pau Monné  * evtchn.c
35779d8adSRoger Pau Monné  *
45779d8adSRoger Pau Monné  * Driver for receiving and demuxing event-channel signals.
55779d8adSRoger Pau Monné  *
65779d8adSRoger Pau Monné  * Copyright (c) 2004-2005, K A Fraser
75779d8adSRoger Pau Monné  * Multi-process extensions Copyright (c) 2004, Steven Smith
85779d8adSRoger Pau Monné  * FreeBSD port Copyright (c) 2014, Roger Pau Monné
95779d8adSRoger Pau Monné  * Fetched from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
105779d8adSRoger Pau Monné  * File: drivers/xen/evtchn.c
115779d8adSRoger Pau Monné  * Git commit: 0dc0064add422bc0ef5165ebe9ece3052bbd457d
125779d8adSRoger Pau Monné  *
135779d8adSRoger Pau Monné  * This program is free software; you can redistribute it and/or
145779d8adSRoger Pau Monné  * modify it under the terms of the GNU General Public License version 2
155779d8adSRoger Pau Monné  * as published by the Free Software Foundation; or, when distributed
165779d8adSRoger Pau Monné  * separately from the Linux kernel or incorporated into other
175779d8adSRoger Pau Monné  * software packages, subject to the following license:
185779d8adSRoger Pau Monné  *
195779d8adSRoger Pau Monné  * Permission is hereby granted, free of charge, to any person obtaining a copy
205779d8adSRoger Pau Monné  * of this source file (the "Software"), to deal in the Software without
215779d8adSRoger Pau Monné  * restriction, including without limitation the rights to use, copy, modify,
225779d8adSRoger Pau Monné  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
235779d8adSRoger Pau Monné  * and to permit persons to whom the Software is furnished to do so, subject to
245779d8adSRoger Pau Monné  * the following conditions:
255779d8adSRoger Pau Monné  *
265779d8adSRoger Pau Monné  * The above copyright notice and this permission notice shall be included in
275779d8adSRoger Pau Monné  * all copies or substantial portions of the Software.
285779d8adSRoger Pau Monné  *
295779d8adSRoger Pau Monné  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
305779d8adSRoger Pau Monné  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
315779d8adSRoger Pau Monné  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
325779d8adSRoger Pau Monné  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
335779d8adSRoger Pau Monné  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
345779d8adSRoger Pau Monné  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
355779d8adSRoger Pau Monné  * IN THE SOFTWARE.
365779d8adSRoger Pau Monné  */
375779d8adSRoger Pau Monné 
385779d8adSRoger Pau Monné #include <sys/param.h>
395779d8adSRoger Pau Monné #include <sys/systm.h>
405779d8adSRoger Pau Monné #include <sys/uio.h>
415779d8adSRoger Pau Monné #include <sys/bus.h>
425779d8adSRoger Pau Monné #include <sys/malloc.h>
435779d8adSRoger Pau Monné #include <sys/kernel.h>
445779d8adSRoger Pau Monné #include <sys/lock.h>
455779d8adSRoger Pau Monné #include <sys/mutex.h>
465779d8adSRoger Pau Monné #include <sys/sx.h>
475779d8adSRoger Pau Monné #include <sys/selinfo.h>
485779d8adSRoger Pau Monné #include <sys/poll.h>
495779d8adSRoger Pau Monné #include <sys/conf.h>
505779d8adSRoger Pau Monné #include <sys/fcntl.h>
515779d8adSRoger Pau Monné #include <sys/ioccom.h>
525779d8adSRoger Pau Monné #include <sys/rman.h>
535779d8adSRoger Pau Monné #include <sys/tree.h>
545779d8adSRoger Pau Monné #include <sys/module.h>
555779d8adSRoger Pau Monné #include <sys/filio.h>
565779d8adSRoger Pau Monné #include <sys/vnode.h>
575779d8adSRoger Pau Monné 
585779d8adSRoger Pau Monné #include <xen/xen-os.h>
595779d8adSRoger Pau Monné #include <xen/evtchn.h>
605779d8adSRoger Pau Monné #include <xen/xen_intr.h>
615779d8adSRoger Pau Monné 
625779d8adSRoger Pau Monné #include <xen/evtchn/evtchnvar.h>
635779d8adSRoger Pau Monné 
645779d8adSRoger Pau Monné MALLOC_DEFINE(M_EVTCHN, "evtchn_dev", "Xen event channel user-space device");
655779d8adSRoger Pau Monné 
665779d8adSRoger Pau Monné struct user_evtchn;
675779d8adSRoger Pau Monné 
685779d8adSRoger Pau Monné static int evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2);
695779d8adSRoger Pau Monné 
705779d8adSRoger Pau Monné RB_HEAD(evtchn_tree, user_evtchn);
715779d8adSRoger Pau Monné 
725779d8adSRoger Pau Monné struct per_user_data {
735779d8adSRoger Pau Monné 	struct mtx bind_mutex; /* serialize bind/unbind operations */
745779d8adSRoger Pau Monné 	struct evtchn_tree evtchns;
755779d8adSRoger Pau Monné 
765779d8adSRoger Pau Monné 	/* Notification ring, accessed via /dev/xen/evtchn. */
775779d8adSRoger Pau Monné #define EVTCHN_RING_SIZE     (PAGE_SIZE / sizeof(evtchn_port_t))
785779d8adSRoger Pau Monné #define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
795779d8adSRoger Pau Monné 	evtchn_port_t *ring;
805779d8adSRoger Pau Monné 	unsigned int ring_cons, ring_prod, ring_overflow;
815779d8adSRoger Pau Monné 	struct sx ring_cons_mutex; /* protect against concurrent readers */
825779d8adSRoger Pau Monné 	struct mtx ring_prod_mutex; /* product against concurrent interrupts */
835779d8adSRoger Pau Monné 	struct selinfo ev_rsel;
845779d8adSRoger Pau Monné };
855779d8adSRoger Pau Monné 
865779d8adSRoger Pau Monné struct user_evtchn {
875779d8adSRoger Pau Monné 	RB_ENTRY(user_evtchn) node;
885779d8adSRoger Pau Monné 	struct per_user_data *user;
895779d8adSRoger Pau Monné 	evtchn_port_t port;
905779d8adSRoger Pau Monné 	xen_intr_handle_t handle;
915779d8adSRoger Pau Monné 	bool enabled;
925779d8adSRoger Pau Monné };
935779d8adSRoger Pau Monné 
945779d8adSRoger Pau Monné RB_GENERATE_STATIC(evtchn_tree, user_evtchn, node, evtchn_cmp);
955779d8adSRoger Pau Monné 
965779d8adSRoger Pau Monné static device_t evtchn_dev;
975779d8adSRoger Pau Monné 
985779d8adSRoger Pau Monné static d_read_t      evtchn_read;
995779d8adSRoger Pau Monné static d_write_t     evtchn_write;
1005779d8adSRoger Pau Monné static d_ioctl_t     evtchn_ioctl;
1015779d8adSRoger Pau Monné static d_poll_t      evtchn_poll;
1025779d8adSRoger Pau Monné static d_open_t      evtchn_open;
1035779d8adSRoger Pau Monné 
1045779d8adSRoger Pau Monné static void evtchn_release(void *arg);
1055779d8adSRoger Pau Monné 
1065779d8adSRoger Pau Monné static struct cdevsw evtchn_devsw = {
1075779d8adSRoger Pau Monné 	.d_version = D_VERSION,
1085779d8adSRoger Pau Monné 	.d_open = evtchn_open,
1095779d8adSRoger Pau Monné 	.d_read = evtchn_read,
1105779d8adSRoger Pau Monné 	.d_write = evtchn_write,
1115779d8adSRoger Pau Monné 	.d_ioctl = evtchn_ioctl,
1125779d8adSRoger Pau Monné 	.d_poll = evtchn_poll,
1135779d8adSRoger Pau Monné 	.d_name = "evtchn",
1145779d8adSRoger Pau Monné };
1155779d8adSRoger Pau Monné 
1165779d8adSRoger Pau Monné /*------------------------- Red-black tree helpers ---------------------------*/
1175779d8adSRoger Pau Monné static int
evtchn_cmp(struct user_evtchn * u1,struct user_evtchn * u2)1185779d8adSRoger Pau Monné evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2)
1195779d8adSRoger Pau Monné {
1205779d8adSRoger Pau Monné 
1215779d8adSRoger Pau Monné 	return (u1->port - u2->port);
1225779d8adSRoger Pau Monné }
1235779d8adSRoger Pau Monné 
1245779d8adSRoger Pau Monné static struct user_evtchn *
find_evtchn(struct per_user_data * u,evtchn_port_t port)1255779d8adSRoger Pau Monné find_evtchn(struct per_user_data *u, evtchn_port_t port)
1265779d8adSRoger Pau Monné {
1275779d8adSRoger Pau Monné 	struct user_evtchn tmp = {
1285779d8adSRoger Pau Monné 		.port = port,
1295779d8adSRoger Pau Monné 	};
1305779d8adSRoger Pau Monné 
1315779d8adSRoger Pau Monné 	return (RB_FIND(evtchn_tree, &u->evtchns, &tmp));
1325779d8adSRoger Pau Monné }
1335779d8adSRoger Pau Monné 
1345779d8adSRoger Pau Monné /*--------------------------- Interrupt handlers -----------------------------*/
1355779d8adSRoger Pau Monné static int
evtchn_filter(void * arg)1365779d8adSRoger Pau Monné evtchn_filter(void *arg)
1375779d8adSRoger Pau Monné {
1385779d8adSRoger Pau Monné 	struct user_evtchn *evtchn;
1395779d8adSRoger Pau Monné 
1405779d8adSRoger Pau Monné 	evtchn = arg;
1415779d8adSRoger Pau Monné 
1425779d8adSRoger Pau Monné 	if (!evtchn->enabled && bootverbose) {
1435779d8adSRoger Pau Monné 		device_printf(evtchn_dev,
1445779d8adSRoger Pau Monné 		    "Received upcall for disabled event channel %d\n",
1455779d8adSRoger Pau Monné 		    evtchn->port);
1465779d8adSRoger Pau Monné 	}
1475779d8adSRoger Pau Monné 
1485779d8adSRoger Pau Monné 	evtchn_mask_port(evtchn->port);
1495779d8adSRoger Pau Monné 	evtchn->enabled = false;
1505779d8adSRoger Pau Monné 
1515779d8adSRoger Pau Monné 	return (FILTER_SCHEDULE_THREAD);
1525779d8adSRoger Pau Monné }
1535779d8adSRoger Pau Monné 
1545779d8adSRoger Pau Monné static void
evtchn_interrupt(void * arg)1555779d8adSRoger Pau Monné evtchn_interrupt(void *arg)
1565779d8adSRoger Pau Monné {
1575779d8adSRoger Pau Monné 	struct user_evtchn *evtchn;
1585779d8adSRoger Pau Monné 	struct per_user_data *u;
1595779d8adSRoger Pau Monné 
1605779d8adSRoger Pau Monné 	evtchn = arg;
1615779d8adSRoger Pau Monné 	u = evtchn->user;
1625779d8adSRoger Pau Monné 
1635779d8adSRoger Pau Monné 	/*
1645779d8adSRoger Pau Monné 	 * Protect against concurrent events using this handler
1655779d8adSRoger Pau Monné 	 * on different CPUs.
1665779d8adSRoger Pau Monné 	 */
1675779d8adSRoger Pau Monné 	mtx_lock(&u->ring_prod_mutex);
1685779d8adSRoger Pau Monné 	if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
1695779d8adSRoger Pau Monné 		u->ring[EVTCHN_RING_MASK(u->ring_prod)] = evtchn->port;
1705779d8adSRoger Pau Monné 		wmb(); /* Ensure ring contents visible */
1715779d8adSRoger Pau Monné 		if (u->ring_cons == u->ring_prod++) {
1725779d8adSRoger Pau Monné 			wakeup(u);
1735779d8adSRoger Pau Monné 			selwakeup(&u->ev_rsel);
1745779d8adSRoger Pau Monné 		}
1755779d8adSRoger Pau Monné 	} else
1765779d8adSRoger Pau Monné 		u->ring_overflow = 1;
1775779d8adSRoger Pau Monné 	mtx_unlock(&u->ring_prod_mutex);
1785779d8adSRoger Pau Monné }
1795779d8adSRoger Pau Monné 
1805779d8adSRoger Pau Monné /*------------------------- Character device methods -------------------------*/
1815779d8adSRoger Pau Monné static int
evtchn_open(struct cdev * dev,int flag,int otyp,struct thread * td)1825779d8adSRoger Pau Monné evtchn_open(struct cdev *dev, int flag, int otyp, struct thread *td)
1835779d8adSRoger Pau Monné {
1845779d8adSRoger Pau Monné 	struct per_user_data *u;
1855779d8adSRoger Pau Monné 	int error;
1865779d8adSRoger Pau Monné 
1875779d8adSRoger Pau Monné 	u = malloc(sizeof(*u), M_EVTCHN, M_WAITOK | M_ZERO);
1885779d8adSRoger Pau Monné 	u->ring = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK | M_ZERO);
1895779d8adSRoger Pau Monné 
1905779d8adSRoger Pau Monné 	/* Initialize locks */
1915779d8adSRoger Pau Monné 	mtx_init(&u->bind_mutex, "evtchn_bind_mutex", NULL, MTX_DEF);
1925779d8adSRoger Pau Monné 	sx_init(&u->ring_cons_mutex, "evtchn_ringc_sx");
1935779d8adSRoger Pau Monné 	mtx_init(&u->ring_prod_mutex, "evtchn_ringp_mutex", NULL, MTX_DEF);
1945779d8adSRoger Pau Monné 
1955779d8adSRoger Pau Monné 	/* Initialize red-black tree. */
1965779d8adSRoger Pau Monné 	RB_INIT(&u->evtchns);
1975779d8adSRoger Pau Monné 
1985779d8adSRoger Pau Monné 	/* Assign the allocated per_user_data to this open instance. */
1995779d8adSRoger Pau Monné 	error = devfs_set_cdevpriv(u, evtchn_release);
2005779d8adSRoger Pau Monné 	if (error != 0) {
2015779d8adSRoger Pau Monné 		mtx_destroy(&u->bind_mutex);
2025779d8adSRoger Pau Monné 		mtx_destroy(&u->ring_prod_mutex);
2035779d8adSRoger Pau Monné 		sx_destroy(&u->ring_cons_mutex);
2045779d8adSRoger Pau Monné 		free(u->ring, M_EVTCHN);
2055779d8adSRoger Pau Monné 		free(u, M_EVTCHN);
2065779d8adSRoger Pau Monné 	}
2075779d8adSRoger Pau Monné 
2085779d8adSRoger Pau Monné 	return (error);
2095779d8adSRoger Pau Monné }
2105779d8adSRoger Pau Monné 
2115779d8adSRoger Pau Monné static void
evtchn_release(void * arg)2125779d8adSRoger Pau Monné evtchn_release(void *arg)
2135779d8adSRoger Pau Monné {
2145779d8adSRoger Pau Monné 	struct per_user_data *u;
2155779d8adSRoger Pau Monné 	struct user_evtchn *evtchn, *tmp;
2165779d8adSRoger Pau Monné 
2175779d8adSRoger Pau Monné 	u = arg;
2185779d8adSRoger Pau Monné 
2195779d8adSRoger Pau Monné 	seldrain(&u->ev_rsel);
2205779d8adSRoger Pau Monné 
2215779d8adSRoger Pau Monné 	RB_FOREACH_SAFE(evtchn, evtchn_tree, &u->evtchns, tmp) {
2225779d8adSRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
2235779d8adSRoger Pau Monné 
2245779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
2255779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
2265779d8adSRoger Pau Monné 	}
2275779d8adSRoger Pau Monné 
2285779d8adSRoger Pau Monné 	mtx_destroy(&u->bind_mutex);
2295779d8adSRoger Pau Monné 	mtx_destroy(&u->ring_prod_mutex);
2305779d8adSRoger Pau Monné 	sx_destroy(&u->ring_cons_mutex);
2315779d8adSRoger Pau Monné 	free(u->ring, M_EVTCHN);
2325779d8adSRoger Pau Monné 	free(u, M_EVTCHN);
2335779d8adSRoger Pau Monné }
2345779d8adSRoger Pau Monné 
2355779d8adSRoger Pau Monné static int
evtchn_read(struct cdev * dev,struct uio * uio,int ioflag)2365779d8adSRoger Pau Monné evtchn_read(struct cdev *dev, struct uio *uio, int ioflag)
2375779d8adSRoger Pau Monné {
2385779d8adSRoger Pau Monné 	int error, count;
2395779d8adSRoger Pau Monné 	unsigned int c, p, bytes1 = 0, bytes2 = 0;
2405779d8adSRoger Pau Monné 	struct per_user_data *u;
2415779d8adSRoger Pau Monné 
2425779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
2435779d8adSRoger Pau Monné 	if (error != 0)
2445779d8adSRoger Pau Monné 		return (EINVAL);
2455779d8adSRoger Pau Monné 
2465779d8adSRoger Pau Monné 	/* Whole number of ports. */
2475779d8adSRoger Pau Monné 	count = uio->uio_resid;
2485779d8adSRoger Pau Monné 	count &= ~(sizeof(evtchn_port_t)-1);
2495779d8adSRoger Pau Monné 
2505779d8adSRoger Pau Monné 	if (count == 0)
2515779d8adSRoger Pau Monné 		return (0);
2525779d8adSRoger Pau Monné 
2535779d8adSRoger Pau Monné 	if (count > PAGE_SIZE)
2545779d8adSRoger Pau Monné 		count = PAGE_SIZE;
2555779d8adSRoger Pau Monné 
2565779d8adSRoger Pau Monné 	sx_xlock(&u->ring_cons_mutex);
2575779d8adSRoger Pau Monné 	for (;;) {
258b5ba8a0fSRoger Pau Monné 		if (u->ring_overflow) {
2595779d8adSRoger Pau Monné 			error = EFBIG;
2605779d8adSRoger Pau Monné 			goto unlock_out;
261b5ba8a0fSRoger Pau Monné 		}
2625779d8adSRoger Pau Monné 
2635779d8adSRoger Pau Monné 		c = u->ring_cons;
2645779d8adSRoger Pau Monné 		p = u->ring_prod;
2655779d8adSRoger Pau Monné 		if (c != p)
2665779d8adSRoger Pau Monné 			break;
2675779d8adSRoger Pau Monné 
2685779d8adSRoger Pau Monné 		if (ioflag & IO_NDELAY) {
269b5ba8a0fSRoger Pau Monné 			error = EWOULDBLOCK;
270b5ba8a0fSRoger Pau Monné 			goto unlock_out;
2715779d8adSRoger Pau Monné 		}
2725779d8adSRoger Pau Monné 
2735779d8adSRoger Pau Monné 		error = sx_sleep(u, &u->ring_cons_mutex, PCATCH, "evtchw", 0);
2745779d8adSRoger Pau Monné 		if ((error != 0) && (error != EWOULDBLOCK))
275b5ba8a0fSRoger Pau Monné 			goto unlock_out;
2765779d8adSRoger Pau Monné 	}
2775779d8adSRoger Pau Monné 
2785779d8adSRoger Pau Monné 	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
2795779d8adSRoger Pau Monné 	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
2805779d8adSRoger Pau Monné 		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
2815779d8adSRoger Pau Monné 		    sizeof(evtchn_port_t);
2825779d8adSRoger Pau Monné 		bytes2 = EVTCHN_RING_MASK(p) * sizeof(evtchn_port_t);
2835779d8adSRoger Pau Monné 	} else {
2845779d8adSRoger Pau Monné 		bytes1 = (p - c) * sizeof(evtchn_port_t);
2855779d8adSRoger Pau Monné 		bytes2 = 0;
2865779d8adSRoger Pau Monné 	}
2875779d8adSRoger Pau Monné 
2885779d8adSRoger Pau Monné 	/* Truncate chunks according to caller's maximum byte count. */
2895779d8adSRoger Pau Monné 	if (bytes1 > count) {
2905779d8adSRoger Pau Monné 		bytes1 = count;
2915779d8adSRoger Pau Monné 		bytes2 = 0;
2925779d8adSRoger Pau Monné 	} else if ((bytes1 + bytes2) > count) {
2935779d8adSRoger Pau Monné 		bytes2 = count - bytes1;
2945779d8adSRoger Pau Monné 	}
2955779d8adSRoger Pau Monné 
2965779d8adSRoger Pau Monné 	error = EFAULT;
2975779d8adSRoger Pau Monné 	rmb(); /* Ensure that we see the port before we copy it. */
2985779d8adSRoger Pau Monné 
2995779d8adSRoger Pau Monné 	if (uiomove(&u->ring[EVTCHN_RING_MASK(c)], bytes1, uio) ||
3005779d8adSRoger Pau Monné 	    ((bytes2 != 0) && uiomove(&u->ring[0], bytes2, uio)))
3015779d8adSRoger Pau Monné 		goto unlock_out;
3025779d8adSRoger Pau Monné 
3035779d8adSRoger Pau Monné 	u->ring_cons += (bytes1 + bytes2) / sizeof(evtchn_port_t);
3045779d8adSRoger Pau Monné 	error = 0;
3055779d8adSRoger Pau Monné 
3065779d8adSRoger Pau Monné unlock_out:
3075779d8adSRoger Pau Monné 	sx_xunlock(&u->ring_cons_mutex);
3085779d8adSRoger Pau Monné 	return (error);
3095779d8adSRoger Pau Monné }
3105779d8adSRoger Pau Monné 
3115779d8adSRoger Pau Monné static int
evtchn_write(struct cdev * dev,struct uio * uio,int ioflag)3125779d8adSRoger Pau Monné evtchn_write(struct cdev *dev, struct uio *uio, int ioflag)
3135779d8adSRoger Pau Monné {
3145779d8adSRoger Pau Monné 	int error, i, count;
3155779d8adSRoger Pau Monné 	evtchn_port_t *kbuf;
3165779d8adSRoger Pau Monné 	struct per_user_data *u;
3175779d8adSRoger Pau Monné 
3185779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
3195779d8adSRoger Pau Monné 	if (error != 0)
3205779d8adSRoger Pau Monné 		return (EINVAL);
3215779d8adSRoger Pau Monné 
3225779d8adSRoger Pau Monné 	kbuf = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK);
3235779d8adSRoger Pau Monné 
3245779d8adSRoger Pau Monné 	count = uio->uio_resid;
3255779d8adSRoger Pau Monné 	/* Whole number of ports. */
3265779d8adSRoger Pau Monné 	count &= ~(sizeof(evtchn_port_t)-1);
3275779d8adSRoger Pau Monné 
3285779d8adSRoger Pau Monné 	error = 0;
3295779d8adSRoger Pau Monné 	if (count == 0)
3305779d8adSRoger Pau Monné 		goto out;
3315779d8adSRoger Pau Monné 
3325779d8adSRoger Pau Monné 	if (count > PAGE_SIZE)
3335779d8adSRoger Pau Monné 		count = PAGE_SIZE;
3345779d8adSRoger Pau Monné 
3355779d8adSRoger Pau Monné 	error = uiomove(kbuf, count, uio);
3365779d8adSRoger Pau Monné 	if (error != 0)
3375779d8adSRoger Pau Monné 		goto out;
3385779d8adSRoger Pau Monné 
3395779d8adSRoger Pau Monné 	mtx_lock(&u->bind_mutex);
3405779d8adSRoger Pau Monné 
3415779d8adSRoger Pau Monné 	for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) {
3425779d8adSRoger Pau Monné 		evtchn_port_t port = kbuf[i];
3435779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
3445779d8adSRoger Pau Monné 
3455779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, port);
3465779d8adSRoger Pau Monné 		if (evtchn && !evtchn->enabled) {
3475779d8adSRoger Pau Monné 			evtchn->enabled = true;
3485779d8adSRoger Pau Monné 			evtchn_unmask_port(evtchn->port);
3495779d8adSRoger Pau Monné 		}
3505779d8adSRoger Pau Monné 	}
3515779d8adSRoger Pau Monné 
3525779d8adSRoger Pau Monné 	mtx_unlock(&u->bind_mutex);
3535779d8adSRoger Pau Monné 	error = 0;
3545779d8adSRoger Pau Monné 
3555779d8adSRoger Pau Monné out:
3565779d8adSRoger Pau Monné 	free(kbuf, M_EVTCHN);
3575779d8adSRoger Pau Monné 	return (error);
3585779d8adSRoger Pau Monné }
3595779d8adSRoger Pau Monné 
3605779d8adSRoger Pau Monné static inline int
evtchn_bind_user_port(struct per_user_data * u,struct user_evtchn * evtchn)3615779d8adSRoger Pau Monné evtchn_bind_user_port(struct per_user_data *u, struct user_evtchn *evtchn)
3625779d8adSRoger Pau Monné {
3635779d8adSRoger Pau Monné 	int error;
3645779d8adSRoger Pau Monné 
3655779d8adSRoger Pau Monné 	evtchn->port = xen_intr_port(evtchn->handle);
3665779d8adSRoger Pau Monné 	evtchn->user = u;
3675779d8adSRoger Pau Monné 	evtchn->enabled = true;
3685779d8adSRoger Pau Monné 	mtx_lock(&u->bind_mutex);
3695779d8adSRoger Pau Monné 	RB_INSERT(evtchn_tree, &u->evtchns, evtchn);
3705779d8adSRoger Pau Monné 	mtx_unlock(&u->bind_mutex);
371ca7af67aSRoger Pau Monné 	error = xen_intr_add_handler(device_get_nameunit(evtchn_dev),
372ca7af67aSRoger Pau Monné 	    evtchn_filter, evtchn_interrupt, evtchn,
373ca7af67aSRoger Pau Monné 	    INTR_TYPE_MISC | INTR_MPSAFE, evtchn->handle);
3745779d8adSRoger Pau Monné 	if (error != 0) {
3755779d8adSRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
3765779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
3775779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
3785779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
3795779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
3805779d8adSRoger Pau Monné 	}
3815779d8adSRoger Pau Monné 	return (error);
3825779d8adSRoger Pau Monné }
3835779d8adSRoger Pau Monné 
3845779d8adSRoger Pau Monné static int
evtchn_ioctl(struct cdev * dev,unsigned long cmd,caddr_t arg,int mode,struct thread * td __unused)3855779d8adSRoger Pau Monné evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
3865779d8adSRoger Pau Monné     int mode, struct thread *td __unused)
3875779d8adSRoger Pau Monné {
3885779d8adSRoger Pau Monné 	struct per_user_data *u;
3895779d8adSRoger Pau Monné 	int error;
3905779d8adSRoger Pau Monné 
3915779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
3925779d8adSRoger Pau Monné 	if (error != 0)
3935779d8adSRoger Pau Monné 		return (EINVAL);
3945779d8adSRoger Pau Monné 
3955779d8adSRoger Pau Monné 	switch (cmd) {
3965779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_VIRQ: {
3975779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_virq *bind;
3985779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
3995779d8adSRoger Pau Monné 
4005779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4015779d8adSRoger Pau Monné 
4025779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_virq *)arg;
4035779d8adSRoger Pau Monné 
4045779d8adSRoger Pau Monné 		error = xen_intr_bind_virq(evtchn_dev, bind->virq, 0,
4055779d8adSRoger Pau Monné 		    NULL, NULL, NULL, 0, &evtchn->handle);
4065779d8adSRoger Pau Monné 		if (error != 0) {
4075779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4085779d8adSRoger Pau Monné 			break;
4095779d8adSRoger Pau Monné 		}
4105779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4115779d8adSRoger Pau Monné 		if (error != 0)
4125779d8adSRoger Pau Monné 			break;
4135779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4145779d8adSRoger Pau Monné 		break;
4155779d8adSRoger Pau Monné 	}
4165779d8adSRoger Pau Monné 
4175779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
4185779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_interdomain *bind;
4195779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4205779d8adSRoger Pau Monné 
4215779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4225779d8adSRoger Pau Monné 
4235779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_interdomain *)arg;
4245779d8adSRoger Pau Monné 
4255779d8adSRoger Pau Monné 		error = xen_intr_bind_remote_port(evtchn_dev,
4265779d8adSRoger Pau Monné 		    bind->remote_domain, bind->remote_port, NULL,
4275779d8adSRoger Pau Monné 		    NULL, NULL, 0, &evtchn->handle);
4285779d8adSRoger Pau Monné 		if (error != 0) {
4295779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4305779d8adSRoger Pau Monné 			break;
4315779d8adSRoger Pau Monné 		}
4325779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4335779d8adSRoger Pau Monné 		if (error != 0)
4345779d8adSRoger Pau Monné 			break;
4355779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4365779d8adSRoger Pau Monné 		break;
4375779d8adSRoger Pau Monné 	}
4385779d8adSRoger Pau Monné 
4395779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
4405779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_unbound_port *bind;
4415779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4425779d8adSRoger Pau Monné 
4435779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4445779d8adSRoger Pau Monné 
4455779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_unbound_port *)arg;
4465779d8adSRoger Pau Monné 
4475779d8adSRoger Pau Monné 		error = xen_intr_alloc_and_bind_local_port(evtchn_dev,
4485779d8adSRoger Pau Monné 		    bind->remote_domain, NULL, NULL, NULL, 0, &evtchn->handle);
4495779d8adSRoger Pau Monné 		if (error != 0) {
4505779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4515779d8adSRoger Pau Monné 			break;
4525779d8adSRoger Pau Monné 		}
4535779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4545779d8adSRoger Pau Monné 		if (error != 0)
4555779d8adSRoger Pau Monné 			break;
4565779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4575779d8adSRoger Pau Monné 		break;
4585779d8adSRoger Pau Monné 	}
4595779d8adSRoger Pau Monné 
4605779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_UNBIND: {
4615779d8adSRoger Pau Monné 		struct ioctl_evtchn_unbind *unbind;
4625779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4635779d8adSRoger Pau Monné 
4645779d8adSRoger Pau Monné 		unbind = (struct ioctl_evtchn_unbind *)arg;
4655779d8adSRoger Pau Monné 
4665779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
4675779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, unbind->port);
4685779d8adSRoger Pau Monné 		if (evtchn == NULL) {
4695779d8adSRoger Pau Monné 			error = ENOTCONN;
4705779d8adSRoger Pau Monné 			break;
4715779d8adSRoger Pau Monné 		}
4725779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
4735779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
474791ca590SRoger Pau Monné 
475791ca590SRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
4765779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
4775779d8adSRoger Pau Monné 		error = 0;
4785779d8adSRoger Pau Monné 		break;
4795779d8adSRoger Pau Monné 	}
4805779d8adSRoger Pau Monné 
4815779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_NOTIFY: {
4825779d8adSRoger Pau Monné 		struct ioctl_evtchn_notify *notify;
4835779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4845779d8adSRoger Pau Monné 
4855779d8adSRoger Pau Monné 		notify = (struct ioctl_evtchn_notify *)arg;
4865779d8adSRoger Pau Monné 
4875779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
4885779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, notify->port);
4895779d8adSRoger Pau Monné 		if (evtchn == NULL) {
4905779d8adSRoger Pau Monné 			error = ENOTCONN;
4915779d8adSRoger Pau Monné 			break;
4925779d8adSRoger Pau Monné 		}
4935779d8adSRoger Pau Monné 
4945779d8adSRoger Pau Monné 		xen_intr_signal(evtchn->handle);
4955779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
4965779d8adSRoger Pau Monné 		error = 0;
4975779d8adSRoger Pau Monné 		break;
4985779d8adSRoger Pau Monné 	}
4995779d8adSRoger Pau Monné 
5005779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_RESET: {
5015779d8adSRoger Pau Monné 		/* Initialise the ring to empty. Clear errors. */
5025779d8adSRoger Pau Monné 		sx_xlock(&u->ring_cons_mutex);
5035779d8adSRoger Pau Monné 		mtx_lock(&u->ring_prod_mutex);
5045779d8adSRoger Pau Monné 		u->ring_cons = u->ring_prod = u->ring_overflow = 0;
5055779d8adSRoger Pau Monné 		mtx_unlock(&u->ring_prod_mutex);
5065779d8adSRoger Pau Monné 		sx_xunlock(&u->ring_cons_mutex);
5075779d8adSRoger Pau Monné 		error = 0;
5085779d8adSRoger Pau Monné 		break;
5095779d8adSRoger Pau Monné 	}
5105779d8adSRoger Pau Monné 
5115779d8adSRoger Pau Monné 	case FIONBIO:
5125779d8adSRoger Pau Monné 	case FIOASYNC:
5135779d8adSRoger Pau Monné 		/* Handled in an upper layer */
5145779d8adSRoger Pau Monné 		error = 0;
5155779d8adSRoger Pau Monné 		break;
5165779d8adSRoger Pau Monné 
5175779d8adSRoger Pau Monné 	default:
5185779d8adSRoger Pau Monné 		error = ENOTTY;
5195779d8adSRoger Pau Monné 		break;
5205779d8adSRoger Pau Monné 	}
5215779d8adSRoger Pau Monné 
5225779d8adSRoger Pau Monné 	return (error);
5235779d8adSRoger Pau Monné }
5245779d8adSRoger Pau Monné 
5255779d8adSRoger Pau Monné static int
evtchn_poll(struct cdev * dev,int events,struct thread * td)5265779d8adSRoger Pau Monné evtchn_poll(struct cdev *dev, int events, struct thread *td)
5275779d8adSRoger Pau Monné {
5285779d8adSRoger Pau Monné 	struct per_user_data *u;
5295779d8adSRoger Pau Monné 	int error, mask;
5305779d8adSRoger Pau Monné 
5315779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
5325779d8adSRoger Pau Monné 	if (error != 0)
5335779d8adSRoger Pau Monné 		return (POLLERR);
5345779d8adSRoger Pau Monné 
5355779d8adSRoger Pau Monné 	/* we can always write */
5365779d8adSRoger Pau Monné 	mask = events & (POLLOUT | POLLWRNORM);
5375779d8adSRoger Pau Monné 
5385779d8adSRoger Pau Monné 	mtx_lock(&u->ring_prod_mutex);
5395779d8adSRoger Pau Monné 	if (events & (POLLIN | POLLRDNORM)) {
5405779d8adSRoger Pau Monné 		if (u->ring_cons != u->ring_prod) {
5415779d8adSRoger Pau Monné 			mask |= events & (POLLIN | POLLRDNORM);
5425779d8adSRoger Pau Monné 		} else {
5435779d8adSRoger Pau Monné 			/* Record that someone is waiting */
5445779d8adSRoger Pau Monné 			selrecord(td, &u->ev_rsel);
5455779d8adSRoger Pau Monné 		}
5465779d8adSRoger Pau Monné 	}
5475779d8adSRoger Pau Monné 	mtx_unlock(&u->ring_prod_mutex);
5485779d8adSRoger Pau Monné 
5495779d8adSRoger Pau Monné 	return (mask);
5505779d8adSRoger Pau Monné }
5515779d8adSRoger Pau Monné 
5525779d8adSRoger Pau Monné /*------------------ Private Device Attachment Functions  --------------------*/
5535779d8adSRoger Pau Monné static void
evtchn_identify(driver_t * driver,device_t parent)5545779d8adSRoger Pau Monné evtchn_identify(driver_t *driver, device_t parent)
5555779d8adSRoger Pau Monné {
5565779d8adSRoger Pau Monné 
5575779d8adSRoger Pau Monné 	KASSERT((xen_domain()),
5585779d8adSRoger Pau Monné 	    ("Trying to attach evtchn device on non Xen domain"));
5595779d8adSRoger Pau Monné 
5605779d8adSRoger Pau Monné 	evtchn_dev = BUS_ADD_CHILD(parent, 0, "evtchn", 0);
5615779d8adSRoger Pau Monné 	if (evtchn_dev == NULL)
5625779d8adSRoger Pau Monné 		panic("unable to attach evtchn user-space device");
5635779d8adSRoger Pau Monné }
5645779d8adSRoger Pau Monné 
5655779d8adSRoger Pau Monné static int
evtchn_probe(device_t dev)5665779d8adSRoger Pau Monné evtchn_probe(device_t dev)
5675779d8adSRoger Pau Monné {
5685779d8adSRoger Pau Monné 
5695779d8adSRoger Pau Monné 	device_set_desc(dev, "Xen event channel user-space device");
5705779d8adSRoger Pau Monné 	return (BUS_PROBE_NOWILDCARD);
5715779d8adSRoger Pau Monné }
5725779d8adSRoger Pau Monné 
5735779d8adSRoger Pau Monné static int
evtchn_attach(device_t dev)5745779d8adSRoger Pau Monné evtchn_attach(device_t dev)
5755779d8adSRoger Pau Monné {
5765779d8adSRoger Pau Monné 
5775779d8adSRoger Pau Monné 	make_dev_credf(MAKEDEV_ETERNAL, &evtchn_devsw, 0, NULL, UID_ROOT,
5785779d8adSRoger Pau Monné 	    GID_WHEEL, 0600, "xen/evtchn");
5795779d8adSRoger Pau Monné 	return (0);
5805779d8adSRoger Pau Monné }
5815779d8adSRoger Pau Monné 
5825779d8adSRoger Pau Monné /*-------------------- Private Device Attachment Data  -----------------------*/
5835779d8adSRoger Pau Monné static device_method_t evtchn_methods[] = {
5845779d8adSRoger Pau Monné 	DEVMETHOD(device_identify, evtchn_identify),
5855779d8adSRoger Pau Monné 	DEVMETHOD(device_probe, evtchn_probe),
5865779d8adSRoger Pau Monné 	DEVMETHOD(device_attach, evtchn_attach),
5875779d8adSRoger Pau Monné 
5885779d8adSRoger Pau Monné 	DEVMETHOD_END
5895779d8adSRoger Pau Monné };
5905779d8adSRoger Pau Monné 
5915779d8adSRoger Pau Monné static driver_t evtchn_driver = {
5925779d8adSRoger Pau Monné 	"evtchn",
5935779d8adSRoger Pau Monné 	evtchn_methods,
5945779d8adSRoger Pau Monné 	0,
5955779d8adSRoger Pau Monné };
5965779d8adSRoger Pau Monné 
597*f929eb1eSJohn Baldwin DRIVER_MODULE(evtchn, xenpv, evtchn_driver, 0, 0);
5985779d8adSRoger Pau Monné MODULE_DEPEND(evtchn, xenpv, 1, 1, 1);
599