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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <sys/types.h>
28 #include <sys/cmn_err.h>
29 #include <sys/conf.h>
30 #include <sys/ddi_impldefs.h>
31 #include <sys/autoconf.h>
32 #include <sys/systm.h>
33 #include <sys/modctl.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/ddi_subrdefs.h>
37 #include <sys/promif.h>
38 #include <sys/machsystm.h>
39 #include <sys/ddi_intr_impl.h>
40 #include <sys/hypervisor_api.h>
41 #include <sys/intr.h>
42 #include <sys/hsvc.h>
43
44 #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28))
45
46 static kmutex_t vnex_id_lock;
47 /*
48 * Vnex name to pil map
49 */
50 typedef struct vnex_regspec {
51 uint64_t physaddr;
52 uint64_t size;
53 } vnex_regspec_t;
54
55 struct vnex_pil_map {
56 caddr_t name;
57 uint32_t pil;
58 };
59
60 /* vnex interrupt descriptor */
61 typedef struct vnex_id {
62 dev_info_t *vid_dip;
63 uint32_t vid_ino;
64 uint64_t vid_ihdl;
65 uint_t (*vid_handler)();
66 caddr_t vid_arg1;
67 caddr_t vid_arg2;
68 ddi_intr_handle_impl_t *vid_ddi_hdlp;
69 uint64_t vid_cfg_hdl;
70 struct vnex_id *vid_next;
71 } vnex_id_t;
72
73 /* vnex interrupt descriptor list */
74 static vnex_id_t *vnex_id_list;
75
76 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
77
78 /*
79 * vnex interrupt descriptor list manipulation functions
80 */
81
82 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino);
83 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino,
84 uint64_t dhdl);
85 static void vnex_add_id(vnex_id_t *vid_p);
86 static void vnex_rem_id(vnex_id_t *vid_p);
87 static void vnex_free_id(vnex_id_t *vid_p);
88
89 uint_t vnex_intr_wrapper(caddr_t arg);
90
91 static struct vnex_pil_map vnex_name_to_pil[] = {
92 {"console", PIL_12},
93 {"fma", PIL_5},
94 {"echo", PIL_3},
95 {"loop", PIL_3},
96 {"sunmc", PIL_3},
97 {"sunvts", PIL_3},
98 {"explorer", PIL_3},
99 {"ncp", PIL_8},
100 {"crypto", PIL_8}
101 };
102
103 #define VNEX_MAX_DEVS (sizeof (vnex_name_to_pil) / \
104 sizeof (struct vnex_pil_map))
105
106 /*
107 * Config information
108 */
109 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
110 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
111
112 static int
113 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
114
115 static struct bus_ops vnex_bus_ops = {
116 BUSO_REV,
117 nullbusmap,
118 NULL, /* NO OP */
119 NULL, /* NO OP */
120 NULL, /* NO OP */
121 i_ddi_map_fault,
122 ddi_no_dma_map,
123 ddi_no_dma_allochdl,
124 NULL,
125 NULL,
126 NULL,
127 NULL,
128 NULL,
129 NULL,
130 vnex_ctl,
131 ddi_bus_prop_op,
132 NULL, /* (*bus_get_eventcookie)(); */
133 NULL, /* (*bus_add_eventcall)(); */
134 NULL, /* (*bus_remove_eventcall)(); */
135 NULL, /* (*bus_post_event)(); */
136 NULL, /* (*bus_intr_ctl)(); */
137 NULL, /* (*bus_config)(); */
138 NULL, /* (*bus_unconfig)(); */
139 NULL, /* (*bus_fm_init)(); */
140 NULL, /* (*bus_fm_fini)(); */
141 NULL, /* (*bus_fm_access_enter)(); */
142 NULL, /* (*bus_fm_access_fini)(); */
143 NULL, /* (*bus_power)(); */
144 vnex_intr_ops /* (*bus_intr_op)(); */
145 };
146
147 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
148 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
149
150 static struct dev_ops pseudo_ops = {
151 DEVO_REV, /* devo_rev, */
152 0, /* refcnt */
153 ddi_no_info, /* info */
154 nulldev, /* identify */
155 nulldev, /* probe */
156 vnex_attach, /* attach */
157 vnex_detach, /* detach */
158 nodev, /* reset */
159 (struct cb_ops *)0, /* driver operations */
160 &vnex_bus_ops, /* bus operations */
161 nulldev, /* power */
162 ddi_quiesce_not_needed, /* quiesce */
163 };
164
165 /*
166 * Module linkage information for the kernel.
167 */
168
169 static struct modldrv modldrv = {
170 &mod_driverops, /* Type of module. This one is a pseudo driver */
171 "sun4v virtual-devices nexus driver",
172 &pseudo_ops, /* driver ops */
173 };
174
175 static struct modlinkage modlinkage = {
176 MODREV_1, (void *)&modldrv, NULL
177 };
178
179 int
_init(void)180 _init(void)
181 {
182 uint64_t mjrnum;
183 uint64_t mnrnum;
184
185 /*
186 * Check HV intr group api versioning.
187 * This driver uses the old interrupt routines which are supported
188 * in old firmware in the CORE API group and in newer firmware in
189 * the INTR API group. Support for these calls will be dropped
190 * once the INTR API group major goes to 2.
191 */
192
193 if ((hsvc_version(HSVC_GROUP_INTR, &mjrnum, &mnrnum) == 0) &&
194 (mjrnum > 1)) {
195 cmn_err(CE_WARN, "niumx: unsupported intr api group: "
196 "maj:0x%lx, min:0x%lx", mjrnum, mnrnum);
197 return (ENOTSUP);
198 }
199
200 return (mod_install(&modlinkage));
201 }
202
203 int
_fini(void)204 _fini(void)
205 {
206 return (mod_remove(&modlinkage));
207 }
208
209 int
_info(struct modinfo * modinfop)210 _info(struct modinfo *modinfop)
211 {
212 return (mod_info(&modlinkage, modinfop));
213 }
214
215 /*ARGSUSED*/
216 void
vnex_intr_dist(void * arg)217 vnex_intr_dist(void *arg)
218 {
219 vnex_id_t *vid_p;
220 uint32_t cpuid;
221 int intr_state;
222 hrtime_t start;
223
224 mutex_enter(&vnex_id_lock);
225
226 for (vid_p = vnex_id_list; vid_p != NULL;
227 vid_p = vid_p->vid_next) {
228 /*
229 * Don't do anything for disabled interrupts.
230 * vnex_enable_intr takes care of redistributing interrupts.
231 */
232 if ((hvio_intr_getvalid(vid_p->vid_ihdl,
233 &intr_state) == H_EOK) && (intr_state == HV_INTR_NOTVALID))
234 continue;
235
236 cpuid = intr_dist_cpuid();
237
238 (void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID);
239 /*
240 * Make a best effort to wait for pending interrupts to finish.
241 * There is not much we can do if we timeout.
242 */
243 start = gethrtime();
244 while (!panicstr &&
245 (hvio_intr_getstate(vid_p->vid_ihdl, &intr_state) ==
246 H_EOK) && (intr_state == HV_INTR_DELIVERED_STATE)) {
247 if (gethrtime() - start > vnex_pending_timeout) {
248 cmn_err(CE_WARN, "vnex_intr_dist: %s%d "
249 "ino 0x%x pending: timedout\n",
250 ddi_driver_name(vid_p->vid_dip),
251 ddi_get_instance(vid_p->vid_dip),
252 vid_p->vid_ino);
253 break;
254 }
255 }
256 (void) hvio_intr_settarget(vid_p->vid_ihdl, cpuid);
257 (void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID);
258 }
259 mutex_exit(&vnex_id_lock);
260 }
261
262 static int
vnex_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)263 vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
264 {
265 switch (cmd) {
266 case DDI_ATTACH:
267 /*
268 * Intitialize interrupt descriptor list
269 * and mutex.
270 */
271 vnex_id_list = NULL;
272 mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL);
273 /*
274 * Add interrupt redistribution callback.
275 */
276 intr_dist_add(vnex_intr_dist, dip);
277 return (DDI_SUCCESS);
278
279 case DDI_RESUME:
280 return (DDI_SUCCESS);
281
282 default:
283 return (DDI_FAILURE);
284 }
285 }
286
287 /*ARGSUSED*/
288 static int
vnex_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)289 vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
290 {
291 switch (cmd) {
292 case DDI_DETACH:
293 return (DDI_FAILURE);
294
295 case DDI_SUSPEND:
296 return (DDI_SUCCESS);
297
298 default:
299 return (DDI_FAILURE);
300 }
301 }
302
303 static int
vnex_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)304 vnex_ctl(dev_info_t *dip, dev_info_t *rdip,
305 ddi_ctl_enum_t ctlop, void *arg, void *result)
306 {
307 char name[12]; /* enough for a decimal integer */
308 int reglen;
309 uint32_t *vnex_regspec;
310
311 switch (ctlop) {
312 case DDI_CTLOPS_REPORTDEV:
313 if (rdip == NULL)
314 return (DDI_FAILURE);
315 cmn_err(CE_CONT, "?virtual-device: %s%d\n",
316 ddi_driver_name(rdip), ddi_get_instance(rdip));
317 return (DDI_SUCCESS);
318
319 case DDI_CTLOPS_INITCHILD:
320 {
321 dev_info_t *child = (dev_info_t *)arg;
322
323 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
324 "reg", (caddr_t)&vnex_regspec, ®len) != DDI_SUCCESS)
325 return (DDI_FAILURE);
326
327 (void) sprintf(name, "%x", *vnex_regspec);
328 ddi_set_name_addr(child, name);
329 ddi_set_parent_data(child, NULL);
330 kmem_free((caddr_t)vnex_regspec, reglen);
331 return (DDI_SUCCESS);
332
333 }
334
335 case DDI_CTLOPS_UNINITCHILD:
336 {
337 dev_info_t *child = (dev_info_t *)arg;
338
339 ddi_set_name_addr(child, NULL);
340 ddi_remove_minor_node(arg, NULL);
341 return (DDI_SUCCESS);
342 }
343
344 /*
345 * These ops correspond to functions that "shouldn't" be called
346 * by a pseudo driver. So we whinge when we're called.
347 */
348 case DDI_CTLOPS_DMAPMAPC:
349 case DDI_CTLOPS_REPORTINT:
350 case DDI_CTLOPS_REGSIZE:
351 {
352 *((off_t *)result) = 0;
353 return (DDI_SUCCESS);
354 }
355 case DDI_CTLOPS_NREGS:
356 {
357 dev_info_t *child = rdip;
358 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
359 "reg", (caddr_t)&vnex_regspec, ®len) != DDI_SUCCESS)
360 return (DDI_FAILURE);
361 *((uint_t *)result) = reglen / sizeof (uint32_t);
362 kmem_free((caddr_t)vnex_regspec, reglen);
363 return (DDI_SUCCESS);
364 }
365 case DDI_CTLOPS_SIDDEV:
366 case DDI_CTLOPS_SLAVEONLY:
367 case DDI_CTLOPS_AFFINITY:
368 case DDI_CTLOPS_IOMIN:
369 case DDI_CTLOPS_POKE:
370 case DDI_CTLOPS_PEEK:
371 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
372 ddi_get_name(dip), ddi_get_instance(dip),
373 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
374 return (DDI_FAILURE);
375
376 /*
377 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
378 */
379 default:
380 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
381 }
382 }
383
384 static int
vnex_get_pil(dev_info_t * rdip)385 vnex_get_pil(dev_info_t *rdip)
386 {
387 int i;
388 caddr_t name;
389
390 name = ddi_node_name(rdip);
391 for (i = 0; i < VNEX_MAX_DEVS; i++) {
392 if (strcmp(vnex_name_to_pil[i].name,
393 name) == 0) {
394 return (vnex_name_to_pil[i].pil);
395 }
396 }
397 /*
398 * if not found pil is 0
399 */
400 return (0);
401 }
402
403 static int
vnex_enable_intr(dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)404 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
405 {
406 vnex_id_t *vid_p;
407 uint32_t cpuid;
408
409 vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
410
411 ASSERT(vid_p != NULL);
412
413 cpuid = intr_dist_cpuid();
414
415 if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) {
416 return (DDI_FAILURE);
417 }
418
419 if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
420 return (DDI_FAILURE);
421 }
422
423 if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) {
424 return (DDI_FAILURE);
425 }
426
427 return (DDI_SUCCESS);
428 }
429
430 static int
vnex_disable_intr(dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)431 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
432 {
433 vnex_id_t *vid_p;
434
435 vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
436
437 ASSERT(vid_p != NULL);
438
439 if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) {
440 return (DDI_FAILURE);
441 }
442
443 return (DDI_SUCCESS);
444 }
445
446 int
vnex_ino_to_inum(dev_info_t * dip,uint32_t ino)447 vnex_ino_to_inum(dev_info_t *dip, uint32_t ino)
448 {
449 vnex_id_t *vid_p;
450 ddi_intr_handle_impl_t *hdlp;
451
452 if ((vid_p = vnex_locate_id(dip, ino)) == NULL)
453 return (-1);
454 else if ((hdlp = vid_p->vid_ddi_hdlp) == NULL)
455 return (-1);
456 else
457 return (hdlp->ih_inum);
458 }
459
460 static int
vnex_add_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)461 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip,
462 ddi_intr_handle_impl_t *hdlp)
463 {
464 int reglen, ret = DDI_SUCCESS;
465 vnex_id_t *vid_p;
466 uint64_t cfg;
467 uint32_t ino;
468 uint64_t ihdl;
469 vnex_regspec_t *reg_p;
470
471 if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
472 DDI_PROP_DONTPASS, "reg", (caddr_t)®_p,
473 ®len) != DDI_SUCCESS) {
474 return (DDI_FAILURE);
475 }
476
477 /*
478 * get the sun4v config handle for this device
479 */
480
481 cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
482 kmem_free(reg_p, reglen);
483 ino = hdlp->ih_vector;
484
485 /*
486 * call hv to get vihdl
487 */
488 if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
489 return (DDI_FAILURE);
490
491 hdlp->ih_vector = ihdl;
492 /*
493 * Allocate a interrupt descriptor (id) with the
494 * the interrupt handler and append it to
495 * the id list.
496 */
497
498 vid_p = vnex_alloc_id(rdip, ino, cfg);
499 vid_p->vid_ihdl = ihdl;
500 vid_p->vid_handler = hdlp->ih_cb_func;
501 vid_p->vid_arg1 = hdlp->ih_cb_arg1;
502 vid_p->vid_arg2 = hdlp->ih_cb_arg2;
503 vid_p->vid_ddi_hdlp = hdlp;
504
505 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
506 (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
507
508 if (hdlp->ih_pri == 0) {
509 hdlp->ih_pri = vnex_get_pil(rdip);
510 }
511
512 ret = i_ddi_add_ivintr(hdlp);
513 if (ret != DDI_SUCCESS) {
514 return (ret);
515 }
516
517 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
518 vid_p->vid_arg1, vid_p->vid_arg2);
519
520 return (ret);
521 }
522
523 static int
vnex_remove_intr(dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)524 vnex_remove_intr(dev_info_t *rdip,
525 ddi_intr_handle_impl_t *hdlp)
526 {
527 vnex_id_t *vid_p;
528 uint32_t ino;
529 int ret = DDI_SUCCESS;
530
531 ino = hdlp->ih_vector;
532 vid_p = vnex_locate_id(rdip, ino);
533
534 hdlp->ih_vector = vid_p->vid_ihdl;
535 i_ddi_rem_ivintr(hdlp);
536
537 vnex_free_id(vid_p);
538
539 return (ret);
540 }
541
542 static int
vnex_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)543 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
544 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
545 {
546 int ret = DDI_SUCCESS;
547
548 switch (intr_op) {
549 case DDI_INTROP_GETCAP:
550 *(int *)result = DDI_INTR_FLAG_LEVEL;
551 break;
552 case DDI_INTROP_ALLOC:
553 *(int *)result = hdlp->ih_scratch1;
554 break;
555 case DDI_INTROP_GETPRI:
556 *(int *)result = hdlp->ih_pri ?
557 hdlp->ih_pri : vnex_get_pil(rdip);
558 break;
559 case DDI_INTROP_FREE:
560 break;
561 case DDI_INTROP_SETPRI:
562 break;
563 case DDI_INTROP_ADDISR:
564 ret = vnex_add_intr(dip, rdip, hdlp);
565 break;
566 case DDI_INTROP_REMISR:
567 ret = vnex_remove_intr(rdip, hdlp);
568 break;
569 case DDI_INTROP_ENABLE:
570 ret = vnex_enable_intr(rdip, hdlp);
571 break;
572 case DDI_INTROP_DISABLE:
573 ret = vnex_disable_intr(rdip, hdlp);
574 break;
575 case DDI_INTROP_NINTRS:
576 case DDI_INTROP_NAVAIL:
577 *(int *)result = i_ddi_get_intx_nintrs(rdip);
578 break;
579 case DDI_INTROP_SUPPORTED_TYPES:
580 *(int *)result = i_ddi_get_intx_nintrs(rdip) ?
581 DDI_INTR_TYPE_FIXED : 0;
582 break;
583 default:
584 ret = DDI_ENOTSUP;
585 break;
586 }
587
588 return (ret);
589 }
590
591 vnex_id_t *
vnex_alloc_id(dev_info_t * dip,uint32_t ino,uint64_t dhdl)592 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
593 {
594 vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
595
596 vid_p->vid_dip = dip;
597 vid_p->vid_ino = ino;
598 vid_p->vid_cfg_hdl = dhdl;
599
600 mutex_enter(&vnex_id_lock);
601 vnex_add_id(vid_p);
602 mutex_exit(&vnex_id_lock);
603
604 return (vid_p);
605 }
606
607 vnex_id_t *
vnex_locate_id(dev_info_t * dip,uint32_t ino)608 vnex_locate_id(dev_info_t *dip, uint32_t ino)
609 {
610 vnex_id_t *vid_p;
611
612 mutex_enter(&vnex_id_lock);
613 vid_p = vnex_id_list;
614
615 while (vid_p != NULL) {
616 if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
617 mutex_exit(&vnex_id_lock);
618 return (vid_p);
619 }
620 vid_p = vid_p->vid_next;
621 }
622 mutex_exit(&vnex_id_lock);
623 return (NULL);
624 }
625
626 static void
vnex_free_id(vnex_id_t * vid_p)627 vnex_free_id(vnex_id_t *vid_p)
628 {
629 mutex_enter(&vnex_id_lock);
630 vnex_rem_id(vid_p);
631 mutex_exit(&vnex_id_lock);
632
633 kmem_free(vid_p, sizeof (*vid_p));
634 }
635
636 static void
vnex_rem_id(vnex_id_t * vid_p)637 vnex_rem_id(vnex_id_t *vid_p)
638 {
639 vnex_id_t *prev_p = vnex_id_list;
640
641 if (vnex_id_list == NULL)
642 cmn_err(CE_PANIC, "vnex: interrupt list empty");
643
644 if (vid_p == NULL)
645 cmn_err(CE_PANIC, "vnex: no element to remove");
646
647 if (vnex_id_list == vid_p) {
648 vnex_id_list = vid_p->vid_next;
649 } else {
650 while (prev_p != NULL && prev_p->vid_next != vid_p)
651 prev_p = prev_p->vid_next;
652
653 if (prev_p == NULL)
654 cmn_err(CE_PANIC, "vnex: element %p not in list",
655 (void *) vid_p);
656
657 prev_p->vid_next = vid_p->vid_next;
658 }
659 }
660
661 static void
vnex_add_id(vnex_id_t * vid_p)662 vnex_add_id(vnex_id_t *vid_p)
663 {
664 vid_p->vid_next = vnex_id_list;
665 vnex_id_list = vid_p;
666 }
667
668 uint_t
vnex_intr_wrapper(caddr_t arg)669 vnex_intr_wrapper(caddr_t arg)
670 {
671 vnex_id_t *vid_p = (vnex_id_t *)arg;
672 int res;
673 uint_t (*handler)();
674 caddr_t handler_arg1;
675 caddr_t handler_arg2;
676
677 handler = vid_p->vid_handler;
678 handler_arg1 = vid_p->vid_arg1;
679 handler_arg2 = vid_p->vid_arg2;
680
681 res = (*handler)(handler_arg1, handler_arg2);
682
683 (void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
684
685 return (res);
686 }
687