xref: /titanic_51/usr/src/uts/common/xen/io/evtchn_dev.c (revision 349b53dd4e695e3d833b5380540385145b2d3ae8)
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, &notify, 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