15084Sjohnlev /*
25084Sjohnlev * CDDL HEADER START
35084Sjohnlev *
45084Sjohnlev * The contents of this file are subject to the terms of the
55084Sjohnlev * Common Development and Distribution License (the "License").
65084Sjohnlev * You may not use this file except in compliance with the License.
75084Sjohnlev *
85084Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev * See the License for the specific language governing permissions
115084Sjohnlev * and limitations under the License.
125084Sjohnlev *
135084Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev *
195084Sjohnlev * CDDL HEADER END
205084Sjohnlev */
215084Sjohnlev
225084Sjohnlev /*
23*10175SStuart.Maybee@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
245084Sjohnlev * Use is subject to license terms.
255084Sjohnlev */
265084Sjohnlev
275084Sjohnlev
285084Sjohnlev /*
295084Sjohnlev * evtchn.c
305084Sjohnlev *
315084Sjohnlev * Driver for receiving and demuxing event-channel signals.
325084Sjohnlev *
335084Sjohnlev * Copyright (c) 2004-2005, K A Fraser
345084Sjohnlev * Multi-process extensions Copyright (c) 2004, Steven Smith
355084Sjohnlev *
365084Sjohnlev * This file may be distributed separately from the Linux kernel, or
375084Sjohnlev * incorporated into other software packages, subject to the following license:
385084Sjohnlev *
395084Sjohnlev * Permission is hereby granted, free of charge, to any person obtaining a copy
405084Sjohnlev * of this source file (the "Software"), to deal in the Software without
415084Sjohnlev * restriction, including without limitation the rights to use, copy, modify,
425084Sjohnlev * merge, publish, distribute, sublicense, and/or sell copies of the Software,
435084Sjohnlev * and to permit persons to whom the Software is furnished to do so, subject to
445084Sjohnlev * the following conditions:
455084Sjohnlev *
465084Sjohnlev * The above copyright notice and this permission notice shall be included in
475084Sjohnlev * all copies or substantial portions of the Software.
485084Sjohnlev *
495084Sjohnlev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
505084Sjohnlev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
515084Sjohnlev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
525084Sjohnlev * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
535084Sjohnlev * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
545084Sjohnlev * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
555084Sjohnlev * IN THE SOFTWARE.
565084Sjohnlev */
575084Sjohnlev
585084Sjohnlev #include <sys/types.h>
595084Sjohnlev #include <sys/hypervisor.h>
605084Sjohnlev #include <sys/machsystm.h>
615084Sjohnlev #include <sys/mutex.h>
625084Sjohnlev #include <sys/evtchn_impl.h>
635084Sjohnlev #include <sys/ddi_impldefs.h>
645084Sjohnlev #include <sys/avintr.h>
655084Sjohnlev #include <sys/cpuvar.h>
665084Sjohnlev #include <sys/smp_impldefs.h>
675084Sjohnlev #include <sys/archsystm.h>
685084Sjohnlev #include <sys/sysmacros.h>
695084Sjohnlev #include <sys/fcntl.h>
705084Sjohnlev #include <sys/open.h>
715084Sjohnlev #include <sys/stat.h>
725084Sjohnlev #include <sys/psm.h>
735084Sjohnlev #include <sys/cpu.h>
745084Sjohnlev #include <sys/cmn_err.h>
755084Sjohnlev #include <sys/xen_errno.h>
766784Sjohnlev #include <sys/policy.h>
775084Sjohnlev #include <xen/sys/evtchn.h>
785084Sjohnlev
795084Sjohnlev /* Some handy macros */
805084Sjohnlev #define EVTCHNDRV_MINOR2INST(minor) ((int)(minor))
815084Sjohnlev #define EVTCHNDRV_DEFAULT_NCLONES 256
825084Sjohnlev #define EVTCHNDRV_INST2SOFTS(inst) \
835084Sjohnlev (ddi_get_soft_state(evtchndrv_statep, (inst)))
845084Sjohnlev
855084Sjohnlev /* Soft state data structure for evtchn driver */
865084Sjohnlev struct evtsoftdata {
875084Sjohnlev dev_info_t *dip;
885084Sjohnlev /* Notification ring, accessed via /dev/xen/evtchn. */
895084Sjohnlev #define EVTCHN_RING_SIZE (PAGESIZE / sizeof (evtchn_port_t))
905084Sjohnlev #define EVTCHN_RING_MASK(_i) ((_i) & (EVTCHN_RING_SIZE - 1))
915084Sjohnlev evtchn_port_t *ring;
925084Sjohnlev unsigned int ring_cons, ring_prod, ring_overflow;
935084Sjohnlev
94*10175SStuart.Maybee@Sun.COM kcondvar_t evtchn_wait; /* Processes wait on this when ring is empty. */
955084Sjohnlev kmutex_t evtchn_lock;
965084Sjohnlev struct pollhead evtchn_pollhead;
975084Sjohnlev
98*10175SStuart.Maybee@Sun.COM pid_t pid; /* last pid to bind to this event channel. */
99*10175SStuart.Maybee@Sun.COM processorid_t cpu; /* cpu thread/evtchn is bound to */
1005084Sjohnlev };
1015084Sjohnlev
1025084Sjohnlev static void *evtchndrv_statep;
1035084Sjohnlev int evtchndrv_nclones = EVTCHNDRV_DEFAULT_NCLONES;
1045084Sjohnlev static int *evtchndrv_clone_tab;
1055084Sjohnlev static dev_info_t *evtchndrv_dip;
1065084Sjohnlev static kmutex_t evtchndrv_clone_tab_mutex;
1075084Sjohnlev
1085084Sjohnlev static int evtchndrv_detach(dev_info_t *, ddi_detach_cmd_t);
1095084Sjohnlev
1105084Sjohnlev /* Who's bound to each port? */
1115084Sjohnlev static struct evtsoftdata *port_user[NR_EVENT_CHANNELS];
1125084Sjohnlev static kmutex_t port_user_lock;
1135084Sjohnlev
1145084Sjohnlev void
evtchn_device_upcall()1155084Sjohnlev evtchn_device_upcall()
1165084Sjohnlev {
1175084Sjohnlev struct evtsoftdata *ep;
1185084Sjohnlev int port;
1195084Sjohnlev
1205084Sjohnlev /*
1215084Sjohnlev * This is quite gross, we had to leave the evtchn that led to this
122*10175SStuart.Maybee@Sun.COM * invocation in a per-cpu mailbox, retrieve it now.
1235084Sjohnlev * We do this because the interface doesn't offer us a way to pass
1245084Sjohnlev * a dynamic argument up through the generic interrupt service layer.
1255084Sjohnlev * The mailbox is safe since we either run with interrupts disabled or
1265084Sjohnlev * non-preemptable till we reach here.
1275084Sjohnlev */
128*10175SStuart.Maybee@Sun.COM port = CPU->cpu_m.mcpu_ec_mbox;
1295084Sjohnlev ASSERT(port != 0);
130*10175SStuart.Maybee@Sun.COM CPU->cpu_m.mcpu_ec_mbox = 0;
1315084Sjohnlev ec_clear_evtchn(port);
1325084Sjohnlev mutex_enter(&port_user_lock);
1335084Sjohnlev
1345084Sjohnlev if ((ep = port_user[port]) != NULL) {
1355084Sjohnlev mutex_enter(&ep->evtchn_lock);
1365084Sjohnlev if ((ep->ring_prod - ep->ring_cons) < EVTCHN_RING_SIZE) {
1375084Sjohnlev ep->ring[EVTCHN_RING_MASK(ep->ring_prod)] = port;
1385084Sjohnlev /*
1395084Sjohnlev * Wake up reader when ring goes non-empty
1405084Sjohnlev */
1415084Sjohnlev if (ep->ring_cons == ep->ring_prod++) {
1425084Sjohnlev cv_signal(&ep->evtchn_wait);
1435084Sjohnlev mutex_exit(&ep->evtchn_lock);
1445084Sjohnlev pollwakeup(&ep->evtchn_pollhead,
1455084Sjohnlev POLLIN | POLLRDNORM);
1465084Sjohnlev goto done;
1475084Sjohnlev }
1485084Sjohnlev } else {
1495084Sjohnlev ep->ring_overflow = 1;
1505084Sjohnlev }
1515084Sjohnlev mutex_exit(&ep->evtchn_lock);
1525084Sjohnlev }
1535084Sjohnlev
1545084Sjohnlev done:
1555084Sjohnlev mutex_exit(&port_user_lock);
1565084Sjohnlev }
1575084Sjohnlev
1585084Sjohnlev /* ARGSUSED */
1595084Sjohnlev static int
evtchndrv_read(dev_t dev,struct uio * uio,cred_t * cr)1606784Sjohnlev evtchndrv_read(dev_t dev, struct uio *uio, cred_t *cr)
1615084Sjohnlev {
1625084Sjohnlev int rc = 0;
1635084Sjohnlev ssize_t count;
1645084Sjohnlev unsigned int c, p, bytes1 = 0, bytes2 = 0;
1655084Sjohnlev struct evtsoftdata *ep;
1665084Sjohnlev minor_t minor = getminor(dev);
1675084Sjohnlev
1686784Sjohnlev if (secpolicy_xvm_control(cr))
1696784Sjohnlev return (EPERM);
1706784Sjohnlev
1715084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
1725084Sjohnlev
1735084Sjohnlev /* Whole number of ports. */
1745084Sjohnlev count = uio->uio_resid;
1755084Sjohnlev count &= ~(sizeof (evtchn_port_t) - 1);
1765084Sjohnlev
1775084Sjohnlev if (count == 0)
1785084Sjohnlev return (0);
1795084Sjohnlev
1805084Sjohnlev if (count > PAGESIZE)
1815084Sjohnlev count = PAGESIZE;
1825084Sjohnlev
1835084Sjohnlev mutex_enter(&ep->evtchn_lock);
1845084Sjohnlev for (;;) {
1855084Sjohnlev if (ep->ring_overflow) {
1865084Sjohnlev rc = EFBIG;
1875084Sjohnlev goto done;
1885084Sjohnlev }
1895084Sjohnlev
1905084Sjohnlev if ((c = ep->ring_cons) != (p = ep->ring_prod))
1915084Sjohnlev break;
1925084Sjohnlev
1935084Sjohnlev if (uio->uio_fmode & O_NONBLOCK) {
1945084Sjohnlev rc = EAGAIN;
1955084Sjohnlev goto done;
1965084Sjohnlev }
1975084Sjohnlev
1985084Sjohnlev if (cv_wait_sig(&ep->evtchn_wait, &ep->evtchn_lock) == 0) {
1995084Sjohnlev rc = EINTR;
2005084Sjohnlev goto done;
2015084Sjohnlev }
2025084Sjohnlev }
2035084Sjohnlev
2045084Sjohnlev /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
2055084Sjohnlev if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
2065084Sjohnlev bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
2075084Sjohnlev sizeof (evtchn_port_t);
2085084Sjohnlev bytes2 = EVTCHN_RING_MASK(p) * sizeof (evtchn_port_t);
2095084Sjohnlev } else {
2105084Sjohnlev bytes1 = (p - c) * sizeof (evtchn_port_t);
2115084Sjohnlev bytes2 = 0;
2125084Sjohnlev }
2135084Sjohnlev
2145084Sjohnlev /* Truncate chunks according to caller's maximum byte count. */
2155084Sjohnlev if (bytes1 > count) {
2165084Sjohnlev bytes1 = count;
2175084Sjohnlev bytes2 = 0;
2185084Sjohnlev } else if ((bytes1 + bytes2) > count) {
2195084Sjohnlev bytes2 = count - bytes1;
2205084Sjohnlev }
2215084Sjohnlev
2225084Sjohnlev if (uiomove(&ep->ring[EVTCHN_RING_MASK(c)], bytes1, UIO_READ, uio) ||
2235084Sjohnlev ((bytes2 != 0) && uiomove(&ep->ring[0], bytes2, UIO_READ, uio))) {
2245084Sjohnlev rc = EFAULT;
2255084Sjohnlev goto done;
2265084Sjohnlev }
2275084Sjohnlev
2285084Sjohnlev ep->ring_cons += (bytes1 + bytes2) / sizeof (evtchn_port_t);
2295084Sjohnlev done:
2305084Sjohnlev mutex_exit(&ep->evtchn_lock);
2315084Sjohnlev return (rc);
2325084Sjohnlev }
2335084Sjohnlev
2345084Sjohnlev /* ARGSUSED */
2355084Sjohnlev static int
evtchndrv_write(dev_t dev,struct uio * uio,cred_t * cr)2366784Sjohnlev evtchndrv_write(dev_t dev, struct uio *uio, cred_t *cr)
2375084Sjohnlev {
2385084Sjohnlev int rc, i;
2395084Sjohnlev ssize_t count;
2405084Sjohnlev evtchn_port_t *kbuf;
2415084Sjohnlev struct evtsoftdata *ep;
2425084Sjohnlev ulong_t flags;
2435084Sjohnlev minor_t minor = getminor(dev);
244*10175SStuart.Maybee@Sun.COM evtchn_port_t sbuf[32];
2455084Sjohnlev
2466784Sjohnlev if (secpolicy_xvm_control(cr))
2476784Sjohnlev return (EPERM);
2486784Sjohnlev
2495084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
2505084Sjohnlev
2515084Sjohnlev
2525084Sjohnlev /* Whole number of ports. */
2535084Sjohnlev count = uio->uio_resid;
2545084Sjohnlev count &= ~(sizeof (evtchn_port_t) - 1);
2555084Sjohnlev
256*10175SStuart.Maybee@Sun.COM if (count == 0)
257*10175SStuart.Maybee@Sun.COM return (0);
2585084Sjohnlev
2595084Sjohnlev if (count > PAGESIZE)
2605084Sjohnlev count = PAGESIZE;
2615084Sjohnlev
262*10175SStuart.Maybee@Sun.COM if (count <= sizeof (sbuf))
263*10175SStuart.Maybee@Sun.COM kbuf = sbuf;
264*10175SStuart.Maybee@Sun.COM else
265*10175SStuart.Maybee@Sun.COM kbuf = kmem_alloc(PAGESIZE, KM_SLEEP);
2665084Sjohnlev if ((rc = uiomove(kbuf, count, UIO_WRITE, uio)) != 0)
2675084Sjohnlev goto out;
2685084Sjohnlev
2695084Sjohnlev mutex_enter(&port_user_lock);
2705084Sjohnlev for (i = 0; i < (count / sizeof (evtchn_port_t)); i++)
2715084Sjohnlev if ((kbuf[i] < NR_EVENT_CHANNELS) &&
2725084Sjohnlev (port_user[kbuf[i]] == ep)) {
2735084Sjohnlev flags = intr_clear();
2745084Sjohnlev ec_unmask_evtchn(kbuf[i]);
2755084Sjohnlev intr_restore(flags);
2765084Sjohnlev }
2775084Sjohnlev mutex_exit(&port_user_lock);
2785084Sjohnlev
2795084Sjohnlev out:
280*10175SStuart.Maybee@Sun.COM if (kbuf != sbuf)
281*10175SStuart.Maybee@Sun.COM kmem_free(kbuf, PAGESIZE);
2825084Sjohnlev return (rc);
2835084Sjohnlev }
2845084Sjohnlev
2855084Sjohnlev static void
evtchn_bind_to_user(struct evtsoftdata * u,int port)2865084Sjohnlev evtchn_bind_to_user(struct evtsoftdata *u, int port)
2875084Sjohnlev {
2885084Sjohnlev ulong_t flags;
2895084Sjohnlev
2905084Sjohnlev /*
2915084Sjohnlev * save away the PID of the last process to bind to this event channel.
2925084Sjohnlev * Useful for debugging.
2935084Sjohnlev */
2945084Sjohnlev u->pid = ddi_get_pid();
2955084Sjohnlev
2965084Sjohnlev mutex_enter(&port_user_lock);
2975084Sjohnlev ASSERT(port_user[port] == NULL);
2985084Sjohnlev port_user[port] = u;
2995084Sjohnlev ec_irq_add_evtchn(ec_dev_irq, port);
3005084Sjohnlev flags = intr_clear();
3015084Sjohnlev ec_unmask_evtchn(port);
3025084Sjohnlev intr_restore(flags);
3035084Sjohnlev mutex_exit(&port_user_lock);
3045084Sjohnlev }
3055084Sjohnlev
3065084Sjohnlev static void
evtchndrv_close_evtchn(int port)3075084Sjohnlev evtchndrv_close_evtchn(int port)
3085084Sjohnlev {
3095084Sjohnlev struct evtsoftdata *ep;
3105084Sjohnlev
3115084Sjohnlev ASSERT(MUTEX_HELD(&port_user_lock));
3125084Sjohnlev ep = port_user[port];
3135084Sjohnlev ASSERT(ep != NULL);
3145084Sjohnlev (void) ec_mask_evtchn(port);
3155084Sjohnlev /*
3165084Sjohnlev * It is possible the event is in transit to us.
3175084Sjohnlev * If it is already in the ring buffer, then a client may
3185084Sjohnlev * get a spurious event notification on the next read of
3195084Sjohnlev * of the evtchn device. Clients will need to be able to
3205084Sjohnlev * handle getting a spurious event notification.
3215084Sjohnlev */
3225084Sjohnlev port_user[port] = NULL;
3235084Sjohnlev /*
3245084Sjohnlev * The event is masked and should stay so, clean it up.
3255084Sjohnlev */
3265084Sjohnlev ec_irq_rm_evtchn(ec_dev_irq, port);
3275084Sjohnlev }
3285084Sjohnlev
3295084Sjohnlev /* ARGSUSED */
3305084Sjohnlev static int
evtchndrv_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cr,int * rvalp)3316784Sjohnlev evtchndrv_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr,
3325084Sjohnlev int *rvalp)
3335084Sjohnlev {
3345084Sjohnlev int err = 0;
3355084Sjohnlev struct evtsoftdata *ep;
3365084Sjohnlev minor_t minor = getminor(dev);
3375084Sjohnlev
3386784Sjohnlev if (secpolicy_xvm_control(cr))
3396784Sjohnlev return (EPERM);
3406784Sjohnlev
3415084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
3425084Sjohnlev
3435084Sjohnlev *rvalp = 0;
3445084Sjohnlev
3455084Sjohnlev switch (cmd) {
3465084Sjohnlev case IOCTL_EVTCHN_BIND_VIRQ: {
3475084Sjohnlev struct ioctl_evtchn_bind_virq bind;
3485084Sjohnlev
3495084Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) {
3505084Sjohnlev err = EFAULT;
3515084Sjohnlev break;
3525084Sjohnlev }
3535084Sjohnlev
3545084Sjohnlev if ((err = xen_bind_virq(bind.virq, 0, rvalp)) != 0)
3555084Sjohnlev break;
3565084Sjohnlev
3575084Sjohnlev evtchn_bind_to_user(ep, *rvalp);
3585084Sjohnlev break;
3595084Sjohnlev }
3605084Sjohnlev
3615084Sjohnlev case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
3625084Sjohnlev struct ioctl_evtchn_bind_interdomain bind;
3635084Sjohnlev
3645084Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) {
3655084Sjohnlev err = EFAULT;
3665084Sjohnlev break;
3675084Sjohnlev }
3685084Sjohnlev
3695084Sjohnlev if ((err = xen_bind_interdomain(bind.remote_domain,
3705084Sjohnlev bind.remote_port, rvalp)) != 0)
3715084Sjohnlev break;
3725084Sjohnlev
3735084Sjohnlev ec_bind_vcpu(*rvalp, 0);
3745084Sjohnlev evtchn_bind_to_user(ep, *rvalp);
3755084Sjohnlev break;
3765084Sjohnlev }
3775084Sjohnlev
3785084Sjohnlev case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
3795084Sjohnlev struct ioctl_evtchn_bind_unbound_port bind;
3805084Sjohnlev
3815084Sjohnlev if (copyin((void *)data, &bind, sizeof (bind))) {
3825084Sjohnlev err = EFAULT;
3835084Sjohnlev break;
3845084Sjohnlev }
3855084Sjohnlev
3865084Sjohnlev if ((err = xen_alloc_unbound_evtchn(bind.remote_domain,
3875084Sjohnlev rvalp)) != 0)
3885084Sjohnlev break;
3895084Sjohnlev
3905084Sjohnlev evtchn_bind_to_user(ep, *rvalp);
3915084Sjohnlev break;
3925084Sjohnlev }
3935084Sjohnlev
3945084Sjohnlev case IOCTL_EVTCHN_UNBIND: {
3955084Sjohnlev struct ioctl_evtchn_unbind unbind;
3965084Sjohnlev
3975084Sjohnlev if (copyin((void *)data, &unbind, sizeof (unbind))) {
3985084Sjohnlev err = EFAULT;
3995084Sjohnlev break;
4005084Sjohnlev }
4015084Sjohnlev
4025084Sjohnlev if (unbind.port >= NR_EVENT_CHANNELS) {
4035084Sjohnlev err = EFAULT;
4045084Sjohnlev break;
4055084Sjohnlev }
4065084Sjohnlev
4075084Sjohnlev mutex_enter(&port_user_lock);
4085084Sjohnlev
4095084Sjohnlev if (port_user[unbind.port] != ep) {
4105084Sjohnlev mutex_exit(&port_user_lock);
4115084Sjohnlev err = ENOTCONN;
4125084Sjohnlev break;
4135084Sjohnlev }
4145084Sjohnlev
4155084Sjohnlev evtchndrv_close_evtchn(unbind.port);
4165084Sjohnlev mutex_exit(&port_user_lock);
4175084Sjohnlev break;
4185084Sjohnlev }
4195084Sjohnlev
4205084Sjohnlev case IOCTL_EVTCHN_NOTIFY: {
4215084Sjohnlev struct ioctl_evtchn_notify notify;
4225084Sjohnlev
4235084Sjohnlev if (copyin((void *)data, ¬ify, sizeof (notify))) {
4245084Sjohnlev err = EFAULT;
4255084Sjohnlev break;
4265084Sjohnlev }
4275084Sjohnlev
4285084Sjohnlev if (notify.port >= NR_EVENT_CHANNELS) {
4295084Sjohnlev err = EINVAL;
4305084Sjohnlev } else if (port_user[notify.port] != ep) {
4315084Sjohnlev err = ENOTCONN;
4325084Sjohnlev } else {
4335084Sjohnlev ec_notify_via_evtchn(notify.port);
4345084Sjohnlev }
4355084Sjohnlev break;
4365084Sjohnlev }
4375084Sjohnlev
4385084Sjohnlev default:
4395084Sjohnlev err = ENOSYS;
4405084Sjohnlev }
4415084Sjohnlev
4425084Sjohnlev return (err);
4435084Sjohnlev }
4445084Sjohnlev
4455084Sjohnlev static int
evtchndrv_poll(dev_t dev,short ev,int anyyet,short * revp,pollhead_t ** phpp)4465084Sjohnlev evtchndrv_poll(dev_t dev, short ev, int anyyet, short *revp, pollhead_t **phpp)
4475084Sjohnlev {
4485084Sjohnlev struct evtsoftdata *ep;
4495084Sjohnlev minor_t minor = getminor(dev);
4505084Sjohnlev short mask = 0;
4515084Sjohnlev
4525084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
4535084Sjohnlev *phpp = (struct pollhead *)NULL;
4545084Sjohnlev
4555084Sjohnlev if (ev & POLLOUT)
4565084Sjohnlev mask |= POLLOUT;
4575084Sjohnlev if (ep->ring_overflow)
4585084Sjohnlev mask |= POLLERR;
4595084Sjohnlev if (ev & (POLLIN | POLLRDNORM)) {
4605084Sjohnlev mutex_enter(&ep->evtchn_lock);
4615084Sjohnlev if (ep->ring_cons != ep->ring_prod)
4625084Sjohnlev mask |= (POLLIN | POLLRDNORM) & ev;
4635084Sjohnlev else
4645084Sjohnlev if (mask == 0 && !anyyet)
4655084Sjohnlev *phpp = &ep->evtchn_pollhead;
4665084Sjohnlev mutex_exit(&ep->evtchn_lock);
4675084Sjohnlev }
4685084Sjohnlev *revp = mask;
4695084Sjohnlev return (0);
4705084Sjohnlev }
4715084Sjohnlev
4725084Sjohnlev
4735084Sjohnlev /* ARGSUSED */
4745084Sjohnlev static int
evtchndrv_open(dev_t * devp,int flag,int otyp,cred_t * credp)4755084Sjohnlev evtchndrv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4765084Sjohnlev {
4775084Sjohnlev struct evtsoftdata *ep;
4785084Sjohnlev minor_t minor = getminor(*devp);
4795084Sjohnlev
4805084Sjohnlev if (otyp == OTYP_BLK)
4815084Sjohnlev return (ENXIO);
4825084Sjohnlev
4835084Sjohnlev /*
4845084Sjohnlev * only allow open on minor = 0 - the clone device
4855084Sjohnlev */
4865084Sjohnlev if (minor != 0)
4875084Sjohnlev return (ENXIO);
4885084Sjohnlev
4895084Sjohnlev /*
4905084Sjohnlev * find a free slot and grab it
4915084Sjohnlev */
4925084Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex);
4935084Sjohnlev for (minor = 1; minor < evtchndrv_nclones; minor++) {
4945084Sjohnlev if (evtchndrv_clone_tab[minor] == 0) {
4955084Sjohnlev evtchndrv_clone_tab[minor] = 1;
4965084Sjohnlev break;
4975084Sjohnlev }
4985084Sjohnlev }
4995084Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex);
5005084Sjohnlev if (minor == evtchndrv_nclones)
5015084Sjohnlev return (EAGAIN);
5025084Sjohnlev
5035084Sjohnlev /* Allocate softstate structure */
5045084Sjohnlev if (ddi_soft_state_zalloc(evtchndrv_statep,
5055084Sjohnlev EVTCHNDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
5065084Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex);
5075084Sjohnlev evtchndrv_clone_tab[minor] = 0;
5085084Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex);
5095084Sjohnlev return (EAGAIN);
5105084Sjohnlev }
5115084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
5125084Sjohnlev
5135084Sjohnlev /* ... and init it */
5145084Sjohnlev ep->dip = evtchndrv_dip;
5155084Sjohnlev
5165084Sjohnlev cv_init(&ep->evtchn_wait, NULL, CV_DEFAULT, NULL);
5175084Sjohnlev mutex_init(&ep->evtchn_lock, NULL, MUTEX_DEFAULT, NULL);
5185084Sjohnlev
5195084Sjohnlev ep->ring = kmem_alloc(PAGESIZE, KM_SLEEP);
5205084Sjohnlev
5215084Sjohnlev /* clone driver */
5225084Sjohnlev *devp = makedevice(getmajor(*devp), minor);
5235084Sjohnlev
5245084Sjohnlev return (0);
5255084Sjohnlev }
5265084Sjohnlev
5275084Sjohnlev /* ARGSUSED */
5285084Sjohnlev static int
evtchndrv_close(dev_t dev,int flag,int otyp,struct cred * credp)5295084Sjohnlev evtchndrv_close(dev_t dev, int flag, int otyp, struct cred *credp)
5305084Sjohnlev {
5315084Sjohnlev struct evtsoftdata *ep;
5325084Sjohnlev minor_t minor = getminor(dev);
5335084Sjohnlev int i;
5345084Sjohnlev
5355084Sjohnlev ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
5365084Sjohnlev if (ep == NULL)
5375084Sjohnlev return (ENXIO);
5385084Sjohnlev
5395084Sjohnlev mutex_enter(&port_user_lock);
5405084Sjohnlev
5415084Sjohnlev
5425084Sjohnlev for (i = 0; i < NR_EVENT_CHANNELS; i++) {
5435084Sjohnlev if (port_user[i] != ep)
5445084Sjohnlev continue;
5455084Sjohnlev
5465084Sjohnlev evtchndrv_close_evtchn(i);
5475084Sjohnlev }
5485084Sjohnlev
5495084Sjohnlev mutex_exit(&port_user_lock);
5505084Sjohnlev
5515084Sjohnlev kmem_free(ep->ring, PAGESIZE);
5525084Sjohnlev ddi_soft_state_free(evtchndrv_statep, EVTCHNDRV_MINOR2INST(minor));
5535084Sjohnlev
5545084Sjohnlev /*
5555084Sjohnlev * free clone tab slot
5565084Sjohnlev */
5575084Sjohnlev mutex_enter(&evtchndrv_clone_tab_mutex);
5585084Sjohnlev evtchndrv_clone_tab[minor] = 0;
5595084Sjohnlev mutex_exit(&evtchndrv_clone_tab_mutex);
5605084Sjohnlev
5615084Sjohnlev return (0);
5625084Sjohnlev }
5635084Sjohnlev
5645084Sjohnlev /* ARGSUSED */
5655084Sjohnlev static int
evtchndrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)5665084Sjohnlev evtchndrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
5675084Sjohnlev {
5685084Sjohnlev dev_t dev = (dev_t)arg;
5695084Sjohnlev minor_t minor = getminor(dev);
5705084Sjohnlev int retval;
5715084Sjohnlev
5725084Sjohnlev switch (cmd) {
5735084Sjohnlev case DDI_INFO_DEVT2DEVINFO:
5745084Sjohnlev if (minor != 0 || evtchndrv_dip == NULL) {
5755084Sjohnlev *result = (void *)NULL;
5765084Sjohnlev retval = DDI_FAILURE;
5775084Sjohnlev } else {
5785084Sjohnlev *result = (void *)evtchndrv_dip;
5795084Sjohnlev retval = DDI_SUCCESS;
5805084Sjohnlev }
5815084Sjohnlev break;
5825084Sjohnlev case DDI_INFO_DEVT2INSTANCE:
5835084Sjohnlev *result = (void *)0;
5845084Sjohnlev retval = DDI_SUCCESS;
5855084Sjohnlev break;
5865084Sjohnlev default:
5875084Sjohnlev retval = DDI_FAILURE;
5885084Sjohnlev }
5895084Sjohnlev return (retval);
5905084Sjohnlev }
5915084Sjohnlev
5925084Sjohnlev
5935084Sjohnlev static int
evtchndrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5945084Sjohnlev evtchndrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5955084Sjohnlev {
5965084Sjohnlev int error;
5975084Sjohnlev int unit = ddi_get_instance(dip);
5985084Sjohnlev
5995084Sjohnlev
6005084Sjohnlev switch (cmd) {
6015084Sjohnlev case DDI_ATTACH:
6025084Sjohnlev break;
6035084Sjohnlev case DDI_RESUME:
6045084Sjohnlev return (DDI_SUCCESS);
6055084Sjohnlev default:
6065084Sjohnlev cmn_err(CE_WARN, "evtchn_attach: unknown cmd 0x%x\n", cmd);
6075084Sjohnlev return (DDI_FAILURE);
6085084Sjohnlev }
6095084Sjohnlev
6105084Sjohnlev /* DDI_ATTACH */
6115084Sjohnlev
6125084Sjohnlev /*
6135084Sjohnlev * only one instance - but we clone using the open routine
6145084Sjohnlev */
6155084Sjohnlev if (ddi_get_instance(dip) > 0)
6165084Sjohnlev return (DDI_FAILURE);
6175084Sjohnlev
6185084Sjohnlev mutex_init(&evtchndrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
6195084Sjohnlev NULL);
6205084Sjohnlev
6215084Sjohnlev error = ddi_create_minor_node(dip, "evtchn", S_IFCHR, unit,
6225084Sjohnlev DDI_PSEUDO, NULL);
6235084Sjohnlev if (error != DDI_SUCCESS)
6245084Sjohnlev goto fail;
6255084Sjohnlev
6265084Sjohnlev /*
6275084Sjohnlev * save dip for getinfo
6285084Sjohnlev */
6295084Sjohnlev evtchndrv_dip = dip;
6305084Sjohnlev ddi_report_dev(dip);
6315084Sjohnlev
6325084Sjohnlev mutex_init(&port_user_lock, NULL, MUTEX_DRIVER, NULL);
6335084Sjohnlev (void) memset(port_user, 0, sizeof (port_user));
6345084Sjohnlev
6355084Sjohnlev ec_dev_irq = ec_dev_alloc_irq();
6365084Sjohnlev (void) add_avintr(NULL, IPL_EVTCHN, (avfunc)evtchn_device_upcall,
6375084Sjohnlev "evtchn_driver", ec_dev_irq, NULL, NULL, NULL, dip);
6385084Sjohnlev
6395084Sjohnlev return (DDI_SUCCESS);
6405084Sjohnlev
6415084Sjohnlev fail:
6425084Sjohnlev (void) evtchndrv_detach(dip, DDI_DETACH);
6435084Sjohnlev return (error);
6445084Sjohnlev }
6455084Sjohnlev
6465084Sjohnlev /*ARGSUSED*/
6475084Sjohnlev static int
evtchndrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6485084Sjohnlev evtchndrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6495084Sjohnlev {
6505084Sjohnlev /*
6515084Sjohnlev * Don't allow detach for now.
6525084Sjohnlev */
6535084Sjohnlev return (DDI_FAILURE);
6545084Sjohnlev }
6555084Sjohnlev
6565084Sjohnlev /* Solaris driver framework */
6575084Sjohnlev
6585084Sjohnlev static struct cb_ops evtchndrv_cb_ops = {
6595084Sjohnlev evtchndrv_open, /* cb_open */
6605084Sjohnlev evtchndrv_close, /* cb_close */
6615084Sjohnlev nodev, /* cb_strategy */
6625084Sjohnlev nodev, /* cb_print */
6635084Sjohnlev nodev, /* cb_dump */
6645084Sjohnlev evtchndrv_read, /* cb_read */
6655084Sjohnlev evtchndrv_write, /* cb_write */
6665084Sjohnlev evtchndrv_ioctl, /* cb_ioctl */
6675084Sjohnlev nodev, /* cb_devmap */
6685084Sjohnlev nodev, /* cb_mmap */
6695084Sjohnlev nodev, /* cb_segmap */
6705084Sjohnlev evtchndrv_poll, /* cb_chpoll */
6715084Sjohnlev ddi_prop_op, /* cb_prop_op */
6725084Sjohnlev 0, /* cb_stream */
6735084Sjohnlev D_NEW | D_MP | D_64BIT /* cb_flag */
6745084Sjohnlev };
6755084Sjohnlev
6765084Sjohnlev static struct dev_ops evtchndrv_dev_ops = {
6775084Sjohnlev DEVO_REV, /* devo_rev */
6785084Sjohnlev 0, /* devo_refcnt */
6795084Sjohnlev evtchndrv_info, /* devo_getinfo */
6805084Sjohnlev nulldev, /* devo_identify */
6815084Sjohnlev nulldev, /* devo_probe */
6825084Sjohnlev evtchndrv_attach, /* devo_attach */
6835084Sjohnlev evtchndrv_detach, /* devo_detach */
6845084Sjohnlev nodev, /* devo_reset */
6855084Sjohnlev &evtchndrv_cb_ops, /* devo_cb_ops */
6865084Sjohnlev NULL, /* devo_bus_ops */
6877656SSherry.Moore@Sun.COM NULL, /* power */
6887656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* devo_quiesce */
6895084Sjohnlev };
6905084Sjohnlev
6915084Sjohnlev static struct modldrv modldrv = {
6925084Sjohnlev &mod_driverops, /* Type of module. This one is a driver */
6937656SSherry.Moore@Sun.COM "Evtchn driver", /* Name of the module. */
6945084Sjohnlev &evtchndrv_dev_ops /* driver ops */
6955084Sjohnlev };
6965084Sjohnlev
6975084Sjohnlev static struct modlinkage modlinkage = {
6985084Sjohnlev MODREV_1,
6995084Sjohnlev &modldrv,
7005084Sjohnlev NULL
7015084Sjohnlev };
7025084Sjohnlev
7035084Sjohnlev int
_init(void)7045084Sjohnlev _init(void)
7055084Sjohnlev {
7065084Sjohnlev int err;
7075084Sjohnlev
7085084Sjohnlev err = ddi_soft_state_init(&evtchndrv_statep,
7095084Sjohnlev sizeof (struct evtsoftdata), 1);
7105084Sjohnlev if (err)
7115084Sjohnlev return (err);
7125084Sjohnlev
7135084Sjohnlev err = mod_install(&modlinkage);
7145084Sjohnlev if (err)
7155084Sjohnlev ddi_soft_state_fini(&evtchndrv_statep);
7165084Sjohnlev else
7175084Sjohnlev evtchndrv_clone_tab = kmem_zalloc(
7185084Sjohnlev sizeof (int) * evtchndrv_nclones, KM_SLEEP);
7195084Sjohnlev return (err);
7205084Sjohnlev }
7215084Sjohnlev
7225084Sjohnlev int
_fini(void)7235084Sjohnlev _fini(void)
7245084Sjohnlev {
7255084Sjohnlev int e;
7265084Sjohnlev
7275084Sjohnlev e = mod_remove(&modlinkage);
7285084Sjohnlev if (e)
7295084Sjohnlev return (e);
7305084Sjohnlev
7315084Sjohnlev ddi_soft_state_fini(&evtchndrv_statep);
7325084Sjohnlev
7335084Sjohnlev return (0);
7345084Sjohnlev }
7355084Sjohnlev
7365084Sjohnlev int
_info(struct modinfo * modinfop)7375084Sjohnlev _info(struct modinfo *modinfop)
7385084Sjohnlev {
7395084Sjohnlev return (mod_info(&modlinkage, modinfop));
7405084Sjohnlev }
741