1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* 23*349b53ddSStuart Maybee * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26843e1988Sjohnlev 27843e1988Sjohnlev 28843e1988Sjohnlev /* 29843e1988Sjohnlev * evtchn.c 30843e1988Sjohnlev * 31843e1988Sjohnlev * Driver for receiving and demuxing event-channel signals. 32843e1988Sjohnlev * 33843e1988Sjohnlev * Copyright (c) 2004-2005, K A Fraser 34843e1988Sjohnlev * Multi-process extensions Copyright (c) 2004, Steven Smith 35843e1988Sjohnlev * 36843e1988Sjohnlev * This file may be distributed separately from the Linux kernel, or 37843e1988Sjohnlev * incorporated into other software packages, subject to the following license: 38843e1988Sjohnlev * 39843e1988Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy 40843e1988Sjohnlev * of this source file (the "Software"), to deal in the Software without 41843e1988Sjohnlev * restriction, including without limitation the rights to use, copy, modify, 42843e1988Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software, 43843e1988Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to 44843e1988Sjohnlev * the following conditions: 45843e1988Sjohnlev * 46843e1988Sjohnlev * The above copyright notice and this permission notice shall be included in 47843e1988Sjohnlev * all copies or substantial portions of the Software. 48843e1988Sjohnlev * 49843e1988Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50843e1988Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51843e1988Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52843e1988Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53843e1988Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 54843e1988Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 55843e1988Sjohnlev * IN THE SOFTWARE. 56843e1988Sjohnlev */ 57843e1988Sjohnlev 58843e1988Sjohnlev #include <sys/types.h> 59843e1988Sjohnlev #include <sys/hypervisor.h> 60843e1988Sjohnlev #include <sys/machsystm.h> 61843e1988Sjohnlev #include <sys/mutex.h> 62843e1988Sjohnlev #include <sys/evtchn_impl.h> 63843e1988Sjohnlev #include <sys/ddi_impldefs.h> 64843e1988Sjohnlev #include <sys/avintr.h> 65843e1988Sjohnlev #include <sys/cpuvar.h> 66843e1988Sjohnlev #include <sys/smp_impldefs.h> 67843e1988Sjohnlev #include <sys/archsystm.h> 68843e1988Sjohnlev #include <sys/sysmacros.h> 69843e1988Sjohnlev #include <sys/fcntl.h> 70843e1988Sjohnlev #include <sys/open.h> 71843e1988Sjohnlev #include <sys/stat.h> 72843e1988Sjohnlev #include <sys/psm.h> 73843e1988Sjohnlev #include <sys/cpu.h> 74843e1988Sjohnlev #include <sys/cmn_err.h> 75843e1988Sjohnlev #include <sys/xen_errno.h> 76b26a64aeSjohnlev #include <sys/policy.h> 77843e1988Sjohnlev #include <xen/sys/evtchn.h> 78843e1988Sjohnlev 79843e1988Sjohnlev /* Some handy macros */ 80843e1988Sjohnlev #define EVTCHNDRV_MINOR2INST(minor) ((int)(minor)) 81843e1988Sjohnlev #define EVTCHNDRV_DEFAULT_NCLONES 256 82843e1988Sjohnlev #define EVTCHNDRV_INST2SOFTS(inst) \ 83843e1988Sjohnlev (ddi_get_soft_state(evtchndrv_statep, (inst))) 84843e1988Sjohnlev 85843e1988Sjohnlev /* Soft state data structure for evtchn driver */ 86843e1988Sjohnlev struct evtsoftdata { 87843e1988Sjohnlev dev_info_t *dip; 88843e1988Sjohnlev /* Notification ring, accessed via /dev/xen/evtchn. */ 89843e1988Sjohnlev #define EVTCHN_RING_SIZE (PAGESIZE / sizeof (evtchn_port_t)) 90843e1988Sjohnlev #define EVTCHN_RING_MASK(_i) ((_i) & (EVTCHN_RING_SIZE - 1)) 91843e1988Sjohnlev evtchn_port_t *ring; 92843e1988Sjohnlev unsigned int ring_cons, ring_prod, ring_overflow; 93843e1988Sjohnlev 94*349b53ddSStuart Maybee kcondvar_t evtchn_wait; /* Processes wait on this when ring is empty. */ 95843e1988Sjohnlev kmutex_t evtchn_lock; 96843e1988Sjohnlev struct pollhead evtchn_pollhead; 97843e1988Sjohnlev 98*349b53ddSStuart Maybee pid_t pid; /* last pid to bind to this event channel. */ 99*349b53ddSStuart Maybee processorid_t cpu; /* cpu thread/evtchn is bound to */ 100843e1988Sjohnlev }; 101843e1988Sjohnlev 102843e1988Sjohnlev static void *evtchndrv_statep; 103843e1988Sjohnlev int evtchndrv_nclones = EVTCHNDRV_DEFAULT_NCLONES; 104843e1988Sjohnlev static int *evtchndrv_clone_tab; 105843e1988Sjohnlev static dev_info_t *evtchndrv_dip; 106843e1988Sjohnlev static kmutex_t evtchndrv_clone_tab_mutex; 107843e1988Sjohnlev 108843e1988Sjohnlev static int evtchndrv_detach(dev_info_t *, ddi_detach_cmd_t); 109843e1988Sjohnlev 110843e1988Sjohnlev /* Who's bound to each port? */ 111843e1988Sjohnlev static struct evtsoftdata *port_user[NR_EVENT_CHANNELS]; 112843e1988Sjohnlev static kmutex_t port_user_lock; 113843e1988Sjohnlev 114843e1988Sjohnlev void 115843e1988Sjohnlev evtchn_device_upcall() 116843e1988Sjohnlev { 117843e1988Sjohnlev struct evtsoftdata *ep; 118843e1988Sjohnlev int port; 119843e1988Sjohnlev 120843e1988Sjohnlev /* 121843e1988Sjohnlev * This is quite gross, we had to leave the evtchn that led to this 122*349b53ddSStuart Maybee * invocation in a per-cpu mailbox, retrieve it now. 123843e1988Sjohnlev * We do this because the interface doesn't offer us a way to pass 124843e1988Sjohnlev * a dynamic argument up through the generic interrupt service layer. 125843e1988Sjohnlev * The mailbox is safe since we either run with interrupts disabled or 126843e1988Sjohnlev * non-preemptable till we reach here. 127843e1988Sjohnlev */ 128*349b53ddSStuart Maybee port = CPU->cpu_m.mcpu_ec_mbox; 129843e1988Sjohnlev ASSERT(port != 0); 130*349b53ddSStuart Maybee CPU->cpu_m.mcpu_ec_mbox = 0; 131843e1988Sjohnlev ec_clear_evtchn(port); 132843e1988Sjohnlev mutex_enter(&port_user_lock); 133843e1988Sjohnlev 134843e1988Sjohnlev if ((ep = port_user[port]) != NULL) { 135843e1988Sjohnlev mutex_enter(&ep->evtchn_lock); 136843e1988Sjohnlev if ((ep->ring_prod - ep->ring_cons) < EVTCHN_RING_SIZE) { 137843e1988Sjohnlev ep->ring[EVTCHN_RING_MASK(ep->ring_prod)] = port; 138843e1988Sjohnlev /* 139843e1988Sjohnlev * Wake up reader when ring goes non-empty 140843e1988Sjohnlev */ 141843e1988Sjohnlev if (ep->ring_cons == ep->ring_prod++) { 142843e1988Sjohnlev cv_signal(&ep->evtchn_wait); 143843e1988Sjohnlev mutex_exit(&ep->evtchn_lock); 144843e1988Sjohnlev pollwakeup(&ep->evtchn_pollhead, 145843e1988Sjohnlev POLLIN | POLLRDNORM); 146843e1988Sjohnlev goto done; 147843e1988Sjohnlev } 148843e1988Sjohnlev } else { 149843e1988Sjohnlev ep->ring_overflow = 1; 150843e1988Sjohnlev } 151843e1988Sjohnlev mutex_exit(&ep->evtchn_lock); 152843e1988Sjohnlev } 153843e1988Sjohnlev 154843e1988Sjohnlev done: 155843e1988Sjohnlev mutex_exit(&port_user_lock); 156843e1988Sjohnlev } 157843e1988Sjohnlev 158843e1988Sjohnlev /* ARGSUSED */ 159843e1988Sjohnlev static int 160b26a64aeSjohnlev evtchndrv_read(dev_t dev, struct uio *uio, cred_t *cr) 161843e1988Sjohnlev { 162843e1988Sjohnlev int rc = 0; 163843e1988Sjohnlev ssize_t count; 164843e1988Sjohnlev unsigned int c, p, bytes1 = 0, bytes2 = 0; 165843e1988Sjohnlev struct evtsoftdata *ep; 166843e1988Sjohnlev minor_t minor = getminor(dev); 167843e1988Sjohnlev 168b26a64aeSjohnlev if (secpolicy_xvm_control(cr)) 169b26a64aeSjohnlev return (EPERM); 170b26a64aeSjohnlev 171843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 172843e1988Sjohnlev 173843e1988Sjohnlev /* Whole number of ports. */ 174843e1988Sjohnlev count = uio->uio_resid; 175843e1988Sjohnlev count &= ~(sizeof (evtchn_port_t) - 1); 176843e1988Sjohnlev 177843e1988Sjohnlev if (count == 0) 178843e1988Sjohnlev return (0); 179843e1988Sjohnlev 180843e1988Sjohnlev if (count > PAGESIZE) 181843e1988Sjohnlev count = PAGESIZE; 182843e1988Sjohnlev 183843e1988Sjohnlev mutex_enter(&ep->evtchn_lock); 184843e1988Sjohnlev for (;;) { 185843e1988Sjohnlev if (ep->ring_overflow) { 186843e1988Sjohnlev rc = EFBIG; 187843e1988Sjohnlev goto done; 188843e1988Sjohnlev } 189843e1988Sjohnlev 190843e1988Sjohnlev if ((c = ep->ring_cons) != (p = ep->ring_prod)) 191843e1988Sjohnlev break; 192843e1988Sjohnlev 193843e1988Sjohnlev if (uio->uio_fmode & O_NONBLOCK) { 194843e1988Sjohnlev rc = EAGAIN; 195843e1988Sjohnlev goto done; 196843e1988Sjohnlev } 197843e1988Sjohnlev 198843e1988Sjohnlev if (cv_wait_sig(&ep->evtchn_wait, &ep->evtchn_lock) == 0) { 199843e1988Sjohnlev rc = EINTR; 200843e1988Sjohnlev goto done; 201843e1988Sjohnlev } 202843e1988Sjohnlev } 203843e1988Sjohnlev 204843e1988Sjohnlev /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */ 205843e1988Sjohnlev if (((c ^ p) & EVTCHN_RING_SIZE) != 0) { 206843e1988Sjohnlev bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) * 207843e1988Sjohnlev sizeof (evtchn_port_t); 208843e1988Sjohnlev bytes2 = EVTCHN_RING_MASK(p) * sizeof (evtchn_port_t); 209843e1988Sjohnlev } else { 210843e1988Sjohnlev bytes1 = (p - c) * sizeof (evtchn_port_t); 211843e1988Sjohnlev bytes2 = 0; 212843e1988Sjohnlev } 213843e1988Sjohnlev 214843e1988Sjohnlev /* Truncate chunks according to caller's maximum byte count. */ 215843e1988Sjohnlev if (bytes1 > count) { 216843e1988Sjohnlev bytes1 = count; 217843e1988Sjohnlev bytes2 = 0; 218843e1988Sjohnlev } else if ((bytes1 + bytes2) > count) { 219843e1988Sjohnlev bytes2 = count - bytes1; 220843e1988Sjohnlev } 221843e1988Sjohnlev 222843e1988Sjohnlev if (uiomove(&ep->ring[EVTCHN_RING_MASK(c)], bytes1, UIO_READ, uio) || 223843e1988Sjohnlev ((bytes2 != 0) && uiomove(&ep->ring[0], bytes2, UIO_READ, uio))) { 224843e1988Sjohnlev rc = EFAULT; 225843e1988Sjohnlev goto done; 226843e1988Sjohnlev } 227843e1988Sjohnlev 228843e1988Sjohnlev ep->ring_cons += (bytes1 + bytes2) / sizeof (evtchn_port_t); 229843e1988Sjohnlev done: 230843e1988Sjohnlev mutex_exit(&ep->evtchn_lock); 231843e1988Sjohnlev return (rc); 232843e1988Sjohnlev } 233843e1988Sjohnlev 234843e1988Sjohnlev /* ARGSUSED */ 235843e1988Sjohnlev static int 236b26a64aeSjohnlev evtchndrv_write(dev_t dev, struct uio *uio, cred_t *cr) 237843e1988Sjohnlev { 238843e1988Sjohnlev int rc, i; 239843e1988Sjohnlev ssize_t count; 240843e1988Sjohnlev evtchn_port_t *kbuf; 241843e1988Sjohnlev struct evtsoftdata *ep; 242843e1988Sjohnlev ulong_t flags; 243843e1988Sjohnlev minor_t minor = getminor(dev); 244*349b53ddSStuart Maybee evtchn_port_t sbuf[32]; 245843e1988Sjohnlev 246b26a64aeSjohnlev if (secpolicy_xvm_control(cr)) 247b26a64aeSjohnlev return (EPERM); 248b26a64aeSjohnlev 249843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 250843e1988Sjohnlev 251843e1988Sjohnlev 252843e1988Sjohnlev /* Whole number of ports. */ 253843e1988Sjohnlev count = uio->uio_resid; 254843e1988Sjohnlev count &= ~(sizeof (evtchn_port_t) - 1); 255843e1988Sjohnlev 256*349b53ddSStuart Maybee if (count == 0) 257*349b53ddSStuart Maybee return (0); 258843e1988Sjohnlev 259843e1988Sjohnlev if (count > PAGESIZE) 260843e1988Sjohnlev count = PAGESIZE; 261843e1988Sjohnlev 262*349b53ddSStuart Maybee if (count <= sizeof (sbuf)) 263*349b53ddSStuart Maybee kbuf = sbuf; 264*349b53ddSStuart Maybee else 265*349b53ddSStuart Maybee kbuf = kmem_alloc(PAGESIZE, KM_SLEEP); 266843e1988Sjohnlev if ((rc = uiomove(kbuf, count, UIO_WRITE, uio)) != 0) 267843e1988Sjohnlev goto out; 268843e1988Sjohnlev 269843e1988Sjohnlev mutex_enter(&port_user_lock); 270843e1988Sjohnlev for (i = 0; i < (count / sizeof (evtchn_port_t)); i++) 271843e1988Sjohnlev if ((kbuf[i] < NR_EVENT_CHANNELS) && 272843e1988Sjohnlev (port_user[kbuf[i]] == ep)) { 273843e1988Sjohnlev flags = intr_clear(); 274843e1988Sjohnlev ec_unmask_evtchn(kbuf[i]); 275843e1988Sjohnlev intr_restore(flags); 276843e1988Sjohnlev } 277843e1988Sjohnlev mutex_exit(&port_user_lock); 278843e1988Sjohnlev 279843e1988Sjohnlev out: 280*349b53ddSStuart Maybee if (kbuf != sbuf) 281843e1988Sjohnlev kmem_free(kbuf, PAGESIZE); 282843e1988Sjohnlev return (rc); 283843e1988Sjohnlev } 284843e1988Sjohnlev 285843e1988Sjohnlev static void 286843e1988Sjohnlev evtchn_bind_to_user(struct evtsoftdata *u, int port) 287843e1988Sjohnlev { 288843e1988Sjohnlev ulong_t flags; 289843e1988Sjohnlev 290843e1988Sjohnlev /* 291843e1988Sjohnlev * save away the PID of the last process to bind to this event channel. 292843e1988Sjohnlev * Useful for debugging. 293843e1988Sjohnlev */ 294843e1988Sjohnlev u->pid = ddi_get_pid(); 295843e1988Sjohnlev 296843e1988Sjohnlev mutex_enter(&port_user_lock); 297843e1988Sjohnlev ASSERT(port_user[port] == NULL); 298843e1988Sjohnlev port_user[port] = u; 299843e1988Sjohnlev ec_irq_add_evtchn(ec_dev_irq, port); 300843e1988Sjohnlev flags = intr_clear(); 301843e1988Sjohnlev ec_unmask_evtchn(port); 302843e1988Sjohnlev intr_restore(flags); 303843e1988Sjohnlev mutex_exit(&port_user_lock); 304843e1988Sjohnlev } 305843e1988Sjohnlev 306843e1988Sjohnlev static void 307843e1988Sjohnlev evtchndrv_close_evtchn(int port) 308843e1988Sjohnlev { 309843e1988Sjohnlev struct evtsoftdata *ep; 310843e1988Sjohnlev 311843e1988Sjohnlev ASSERT(MUTEX_HELD(&port_user_lock)); 312843e1988Sjohnlev ep = port_user[port]; 313843e1988Sjohnlev ASSERT(ep != NULL); 314843e1988Sjohnlev (void) ec_mask_evtchn(port); 315843e1988Sjohnlev /* 316843e1988Sjohnlev * It is possible the event is in transit to us. 317843e1988Sjohnlev * If it is already in the ring buffer, then a client may 318843e1988Sjohnlev * get a spurious event notification on the next read of 319843e1988Sjohnlev * of the evtchn device. Clients will need to be able to 320843e1988Sjohnlev * handle getting a spurious event notification. 321843e1988Sjohnlev */ 322843e1988Sjohnlev port_user[port] = NULL; 323843e1988Sjohnlev /* 324843e1988Sjohnlev * The event is masked and should stay so, clean it up. 325843e1988Sjohnlev */ 326843e1988Sjohnlev ec_irq_rm_evtchn(ec_dev_irq, port); 327843e1988Sjohnlev } 328843e1988Sjohnlev 329843e1988Sjohnlev /* ARGSUSED */ 330843e1988Sjohnlev static int 331b26a64aeSjohnlev evtchndrv_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr, 332843e1988Sjohnlev int *rvalp) 333843e1988Sjohnlev { 334843e1988Sjohnlev int err = 0; 335843e1988Sjohnlev struct evtsoftdata *ep; 336843e1988Sjohnlev minor_t minor = getminor(dev); 337843e1988Sjohnlev 338b26a64aeSjohnlev if (secpolicy_xvm_control(cr)) 339b26a64aeSjohnlev return (EPERM); 340b26a64aeSjohnlev 341843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 342843e1988Sjohnlev 343843e1988Sjohnlev *rvalp = 0; 344843e1988Sjohnlev 345843e1988Sjohnlev switch (cmd) { 346843e1988Sjohnlev case IOCTL_EVTCHN_BIND_VIRQ: { 347843e1988Sjohnlev struct ioctl_evtchn_bind_virq bind; 348843e1988Sjohnlev 349843e1988Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) { 350843e1988Sjohnlev err = EFAULT; 351843e1988Sjohnlev break; 352843e1988Sjohnlev } 353843e1988Sjohnlev 354843e1988Sjohnlev if ((err = xen_bind_virq(bind.virq, 0, rvalp)) != 0) 355843e1988Sjohnlev break; 356843e1988Sjohnlev 357843e1988Sjohnlev evtchn_bind_to_user(ep, *rvalp); 358843e1988Sjohnlev break; 359843e1988Sjohnlev } 360843e1988Sjohnlev 361843e1988Sjohnlev case IOCTL_EVTCHN_BIND_INTERDOMAIN: { 362843e1988Sjohnlev struct ioctl_evtchn_bind_interdomain bind; 363843e1988Sjohnlev 364843e1988Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) { 365843e1988Sjohnlev err = EFAULT; 366843e1988Sjohnlev break; 367843e1988Sjohnlev } 368843e1988Sjohnlev 369843e1988Sjohnlev if ((err = xen_bind_interdomain(bind.remote_domain, 370843e1988Sjohnlev bind.remote_port, rvalp)) != 0) 371843e1988Sjohnlev break; 372843e1988Sjohnlev 373843e1988Sjohnlev ec_bind_vcpu(*rvalp, 0); 374843e1988Sjohnlev evtchn_bind_to_user(ep, *rvalp); 375843e1988Sjohnlev break; 376843e1988Sjohnlev } 377843e1988Sjohnlev 378843e1988Sjohnlev case IOCTL_EVTCHN_BIND_UNBOUND_PORT: { 379843e1988Sjohnlev struct ioctl_evtchn_bind_unbound_port bind; 380843e1988Sjohnlev 381843e1988Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) { 382843e1988Sjohnlev err = EFAULT; 383843e1988Sjohnlev break; 384843e1988Sjohnlev } 385843e1988Sjohnlev 386843e1988Sjohnlev if ((err = xen_alloc_unbound_evtchn(bind.remote_domain, 387843e1988Sjohnlev rvalp)) != 0) 388843e1988Sjohnlev break; 389843e1988Sjohnlev 390843e1988Sjohnlev evtchn_bind_to_user(ep, *rvalp); 391843e1988Sjohnlev break; 392843e1988Sjohnlev } 393843e1988Sjohnlev 394843e1988Sjohnlev case IOCTL_EVTCHN_UNBIND: { 395843e1988Sjohnlev struct ioctl_evtchn_unbind unbind; 396843e1988Sjohnlev 397843e1988Sjohnlev if (copyin((void *)data, &unbind, sizeof (unbind))) { 398843e1988Sjohnlev err = EFAULT; 399843e1988Sjohnlev break; 400843e1988Sjohnlev } 401843e1988Sjohnlev 402843e1988Sjohnlev if (unbind.port >= NR_EVENT_CHANNELS) { 403843e1988Sjohnlev err = EFAULT; 404843e1988Sjohnlev break; 405843e1988Sjohnlev } 406843e1988Sjohnlev 407843e1988Sjohnlev mutex_enter(&port_user_lock); 408843e1988Sjohnlev 409843e1988Sjohnlev if (port_user[unbind.port] != ep) { 410843e1988Sjohnlev mutex_exit(&port_user_lock); 411843e1988Sjohnlev err = ENOTCONN; 412843e1988Sjohnlev break; 413843e1988Sjohnlev } 414843e1988Sjohnlev 415843e1988Sjohnlev evtchndrv_close_evtchn(unbind.port); 416843e1988Sjohnlev mutex_exit(&port_user_lock); 417843e1988Sjohnlev break; 418843e1988Sjohnlev } 419843e1988Sjohnlev 420843e1988Sjohnlev case IOCTL_EVTCHN_NOTIFY: { 421843e1988Sjohnlev struct ioctl_evtchn_notify notify; 422843e1988Sjohnlev 423843e1988Sjohnlev if (copyin((void *)data, ¬ify, sizeof (notify))) { 424843e1988Sjohnlev err = EFAULT; 425843e1988Sjohnlev break; 426843e1988Sjohnlev } 427843e1988Sjohnlev 428843e1988Sjohnlev if (notify.port >= NR_EVENT_CHANNELS) { 429843e1988Sjohnlev err = EINVAL; 430843e1988Sjohnlev } else if (port_user[notify.port] != ep) { 431843e1988Sjohnlev err = ENOTCONN; 432843e1988Sjohnlev } else { 433843e1988Sjohnlev ec_notify_via_evtchn(notify.port); 434843e1988Sjohnlev } 435843e1988Sjohnlev break; 436843e1988Sjohnlev } 437843e1988Sjohnlev 438843e1988Sjohnlev default: 439843e1988Sjohnlev err = ENOSYS; 440843e1988Sjohnlev } 441843e1988Sjohnlev 442843e1988Sjohnlev return (err); 443843e1988Sjohnlev } 444843e1988Sjohnlev 445843e1988Sjohnlev static int 446843e1988Sjohnlev evtchndrv_poll(dev_t dev, short ev, int anyyet, short *revp, pollhead_t **phpp) 447843e1988Sjohnlev { 448843e1988Sjohnlev struct evtsoftdata *ep; 449843e1988Sjohnlev minor_t minor = getminor(dev); 450843e1988Sjohnlev short mask = 0; 451843e1988Sjohnlev 452843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 453843e1988Sjohnlev *phpp = (struct pollhead *)NULL; 454843e1988Sjohnlev 455843e1988Sjohnlev if (ev & POLLOUT) 456843e1988Sjohnlev mask |= POLLOUT; 457843e1988Sjohnlev if (ep->ring_overflow) 458843e1988Sjohnlev mask |= POLLERR; 459843e1988Sjohnlev if (ev & (POLLIN | POLLRDNORM)) { 460843e1988Sjohnlev mutex_enter(&ep->evtchn_lock); 461843e1988Sjohnlev if (ep->ring_cons != ep->ring_prod) 462843e1988Sjohnlev mask |= (POLLIN | POLLRDNORM) & ev; 463843e1988Sjohnlev else 464843e1988Sjohnlev if (mask == 0 && !anyyet) 465843e1988Sjohnlev *phpp = &ep->evtchn_pollhead; 466843e1988Sjohnlev mutex_exit(&ep->evtchn_lock); 467843e1988Sjohnlev } 468843e1988Sjohnlev *revp = mask; 469843e1988Sjohnlev return (0); 470843e1988Sjohnlev } 471843e1988Sjohnlev 472843e1988Sjohnlev 473843e1988Sjohnlev /* ARGSUSED */ 474843e1988Sjohnlev static int 475843e1988Sjohnlev evtchndrv_open(dev_t *devp, int flag, int otyp, cred_t *credp) 476843e1988Sjohnlev { 477843e1988Sjohnlev struct evtsoftdata *ep; 478843e1988Sjohnlev minor_t minor = getminor(*devp); 479843e1988Sjohnlev 480843e1988Sjohnlev if (otyp == OTYP_BLK) 481843e1988Sjohnlev return (ENXIO); 482843e1988Sjohnlev 483843e1988Sjohnlev /* 484843e1988Sjohnlev * only allow open on minor = 0 - the clone device 485843e1988Sjohnlev */ 486843e1988Sjohnlev if (minor != 0) 487843e1988Sjohnlev return (ENXIO); 488843e1988Sjohnlev 489843e1988Sjohnlev /* 490843e1988Sjohnlev * find a free slot and grab it 491843e1988Sjohnlev */ 492843e1988Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex); 493843e1988Sjohnlev for (minor = 1; minor < evtchndrv_nclones; minor++) { 494843e1988Sjohnlev if (evtchndrv_clone_tab[minor] == 0) { 495843e1988Sjohnlev evtchndrv_clone_tab[minor] = 1; 496843e1988Sjohnlev break; 497843e1988Sjohnlev } 498843e1988Sjohnlev } 499843e1988Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex); 500843e1988Sjohnlev if (minor == evtchndrv_nclones) 501843e1988Sjohnlev return (EAGAIN); 502843e1988Sjohnlev 503843e1988Sjohnlev /* Allocate softstate structure */ 504843e1988Sjohnlev if (ddi_soft_state_zalloc(evtchndrv_statep, 505843e1988Sjohnlev EVTCHNDRV_MINOR2INST(minor)) != DDI_SUCCESS) { 506843e1988Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex); 507843e1988Sjohnlev evtchndrv_clone_tab[minor] = 0; 508843e1988Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex); 509843e1988Sjohnlev return (EAGAIN); 510843e1988Sjohnlev } 511843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 512843e1988Sjohnlev 513843e1988Sjohnlev /* ... and init it */ 514843e1988Sjohnlev ep->dip = evtchndrv_dip; 515843e1988Sjohnlev 516843e1988Sjohnlev cv_init(&ep->evtchn_wait, NULL, CV_DEFAULT, NULL); 517843e1988Sjohnlev mutex_init(&ep->evtchn_lock, NULL, MUTEX_DEFAULT, NULL); 518843e1988Sjohnlev 519843e1988Sjohnlev ep->ring = kmem_alloc(PAGESIZE, KM_SLEEP); 520843e1988Sjohnlev 521843e1988Sjohnlev /* clone driver */ 522843e1988Sjohnlev *devp = makedevice(getmajor(*devp), minor); 523843e1988Sjohnlev 524843e1988Sjohnlev return (0); 525843e1988Sjohnlev } 526843e1988Sjohnlev 527843e1988Sjohnlev /* ARGSUSED */ 528843e1988Sjohnlev static int 529843e1988Sjohnlev evtchndrv_close(dev_t dev, int flag, int otyp, struct cred *credp) 530843e1988Sjohnlev { 531843e1988Sjohnlev struct evtsoftdata *ep; 532843e1988Sjohnlev minor_t minor = getminor(dev); 533843e1988Sjohnlev int i; 534843e1988Sjohnlev 535843e1988Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor)); 536843e1988Sjohnlev if (ep == NULL) 537843e1988Sjohnlev return (ENXIO); 538843e1988Sjohnlev 539843e1988Sjohnlev mutex_enter(&port_user_lock); 540843e1988Sjohnlev 541843e1988Sjohnlev 542843e1988Sjohnlev for (i = 0; i < NR_EVENT_CHANNELS; i++) { 543843e1988Sjohnlev if (port_user[i] != ep) 544843e1988Sjohnlev continue; 545843e1988Sjohnlev 546843e1988Sjohnlev evtchndrv_close_evtchn(i); 547843e1988Sjohnlev } 548843e1988Sjohnlev 549843e1988Sjohnlev mutex_exit(&port_user_lock); 550843e1988Sjohnlev 551843e1988Sjohnlev kmem_free(ep->ring, PAGESIZE); 552843e1988Sjohnlev ddi_soft_state_free(evtchndrv_statep, EVTCHNDRV_MINOR2INST(minor)); 553843e1988Sjohnlev 554843e1988Sjohnlev /* 555843e1988Sjohnlev * free clone tab slot 556843e1988Sjohnlev */ 557843e1988Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex); 558843e1988Sjohnlev evtchndrv_clone_tab[minor] = 0; 559843e1988Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex); 560843e1988Sjohnlev 561843e1988Sjohnlev return (0); 562843e1988Sjohnlev } 563843e1988Sjohnlev 564843e1988Sjohnlev /* ARGSUSED */ 565843e1988Sjohnlev static int 566843e1988Sjohnlev evtchndrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 567843e1988Sjohnlev { 568843e1988Sjohnlev dev_t dev = (dev_t)arg; 569843e1988Sjohnlev minor_t minor = getminor(dev); 570843e1988Sjohnlev int retval; 571843e1988Sjohnlev 572843e1988Sjohnlev switch (cmd) { 573843e1988Sjohnlev case DDI_INFO_DEVT2DEVINFO: 574843e1988Sjohnlev if (minor != 0 || evtchndrv_dip == NULL) { 575843e1988Sjohnlev *result = (void *)NULL; 576843e1988Sjohnlev retval = DDI_FAILURE; 577843e1988Sjohnlev } else { 578843e1988Sjohnlev *result = (void *)evtchndrv_dip; 579843e1988Sjohnlev retval = DDI_SUCCESS; 580843e1988Sjohnlev } 581843e1988Sjohnlev break; 582843e1988Sjohnlev case DDI_INFO_DEVT2INSTANCE: 583843e1988Sjohnlev *result = (void *)0; 584843e1988Sjohnlev retval = DDI_SUCCESS; 585843e1988Sjohnlev break; 586843e1988Sjohnlev default: 587843e1988Sjohnlev retval = DDI_FAILURE; 588843e1988Sjohnlev } 589843e1988Sjohnlev return (retval); 590843e1988Sjohnlev } 591843e1988Sjohnlev 592843e1988Sjohnlev 593843e1988Sjohnlev static int 594843e1988Sjohnlev evtchndrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 595843e1988Sjohnlev { 596843e1988Sjohnlev int error; 597843e1988Sjohnlev int unit = ddi_get_instance(dip); 598843e1988Sjohnlev 599843e1988Sjohnlev 600843e1988Sjohnlev switch (cmd) { 601843e1988Sjohnlev case DDI_ATTACH: 602843e1988Sjohnlev break; 603843e1988Sjohnlev case DDI_RESUME: 604843e1988Sjohnlev return (DDI_SUCCESS); 605843e1988Sjohnlev default: 606843e1988Sjohnlev cmn_err(CE_WARN, "evtchn_attach: unknown cmd 0x%x\n", cmd); 607843e1988Sjohnlev return (DDI_FAILURE); 608843e1988Sjohnlev } 609843e1988Sjohnlev 610843e1988Sjohnlev /* DDI_ATTACH */ 611843e1988Sjohnlev 612843e1988Sjohnlev /* 613843e1988Sjohnlev * only one instance - but we clone using the open routine 614843e1988Sjohnlev */ 615843e1988Sjohnlev if (ddi_get_instance(dip) > 0) 616843e1988Sjohnlev return (DDI_FAILURE); 617843e1988Sjohnlev 618843e1988Sjohnlev mutex_init(&evtchndrv_clone_tab_mutex, NULL, MUTEX_DRIVER, 619843e1988Sjohnlev NULL); 620843e1988Sjohnlev 621843e1988Sjohnlev error = ddi_create_minor_node(dip, "evtchn", S_IFCHR, unit, 622843e1988Sjohnlev DDI_PSEUDO, NULL); 623843e1988Sjohnlev if (error != DDI_SUCCESS) 624843e1988Sjohnlev goto fail; 625843e1988Sjohnlev 626843e1988Sjohnlev /* 627843e1988Sjohnlev * save dip for getinfo 628843e1988Sjohnlev */ 629843e1988Sjohnlev evtchndrv_dip = dip; 630843e1988Sjohnlev ddi_report_dev(dip); 631843e1988Sjohnlev 632843e1988Sjohnlev mutex_init(&port_user_lock, NULL, MUTEX_DRIVER, NULL); 633843e1988Sjohnlev (void) memset(port_user, 0, sizeof (port_user)); 634843e1988Sjohnlev 635843e1988Sjohnlev ec_dev_irq = ec_dev_alloc_irq(); 636843e1988Sjohnlev (void) add_avintr(NULL, IPL_EVTCHN, (avfunc)evtchn_device_upcall, 637843e1988Sjohnlev "evtchn_driver", ec_dev_irq, NULL, NULL, NULL, dip); 638843e1988Sjohnlev 639843e1988Sjohnlev return (DDI_SUCCESS); 640843e1988Sjohnlev 641843e1988Sjohnlev fail: 642843e1988Sjohnlev (void) evtchndrv_detach(dip, DDI_DETACH); 643843e1988Sjohnlev return (error); 644843e1988Sjohnlev } 645843e1988Sjohnlev 646843e1988Sjohnlev /*ARGSUSED*/ 647843e1988Sjohnlev static int 648843e1988Sjohnlev evtchndrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 649843e1988Sjohnlev { 650843e1988Sjohnlev /* 651843e1988Sjohnlev * Don't allow detach for now. 652843e1988Sjohnlev */ 653843e1988Sjohnlev return (DDI_FAILURE); 654843e1988Sjohnlev } 655843e1988Sjohnlev 656843e1988Sjohnlev /* Solaris driver framework */ 657843e1988Sjohnlev 658843e1988Sjohnlev static struct cb_ops evtchndrv_cb_ops = { 659843e1988Sjohnlev evtchndrv_open, /* cb_open */ 660843e1988Sjohnlev evtchndrv_close, /* cb_close */ 661843e1988Sjohnlev nodev, /* cb_strategy */ 662843e1988Sjohnlev nodev, /* cb_print */ 663843e1988Sjohnlev nodev, /* cb_dump */ 664843e1988Sjohnlev evtchndrv_read, /* cb_read */ 665843e1988Sjohnlev evtchndrv_write, /* cb_write */ 666843e1988Sjohnlev evtchndrv_ioctl, /* cb_ioctl */ 667843e1988Sjohnlev nodev, /* cb_devmap */ 668843e1988Sjohnlev nodev, /* cb_mmap */ 669843e1988Sjohnlev nodev, /* cb_segmap */ 670843e1988Sjohnlev evtchndrv_poll, /* cb_chpoll */ 671843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */ 672843e1988Sjohnlev 0, /* cb_stream */ 673843e1988Sjohnlev D_NEW | D_MP | D_64BIT /* cb_flag */ 674843e1988Sjohnlev }; 675843e1988Sjohnlev 676843e1988Sjohnlev static struct dev_ops evtchndrv_dev_ops = { 677843e1988Sjohnlev DEVO_REV, /* devo_rev */ 678843e1988Sjohnlev 0, /* devo_refcnt */ 679843e1988Sjohnlev evtchndrv_info, /* devo_getinfo */ 680843e1988Sjohnlev nulldev, /* devo_identify */ 681843e1988Sjohnlev nulldev, /* devo_probe */ 682843e1988Sjohnlev evtchndrv_attach, /* devo_attach */ 683843e1988Sjohnlev evtchndrv_detach, /* devo_detach */ 684843e1988Sjohnlev nodev, /* devo_reset */ 685843e1988Sjohnlev &evtchndrv_cb_ops, /* devo_cb_ops */ 686843e1988Sjohnlev NULL, /* devo_bus_ops */ 68719397407SSherry Moore NULL, /* power */ 68819397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 689843e1988Sjohnlev }; 690843e1988Sjohnlev 691843e1988Sjohnlev static struct modldrv modldrv = { 692843e1988Sjohnlev &mod_driverops, /* Type of module. This one is a driver */ 69319397407SSherry Moore "Evtchn driver", /* Name of the module. */ 694843e1988Sjohnlev &evtchndrv_dev_ops /* driver ops */ 695843e1988Sjohnlev }; 696843e1988Sjohnlev 697843e1988Sjohnlev static struct modlinkage modlinkage = { 698843e1988Sjohnlev MODREV_1, 699843e1988Sjohnlev &modldrv, 700843e1988Sjohnlev NULL 701843e1988Sjohnlev }; 702843e1988Sjohnlev 703843e1988Sjohnlev int 704843e1988Sjohnlev _init(void) 705843e1988Sjohnlev { 706843e1988Sjohnlev int err; 707843e1988Sjohnlev 708843e1988Sjohnlev err = ddi_soft_state_init(&evtchndrv_statep, 709843e1988Sjohnlev sizeof (struct evtsoftdata), 1); 710843e1988Sjohnlev if (err) 711843e1988Sjohnlev return (err); 712843e1988Sjohnlev 713843e1988Sjohnlev err = mod_install(&modlinkage); 714843e1988Sjohnlev if (err) 715843e1988Sjohnlev ddi_soft_state_fini(&evtchndrv_statep); 716843e1988Sjohnlev else 717843e1988Sjohnlev evtchndrv_clone_tab = kmem_zalloc( 718843e1988Sjohnlev sizeof (int) * evtchndrv_nclones, KM_SLEEP); 719843e1988Sjohnlev return (err); 720843e1988Sjohnlev } 721843e1988Sjohnlev 722843e1988Sjohnlev int 723843e1988Sjohnlev _fini(void) 724843e1988Sjohnlev { 725843e1988Sjohnlev int e; 726843e1988Sjohnlev 727843e1988Sjohnlev e = mod_remove(&modlinkage); 728843e1988Sjohnlev if (e) 729843e1988Sjohnlev return (e); 730843e1988Sjohnlev 731843e1988Sjohnlev ddi_soft_state_fini(&evtchndrv_statep); 732843e1988Sjohnlev 733843e1988Sjohnlev return (0); 734843e1988Sjohnlev } 735843e1988Sjohnlev 736843e1988Sjohnlev int 737843e1988Sjohnlev _info(struct modinfo *modinfop) 738843e1988Sjohnlev { 739843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 740843e1988Sjohnlev } 741