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