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