xref: /freebsd/sys/dev/xen/evtchn/evtchn_dev.c (revision ca7af67ac958d3749ebfc4771cecf0cd7525e6a0)
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/cdefs.h>
395779d8adSRoger Pau Monné __FBSDID("$FreeBSD$");
405779d8adSRoger Pau Monné 
415779d8adSRoger Pau Monné #include <sys/param.h>
425779d8adSRoger Pau Monné #include <sys/systm.h>
435779d8adSRoger Pau Monné #include <sys/uio.h>
445779d8adSRoger Pau Monné #include <sys/bus.h>
455779d8adSRoger Pau Monné #include <sys/malloc.h>
465779d8adSRoger Pau Monné #include <sys/kernel.h>
475779d8adSRoger Pau Monné #include <sys/lock.h>
485779d8adSRoger Pau Monné #include <sys/mutex.h>
495779d8adSRoger Pau Monné #include <sys/sx.h>
505779d8adSRoger Pau Monné #include <sys/selinfo.h>
515779d8adSRoger Pau Monné #include <sys/poll.h>
525779d8adSRoger Pau Monné #include <sys/conf.h>
535779d8adSRoger Pau Monné #include <sys/fcntl.h>
545779d8adSRoger Pau Monné #include <sys/ioccom.h>
555779d8adSRoger Pau Monné #include <sys/rman.h>
565779d8adSRoger Pau Monné #include <sys/tree.h>
575779d8adSRoger Pau Monné #include <sys/module.h>
585779d8adSRoger Pau Monné #include <sys/filio.h>
595779d8adSRoger Pau Monné #include <sys/vnode.h>
605779d8adSRoger Pau Monné 
615779d8adSRoger Pau Monné #include <machine/intr_machdep.h>
625779d8adSRoger Pau Monné #include <machine/xen/synch_bitops.h>
635779d8adSRoger Pau Monné 
645779d8adSRoger Pau Monné #include <xen/xen-os.h>
655779d8adSRoger Pau Monné #include <xen/evtchn.h>
665779d8adSRoger Pau Monné #include <xen/xen_intr.h>
675779d8adSRoger Pau Monné 
685779d8adSRoger Pau Monné #include <xen/evtchn/evtchnvar.h>
695779d8adSRoger Pau Monné 
705779d8adSRoger Pau Monné MALLOC_DEFINE(M_EVTCHN, "evtchn_dev", "Xen event channel user-space device");
715779d8adSRoger Pau Monné 
725779d8adSRoger Pau Monné struct user_evtchn;
735779d8adSRoger Pau Monné 
745779d8adSRoger Pau Monné static int evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2);
755779d8adSRoger Pau Monné 
765779d8adSRoger Pau Monné RB_HEAD(evtchn_tree, user_evtchn);
775779d8adSRoger Pau Monné 
785779d8adSRoger Pau Monné struct per_user_data {
795779d8adSRoger Pau Monné 	struct mtx bind_mutex; /* serialize bind/unbind operations */
805779d8adSRoger Pau Monné 	struct evtchn_tree evtchns;
815779d8adSRoger Pau Monné 
825779d8adSRoger Pau Monné 	/* Notification ring, accessed via /dev/xen/evtchn. */
835779d8adSRoger Pau Monné #define EVTCHN_RING_SIZE     (PAGE_SIZE / sizeof(evtchn_port_t))
845779d8adSRoger Pau Monné #define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
855779d8adSRoger Pau Monné 	evtchn_port_t *ring;
865779d8adSRoger Pau Monné 	unsigned int ring_cons, ring_prod, ring_overflow;
875779d8adSRoger Pau Monné 	struct sx ring_cons_mutex; /* protect against concurrent readers */
885779d8adSRoger Pau Monné 	struct mtx ring_prod_mutex; /* product against concurrent interrupts */
895779d8adSRoger Pau Monné 	struct selinfo ev_rsel;
905779d8adSRoger Pau Monné };
915779d8adSRoger Pau Monné 
925779d8adSRoger Pau Monné struct user_evtchn {
935779d8adSRoger Pau Monné 	RB_ENTRY(user_evtchn) node;
945779d8adSRoger Pau Monné 	struct per_user_data *user;
955779d8adSRoger Pau Monné 	evtchn_port_t port;
965779d8adSRoger Pau Monné 	xen_intr_handle_t handle;
975779d8adSRoger Pau Monné 	bool enabled;
985779d8adSRoger Pau Monné };
995779d8adSRoger Pau Monné 
1005779d8adSRoger Pau Monné RB_GENERATE_STATIC(evtchn_tree, user_evtchn, node, evtchn_cmp);
1015779d8adSRoger Pau Monné 
1025779d8adSRoger Pau Monné static device_t evtchn_dev;
1035779d8adSRoger Pau Monné 
1045779d8adSRoger Pau Monné static d_read_t      evtchn_read;
1055779d8adSRoger Pau Monné static d_write_t     evtchn_write;
1065779d8adSRoger Pau Monné static d_ioctl_t     evtchn_ioctl;
1075779d8adSRoger Pau Monné static d_poll_t      evtchn_poll;
1085779d8adSRoger Pau Monné static d_open_t      evtchn_open;
1095779d8adSRoger Pau Monné 
1105779d8adSRoger Pau Monné static void evtchn_release(void *arg);
1115779d8adSRoger Pau Monné 
1125779d8adSRoger Pau Monné static struct cdevsw evtchn_devsw = {
1135779d8adSRoger Pau Monné 	.d_version = D_VERSION,
1145779d8adSRoger Pau Monné 	.d_open = evtchn_open,
1155779d8adSRoger Pau Monné 	.d_read = evtchn_read,
1165779d8adSRoger Pau Monné 	.d_write = evtchn_write,
1175779d8adSRoger Pau Monné 	.d_ioctl = evtchn_ioctl,
1185779d8adSRoger Pau Monné 	.d_poll = evtchn_poll,
1195779d8adSRoger Pau Monné 	.d_name = "evtchn",
1205779d8adSRoger Pau Monné };
1215779d8adSRoger Pau Monné 
1225779d8adSRoger Pau Monné /*------------------------- Red-black tree helpers ---------------------------*/
1235779d8adSRoger Pau Monné static int
1245779d8adSRoger Pau Monné evtchn_cmp(struct user_evtchn *u1, struct user_evtchn *u2)
1255779d8adSRoger Pau Monné {
1265779d8adSRoger Pau Monné 
1275779d8adSRoger Pau Monné 	return (u1->port - u2->port);
1285779d8adSRoger Pau Monné }
1295779d8adSRoger Pau Monné 
1305779d8adSRoger Pau Monné static struct user_evtchn *
1315779d8adSRoger Pau Monné find_evtchn(struct per_user_data *u, evtchn_port_t port)
1325779d8adSRoger Pau Monné {
1335779d8adSRoger Pau Monné 	struct user_evtchn tmp = {
1345779d8adSRoger Pau Monné 		.port = port,
1355779d8adSRoger Pau Monné 	};
1365779d8adSRoger Pau Monné 
1375779d8adSRoger Pau Monné 	return (RB_FIND(evtchn_tree, &u->evtchns, &tmp));
1385779d8adSRoger Pau Monné }
1395779d8adSRoger Pau Monné 
1405779d8adSRoger Pau Monné /*--------------------------- Interrupt handlers -----------------------------*/
1415779d8adSRoger Pau Monné static int
1425779d8adSRoger Pau Monné evtchn_filter(void *arg)
1435779d8adSRoger Pau Monné {
1445779d8adSRoger Pau Monné 	struct user_evtchn *evtchn;
1455779d8adSRoger Pau Monné 
1465779d8adSRoger Pau Monné 	evtchn = arg;
1475779d8adSRoger Pau Monné 
1485779d8adSRoger Pau Monné 	if (!evtchn->enabled && bootverbose) {
1495779d8adSRoger Pau Monné 		device_printf(evtchn_dev,
1505779d8adSRoger Pau Monné 		    "Received upcall for disabled event channel %d\n",
1515779d8adSRoger Pau Monné 		    evtchn->port);
1525779d8adSRoger Pau Monné 	}
1535779d8adSRoger Pau Monné 
1545779d8adSRoger Pau Monné 	evtchn_mask_port(evtchn->port);
1555779d8adSRoger Pau Monné 	evtchn->enabled = false;
1565779d8adSRoger Pau Monné 
1575779d8adSRoger Pau Monné 	return (FILTER_SCHEDULE_THREAD);
1585779d8adSRoger Pau Monné }
1595779d8adSRoger Pau Monné 
1605779d8adSRoger Pau Monné static void
1615779d8adSRoger Pau Monné evtchn_interrupt(void *arg)
1625779d8adSRoger Pau Monné {
1635779d8adSRoger Pau Monné 	struct user_evtchn *evtchn;
1645779d8adSRoger Pau Monné 	struct per_user_data *u;
1655779d8adSRoger Pau Monné 
1665779d8adSRoger Pau Monné 	evtchn = arg;
1675779d8adSRoger Pau Monné 	u = evtchn->user;
1685779d8adSRoger Pau Monné 
1695779d8adSRoger Pau Monné 	/*
1705779d8adSRoger Pau Monné 	 * Protect against concurrent events using this handler
1715779d8adSRoger Pau Monné 	 * on different CPUs.
1725779d8adSRoger Pau Monné 	 */
1735779d8adSRoger Pau Monné 	mtx_lock(&u->ring_prod_mutex);
1745779d8adSRoger Pau Monné 	if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
1755779d8adSRoger Pau Monné 		u->ring[EVTCHN_RING_MASK(u->ring_prod)] = evtchn->port;
1765779d8adSRoger Pau Monné 		wmb(); /* Ensure ring contents visible */
1775779d8adSRoger Pau Monné 		if (u->ring_cons == u->ring_prod++) {
1785779d8adSRoger Pau Monné 			wakeup(u);
1795779d8adSRoger Pau Monné 			selwakeup(&u->ev_rsel);
1805779d8adSRoger Pau Monné 		}
1815779d8adSRoger Pau Monné 	} else
1825779d8adSRoger Pau Monné 		u->ring_overflow = 1;
1835779d8adSRoger Pau Monné 	mtx_unlock(&u->ring_prod_mutex);
1845779d8adSRoger Pau Monné }
1855779d8adSRoger Pau Monné 
1865779d8adSRoger Pau Monné /*------------------------- Character device methods -------------------------*/
1875779d8adSRoger Pau Monné static int
1885779d8adSRoger Pau Monné evtchn_open(struct cdev *dev, int flag, int otyp, struct thread *td)
1895779d8adSRoger Pau Monné {
1905779d8adSRoger Pau Monné 	struct per_user_data *u;
1915779d8adSRoger Pau Monné 	int error;
1925779d8adSRoger Pau Monné 
1935779d8adSRoger Pau Monné 	u = malloc(sizeof(*u), M_EVTCHN, M_WAITOK | M_ZERO);
1945779d8adSRoger Pau Monné 	u->ring = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK | M_ZERO);
1955779d8adSRoger Pau Monné 
1965779d8adSRoger Pau Monné 	/* Initialize locks */
1975779d8adSRoger Pau Monné 	mtx_init(&u->bind_mutex, "evtchn_bind_mutex", NULL, MTX_DEF);
1985779d8adSRoger Pau Monné 	sx_init(&u->ring_cons_mutex, "evtchn_ringc_sx");
1995779d8adSRoger Pau Monné 	mtx_init(&u->ring_prod_mutex, "evtchn_ringp_mutex", NULL, MTX_DEF);
2005779d8adSRoger Pau Monné 
2015779d8adSRoger Pau Monné 	/* Initialize red-black tree. */
2025779d8adSRoger Pau Monné 	RB_INIT(&u->evtchns);
2035779d8adSRoger Pau Monné 
2045779d8adSRoger Pau Monné 	/* Assign the allocated per_user_data to this open instance. */
2055779d8adSRoger Pau Monné 	error = devfs_set_cdevpriv(u, evtchn_release);
2065779d8adSRoger Pau Monné 	if (error != 0) {
2075779d8adSRoger Pau Monné 		mtx_destroy(&u->bind_mutex);
2085779d8adSRoger Pau Monné 		mtx_destroy(&u->ring_prod_mutex);
2095779d8adSRoger Pau Monné 		sx_destroy(&u->ring_cons_mutex);
2105779d8adSRoger Pau Monné 		free(u->ring, M_EVTCHN);
2115779d8adSRoger Pau Monné 		free(u, M_EVTCHN);
2125779d8adSRoger Pau Monné 	}
2135779d8adSRoger Pau Monné 
2145779d8adSRoger Pau Monné 	return (error);
2155779d8adSRoger Pau Monné }
2165779d8adSRoger Pau Monné 
2175779d8adSRoger Pau Monné static void
2185779d8adSRoger Pau Monné evtchn_release(void *arg)
2195779d8adSRoger Pau Monné {
2205779d8adSRoger Pau Monné 	struct per_user_data *u;
2215779d8adSRoger Pau Monné 	struct user_evtchn *evtchn, *tmp;
2225779d8adSRoger Pau Monné 
2235779d8adSRoger Pau Monné 	u = arg;
2245779d8adSRoger Pau Monné 
2255779d8adSRoger Pau Monné 	seldrain(&u->ev_rsel);
2265779d8adSRoger Pau Monné 
2275779d8adSRoger Pau Monné 	RB_FOREACH_SAFE(evtchn, evtchn_tree, &u->evtchns, tmp) {
2285779d8adSRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
2295779d8adSRoger Pau Monné 
2305779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
2315779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
2325779d8adSRoger Pau Monné 	}
2335779d8adSRoger Pau Monné 
2345779d8adSRoger Pau Monné 	mtx_destroy(&u->bind_mutex);
2355779d8adSRoger Pau Monné 	mtx_destroy(&u->ring_prod_mutex);
2365779d8adSRoger Pau Monné 	sx_destroy(&u->ring_cons_mutex);
2375779d8adSRoger Pau Monné 	free(u->ring, M_EVTCHN);
2385779d8adSRoger Pau Monné 	free(u, M_EVTCHN);
2395779d8adSRoger Pau Monné }
2405779d8adSRoger Pau Monné 
2415779d8adSRoger Pau Monné static int
2425779d8adSRoger Pau Monné evtchn_read(struct cdev *dev, struct uio *uio, int ioflag)
2435779d8adSRoger Pau Monné {
2445779d8adSRoger Pau Monné 	int error, count;
2455779d8adSRoger Pau Monné 	unsigned int c, p, bytes1 = 0, bytes2 = 0;
2465779d8adSRoger Pau Monné 	struct per_user_data *u;
2475779d8adSRoger Pau Monné 
2485779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
2495779d8adSRoger Pau Monné 	if (error != 0)
2505779d8adSRoger Pau Monné 		return (EINVAL);
2515779d8adSRoger Pau Monné 
2525779d8adSRoger Pau Monné 	/* Whole number of ports. */
2535779d8adSRoger Pau Monné 	count = uio->uio_resid;
2545779d8adSRoger Pau Monné 	count &= ~(sizeof(evtchn_port_t)-1);
2555779d8adSRoger Pau Monné 
2565779d8adSRoger Pau Monné 	if (count == 0)
2575779d8adSRoger Pau Monné 		return (0);
2585779d8adSRoger Pau Monné 
2595779d8adSRoger Pau Monné 	if (count > PAGE_SIZE)
2605779d8adSRoger Pau Monné 		count = PAGE_SIZE;
2615779d8adSRoger Pau Monné 
2625779d8adSRoger Pau Monné 	sx_xlock(&u->ring_cons_mutex);
2635779d8adSRoger Pau Monné 	for (;;) {
2645779d8adSRoger Pau Monné 		error = EFBIG;
2655779d8adSRoger Pau Monné 		if (u->ring_overflow)
2665779d8adSRoger Pau Monné 			goto unlock_out;
2675779d8adSRoger Pau Monné 
2685779d8adSRoger Pau Monné 		c = u->ring_cons;
2695779d8adSRoger Pau Monné 		p = u->ring_prod;
2705779d8adSRoger Pau Monné 		if (c != p)
2715779d8adSRoger Pau Monné 			break;
2725779d8adSRoger Pau Monné 
2735779d8adSRoger Pau Monné 		if (ioflag & IO_NDELAY) {
2745779d8adSRoger Pau Monné 			sx_xunlock(&u->ring_cons_mutex);
2755779d8adSRoger Pau Monné 			return (EWOULDBLOCK);
2765779d8adSRoger Pau Monné 		}
2775779d8adSRoger Pau Monné 
2785779d8adSRoger Pau Monné 		error = sx_sleep(u, &u->ring_cons_mutex, PCATCH, "evtchw", 0);
2795779d8adSRoger Pau Monné 		if ((error != 0) && (error != EWOULDBLOCK))
2805779d8adSRoger Pau Monné 			return (error);
2815779d8adSRoger Pau Monné 	}
2825779d8adSRoger Pau Monné 
2835779d8adSRoger Pau Monné 	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
2845779d8adSRoger Pau Monné 	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
2855779d8adSRoger Pau Monné 		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
2865779d8adSRoger Pau Monné 		    sizeof(evtchn_port_t);
2875779d8adSRoger Pau Monné 		bytes2 = EVTCHN_RING_MASK(p) * sizeof(evtchn_port_t);
2885779d8adSRoger Pau Monné 	} else {
2895779d8adSRoger Pau Monné 		bytes1 = (p - c) * sizeof(evtchn_port_t);
2905779d8adSRoger Pau Monné 		bytes2 = 0;
2915779d8adSRoger Pau Monné 	}
2925779d8adSRoger Pau Monné 
2935779d8adSRoger Pau Monné 	/* Truncate chunks according to caller's maximum byte count. */
2945779d8adSRoger Pau Monné 	if (bytes1 > count) {
2955779d8adSRoger Pau Monné 		bytes1 = count;
2965779d8adSRoger Pau Monné 		bytes2 = 0;
2975779d8adSRoger Pau Monné 	} else if ((bytes1 + bytes2) > count) {
2985779d8adSRoger Pau Monné 		bytes2 = count - bytes1;
2995779d8adSRoger Pau Monné 	}
3005779d8adSRoger Pau Monné 
3015779d8adSRoger Pau Monné 	error = EFAULT;
3025779d8adSRoger Pau Monné 	rmb(); /* Ensure that we see the port before we copy it. */
3035779d8adSRoger Pau Monné 
3045779d8adSRoger Pau Monné 	if (uiomove(&u->ring[EVTCHN_RING_MASK(c)], bytes1, uio) ||
3055779d8adSRoger Pau Monné 	    ((bytes2 != 0) && uiomove(&u->ring[0], bytes2, uio)))
3065779d8adSRoger Pau Monné 		goto unlock_out;
3075779d8adSRoger Pau Monné 
3085779d8adSRoger Pau Monné 	u->ring_cons += (bytes1 + bytes2) / sizeof(evtchn_port_t);
3095779d8adSRoger Pau Monné 	error = 0;
3105779d8adSRoger Pau Monné 
3115779d8adSRoger Pau Monné unlock_out:
3125779d8adSRoger Pau Monné 	sx_xunlock(&u->ring_cons_mutex);
3135779d8adSRoger Pau Monné 	return (error);
3145779d8adSRoger Pau Monné }
3155779d8adSRoger Pau Monné 
3165779d8adSRoger Pau Monné static int
3175779d8adSRoger Pau Monné evtchn_write(struct cdev *dev, struct uio *uio, int ioflag)
3185779d8adSRoger Pau Monné {
3195779d8adSRoger Pau Monné 	int error, i, count;
3205779d8adSRoger Pau Monné 	evtchn_port_t *kbuf;
3215779d8adSRoger Pau Monné 	struct per_user_data *u;
3225779d8adSRoger Pau Monné 
3235779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
3245779d8adSRoger Pau Monné 	if (error != 0)
3255779d8adSRoger Pau Monné 		return (EINVAL);
3265779d8adSRoger Pau Monné 
3275779d8adSRoger Pau Monné 	kbuf = malloc(PAGE_SIZE, M_EVTCHN, M_WAITOK);
3285779d8adSRoger Pau Monné 
3295779d8adSRoger Pau Monné 	count = uio->uio_resid;
3305779d8adSRoger Pau Monné 	/* Whole number of ports. */
3315779d8adSRoger Pau Monné 	count &= ~(sizeof(evtchn_port_t)-1);
3325779d8adSRoger Pau Monné 
3335779d8adSRoger Pau Monné 	error = 0;
3345779d8adSRoger Pau Monné 	if (count == 0)
3355779d8adSRoger Pau Monné 		goto out;
3365779d8adSRoger Pau Monné 
3375779d8adSRoger Pau Monné 	if (count > PAGE_SIZE)
3385779d8adSRoger Pau Monné 		count = PAGE_SIZE;
3395779d8adSRoger Pau Monné 
3405779d8adSRoger Pau Monné 	error = uiomove(kbuf, count, uio);
3415779d8adSRoger Pau Monné 	if (error != 0)
3425779d8adSRoger Pau Monné 		goto out;
3435779d8adSRoger Pau Monné 
3445779d8adSRoger Pau Monné 	mtx_lock(&u->bind_mutex);
3455779d8adSRoger Pau Monné 
3465779d8adSRoger Pau Monné 	for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) {
3475779d8adSRoger Pau Monné 		evtchn_port_t port = kbuf[i];
3485779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
3495779d8adSRoger Pau Monné 
3505779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, port);
3515779d8adSRoger Pau Monné 		if (evtchn && !evtchn->enabled) {
3525779d8adSRoger Pau Monné 			evtchn->enabled = true;
3535779d8adSRoger Pau Monné 			evtchn_unmask_port(evtchn->port);
3545779d8adSRoger Pau Monné 		}
3555779d8adSRoger Pau Monné 	}
3565779d8adSRoger Pau Monné 
3575779d8adSRoger Pau Monné 	mtx_unlock(&u->bind_mutex);
3585779d8adSRoger Pau Monné 	error = 0;
3595779d8adSRoger Pau Monné 
3605779d8adSRoger Pau Monné out:
3615779d8adSRoger Pau Monné 	free(kbuf, M_EVTCHN);
3625779d8adSRoger Pau Monné 	return (error);
3635779d8adSRoger Pau Monné }
3645779d8adSRoger Pau Monné 
3655779d8adSRoger Pau Monné static inline int
3665779d8adSRoger Pau Monné evtchn_bind_user_port(struct per_user_data *u, struct user_evtchn *evtchn)
3675779d8adSRoger Pau Monné {
3685779d8adSRoger Pau Monné 	int error;
3695779d8adSRoger Pau Monné 
3705779d8adSRoger Pau Monné 	evtchn->port = xen_intr_port(evtchn->handle);
3715779d8adSRoger Pau Monné 	evtchn->user = u;
3725779d8adSRoger Pau Monné 	evtchn->enabled = true;
3735779d8adSRoger Pau Monné 	mtx_lock(&u->bind_mutex);
3745779d8adSRoger Pau Monné 	RB_INSERT(evtchn_tree, &u->evtchns, evtchn);
3755779d8adSRoger Pau Monné 	mtx_unlock(&u->bind_mutex);
376*ca7af67aSRoger Pau Monné 	error = xen_intr_add_handler(device_get_nameunit(evtchn_dev),
377*ca7af67aSRoger Pau Monné 	    evtchn_filter, evtchn_interrupt, evtchn,
378*ca7af67aSRoger Pau Monné 	    INTR_TYPE_MISC | INTR_MPSAFE, evtchn->handle);
3795779d8adSRoger Pau Monné 	if (error != 0) {
3805779d8adSRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
3815779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
3825779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
3835779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
3845779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
3855779d8adSRoger Pau Monné 	}
3865779d8adSRoger Pau Monné 	return (error);
3875779d8adSRoger Pau Monné }
3885779d8adSRoger Pau Monné 
3895779d8adSRoger Pau Monné static int
3905779d8adSRoger Pau Monné evtchn_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg,
3915779d8adSRoger Pau Monné     int mode, struct thread *td __unused)
3925779d8adSRoger Pau Monné {
3935779d8adSRoger Pau Monné 	struct per_user_data *u;
3945779d8adSRoger Pau Monné 	int error;
3955779d8adSRoger Pau Monné 
3965779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
3975779d8adSRoger Pau Monné 	if (error != 0)
3985779d8adSRoger Pau Monné 		return (EINVAL);
3995779d8adSRoger Pau Monné 
4005779d8adSRoger Pau Monné 	switch (cmd) {
4015779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_VIRQ: {
4025779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_virq *bind;
4035779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4045779d8adSRoger Pau Monné 
4055779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4065779d8adSRoger Pau Monné 
4075779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_virq *)arg;
4085779d8adSRoger Pau Monné 
4095779d8adSRoger Pau Monné 		error = xen_intr_bind_virq(evtchn_dev, bind->virq, 0,
4105779d8adSRoger Pau Monné 		    NULL, NULL, NULL, 0, &evtchn->handle);
4115779d8adSRoger Pau Monné 		if (error != 0) {
4125779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4135779d8adSRoger Pau Monné 			break;
4145779d8adSRoger Pau Monné 		}
4155779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4165779d8adSRoger Pau Monné 		if (error != 0)
4175779d8adSRoger Pau Monné 			break;
4185779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4195779d8adSRoger Pau Monné 		break;
4205779d8adSRoger Pau Monné 	}
4215779d8adSRoger Pau Monné 
4225779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
4235779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_interdomain *bind;
4245779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4255779d8adSRoger Pau Monné 
4265779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4275779d8adSRoger Pau Monné 
4285779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_interdomain *)arg;
4295779d8adSRoger Pau Monné 
4305779d8adSRoger Pau Monné 		error = xen_intr_bind_remote_port(evtchn_dev,
4315779d8adSRoger Pau Monné 		    bind->remote_domain, bind->remote_port, NULL,
4325779d8adSRoger Pau Monné 		    NULL, NULL, 0, &evtchn->handle);
4335779d8adSRoger Pau Monné 		if (error != 0) {
4345779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4355779d8adSRoger Pau Monné 			break;
4365779d8adSRoger Pau Monné 		}
4375779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4385779d8adSRoger Pau Monné 		if (error != 0)
4395779d8adSRoger Pau Monné 			break;
4405779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4415779d8adSRoger Pau Monné 		break;
4425779d8adSRoger Pau Monné 	}
4435779d8adSRoger Pau Monné 
4445779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
4455779d8adSRoger Pau Monné 		struct ioctl_evtchn_bind_unbound_port *bind;
4465779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4475779d8adSRoger Pau Monné 
4485779d8adSRoger Pau Monné 		evtchn = malloc(sizeof(*evtchn), M_EVTCHN, M_WAITOK | M_ZERO);
4495779d8adSRoger Pau Monné 
4505779d8adSRoger Pau Monné 		bind = (struct ioctl_evtchn_bind_unbound_port *)arg;
4515779d8adSRoger Pau Monné 
4525779d8adSRoger Pau Monné 		error = xen_intr_alloc_and_bind_local_port(evtchn_dev,
4535779d8adSRoger Pau Monné 		    bind->remote_domain, NULL, NULL, NULL, 0, &evtchn->handle);
4545779d8adSRoger Pau Monné 		if (error != 0) {
4555779d8adSRoger Pau Monné 			free(evtchn, M_EVTCHN);
4565779d8adSRoger Pau Monné 			break;
4575779d8adSRoger Pau Monné 		}
4585779d8adSRoger Pau Monné 		error = evtchn_bind_user_port(u, evtchn);
4595779d8adSRoger Pau Monné 		if (error != 0)
4605779d8adSRoger Pau Monné 			break;
4615779d8adSRoger Pau Monné 		bind->port = evtchn->port;
4625779d8adSRoger Pau Monné 		break;
4635779d8adSRoger Pau Monné 	}
4645779d8adSRoger Pau Monné 
4655779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_UNBIND: {
4665779d8adSRoger Pau Monné 		struct ioctl_evtchn_unbind *unbind;
4675779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4685779d8adSRoger Pau Monné 
4695779d8adSRoger Pau Monné 		unbind = (struct ioctl_evtchn_unbind *)arg;
4705779d8adSRoger Pau Monné 
4715779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
4725779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, unbind->port);
4735779d8adSRoger Pau Monné 		if (evtchn == NULL) {
4745779d8adSRoger Pau Monné 			error = ENOTCONN;
4755779d8adSRoger Pau Monné 			break;
4765779d8adSRoger Pau Monné 		}
4775779d8adSRoger Pau Monné 
4785779d8adSRoger Pau Monné 		xen_intr_unbind(&evtchn->handle);
4795779d8adSRoger Pau Monné 		RB_REMOVE(evtchn_tree, &u->evtchns, evtchn);
4805779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
4815779d8adSRoger Pau Monné 		free(evtchn, M_EVTCHN);
4825779d8adSRoger Pau Monné 		error = 0;
4835779d8adSRoger Pau Monné 		break;
4845779d8adSRoger Pau Monné 	}
4855779d8adSRoger Pau Monné 
4865779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_NOTIFY: {
4875779d8adSRoger Pau Monné 		struct ioctl_evtchn_notify *notify;
4885779d8adSRoger Pau Monné 		struct user_evtchn *evtchn;
4895779d8adSRoger Pau Monné 
4905779d8adSRoger Pau Monné 		notify = (struct ioctl_evtchn_notify *)arg;
4915779d8adSRoger Pau Monné 
4925779d8adSRoger Pau Monné 		mtx_lock(&u->bind_mutex);
4935779d8adSRoger Pau Monné 		evtchn = find_evtchn(u, notify->port);
4945779d8adSRoger Pau Monné 		if (evtchn == NULL) {
4955779d8adSRoger Pau Monné 			error = ENOTCONN;
4965779d8adSRoger Pau Monné 			break;
4975779d8adSRoger Pau Monné 		}
4985779d8adSRoger Pau Monné 
4995779d8adSRoger Pau Monné 		xen_intr_signal(evtchn->handle);
5005779d8adSRoger Pau Monné 		mtx_unlock(&u->bind_mutex);
5015779d8adSRoger Pau Monné 		error = 0;
5025779d8adSRoger Pau Monné 		break;
5035779d8adSRoger Pau Monné 	}
5045779d8adSRoger Pau Monné 
5055779d8adSRoger Pau Monné 	case IOCTL_EVTCHN_RESET: {
5065779d8adSRoger Pau Monné 		/* Initialise the ring to empty. Clear errors. */
5075779d8adSRoger Pau Monné 		sx_xlock(&u->ring_cons_mutex);
5085779d8adSRoger Pau Monné 		mtx_lock(&u->ring_prod_mutex);
5095779d8adSRoger Pau Monné 		u->ring_cons = u->ring_prod = u->ring_overflow = 0;
5105779d8adSRoger Pau Monné 		mtx_unlock(&u->ring_prod_mutex);
5115779d8adSRoger Pau Monné 		sx_xunlock(&u->ring_cons_mutex);
5125779d8adSRoger Pau Monné 		error = 0;
5135779d8adSRoger Pau Monné 		break;
5145779d8adSRoger Pau Monné 	}
5155779d8adSRoger Pau Monné 
5165779d8adSRoger Pau Monné 	case FIONBIO:
5175779d8adSRoger Pau Monné 	case FIOASYNC:
5185779d8adSRoger Pau Monné 		/* Handled in an upper layer */
5195779d8adSRoger Pau Monné 		error = 0;
5205779d8adSRoger Pau Monné 		break;
5215779d8adSRoger Pau Monné 
5225779d8adSRoger Pau Monné 	default:
5235779d8adSRoger Pau Monné 		error = ENOTTY;
5245779d8adSRoger Pau Monné 		break;
5255779d8adSRoger Pau Monné 	}
5265779d8adSRoger Pau Monné 
5275779d8adSRoger Pau Monné 	return (error);
5285779d8adSRoger Pau Monné }
5295779d8adSRoger Pau Monné 
5305779d8adSRoger Pau Monné static int
5315779d8adSRoger Pau Monné evtchn_poll(struct cdev *dev, int events, struct thread *td)
5325779d8adSRoger Pau Monné {
5335779d8adSRoger Pau Monné 	struct per_user_data *u;
5345779d8adSRoger Pau Monné 	int error, mask;
5355779d8adSRoger Pau Monné 
5365779d8adSRoger Pau Monné 	error = devfs_get_cdevpriv((void **)&u);
5375779d8adSRoger Pau Monné 	if (error != 0)
5385779d8adSRoger Pau Monné 		return (POLLERR);
5395779d8adSRoger Pau Monné 
5405779d8adSRoger Pau Monné 	/* we can always write */
5415779d8adSRoger Pau Monné 	mask = events & (POLLOUT | POLLWRNORM);
5425779d8adSRoger Pau Monné 
5435779d8adSRoger Pau Monné 	mtx_lock(&u->ring_prod_mutex);
5445779d8adSRoger Pau Monné 	if (events & (POLLIN | POLLRDNORM)) {
5455779d8adSRoger Pau Monné 		if (u->ring_cons != u->ring_prod) {
5465779d8adSRoger Pau Monné 			mask |= events & (POLLIN | POLLRDNORM);
5475779d8adSRoger Pau Monné 		} else {
5485779d8adSRoger Pau Monné 			/* Record that someone is waiting */
5495779d8adSRoger Pau Monné 			selrecord(td, &u->ev_rsel);
5505779d8adSRoger Pau Monné 		}
5515779d8adSRoger Pau Monné 	}
5525779d8adSRoger Pau Monné 	mtx_unlock(&u->ring_prod_mutex);
5535779d8adSRoger Pau Monné 
5545779d8adSRoger Pau Monné 	return (mask);
5555779d8adSRoger Pau Monné }
5565779d8adSRoger Pau Monné 
5575779d8adSRoger Pau Monné /*------------------ Private Device Attachment Functions  --------------------*/
5585779d8adSRoger Pau Monné static void
5595779d8adSRoger Pau Monné evtchn_identify(driver_t *driver, device_t parent)
5605779d8adSRoger Pau Monné {
5615779d8adSRoger Pau Monné 
5625779d8adSRoger Pau Monné 	KASSERT((xen_domain()),
5635779d8adSRoger Pau Monné 	    ("Trying to attach evtchn device on non Xen domain"));
5645779d8adSRoger Pau Monné 
5655779d8adSRoger Pau Monné 	evtchn_dev = BUS_ADD_CHILD(parent, 0, "evtchn", 0);
5665779d8adSRoger Pau Monné 	if (evtchn_dev == NULL)
5675779d8adSRoger Pau Monné 		panic("unable to attach evtchn user-space device");
5685779d8adSRoger Pau Monné }
5695779d8adSRoger Pau Monné 
5705779d8adSRoger Pau Monné static int
5715779d8adSRoger Pau Monné evtchn_probe(device_t dev)
5725779d8adSRoger Pau Monné {
5735779d8adSRoger Pau Monné 
5745779d8adSRoger Pau Monné 	device_set_desc(dev, "Xen event channel user-space device");
5755779d8adSRoger Pau Monné 	return (BUS_PROBE_NOWILDCARD);
5765779d8adSRoger Pau Monné }
5775779d8adSRoger Pau Monné 
5785779d8adSRoger Pau Monné static int
5795779d8adSRoger Pau Monné evtchn_attach(device_t dev)
5805779d8adSRoger Pau Monné {
5815779d8adSRoger Pau Monné 
5825779d8adSRoger Pau Monné 	make_dev_credf(MAKEDEV_ETERNAL, &evtchn_devsw, 0, NULL, UID_ROOT,
5835779d8adSRoger Pau Monné 	    GID_WHEEL, 0600, "xen/evtchn");
5845779d8adSRoger Pau Monné 	return (0);
5855779d8adSRoger Pau Monné }
5865779d8adSRoger Pau Monné 
5875779d8adSRoger Pau Monné /*-------------------- Private Device Attachment Data  -----------------------*/
5885779d8adSRoger Pau Monné static device_method_t evtchn_methods[] = {
5895779d8adSRoger Pau Monné 	DEVMETHOD(device_identify, evtchn_identify),
5905779d8adSRoger Pau Monné 	DEVMETHOD(device_probe, evtchn_probe),
5915779d8adSRoger Pau Monné 	DEVMETHOD(device_attach, evtchn_attach),
5925779d8adSRoger Pau Monné 
5935779d8adSRoger Pau Monné 	DEVMETHOD_END
5945779d8adSRoger Pau Monné };
5955779d8adSRoger Pau Monné 
5965779d8adSRoger Pau Monné static driver_t evtchn_driver = {
5975779d8adSRoger Pau Monné 	"evtchn",
5985779d8adSRoger Pau Monné 	evtchn_methods,
5995779d8adSRoger Pau Monné 	0,
6005779d8adSRoger Pau Monné };
6015779d8adSRoger Pau Monné 
6025779d8adSRoger Pau Monné devclass_t evtchn_devclass;
6035779d8adSRoger Pau Monné 
6045779d8adSRoger Pau Monné DRIVER_MODULE(evtchn, xenpv, evtchn_driver, evtchn_devclass, 0, 0);
6055779d8adSRoger Pau Monné MODULE_DEPEND(evtchn, xenpv, 1, 1, 1);
606