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