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 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
28 */
29
30 /*
31 * Host to hypervisor virtual devices nexus driver
32 *
33 * TODO:
34 * - Add watchpoints on vbd/vif and enumerate/offline on watch callback
35 * - Add DR IOCTLs
36 * - Filter/restrict property lookups into xenstore
37 */
38
39 #include <sys/conf.h>
40 #include <sys/kmem.h>
41 #include <sys/debug.h>
42 #include <sys/modctl.h>
43 #include <sys/autoconf.h>
44 #include <sys/ddi_impldefs.h>
45 #include <sys/ddi_subrdefs.h>
46 #include <sys/ddi.h>
47 #include <sys/sunddi.h>
48 #include <sys/sunndi.h>
49 #include <sys/avintr.h>
50 #include <sys/psm.h>
51 #include <sys/spl.h>
52 #include <sys/promif.h>
53 #include <sys/list.h>
54 #include <sys/bootconf.h>
55 #include <sys/bootsvcs.h>
56 #include <util/sscanf.h>
57 #include <sys/mach_intr.h>
58 #include <sys/bootinfo.h>
59 #ifdef XPV_HVM_DRIVER
60 #include <sys/xpv_support.h>
61 #include <sys/hypervisor.h>
62 #include <sys/archsystm.h>
63 #include <sys/cpu.h>
64 #include <public/xen.h>
65 #include <public/event_channel.h>
66 #include <public/io/xenbus.h>
67 #else
68 #include <sys/hypervisor.h>
69 #include <sys/evtchn_impl.h>
70 #include <sys/xen_mmu.h>
71 #endif
72 #include <xen/sys/xenbus_impl.h>
73 #include <xen/sys/xendev.h>
74
75 /*
76 * DDI dev_ops entrypoints
77 */
78 static int xpvd_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
79 static int xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
80 static int xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
81
82
83 /*
84 * NDI bus_ops entrypoints
85 */
86 static int xpvd_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
87 void *);
88 static int xpvd_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
89 ddi_intr_handle_impl_t *, void *);
90 static int xpvd_prop_op(dev_t, dev_info_t *, dev_info_t *, ddi_prop_op_t,
91 int, char *, caddr_t, int *);
92 static int xpvd_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
93 void *, dev_info_t **);
94 static int xpvd_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
95 void *);
96 static int xpvd_get_eventcookie(dev_info_t *, dev_info_t *,
97 char *, ddi_eventcookie_t *);
98 static int xpvd_add_eventcall(dev_info_t *, dev_info_t *,
99 ddi_eventcookie_t, void (*)(dev_info_t *,
100 ddi_eventcookie_t, void *, void *),
101 void *, ddi_callback_id_t *);
102 static int xpvd_remove_eventcall(dev_info_t *, ddi_callback_id_t);
103 static int xpvd_post_event(dev_info_t *, dev_info_t *,
104 ddi_eventcookie_t, void *);
105
106 /*
107 * misc functions
108 */
109 static int xpvd_enable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int);
110 static void xpvd_disable_intr(dev_info_t *, ddi_intr_handle_impl_t *, int);
111 static int xpvd_removechild(dev_info_t *);
112 static int xpvd_initchild(dev_info_t *);
113 static int xpvd_name_child(dev_info_t *, char *, int);
114 static boolean_t i_xpvd_parse_devname(char *, xendev_devclass_t *,
115 domid_t *, int *);
116
117
118 /* Extern declarations */
119 extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
120 psm_intr_op_t, int *);
121
122 struct bus_ops xpvd_bus_ops = {
123 BUSO_REV,
124 i_ddi_bus_map,
125 NULL,
126 NULL,
127 NULL,
128 i_ddi_map_fault,
129 NULL,
130 ddi_dma_allochdl,
131 ddi_dma_freehdl,
132 ddi_dma_bindhdl,
133 ddi_dma_unbindhdl,
134 ddi_dma_flush,
135 ddi_dma_win,
136 ddi_dma_mctl,
137 xpvd_ctlops,
138 xpvd_prop_op,
139 xpvd_get_eventcookie,
140 xpvd_add_eventcall,
141 xpvd_remove_eventcall,
142 xpvd_post_event,
143 0, /* (*bus_intr_ctl)(); */
144 xpvd_bus_config,
145 xpvd_bus_unconfig,
146 NULL, /* (*bus_fm_init)(); */
147 NULL, /* (*bus_fm_fini)(); */
148 NULL, /* (*bus_fm_access_enter)(); */
149 NULL, /* (*bus_fm_access_exit)(); */
150 NULL, /* (*bus_power)(); */
151 xpvd_intr_ops /* (*bus_intr_op)(); */
152 };
153
154 struct dev_ops xpvd_ops = {
155 DEVO_REV, /* devo_rev */
156 0, /* refcnt */
157 xpvd_info, /* info */
158 nulldev, /* identify */
159 nulldev, /* probe */
160 xpvd_attach, /* attach */
161 xpvd_detach, /* detach */
162 nulldev, /* reset */
163 (struct cb_ops *)0, /* driver operations */
164 &xpvd_bus_ops, /* bus operations */
165 NULL, /* power */
166 ddi_quiesce_not_needed, /* quiesce */
167 };
168
169
170 dev_info_t *xpvd_dip;
171
172 #define CF_DBG 0x1
173 #define ALL_DBG 0xff
174
175 static ndi_event_definition_t xpvd_ndi_event_defs[] = {
176 { 0, XS_OE_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
177 { 1, XS_HP_STATE, EPL_KERNEL, NDI_EVENT_POST_TO_TGT },
178 };
179
180 #define XENDEV_N_NDI_EVENTS \
181 (sizeof (xpvd_ndi_event_defs) / sizeof (xpvd_ndi_event_defs[0]))
182
183 static ndi_event_set_t xpvd_ndi_events = {
184 NDI_EVENTS_REV1, XENDEV_N_NDI_EVENTS, xpvd_ndi_event_defs
185 };
186
187 static ndi_event_hdl_t xpvd_ndi_event_handle;
188
189 /*
190 * Hypervisor interrupt capabilities
191 */
192 #define XENDEV_INTR_CAPABILITIES \
193 (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_PENDING)
194
195 /*
196 * Module linkage information for the kernel.
197 */
198
199 static struct modldrv modldrv = {
200 &mod_driverops, /* Type of module */
201 "virtual device nexus driver",
202 &xpvd_ops, /* driver ops */
203 };
204
205 static struct modlinkage modlinkage = {
206 MODREV_1,
207 { (void *)&modldrv, NULL }
208 };
209
210 int
211 _init(void)
212 {
213 return (mod_install(&modlinkage));
214 }
215
216 int
217 _fini(void)
218 {
219 return (mod_remove(&modlinkage));
220 }
221
222 int
223 _info(struct modinfo *modinfop)
224 {
225 return (mod_info(&modlinkage, modinfop));
226 }
227
228 /* ARGSUSED */
229 static int
230 xpvd_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
231 {
232 switch (cmd) {
233 default:
234 return (DDI_FAILURE);
235
236 case DDI_INFO_DEVT2INSTANCE:
237 *result = (void *)0;
238 return (DDI_SUCCESS);
239
240 case DDI_INFO_DEVT2DEVINFO:
241 *result = (void *)xpvd_dip;
242 return (DDI_SUCCESS);
243 }
244 }
245
246 /*ARGSUSED*/
247 static int
248 xpvd_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
249 {
250 extern void xvdi_watch_devices(int);
251 #ifdef XPV_HVM_DRIVER
252 extern dev_info_t *xpv_dip;
253
254 if (xpv_dip == NULL) {
255 if (ddi_hold_installed_driver(ddi_name_to_major("xpv")) ==
256 NULL) {
257 cmn_err(CE_WARN, "Couldn't initialize xpv framework");
258 return (DDI_FAILURE);
259 }
260 }
261 #endif /* XPV_HVM_DRIVER */
262
263 if (ndi_event_alloc_hdl(devi, 0, &xpvd_ndi_event_handle,
264 NDI_SLEEP) != NDI_SUCCESS) {
265 xpvd_dip = NULL;
266 return (DDI_FAILURE);
267 }
268 if (ndi_event_bind_set(xpvd_ndi_event_handle, &xpvd_ndi_events,
269 NDI_SLEEP) != NDI_SUCCESS) {
270 (void) ndi_event_free_hdl(xpvd_ndi_event_handle);
271 xpvd_dip = NULL;
272 return (DDI_FAILURE);
273 }
274
275 #ifdef XPV_HVM_DRIVER
276 (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1);
277
278 /*
279 * Report our version to dom0.
280 */
281 if (xenbus_printf(XBT_NULL, "guest/xpvd", "version", "%d",
282 HVMPV_XPVD_VERS))
283 cmn_err(CE_WARN, "xpvd: couldn't write version\n");
284 #endif /* XPV_HVM_DRIVER */
285
286 /* watch both frontend and backend for new devices */
287 if (DOMAIN_IS_INITDOMAIN(xen_info))
288 (void) xs_register_xenbus_callback(xvdi_watch_devices);
289 else
290 xvdi_watch_devices(XENSTORE_UP);
291
292 xpvd_dip = devi;
293 ddi_report_dev(devi);
294
295 return (DDI_SUCCESS);
296 }
297
298 /*ARGSUSED*/
299 static int
300 xpvd_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
301 {
302 return (DDI_FAILURE);
303 }
304
305 /*
306 * xpvd_prop_op()
307 *
308 * Query xenstore for the value of properties if DDI_PROP_NOTPROM
309 * is not set. Xenstore property values are represented as ascii strings.
310 */
311 static int
312 xpvd_prop_op(dev_t dev, dev_info_t *dip, dev_info_t *ch_dip,
313 ddi_prop_op_t prop_op, int mod_flags, char *name, caddr_t valuep,
314 int *lengthp)
315 {
316 caddr_t buff;
317 struct xendev_ppd *pdp;
318 void *prop_str;
319 size_t prop_len;
320 unsigned int len;
321 int rv;
322
323 pdp = (struct xendev_ppd *)ddi_get_parent_data(ch_dip);
324
325 if ((pdp == NULL) || !(mod_flags & (DDI_PROP_CANSLEEP)) ||
326 (mod_flags & DDI_PROP_NOTPROM) || (pdp->xd_xsdev.nodename == NULL))
327 goto toss_off;
328 /*
329 * First try reading the property off the the frontend. if that
330 * fails, try and read it from the backend node. If that
331 * also fails, pass the request on the DDI framework
332 */
333 prop_str = NULL;
334 if ((xenbus_read(XBT_NULL, pdp->xd_xsdev.nodename, name, &prop_str,
335 &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0))
336 goto got_xs_prop;
337
338 prop_str = NULL;
339 if ((pdp->xd_xsdev.otherend != NULL) &&
340 (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, name, &prop_str,
341 &len) == 0) && (prop_str != NULL) && (strlen(prop_str) != 0))
342 goto got_xs_prop;
343
344 toss_off:
345 return (ddi_bus_prop_op(dev, dip, ch_dip, prop_op,
346 mod_flags | DDI_PROP_NOTPROM, name, valuep, lengthp));
347
348 got_xs_prop:
349 prop_len = strlen(prop_str) + 1;
350 rv = DDI_PROP_SUCCESS;
351
352 switch (prop_op) {
353 case PROP_LEN:
354 *lengthp = prop_len;
355 break;
356
357 case PROP_LEN_AND_VAL_ALLOC:
358 buff = kmem_alloc((size_t)prop_len, KM_SLEEP);
359 *(caddr_t *)valuep = (caddr_t)buff;
360 break;
361 case PROP_LEN_AND_VAL_BUF:
362 buff = (caddr_t)valuep;
363 if (*lengthp < prop_len)
364 rv = DDI_PROP_BUF_TOO_SMALL;
365 break;
366 default:
367 rv = DDI_PROP_INVAL_ARG;
368 break;
369 }
370
371 if ((rv == DDI_PROP_SUCCESS) && (prop_len > 0)) {
372 bcopy(prop_str, buff, prop_len);
373 *lengthp = prop_len;
374 }
375 kmem_free(prop_str, len);
376 return (rv);
377 }
378
379
380 /*
381 * return address of the device's interrupt spec structure.
382 */
383 /*ARGSUSED*/
384 struct intrspec *
385 xpvd_get_ispec(dev_info_t *rdip, uint_t inumber)
386 {
387 struct xendev_ppd *pdp;
388
389 ASSERT(inumber == 0);
390
391 if ((pdp = ddi_get_parent_data(rdip)) == NULL)
392 return (NULL);
393
394 return (&pdp->xd_ispec);
395 }
396
397 /*
398 * return (and determine) the interrupt priority of the device.
399 */
400 /*ARGSUSED*/
401 static int
402 xpvd_get_priority(dev_info_t *dip, int inum, int *pri)
403 {
404 struct xendev_ppd *pdp;
405 struct intrspec *ispec;
406 int *intpriorities;
407 uint_t num_intpriorities;
408
409 DDI_INTR_NEXDBG((CE_CONT, "xpvd_get_priority: dip = 0x%p\n",
410 (void *)dip));
411
412 ASSERT(inum == 0);
413
414 if ((pdp = ddi_get_parent_data(dip)) == NULL)
415 return (DDI_FAILURE);
416
417 ispec = &pdp->xd_ispec;
418
419 /*
420 * Set the default priority based on the device class. The
421 * "interrupt-priorities" property can be used to override
422 * the default.
423 */
424 if (ispec->intrspec_pri == 0) {
425 ispec->intrspec_pri = xendev_devclass_ipl(pdp->xd_devclass);
426 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
427 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
428 "interrupt-priorities", &intpriorities,
429 &num_intpriorities) == DDI_PROP_SUCCESS) {
430 ispec->intrspec_pri = intpriorities[0];
431 ddi_prop_free(intpriorities);
432 }
433 }
434 *pri = ispec->intrspec_pri;
435 return (DDI_SUCCESS);
436 }
437
438
439 /*
440 * xpvd_intr_ops: bus_intr_op() function for interrupt support
441 */
442 /* ARGSUSED */
443 static int
444 xpvd_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
445 ddi_intr_handle_impl_t *hdlp, void *result)
446 {
447 int priority = 0;
448 struct intrspec *ispec;
449 struct xendev_ppd *pdp;
450
451 DDI_INTR_NEXDBG((CE_CONT,
452 "xpvd_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
453 (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
454
455 /* Process the request */
456 switch (intr_op) {
457 case DDI_INTROP_SUPPORTED_TYPES:
458 /* Fixed supported by default */
459 *(int *)result = DDI_INTR_TYPE_FIXED;
460 break;
461
462 case DDI_INTROP_NINTRS:
463 *(int *)result = 1;
464 break;
465
466 case DDI_INTROP_ALLOC:
467 /*
468 * FIXED interrupts: just return available interrupts
469 */
470 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
471 /*
472 * event channels are edge-triggered, maskable,
473 * and support int pending.
474 */
475 hdlp->ih_cap |= XENDEV_INTR_CAPABILITIES;
476 *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */
477 } else {
478 return (DDI_FAILURE);
479 }
480 break;
481
482 case DDI_INTROP_FREE:
483 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
484 if (ispec == NULL)
485 return (DDI_FAILURE);
486 ispec->intrspec_pri = 0; /* mark as un-initialized */
487 break;
488
489 case DDI_INTROP_GETPRI:
490 if (xpvd_get_priority(rdip, hdlp->ih_inum, &priority) !=
491 DDI_SUCCESS)
492 return (DDI_FAILURE);
493 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: priority = 0x%x\n",
494 priority));
495 *(int *)result = priority;
496 break;
497
498 case DDI_INTROP_SETPRI:
499 /* Validate the interrupt priority passed */
500 if (*(int *)result > LOCK_LEVEL)
501 return (DDI_FAILURE);
502
503 /* Ensure that PSM is all initialized */
504 if (psm_intr_ops == NULL)
505 return (DDI_FAILURE);
506
507 /* Change the priority */
508 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
509 PSM_FAILURE)
510 return (DDI_FAILURE);
511
512 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
513 if (ispec == NULL)
514 return (DDI_FAILURE);
515 ispec->intrspec_pri = *(int *)result;
516 break;
517
518 case DDI_INTROP_ADDISR:
519 /* update ispec */
520 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
521 if (ispec == NULL)
522 return (DDI_FAILURE);
523 ispec->intrspec_func = hdlp->ih_cb_func;
524
525 break;
526
527 case DDI_INTROP_REMISR:
528 ispec = xpvd_get_ispec(rdip, (int)hdlp->ih_inum);
529 pdp = (struct xendev_ppd *)ddi_get_parent_data(rdip);
530
531 ASSERT(pdp != NULL);
532 ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
533
534 if (ispec) {
535 ispec->intrspec_vec = 0;
536 ispec->intrspec_func = (uint_t (*)()) 0;
537 }
538 pdp->xd_evtchn = INVALID_EVTCHN;
539 break;
540
541 case DDI_INTROP_GETCAP:
542 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
543 /*
544 * event channels are edge-triggered, maskable,
545 * and support int pending.
546 */
547 *(int *)result = XENDEV_INTR_CAPABILITIES;
548 } else {
549 *(int *)result = 0;
550 return (DDI_FAILURE);
551 }
552 DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETCAP returned = %x\n",
553 *(int *)result));
554 break;
555 case DDI_INTROP_SETCAP:
556 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: SETCAP cap=0x%x\n",
557 *(int *)result));
558 if (psm_intr_ops == NULL)
559 return (DDI_FAILURE);
560
561 if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
562 DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
563 " returned failure\n"));
564 return (DDI_FAILURE);
565 }
566 break;
567
568 case DDI_INTROP_ENABLE:
569 if (psm_intr_ops == NULL)
570 return (DDI_FAILURE);
571
572 if (xpvd_enable_intr(rdip, hdlp, (int)hdlp->ih_inum) !=
573 DDI_SUCCESS)
574 return (DDI_FAILURE);
575
576 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: ENABLE vec=0x%x\n",
577 hdlp->ih_vector));
578 break;
579
580 case DDI_INTROP_DISABLE:
581 if (psm_intr_ops == NULL)
582 return (DDI_FAILURE);
583 xpvd_disable_intr(rdip, hdlp, hdlp->ih_inum);
584 DDI_INTR_NEXDBG((CE_CONT, "xpvd_intr_ops: DISABLE vec = %x\n",
585 hdlp->ih_vector));
586 break;
587
588 case DDI_INTROP_BLOCKENABLE:
589 case DDI_INTROP_BLOCKDISABLE:
590 return (DDI_FAILURE);
591
592 case DDI_INTROP_SETMASK:
593 case DDI_INTROP_CLRMASK:
594 #ifdef XPV_HVM_DRIVER
595 return (DDI_ENOTSUP);
596 #else
597 /*
598 * Handle this here
599 */
600 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
601 return (DDI_FAILURE);
602 if (intr_op == DDI_INTROP_SETMASK) {
603 ec_disable_irq(hdlp->ih_vector);
604 } else {
605 ec_enable_irq(hdlp->ih_vector);
606 }
607 break;
608 #endif
609 case DDI_INTROP_GETPENDING:
610 #ifdef XPV_HVM_DRIVER
611 return (DDI_ENOTSUP);
612 #else
613 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
614 return (DDI_FAILURE);
615 *(int *)result = ec_pending_irq(hdlp->ih_vector);
616 DDI_INTR_NEXDBG((CE_CONT, "xpvd: GETPENDING returned = %x\n",
617 *(int *)result));
618 break;
619 #endif
620
621 case DDI_INTROP_NAVAIL:
622 *(int *)result = 1;
623 DDI_INTR_NEXDBG((CE_CONT, "xpvd: NAVAIL returned = %x\n",
624 *(int *)result));
625 break;
626
627 default:
628 return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
629 }
630
631 return (DDI_SUCCESS);
632 }
633
634
635 static int
636 xpvd_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum)
637 {
638 int vector;
639 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
640
641 DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: hdlp %p inum %x\n",
642 (void *)hdlp, inum));
643
644 ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum);
645 if (ihdl_plat_datap->ip_ispecp == NULL)
646 return (DDI_FAILURE);
647
648 /* translate the interrupt if needed */
649 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
650 DDI_INTR_NEXDBG((CE_CONT, "xpvd_enable_intr: priority=%x vector=%x\n",
651 hdlp->ih_pri, vector));
652
653 /* Add the interrupt handler */
654 if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
655 DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
656 hdlp->ih_cb_arg2, NULL, rdip))
657 return (DDI_FAILURE);
658
659 /* Note this really is an irq. */
660 hdlp->ih_vector = (ushort_t)vector;
661
662 return (DDI_SUCCESS);
663 }
664
665
666 static void
667 xpvd_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp, int inum)
668 {
669 int vector;
670 ihdl_plat_t *ihdl_plat_datap = (ihdl_plat_t *)hdlp->ih_private;
671
672 DDI_INTR_NEXDBG((CE_CONT, "xpvd_disable_intr: \n"));
673 ihdl_plat_datap->ip_ispecp = xpvd_get_ispec(rdip, inum);
674 if (ihdl_plat_datap->ip_ispecp == NULL)
675 return;
676
677 /* translate the interrupt if needed */
678 (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
679
680 /* Disable the interrupt handler */
681 rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
682 ihdl_plat_datap->ip_ispecp = NULL;
683 }
684
685 /*ARGSUSED*/
686 static int
687 xpvd_ctlops(dev_info_t *dip, dev_info_t *rdip,
688 ddi_ctl_enum_t ctlop, void *arg, void *result)
689 {
690 switch (ctlop) {
691 case DDI_CTLOPS_REPORTDEV:
692 if (rdip == (dev_info_t *)0)
693 return (DDI_FAILURE);
694 cmn_err(CE_CONT, "?%s@%s, %s%d\n", ddi_node_name(rdip),
695 ddi_get_name_addr(rdip), ddi_driver_name(rdip),
696 ddi_get_instance(rdip));
697 return (DDI_SUCCESS);
698
699 case DDI_CTLOPS_INITCHILD:
700 return (xpvd_initchild((dev_info_t *)arg));
701
702 case DDI_CTLOPS_UNINITCHILD:
703 return (xpvd_removechild((dev_info_t *)arg));
704
705 case DDI_CTLOPS_SIDDEV:
706 return (DDI_SUCCESS);
707
708 case DDI_CTLOPS_REGSIZE:
709 case DDI_CTLOPS_NREGS:
710 return (DDI_FAILURE);
711
712 case DDI_CTLOPS_POWER: {
713 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
714 }
715
716 default:
717 return (ddi_ctlops(dip, rdip, ctlop, arg, result));
718 }
719
720 /* NOTREACHED */
721
722 }
723
724 /*
725 * Assign the address portion of the node name
726 */
727 static int
728 xpvd_name_child(dev_info_t *child, char *addr, int addrlen)
729 {
730 int *domain, *vdev;
731 uint_t ndomain, nvdev;
732 char *prop_str;
733
734 /*
735 * i_xpvd_parse_devname() knows the formats used by this
736 * routine. If this code changes, so must that.
737 */
738
739 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
740 "domain", &domain, &ndomain) != DDI_PROP_SUCCESS)
741 return (DDI_FAILURE);
742 ASSERT(ndomain == 1);
743
744 /*
745 * Use "domain" and "vdev" properties (backend drivers).
746 */
747 if (*domain != DOMID_SELF) {
748 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
749 DDI_PROP_DONTPASS, "vdev", &vdev, &nvdev)
750 != DDI_PROP_SUCCESS) {
751 ddi_prop_free(domain);
752 return (DDI_FAILURE);
753 }
754 ASSERT(nvdev == 1);
755
756 (void) snprintf(addr, addrlen, "%d,%d", domain[0], vdev[0]);
757 ddi_prop_free(vdev);
758 ddi_prop_free(domain);
759 return (DDI_SUCCESS);
760 }
761 ddi_prop_free(domain);
762
763 /*
764 * Use "unit-address" property (frontend/softdev drivers).
765 */
766 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
767 "unit-address", &prop_str) != DDI_PROP_SUCCESS)
768 return (DDI_FAILURE);
769 (void) strlcpy(addr, prop_str, addrlen);
770 ddi_prop_free(prop_str);
771 return (DDI_SUCCESS);
772 }
773
774 static int
775 xpvd_initchild(dev_info_t *child)
776 {
777 char addr[80];
778
779 /*
780 * Pseudo nodes indicate a prototype node with per-instance
781 * properties to be merged into the real h/w device node.
782 */
783 if (ndi_dev_is_persistent_node(child) == 0) {
784 ddi_set_parent_data(child, NULL);
785
786 /*
787 * Try to merge the properties from this prototype
788 * node into real h/w nodes.
789 */
790 if (ndi_merge_node(child, xpvd_name_child) == DDI_SUCCESS) {
791 /*
792 * Merged ok - return failure to remove the node.
793 */
794 ddi_set_name_addr(child, NULL);
795 return (DDI_FAILURE);
796 }
797
798 /*
799 * The child was not merged into a h/w node,
800 * but there's not much we can do with it other
801 * than return failure to cause the node to be removed.
802 */
803 cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
804 ddi_get_name(child), ddi_get_name_addr(child),
805 ddi_get_name(child));
806 ddi_set_name_addr(child, NULL);
807 return (DDI_NOT_WELL_FORMED);
808 }
809
810 if (xvdi_init_dev(child) != DDI_SUCCESS)
811 return (DDI_FAILURE);
812
813 if (xpvd_name_child(child, addr, sizeof (addr)) != DDI_SUCCESS) {
814 xvdi_uninit_dev(child);
815 return (DDI_FAILURE);
816 }
817 ddi_set_name_addr(child, addr);
818
819 return (DDI_SUCCESS);
820 }
821
822 static int
823 xpvd_removechild(dev_info_t *dip)
824 {
825 xvdi_uninit_dev(dip);
826
827 ddi_set_name_addr(dip, NULL);
828
829 /*
830 * Strip the node to properly convert it back to prototype
831 * form.
832 */
833 ddi_remove_minor_node(dip, NULL);
834
835 return (DDI_SUCCESS);
836 }
837
838 static int
839 xpvd_bus_unconfig(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op,
840 void *device_name)
841 {
842 return (ndi_busop_bus_unconfig(parent, flag, op, device_name));
843 }
844
845 /*
846 * Given the name of a child of xpvd, determine the device class,
847 * domain and vdevnum to which it refers.
848 */
849 static boolean_t
850 i_xpvd_parse_devname(char *name, xendev_devclass_t *devclassp,
851 domid_t *domp, int *vdevp)
852 {
853 int len = strlen(name) + 1;
854 char *device_name = i_ddi_strdup(name, KM_SLEEP);
855 char *cname = NULL, *caddr = NULL;
856 boolean_t ret;
857
858 i_ddi_parse_name(device_name, &cname, &caddr, NULL);
859
860 if ((cname == NULL) || (strlen(cname) == 0) ||
861 (caddr == NULL) || (strlen(caddr) == 0)) {
862 ret = B_FALSE;
863 goto done;
864 }
865
866 *devclassp = xendev_nodename_to_devclass(cname);
867 if (*devclassp < 0) {
868 ret = B_FALSE;
869 goto done;
870 }
871
872 /*
873 * Parsing the address component requires knowledge of how
874 * xpvd_name_child() works. If that code changes, so must
875 * this.
876 */
877
878 /* Backend format is "<domain>,<vdev>". */
879 if (sscanf(caddr, "%hu,%d", domp, vdevp) == 2) {
880 ret = B_TRUE;
881 goto done;
882 }
883
884 /* Frontend format is "<vdev>". */
885 *domp = DOMID_SELF;
886 if (sscanf(caddr, "%d", vdevp) == 1)
887 ret = B_TRUE;
888 done:
889 kmem_free(device_name, len);
890 return (ret);
891 }
892
893 /*
894 * xpvd_bus_config()
895 *
896 * BUS_CONFIG_ONE:
897 * Enumerate the exact instance of a driver.
898 *
899 * BUS_CONFIG_ALL:
900 * Enumerate all the instances of all the possible children (seen before
901 * and never seen before).
902 *
903 * BUS_CONFIG_DRIVER:
904 * Enumerate all the instances of a particular driver.
905 */
906 static int
907 xpvd_bus_config(dev_info_t *parent, uint_t flag, ddi_bus_config_op_t op,
908 void *arg, dev_info_t **childp)
909 {
910 int circ;
911 char *cname = NULL;
912
913 ndi_devi_enter(parent, &circ);
914
915 switch (op) {
916 case BUS_CONFIG_ONE: {
917 xendev_devclass_t devclass;
918 domid_t dom;
919 int vdev;
920
921 if (!i_xpvd_parse_devname(arg, &devclass, &dom, &vdev)) {
922 ndi_devi_exit(parent, circ);
923 return (NDI_FAILURE);
924 }
925
926 *childp = xvdi_find_dev(parent, devclass, dom, vdev);
927 if (*childp == NULL)
928 *childp = xvdi_create_dev(parent, devclass, dom, vdev);
929
930 ndi_devi_exit(parent, circ);
931
932 if (*childp == NULL)
933 return (NDI_FAILURE);
934 else
935 return (ndi_busop_bus_config(parent, flag,
936 op, arg, childp, 0));
937 }
938
939 case BUS_CONFIG_DRIVER: {
940 xendev_devclass_t devclass = XEN_INVAL;
941
942 cname = ddi_major_to_name((major_t)(uintptr_t)arg);
943 if (cname != NULL)
944 devclass = xendev_nodename_to_devclass(cname);
945
946 if (devclass == XEN_INVAL) {
947 ndi_devi_exit(parent, circ);
948 return (NDI_FAILURE);
949 } else {
950 xendev_enum_class(parent, devclass);
951 ndi_devi_exit(parent, circ);
952 return (ndi_busop_bus_config(parent, flag, op,
953 arg, childp, 0));
954 }
955 /* NOTREACHED */
956 }
957
958 case BUS_CONFIG_ALL:
959 xendev_enum_all(parent, B_FALSE);
960 ndi_devi_exit(parent, circ);
961
962 return (ndi_busop_bus_config(parent, flag, op,
963 arg, childp, 0));
964
965 default:
966 ndi_devi_exit(parent, circ);
967 return (NDI_FAILURE);
968 }
969 }
970
971 /*ARGSUSED*/
972 static int
973 xpvd_get_eventcookie(dev_info_t *dip, dev_info_t *rdip,
974 char *eventname, ddi_eventcookie_t *cookie)
975 {
976 return (ndi_event_retrieve_cookie(xpvd_ndi_event_handle,
977 rdip, eventname, cookie, NDI_EVENT_NOPASS));
978 }
979
980 /*ARGSUSED*/
981 static int
982 xpvd_add_eventcall(dev_info_t *dip, dev_info_t *rdip,
983 ddi_eventcookie_t cookie, void (*callback)(dev_info_t *dip,
984 ddi_eventcookie_t cookie, void *arg, void *bus_impldata),
985 void *arg, ddi_callback_id_t *cb_id)
986 {
987 return (ndi_event_add_callback(xpvd_ndi_event_handle,
988 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
989 }
990
991 /*ARGSUSED*/
992 static int
993 xpvd_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
994 {
995 return (ndi_event_remove_callback(xpvd_ndi_event_handle,
996 cb_id));
997 }
998
999 /*ARGSUSED*/
1000 static int
1001 xpvd_post_event(dev_info_t *dip, dev_info_t *rdip,
1002 ddi_eventcookie_t cookie, void *bus_impldata)
1003 {
1004 return (ndi_event_run_callbacks(xpvd_ndi_event_handle, rdip,
1005 cookie, bus_impldata));
1006 }