Home | History | Annotate | Download | only in io
      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