xref: /onnv-gate/usr/src/uts/common/xen/os/xvdi.c (revision 7679:313c6f74b70c)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
235910Smrj  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  * Xen virtual device driver interfaces
295084Sjohnlev  */
305084Sjohnlev 
315084Sjohnlev /*
325084Sjohnlev  * todo:
335084Sjohnlev  * + name space clean up:
345084Sjohnlev  *	xvdi_* - public xen interfaces, for use by all leaf drivers
355084Sjohnlev  *	xd_* - public xen data structures
365084Sjohnlev  *	i_xvdi_* - implementation private functions
375084Sjohnlev  *	xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
385084Sjohnlev  * + add mdb dcmds to dump ring status
395084Sjohnlev  * + implement xvdi_xxx to wrap xenbus_xxx read/write function
405084Sjohnlev  * + convert (xendev_ring_t *) into xvdi_ring_handle_t
415084Sjohnlev  */
425084Sjohnlev #include <sys/conf.h>
435084Sjohnlev #include <sys/param.h>
445084Sjohnlev #include <sys/kmem.h>
455084Sjohnlev #include <vm/seg_kmem.h>
465084Sjohnlev #include <sys/debug.h>
475084Sjohnlev #include <sys/modctl.h>
485084Sjohnlev #include <sys/autoconf.h>
495084Sjohnlev #include <sys/ddi_impldefs.h>
505084Sjohnlev #include <sys/ddi_subrdefs.h>
515084Sjohnlev #include <sys/ddi.h>
525084Sjohnlev #include <sys/sunddi.h>
535084Sjohnlev #include <sys/sunndi.h>
545084Sjohnlev #include <sys/sunldi.h>
555084Sjohnlev #include <sys/fs/dv_node.h>
565084Sjohnlev #include <sys/avintr.h>
575084Sjohnlev #include <sys/psm.h>
585084Sjohnlev #include <sys/spl.h>
595084Sjohnlev #include <sys/promif.h>
605084Sjohnlev #include <sys/list.h>
615084Sjohnlev #include <sys/bootconf.h>
625084Sjohnlev #include <sys/bootsvcs.h>
635084Sjohnlev #include <sys/bootinfo.h>
645084Sjohnlev #include <sys/note.h>
655741Smrj #ifdef XPV_HVM_DRIVER
665741Smrj #include <sys/xpv_support.h>
675741Smrj #include <sys/hypervisor.h>
685741Smrj #include <public/grant_table.h>
695741Smrj #include <public/xen.h>
705741Smrj #include <public/io/xenbus.h>
715741Smrj #include <public/io/xs_wire.h>
725741Smrj #include <public/event_channel.h>
735741Smrj #include <public/io/xenbus.h>
745741Smrj #else /* XPV_HVM_DRIVER */
755741Smrj #include <sys/hypervisor.h>
765084Sjohnlev #include <sys/xen_mmu.h>
775084Sjohnlev #include <xen/sys/xenbus_impl.h>
785741Smrj #include <sys/evtchn_impl.h>
795741Smrj #endif /* XPV_HVM_DRIVER */
805741Smrj #include <sys/gnttab.h>
815084Sjohnlev #include <xen/sys/xendev.h>
825084Sjohnlev #include <vm/hat_i86.h>
835084Sjohnlev #include <sys/scsi/generic/inquiry.h>
845084Sjohnlev #include <util/sscanf.h>
855084Sjohnlev #include <xen/public/io/xs_wire.h>
865084Sjohnlev 
875084Sjohnlev 
886318Sedp #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
896318Sedp #define	isxdigit(ch)	(isdigit(ch) || ((ch) >= 'a' && (ch) <= 'f') || \
906318Sedp 			((ch) >= 'A' && (ch) <= 'F'))
916318Sedp 
925084Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
935084Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
945741Smrj #ifndef XPV_HVM_DRIVER
955084Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
965741Smrj #endif
975084Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
985084Sjohnlev 
995084Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
1005084Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
1015084Sjohnlev 
1025084Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
1035084Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
1045084Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
1055084Sjohnlev static void i_xvdi_oestate_handler(void *);
1065084Sjohnlev 
1075084Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
1085084Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
1095084Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
1105084Sjohnlev     unsigned int);
1115084Sjohnlev static void i_xvdi_hpstate_handler(void *);
1125084Sjohnlev 
1135084Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
1145084Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
1155084Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
1165084Sjohnlev     unsigned in);
1175084Sjohnlev 
1185084Sjohnlev static void xendev_offline_device(void *);
1195084Sjohnlev 
1205084Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
1215084Sjohnlev     unsigned int);
1225084Sjohnlev static void i_xvdi_probe_path_handler(void *);
1235084Sjohnlev 
124*7679SMax.Zhen@Sun.COM typedef struct oestate_evt {
125*7679SMax.Zhen@Sun.COM 	dev_info_t *dip;
126*7679SMax.Zhen@Sun.COM 	XenbusState state;
127*7679SMax.Zhen@Sun.COM } i_oestate_evt_t;
128*7679SMax.Zhen@Sun.COM 
1295084Sjohnlev typedef struct xd_cfg {
1305084Sjohnlev 	xendev_devclass_t devclass;
1315084Sjohnlev 	char *xsdev;
1325084Sjohnlev 	char *xs_path_fe;
1335084Sjohnlev 	char *xs_path_be;
1345084Sjohnlev 	char *node_fe;
1355084Sjohnlev 	char *node_be;
1365084Sjohnlev 	char *device_type;
1375084Sjohnlev 	int xd_ipl;
1385084Sjohnlev 	int flags;
1395084Sjohnlev } i_xd_cfg_t;
1405084Sjohnlev 
1415084Sjohnlev #define	XD_DOM_ZERO	0x01	/* dom0 only. */
1425084Sjohnlev #define	XD_DOM_GUEST	0x02	/* Guest domains (i.e. non-dom0). */
1435084Sjohnlev #define	XD_DOM_IO	0x04	/* IO domains. */
1445084Sjohnlev 
1455084Sjohnlev #define	XD_DOM_ALL	(XD_DOM_ZERO | XD_DOM_GUEST)
1465084Sjohnlev 
1475084Sjohnlev static i_xd_cfg_t xdci[] = {
1485084Sjohnlev 	{ XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
1495084Sjohnlev 	    "console", IPL_CONS, XD_DOM_ALL, },
1505084Sjohnlev 
1515084Sjohnlev 	{ XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
1525084Sjohnlev 	    "network", IPL_VIF, XD_DOM_ALL, },
1535084Sjohnlev 
1545084Sjohnlev 	{ XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
1555084Sjohnlev 	    "block", IPL_VBD, XD_DOM_ALL, },
1565084Sjohnlev 
1575084Sjohnlev 	{ XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
1585084Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
1595084Sjohnlev 
1605084Sjohnlev 	{ XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
1615084Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
1625084Sjohnlev 
1635084Sjohnlev 	{ XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
1645084Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
1655084Sjohnlev 
1665084Sjohnlev 	{ XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
1675084Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
1685084Sjohnlev 
1695084Sjohnlev 	{ XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
1705084Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
1715084Sjohnlev };
1725084Sjohnlev #define	NXDC	(sizeof (xdci) / sizeof (xdci[0]))
1735084Sjohnlev 
1745084Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
1755084Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
1765084Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
1775084Sjohnlev 
1785084Sjohnlev /*
1795084Sjohnlev  * Xen device channel device access and DMA attributes
1805084Sjohnlev  */
1815084Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
1825084Sjohnlev 	DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
1835084Sjohnlev };
1845084Sjohnlev 
1855084Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
1865084Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
1875084Sjohnlev 	0,			/* lowest usable address */
1885084Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
1895084Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
1905084Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
1915084Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
1925084Sjohnlev 	1,			/* minimum transfer */
1935084Sjohnlev 	0xffffffffU,		/* maximum transfer */
1945084Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
1955084Sjohnlev 	1,			/* maximum number of segments */
1965084Sjohnlev 	1,			/* granularity */
1975084Sjohnlev 	0,			/* flags (reserved) */
1985084Sjohnlev };
1995084Sjohnlev 
2005084Sjohnlev static dev_info_t *xendev_dip = NULL;
2015084Sjohnlev 
2025084Sjohnlev #define	XVDI_DBG_STATE	0x01
2035084Sjohnlev #define	XVDI_DBG_PROBE	0x02
2045084Sjohnlev 
2055084Sjohnlev #ifdef DEBUG
2065316Sjohnlev int i_xvdi_debug = 0;
2075084Sjohnlev 
2085084Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)			\
2095084Sjohnlev {							\
2105084Sjohnlev 	if (i_xvdi_debug & (flag))			\
2115084Sjohnlev 		prom_printf((format), __VA_ARGS__);	\
2125084Sjohnlev }
2135084Sjohnlev #else
2145084Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)
2155084Sjohnlev #endif /* DEBUG */
2165084Sjohnlev 
2175084Sjohnlev static i_xd_cfg_t *
2185084Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
2195084Sjohnlev {
2205084Sjohnlev 	i_xd_cfg_t *xdcp;
2215084Sjohnlev 	int i;
2225084Sjohnlev 
2235084Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
2245084Sjohnlev 		if (xdcp->devclass == devclass)
2255084Sjohnlev 			return (xdcp);
2265084Sjohnlev 
2275084Sjohnlev 	return (NULL);
2285084Sjohnlev }
2295084Sjohnlev 
2305084Sjohnlev int
2315084Sjohnlev xvdi_init_dev(dev_info_t *dip)
2325084Sjohnlev {
2335084Sjohnlev 	xendev_devclass_t devcls;
2345084Sjohnlev 	int vdevnum;
2355084Sjohnlev 	domid_t domid;
2365084Sjohnlev 	struct xendev_ppd *pdp;
2375084Sjohnlev 	i_xd_cfg_t *xdcp;
2385084Sjohnlev 	boolean_t backend;
2395084Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
2405084Sjohnlev 	char *xsname;
2416500Sjhd 	void *prop_str;
2426503Sjhd 	unsigned int prop_len;
2436500Sjhd 	char unitaddr[8];
2445084Sjohnlev 
2455084Sjohnlev 	devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2465084Sjohnlev 	    DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
2475084Sjohnlev 	vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2486175Sjohnlev 	    DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
2495084Sjohnlev 	domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2505084Sjohnlev 	    DDI_PROP_DONTPASS, "domain", DOMID_SELF);
2515084Sjohnlev 
2525084Sjohnlev 	backend = (domid != DOMID_SELF);
2535084Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devcls);
2545084Sjohnlev 	if (xdcp->device_type != NULL)
2555084Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2565084Sjohnlev 		    "device_type", xdcp->device_type);
2575084Sjohnlev 
2585084Sjohnlev 	pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
2595084Sjohnlev 	pdp->xd_domain = domid;
2605084Sjohnlev 	pdp->xd_vdevnum = vdevnum;
2615084Sjohnlev 	pdp->xd_devclass = devcls;
2625084Sjohnlev 	pdp->xd_evtchn = INVALID_EVTCHN;
2635084Sjohnlev 	mutex_init(&pdp->xd_lk, NULL, MUTEX_DRIVER, NULL);
2645084Sjohnlev 	ddi_set_parent_data(dip, pdp);
2655084Sjohnlev 
2665084Sjohnlev 	/*
2675084Sjohnlev 	 * devices that do not need to interact with xenstore
2685084Sjohnlev 	 */
2696175Sjohnlev 	if (vdevnum == VDEV_NOXS) {
2705084Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2715084Sjohnlev 		    "unit-address", "0");
2725084Sjohnlev 		if (devcls == XEN_CONSOLE)
2735084Sjohnlev 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2745084Sjohnlev 			    "pm-hardware-state", "needs-suspend-resume");
2755084Sjohnlev 		return (DDI_SUCCESS);
2765084Sjohnlev 	}
2775084Sjohnlev 
2785084Sjohnlev 	/*
2795084Sjohnlev 	 * PV devices that need to probe xenstore
2805084Sjohnlev 	 */
2815084Sjohnlev 
2825084Sjohnlev 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
2835084Sjohnlev 	    "pm-hardware-state", "needs-suspend-resume");
2845084Sjohnlev 
2855084Sjohnlev 	xsname = xsnamebuf;
2865084Sjohnlev 	if (!backend)
2875084Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
2885084Sjohnlev 		    "%s/%d", xdcp->xs_path_fe, vdevnum);
2895084Sjohnlev 	else
2905084Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
2915084Sjohnlev 		    "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
2925159Sjohnlev 	if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
2935159Sjohnlev 		/* Don't try to init a dev that may be closing */
2945159Sjohnlev 		mutex_destroy(&pdp->xd_lk);
2955159Sjohnlev 		kmem_free(pdp, sizeof (*pdp));
2965159Sjohnlev 		ddi_set_parent_data(dip, NULL);
2975159Sjohnlev 		return (DDI_FAILURE);
2985159Sjohnlev 	}
2995084Sjohnlev 
3005084Sjohnlev 	pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
3015084Sjohnlev 	pdp->xd_xsdev.devicetype = xdcp->xsdev;
3025084Sjohnlev 	pdp->xd_xsdev.frontend = (backend ? 0 : 1);
3035084Sjohnlev 	pdp->xd_xsdev.data = dip;
3045084Sjohnlev 	pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
3055084Sjohnlev 	if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
3065084Sjohnlev 		cmn_err(CE_WARN, "xvdi_init_dev: "
3075084Sjohnlev 		    "cannot add watches for %s", xsname);
3085084Sjohnlev 		xvdi_uninit_dev(dip);
3095084Sjohnlev 		return (DDI_FAILURE);
3105084Sjohnlev 	}
3115084Sjohnlev 
3126500Sjhd 	if (backend)
3136500Sjhd 		return (DDI_SUCCESS);
3145084Sjohnlev 
3156500Sjhd 	/*
3166500Sjhd 	 * The unit-address for frontend devices is the name of the
3176500Sjhd 	 * of the xenstore node containing the device configuration
3186500Sjhd 	 * and is contained in the 'vdev' property.
3196500Sjhd 	 * VIF devices are named using an incrementing integer.
3206500Sjhd 	 * VBD devices are either named using the 16-bit dev_t value
3216500Sjhd 	 * for linux 'hd' and 'xvd' devices, or a simple integer value
3226500Sjhd 	 * in the range 0..767.  768 is the base value of the linux
3236500Sjhd 	 * dev_t namespace, the dev_t value for 'hda'.
3246500Sjhd 	 */
3256500Sjhd 	(void) snprintf(unitaddr, sizeof (unitaddr), "%d", vdevnum);
3266500Sjhd 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "unit-address",
3276500Sjhd 	    unitaddr);
3286318Sedp 
3296500Sjhd 	switch (devcls) {
3306500Sjhd 	case XEN_VNET:
3316500Sjhd 		if (xenbus_read(XBT_NULL, xsname, "mac", (void *)&prop_str,
3326500Sjhd 		    &prop_len) != 0)
3335084Sjohnlev 			break;
3346500Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "mac",
3356500Sjhd 		    prop_str);
3366500Sjhd 		kmem_free(prop_str, prop_len);
3376500Sjhd 		break;
3386500Sjhd 	case XEN_VBLK:
3396500Sjhd 		/*
3406500Sjhd 		 * cache a copy of the otherend name
3416500Sjhd 		 * for ease of observeability
3426500Sjhd 		 */
3436500Sjhd 		if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend, "dev",
3446500Sjhd 		    &prop_str, &prop_len) != 0)
3455084Sjohnlev 			break;
3466500Sjhd 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
3476500Sjhd 		    "dev-address", prop_str);
3486500Sjhd 		kmem_free(prop_str, prop_len);
3496500Sjhd 		break;
3506500Sjhd 	default:
3516500Sjhd 		break;
3525084Sjohnlev 	}
3535084Sjohnlev 
3545084Sjohnlev 	return (DDI_SUCCESS);
3555084Sjohnlev }
3565084Sjohnlev 
3575084Sjohnlev void
3585084Sjohnlev xvdi_uninit_dev(dev_info_t *dip)
3595084Sjohnlev {
3605084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
3615084Sjohnlev 
3625084Sjohnlev 	if (pdp != NULL) {
3635084Sjohnlev 		/* Remove any registered callbacks. */
3645084Sjohnlev 		xvdi_remove_event_handler(dip, NULL);
3655084Sjohnlev 
3665084Sjohnlev 		/* Remove any registered watches. */
3675084Sjohnlev 		i_xvdi_rem_watches(dip);
3685084Sjohnlev 
3695159Sjohnlev 		/* tell other end to close */
3705741Smrj 		if (pdp->xd_xsdev.otherend_id != (domid_t)-1)
3715741Smrj 			(void) xvdi_switch_state(dip, XBT_NULL,
3725741Smrj 			    XenbusStateClosed);
3735159Sjohnlev 
3745084Sjohnlev 		if (pdp->xd_xsdev.nodename != NULL)
3755084Sjohnlev 			kmem_free((char *)(pdp->xd_xsdev.nodename),
3765084Sjohnlev 			    strlen(pdp->xd_xsdev.nodename) + 1);
3775084Sjohnlev 
3785084Sjohnlev 		ddi_set_parent_data(dip, NULL);
3795084Sjohnlev 
3805084Sjohnlev 		mutex_destroy(&pdp->xd_lk);
3815084Sjohnlev 		kmem_free(pdp, sizeof (*pdp));
3825084Sjohnlev 	}
3835084Sjohnlev }
3845084Sjohnlev 
3855084Sjohnlev /*
3865084Sjohnlev  * Bind the event channel for this device instance.
3875084Sjohnlev  * Currently we only support one evtchn per device instance.
3885084Sjohnlev  */
3895084Sjohnlev int
3905084Sjohnlev xvdi_bind_evtchn(dev_info_t *dip, evtchn_port_t evtchn)
3915084Sjohnlev {
3925084Sjohnlev 	struct xendev_ppd *pdp;
3935084Sjohnlev 	domid_t oeid;
3945084Sjohnlev 	int r;
3955084Sjohnlev 
3965084Sjohnlev 	pdp = ddi_get_parent_data(dip);
3975084Sjohnlev 	ASSERT(pdp != NULL);
3985084Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
3995084Sjohnlev 
4005084Sjohnlev 	mutex_enter(&pdp->xd_lk);
4015084Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
4025084Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
4035084Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
4045084Sjohnlev 		} else {
4055084Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4065084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4075084Sjohnlev 			return (DDI_SUCCESS);
4085084Sjohnlev 		}
4095084Sjohnlev 	} else {
4105084Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
4115084Sjohnlev 		if (oeid == (domid_t)-1) {
4125084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4135084Sjohnlev 			return (DDI_FAILURE);
4145084Sjohnlev 		}
4155084Sjohnlev 
4165084Sjohnlev 		if ((r = xen_bind_interdomain(oeid, evtchn, &pdp->xd_evtchn))) {
4175084Sjohnlev 			xvdi_dev_error(dip, r, "bind event channel");
4185084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4195084Sjohnlev 			return (DDI_FAILURE);
4205084Sjohnlev 		}
4215084Sjohnlev 	}
4225741Smrj #ifndef XPV_HVM_DRIVER
4235084Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
4245741Smrj #endif
4255084Sjohnlev 	mutex_exit(&pdp->xd_lk);
4265084Sjohnlev 
4275084Sjohnlev 	return (DDI_SUCCESS);
4285084Sjohnlev }
4295084Sjohnlev 
4305084Sjohnlev /*
4315084Sjohnlev  * Allocate an event channel for this device instance.
4325084Sjohnlev  * Currently we only support one evtchn per device instance.
4335084Sjohnlev  */
4345084Sjohnlev int
4355084Sjohnlev xvdi_alloc_evtchn(dev_info_t *dip)
4365084Sjohnlev {
4375084Sjohnlev 	struct xendev_ppd *pdp;
4385084Sjohnlev 	domid_t oeid;
4395084Sjohnlev 	int rv;
4405084Sjohnlev 
4415084Sjohnlev 	pdp = ddi_get_parent_data(dip);
4425084Sjohnlev 	ASSERT(pdp != NULL);
4435084Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
4445084Sjohnlev 
4455084Sjohnlev 	mutex_enter(&pdp->xd_lk);
4465084Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
4475084Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
4485084Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
4495084Sjohnlev 		} else {
4505084Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
4515084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4525084Sjohnlev 			return (DDI_SUCCESS);
4535084Sjohnlev 		}
4545084Sjohnlev 	} else {
4555084Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
4565084Sjohnlev 		if (oeid == (domid_t)-1) {
4575084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4585084Sjohnlev 			return (DDI_FAILURE);
4595084Sjohnlev 		}
4605084Sjohnlev 
4615084Sjohnlev 		if ((rv = xen_alloc_unbound_evtchn(oeid, &pdp->xd_evtchn))) {
4625084Sjohnlev 			xvdi_dev_error(dip, rv, "bind event channel");
4635084Sjohnlev 			mutex_exit(&pdp->xd_lk);
4645084Sjohnlev 			return (DDI_FAILURE);
4655084Sjohnlev 		}
4665084Sjohnlev 	}
4675741Smrj #ifndef XPV_HVM_DRIVER
4685084Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
4695741Smrj #endif
4705084Sjohnlev 	mutex_exit(&pdp->xd_lk);
4715084Sjohnlev 
4725084Sjohnlev 	return (DDI_SUCCESS);
4735084Sjohnlev }
4745084Sjohnlev 
4755084Sjohnlev /*
4765084Sjohnlev  * Unbind the event channel for this device instance.
4775084Sjohnlev  * Currently we only support one evtchn per device instance.
4785084Sjohnlev  */
4795084Sjohnlev void
4805084Sjohnlev xvdi_free_evtchn(dev_info_t *dip)
4815084Sjohnlev {
4825084Sjohnlev 	struct xendev_ppd *pdp;
4835084Sjohnlev 
4845084Sjohnlev 	pdp = ddi_get_parent_data(dip);
4855084Sjohnlev 	ASSERT(pdp != NULL);
4865084Sjohnlev 
4875084Sjohnlev 	mutex_enter(&pdp->xd_lk);
4885084Sjohnlev 	if (pdp->xd_evtchn != INVALID_EVTCHN) {
4895741Smrj #ifndef XPV_HVM_DRIVER
4905084Sjohnlev 		ec_unbind_irq(pdp->xd_ispec.intrspec_vec);
4915741Smrj 		pdp->xd_ispec.intrspec_vec = 0;
4925741Smrj #endif
4935084Sjohnlev 		pdp->xd_evtchn = INVALID_EVTCHN;
4945084Sjohnlev 	}
4955084Sjohnlev 	mutex_exit(&pdp->xd_lk);
4965084Sjohnlev }
4975084Sjohnlev 
4985741Smrj #ifndef XPV_HVM_DRIVER
4995084Sjohnlev /*
5005084Sjohnlev  * Map an inter-domain communication ring for a virtual device.
5015084Sjohnlev  * This is used by backend drivers.
5025084Sjohnlev  */
5035084Sjohnlev int
5045084Sjohnlev xvdi_map_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
5055084Sjohnlev     grant_ref_t gref, xendev_ring_t **ringpp)
5065084Sjohnlev {
5075084Sjohnlev 	domid_t oeid;
5085084Sjohnlev 	gnttab_map_grant_ref_t mapop;
5095084Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
5105084Sjohnlev 	caddr_t ringva;
5115084Sjohnlev 	ddi_acc_hdl_t *ap;
5125084Sjohnlev 	ddi_acc_impl_t *iap;
5135084Sjohnlev 	xendev_ring_t *ring;
5145084Sjohnlev 	int err;
5155084Sjohnlev 	char errstr[] = "mapping in ring buffer";
5165084Sjohnlev 
5175084Sjohnlev 	ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
5185084Sjohnlev 	oeid = xvdi_get_oeid(dip);
5195084Sjohnlev 
5205084Sjohnlev 	/* alloc va in backend dom for ring buffer */
5215084Sjohnlev 	ringva = vmem_xalloc(heap_arena, PAGESIZE, PAGESIZE,
5225084Sjohnlev 	    0, 0, 0, 0, VM_SLEEP);
5235084Sjohnlev 
5245084Sjohnlev 	/* map in ring page */
5255084Sjohnlev 	hat_prepare_mapping(kas.a_hat, ringva);
5265084Sjohnlev 	mapop.host_addr = (uint64_t)(uintptr_t)ringva;
5275084Sjohnlev 	mapop.flags = GNTMAP_host_map;
5285084Sjohnlev 	mapop.ref = gref;
5295084Sjohnlev 	mapop.dom = oeid;
5305084Sjohnlev 	err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &mapop, 1);
5315084Sjohnlev 	if (err) {
5325084Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
5335084Sjohnlev 		goto errout1;
5345084Sjohnlev 	}
5355084Sjohnlev 
5365084Sjohnlev 	if (mapop.status != 0) {
5375084Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
5385084Sjohnlev 		goto errout2;
5395084Sjohnlev 	}
5405084Sjohnlev 	ring->xr_vaddr = ringva;
5415084Sjohnlev 	ring->xr_grant_hdl = mapop.handle;
5425084Sjohnlev 	ring->xr_gref = gref;
5435084Sjohnlev 
5445084Sjohnlev 	/*
5455084Sjohnlev 	 * init an acc handle and associate it w/ this ring
5465084Sjohnlev 	 * this is only for backend drivers. we get the memory by calling
5475084Sjohnlev 	 * vmem_xalloc(), instead of calling any ddi function, so we have
5485084Sjohnlev 	 * to init an acc handle by ourselves
5495084Sjohnlev 	 */
5505084Sjohnlev 	ring->xr_acc_hdl = impl_acc_hdl_alloc(KM_SLEEP, NULL);
5515084Sjohnlev 	ap = impl_acc_hdl_get(ring->xr_acc_hdl);
5525084Sjohnlev 	ap->ah_vers = VERS_ACCHDL;
5535084Sjohnlev 	ap->ah_dip = dip;
5545084Sjohnlev 	ap->ah_xfermodes = DDI_DMA_CONSISTENT;
5555084Sjohnlev 	ap->ah_acc = xendev_dc_accattr;
5565084Sjohnlev 	iap = (ddi_acc_impl_t *)ap->ah_platform_private;
5575084Sjohnlev 	iap->ahi_acc_attr |= DDI_ACCATTR_CPU_VADDR;
5585084Sjohnlev 	impl_acc_hdl_init(ap);
5595084Sjohnlev 	ap->ah_offset = 0;
5605084Sjohnlev 	ap->ah_len = (off_t)PAGESIZE;
5615084Sjohnlev 	ap->ah_addr = ring->xr_vaddr;
5625084Sjohnlev 
5635084Sjohnlev 	/* init backend ring */
5645084Sjohnlev 	xvdi_ring_init_back_ring(ring, nentry, entrysize);
5655084Sjohnlev 
5665084Sjohnlev 	*ringpp = ring;
5675084Sjohnlev 
5685084Sjohnlev 	return (DDI_SUCCESS);
5695084Sjohnlev 
5705084Sjohnlev errout2:
5715084Sjohnlev 	/* unmap ring page */
5725084Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ringva;
5735084Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
5745084Sjohnlev 	unmapop.dev_bus_addr = NULL;
5755084Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
5765084Sjohnlev 	hat_release_mapping(kas.a_hat, ringva);
5775084Sjohnlev errout1:
5785084Sjohnlev 	vmem_xfree(heap_arena, ringva, PAGESIZE);
5795084Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
5805084Sjohnlev 	return (DDI_FAILURE);
5815084Sjohnlev }
5825084Sjohnlev 
5835084Sjohnlev /*
5845084Sjohnlev  * Unmap a ring for a virtual device.
5855084Sjohnlev  * This is used by backend drivers.
5865084Sjohnlev  */
5875084Sjohnlev void
5885084Sjohnlev xvdi_unmap_ring(xendev_ring_t *ring)
5895084Sjohnlev {
5905084Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
5915084Sjohnlev 
5925084Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
5935084Sjohnlev 
5945084Sjohnlev 	impl_acc_hdl_free(ring->xr_acc_hdl);
5955084Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ring->xr_vaddr;
5965084Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
5975084Sjohnlev 	unmapop.dev_bus_addr = NULL;
5985084Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
5995084Sjohnlev 	hat_release_mapping(kas.a_hat, ring->xr_vaddr);
6005084Sjohnlev 	vmem_xfree(heap_arena, ring->xr_vaddr, PAGESIZE);
6015084Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
6025084Sjohnlev }
6035741Smrj #endif /* XPV_HVM_DRIVER */
6045084Sjohnlev 
6055084Sjohnlev /*
6065084Sjohnlev  * Re-initialise an inter-domain communications ring for the backend domain.
6075084Sjohnlev  * ring will be re-initialized after re-grant succeed
6085084Sjohnlev  * ring will be freed if fails to re-grant access to backend domain
6095084Sjohnlev  * so, don't keep useful data in the ring
6105084Sjohnlev  * used only in frontend driver
6115084Sjohnlev  */
6125084Sjohnlev static void
6135084Sjohnlev xvdi_reinit_ring(dev_info_t *dip, grant_ref_t *gref, xendev_ring_t *ringp)
6145084Sjohnlev {
6155084Sjohnlev 	paddr_t rpaddr;
6165084Sjohnlev 	maddr_t rmaddr;
6175084Sjohnlev 
6185084Sjohnlev 	ASSERT((ringp != NULL) && (ringp->xr_paddr != 0));
6195084Sjohnlev 	rpaddr = ringp->xr_paddr;
6205084Sjohnlev 
6215084Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? rpaddr : pa_to_ma(rpaddr);
6225084Sjohnlev 	gnttab_grant_foreign_access_ref(ringp->xr_gref, xvdi_get_oeid(dip),
6235084Sjohnlev 	    rmaddr >> PAGESHIFT, 0);
6245084Sjohnlev 	*gref = ringp->xr_gref;
6255084Sjohnlev 
6265084Sjohnlev 	/* init frontend ring */
6275084Sjohnlev 	xvdi_ring_init_sring(ringp);
6285084Sjohnlev 	xvdi_ring_init_front_ring(ringp, ringp->xr_sring.fr.nr_ents,
6295084Sjohnlev 	    ringp->xr_entry_size);
6305084Sjohnlev }
6315084Sjohnlev 
6325084Sjohnlev /*
6335084Sjohnlev  * allocate Xen inter-domain communications ring for Xen virtual devices
6345084Sjohnlev  * used only in frontend driver
6355084Sjohnlev  * if *ringpp is not NULL, we'll simply re-init it
6365084Sjohnlev  */
6375084Sjohnlev int
6385084Sjohnlev xvdi_alloc_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
6395084Sjohnlev     grant_ref_t *gref, xendev_ring_t **ringpp)
6405084Sjohnlev {
6415084Sjohnlev 	size_t len;
6425084Sjohnlev 	xendev_ring_t *ring;
6435084Sjohnlev 	ddi_dma_cookie_t dma_cookie;
6445084Sjohnlev 	uint_t ncookies;
6455084Sjohnlev 	grant_ref_t ring_gref;
6465084Sjohnlev 	domid_t oeid;
6475084Sjohnlev 	maddr_t rmaddr;
6485084Sjohnlev 
6495084Sjohnlev 	if (*ringpp) {
6505084Sjohnlev 		xvdi_reinit_ring(dip, gref, *ringpp);
6515084Sjohnlev 		return (DDI_SUCCESS);
6525084Sjohnlev 	}
6535084Sjohnlev 
6545084Sjohnlev 	*ringpp = ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
6555084Sjohnlev 	oeid = xvdi_get_oeid(dip);
6565084Sjohnlev 
6575084Sjohnlev 	/*
6585084Sjohnlev 	 * Allocate page for this ring buffer
6595084Sjohnlev 	 */
6605084Sjohnlev 	if (ddi_dma_alloc_handle(dip, &xendev_dc_dmaattr, DDI_DMA_SLEEP,
6615084Sjohnlev 	    0, &ring->xr_dma_hdl) != DDI_SUCCESS)
6625084Sjohnlev 		goto err;
6635084Sjohnlev 
6645084Sjohnlev 	if (ddi_dma_mem_alloc(ring->xr_dma_hdl, PAGESIZE,
6655084Sjohnlev 	    &xendev_dc_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
6665084Sjohnlev 	    &ring->xr_vaddr, &len, &ring->xr_acc_hdl) != DDI_SUCCESS) {
6675084Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
6685084Sjohnlev 		goto err;
6695084Sjohnlev 	}
6705084Sjohnlev 
6715084Sjohnlev 	if (ddi_dma_addr_bind_handle(ring->xr_dma_hdl, NULL,
6725084Sjohnlev 	    ring->xr_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
6735084Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED) {
6745084Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
6755084Sjohnlev 		ring->xr_vaddr = NULL;
6765084Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
6775084Sjohnlev 		goto err;
6785084Sjohnlev 	}
6795084Sjohnlev 	ASSERT(ncookies == 1);
6805084Sjohnlev 	ring->xr_paddr = dma_cookie.dmac_laddress;
6815084Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? ring->xr_paddr :
6825084Sjohnlev 	    pa_to_ma(ring->xr_paddr);
6835084Sjohnlev 
6845084Sjohnlev 	if ((ring_gref = gnttab_grant_foreign_access(oeid,
6855084Sjohnlev 	    rmaddr >> PAGESHIFT, 0)) == (grant_ref_t)-1) {
6865084Sjohnlev 		(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
6875084Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
6885084Sjohnlev 		ring->xr_vaddr = NULL;
6895084Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
6905084Sjohnlev 		goto err;
6915084Sjohnlev 	}
6925084Sjohnlev 	*gref = ring->xr_gref = ring_gref;
6935084Sjohnlev 
6945084Sjohnlev 	/* init frontend ring */
6955084Sjohnlev 	xvdi_ring_init_sring(ring);
6965084Sjohnlev 	xvdi_ring_init_front_ring(ring, nentry, entrysize);
6975084Sjohnlev 
6985084Sjohnlev 	return (DDI_SUCCESS);
6995084Sjohnlev 
7005084Sjohnlev err:
7015084Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
7025084Sjohnlev 	return (DDI_FAILURE);
7035084Sjohnlev }
7045084Sjohnlev 
7055084Sjohnlev /*
7065084Sjohnlev  * Release ring buffers allocated for Xen devices
7075084Sjohnlev  * used for frontend driver
7085084Sjohnlev  */
7095084Sjohnlev void
7105084Sjohnlev xvdi_free_ring(xendev_ring_t *ring)
7115084Sjohnlev {
7125084Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
7135084Sjohnlev 
7145084Sjohnlev 	(void) gnttab_end_foreign_access_ref(ring->xr_gref, 0);
7155084Sjohnlev 	(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
7165084Sjohnlev 	ddi_dma_mem_free(&ring->xr_acc_hdl);
7175084Sjohnlev 	ddi_dma_free_handle(&ring->xr_dma_hdl);
7185084Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
7195084Sjohnlev }
7205084Sjohnlev 
7215084Sjohnlev dev_info_t *
7225084Sjohnlev xvdi_create_dev(dev_info_t *parent, xendev_devclass_t devclass,
7235084Sjohnlev     domid_t dom, int vdev)
7245084Sjohnlev {
7255084Sjohnlev 	dev_info_t *dip;
7265084Sjohnlev 	boolean_t backend;
7275084Sjohnlev 	i_xd_cfg_t *xdcp;
7285084Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
7295084Sjohnlev 	char *type, *node = NULL, *xsname = NULL;
7305084Sjohnlev 	unsigned int tlen;
7315159Sjohnlev 	int ret;
7325084Sjohnlev 
7335084Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
7345084Sjohnlev 
7355084Sjohnlev 	backend = (dom != DOMID_SELF);
7365084Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
7375084Sjohnlev 	ASSERT(xdcp != NULL);
7385084Sjohnlev 
7396175Sjohnlev 	if (vdev != VDEV_NOXS) {
7405084Sjohnlev 		if (!backend) {
7415084Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
7425084Sjohnlev 			    "%s/%d", xdcp->xs_path_fe, vdev);
7435084Sjohnlev 			xsname = xsnamebuf;
7445084Sjohnlev 			node = xdcp->node_fe;
7455084Sjohnlev 		} else {
7465084Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
7475084Sjohnlev 			    "%s/%d/%d", xdcp->xs_path_be, dom, vdev);
7485084Sjohnlev 			xsname = xsnamebuf;
7495084Sjohnlev 			node = xdcp->node_be;
7505084Sjohnlev 		}
7515084Sjohnlev 	} else {
7525084Sjohnlev 		node = xdcp->node_fe;
7535084Sjohnlev 	}
7545084Sjohnlev 
7555084Sjohnlev 	/* Must have a driver to use. */
7565084Sjohnlev 	if (node == NULL)
7575084Sjohnlev 		return (NULL);
7585084Sjohnlev 
7595084Sjohnlev 	/*
7605084Sjohnlev 	 * We need to check the state of this device before we go
7615084Sjohnlev 	 * further, otherwise we'll end up with a dead loop if
7625084Sjohnlev 	 * anything goes wrong.
7635084Sjohnlev 	 */
7645084Sjohnlev 	if ((xsname != NULL) &&
7655084Sjohnlev 	    (xenbus_read_driver_state(xsname) >= XenbusStateClosing))
7665084Sjohnlev 		return (NULL);
7675084Sjohnlev 
7685084Sjohnlev 	ndi_devi_alloc_sleep(parent, node, DEVI_SID_NODEID, &dip);
7695084Sjohnlev 
7705084Sjohnlev 	/*
7715084Sjohnlev 	 * Driver binding uses the compatible property _before_ the
7725084Sjohnlev 	 * node name, so we set the node name to the 'model' of the
7735084Sjohnlev 	 * device (i.e. 'xnb' or 'xdb') and, if 'type' is present,
7745084Sjohnlev 	 * encode both the model and the type in a compatible property
7755084Sjohnlev 	 * (i.e. 'xnb,netfront' or 'xnb,SUNW_mac').  This allows a
7765084Sjohnlev 	 * driver binding based on the <model,type> pair _before_ a
7775084Sjohnlev 	 * binding based on the node name.
7785084Sjohnlev 	 */
7795084Sjohnlev 	if ((xsname != NULL) &&
7805084Sjohnlev 	    (xenbus_read(XBT_NULL, xsname, "type", (void *)&type, &tlen)
7815084Sjohnlev 	    == 0)) {
7825084Sjohnlev 		size_t clen;
7835084Sjohnlev 		char *c[1];
7845084Sjohnlev 
7855084Sjohnlev 		clen = strlen(node) + strlen(type) + 2;
7865084Sjohnlev 		c[0] = kmem_alloc(clen, KM_SLEEP);
7875084Sjohnlev 		(void) snprintf(c[0], clen, "%s,%s", node, type);
7885084Sjohnlev 
7895084Sjohnlev 		(void) ndi_prop_update_string_array(DDI_DEV_T_NONE,
7905084Sjohnlev 		    dip, "compatible", (char **)c, 1);
7915084Sjohnlev 
7925084Sjohnlev 		kmem_free(c[0], clen);
7935084Sjohnlev 		kmem_free(type, tlen);
7945084Sjohnlev 	}
7955084Sjohnlev 
7965084Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "devclass", devclass);
7975084Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "domain", dom);
7985084Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vdev", vdev);
7995084Sjohnlev 
8005084Sjohnlev 	if (i_ddi_devi_attached(parent))
8015159Sjohnlev 		ret = ndi_devi_online(dip, 0);
8025084Sjohnlev 	else
8035159Sjohnlev 		ret = ndi_devi_bind_driver(dip, 0);
8045159Sjohnlev 	if (ret != NDI_SUCCESS)
8055159Sjohnlev 		(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
8065084Sjohnlev 
8075084Sjohnlev 	return (dip);
8085084Sjohnlev }
8095084Sjohnlev 
8105084Sjohnlev /*
8115084Sjohnlev  * xendev_enum_class()
8125084Sjohnlev  */
8135084Sjohnlev void
8145084Sjohnlev xendev_enum_class(dev_info_t *parent, xendev_devclass_t devclass)
8155084Sjohnlev {
8165910Smrj 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
8175910Smrj 	boolean_t domU = !dom0;
8185084Sjohnlev 	i_xd_cfg_t *xdcp;
8195084Sjohnlev 
8205084Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
8215084Sjohnlev 	ASSERT(xdcp != NULL);
8225084Sjohnlev 
8235910Smrj 	if (dom0 && !(xdcp->flags & XD_DOM_ZERO))
8245910Smrj 		return;
8255910Smrj 
8265910Smrj 	if (domU && !(xdcp->flags & XD_DOM_GUEST))
8275910Smrj 		return;
8285910Smrj 
8295084Sjohnlev 	if (xdcp->xsdev == NULL) {
8305084Sjohnlev 		int circ;
8315084Sjohnlev 
8325084Sjohnlev 		/*
8335084Sjohnlev 		 * Don't need to probe this kind of device from the
8345084Sjohnlev 		 * store, just create one if it doesn't exist.
8355084Sjohnlev 		 */
8365084Sjohnlev 
8375084Sjohnlev 		ndi_devi_enter(parent, &circ);
8386175Sjohnlev 		if (xvdi_find_dev(parent, devclass, DOMID_SELF, VDEV_NOXS)
8395084Sjohnlev 		    == NULL)
8405084Sjohnlev 			(void) xvdi_create_dev(parent, devclass,
8416175Sjohnlev 			    DOMID_SELF, VDEV_NOXS);
8425084Sjohnlev 		ndi_devi_exit(parent, circ);
8435084Sjohnlev 	} else {
8445084Sjohnlev 		/*
8455084Sjohnlev 		 * Probe this kind of device from the store, both
8465084Sjohnlev 		 * frontend and backend.
8475084Sjohnlev 		 */
8485084Sjohnlev 
8495084Sjohnlev 		i_xvdi_enum_fe(parent, xdcp);
8505084Sjohnlev 		i_xvdi_enum_be(parent, xdcp);
8515084Sjohnlev 	}
8525084Sjohnlev }
8535084Sjohnlev 
8545084Sjohnlev /*
8555084Sjohnlev  * xendev_enum_all()
8565084Sjohnlev  */
8575084Sjohnlev void
8585084Sjohnlev xendev_enum_all(dev_info_t *parent, boolean_t store_unavailable)
8595084Sjohnlev {
8605084Sjohnlev 	int i;
8615084Sjohnlev 	i_xd_cfg_t *xdcp;
8625084Sjohnlev 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
8635084Sjohnlev 
8645084Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
8655084Sjohnlev 		/*
8665084Sjohnlev 		 * Dom0 relies on watchpoints to create non-soft
8675084Sjohnlev 		 * devices - don't attempt to iterate over the store.
8685084Sjohnlev 		 */
8695084Sjohnlev 		if (dom0 && (xdcp->xsdev != NULL))
8705084Sjohnlev 			continue;
8715084Sjohnlev 
8725084Sjohnlev 		/*
8735084Sjohnlev 		 * If the store is not yet available, don't attempt to
8745084Sjohnlev 		 * iterate.
8755084Sjohnlev 		 */
8765084Sjohnlev 		if (store_unavailable && (xdcp->xsdev != NULL))
8775084Sjohnlev 			continue;
8785084Sjohnlev 
8795084Sjohnlev 		xendev_enum_class(parent, xdcp->devclass);
8805084Sjohnlev 	}
8815084Sjohnlev }
8825084Sjohnlev 
8835084Sjohnlev xendev_devclass_t
8845084Sjohnlev xendev_nodename_to_devclass(char *nodename)
8855084Sjohnlev {
8865084Sjohnlev 	int i;
8875084Sjohnlev 	i_xd_cfg_t *xdcp;
8885084Sjohnlev 
8895084Sjohnlev 	/*
8905084Sjohnlev 	 * This relies on the convention that variants of a base
8915084Sjohnlev 	 * driver share the same prefix and that there are no drivers
8925084Sjohnlev 	 * which share a common prefix with the name of any other base
8935084Sjohnlev 	 * drivers.
8945084Sjohnlev 	 *
8955084Sjohnlev 	 * So for a base driver 'xnb' (which is the name listed in
8965084Sjohnlev 	 * xdci) the variants all begin with the string 'xnb' (in fact
8975084Sjohnlev 	 * they are 'xnbe', 'xnbo' and 'xnbu') and there are no other
8985084Sjohnlev 	 * base drivers which have the prefix 'xnb'.
8995084Sjohnlev 	 */
9005084Sjohnlev 	ASSERT(nodename != NULL);
9015084Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
9025084Sjohnlev 		if (((xdcp->node_fe != NULL) &&
9035084Sjohnlev 		    (strncmp(nodename, xdcp->node_fe,
9045084Sjohnlev 		    strlen(xdcp->node_fe)) == 0)) ||
9055084Sjohnlev 		    ((xdcp->node_be != NULL) &&
9065084Sjohnlev 		    (strncmp(nodename, xdcp->node_be,
9075084Sjohnlev 		    strlen(xdcp->node_be)) == 0)))
9085084Sjohnlev 
9095084Sjohnlev 			return (xdcp->devclass);
9105084Sjohnlev 	}
9115084Sjohnlev 	return (XEN_INVAL);
9125084Sjohnlev }
9135084Sjohnlev 
9145084Sjohnlev int
9155084Sjohnlev xendev_devclass_ipl(xendev_devclass_t devclass)
9165084Sjohnlev {
9175084Sjohnlev 	i_xd_cfg_t *xdcp;
9185084Sjohnlev 
9195084Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
9205084Sjohnlev 	ASSERT(xdcp != NULL);
9215084Sjohnlev 
9225084Sjohnlev 	return (xdcp->xd_ipl);
9235084Sjohnlev }
9245084Sjohnlev 
9255084Sjohnlev /*
9265084Sjohnlev  * Determine if a devinfo instance exists of a particular device
9275084Sjohnlev  * class, domain and xenstore virtual device number.
9285084Sjohnlev  */
9295084Sjohnlev dev_info_t *
9305084Sjohnlev xvdi_find_dev(dev_info_t *parent, xendev_devclass_t devclass,
9315084Sjohnlev     domid_t dom, int vdev)
9325084Sjohnlev {
9335084Sjohnlev 	dev_info_t *dip;
9345084Sjohnlev 
9355084Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
9365084Sjohnlev 
9375084Sjohnlev 	switch (devclass) {
9385084Sjohnlev 	case XEN_CONSOLE:
9395084Sjohnlev 	case XEN_XENBUS:
9405084Sjohnlev 	case XEN_DOMCAPS:
9415084Sjohnlev 	case XEN_BALLOON:
9425084Sjohnlev 	case XEN_EVTCHN:
9435084Sjohnlev 	case XEN_PRIVCMD:
9445084Sjohnlev 		/* Console and soft devices have no vdev. */
9456175Sjohnlev 		vdev = VDEV_NOXS;
9465084Sjohnlev 		break;
9475084Sjohnlev 	default:
9485084Sjohnlev 		break;
9495084Sjohnlev 	}
9505084Sjohnlev 
9515084Sjohnlev 	for (dip = ddi_get_child(parent); dip != NULL;
9525084Sjohnlev 	    dip = ddi_get_next_sibling(dip)) {
9535084Sjohnlev 		int *vdevnump, *domidp, *devclsp, vdevnum;
9545084Sjohnlev 		uint_t ndomid, nvdevnum, ndevcls;
9555084Sjohnlev 		xendev_devclass_t devcls;
9565084Sjohnlev 		domid_t domid;
9575084Sjohnlev 		struct xendev_ppd *pdp = ddi_get_parent_data(dip);
9585084Sjohnlev 
9595084Sjohnlev 		if (pdp == NULL) {
9605084Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9615084Sjohnlev 			    DDI_PROP_DONTPASS, "domain", &domidp, &ndomid) !=
9625084Sjohnlev 			    DDI_PROP_SUCCESS)
9635084Sjohnlev 				continue;
9645084Sjohnlev 			ASSERT(ndomid == 1);
9655084Sjohnlev 			domid = (domid_t)*domidp;
9665084Sjohnlev 			ddi_prop_free(domidp);
9675084Sjohnlev 
9685084Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9695084Sjohnlev 			    DDI_PROP_DONTPASS, "vdev", &vdevnump, &nvdevnum) !=
9705084Sjohnlev 			    DDI_PROP_SUCCESS)
9715084Sjohnlev 				continue;
9725084Sjohnlev 			ASSERT(nvdevnum == 1);
9735084Sjohnlev 			vdevnum = *vdevnump;
9745084Sjohnlev 			ddi_prop_free(vdevnump);
9755084Sjohnlev 
9765084Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
9775084Sjohnlev 			    DDI_PROP_DONTPASS, "devclass", &devclsp,
9785084Sjohnlev 			    &ndevcls) != DDI_PROP_SUCCESS)
9795084Sjohnlev 				continue;
9805084Sjohnlev 			ASSERT(ndevcls == 1);
9815084Sjohnlev 			devcls = (xendev_devclass_t)*devclsp;
9825084Sjohnlev 			ddi_prop_free(devclsp);
9835084Sjohnlev 		} else {
9845084Sjohnlev 			domid = pdp->xd_domain;
9855084Sjohnlev 			vdevnum = pdp->xd_vdevnum;
9865084Sjohnlev 			devcls = pdp->xd_devclass;
9875084Sjohnlev 		}
9885084Sjohnlev 
9895084Sjohnlev 		if ((domid == dom) && (vdevnum == vdev) && (devcls == devclass))
9905084Sjohnlev 			return (dip);
9915084Sjohnlev 	}
9925084Sjohnlev 	return (NULL);
9935084Sjohnlev }
9945084Sjohnlev 
9955084Sjohnlev int
9965084Sjohnlev xvdi_get_evtchn(dev_info_t *xdip)
9975084Sjohnlev {
9985084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
9995084Sjohnlev 
10005084Sjohnlev 	ASSERT(pdp != NULL);
10015084Sjohnlev 	return (pdp->xd_evtchn);
10025084Sjohnlev }
10035084Sjohnlev 
10045084Sjohnlev int
10055084Sjohnlev xvdi_get_vdevnum(dev_info_t *xdip)
10065084Sjohnlev {
10075084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10085084Sjohnlev 
10095084Sjohnlev 	ASSERT(pdp != NULL);
10105084Sjohnlev 	return (pdp->xd_vdevnum);
10115084Sjohnlev }
10125084Sjohnlev 
10135084Sjohnlev char *
10145084Sjohnlev xvdi_get_xsname(dev_info_t *xdip)
10155084Sjohnlev {
10165084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10175084Sjohnlev 
10185084Sjohnlev 	ASSERT(pdp != NULL);
10195084Sjohnlev 	return ((char *)(pdp->xd_xsdev.nodename));
10205084Sjohnlev }
10215084Sjohnlev 
10225084Sjohnlev char *
10235084Sjohnlev xvdi_get_oename(dev_info_t *xdip)
10245084Sjohnlev {
10255084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10265084Sjohnlev 
10275084Sjohnlev 	ASSERT(pdp != NULL);
10285084Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
10295084Sjohnlev 		return (NULL);
10305084Sjohnlev 	return ((char *)(pdp->xd_xsdev.otherend));
10315084Sjohnlev }
10325084Sjohnlev 
10335084Sjohnlev struct xenbus_device *
10345084Sjohnlev xvdi_get_xsd(dev_info_t *xdip)
10355084Sjohnlev {
10365084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10375084Sjohnlev 
10385084Sjohnlev 	ASSERT(pdp != NULL);
10395084Sjohnlev 	return (&pdp->xd_xsdev);
10405084Sjohnlev }
10415084Sjohnlev 
10425084Sjohnlev domid_t
10435084Sjohnlev xvdi_get_oeid(dev_info_t *xdip)
10445084Sjohnlev {
10455084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
10465084Sjohnlev 
10475084Sjohnlev 	ASSERT(pdp != NULL);
10485084Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
10495084Sjohnlev 		return ((domid_t)-1);
10505084Sjohnlev 	return ((domid_t)(pdp->xd_xsdev.otherend_id));
10515084Sjohnlev }
10525084Sjohnlev 
10535084Sjohnlev void
10545084Sjohnlev xvdi_dev_error(dev_info_t *dip, int errno, char *errstr)
10555084Sjohnlev {
10565084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10575084Sjohnlev 
10585084Sjohnlev 	ASSERT(pdp != NULL);
10595084Sjohnlev 	xenbus_dev_error(&pdp->xd_xsdev, errno, errstr);
10605084Sjohnlev }
10615084Sjohnlev 
10625084Sjohnlev void
10635084Sjohnlev xvdi_fatal_error(dev_info_t *dip, int errno, char *errstr)
10645084Sjohnlev {
10655084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10665084Sjohnlev 
10675084Sjohnlev 	ASSERT(pdp != NULL);
10685084Sjohnlev 	xenbus_dev_fatal(&pdp->xd_xsdev, errno, errstr);
10695084Sjohnlev }
10705084Sjohnlev 
10715084Sjohnlev static void
10725084Sjohnlev i_xvdi_oestate_handler(void *arg)
10735084Sjohnlev {
1074*7679SMax.Zhen@Sun.COM 	i_oestate_evt_t *evt = (i_oestate_evt_t *)arg;
1075*7679SMax.Zhen@Sun.COM 	dev_info_t *dip = evt->dip;
10765084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
10775084Sjohnlev 	XenbusState oestate = pdp->xd_xsdev.otherend_state;
1078*7679SMax.Zhen@Sun.COM 	XenbusState curr_oestate = evt->state;
10795084Sjohnlev 	ddi_eventcookie_t evc;
10805084Sjohnlev 
1081*7679SMax.Zhen@Sun.COM 	/* evt is alloc'ed in i_xvdi_oestate_cb */
1082*7679SMax.Zhen@Sun.COM 	kmem_free(evt, sizeof (i_oestate_evt_t));
1083*7679SMax.Zhen@Sun.COM 
1084*7679SMax.Zhen@Sun.COM 	/*
1085*7679SMax.Zhen@Sun.COM 	 * If the oestate we're handling is not the latest one,
1086*7679SMax.Zhen@Sun.COM 	 * it does not make any sense to continue handling it.
1087*7679SMax.Zhen@Sun.COM 	 */
1088*7679SMax.Zhen@Sun.COM 	if (curr_oestate != oestate)
1089*7679SMax.Zhen@Sun.COM 		return;
1090*7679SMax.Zhen@Sun.COM 
10915084Sjohnlev 	mutex_enter(&pdp->xd_lk);
10925084Sjohnlev 
10935084Sjohnlev 	if (pdp->xd_oe_ehid != NULL) {
10945084Sjohnlev 		/* send notification to driver */
10955084Sjohnlev 		if (ddi_get_eventcookie(dip, XS_OE_STATE,
10965084Sjohnlev 		    &evc) == DDI_SUCCESS) {
10975084Sjohnlev 			mutex_exit(&pdp->xd_lk);
10985084Sjohnlev 			(void) ndi_post_event(dip, dip, evc, &oestate);
10995084Sjohnlev 			mutex_enter(&pdp->xd_lk);
11005084Sjohnlev 		}
11015084Sjohnlev 	} else {
11025084Sjohnlev 		/*
11035084Sjohnlev 		 * take default action, if driver hasn't registered its
11045084Sjohnlev 		 * event handler yet
11055084Sjohnlev 		 */
11065084Sjohnlev 		if (oestate == XenbusStateClosing) {
11075084Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
11085084Sjohnlev 			    XenbusStateClosed);
11095084Sjohnlev 		} else if (oestate == XenbusStateClosed) {
11105084Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
11115084Sjohnlev 			    XenbusStateClosed);
11125084Sjohnlev 			(void) xvdi_post_event(dip, XEN_HP_REMOVE);
11135084Sjohnlev 		}
11145084Sjohnlev 	}
11155084Sjohnlev 
11165084Sjohnlev 	mutex_exit(&pdp->xd_lk);
11175084Sjohnlev 
11185084Sjohnlev 	/*
11195084Sjohnlev 	 * We'll try to remove the devinfo node of this device if the
11205084Sjohnlev 	 * other end has closed.
11215084Sjohnlev 	 */
11225084Sjohnlev 	if (oestate == XenbusStateClosed)
11235084Sjohnlev 		(void) ddi_taskq_dispatch(DEVI(ddi_get_parent(dip))->devi_taskq,
11245084Sjohnlev 		    xendev_offline_device, dip, DDI_SLEEP);
11255084Sjohnlev }
11265084Sjohnlev 
11275084Sjohnlev static void
11285084Sjohnlev i_xvdi_hpstate_handler(void *arg)
11295084Sjohnlev {
11305084Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
11315084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
11325084Sjohnlev 	ddi_eventcookie_t evc;
11335084Sjohnlev 	char *hp_status;
11345084Sjohnlev 	unsigned int hpl;
11355084Sjohnlev 
11365084Sjohnlev 	mutex_enter(&pdp->xd_lk);
11375084Sjohnlev 	if ((ddi_get_eventcookie(dip, XS_HP_STATE, &evc) == DDI_SUCCESS) &&
11385084Sjohnlev 	    (xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
11395084Sjohnlev 	    (void *)&hp_status, &hpl) == 0)) {
11405084Sjohnlev 
11415084Sjohnlev 		xendev_hotplug_state_t new_state = Unrecognized;
11425084Sjohnlev 
11435084Sjohnlev 		if (strcmp(hp_status, "connected") == 0)
11445084Sjohnlev 			new_state = Connected;
11455084Sjohnlev 
11465084Sjohnlev 		mutex_exit(&pdp->xd_lk);
11475084Sjohnlev 
11485084Sjohnlev 		(void) ndi_post_event(dip, dip, evc, &new_state);
11495084Sjohnlev 		kmem_free(hp_status, hpl);
11505084Sjohnlev 		return;
11515084Sjohnlev 	}
11525084Sjohnlev 	mutex_exit(&pdp->xd_lk);
11535084Sjohnlev }
11545084Sjohnlev 
11555084Sjohnlev void
11565084Sjohnlev xvdi_notify_oe(dev_info_t *dip)
11575084Sjohnlev {
11585084Sjohnlev 	struct xendev_ppd *pdp;
11595084Sjohnlev 
11605084Sjohnlev 	pdp = ddi_get_parent_data(dip);
11615084Sjohnlev 	ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
11625084Sjohnlev 	ec_notify_via_evtchn(pdp->xd_evtchn);
11635084Sjohnlev }
11645084Sjohnlev 
11655084Sjohnlev static void
11665084Sjohnlev i_xvdi_bepath_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
11675084Sjohnlev {
11685084Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
11695084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
11705084Sjohnlev 	char *be = NULL;
11715084Sjohnlev 	unsigned int bel;
11725084Sjohnlev 
11735084Sjohnlev 	ASSERT(len > XS_WATCH_PATH);
11745084Sjohnlev 	ASSERT(vec[XS_WATCH_PATH] != NULL);
11755084Sjohnlev 
11765084Sjohnlev 	/*
11775084Sjohnlev 	 * If the backend is not the same as that we already stored,
11785084Sjohnlev 	 * re-set our watch for its' state.
11795084Sjohnlev 	 */
11805084Sjohnlev 	if ((xenbus_read(XBT_NULL, "", vec[XS_WATCH_PATH], (void *)be, &bel)
11815084Sjohnlev 	    == 0) && (strcmp(be, pdp->xd_xsdev.otherend) != 0))
11825084Sjohnlev 		(void) i_xvdi_add_watch_oestate(dip);
11835084Sjohnlev 
11845084Sjohnlev 	if (be != NULL) {
11855084Sjohnlev 		ASSERT(bel > 0);
11865084Sjohnlev 		kmem_free(be, bel);
11875084Sjohnlev 	}
11885084Sjohnlev }
11895084Sjohnlev 
11905084Sjohnlev static int
11915084Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip)
11925084Sjohnlev {
11935084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
11945084Sjohnlev 
11955084Sjohnlev 	ASSERT(pdp != NULL);
11965084Sjohnlev 	ASSERT(pdp->xd_xsdev.nodename != NULL);
11975084Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
11985084Sjohnlev 
11995084Sjohnlev 	/*
12005084Sjohnlev 	 * Create taskq for delivering other end state change event to
12015084Sjohnlev 	 * this device later.
12025084Sjohnlev 	 *
12035084Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
12045084Sjohnlev 	 * in order.
12055084Sjohnlev 	 *
12065084Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
12075084Sjohnlev 	 * xenstore change under the path that it is watching. If two
12085084Sjohnlev 	 * changes happen consecutively in a very short amount of
12095084Sjohnlev 	 * time, it is likely that the driver will see only the last
12105084Sjohnlev 	 * one.
12115084Sjohnlev 	 */
12125084Sjohnlev 	if (pdp->xd_oe_taskq == NULL)
12135084Sjohnlev 		if ((pdp->xd_oe_taskq = ddi_taskq_create(dip,
12145084Sjohnlev 		    "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
12155084Sjohnlev 			return (DDI_FAILURE);
12165084Sjohnlev 
12175084Sjohnlev 	/*
12185084Sjohnlev 	 * Watch for changes to the XenbusState of otherend.
12195084Sjohnlev 	 */
12205084Sjohnlev 	pdp->xd_xsdev.otherend_state = XenbusStateUnknown;
12215084Sjohnlev 	pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb;
12225084Sjohnlev 
12235084Sjohnlev 	if (talk_to_otherend(&pdp->xd_xsdev) != 0) {
12245084Sjohnlev 		i_xvdi_rem_watch_oestate(dip);
12255084Sjohnlev 		return (DDI_FAILURE);
12265084Sjohnlev 	}
12275084Sjohnlev 
12285084Sjohnlev 	return (DDI_SUCCESS);
12295084Sjohnlev }
12305084Sjohnlev 
12315084Sjohnlev static void
12325084Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip)
12335084Sjohnlev {
12345084Sjohnlev 	struct xendev_ppd *pdp;
12355084Sjohnlev 	struct xenbus_device *dev;
12365084Sjohnlev 
12375084Sjohnlev 	pdp = ddi_get_parent_data(dip);
12385084Sjohnlev 	ASSERT(pdp != NULL);
12395084Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
12405084Sjohnlev 
12415084Sjohnlev 	dev = &pdp->xd_xsdev;
12425084Sjohnlev 
12435084Sjohnlev 	/* Unwatch for changes to XenbusState of otherend */
12445084Sjohnlev 	if (dev->otherend_watch.node != NULL) {
12455084Sjohnlev 		mutex_exit(&pdp->xd_lk);
12465084Sjohnlev 		unregister_xenbus_watch(&dev->otherend_watch);
12475084Sjohnlev 		mutex_enter(&pdp->xd_lk);
12485084Sjohnlev 	}
12495084Sjohnlev 
12505084Sjohnlev 	/* make sure no event handler is running */
12515084Sjohnlev 	if (pdp->xd_oe_taskq != NULL) {
12525084Sjohnlev 		mutex_exit(&pdp->xd_lk);
12535084Sjohnlev 		ddi_taskq_destroy(pdp->xd_oe_taskq);
12545084Sjohnlev 		mutex_enter(&pdp->xd_lk);
12555084Sjohnlev 		pdp->xd_oe_taskq = NULL;
12565084Sjohnlev 	}
12575084Sjohnlev 
12585084Sjohnlev 	/* clean up */
12595084Sjohnlev 	dev->otherend_state = XenbusStateUnknown;
12605084Sjohnlev 	dev->otherend_id = (domid_t)-1;
12615084Sjohnlev 	if (dev->otherend_watch.node != NULL)
12625084Sjohnlev 		kmem_free((void *)dev->otherend_watch.node,
12635084Sjohnlev 		    strlen(dev->otherend_watch.node) + 1);
12645084Sjohnlev 	dev->otherend_watch.node = NULL;
12655084Sjohnlev 	if (dev->otherend != NULL)
12665084Sjohnlev 		kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1);
12675084Sjohnlev 	dev->otherend = NULL;
12685084Sjohnlev }
12695084Sjohnlev 
12705084Sjohnlev static int
12715084Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip)
12725084Sjohnlev {
12735084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
12745084Sjohnlev 
12755084Sjohnlev 	ASSERT(pdp != NULL);
12765084Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
12775084Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
12785084Sjohnlev 
12795084Sjohnlev 	/*
12805084Sjohnlev 	 * Create taskq for delivering hotplug status change event to
12815084Sjohnlev 	 * this device later.
12825084Sjohnlev 	 *
12835084Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
12845084Sjohnlev 	 * in order.
12855084Sjohnlev 	 *
12865084Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
12875084Sjohnlev 	 * hotplug status change under the path that it is
12885084Sjohnlev 	 * watching. If two changes happen consecutively in a very
12895084Sjohnlev 	 * short amount of time, it is likely that the driver only
12905084Sjohnlev 	 * sees the last one.
12915084Sjohnlev 	 */
12925084Sjohnlev 	if (pdp->xd_hp_taskq == NULL)
12935084Sjohnlev 		if ((pdp->xd_hp_taskq = ddi_taskq_create(dip,
12945084Sjohnlev 		    "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
12955084Sjohnlev 			return (DDI_FAILURE);
12965084Sjohnlev 
12975084Sjohnlev 	if (pdp->xd_hp_watch.node == NULL) {
12985084Sjohnlev 		size_t len;
12995084Sjohnlev 		char *path;
13005084Sjohnlev 
13015084Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
13025084Sjohnlev 
13035084Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) +
13045084Sjohnlev 		    strlen("/hotplug-status") + 1;
13055084Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
13065084Sjohnlev 		(void) snprintf(path, len, "%s/hotplug-status",
13075084Sjohnlev 		    pdp->xd_xsdev.nodename);
13085084Sjohnlev 
13095084Sjohnlev 		pdp->xd_hp_watch.node = path;
13105084Sjohnlev 		pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb;
13115084Sjohnlev 		pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */
13125084Sjohnlev 		if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) {
13135084Sjohnlev 			i_xvdi_rem_watch_hpstate(dip);
13145084Sjohnlev 			return (DDI_FAILURE);
13155084Sjohnlev 		}
13165084Sjohnlev 	}
13175084Sjohnlev 
13185084Sjohnlev 	return (DDI_SUCCESS);
13195084Sjohnlev }
13205084Sjohnlev 
13215084Sjohnlev static void
13225084Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip)
13235084Sjohnlev {
13245084Sjohnlev 	struct xendev_ppd *pdp;
13255084Sjohnlev 	pdp = ddi_get_parent_data(dip);
13265084Sjohnlev 
13275084Sjohnlev 	ASSERT(pdp != NULL);
13285084Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
13295084Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
13305084Sjohnlev 
13315084Sjohnlev 	/* Unwatch for changes to "hotplug-status" node for backend device. */
13325084Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
13335084Sjohnlev 		mutex_exit(&pdp->xd_lk);
13345084Sjohnlev 		unregister_xenbus_watch(&pdp->xd_hp_watch);
13355084Sjohnlev 		mutex_enter(&pdp->xd_lk);
13365084Sjohnlev 	}
13375084Sjohnlev 
13385084Sjohnlev 	/* Make sure no event handler is running. */
13395084Sjohnlev 	if (pdp->xd_hp_taskq != NULL) {
13405084Sjohnlev 		mutex_exit(&pdp->xd_lk);
13415084Sjohnlev 		ddi_taskq_destroy(pdp->xd_hp_taskq);
13425084Sjohnlev 		mutex_enter(&pdp->xd_lk);
13435084Sjohnlev 		pdp->xd_hp_taskq = NULL;
13445084Sjohnlev 	}
13455084Sjohnlev 
13465084Sjohnlev 	/* Clean up. */
13475084Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
13485084Sjohnlev 		kmem_free((void *)pdp->xd_hp_watch.node,
13495084Sjohnlev 		    strlen(pdp->xd_hp_watch.node) + 1);
13505084Sjohnlev 		pdp->xd_hp_watch.node = NULL;
13515084Sjohnlev 	}
13525084Sjohnlev }
13535084Sjohnlev 
13545084Sjohnlev static int
13555084Sjohnlev i_xvdi_add_watches(dev_info_t *dip)
13565084Sjohnlev {
13575084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
13585084Sjohnlev 
13595084Sjohnlev 	ASSERT(pdp != NULL);
13605084Sjohnlev 
13615084Sjohnlev 	mutex_enter(&pdp->xd_lk);
13625084Sjohnlev 
13635084Sjohnlev 	if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) {
13645084Sjohnlev 		mutex_exit(&pdp->xd_lk);
13655084Sjohnlev 		return (DDI_FAILURE);
13665084Sjohnlev 	}
13675084Sjohnlev 
13685084Sjohnlev 	if (pdp->xd_xsdev.frontend == 1) {
13695084Sjohnlev 		/*
13705084Sjohnlev 		 * Frontend devices must watch for the backend path
13715084Sjohnlev 		 * changing.
13725084Sjohnlev 		 */
13735084Sjohnlev 		if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS)
13745084Sjohnlev 			goto unwatch_and_fail;
13755084Sjohnlev 	} else {
13765084Sjohnlev 		/*
13775084Sjohnlev 		 * Backend devices must watch for hotplug events.
13785084Sjohnlev 		 */
13795084Sjohnlev 		if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS)
13805084Sjohnlev 			goto unwatch_and_fail;
13815084Sjohnlev 	}
13825084Sjohnlev 
13835084Sjohnlev 	mutex_exit(&pdp->xd_lk);
13845084Sjohnlev 
13855084Sjohnlev 	return (DDI_SUCCESS);
13865084Sjohnlev 
13875084Sjohnlev unwatch_and_fail:
13885084Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
13895084Sjohnlev 	mutex_exit(&pdp->xd_lk);
13905084Sjohnlev 
13915084Sjohnlev 	return (DDI_FAILURE);
13925084Sjohnlev }
13935084Sjohnlev 
13945084Sjohnlev static void
13955084Sjohnlev i_xvdi_rem_watches(dev_info_t *dip)
13965084Sjohnlev {
13975084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
13985084Sjohnlev 
13995084Sjohnlev 	ASSERT(pdp != NULL);
14005084Sjohnlev 
14015084Sjohnlev 	mutex_enter(&pdp->xd_lk);
14025084Sjohnlev 
14035084Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
14045084Sjohnlev 
14055084Sjohnlev 	if (pdp->xd_xsdev.frontend == 1)
14065084Sjohnlev 		i_xvdi_rem_watch_bepath(dip);
14075084Sjohnlev 	else
14085084Sjohnlev 		i_xvdi_rem_watch_hpstate(dip);
14095084Sjohnlev 
14105084Sjohnlev 	mutex_exit(&pdp->xd_lk);
14115084Sjohnlev }
14125084Sjohnlev 
14135084Sjohnlev static int
14145084Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip)
14155084Sjohnlev {
14165084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
14175084Sjohnlev 
14185084Sjohnlev 	ASSERT(pdp != NULL);
14195084Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
14205084Sjohnlev 
14215084Sjohnlev 	/*
14225084Sjohnlev 	 * Frontend devices need to watch for the backend path changing.
14235084Sjohnlev 	 */
14245084Sjohnlev 	if (pdp->xd_bepath_watch.node == NULL) {
14255084Sjohnlev 		size_t len;
14265084Sjohnlev 		char *path;
14275084Sjohnlev 
14285084Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
14295084Sjohnlev 
14305084Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1;
14315084Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
14325084Sjohnlev 		(void) snprintf(path, len, "%s/backend",
14335084Sjohnlev 		    pdp->xd_xsdev.nodename);
14345084Sjohnlev 
14355084Sjohnlev 		pdp->xd_bepath_watch.node = path;
14365084Sjohnlev 		pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb;
14375084Sjohnlev 		pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip;
14385084Sjohnlev 		if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) {
14395084Sjohnlev 			kmem_free(path, len);
14405084Sjohnlev 			pdp->xd_bepath_watch.node = NULL;
14415084Sjohnlev 			return (DDI_FAILURE);
14425084Sjohnlev 		}
14435084Sjohnlev 	}
14445084Sjohnlev 
14455084Sjohnlev 	return (DDI_SUCCESS);
14465084Sjohnlev }
14475084Sjohnlev 
14485084Sjohnlev static void
14495084Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip)
14505084Sjohnlev {
14515084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
14525084Sjohnlev 
14535084Sjohnlev 	ASSERT(pdp != NULL);
14545084Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
14555084Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
14565084Sjohnlev 
14575084Sjohnlev 	if (pdp->xd_bepath_watch.node != NULL) {
14585084Sjohnlev 		mutex_exit(&pdp->xd_lk);
14595084Sjohnlev 		unregister_xenbus_watch(&pdp->xd_bepath_watch);
14605084Sjohnlev 		mutex_enter(&pdp->xd_lk);
14615084Sjohnlev 
14625084Sjohnlev 		kmem_free((void *)(pdp->xd_bepath_watch.node),
14635084Sjohnlev 		    strlen(pdp->xd_bepath_watch.node) + 1);
14645084Sjohnlev 		pdp->xd_bepath_watch.node = NULL;
14655084Sjohnlev 	}
14665084Sjohnlev }
14675084Sjohnlev 
14685084Sjohnlev int
14695084Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt,
14705084Sjohnlev     XenbusState newState)
14715084Sjohnlev {
14725084Sjohnlev 	int rv;
14735084Sjohnlev 	struct xendev_ppd *pdp;
14745084Sjohnlev 
14755084Sjohnlev 	pdp = ddi_get_parent_data(dip);
14765084Sjohnlev 	ASSERT(pdp != NULL);
14775084Sjohnlev 
14785084Sjohnlev 	XVDI_DPRINTF(XVDI_DBG_STATE,
1479*7679SMax.Zhen@Sun.COM 	    "xvdi_switch_state: %s@%s's xenbus state moves to %d\n",
1480*7679SMax.Zhen@Sun.COM 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
1481*7679SMax.Zhen@Sun.COM 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
1482*7679SMax.Zhen@Sun.COM 	    newState);
14835084Sjohnlev 
14845084Sjohnlev 	rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState);
14855084Sjohnlev 	if (rv > 0)
14865084Sjohnlev 		cmn_err(CE_WARN, "xvdi_switch_state: change state failed");
14875084Sjohnlev 
14885084Sjohnlev 	return (rv);
14895084Sjohnlev }
14905084Sjohnlev 
14915084Sjohnlev /*
14925084Sjohnlev  * Notify hotplug script running in userland
14935084Sjohnlev  */
14945084Sjohnlev int
14955084Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc)
14965084Sjohnlev {
14975084Sjohnlev 	struct xendev_ppd *pdp;
14985084Sjohnlev 	nvlist_t *attr_list = NULL;
14995084Sjohnlev 	i_xd_cfg_t *xdcp;
15005084Sjohnlev 	sysevent_id_t eid;
15015084Sjohnlev 	int err;
15025084Sjohnlev 	char devname[256]; /* XXPV dme: ? */
15035084Sjohnlev 
15045084Sjohnlev 	pdp = ddi_get_parent_data(dip);
15055084Sjohnlev 	ASSERT(pdp != NULL);
15065084Sjohnlev 
15075084Sjohnlev 	xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass);
15085084Sjohnlev 	ASSERT(xdcp != NULL);
15095084Sjohnlev 
15105084Sjohnlev 	(void) snprintf(devname, sizeof (devname) - 1, "%s%d",
15115084Sjohnlev 	    ddi_driver_name(dip),  ddi_get_instance(dip));
15125084Sjohnlev 
15135084Sjohnlev 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP);
15145084Sjohnlev 	if (err != DDI_SUCCESS)
15155084Sjohnlev 		goto failure;
15165084Sjohnlev 
15175084Sjohnlev 	err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain);
15185084Sjohnlev 	if (err != DDI_SUCCESS)
15195084Sjohnlev 		goto failure;
15205084Sjohnlev 	err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum);
15215084Sjohnlev 	if (err != DDI_SUCCESS)
15225084Sjohnlev 		goto failure;
15235084Sjohnlev 	err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev);
15245084Sjohnlev 	if (err != DDI_SUCCESS)
15255084Sjohnlev 		goto failure;
15265084Sjohnlev 	err = nvlist_add_string(attr_list, "device", devname);
15275084Sjohnlev 	if (err != DDI_SUCCESS)
15285084Sjohnlev 		goto failure;
15295084Sjohnlev 	err = nvlist_add_string(attr_list, "fob",
15305084Sjohnlev 	    ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend"));
15315084Sjohnlev 	if (err != DDI_SUCCESS)
15325084Sjohnlev 		goto failure;
15335084Sjohnlev 
15345084Sjohnlev 	switch (hpc) {
15355084Sjohnlev 	case XEN_HP_ADD:
15365084Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
15375084Sjohnlev 		    "add", attr_list, &eid, DDI_NOSLEEP);
15385084Sjohnlev 		break;
15395084Sjohnlev 	case XEN_HP_REMOVE:
15405084Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
15415084Sjohnlev 		    "remove", attr_list, &eid, DDI_NOSLEEP);
15425084Sjohnlev 		break;
15435084Sjohnlev 	default:
15445084Sjohnlev 		err = DDI_FAILURE;
15455084Sjohnlev 		goto failure;
15465084Sjohnlev 	}
15475084Sjohnlev 
15485084Sjohnlev failure:
15495084Sjohnlev 	if (attr_list != NULL)
15505084Sjohnlev 		nvlist_free(attr_list);
15515084Sjohnlev 
15525084Sjohnlev 	return (err);
15535084Sjohnlev }
15545084Sjohnlev 
15555084Sjohnlev /* ARGSUSED */
15565084Sjohnlev static void
15575084Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec,
15585084Sjohnlev     unsigned int len)
15595084Sjohnlev {
15605084Sjohnlev 	char *path;
15615084Sjohnlev 
15625084Sjohnlev 	if (xendev_dip == NULL)
15635084Sjohnlev 		xendev_dip = ddi_find_devinfo("xpvd", -1, 0);
15645084Sjohnlev 
15655084Sjohnlev 	path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP);
15665084Sjohnlev 
15675084Sjohnlev 	(void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq,
15685084Sjohnlev 	    i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP);
15695084Sjohnlev }
15705084Sjohnlev 
15715084Sjohnlev static void
15725084Sjohnlev i_xvdi_watch_device(char *path)
15735084Sjohnlev {
15745084Sjohnlev 	struct xenbus_watch *w;
15755084Sjohnlev 
15765084Sjohnlev 	ASSERT(path != NULL);
15775084Sjohnlev 
15785084Sjohnlev 	w = kmem_zalloc(sizeof (*w), KM_SLEEP);
15795084Sjohnlev 	w->node = path;
15805084Sjohnlev 	w->callback = &i_xvdi_probe_path_cb;
15815084Sjohnlev 	w->dev = NULL;
15825084Sjohnlev 
15835084Sjohnlev 	if (register_xenbus_watch(w) != 0) {
15845084Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_watch_device: "
15855084Sjohnlev 		    "cannot set watch on %s", path);
15865084Sjohnlev 		kmem_free(w, sizeof (*w));
15875084Sjohnlev 		return;
15885084Sjohnlev 	}
15895084Sjohnlev }
15905084Sjohnlev 
15915084Sjohnlev void
15925084Sjohnlev xvdi_watch_devices(int newstate)
15935084Sjohnlev {
15945084Sjohnlev 	int devclass;
15955084Sjohnlev 
15965084Sjohnlev 	/*
15975084Sjohnlev 	 * Watch for devices being created in the store.
15985084Sjohnlev 	 */
15995084Sjohnlev 	if (newstate == XENSTORE_DOWN)
16005084Sjohnlev 		return;
16015084Sjohnlev 	for (devclass = 0; devclass < NXDC; devclass++) {
16025084Sjohnlev 		if (xdci[devclass].xs_path_fe != NULL)
16035084Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_fe);
16045084Sjohnlev 		if (xdci[devclass].xs_path_be != NULL)
16055084Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_be);
16065084Sjohnlev 	}
16075084Sjohnlev }
16085084Sjohnlev 
16095084Sjohnlev /*
16105084Sjohnlev  * Iterate over the store looking for backend devices to create.
16115084Sjohnlev  */
16125084Sjohnlev static void
16135084Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp)
16145084Sjohnlev {
16155084Sjohnlev 	char **domains;
16165084Sjohnlev 	unsigned int ndomains;
16175084Sjohnlev 	int ldomains, i;
16185084Sjohnlev 
16195084Sjohnlev 	if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "",
16205084Sjohnlev 	    &ndomains)) == NULL)
16215084Sjohnlev 		return;
16225084Sjohnlev 
16235084Sjohnlev 	for (i = 0, ldomains = 0; i < ndomains; i++) {
16245084Sjohnlev 		ldomains += strlen(domains[i]) + 1 + sizeof (char *);
16255084Sjohnlev 
16265084Sjohnlev 		i_xvdi_enum_worker(parent, xdcp, domains[i]);
16275084Sjohnlev 	}
16285084Sjohnlev 	kmem_free(domains, ldomains);
16295084Sjohnlev }
16305084Sjohnlev 
16315084Sjohnlev /*
16325084Sjohnlev  * Iterate over the store looking for frontend devices to create.
16335084Sjohnlev  */
16345084Sjohnlev static void
16355084Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp)
16365084Sjohnlev {
16375084Sjohnlev 	i_xvdi_enum_worker(parent, xdcp, NULL);
16385084Sjohnlev }
16395084Sjohnlev 
16405084Sjohnlev static void
16415084Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp,
16425084Sjohnlev     char *domain)
16435084Sjohnlev {
16445084Sjohnlev 	char *path, *domain_path, *ep;
16455084Sjohnlev 	char **devices;
16465084Sjohnlev 	unsigned int ndevices;
16475084Sjohnlev 	int ldevices, j, circ;
16485084Sjohnlev 	domid_t dom;
16496460Sedp 	long tmplong;
16505084Sjohnlev 
16515084Sjohnlev 	if (domain == NULL) {
16525084Sjohnlev 		dom = DOMID_SELF;
16535084Sjohnlev 		path = xdcp->xs_path_fe;
16545084Sjohnlev 		domain_path = "";
16555084Sjohnlev 	} else {
16566460Sedp 		(void) ddi_strtol(domain, &ep, 0, &tmplong);
16576460Sedp 		dom = tmplong;
16585084Sjohnlev 		path = xdcp->xs_path_be;
16595084Sjohnlev 		domain_path = domain;
16605084Sjohnlev 	}
16615084Sjohnlev 
16625084Sjohnlev 	if ((devices = xenbus_directory(XBT_NULL, path, domain_path,
16635084Sjohnlev 	    &ndevices)) == NULL)
16645084Sjohnlev 		return;
16655084Sjohnlev 
16665084Sjohnlev 	for (j = 0, ldevices = 0; j < ndevices; j++) {
16675084Sjohnlev 		int vdev;
16685084Sjohnlev 
16695084Sjohnlev 		ldevices += strlen(devices[j]) + 1 + sizeof (char *);
16706460Sedp 		(void) ddi_strtol(devices[j], &ep, 0, &tmplong);
16716460Sedp 		vdev = tmplong;
16725084Sjohnlev 
16735084Sjohnlev 		ndi_devi_enter(parent, &circ);
16745084Sjohnlev 
16756460Sedp 		if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL)
16765084Sjohnlev 			(void) xvdi_create_dev(parent, xdcp->devclass,
16775084Sjohnlev 			    dom, vdev);
16785084Sjohnlev 
16795084Sjohnlev 		ndi_devi_exit(parent, circ);
16805084Sjohnlev 	}
16815084Sjohnlev 	kmem_free(devices, ldevices);
16825084Sjohnlev }
16835084Sjohnlev 
16845084Sjohnlev /*
16855084Sjohnlev  * Leaf drivers should call this in their detach() routine during suspend.
16865084Sjohnlev  */
16875084Sjohnlev void
16885084Sjohnlev xvdi_suspend(dev_info_t *dip)
16895084Sjohnlev {
16905084Sjohnlev 	i_xvdi_rem_watches(dip);
16915084Sjohnlev }
16925084Sjohnlev 
16935084Sjohnlev /*
16945084Sjohnlev  * Leaf drivers should call this in their attach() routine during resume.
16955084Sjohnlev  */
16965084Sjohnlev int
16975084Sjohnlev xvdi_resume(dev_info_t *dip)
16985084Sjohnlev {
16995084Sjohnlev 	return (i_xvdi_add_watches(dip));
17005084Sjohnlev }
17015084Sjohnlev 
17025084Sjohnlev /*
17035084Sjohnlev  * Add event handler for the leaf driver
17045084Sjohnlev  * to handle event triggered by the change in xenstore
17055084Sjohnlev  */
17065084Sjohnlev int
17075084Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name,
17085084Sjohnlev     void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *))
17095084Sjohnlev {
17105084Sjohnlev 	ddi_eventcookie_t ecv;
17115084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
17125084Sjohnlev 	ddi_callback_id_t *cbid;
17135084Sjohnlev 
17145084Sjohnlev 	ASSERT(pdp != NULL);
17155084Sjohnlev 
17165084Sjohnlev 	mutex_enter(&pdp->xd_lk);
17175084Sjohnlev 
17185084Sjohnlev 	if (strcmp(name, XS_OE_STATE) == 0) {
17195084Sjohnlev 		ASSERT(pdp->xd_xsdev.otherend != NULL);
17205084Sjohnlev 
17215084Sjohnlev 		cbid = &pdp->xd_oe_ehid;
17225084Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
17235084Sjohnlev 		if (pdp->xd_xsdev.frontend == 1) {
17245084Sjohnlev 			mutex_exit(&pdp->xd_lk);
17255084Sjohnlev 			return (DDI_FAILURE);
17265084Sjohnlev 		}
17275084Sjohnlev 
17285084Sjohnlev 		ASSERT(pdp->xd_hp_watch.node != NULL);
17295084Sjohnlev 
17305084Sjohnlev 		cbid = &pdp->xd_hp_ehid;
17315084Sjohnlev 	} else {
17325084Sjohnlev 		/* Unsupported watch. */
17335084Sjohnlev 		mutex_exit(&pdp->xd_lk);
17345084Sjohnlev 		return (DDI_FAILURE);
17355084Sjohnlev 	}
17365084Sjohnlev 
17375084Sjohnlev 	/*
17385084Sjohnlev 	 * No event handler provided, take default action to handle
17395084Sjohnlev 	 * event.
17405084Sjohnlev 	 */
17415084Sjohnlev 	if (evthandler == NULL) {
17425084Sjohnlev 		mutex_exit(&pdp->xd_lk);
17435084Sjohnlev 		return (DDI_SUCCESS);
17445084Sjohnlev 	}
17455084Sjohnlev 
17465084Sjohnlev 	ASSERT(*cbid == NULL);
17475084Sjohnlev 
17485084Sjohnlev 	if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) {
17495084Sjohnlev 		cmn_err(CE_WARN, "failed to find %s cookie for %s@%s",
17505084Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
17515084Sjohnlev 		mutex_exit(&pdp->xd_lk);
17525084Sjohnlev 		return (DDI_FAILURE);
17535084Sjohnlev 	}
17545084Sjohnlev 	if (ddi_add_event_handler(dip, ecv, evthandler, NULL, cbid)
17555084Sjohnlev 	    != DDI_SUCCESS) {
17565084Sjohnlev 		cmn_err(CE_WARN, "failed to add %s event handler for %s@%s",
17575084Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
17585084Sjohnlev 		*cbid = NULL;
17595084Sjohnlev 		mutex_exit(&pdp->xd_lk);
17605084Sjohnlev 		return (DDI_FAILURE);
17615084Sjohnlev 	}
17625084Sjohnlev 
17635084Sjohnlev 	mutex_exit(&pdp->xd_lk);
17645084Sjohnlev 
17655084Sjohnlev 	return (DDI_SUCCESS);
17665084Sjohnlev }
17675084Sjohnlev 
17685084Sjohnlev /*
17695084Sjohnlev  * Remove event handler for the leaf driver and unwatch xenstore
17705084Sjohnlev  * so, driver will not be notified when xenstore entry changed later
17715084Sjohnlev  */
17725084Sjohnlev void
17735084Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name)
17745084Sjohnlev {
17755084Sjohnlev 	struct xendev_ppd *pdp;
17765084Sjohnlev 	boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE;
17775084Sjohnlev 	ddi_callback_id_t oeid = NULL, hpid = NULL;
17785084Sjohnlev 
17795084Sjohnlev 	pdp = ddi_get_parent_data(dip);
17805084Sjohnlev 	ASSERT(pdp != NULL);
17815084Sjohnlev 
17825084Sjohnlev 	if (name == NULL) {
17835084Sjohnlev 		rem_oe = B_TRUE;
17845084Sjohnlev 		rem_hp = B_TRUE;
17855084Sjohnlev 	} else if (strcmp(name, XS_OE_STATE) == 0) {
17865084Sjohnlev 		rem_oe = B_TRUE;
17875084Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
17885084Sjohnlev 		rem_hp = B_TRUE;
17895084Sjohnlev 	} else {
17905084Sjohnlev 		cmn_err(CE_WARN, "event %s not supported, cannot remove", name);
17915084Sjohnlev 		return;
17925084Sjohnlev 	}
17935084Sjohnlev 
17945084Sjohnlev 	mutex_enter(&pdp->xd_lk);
17955084Sjohnlev 
17965084Sjohnlev 	if (rem_oe && (pdp->xd_oe_ehid != NULL)) {
17975084Sjohnlev 		oeid = pdp->xd_oe_ehid;
17985084Sjohnlev 		pdp->xd_oe_ehid = NULL;
17995084Sjohnlev 	}
18005084Sjohnlev 
18015084Sjohnlev 	if (rem_hp && (pdp->xd_hp_ehid != NULL)) {
18025084Sjohnlev 		hpid = pdp->xd_hp_ehid;
18035084Sjohnlev 		pdp->xd_hp_ehid = NULL;
18045084Sjohnlev 	}
18055084Sjohnlev 
18065084Sjohnlev 	mutex_exit(&pdp->xd_lk);
18075084Sjohnlev 
18085084Sjohnlev 	if (oeid != NULL)
18095084Sjohnlev 		(void) ddi_remove_event_handler(oeid);
18105084Sjohnlev 	if (hpid != NULL)
18115084Sjohnlev 		(void) ddi_remove_event_handler(hpid);
18125084Sjohnlev }
18135084Sjohnlev 
18145084Sjohnlev 
18155084Sjohnlev /*
18165084Sjohnlev  * common ring interfaces
18175084Sjohnlev  */
18185084Sjohnlev 
18195084Sjohnlev #define	FRONT_RING(_ringp)	(&(_ringp)->xr_sring.fr)
18205084Sjohnlev #define	BACK_RING(_ringp)	(&(_ringp)->xr_sring.br)
18215084Sjohnlev #define	GET_RING_SIZE(_ringp)	RING_SIZE(FRONT_RING(ringp))
18225084Sjohnlev #define	GET_RING_ENTRY_FE(_ringp, _idx)		\
18235084Sjohnlev 	(FRONT_RING(_ringp)->sring->ring +	\
18245084Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
18255084Sjohnlev #define	GET_RING_ENTRY_BE(_ringp, _idx)		\
18265084Sjohnlev 	(BACK_RING(_ringp)->sring->ring +	\
18275084Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
18285084Sjohnlev 
18295084Sjohnlev unsigned int
18305084Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp)
18315084Sjohnlev {
18325084Sjohnlev 	comif_ring_fe_t *frp;
18335084Sjohnlev 	comif_ring_be_t *brp;
18345084Sjohnlev 
18355084Sjohnlev 	if (ringp->xr_frontend) {
18365084Sjohnlev 		frp = FRONT_RING(ringp);
18375084Sjohnlev 		return (GET_RING_SIZE(ringp) -
18385084Sjohnlev 		    (frp->req_prod_pvt - frp->rsp_cons));
18395084Sjohnlev 	} else {
18405084Sjohnlev 		brp = BACK_RING(ringp);
18415084Sjohnlev 		return (GET_RING_SIZE(ringp) -
18425084Sjohnlev 		    (brp->rsp_prod_pvt - brp->req_cons));
18435084Sjohnlev 	}
18445084Sjohnlev }
18455084Sjohnlev 
18465084Sjohnlev int
18475084Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp)
18485084Sjohnlev {
18495084Sjohnlev 	comif_ring_be_t *brp;
18505084Sjohnlev 
18515084Sjohnlev 	ASSERT(!ringp->xr_frontend);
18525084Sjohnlev 	brp = BACK_RING(ringp);
18535084Sjohnlev 	return ((brp->req_cons !=
18545084Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) &&
18555084Sjohnlev 	    ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp)));
18565084Sjohnlev }
18575084Sjohnlev 
18585084Sjohnlev int
18595084Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp)
18605084Sjohnlev {
18615084Sjohnlev 	comif_ring_fe_t *frp;
18625084Sjohnlev 
18635084Sjohnlev 	ASSERT(ringp->xr_frontend);
18645084Sjohnlev 	frp = FRONT_RING(ringp);
18655084Sjohnlev 	return (frp->req_prod_pvt !=
18665084Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
18675084Sjohnlev }
18685084Sjohnlev 
18695084Sjohnlev int
18705084Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp)
18715084Sjohnlev {
18725084Sjohnlev 	comif_ring_fe_t *frp;
18735084Sjohnlev 
18745084Sjohnlev 	ASSERT(ringp->xr_frontend);
18755084Sjohnlev 	frp = FRONT_RING(ringp);
18765084Sjohnlev 	return (frp->rsp_cons !=
18775084Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
18785084Sjohnlev }
18795084Sjohnlev 
18805084Sjohnlev /* NOTE: req_event will be increased as needed */
18815084Sjohnlev void *
18825084Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp)
18835084Sjohnlev {
18845084Sjohnlev 	comif_ring_fe_t *frp;
18855084Sjohnlev 	comif_ring_be_t *brp;
18865084Sjohnlev 
18875084Sjohnlev 	if (ringp->xr_frontend) {
18885084Sjohnlev 		/* for frontend ring */
18895084Sjohnlev 		frp = FRONT_RING(ringp);
18905084Sjohnlev 		if (!RING_FULL(frp))
18915084Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++));
18925084Sjohnlev 		else
18935084Sjohnlev 			return (NULL);
18945084Sjohnlev 	} else {
18955084Sjohnlev 		/* for backend ring */
18965084Sjohnlev 		brp = BACK_RING(ringp);
18975084Sjohnlev 		/* RING_FINAL_CHECK_FOR_REQUESTS() */
18985084Sjohnlev 		if (xvdi_ring_has_unconsumed_requests(ringp))
18995084Sjohnlev 			return (GET_RING_ENTRY_BE(ringp, brp->req_cons++));
19005084Sjohnlev 		else {
19015084Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event,
19025084Sjohnlev 			    brp->req_cons + 1);
19035084Sjohnlev 			membar_enter();
19045084Sjohnlev 			if (xvdi_ring_has_unconsumed_requests(ringp))
19055084Sjohnlev 				return (GET_RING_ENTRY_BE(ringp,
19065084Sjohnlev 				    brp->req_cons++));
19075084Sjohnlev 			else
19085084Sjohnlev 				return (NULL);
19095084Sjohnlev 		}
19105084Sjohnlev 	}
19115084Sjohnlev }
19125084Sjohnlev 
19135084Sjohnlev int
19145084Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp)
19155084Sjohnlev {
19165084Sjohnlev 	RING_IDX old, new, reqevt;
19175084Sjohnlev 	comif_ring_fe_t *frp;
19185084Sjohnlev 
19195084Sjohnlev 	/* only frontend should be able to push request */
19205084Sjohnlev 	ASSERT(ringp->xr_frontend);
19215084Sjohnlev 
19225084Sjohnlev 	/* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */
19235084Sjohnlev 	frp = FRONT_RING(ringp);
19245084Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod);
19255084Sjohnlev 	new = frp->req_prod_pvt;
19265084Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new);
19275084Sjohnlev 	membar_enter();
19285084Sjohnlev 	reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event);
19295084Sjohnlev 	return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old));
19305084Sjohnlev }
19315084Sjohnlev 
19325084Sjohnlev /* NOTE: rsp_event will be increased as needed */
19335084Sjohnlev void *
19345084Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp)
19355084Sjohnlev {
19365084Sjohnlev 	comif_ring_fe_t *frp;
19375084Sjohnlev 	comif_ring_be_t *brp;
19385084Sjohnlev 
19395084Sjohnlev 	if (!ringp->xr_frontend) {
19405084Sjohnlev 		/* for backend ring */
19415084Sjohnlev 		brp = BACK_RING(ringp);
19425084Sjohnlev 		return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++));
19435084Sjohnlev 	} else {
19445084Sjohnlev 		/* for frontend ring */
19455084Sjohnlev 		frp = FRONT_RING(ringp);
19465084Sjohnlev 		/* RING_FINAL_CHECK_FOR_RESPONSES() */
19475084Sjohnlev 		if (xvdi_ring_has_unconsumed_responses(ringp))
19485084Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++));
19495084Sjohnlev 		else {
19505084Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event,
19515084Sjohnlev 			    frp->rsp_cons + 1);
19525084Sjohnlev 			membar_enter();
19535084Sjohnlev 			if (xvdi_ring_has_unconsumed_responses(ringp))
19545084Sjohnlev 				return (GET_RING_ENTRY_FE(ringp,
19555084Sjohnlev 				    frp->rsp_cons++));
19565084Sjohnlev 			else
19575084Sjohnlev 				return (NULL);
19585084Sjohnlev 		}
19595084Sjohnlev 	}
19605084Sjohnlev }
19615084Sjohnlev 
19625084Sjohnlev int
19635084Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp)
19645084Sjohnlev {
19655084Sjohnlev 	RING_IDX old, new, rspevt;
19665084Sjohnlev 	comif_ring_be_t *brp;
19675084Sjohnlev 
19685084Sjohnlev 	/* only backend should be able to push response */
19695084Sjohnlev 	ASSERT(!ringp->xr_frontend);
19705084Sjohnlev 
19715084Sjohnlev 	/* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */
19725084Sjohnlev 	brp = BACK_RING(ringp);
19735084Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod);
19745084Sjohnlev 	new = brp->rsp_prod_pvt;
19755084Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new);
19765084Sjohnlev 	membar_enter();
19775084Sjohnlev 	rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event);
19785084Sjohnlev 	return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old));
19795084Sjohnlev }
19805084Sjohnlev 
19815084Sjohnlev static void
19825084Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp)
19835084Sjohnlev {
19845084Sjohnlev 	ddi_acc_handle_t acchdl;
19855084Sjohnlev 	comif_sring_t *xsrp;
19865084Sjohnlev 	int i;
19875084Sjohnlev 
19885084Sjohnlev 	xsrp = (comif_sring_t *)ringp->xr_vaddr;
19895084Sjohnlev 	acchdl = ringp->xr_acc_hdl;
19905084Sjohnlev 
19915084Sjohnlev 	/* shared ring initialization */
19925084Sjohnlev 	ddi_put32(acchdl, &xsrp->req_prod, 0);
19935084Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_prod, 0);
19945084Sjohnlev 	ddi_put32(acchdl, &xsrp->req_event, 1);
19955084Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_event, 1);
19965084Sjohnlev 	for (i = 0; i < sizeof (xsrp->pad); i++)
19975084Sjohnlev 		ddi_put8(acchdl, xsrp->pad + i, 0);
19985084Sjohnlev }
19995084Sjohnlev 
20005084Sjohnlev static void
20015084Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
20025084Sjohnlev {
20035084Sjohnlev 	comif_ring_fe_t *xfrp;
20045084Sjohnlev 
20055084Sjohnlev 	xfrp = &ringp->xr_sring.fr;
20065084Sjohnlev 	xfrp->req_prod_pvt = 0;
20075084Sjohnlev 	xfrp->rsp_cons = 0;
20085084Sjohnlev 	xfrp->nr_ents = nentry;
20095084Sjohnlev 	xfrp->sring = (comif_sring_t *)ringp->xr_vaddr;
20105084Sjohnlev 
20115084Sjohnlev 	ringp->xr_frontend = 1;
20125084Sjohnlev 	ringp->xr_entry_size = entrysize;
20135084Sjohnlev }
20145084Sjohnlev 
20155741Smrj #ifndef XPV_HVM_DRIVER
20165084Sjohnlev static void
20175084Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
20185084Sjohnlev {
20195084Sjohnlev 	comif_ring_be_t *xbrp;
20205084Sjohnlev 
20215084Sjohnlev 	xbrp = &ringp->xr_sring.br;
20225084Sjohnlev 	xbrp->rsp_prod_pvt = 0;
20235084Sjohnlev 	xbrp->req_cons = 0;
20245084Sjohnlev 	xbrp->nr_ents = nentry;
20255084Sjohnlev 	xbrp->sring = (comif_sring_t *)ringp->xr_vaddr;
20265084Sjohnlev 
20275084Sjohnlev 	ringp->xr_frontend = 0;
20285084Sjohnlev 	ringp->xr_entry_size = entrysize;
20295084Sjohnlev }
20305741Smrj #endif /* XPV_HVM_DRIVER */
20315084Sjohnlev 
20325084Sjohnlev static void
20335084Sjohnlev xendev_offline_device(void *arg)
20345084Sjohnlev {
20355084Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
20365084Sjohnlev 	char devname[MAXNAMELEN] = {0};
20375084Sjohnlev 
20385084Sjohnlev 	/*
20395084Sjohnlev 	 * This is currently the only chance to delete a devinfo node, which
20405084Sjohnlev 	 * is _not_ always successful.
20415084Sjohnlev 	 */
20425084Sjohnlev 	(void) ddi_deviname(dip, devname);
20435084Sjohnlev 	(void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE);
20445084Sjohnlev 	(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
20455084Sjohnlev }
20465084Sjohnlev 
20475084Sjohnlev static void
20485084Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate)
20495084Sjohnlev {
20505084Sjohnlev 	dev_info_t *dip = (dev_info_t *)dev->data;
20515084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2052*7679SMax.Zhen@Sun.COM 	i_oestate_evt_t *evt = NULL;
2053*7679SMax.Zhen@Sun.COM 
2054*7679SMax.Zhen@Sun.COM 	XVDI_DPRINTF(XVDI_DBG_STATE,
2055*7679SMax.Zhen@Sun.COM 	    "i_xvdi_oestate_cb: %s@%s sees oestate change to %d\n",
2056*7679SMax.Zhen@Sun.COM 	    ddi_binding_name(dip) == NULL ? "null" : ddi_binding_name(dip),
2057*7679SMax.Zhen@Sun.COM 	    ddi_get_name_addr(dip) == NULL ? "null" : ddi_get_name_addr(dip),
2058*7679SMax.Zhen@Sun.COM 	    oestate);
20595084Sjohnlev 
20605084Sjohnlev 	/*
2061*7679SMax.Zhen@Sun.COM 	 * Don't trigger two consecutive ndi_devi_offline
2062*7679SMax.Zhen@Sun.COM 	 * on the same dip.
20635084Sjohnlev 	 */
20645084Sjohnlev 	if ((oestate == XenbusStateClosed) &&
20655084Sjohnlev 	    (dev->otherend_state == XenbusStateClosed))
20665084Sjohnlev 		return;
20675084Sjohnlev 
20685084Sjohnlev 	dev->otherend_state = oestate;
2069*7679SMax.Zhen@Sun.COM 
2070*7679SMax.Zhen@Sun.COM 	/*
2071*7679SMax.Zhen@Sun.COM 	 * Try to deliver the oestate change event to the dip
2072*7679SMax.Zhen@Sun.COM 	 */
2073*7679SMax.Zhen@Sun.COM 	evt = kmem_alloc(sizeof (i_oestate_evt_t), KM_SLEEP);
2074*7679SMax.Zhen@Sun.COM 	evt->dip = dip;
2075*7679SMax.Zhen@Sun.COM 	evt->state = oestate;
20765084Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
2077*7679SMax.Zhen@Sun.COM 	    i_xvdi_oestate_handler, (void *)evt, DDI_SLEEP);
20785084Sjohnlev }
20795084Sjohnlev 
20805084Sjohnlev /*ARGSUSED*/
20815084Sjohnlev static void
20825084Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec,
20835084Sjohnlev     unsigned int len)
20845084Sjohnlev {
20855084Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
20865084Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
20875084Sjohnlev 
2088*7679SMax.Zhen@Sun.COM #ifdef DEBUG
2089*7679SMax.Zhen@Sun.COM 	char *hp_status = NULL;
2090*7679SMax.Zhen@Sun.COM 	unsigned int hpl = 0;
2091*7679SMax.Zhen@Sun.COM 
2092*7679SMax.Zhen@Sun.COM 	(void) xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
2093*7679SMax.Zhen@Sun.COM 	    (void *)&hp_status, &hpl);
2094*7679SMax.Zhen@Sun.COM 	XVDI_DPRINTF(XVDI_DBG_STATE,
2095*7679SMax.Zhen@Sun.COM 	    "i_xvdi_hpstate_cb: %s@%s sees hpstate change to %s\n",
2096*7679SMax.Zhen@Sun.COM 	    ddi_binding_name(dip) == NULL ?  "null" : ddi_binding_name(dip),
2097*7679SMax.Zhen@Sun.COM 	    ddi_get_name_addr(dip) == NULL ?  "null" : ddi_get_name_addr(dip),
2098*7679SMax.Zhen@Sun.COM 	    hp_status == NULL ? "null" : hp_status);
2099*7679SMax.Zhen@Sun.COM 	if (hp_status != NULL)
2100*7679SMax.Zhen@Sun.COM 		kmem_free(hp_status, hpl);
2101*7679SMax.Zhen@Sun.COM #endif /* DEBUG */
2102*7679SMax.Zhen@Sun.COM 
21035084Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_hp_taskq,
21045084Sjohnlev 	    i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP);
21055084Sjohnlev }
21065084Sjohnlev 
21075084Sjohnlev static void
21085084Sjohnlev i_xvdi_probe_path_handler(void *arg)
21095084Sjohnlev {
21105084Sjohnlev 	dev_info_t *parent;
21115084Sjohnlev 	char *path = arg, *p = NULL;
21125084Sjohnlev 	int i, vdev, circ;
21135084Sjohnlev 	i_xd_cfg_t *xdcp;
21145084Sjohnlev 	boolean_t frontend;
21155084Sjohnlev 	domid_t dom;
21165084Sjohnlev 
21175084Sjohnlev 	for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) {
21185084Sjohnlev 
21195084Sjohnlev 		if ((xdcp->xs_path_fe != NULL) &&
21205084Sjohnlev 		    (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe))
21215084Sjohnlev 		    == 0)) {
21225084Sjohnlev 
21235084Sjohnlev 			frontend = B_TRUE;
21245084Sjohnlev 			p = path + strlen(xdcp->xs_path_fe);
21255084Sjohnlev 			break;
21265084Sjohnlev 		}
21275084Sjohnlev 
21285084Sjohnlev 		if ((xdcp->xs_path_be != NULL) &&
21295084Sjohnlev 		    (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be))
21305084Sjohnlev 		    == 0)) {
21315084Sjohnlev 
21325084Sjohnlev 			frontend = B_FALSE;
21335084Sjohnlev 			p = path + strlen(xdcp->xs_path_be);
21345084Sjohnlev 			break;
21355084Sjohnlev 		}
21365084Sjohnlev 
21375084Sjohnlev 	}
21385084Sjohnlev 
21395084Sjohnlev 	if (p == NULL) {
21405084Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
21415084Sjohnlev 		    "unexpected path prefix in %s", path);
21425084Sjohnlev 		goto done;
21435084Sjohnlev 	}
21445084Sjohnlev 
21455084Sjohnlev 	if (frontend) {
21465084Sjohnlev 		dom = DOMID_SELF;
21475084Sjohnlev 		if (sscanf(p, "/%d/", &vdev) != 1) {
21485084Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
21495084Sjohnlev 			    "i_xvdi_probe_path_handler: "
21505084Sjohnlev 			    "cannot parse frontend path %s",
21515084Sjohnlev 			    path);
21525084Sjohnlev 			goto done;
21535084Sjohnlev 		}
21545084Sjohnlev 	} else {
21557632SNick.Todd@Sun.COM 		if (sscanf(p, "/%hu/%d/", &dom, &vdev) != 2) {
21565084Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
21575084Sjohnlev 			    "i_xvdi_probe_path_handler: "
21585084Sjohnlev 			    "cannot parse backend path %s",
21595084Sjohnlev 			    path);
21605084Sjohnlev 			goto done;
21615084Sjohnlev 		}
21625084Sjohnlev 	}
21635084Sjohnlev 
21646175Sjohnlev 	/*
21656175Sjohnlev 	 * This is an oxymoron, so indicates a bogus configuration we
21666175Sjohnlev 	 * must check for.
21676175Sjohnlev 	 */
21686175Sjohnlev 	if (vdev == VDEV_NOXS) {
21696175Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
21706175Sjohnlev 		    "invalid path %s", path);
21716175Sjohnlev 		goto done;
21726175Sjohnlev 	}
21736175Sjohnlev 
21745084Sjohnlev 	parent = xendev_dip;
21755084Sjohnlev 	ASSERT(parent != NULL);
21765084Sjohnlev 
21775084Sjohnlev 	ndi_devi_enter(parent, &circ);
21785084Sjohnlev 
21795084Sjohnlev 	if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) {
21805084Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
21815084Sjohnlev 		    "i_xvdi_probe_path_handler: create for %s", path);
21825084Sjohnlev 		(void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev);
21835084Sjohnlev 	} else {
21845084Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
21855084Sjohnlev 		    "i_xvdi_probe_path_handler: %s already exists", path);
21865084Sjohnlev 	}
21875084Sjohnlev 
21885084Sjohnlev 	ndi_devi_exit(parent, circ);
21895084Sjohnlev 
21905084Sjohnlev done:
21915084Sjohnlev 	kmem_free(path, strlen(path) + 1);
21925084Sjohnlev }
2193