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