xref: /titanic_52/usr/src/uts/common/xen/io/evtchn_dev.c (revision fca4268092e9961ebb9b5e0098dcebc545023586)
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 2009 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 	kcondvar_t evtchn_wait; /* Processes wait on this when ring is empty. */
95 	kmutex_t evtchn_lock;
96 	struct pollhead evtchn_pollhead;
97 
98 	pid_t pid;		/* last pid to bind to this event channel. */
99 	processorid_t cpu;	/* cpu thread/evtchn is bound to */
100 };
101 
102 static void *evtchndrv_statep;
103 int evtchndrv_nclones = EVTCHNDRV_DEFAULT_NCLONES;
104 static int *evtchndrv_clone_tab;
105 static dev_info_t *evtchndrv_dip;
106 static kmutex_t evtchndrv_clone_tab_mutex;
107 
108 static int evtchndrv_detach(dev_info_t *, ddi_detach_cmd_t);
109 
110 /* Who's bound to each port? */
111 static struct evtsoftdata *port_user[NR_EVENT_CHANNELS];
112 static kmutex_t port_user_lock;
113 
114 void
115 evtchn_device_upcall()
116 {
117 	struct evtsoftdata *ep;
118 	int port;
119 
120 	/*
121 	 * This is quite gross, we had to leave the evtchn that led to this
122 	 * invocation in a per-cpu mailbox, retrieve it now.
123 	 * We do this because the interface doesn't offer us a way to pass
124 	 * a dynamic argument up through the generic interrupt service layer.
125 	 * The mailbox is safe since we either run with interrupts disabled or
126 	 * non-preemptable till we reach here.
127 	 */
128 	port = CPU->cpu_m.mcpu_ec_mbox;
129 	ASSERT(port != 0);
130 	CPU->cpu_m.mcpu_ec_mbox = 0;
131 	ec_clear_evtchn(port);
132 	mutex_enter(&port_user_lock);
133 
134 	if ((ep = port_user[port]) != NULL) {
135 		mutex_enter(&ep->evtchn_lock);
136 		if ((ep->ring_prod - ep->ring_cons) < EVTCHN_RING_SIZE) {
137 			ep->ring[EVTCHN_RING_MASK(ep->ring_prod)] = port;
138 			/*
139 			 * Wake up reader when ring goes non-empty
140 			 */
141 			if (ep->ring_cons == ep->ring_prod++) {
142 				cv_signal(&ep->evtchn_wait);
143 				mutex_exit(&ep->evtchn_lock);
144 				pollwakeup(&ep->evtchn_pollhead,
145 				    POLLIN | POLLRDNORM);
146 				goto done;
147 			}
148 		} else {
149 			ep->ring_overflow = 1;
150 		}
151 		mutex_exit(&ep->evtchn_lock);
152 	}
153 
154 done:
155 	mutex_exit(&port_user_lock);
156 }
157 
158 /* ARGSUSED */
159 static int
160 evtchndrv_read(dev_t dev, struct uio *uio, cred_t *cr)
161 {
162 	int rc = 0;
163 	ssize_t count;
164 	unsigned int c, p, bytes1 = 0, bytes2 = 0;
165 	struct evtsoftdata *ep;
166 	minor_t minor = getminor(dev);
167 
168 	if (secpolicy_xvm_control(cr))
169 		return (EPERM);
170 
171 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
172 
173 	/* Whole number of ports. */
174 	count = uio->uio_resid;
175 	count &= ~(sizeof (evtchn_port_t) - 1);
176 
177 	if (count == 0)
178 		return (0);
179 
180 	if (count > PAGESIZE)
181 		count = PAGESIZE;
182 
183 	mutex_enter(&ep->evtchn_lock);
184 	for (;;) {
185 		if (ep->ring_overflow) {
186 			rc = EFBIG;
187 			goto done;
188 		}
189 
190 		if ((c = ep->ring_cons) != (p = ep->ring_prod))
191 			break;
192 
193 		if (uio->uio_fmode & O_NONBLOCK) {
194 			rc = EAGAIN;
195 			goto done;
196 		}
197 
198 		if (cv_wait_sig(&ep->evtchn_wait, &ep->evtchn_lock) == 0) {
199 			rc = EINTR;
200 			goto done;
201 		}
202 	}
203 
204 	/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
205 	if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
206 		bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
207 		    sizeof (evtchn_port_t);
208 		bytes2 = EVTCHN_RING_MASK(p) * sizeof (evtchn_port_t);
209 	} else {
210 		bytes1 = (p - c) * sizeof (evtchn_port_t);
211 		bytes2 = 0;
212 	}
213 
214 	/* Truncate chunks according to caller's maximum byte count. */
215 	if (bytes1 > count) {
216 		bytes1 = count;
217 		bytes2 = 0;
218 	} else if ((bytes1 + bytes2) > count) {
219 		bytes2 = count - bytes1;
220 	}
221 
222 	if (uiomove(&ep->ring[EVTCHN_RING_MASK(c)], bytes1, UIO_READ, uio) ||
223 	    ((bytes2 != 0) && uiomove(&ep->ring[0], bytes2, UIO_READ, uio))) {
224 		rc = EFAULT;
225 		goto done;
226 	}
227 
228 	ep->ring_cons += (bytes1 + bytes2) / sizeof (evtchn_port_t);
229 done:
230 	mutex_exit(&ep->evtchn_lock);
231 	return (rc);
232 }
233 
234 /* ARGSUSED */
235 static int
236 evtchndrv_write(dev_t dev, struct uio *uio, cred_t *cr)
237 {
238 	int  rc, i;
239 	ssize_t count;
240 	evtchn_port_t *kbuf;
241 	struct evtsoftdata *ep;
242 	ulong_t flags;
243 	minor_t minor = getminor(dev);
244 	evtchn_port_t sbuf[32];
245 
246 	if (secpolicy_xvm_control(cr))
247 		return (EPERM);
248 
249 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
250 
251 
252 	/* Whole number of ports. */
253 	count = uio->uio_resid;
254 	count &= ~(sizeof (evtchn_port_t) - 1);
255 
256 	if (count == 0)
257 		return (0);
258 
259 	if (count > PAGESIZE)
260 		count = PAGESIZE;
261 
262 	if (count <= sizeof (sbuf))
263 		kbuf = sbuf;
264 	else
265 		kbuf = kmem_alloc(PAGESIZE, KM_SLEEP);
266 	if ((rc = uiomove(kbuf, count, UIO_WRITE, uio)) != 0)
267 		goto out;
268 
269 	mutex_enter(&port_user_lock);
270 	for (i = 0; i < (count / sizeof (evtchn_port_t)); i++)
271 		if ((kbuf[i] < NR_EVENT_CHANNELS) &&
272 		    (port_user[kbuf[i]] == ep)) {
273 			flags = intr_clear();
274 			ec_unmask_evtchn(kbuf[i]);
275 			intr_restore(flags);
276 		}
277 	mutex_exit(&port_user_lock);
278 
279 out:
280 	if (kbuf != sbuf)
281 		kmem_free(kbuf, PAGESIZE);
282 	return (rc);
283 }
284 
285 static void
286 evtchn_bind_to_user(struct evtsoftdata *u, int port)
287 {
288 	ulong_t flags;
289 
290 	/*
291 	 * save away the PID of the last process to bind to this event channel.
292 	 * Useful for debugging.
293 	 */
294 	u->pid = ddi_get_pid();
295 
296 	mutex_enter(&port_user_lock);
297 	ASSERT(port_user[port] == NULL);
298 	port_user[port] = u;
299 	ec_irq_add_evtchn(ec_dev_irq, port);
300 	flags = intr_clear();
301 	ec_unmask_evtchn(port);
302 	intr_restore(flags);
303 	mutex_exit(&port_user_lock);
304 }
305 
306 static void
307 evtchndrv_close_evtchn(int port)
308 {
309 	struct evtsoftdata *ep;
310 
311 	ASSERT(MUTEX_HELD(&port_user_lock));
312 	ep = port_user[port];
313 	ASSERT(ep != NULL);
314 	(void) ec_mask_evtchn(port);
315 	/*
316 	 * It is possible the event is in transit to us.
317 	 * If it is already in the ring buffer, then a client may
318 	 * get a spurious event notification on the next read of
319 	 * of the evtchn device.  Clients will need to be able to
320 	 * handle getting a spurious event notification.
321 	 */
322 	port_user[port] = NULL;
323 	/*
324 	 * The event is masked and should stay so, clean it up.
325 	 */
326 	ec_irq_rm_evtchn(ec_dev_irq, port);
327 }
328 
329 /* ARGSUSED */
330 static int
331 evtchndrv_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr,
332     int *rvalp)
333 {
334 	int err = 0;
335 	struct evtsoftdata *ep;
336 	minor_t minor = getminor(dev);
337 
338 	if (secpolicy_xvm_control(cr))
339 		return (EPERM);
340 
341 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
342 
343 	*rvalp = 0;
344 
345 	switch (cmd) {
346 	case IOCTL_EVTCHN_BIND_VIRQ: {
347 		struct ioctl_evtchn_bind_virq bind;
348 
349 		if (copyin((void *)data, &bind, sizeof (bind))) {
350 			err = EFAULT;
351 			break;
352 		}
353 
354 		if ((err = xen_bind_virq(bind.virq, 0, rvalp)) != 0)
355 			break;
356 
357 		evtchn_bind_to_user(ep, *rvalp);
358 		break;
359 	}
360 
361 	case IOCTL_EVTCHN_BIND_INTERDOMAIN: {
362 		struct ioctl_evtchn_bind_interdomain bind;
363 
364 		if (copyin((void *)data, &bind, sizeof (bind))) {
365 			err = EFAULT;
366 			break;
367 		}
368 
369 		if ((err = xen_bind_interdomain(bind.remote_domain,
370 		    bind.remote_port, rvalp)) != 0)
371 			break;
372 
373 		ec_bind_vcpu(*rvalp, 0);
374 		evtchn_bind_to_user(ep, *rvalp);
375 		break;
376 	}
377 
378 	case IOCTL_EVTCHN_BIND_UNBOUND_PORT: {
379 		struct ioctl_evtchn_bind_unbound_port bind;
380 
381 		if (copyin((void *)data, &bind, sizeof (bind))) {
382 			err = EFAULT;
383 			break;
384 		}
385 
386 		if ((err = xen_alloc_unbound_evtchn(bind.remote_domain,
387 		    rvalp)) != 0)
388 			break;
389 
390 		evtchn_bind_to_user(ep, *rvalp);
391 		break;
392 	}
393 
394 	case IOCTL_EVTCHN_UNBIND: {
395 		struct ioctl_evtchn_unbind unbind;
396 
397 		if (copyin((void *)data, &unbind, sizeof (unbind))) {
398 			err = EFAULT;
399 			break;
400 		}
401 
402 		if (unbind.port >= NR_EVENT_CHANNELS) {
403 			err = EFAULT;
404 			break;
405 		}
406 
407 		mutex_enter(&port_user_lock);
408 
409 		if (port_user[unbind.port] != ep) {
410 			mutex_exit(&port_user_lock);
411 			err = ENOTCONN;
412 			break;
413 		}
414 
415 		evtchndrv_close_evtchn(unbind.port);
416 		mutex_exit(&port_user_lock);
417 		break;
418 	}
419 
420 	case IOCTL_EVTCHN_NOTIFY: {
421 		struct ioctl_evtchn_notify notify;
422 
423 		if (copyin((void *)data, &notify, sizeof (notify))) {
424 			err = EFAULT;
425 			break;
426 		}
427 
428 		if (notify.port >= NR_EVENT_CHANNELS) {
429 			err = EINVAL;
430 		} else if (port_user[notify.port] != ep) {
431 			err = ENOTCONN;
432 		} else {
433 			ec_notify_via_evtchn(notify.port);
434 		}
435 		break;
436 	}
437 
438 	default:
439 		err = ENOSYS;
440 	}
441 
442 	return (err);
443 }
444 
445 static int
446 evtchndrv_poll(dev_t dev, short ev, int anyyet, short *revp, pollhead_t **phpp)
447 {
448 	struct evtsoftdata *ep;
449 	minor_t minor = getminor(dev);
450 	short mask = 0;
451 
452 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
453 	*phpp = (struct pollhead *)NULL;
454 
455 	if (ev & POLLOUT)
456 		mask |= POLLOUT;
457 	if (ep->ring_overflow)
458 		mask |= POLLERR;
459 	if (ev & (POLLIN | POLLRDNORM)) {
460 		mutex_enter(&ep->evtchn_lock);
461 		if (ep->ring_cons != ep->ring_prod)
462 			mask |= (POLLIN | POLLRDNORM) & ev;
463 		else
464 			if (mask == 0 && !anyyet)
465 				*phpp = &ep->evtchn_pollhead;
466 		mutex_exit(&ep->evtchn_lock);
467 	}
468 	*revp = mask;
469 	return (0);
470 }
471 
472 
473 /* ARGSUSED */
474 static int
475 evtchndrv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
476 {
477 	struct evtsoftdata *ep;
478 	minor_t minor = getminor(*devp);
479 
480 	if (otyp == OTYP_BLK)
481 		return (ENXIO);
482 
483 	/*
484 	 * only allow open on minor = 0 - the clone device
485 	 */
486 	if (minor != 0)
487 		return (ENXIO);
488 
489 	/*
490 	 * find a free slot and grab it
491 	 */
492 	mutex_enter(&evtchndrv_clone_tab_mutex);
493 	for (minor = 1; minor < evtchndrv_nclones; minor++) {
494 		if (evtchndrv_clone_tab[minor] == 0) {
495 			evtchndrv_clone_tab[minor] = 1;
496 			break;
497 		}
498 	}
499 	mutex_exit(&evtchndrv_clone_tab_mutex);
500 	if (minor == evtchndrv_nclones)
501 		return (EAGAIN);
502 
503 	/* Allocate softstate structure */
504 	if (ddi_soft_state_zalloc(evtchndrv_statep,
505 	    EVTCHNDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
506 		mutex_enter(&evtchndrv_clone_tab_mutex);
507 		evtchndrv_clone_tab[minor] = 0;
508 		mutex_exit(&evtchndrv_clone_tab_mutex);
509 		return (EAGAIN);
510 	}
511 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
512 
513 	/* ... and init it */
514 	ep->dip = evtchndrv_dip;
515 
516 	cv_init(&ep->evtchn_wait, NULL, CV_DEFAULT, NULL);
517 	mutex_init(&ep->evtchn_lock, NULL, MUTEX_DEFAULT, NULL);
518 
519 	ep->ring = kmem_alloc(PAGESIZE, KM_SLEEP);
520 
521 	/* clone driver */
522 	*devp = makedevice(getmajor(*devp), minor);
523 
524 	return (0);
525 }
526 
527 /* ARGSUSED */
528 static int
529 evtchndrv_close(dev_t dev, int flag, int otyp, struct cred *credp)
530 {
531 	struct evtsoftdata *ep;
532 	minor_t minor = getminor(dev);
533 	int i;
534 
535 	ep = EVTCHNDRV_INST2SOFTS(EVTCHNDRV_MINOR2INST(minor));
536 	if (ep == NULL)
537 		return (ENXIO);
538 
539 	mutex_enter(&port_user_lock);
540 
541 
542 	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
543 		if (port_user[i] != ep)
544 			continue;
545 
546 		evtchndrv_close_evtchn(i);
547 	}
548 
549 	mutex_exit(&port_user_lock);
550 
551 	kmem_free(ep->ring, PAGESIZE);
552 	ddi_soft_state_free(evtchndrv_statep, EVTCHNDRV_MINOR2INST(minor));
553 
554 	/*
555 	 * free clone tab slot
556 	 */
557 	mutex_enter(&evtchndrv_clone_tab_mutex);
558 	evtchndrv_clone_tab[minor] = 0;
559 	mutex_exit(&evtchndrv_clone_tab_mutex);
560 
561 	return (0);
562 }
563 
564 /* ARGSUSED */
565 static int
566 evtchndrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
567 {
568 	dev_t	dev = (dev_t)arg;
569 	minor_t	minor = getminor(dev);
570 	int	retval;
571 
572 	switch (cmd) {
573 	case DDI_INFO_DEVT2DEVINFO:
574 		if (minor != 0 || evtchndrv_dip == NULL) {
575 			*result = (void *)NULL;
576 			retval = DDI_FAILURE;
577 		} else {
578 			*result = (void *)evtchndrv_dip;
579 			retval = DDI_SUCCESS;
580 		}
581 		break;
582 	case DDI_INFO_DEVT2INSTANCE:
583 		*result = (void *)0;
584 		retval = DDI_SUCCESS;
585 		break;
586 	default:
587 		retval = DDI_FAILURE;
588 	}
589 	return (retval);
590 }
591 
592 
593 static int
594 evtchndrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
595 {
596 	int	error;
597 	int	unit = ddi_get_instance(dip);
598 
599 
600 	switch (cmd) {
601 	case DDI_ATTACH:
602 		break;
603 	case DDI_RESUME:
604 		return (DDI_SUCCESS);
605 	default:
606 		cmn_err(CE_WARN, "evtchn_attach: unknown cmd 0x%x\n", cmd);
607 		return (DDI_FAILURE);
608 	}
609 
610 	/* DDI_ATTACH */
611 
612 	/*
613 	 * only one instance - but we clone using the open routine
614 	 */
615 	if (ddi_get_instance(dip) > 0)
616 		return (DDI_FAILURE);
617 
618 	mutex_init(&evtchndrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
619 	    NULL);
620 
621 	error = ddi_create_minor_node(dip, "evtchn", S_IFCHR, unit,
622 	    DDI_PSEUDO, NULL);
623 	if (error != DDI_SUCCESS)
624 		goto fail;
625 
626 	/*
627 	 * save dip for getinfo
628 	 */
629 	evtchndrv_dip = dip;
630 	ddi_report_dev(dip);
631 
632 	mutex_init(&port_user_lock, NULL, MUTEX_DRIVER, NULL);
633 	(void) memset(port_user, 0, sizeof (port_user));
634 
635 	ec_dev_irq = ec_dev_alloc_irq();
636 	(void) add_avintr(NULL, IPL_EVTCHN, (avfunc)evtchn_device_upcall,
637 	    "evtchn_driver", ec_dev_irq, NULL, NULL, NULL, dip);
638 
639 	return (DDI_SUCCESS);
640 
641 fail:
642 	(void) evtchndrv_detach(dip, DDI_DETACH);
643 	return (error);
644 }
645 
646 /*ARGSUSED*/
647 static int
648 evtchndrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
649 {
650 	/*
651 	 * Don't allow detach for now.
652 	 */
653 	return (DDI_FAILURE);
654 }
655 
656 /* Solaris driver framework */
657 
658 static 	struct cb_ops evtchndrv_cb_ops = {
659 	evtchndrv_open,		/* cb_open */
660 	evtchndrv_close,	/* cb_close */
661 	nodev,			/* cb_strategy */
662 	nodev,			/* cb_print */
663 	nodev,			/* cb_dump */
664 	evtchndrv_read,		/* cb_read */
665 	evtchndrv_write,	/* cb_write */
666 	evtchndrv_ioctl,	/* cb_ioctl */
667 	nodev,			/* cb_devmap */
668 	nodev,			/* cb_mmap */
669 	nodev,			/* cb_segmap */
670 	evtchndrv_poll,		/* cb_chpoll */
671 	ddi_prop_op,		/* cb_prop_op */
672 	0,			/* cb_stream */
673 	D_NEW | D_MP | D_64BIT	/* cb_flag */
674 };
675 
676 static struct dev_ops evtchndrv_dev_ops = {
677 	DEVO_REV,		/* devo_rev */
678 	0,			/* devo_refcnt */
679 	evtchndrv_info,		/* devo_getinfo */
680 	nulldev,		/* devo_identify */
681 	nulldev,		/* devo_probe */
682 	evtchndrv_attach,	/* devo_attach */
683 	evtchndrv_detach,	/* devo_detach */
684 	nodev,			/* devo_reset */
685 	&evtchndrv_cb_ops,	/* devo_cb_ops */
686 	NULL,			/* devo_bus_ops */
687 	NULL,			/* power */
688 	ddi_quiesce_not_needed,		/* devo_quiesce */
689 };
690 
691 static struct modldrv modldrv = {
692 	&mod_driverops,		/* Type of module.  This one is a driver */
693 	"Evtchn driver",	/* Name of the module. */
694 	&evtchndrv_dev_ops	/* driver ops */
695 };
696 
697 static struct modlinkage modlinkage = {
698 	MODREV_1,
699 	&modldrv,
700 	NULL
701 };
702 
703 int
704 _init(void)
705 {
706 	int err;
707 
708 	err = ddi_soft_state_init(&evtchndrv_statep,
709 	    sizeof (struct evtsoftdata), 1);
710 	if (err)
711 		return (err);
712 
713 	err = mod_install(&modlinkage);
714 	if (err)
715 		ddi_soft_state_fini(&evtchndrv_statep);
716 	else
717 		evtchndrv_clone_tab = kmem_zalloc(
718 		    sizeof (int) * evtchndrv_nclones, KM_SLEEP);
719 	return (err);
720 }
721 
722 int
723 _fini(void)
724 {
725 	int e;
726 
727 	e = mod_remove(&modlinkage);
728 	if (e)
729 		return (e);
730 
731 	ddi_soft_state_fini(&evtchndrv_statep);
732 
733 	return (0);
734 }
735 
736 int
737 _info(struct modinfo *modinfop)
738 {
739 	return (mod_info(&modlinkage, modinfop));
740 }
741