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
evtchn_device_upcall()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
evtchndrv_read(dev_t dev,struct uio * uio,cred_t * cr)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
evtchndrv_write(dev_t dev,struct uio * uio,cred_t * cr)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
evtchn_bind_to_user(struct evtsoftdata * u,int port)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
evtchndrv_close_evtchn(int port)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
evtchndrv_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cr,int * rvalp)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
evtchndrv_poll(dev_t dev,short ev,int anyyet,short * revp,pollhead_t ** phpp)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
evtchndrv_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
evtchndrv_close(dev_t dev,int flag,int otyp,struct cred * credp)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
evtchndrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)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
evtchndrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
evtchndrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)737843e1988Sjohnlev _info(struct modinfo *modinfop)
738843e1988Sjohnlev {
739843e1988Sjohnlev return (mod_info(&modlinkage, modinfop));
740843e1988Sjohnlev }
741