xref: /illumos-gate/usr/src/uts/common/xen/io/evtchn_dev.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * evtchn.c
31  *
32  * Driver for receiving and demuxing event-channel signals.
33  *
34  * Copyright (c) 2004-2005, K A Fraser
35  * Multi-process extensions Copyright (c) 2004, Steven Smith
36  *
37  * This file may be distributed separately from the Linux kernel, or
38  * incorporated into other software packages, subject to the following license:
39  *
40  * Permission is hereby granted, free of charge, to any person obtaining a copy
41  * of this source file (the "Software"), to deal in the Software without
42  * restriction, including without limitation the rights to use, copy, modify,
43  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
44  * and to permit persons to whom the Software is furnished to do so, subject to
45  * the following conditions:
46  *
47  * The above copyright notice and this permission notice shall be included in
48  * all copies or substantial portions of the Software.
49  *
50  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
55  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
56  * IN THE SOFTWARE.
57  */
58 
59 #include <sys/types.h>
60 #include <sys/hypervisor.h>
61 #include <sys/machsystm.h>
62 #include <sys/mutex.h>
63 #include <sys/evtchn_impl.h>
64 #include <sys/ddi_impldefs.h>
65 #include <sys/avintr.h>
66 #include <sys/cpuvar.h>
67 #include <sys/smp_impldefs.h>
68 #include <sys/archsystm.h>
69 #include <sys/sysmacros.h>
70 #include <sys/fcntl.h>
71 #include <sys/open.h>
72 #include <sys/stat.h>
73 #include <sys/psm.h>
74 #include <sys/cpu.h>
75 #include <sys/cmn_err.h>
76 #include <sys/xen_errno.h>
77 #include <xen/sys/evtchn.h>
78 
79 /* Some handy macros */
80 #define	EVTCHNDRV_MINOR2INST(minor)	((int)(minor))
81 #define	EVTCHNDRV_DEFAULT_NCLONES 	256
82 #define	EVTCHNDRV_INST2SOFTS(inst)	\
83 	(ddi_get_soft_state(evtchndrv_statep, (inst)))
84 
85 /* Soft state data structure for evtchn driver */
86 struct evtsoftdata {
87 	dev_info_t *dip;
88 	/* Notification ring, accessed via /dev/xen/evtchn. */
89 #define	EVTCHN_RING_SIZE	(PAGESIZE / sizeof (evtchn_port_t))
90 #define	EVTCHN_RING_MASK(_i)	((_i) & (EVTCHN_RING_SIZE - 1))
91 	evtchn_port_t *ring;
92 	unsigned int ring_cons, ring_prod, ring_overflow;
93 
94 	/* Processes wait on this queue when ring is empty. */
95 	kcondvar_t evtchn_wait;
96 	kmutex_t evtchn_lock;
97 	struct pollhead evtchn_pollhead;
98 
99 	/* last pid to bind to this event channel. debug aid. */
100 	pid_t pid;
101 };
102 
103 static void *evtchndrv_statep;
104 int evtchndrv_nclones = EVTCHNDRV_DEFAULT_NCLONES;
105 static int *evtchndrv_clone_tab;
106 static dev_info_t *evtchndrv_dip;
107 static kmutex_t evtchndrv_clone_tab_mutex;
108 
109 static int evtchndrv_detach(dev_info_t *, ddi_detach_cmd_t);
110 
111 /* Who's bound to each port? */
112 static struct evtsoftdata *port_user[NR_EVENT_CHANNELS];
113 static kmutex_t port_user_lock;
114 
115 void
116 evtchn_device_upcall()
117 {
118 	struct evtsoftdata *ep;
119 	int port;
120 
121 	/*
122 	 * This is quite gross, we had to leave the evtchn that led to this
123 	 * invocation in a global mailbox, retrieve it now.
124 	 * We do this because the interface doesn't offer us a way to pass
125 	 * a dynamic argument up through the generic interrupt service layer.
126 	 * The mailbox is safe since we either run with interrupts disabled or
127 	 * non-preemptable till we reach here.
128 	 */
129 	port = ec_dev_mbox;
130 	ASSERT(port != 0);
131 	ec_dev_mbox = 0;
132 	ec_clear_evtchn(port);
133 	mutex_enter(&port_user_lock);
134 
135 	if ((ep = port_user[port]) != NULL) {
136 		mutex_enter(&ep->evtchn_lock);
137 		if ((ep->ring_prod - ep->ring_cons) < EVTCHN_RING_SIZE) {
138 			ep->ring[EVTCHN_RING_MASK(ep->ring_prod)] = port;
139 			/*
140 			 * Wake up reader when ring goes non-empty
141 			 */
142 			if (ep->ring_cons == ep->ring_prod++) {
143 				cv_signal(&ep->evtchn_wait);
144 				mutex_exit(&ep->evtchn_lock);
145 				pollwakeup(&ep->evtchn_pollhead,
146 				    POLLIN | POLLRDNORM);
147 				goto done;
148 			}
149 		} else {
150 			ep->ring_overflow = 1;
151 		}
152 		mutex_exit(&ep->evtchn_lock);
153 	}
154 
155 done:
156 	mutex_exit(&port_user_lock);
157 }
158 
159 /* ARGSUSED */
160 static int
161 evtchndrv_read(dev_t dev, struct uio *uio, cred_t *cred)
162 {
163 	int rc = 0;
164 	ssize_t count;
165 	unsigned int c, p, bytes1 = 0, bytes2 = 0;
166 	struct evtsoftdata *ep;
167 	minor_t minor = getminor(dev);
168 
169 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
170 
171 	/* Whole number of ports. */
172 	count = uio->uio_resid;
173 	count &= ~(sizeof (evtchn_port_t) - 1);
174 
175 	if (count == 0)
176 		return (0);
177 
178 	if (count > PAGESIZE)
179 		count = PAGESIZE;
180 
181 	mutex_enter(&ep->evtchn_lock);
182 	for (;;) {
183 		if (ep->ring_overflow) {
184 			rc = EFBIG;
185 			goto done;
186 		}
187 
188 		if ((c = ep->ring_cons) != (p = ep->ring_prod))
189 			break;
190 
191 		if (uio->uio_fmode & O_NONBLOCK) {
192 			rc = EAGAIN;
193 			goto done;
194 		}
195 
196 		if (cv_wait_sig(&ep->evtchn_wait, &ep->evtchn_lock) == 0) {
197 			rc = EINTR;
198 			goto done;
199 		}
200 	}
201 
202 	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
203 	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
204 		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
205 		    sizeof (evtchn_port_t);
206 		bytes2 = EVTCHN_RING_MASK(p) * sizeof (evtchn_port_t);
207 	} else {
208 		bytes1 = (p - c) * sizeof (evtchn_port_t);
209 		bytes2 = 0;
210 	}
211 
212 	/* Truncate chunks according to caller's maximum byte count. */
213 	if (bytes1 > count) {
214 		bytes1 = count;
215 		bytes2 = 0;
216 	} else if ((bytes1 + bytes2) > count) {
217 		bytes2 = count - bytes1;
218 	}
219 
220 	if (uiomove(&ep->ring[EVTCHN_RING_MASK(c)], bytes1, UIO_READ, uio) ||
221 	    ((bytes2 != 0) && uiomove(&ep->ring[0], bytes2, UIO_READ, uio))) {
222 		rc = EFAULT;
223 		goto done;
224 	}
225 
226 	ep->ring_cons += (bytes1 + bytes2) / sizeof (evtchn_port_t);
227 done:
228 	mutex_exit(&ep->evtchn_lock);
229 	return (rc);
230 }
231 
232 /* ARGSUSED */
233 static int
234 evtchndrv_write(dev_t dev, struct uio *uio, cred_t *cred)
235 {
236 	int  rc, i;
237 	ssize_t count;
238 	evtchn_port_t *kbuf;
239 	struct evtsoftdata *ep;
240 	ulong_t flags;
241 	minor_t minor = getminor(dev);
242 
243 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
244 
245 	kbuf = kmem_alloc(PAGESIZE, KM_SLEEP);
246 
247 	/* Whole number of ports. */
248 	count = uio->uio_resid;
249 	count &= ~(sizeof (evtchn_port_t) - 1);
250 
251 	if (count == 0) {
252 		rc = 0;
253 		goto out;
254 	}
255 
256 	if (count > PAGESIZE)
257 		count = PAGESIZE;
258 
259 	if ((rc = uiomove(kbuf, count, UIO_WRITE, uio)) != 0)
260 		goto out;
261 
262 	mutex_enter(&port_user_lock);
263 	for (i = 0; i < (count / sizeof (evtchn_port_t)); i++)
264 		if ((kbuf[i] < NR_EVENT_CHANNELS) &&
265 		    (port_user[kbuf[i]] == ep)) {
266 			flags = intr_clear();
267 			ec_unmask_evtchn(kbuf[i]);
268 			intr_restore(flags);
269 		}
270 	mutex_exit(&port_user_lock);
271 
272 out:
273 	kmem_free(kbuf, PAGESIZE);
274 	return (rc);
275 }
276 
277 static void
278 evtchn_bind_to_user(struct evtsoftdata *u, int port)
279 {
280 	ulong_t flags;
281 
282 	/*
283 	 * save away the PID of the last process to bind to this event channel.
284 	 * Useful for debugging.
285 	 */
286 	u->pid = ddi_get_pid();
287 
288 	mutex_enter(&port_user_lock);
289 	ASSERT(port_user[port] == NULL);
290 	port_user[port] = u;
291 	ec_irq_add_evtchn(ec_dev_irq, port);
292 	flags = intr_clear();
293 	ec_unmask_evtchn(port);
294 	intr_restore(flags);
295 	mutex_exit(&port_user_lock);
296 }
297 
298 static void
299 evtchndrv_close_evtchn(int port)
300 {
301 	struct evtsoftdata *ep;
302 
303 	ASSERT(MUTEX_HELD(&port_user_lock));
304 	ep = port_user[port];
305 	ASSERT(ep != NULL);
306 	(void) ec_mask_evtchn(port);
307 	/*
308 	 * It is possible the event is in transit to us.
309 	 * If it is already in the ring buffer, then a client may
310 	 * get a spurious event notification on the next read of
311 	 * of the evtchn device.  Clients will need to be able to
312 	 * handle getting a spurious event notification.
313 	 */
314 	port_user[port] = NULL;
315 	/*
316 	 * The event is masked and should stay so, clean it up.
317 	 */
318 	ec_irq_rm_evtchn(ec_dev_irq, port);
319 }
320 
321 /* ARGSUSED */
322 static int
323 evtchndrv_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred,
324     int *rvalp)
325 {
326 	int err = 0;
327 	struct evtsoftdata *ep;
328 	minor_t minor = getminor(dev);
329 
330 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
331 
332 	*rvalp = 0;
333 
334 	switch (cmd) {
335 	case IOCTL_EVTCHN_BIND_VIRQ: {
336 		struct ioctl_evtchn_bind_virq bind;
337 
338 		if (copyin((void *)data, &bind, sizeof (bind))) {
339 			err = EFAULT;
340 			break;
341 		}
342 
343 		if ((err = xen_bind_virq(bind.virq, 0, rvalp)) != 0)
344 			break;
345 
346 		evtchn_bind_to_user(ep, *rvalp);
347 		break;
348 	}
349 
350 	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
351 		struct ioctl_evtchn_bind_interdomain bind;
352 
353 		if (copyin((void *)data, &bind, sizeof (bind))) {
354 			err = EFAULT;
355 			break;
356 		}
357 
358 		if ((err = xen_bind_interdomain(bind.remote_domain,
359 		    bind.remote_port, rvalp)) != 0)
360 			break;
361 
362 		ec_bind_vcpu(*rvalp, 0);
363 		evtchn_bind_to_user(ep, *rvalp);
364 		break;
365 	}
366 
367 	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
368 		struct ioctl_evtchn_bind_unbound_port bind;
369 
370 		if (copyin((void *)data, &bind, sizeof (bind))) {
371 			err = EFAULT;
372 			break;
373 		}
374 
375 		if ((err = xen_alloc_unbound_evtchn(bind.remote_domain,
376 		    rvalp)) != 0)
377 			break;
378 
379 		evtchn_bind_to_user(ep, *rvalp);
380 		break;
381 	}
382 
383 	case IOCTL_EVTCHN_UNBIND: {
384 		struct ioctl_evtchn_unbind unbind;
385 
386 		if (copyin((void *)data, &unbind, sizeof (unbind))) {
387 			err = EFAULT;
388 			break;
389 		}
390 
391 		if (unbind.port >= NR_EVENT_CHANNELS) {
392 			err = EFAULT;
393 			break;
394 		}
395 
396 		mutex_enter(&port_user_lock);
397 
398 		if (port_user[unbind.port] != ep) {
399 			mutex_exit(&port_user_lock);
400 			err = ENOTCONN;
401 			break;
402 		}
403 
404 		evtchndrv_close_evtchn(unbind.port);
405 		mutex_exit(&port_user_lock);
406 		break;
407 	}
408 
409 	case IOCTL_EVTCHN_NOTIFY: {
410 		struct ioctl_evtchn_notify notify;
411 
412 		if (copyin((void *)data, &notify, sizeof (notify))) {
413 			err = EFAULT;
414 			break;
415 		}
416 
417 		if (notify.port >= NR_EVENT_CHANNELS) {
418 			err = EINVAL;
419 		} else if (port_user[notify.port] != ep) {
420 			err = ENOTCONN;
421 		} else {
422 			ec_notify_via_evtchn(notify.port);
423 		}
424 		break;
425 	}
426 
427 	default:
428 		err = ENOSYS;
429 	}
430 
431 	return (err);
432 }
433 
434 static int
435 evtchndrv_poll(dev_t dev, short ev, int anyyet, short *revp, pollhead_t **phpp)
436 {
437 	struct evtsoftdata *ep;
438 	minor_t minor = getminor(dev);
439 	short mask = 0;
440 
441 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
442 	*phpp = (struct pollhead *)NULL;
443 
444 	if (ev & POLLOUT)
445 		mask |= POLLOUT;
446 	if (ep->ring_overflow)
447 		mask |= POLLERR;
448 	if (ev & (POLLIN | POLLRDNORM)) {
449 		mutex_enter(&ep->evtchn_lock);
450 		if (ep->ring_cons != ep->ring_prod)
451 			mask |= (POLLIN | POLLRDNORM) & ev;
452 		else
453 			if (mask == 0 && !anyyet)
454 				*phpp = &ep->evtchn_pollhead;
455 		mutex_exit(&ep->evtchn_lock);
456 	}
457 	*revp = mask;
458 	return (0);
459 }
460 
461 
462 /* ARGSUSED */
463 static int
464 evtchndrv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
465 {
466 	struct evtsoftdata *ep;
467 	minor_t minor = getminor(*devp);
468 
469 	if (otyp == OTYP_BLK)
470 		return (ENXIO);
471 
472 	/*
473 	 * only allow open on minor = 0 - the clone device
474 	 */
475 	if (minor != 0)
476 		return (ENXIO);
477 
478 	/*
479 	 * find a free slot and grab it
480 	 */
481 	mutex_enter(&evtchndrv_clone_tab_mutex);
482 	for (minor = 1; minor < evtchndrv_nclones; minor++) {
483 		if (evtchndrv_clone_tab[minor] == 0) {
484 			evtchndrv_clone_tab[minor] = 1;
485 			break;
486 		}
487 	}
488 	mutex_exit(&evtchndrv_clone_tab_mutex);
489 	if (minor == evtchndrv_nclones)
490 		return (EAGAIN);
491 
492 	/* Allocate softstate structure */
493 	if (ddi_soft_state_zalloc(evtchndrv_statep,
494 	    EVTCHNDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
495 		mutex_enter(&evtchndrv_clone_tab_mutex);
496 		evtchndrv_clone_tab[minor] = 0;
497 		mutex_exit(&evtchndrv_clone_tab_mutex);
498 		return (EAGAIN);
499 	}
500 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
501 
502 	/* ... and init it */
503 	ep->dip = evtchndrv_dip;
504 
505 	cv_init(&ep->evtchn_wait, NULL, CV_DEFAULT, NULL);
506 	mutex_init(&ep->evtchn_lock, NULL, MUTEX_DEFAULT, NULL);
507 
508 	ep->ring = kmem_alloc(PAGESIZE, KM_SLEEP);
509 
510 	/* clone driver */
511 	*devp = makedevice(getmajor(*devp), minor);
512 
513 	return (0);
514 }
515 
516 /* ARGSUSED */
517 static int
518 evtchndrv_close(dev_t dev, int flag, int otyp, struct cred *credp)
519 {
520 	struct evtsoftdata *ep;
521 	minor_t minor = getminor(dev);
522 	int i;
523 
524 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
525 	if (ep == NULL)
526 		return (ENXIO);
527 
528 	mutex_enter(&port_user_lock);
529 
530 
531 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
532 		if (port_user[i] != ep)
533 			continue;
534 
535 		evtchndrv_close_evtchn(i);
536 	}
537 
538 	mutex_exit(&port_user_lock);
539 
540 	kmem_free(ep->ring, PAGESIZE);
541 	ddi_soft_state_free(evtchndrv_statep, EVTCHNDRV_MINOR2INST(minor));
542 
543 	/*
544 	 * free clone tab slot
545 	 */
546 	mutex_enter(&evtchndrv_clone_tab_mutex);
547 	evtchndrv_clone_tab[minor] = 0;
548 	mutex_exit(&evtchndrv_clone_tab_mutex);
549 
550 	return (0);
551 }
552 
553 /* ARGSUSED */
554 static int
555 evtchndrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
556 {
557 	dev_t	dev = (dev_t)arg;
558 	minor_t	minor = getminor(dev);
559 	int	retval;
560 
561 	switch (cmd) {
562 	case DDI_INFO_DEVT2DEVINFO:
563 		if (minor != 0 || evtchndrv_dip == NULL) {
564 			*result = (void *)NULL;
565 			retval = DDI_FAILURE;
566 		} else {
567 			*result = (void *)evtchndrv_dip;
568 			retval = DDI_SUCCESS;
569 		}
570 		break;
571 	case DDI_INFO_DEVT2INSTANCE:
572 		*result = (void *)0;
573 		retval = DDI_SUCCESS;
574 		break;
575 	default:
576 		retval = DDI_FAILURE;
577 	}
578 	return (retval);
579 }
580 
581 
582 static int
583 evtchndrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
584 {
585 	int	error;
586 	int	unit = ddi_get_instance(dip);
587 
588 
589 	switch (cmd) {
590 	case DDI_ATTACH:
591 		break;
592 	case DDI_RESUME:
593 		return (DDI_SUCCESS);
594 	default:
595 		cmn_err(CE_WARN, "evtchn_attach: unknown cmd 0x%x\n", cmd);
596 		return (DDI_FAILURE);
597 	}
598 
599 	/* DDI_ATTACH */
600 
601 	/*
602 	 * only one instance - but we clone using the open routine
603 	 */
604 	if (ddi_get_instance(dip) > 0)
605 		return (DDI_FAILURE);
606 
607 	mutex_init(&evtchndrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
608 	    NULL);
609 
610 	error = ddi_create_minor_node(dip, "evtchn", S_IFCHR, unit,
611 	    DDI_PSEUDO, NULL);
612 	if (error != DDI_SUCCESS)
613 		goto fail;
614 
615 	/*
616 	 * save dip for getinfo
617 	 */
618 	evtchndrv_dip = dip;
619 	ddi_report_dev(dip);
620 
621 	mutex_init(&port_user_lock, NULL, MUTEX_DRIVER, NULL);
622 	(void) memset(port_user, 0, sizeof (port_user));
623 
624 	ec_dev_irq = ec_dev_alloc_irq();
625 	(void) add_avintr(NULL, IPL_EVTCHN, (avfunc)evtchn_device_upcall,
626 	    "evtchn_driver", ec_dev_irq, NULL, NULL, NULL, dip);
627 
628 	return (DDI_SUCCESS);
629 
630 fail:
631 	(void) evtchndrv_detach(dip, DDI_DETACH);
632 	return (error);
633 }
634 
635 /*ARGSUSED*/
636 static int
637 evtchndrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
638 {
639 	/*
640 	 * Don't allow detach for now.
641 	 */
642 	return (DDI_FAILURE);
643 }
644 
645 /* Solaris driver framework */
646 
647 static 	struct cb_ops evtchndrv_cb_ops = {
648 	evtchndrv_open,		/* cb_open */
649 	evtchndrv_close,	/* cb_close */
650 	nodev,			/* cb_strategy */
651 	nodev,			/* cb_print */
652 	nodev,			/* cb_dump */
653 	evtchndrv_read,		/* cb_read */
654 	evtchndrv_write,	/* cb_write */
655 	evtchndrv_ioctl,	/* cb_ioctl */
656 	nodev,			/* cb_devmap */
657 	nodev,			/* cb_mmap */
658 	nodev,			/* cb_segmap */
659 	evtchndrv_poll,		/* cb_chpoll */
660 	ddi_prop_op,		/* cb_prop_op */
661 	0,			/* cb_stream */
662 	D_NEW | D_MP | D_64BIT	/* cb_flag */
663 };
664 
665 static struct dev_ops evtchndrv_dev_ops = {
666 	DEVO_REV,		/* devo_rev */
667 	0,			/* devo_refcnt */
668 	evtchndrv_info,		/* devo_getinfo */
669 	nulldev,		/* devo_identify */
670 	nulldev,		/* devo_probe */
671 	evtchndrv_attach,	/* devo_attach */
672 	evtchndrv_detach,	/* devo_detach */
673 	nodev,			/* devo_reset */
674 	&evtchndrv_cb_ops,	/* devo_cb_ops */
675 	NULL,			/* devo_bus_ops */
676 	NULL			/* power */
677 };
678 
679 static struct modldrv modldrv = {
680 	&mod_driverops,		/* Type of module.  This one is a driver */
681 	"Evtchn driver v1.0",	/* Name of the module. */
682 	&evtchndrv_dev_ops	/* driver ops */
683 };
684 
685 static struct modlinkage modlinkage = {
686 	MODREV_1,
687 	&modldrv,
688 	NULL
689 };
690 
691 int
692 _init(void)
693 {
694 	int err;
695 
696 	err = ddi_soft_state_init(&evtchndrv_statep,
697 	    sizeof (struct evtsoftdata), 1);
698 	if (err)
699 		return (err);
700 
701 	err = mod_install(&modlinkage);
702 	if (err)
703 		ddi_soft_state_fini(&evtchndrv_statep);
704 	else
705 		evtchndrv_clone_tab = kmem_zalloc(
706 		    sizeof (int) * evtchndrv_nclones, KM_SLEEP);
707 	return (err);
708 }
709 
710 int
711 _fini(void)
712 {
713 	int e;
714 
715 	e = mod_remove(&modlinkage);
716 	if (e)
717 		return (e);
718 
719 	ddi_soft_state_fini(&evtchndrv_statep);
720 
721 	return (0);
722 }
723 
724 int
725 _info(struct modinfo *modinfop)
726 {
727 	return (mod_info(&modlinkage, modinfop));
728 }
729