xref: /illumos-gate/usr/src/uts/common/xen/os/xvdi.c (revision 1ca30e39)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23d798155dSmrj  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev #pragma ident	"%Z%%M%	%I%	%E% SMI"
28843e1988Sjohnlev 
29843e1988Sjohnlev /*
30843e1988Sjohnlev  * Xen virtual device driver interfaces
31843e1988Sjohnlev  */
32843e1988Sjohnlev 
33843e1988Sjohnlev /*
34843e1988Sjohnlev  * todo:
35843e1988Sjohnlev  * + name space clean up:
36843e1988Sjohnlev  *	xvdi_* - public xen interfaces, for use by all leaf drivers
37843e1988Sjohnlev  *	xd_* - public xen data structures
38843e1988Sjohnlev  *	i_xvdi_* - implementation private functions
39843e1988Sjohnlev  *	xendev_* - xendev driver interfaces, both internal and in cb_ops/bus_ops
40843e1988Sjohnlev  * + add mdb dcmds to dump ring status
41843e1988Sjohnlev  * + implement xvdi_xxx to wrap xenbus_xxx read/write function
42843e1988Sjohnlev  * + convert (xendev_ring_t *) into xvdi_ring_handle_t
43843e1988Sjohnlev  */
44843e1988Sjohnlev #include <sys/conf.h>
45843e1988Sjohnlev #include <sys/param.h>
46843e1988Sjohnlev #include <sys/kmem.h>
47843e1988Sjohnlev #include <vm/seg_kmem.h>
48843e1988Sjohnlev #include <sys/debug.h>
49843e1988Sjohnlev #include <sys/modctl.h>
50843e1988Sjohnlev #include <sys/autoconf.h>
51843e1988Sjohnlev #include <sys/ddi_impldefs.h>
52843e1988Sjohnlev #include <sys/ddi_subrdefs.h>
53843e1988Sjohnlev #include <sys/ddi.h>
54843e1988Sjohnlev #include <sys/sunddi.h>
55843e1988Sjohnlev #include <sys/sunndi.h>
56843e1988Sjohnlev #include <sys/sunldi.h>
57843e1988Sjohnlev #include <sys/fs/dv_node.h>
58843e1988Sjohnlev #include <sys/avintr.h>
59843e1988Sjohnlev #include <sys/psm.h>
60843e1988Sjohnlev #include <sys/spl.h>
61843e1988Sjohnlev #include <sys/promif.h>
62843e1988Sjohnlev #include <sys/list.h>
63843e1988Sjohnlev #include <sys/bootconf.h>
64843e1988Sjohnlev #include <sys/bootsvcs.h>
65843e1988Sjohnlev #include <sys/bootinfo.h>
66843e1988Sjohnlev #include <sys/note.h>
67551bc2a6Smrj #ifdef XPV_HVM_DRIVER
68551bc2a6Smrj #include <sys/xpv_support.h>
69551bc2a6Smrj #include <sys/hypervisor.h>
70551bc2a6Smrj #include <public/grant_table.h>
71551bc2a6Smrj #include <public/xen.h>
72551bc2a6Smrj #include <public/io/xenbus.h>
73551bc2a6Smrj #include <public/io/xs_wire.h>
74551bc2a6Smrj #include <public/event_channel.h>
75551bc2a6Smrj #include <public/io/xenbus.h>
76551bc2a6Smrj #else /* XPV_HVM_DRIVER */
77551bc2a6Smrj #include <sys/hypervisor.h>
78843e1988Sjohnlev #include <sys/xen_mmu.h>
79843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
80551bc2a6Smrj #include <sys/evtchn_impl.h>
81551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
82551bc2a6Smrj #include <sys/gnttab.h>
83843e1988Sjohnlev #include <xen/sys/xendev.h>
84843e1988Sjohnlev #include <vm/hat_i86.h>
85843e1988Sjohnlev #include <sys/scsi/generic/inquiry.h>
86843e1988Sjohnlev #include <util/sscanf.h>
87843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
88843e1988Sjohnlev 
89843e1988Sjohnlev 
90843e1988Sjohnlev static void xvdi_ring_init_sring(xendev_ring_t *);
91843e1988Sjohnlev static void xvdi_ring_init_front_ring(xendev_ring_t *, size_t, size_t);
92551bc2a6Smrj #ifndef XPV_HVM_DRIVER
93843e1988Sjohnlev static void xvdi_ring_init_back_ring(xendev_ring_t *, size_t, size_t);
94551bc2a6Smrj #endif
95843e1988Sjohnlev static void xvdi_reinit_ring(dev_info_t *, grant_ref_t *, xendev_ring_t *);
96843e1988Sjohnlev 
97843e1988Sjohnlev static int i_xvdi_add_watches(dev_info_t *);
98843e1988Sjohnlev static void i_xvdi_rem_watches(dev_info_t *);
99843e1988Sjohnlev 
100843e1988Sjohnlev static int i_xvdi_add_watch_oestate(dev_info_t *);
101843e1988Sjohnlev static void i_xvdi_rem_watch_oestate(dev_info_t *);
102843e1988Sjohnlev static void i_xvdi_oestate_cb(struct xenbus_device *, XenbusState);
103843e1988Sjohnlev static void i_xvdi_oestate_handler(void *);
104843e1988Sjohnlev 
105843e1988Sjohnlev static int i_xvdi_add_watch_hpstate(dev_info_t *);
106843e1988Sjohnlev static void i_xvdi_rem_watch_hpstate(dev_info_t *);
107843e1988Sjohnlev static void i_xvdi_hpstate_cb(struct xenbus_watch *, const char **,
108843e1988Sjohnlev     unsigned int);
109843e1988Sjohnlev static void i_xvdi_hpstate_handler(void *);
110843e1988Sjohnlev 
111843e1988Sjohnlev static int i_xvdi_add_watch_bepath(dev_info_t *);
112843e1988Sjohnlev static void i_xvdi_rem_watch_bepath(dev_info_t *);
113843e1988Sjohnlev static void i_xvdi_bepath_cb(struct xenbus_watch *, const char **,
114843e1988Sjohnlev     unsigned in);
115843e1988Sjohnlev 
116843e1988Sjohnlev static void xendev_offline_device(void *);
117843e1988Sjohnlev 
118843e1988Sjohnlev static void i_xvdi_probe_path_cb(struct xenbus_watch *, const char **,
119843e1988Sjohnlev     unsigned int);
120843e1988Sjohnlev static void i_xvdi_probe_path_handler(void *);
121843e1988Sjohnlev 
122843e1988Sjohnlev typedef struct xd_cfg {
123843e1988Sjohnlev 	xendev_devclass_t devclass;
124843e1988Sjohnlev 	char *xsdev;
125843e1988Sjohnlev 	char *xs_path_fe;
126843e1988Sjohnlev 	char *xs_path_be;
127843e1988Sjohnlev 	char *node_fe;
128843e1988Sjohnlev 	char *node_be;
129843e1988Sjohnlev 	char *device_type;
130843e1988Sjohnlev 	int xd_ipl;
131843e1988Sjohnlev 	int flags;
132843e1988Sjohnlev } i_xd_cfg_t;
133843e1988Sjohnlev 
134843e1988Sjohnlev #define	XD_DOM_ZERO	0x01	/* dom0 only. */
135843e1988Sjohnlev #define	XD_DOM_GUEST	0x02	/* Guest domains (i.e. non-dom0). */
136843e1988Sjohnlev #define	XD_DOM_IO	0x04	/* IO domains. */
137843e1988Sjohnlev 
138843e1988Sjohnlev #define	XD_DOM_ALL	(XD_DOM_ZERO | XD_DOM_GUEST)
139843e1988Sjohnlev 
140843e1988Sjohnlev static i_xd_cfg_t xdci[] = {
141843e1988Sjohnlev 	{ XEN_CONSOLE, NULL, NULL, NULL, "xencons", NULL,
142843e1988Sjohnlev 	    "console", IPL_CONS, XD_DOM_ALL, },
143843e1988Sjohnlev 
144843e1988Sjohnlev 	{ XEN_VNET, "vif", "device/vif", "backend/vif", "xnf", "xnb",
145843e1988Sjohnlev 	    "network", IPL_VIF, XD_DOM_ALL, },
146843e1988Sjohnlev 
147843e1988Sjohnlev 	{ XEN_VBLK, "vbd", "device/vbd", "backend/vbd", "xdf", "xdb",
148843e1988Sjohnlev 	    "block", IPL_VBD, XD_DOM_ALL, },
149843e1988Sjohnlev 
150843e1988Sjohnlev 	{ XEN_XENBUS, NULL, NULL, NULL, "xenbus", NULL,
151843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
152843e1988Sjohnlev 
153843e1988Sjohnlev 	{ XEN_DOMCAPS, NULL, NULL, NULL, "domcaps", NULL,
154843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
155843e1988Sjohnlev 
156843e1988Sjohnlev 	{ XEN_BALLOON, NULL, NULL, NULL, "balloon", NULL,
157843e1988Sjohnlev 	    NULL, 0, XD_DOM_ALL, },
158843e1988Sjohnlev 
159843e1988Sjohnlev 	{ XEN_EVTCHN, NULL, NULL, NULL, "evtchn", NULL,
160843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
161843e1988Sjohnlev 
162843e1988Sjohnlev 	{ XEN_PRIVCMD, NULL, NULL, NULL, "privcmd", NULL,
163843e1988Sjohnlev 	    NULL, 0, XD_DOM_ZERO, },
164843e1988Sjohnlev };
165843e1988Sjohnlev #define	NXDC	(sizeof (xdci) / sizeof (xdci[0]))
166843e1988Sjohnlev 
167843e1988Sjohnlev static void i_xvdi_enum_fe(dev_info_t *, i_xd_cfg_t *);
168843e1988Sjohnlev static void i_xvdi_enum_be(dev_info_t *, i_xd_cfg_t *);
169843e1988Sjohnlev static void i_xvdi_enum_worker(dev_info_t *, i_xd_cfg_t *, char *);
170843e1988Sjohnlev 
171843e1988Sjohnlev /*
172843e1988Sjohnlev  * Xen device channel device access and DMA attributes
173843e1988Sjohnlev  */
174843e1988Sjohnlev static ddi_device_acc_attr_t xendev_dc_accattr = {
175843e1988Sjohnlev 	DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC
176843e1988Sjohnlev };
177843e1988Sjohnlev 
178843e1988Sjohnlev static ddi_dma_attr_t xendev_dc_dmaattr = {
179843e1988Sjohnlev 	DMA_ATTR_V0,		/* version of this structure */
180843e1988Sjohnlev 	0,			/* lowest usable address */
181843e1988Sjohnlev 	0xffffffffffffffffULL,	/* highest usable address */
182843e1988Sjohnlev 	0x7fffffff,		/* maximum DMAable byte count */
183843e1988Sjohnlev 	MMU_PAGESIZE,		/* alignment in bytes */
184843e1988Sjohnlev 	0x7ff,			/* bitmap of burst sizes */
185843e1988Sjohnlev 	1,			/* minimum transfer */
186843e1988Sjohnlev 	0xffffffffU,		/* maximum transfer */
187843e1988Sjohnlev 	0xffffffffffffffffULL,	/* maximum segment length */
188843e1988Sjohnlev 	1,			/* maximum number of segments */
189843e1988Sjohnlev 	1,			/* granularity */
190843e1988Sjohnlev 	0,			/* flags (reserved) */
191843e1988Sjohnlev };
192843e1988Sjohnlev 
193843e1988Sjohnlev static dev_info_t *xendev_dip = NULL;
194843e1988Sjohnlev 
195843e1988Sjohnlev #define	XVDI_DBG_STATE	0x01
196843e1988Sjohnlev #define	XVDI_DBG_PROBE	0x02
197843e1988Sjohnlev 
198843e1988Sjohnlev #ifdef DEBUG
199ab4a9bebSjohnlev int i_xvdi_debug = 0;
200843e1988Sjohnlev 
201843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)			\
202843e1988Sjohnlev {							\
203843e1988Sjohnlev 	if (i_xvdi_debug & (flag))			\
204843e1988Sjohnlev 		prom_printf((format), __VA_ARGS__);	\
205843e1988Sjohnlev }
206843e1988Sjohnlev #else
207843e1988Sjohnlev #define	XVDI_DPRINTF(flag, format, ...)
208843e1988Sjohnlev #endif /* DEBUG */
209843e1988Sjohnlev 
210843e1988Sjohnlev static i_xd_cfg_t *
211843e1988Sjohnlev i_xvdi_devclass2cfg(xendev_devclass_t devclass)
212843e1988Sjohnlev {
213843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
214843e1988Sjohnlev 	int i;
215843e1988Sjohnlev 
216843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++)
217843e1988Sjohnlev 		if (xdcp->devclass == devclass)
218843e1988Sjohnlev 			return (xdcp);
219843e1988Sjohnlev 
220843e1988Sjohnlev 	return (NULL);
221843e1988Sjohnlev }
222843e1988Sjohnlev 
223843e1988Sjohnlev int
224843e1988Sjohnlev xvdi_init_dev(dev_info_t *dip)
225843e1988Sjohnlev {
226843e1988Sjohnlev 	xendev_devclass_t devcls;
227843e1988Sjohnlev 	int vdevnum;
228843e1988Sjohnlev 	domid_t domid;
229843e1988Sjohnlev 	struct xendev_ppd *pdp;
230843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
231843e1988Sjohnlev 	boolean_t backend;
232843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
233843e1988Sjohnlev 	char *xsname;
234843e1988Sjohnlev 
235843e1988Sjohnlev 	devcls = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
236843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "devclass", XEN_INVAL);
237843e1988Sjohnlev 	vdevnum = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
238*1ca30e39Sjohnlev 	    DDI_PROP_DONTPASS, "vdev", VDEV_NOXS);
239843e1988Sjohnlev 	domid = (domid_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
240843e1988Sjohnlev 	    DDI_PROP_DONTPASS, "domain", DOMID_SELF);
241843e1988Sjohnlev 
242843e1988Sjohnlev 	backend = (domid != DOMID_SELF);
243843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devcls);
244843e1988Sjohnlev 	if (xdcp->device_type != NULL)
245843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
246843e1988Sjohnlev 		    "device_type", xdcp->device_type);
247843e1988Sjohnlev 
248843e1988Sjohnlev 	pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
249843e1988Sjohnlev 	pdp->xd_domain = domid;
250843e1988Sjohnlev 	pdp->xd_vdevnum = vdevnum;
251843e1988Sjohnlev 	pdp->xd_devclass = devcls;
252843e1988Sjohnlev 	pdp->xd_evtchn = INVALID_EVTCHN;
253843e1988Sjohnlev 	mutex_init(&pdp->xd_lk, NULL, MUTEX_DRIVER, NULL);
254843e1988Sjohnlev 	ddi_set_parent_data(dip, pdp);
255843e1988Sjohnlev 
256843e1988Sjohnlev 	/*
257843e1988Sjohnlev 	 * devices that do not need to interact with xenstore
258843e1988Sjohnlev 	 */
259*1ca30e39Sjohnlev 	if (vdevnum == VDEV_NOXS) {
260843e1988Sjohnlev 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
261843e1988Sjohnlev 		    "unit-address", "0");
262843e1988Sjohnlev 		if (devcls == XEN_CONSOLE)
263843e1988Sjohnlev 			(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
264843e1988Sjohnlev 			    "pm-hardware-state", "needs-suspend-resume");
265843e1988Sjohnlev 		return (DDI_SUCCESS);
266843e1988Sjohnlev 	}
267843e1988Sjohnlev 
268843e1988Sjohnlev 	/*
269843e1988Sjohnlev 	 * PV devices that need to probe xenstore
270843e1988Sjohnlev 	 */
271843e1988Sjohnlev 
272843e1988Sjohnlev 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
273843e1988Sjohnlev 	    "pm-hardware-state", "needs-suspend-resume");
274843e1988Sjohnlev 
275843e1988Sjohnlev 	xsname = xsnamebuf;
276843e1988Sjohnlev 	if (!backend)
277843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
278843e1988Sjohnlev 		    "%s/%d", xdcp->xs_path_fe, vdevnum);
279843e1988Sjohnlev 	else
280843e1988Sjohnlev 		(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
281843e1988Sjohnlev 		    "%s/%d/%d", xdcp->xs_path_be, domid, vdevnum);
2821d03c31eSjohnlev 	if ((xenbus_read_driver_state(xsname) >= XenbusStateClosing)) {
2831d03c31eSjohnlev 		/* Don't try to init a dev that may be closing */
2841d03c31eSjohnlev 		mutex_destroy(&pdp->xd_lk);
2851d03c31eSjohnlev 		kmem_free(pdp, sizeof (*pdp));
2861d03c31eSjohnlev 		ddi_set_parent_data(dip, NULL);
2871d03c31eSjohnlev 		return (DDI_FAILURE);
2881d03c31eSjohnlev 	}
289843e1988Sjohnlev 
290843e1988Sjohnlev 	pdp->xd_xsdev.nodename = i_ddi_strdup(xsname, KM_SLEEP);
291843e1988Sjohnlev 	pdp->xd_xsdev.devicetype = xdcp->xsdev;
292843e1988Sjohnlev 	pdp->xd_xsdev.frontend = (backend ? 0 : 1);
293843e1988Sjohnlev 	pdp->xd_xsdev.data = dip;
294843e1988Sjohnlev 	pdp->xd_xsdev.otherend_id = (backend ? domid : -1);
295843e1988Sjohnlev 	if (i_xvdi_add_watches(dip) != DDI_SUCCESS) {
296843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_init_dev: "
297843e1988Sjohnlev 		    "cannot add watches for %s", xsname);
298843e1988Sjohnlev 		xvdi_uninit_dev(dip);
299843e1988Sjohnlev 		return (DDI_FAILURE);
300843e1988Sjohnlev 	}
301843e1988Sjohnlev 
302843e1988Sjohnlev 	/*
303843e1988Sjohnlev 	 * frontend device will use "unit-addr" as
304843e1988Sjohnlev 	 * the bus address, which will be set here
305843e1988Sjohnlev 	 */
306843e1988Sjohnlev 	if (!backend) {
307843e1988Sjohnlev 		void *prop_str;
308843e1988Sjohnlev 		unsigned int prop_len, addr;
309843e1988Sjohnlev 
310843e1988Sjohnlev 		switch (devcls) {
311843e1988Sjohnlev 		case XEN_VNET:
312843e1988Sjohnlev 			if (xenbus_read(XBT_NULL, xsname, "mac", &prop_str,
313843e1988Sjohnlev 			    &prop_len) == 0) {
314843e1988Sjohnlev 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
315843e1988Sjohnlev 				    dip, "mac", prop_str);
316843e1988Sjohnlev 				kmem_free(prop_str, prop_len);
317843e1988Sjohnlev 			}
318843e1988Sjohnlev 			prop_str = NULL;
319843e1988Sjohnlev 			if (xenbus_scanf(XBT_NULL, xsname, "handle", "%u",
320843e1988Sjohnlev 			    &addr) == 0) {
321843e1988Sjohnlev 				char unitaddr[9]; /* hold 32-bit hex */
322843e1988Sjohnlev 
323843e1988Sjohnlev 				(void) snprintf(unitaddr, 9, "%x", addr);
324843e1988Sjohnlev 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
325843e1988Sjohnlev 				    dip, "unit-address", unitaddr);
326843e1988Sjohnlev 			}
327843e1988Sjohnlev 			break;
328843e1988Sjohnlev 		case XEN_VBLK:
329843e1988Sjohnlev 			if (xenbus_read(XBT_NULL, pdp->xd_xsdev.otherend,
330843e1988Sjohnlev 			    "dev", &prop_str, &prop_len) == 0) {
331843e1988Sjohnlev 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
332843e1988Sjohnlev 				    dip, "unit-address", prop_str);
333843e1988Sjohnlev 				kmem_free(prop_str, prop_len);
334843e1988Sjohnlev 			}
335551bc2a6Smrj #ifdef XPV_HVM_DRIVER
336551bc2a6Smrj 			/*
337551bc2a6Smrj 			 * The mapping between the 'dev' name and the
338551bc2a6Smrj 			 * device ID maintained by Xenstore has to be
339551bc2a6Smrj 			 * tracked explicitly in HVM domains.
340551bc2a6Smrj 			 */
341551bc2a6Smrj 			prop_str = strrchr(pdp->xd_xsdev.otherend, '/');
342551bc2a6Smrj 			if (prop_str != NULL) {
343551bc2a6Smrj 				prop_str = ((caddr_t)prop_str) + 1;
344551bc2a6Smrj 				(void) ndi_prop_update_string(DDI_DEV_T_NONE,
345551bc2a6Smrj 				    dip, "xenstore-id", prop_str);
346551bc2a6Smrj 			}
347551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
348843e1988Sjohnlev 			break;
349843e1988Sjohnlev 		default:
350843e1988Sjohnlev 			break;
351843e1988Sjohnlev 		}
352843e1988Sjohnlev 	}
353843e1988Sjohnlev 
354843e1988Sjohnlev 	return (DDI_SUCCESS);
355843e1988Sjohnlev }
356843e1988Sjohnlev 
357843e1988Sjohnlev void
358843e1988Sjohnlev xvdi_uninit_dev(dev_info_t *dip)
359843e1988Sjohnlev {
360843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
361843e1988Sjohnlev 
362843e1988Sjohnlev 	if (pdp != NULL) {
363843e1988Sjohnlev 		/* Remove any registered callbacks. */
364843e1988Sjohnlev 		xvdi_remove_event_handler(dip, NULL);
365843e1988Sjohnlev 
366843e1988Sjohnlev 		/* Remove any registered watches. */
367843e1988Sjohnlev 		i_xvdi_rem_watches(dip);
368843e1988Sjohnlev 
3691d03c31eSjohnlev 		/* tell other end to close */
370551bc2a6Smrj 		if (pdp->xd_xsdev.otherend_id != (domid_t)-1)
371551bc2a6Smrj 			(void) xvdi_switch_state(dip, XBT_NULL,
372551bc2a6Smrj 			    XenbusStateClosed);
3731d03c31eSjohnlev 
374843e1988Sjohnlev 		if (pdp->xd_xsdev.nodename != NULL)
375843e1988Sjohnlev 			kmem_free((char *)(pdp->xd_xsdev.nodename),
376843e1988Sjohnlev 			    strlen(pdp->xd_xsdev.nodename) + 1);
377843e1988Sjohnlev 
378843e1988Sjohnlev 		ddi_set_parent_data(dip, NULL);
379843e1988Sjohnlev 
380843e1988Sjohnlev 		mutex_destroy(&pdp->xd_lk);
381843e1988Sjohnlev 		kmem_free(pdp, sizeof (*pdp));
382843e1988Sjohnlev 	}
383843e1988Sjohnlev }
384843e1988Sjohnlev 
385843e1988Sjohnlev /*
386843e1988Sjohnlev  * Bind the event channel for this device instance.
387843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
388843e1988Sjohnlev  */
389843e1988Sjohnlev int
390843e1988Sjohnlev xvdi_bind_evtchn(dev_info_t *dip, evtchn_port_t evtchn)
391843e1988Sjohnlev {
392843e1988Sjohnlev 	struct xendev_ppd *pdp;
393843e1988Sjohnlev 	domid_t oeid;
394843e1988Sjohnlev 	int r;
395843e1988Sjohnlev 
396843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
397843e1988Sjohnlev 	ASSERT(pdp != NULL);
398843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
399843e1988Sjohnlev 
400843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
401843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
402843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
403843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
404843e1988Sjohnlev 		} else {
405843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
406843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
407843e1988Sjohnlev 			return (DDI_SUCCESS);
408843e1988Sjohnlev 		}
409843e1988Sjohnlev 	} else {
410843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
411843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
412843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
413843e1988Sjohnlev 			return (DDI_FAILURE);
414843e1988Sjohnlev 		}
415843e1988Sjohnlev 
416843e1988Sjohnlev 		if ((r = xen_bind_interdomain(oeid, evtchn, &pdp->xd_evtchn))) {
417843e1988Sjohnlev 			xvdi_dev_error(dip, r, "bind event channel");
418843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
419843e1988Sjohnlev 			return (DDI_FAILURE);
420843e1988Sjohnlev 		}
421843e1988Sjohnlev 	}
422551bc2a6Smrj #ifndef XPV_HVM_DRIVER
423843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
424551bc2a6Smrj #endif
425843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
426843e1988Sjohnlev 
427843e1988Sjohnlev 	return (DDI_SUCCESS);
428843e1988Sjohnlev }
429843e1988Sjohnlev 
430843e1988Sjohnlev /*
431843e1988Sjohnlev  * Allocate an event channel for this device instance.
432843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
433843e1988Sjohnlev  */
434843e1988Sjohnlev int
435843e1988Sjohnlev xvdi_alloc_evtchn(dev_info_t *dip)
436843e1988Sjohnlev {
437843e1988Sjohnlev 	struct xendev_ppd *pdp;
438843e1988Sjohnlev 	domid_t oeid;
439843e1988Sjohnlev 	int rv;
440843e1988Sjohnlev 
441843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
442843e1988Sjohnlev 	ASSERT(pdp != NULL);
443843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn == INVALID_EVTCHN);
444843e1988Sjohnlev 
445843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
446843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE) {
447843e1988Sjohnlev 		if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
448843e1988Sjohnlev 			pdp->xd_evtchn = xen_info->console.domU.evtchn;
449843e1988Sjohnlev 		} else {
450843e1988Sjohnlev 			pdp->xd_evtchn = INVALID_EVTCHN;
451843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
452843e1988Sjohnlev 			return (DDI_SUCCESS);
453843e1988Sjohnlev 		}
454843e1988Sjohnlev 	} else {
455843e1988Sjohnlev 		oeid = pdp->xd_xsdev.otherend_id;
456843e1988Sjohnlev 		if (oeid == (domid_t)-1) {
457843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
458843e1988Sjohnlev 			return (DDI_FAILURE);
459843e1988Sjohnlev 		}
460843e1988Sjohnlev 
461843e1988Sjohnlev 		if ((rv = xen_alloc_unbound_evtchn(oeid, &pdp->xd_evtchn))) {
462843e1988Sjohnlev 			xvdi_dev_error(dip, rv, "bind event channel");
463843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
464843e1988Sjohnlev 			return (DDI_FAILURE);
465843e1988Sjohnlev 		}
466843e1988Sjohnlev 	}
467551bc2a6Smrj #ifndef XPV_HVM_DRIVER
468843e1988Sjohnlev 	pdp->xd_ispec.intrspec_vec = ec_bind_evtchn_to_irq(pdp->xd_evtchn);
469551bc2a6Smrj #endif
470843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
471843e1988Sjohnlev 
472843e1988Sjohnlev 	return (DDI_SUCCESS);
473843e1988Sjohnlev }
474843e1988Sjohnlev 
475843e1988Sjohnlev /*
476843e1988Sjohnlev  * Unbind the event channel for this device instance.
477843e1988Sjohnlev  * Currently we only support one evtchn per device instance.
478843e1988Sjohnlev  */
479843e1988Sjohnlev void
480843e1988Sjohnlev xvdi_free_evtchn(dev_info_t *dip)
481843e1988Sjohnlev {
482843e1988Sjohnlev 	struct xendev_ppd *pdp;
483843e1988Sjohnlev 
484843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
485843e1988Sjohnlev 	ASSERT(pdp != NULL);
486843e1988Sjohnlev 
487843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
488843e1988Sjohnlev 	if (pdp->xd_evtchn != INVALID_EVTCHN) {
489551bc2a6Smrj #ifndef XPV_HVM_DRIVER
490843e1988Sjohnlev 		ec_unbind_irq(pdp->xd_ispec.intrspec_vec);
491843e1988Sjohnlev 		pdp->xd_ispec.intrspec_vec = 0;
492551bc2a6Smrj #endif
493551bc2a6Smrj 		pdp->xd_evtchn = INVALID_EVTCHN;
494843e1988Sjohnlev 	}
495843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
496843e1988Sjohnlev }
497843e1988Sjohnlev 
498551bc2a6Smrj #ifndef XPV_HVM_DRIVER
499843e1988Sjohnlev /*
500843e1988Sjohnlev  * Map an inter-domain communication ring for a virtual device.
501843e1988Sjohnlev  * This is used by backend drivers.
502843e1988Sjohnlev  */
503843e1988Sjohnlev int
504843e1988Sjohnlev xvdi_map_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
505843e1988Sjohnlev     grant_ref_t gref, xendev_ring_t **ringpp)
506843e1988Sjohnlev {
507843e1988Sjohnlev 	domid_t oeid;
508843e1988Sjohnlev 	gnttab_map_grant_ref_t mapop;
509843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
510843e1988Sjohnlev 	caddr_t ringva;
511843e1988Sjohnlev 	ddi_acc_hdl_t *ap;
512843e1988Sjohnlev 	ddi_acc_impl_t *iap;
513843e1988Sjohnlev 	xendev_ring_t *ring;
514843e1988Sjohnlev 	int err;
515843e1988Sjohnlev 	char errstr[] = "mapping in ring buffer";
516843e1988Sjohnlev 
517843e1988Sjohnlev 	ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
518843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
519843e1988Sjohnlev 
520843e1988Sjohnlev 	/* alloc va in backend dom for ring buffer */
521843e1988Sjohnlev 	ringva = vmem_xalloc(heap_arena, PAGESIZE, PAGESIZE,
522843e1988Sjohnlev 	    0, 0, 0, 0, VM_SLEEP);
523843e1988Sjohnlev 
524843e1988Sjohnlev 	/* map in ring page */
525843e1988Sjohnlev 	hat_prepare_mapping(kas.a_hat, ringva);
526843e1988Sjohnlev 	mapop.host_addr = (uint64_t)(uintptr_t)ringva;
527843e1988Sjohnlev 	mapop.flags = GNTMAP_host_map;
528843e1988Sjohnlev 	mapop.ref = gref;
529843e1988Sjohnlev 	mapop.dom = oeid;
530843e1988Sjohnlev 	err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &mapop, 1);
531843e1988Sjohnlev 	if (err) {
532843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
533843e1988Sjohnlev 		goto errout1;
534843e1988Sjohnlev 	}
535843e1988Sjohnlev 
536843e1988Sjohnlev 	if (mapop.status != 0) {
537843e1988Sjohnlev 		xvdi_fatal_error(dip, err, errstr);
538843e1988Sjohnlev 		goto errout2;
539843e1988Sjohnlev 	}
540843e1988Sjohnlev 	ring->xr_vaddr = ringva;
541843e1988Sjohnlev 	ring->xr_grant_hdl = mapop.handle;
542843e1988Sjohnlev 	ring->xr_gref = gref;
543843e1988Sjohnlev 
544843e1988Sjohnlev 	/*
545843e1988Sjohnlev 	 * init an acc handle and associate it w/ this ring
546843e1988Sjohnlev 	 * this is only for backend drivers. we get the memory by calling
547843e1988Sjohnlev 	 * vmem_xalloc(), instead of calling any ddi function, so we have
548843e1988Sjohnlev 	 * to init an acc handle by ourselves
549843e1988Sjohnlev 	 */
550843e1988Sjohnlev 	ring->xr_acc_hdl = impl_acc_hdl_alloc(KM_SLEEP, NULL);
551843e1988Sjohnlev 	ap = impl_acc_hdl_get(ring->xr_acc_hdl);
552843e1988Sjohnlev 	ap->ah_vers = VERS_ACCHDL;
553843e1988Sjohnlev 	ap->ah_dip = dip;
554843e1988Sjohnlev 	ap->ah_xfermodes = DDI_DMA_CONSISTENT;
555843e1988Sjohnlev 	ap->ah_acc = xendev_dc_accattr;
556843e1988Sjohnlev 	iap = (ddi_acc_impl_t *)ap->ah_platform_private;
557843e1988Sjohnlev 	iap->ahi_acc_attr |= DDI_ACCATTR_CPU_VADDR;
558843e1988Sjohnlev 	impl_acc_hdl_init(ap);
559843e1988Sjohnlev 	ap->ah_offset = 0;
560843e1988Sjohnlev 	ap->ah_len = (off_t)PAGESIZE;
561843e1988Sjohnlev 	ap->ah_addr = ring->xr_vaddr;
562843e1988Sjohnlev 
563843e1988Sjohnlev 	/* init backend ring */
564843e1988Sjohnlev 	xvdi_ring_init_back_ring(ring, nentry, entrysize);
565843e1988Sjohnlev 
566843e1988Sjohnlev 	*ringpp = ring;
567843e1988Sjohnlev 
568843e1988Sjohnlev 	return (DDI_SUCCESS);
569843e1988Sjohnlev 
570843e1988Sjohnlev errout2:
571843e1988Sjohnlev 	/* unmap ring page */
572843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ringva;
573843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
574843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
575843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
576843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ringva);
577843e1988Sjohnlev errout1:
578843e1988Sjohnlev 	vmem_xfree(heap_arena, ringva, PAGESIZE);
579843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
580843e1988Sjohnlev 	return (DDI_FAILURE);
581843e1988Sjohnlev }
582843e1988Sjohnlev 
583843e1988Sjohnlev /*
584843e1988Sjohnlev  * Unmap a ring for a virtual device.
585843e1988Sjohnlev  * This is used by backend drivers.
586843e1988Sjohnlev  */
587843e1988Sjohnlev void
588843e1988Sjohnlev xvdi_unmap_ring(xendev_ring_t *ring)
589843e1988Sjohnlev {
590843e1988Sjohnlev 	gnttab_unmap_grant_ref_t unmapop;
591843e1988Sjohnlev 
592843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
593843e1988Sjohnlev 
594843e1988Sjohnlev 	impl_acc_hdl_free(ring->xr_acc_hdl);
595843e1988Sjohnlev 	unmapop.host_addr = (uint64_t)(uintptr_t)ring->xr_vaddr;
596843e1988Sjohnlev 	unmapop.handle = ring->xr_grant_hdl;
597843e1988Sjohnlev 	unmapop.dev_bus_addr = NULL;
598843e1988Sjohnlev 	(void) HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmapop, 1);
599843e1988Sjohnlev 	hat_release_mapping(kas.a_hat, ring->xr_vaddr);
600843e1988Sjohnlev 	vmem_xfree(heap_arena, ring->xr_vaddr, PAGESIZE);
601843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
602843e1988Sjohnlev }
603551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
604843e1988Sjohnlev 
605843e1988Sjohnlev /*
606843e1988Sjohnlev  * Re-initialise an inter-domain communications ring for the backend domain.
607843e1988Sjohnlev  * ring will be re-initialized after re-grant succeed
608843e1988Sjohnlev  * ring will be freed if fails to re-grant access to backend domain
609843e1988Sjohnlev  * so, don't keep useful data in the ring
610843e1988Sjohnlev  * used only in frontend driver
611843e1988Sjohnlev  */
612843e1988Sjohnlev static void
613843e1988Sjohnlev xvdi_reinit_ring(dev_info_t *dip, grant_ref_t *gref, xendev_ring_t *ringp)
614843e1988Sjohnlev {
615843e1988Sjohnlev 	paddr_t rpaddr;
616843e1988Sjohnlev 	maddr_t rmaddr;
617843e1988Sjohnlev 
618843e1988Sjohnlev 	ASSERT((ringp != NULL) && (ringp->xr_paddr != 0));
619843e1988Sjohnlev 	rpaddr = ringp->xr_paddr;
620843e1988Sjohnlev 
621843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? rpaddr : pa_to_ma(rpaddr);
622843e1988Sjohnlev 	gnttab_grant_foreign_access_ref(ringp->xr_gref, xvdi_get_oeid(dip),
623843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0);
624843e1988Sjohnlev 	*gref = ringp->xr_gref;
625843e1988Sjohnlev 
626843e1988Sjohnlev 	/* init frontend ring */
627843e1988Sjohnlev 	xvdi_ring_init_sring(ringp);
628843e1988Sjohnlev 	xvdi_ring_init_front_ring(ringp, ringp->xr_sring.fr.nr_ents,
629843e1988Sjohnlev 	    ringp->xr_entry_size);
630843e1988Sjohnlev }
631843e1988Sjohnlev 
632843e1988Sjohnlev /*
633843e1988Sjohnlev  * allocate Xen inter-domain communications ring for Xen virtual devices
634843e1988Sjohnlev  * used only in frontend driver
635843e1988Sjohnlev  * if *ringpp is not NULL, we'll simply re-init it
636843e1988Sjohnlev  */
637843e1988Sjohnlev int
638843e1988Sjohnlev xvdi_alloc_ring(dev_info_t *dip, size_t nentry, size_t entrysize,
639843e1988Sjohnlev     grant_ref_t *gref, xendev_ring_t **ringpp)
640843e1988Sjohnlev {
641843e1988Sjohnlev 	size_t len;
642843e1988Sjohnlev 	xendev_ring_t *ring;
643843e1988Sjohnlev 	ddi_dma_cookie_t dma_cookie;
644843e1988Sjohnlev 	uint_t ncookies;
645843e1988Sjohnlev 	grant_ref_t ring_gref;
646843e1988Sjohnlev 	domid_t oeid;
647843e1988Sjohnlev 	maddr_t rmaddr;
648843e1988Sjohnlev 
649843e1988Sjohnlev 	if (*ringpp) {
650843e1988Sjohnlev 		xvdi_reinit_ring(dip, gref, *ringpp);
651843e1988Sjohnlev 		return (DDI_SUCCESS);
652843e1988Sjohnlev 	}
653843e1988Sjohnlev 
654843e1988Sjohnlev 	*ringpp = ring = kmem_zalloc(sizeof (xendev_ring_t), KM_SLEEP);
655843e1988Sjohnlev 	oeid = xvdi_get_oeid(dip);
656843e1988Sjohnlev 
657843e1988Sjohnlev 	/*
658843e1988Sjohnlev 	 * Allocate page for this ring buffer
659843e1988Sjohnlev 	 */
660843e1988Sjohnlev 	if (ddi_dma_alloc_handle(dip, &xendev_dc_dmaattr, DDI_DMA_SLEEP,
661843e1988Sjohnlev 	    0, &ring->xr_dma_hdl) != DDI_SUCCESS)
662843e1988Sjohnlev 		goto err;
663843e1988Sjohnlev 
664843e1988Sjohnlev 	if (ddi_dma_mem_alloc(ring->xr_dma_hdl, PAGESIZE,
665843e1988Sjohnlev 	    &xendev_dc_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
666843e1988Sjohnlev 	    &ring->xr_vaddr, &len, &ring->xr_acc_hdl) != DDI_SUCCESS) {
667843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
668843e1988Sjohnlev 		goto err;
669843e1988Sjohnlev 	}
670843e1988Sjohnlev 
671843e1988Sjohnlev 	if (ddi_dma_addr_bind_handle(ring->xr_dma_hdl, NULL,
672843e1988Sjohnlev 	    ring->xr_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
673843e1988Sjohnlev 	    DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED) {
674843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
675843e1988Sjohnlev 		ring->xr_vaddr = NULL;
676843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
677843e1988Sjohnlev 		goto err;
678843e1988Sjohnlev 	}
679843e1988Sjohnlev 	ASSERT(ncookies == 1);
680843e1988Sjohnlev 	ring->xr_paddr = dma_cookie.dmac_laddress;
681843e1988Sjohnlev 	rmaddr = DOMAIN_IS_INITDOMAIN(xen_info) ? ring->xr_paddr :
682843e1988Sjohnlev 	    pa_to_ma(ring->xr_paddr);
683843e1988Sjohnlev 
684843e1988Sjohnlev 	if ((ring_gref = gnttab_grant_foreign_access(oeid,
685843e1988Sjohnlev 	    rmaddr >> PAGESHIFT, 0)) == (grant_ref_t)-1) {
686843e1988Sjohnlev 		(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
687843e1988Sjohnlev 		ddi_dma_mem_free(&ring->xr_acc_hdl);
688843e1988Sjohnlev 		ring->xr_vaddr = NULL;
689843e1988Sjohnlev 		ddi_dma_free_handle(&ring->xr_dma_hdl);
690843e1988Sjohnlev 		goto err;
691843e1988Sjohnlev 	}
692843e1988Sjohnlev 	*gref = ring->xr_gref = ring_gref;
693843e1988Sjohnlev 
694843e1988Sjohnlev 	/* init frontend ring */
695843e1988Sjohnlev 	xvdi_ring_init_sring(ring);
696843e1988Sjohnlev 	xvdi_ring_init_front_ring(ring, nentry, entrysize);
697843e1988Sjohnlev 
698843e1988Sjohnlev 	return (DDI_SUCCESS);
699843e1988Sjohnlev 
700843e1988Sjohnlev err:
701843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
702843e1988Sjohnlev 	return (DDI_FAILURE);
703843e1988Sjohnlev }
704843e1988Sjohnlev 
705843e1988Sjohnlev /*
706843e1988Sjohnlev  * Release ring buffers allocated for Xen devices
707843e1988Sjohnlev  * used for frontend driver
708843e1988Sjohnlev  */
709843e1988Sjohnlev void
710843e1988Sjohnlev xvdi_free_ring(xendev_ring_t *ring)
711843e1988Sjohnlev {
712843e1988Sjohnlev 	ASSERT((ring != NULL) && (ring->xr_vaddr != NULL));
713843e1988Sjohnlev 
714843e1988Sjohnlev 	(void) gnttab_end_foreign_access_ref(ring->xr_gref, 0);
715843e1988Sjohnlev 	(void) ddi_dma_unbind_handle(ring->xr_dma_hdl);
716843e1988Sjohnlev 	ddi_dma_mem_free(&ring->xr_acc_hdl);
717843e1988Sjohnlev 	ddi_dma_free_handle(&ring->xr_dma_hdl);
718843e1988Sjohnlev 	kmem_free(ring, sizeof (xendev_ring_t));
719843e1988Sjohnlev }
720843e1988Sjohnlev 
721843e1988Sjohnlev dev_info_t *
722843e1988Sjohnlev xvdi_create_dev(dev_info_t *parent, xendev_devclass_t devclass,
723843e1988Sjohnlev     domid_t dom, int vdev)
724843e1988Sjohnlev {
725843e1988Sjohnlev 	dev_info_t *dip;
726843e1988Sjohnlev 	boolean_t backend;
727843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
728843e1988Sjohnlev 	char xsnamebuf[TYPICALMAXPATHLEN];
729843e1988Sjohnlev 	char *type, *node = NULL, *xsname = NULL;
730843e1988Sjohnlev 	unsigned int tlen;
7311d03c31eSjohnlev 	int ret;
732843e1988Sjohnlev 
733843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
734843e1988Sjohnlev 
735843e1988Sjohnlev 	backend = (dom != DOMID_SELF);
736843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
737843e1988Sjohnlev 	ASSERT(xdcp != NULL);
738843e1988Sjohnlev 
739*1ca30e39Sjohnlev 	if (vdev != VDEV_NOXS) {
740843e1988Sjohnlev 		if (!backend) {
741843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
742843e1988Sjohnlev 			    "%s/%d", xdcp->xs_path_fe, vdev);
743843e1988Sjohnlev 			xsname = xsnamebuf;
744843e1988Sjohnlev 			node = xdcp->node_fe;
745843e1988Sjohnlev 		} else {
746843e1988Sjohnlev 			(void) snprintf(xsnamebuf, sizeof (xsnamebuf),
747843e1988Sjohnlev 			    "%s/%d/%d", xdcp->xs_path_be, dom, vdev);
748843e1988Sjohnlev 			xsname = xsnamebuf;
749843e1988Sjohnlev 			node = xdcp->node_be;
750843e1988Sjohnlev 		}
751843e1988Sjohnlev 	} else {
752843e1988Sjohnlev 		node = xdcp->node_fe;
753843e1988Sjohnlev 	}
754843e1988Sjohnlev 
755843e1988Sjohnlev 	/* Must have a driver to use. */
756843e1988Sjohnlev 	if (node == NULL)
757843e1988Sjohnlev 		return (NULL);
758843e1988Sjohnlev 
759843e1988Sjohnlev 	/*
760843e1988Sjohnlev 	 * We need to check the state of this device before we go
761843e1988Sjohnlev 	 * further, otherwise we'll end up with a dead loop if
762843e1988Sjohnlev 	 * anything goes wrong.
763843e1988Sjohnlev 	 */
764843e1988Sjohnlev 	if ((xsname != NULL) &&
765843e1988Sjohnlev 	    (xenbus_read_driver_state(xsname) >= XenbusStateClosing))
766843e1988Sjohnlev 		return (NULL);
767843e1988Sjohnlev 
768843e1988Sjohnlev 	ndi_devi_alloc_sleep(parent, node, DEVI_SID_NODEID, &dip);
769843e1988Sjohnlev 
770843e1988Sjohnlev 	/*
771843e1988Sjohnlev 	 * Driver binding uses the compatible property _before_ the
772843e1988Sjohnlev 	 * node name, so we set the node name to the 'model' of the
773843e1988Sjohnlev 	 * device (i.e. 'xnb' or 'xdb') and, if 'type' is present,
774843e1988Sjohnlev 	 * encode both the model and the type in a compatible property
775843e1988Sjohnlev 	 * (i.e. 'xnb,netfront' or 'xnb,SUNW_mac').  This allows a
776843e1988Sjohnlev 	 * driver binding based on the <model,type> pair _before_ a
777843e1988Sjohnlev 	 * binding based on the node name.
778843e1988Sjohnlev 	 */
779843e1988Sjohnlev 	if ((xsname != NULL) &&
780843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, xsname, "type", (void *)&type, &tlen)
781843e1988Sjohnlev 	    == 0)) {
782843e1988Sjohnlev 		size_t clen;
783843e1988Sjohnlev 		char *c[1];
784843e1988Sjohnlev 
785843e1988Sjohnlev 		clen = strlen(node) + strlen(type) + 2;
786843e1988Sjohnlev 		c[0] = kmem_alloc(clen, KM_SLEEP);
787843e1988Sjohnlev 		(void) snprintf(c[0], clen, "%s,%s", node, type);
788843e1988Sjohnlev 
789843e1988Sjohnlev 		(void) ndi_prop_update_string_array(DDI_DEV_T_NONE,
790843e1988Sjohnlev 		    dip, "compatible", (char **)c, 1);
791843e1988Sjohnlev 
792843e1988Sjohnlev 		kmem_free(c[0], clen);
793843e1988Sjohnlev 		kmem_free(type, tlen);
794843e1988Sjohnlev 	}
795843e1988Sjohnlev 
796843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "devclass", devclass);
797843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "domain", dom);
798843e1988Sjohnlev 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vdev", vdev);
799843e1988Sjohnlev 
800843e1988Sjohnlev 	if (i_ddi_devi_attached(parent))
8011d03c31eSjohnlev 		ret = ndi_devi_online(dip, 0);
802843e1988Sjohnlev 	else
8031d03c31eSjohnlev 		ret = ndi_devi_bind_driver(dip, 0);
8041d03c31eSjohnlev 	if (ret != NDI_SUCCESS)
8051d03c31eSjohnlev 		(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
806843e1988Sjohnlev 
807843e1988Sjohnlev 	return (dip);
808843e1988Sjohnlev }
809843e1988Sjohnlev 
810843e1988Sjohnlev /*
811843e1988Sjohnlev  * xendev_enum_class()
812843e1988Sjohnlev  */
813843e1988Sjohnlev void
814843e1988Sjohnlev xendev_enum_class(dev_info_t *parent, xendev_devclass_t devclass)
815843e1988Sjohnlev {
816d798155dSmrj 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
817d798155dSmrj 	boolean_t domU = !dom0;
818843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
819843e1988Sjohnlev 
820843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
821843e1988Sjohnlev 	ASSERT(xdcp != NULL);
822843e1988Sjohnlev 
823d798155dSmrj 	if (dom0 && !(xdcp->flags & XD_DOM_ZERO))
824d798155dSmrj 		return;
825d798155dSmrj 
826d798155dSmrj 	if (domU && !(xdcp->flags & XD_DOM_GUEST))
827d798155dSmrj 		return;
828d798155dSmrj 
829843e1988Sjohnlev 	if (xdcp->xsdev == NULL) {
830843e1988Sjohnlev 		int circ;
831843e1988Sjohnlev 
832843e1988Sjohnlev 		/*
833843e1988Sjohnlev 		 * Don't need to probe this kind of device from the
834843e1988Sjohnlev 		 * store, just create one if it doesn't exist.
835843e1988Sjohnlev 		 */
836843e1988Sjohnlev 
837843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
838*1ca30e39Sjohnlev 		if (xvdi_find_dev(parent, devclass, DOMID_SELF, VDEV_NOXS)
839843e1988Sjohnlev 		    == NULL)
840843e1988Sjohnlev 			(void) xvdi_create_dev(parent, devclass,
841*1ca30e39Sjohnlev 			    DOMID_SELF, VDEV_NOXS);
842843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
843843e1988Sjohnlev 	} else {
844843e1988Sjohnlev 		/*
845843e1988Sjohnlev 		 * Probe this kind of device from the store, both
846843e1988Sjohnlev 		 * frontend and backend.
847843e1988Sjohnlev 		 */
848843e1988Sjohnlev 
849843e1988Sjohnlev 		i_xvdi_enum_fe(parent, xdcp);
850843e1988Sjohnlev 		i_xvdi_enum_be(parent, xdcp);
851843e1988Sjohnlev 	}
852843e1988Sjohnlev }
853843e1988Sjohnlev 
854843e1988Sjohnlev /*
855843e1988Sjohnlev  * xendev_enum_all()
856843e1988Sjohnlev  */
857843e1988Sjohnlev void
858843e1988Sjohnlev xendev_enum_all(dev_info_t *parent, boolean_t store_unavailable)
859843e1988Sjohnlev {
860843e1988Sjohnlev 	int i;
861843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
862843e1988Sjohnlev 	boolean_t dom0 = DOMAIN_IS_INITDOMAIN(xen_info);
863843e1988Sjohnlev 
864843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
865843e1988Sjohnlev 		/*
866843e1988Sjohnlev 		 * Dom0 relies on watchpoints to create non-soft
867843e1988Sjohnlev 		 * devices - don't attempt to iterate over the store.
868843e1988Sjohnlev 		 */
869843e1988Sjohnlev 		if (dom0 && (xdcp->xsdev != NULL))
870843e1988Sjohnlev 			continue;
871843e1988Sjohnlev 
872843e1988Sjohnlev 		/*
873843e1988Sjohnlev 		 * If the store is not yet available, don't attempt to
874843e1988Sjohnlev 		 * iterate.
875843e1988Sjohnlev 		 */
876843e1988Sjohnlev 		if (store_unavailable && (xdcp->xsdev != NULL))
877843e1988Sjohnlev 			continue;
878843e1988Sjohnlev 
879843e1988Sjohnlev 		xendev_enum_class(parent, xdcp->devclass);
880843e1988Sjohnlev 	}
881843e1988Sjohnlev }
882843e1988Sjohnlev 
883843e1988Sjohnlev xendev_devclass_t
884843e1988Sjohnlev xendev_nodename_to_devclass(char *nodename)
885843e1988Sjohnlev {
886843e1988Sjohnlev 	int i;
887843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
888843e1988Sjohnlev 
889843e1988Sjohnlev 	/*
890843e1988Sjohnlev 	 * This relies on the convention that variants of a base
891843e1988Sjohnlev 	 * driver share the same prefix and that there are no drivers
892843e1988Sjohnlev 	 * which share a common prefix with the name of any other base
893843e1988Sjohnlev 	 * drivers.
894843e1988Sjohnlev 	 *
895843e1988Sjohnlev 	 * So for a base driver 'xnb' (which is the name listed in
896843e1988Sjohnlev 	 * xdci) the variants all begin with the string 'xnb' (in fact
897843e1988Sjohnlev 	 * they are 'xnbe', 'xnbo' and 'xnbu') and there are no other
898843e1988Sjohnlev 	 * base drivers which have the prefix 'xnb'.
899843e1988Sjohnlev 	 */
900843e1988Sjohnlev 	ASSERT(nodename != NULL);
901843e1988Sjohnlev 	for (i = 0, xdcp = xdci; i < NXDC; i++, xdcp++) {
902843e1988Sjohnlev 		if (((xdcp->node_fe != NULL) &&
903843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_fe,
904843e1988Sjohnlev 		    strlen(xdcp->node_fe)) == 0)) ||
905843e1988Sjohnlev 		    ((xdcp->node_be != NULL) &&
906843e1988Sjohnlev 		    (strncmp(nodename, xdcp->node_be,
907843e1988Sjohnlev 		    strlen(xdcp->node_be)) == 0)))
908843e1988Sjohnlev 
909843e1988Sjohnlev 			return (xdcp->devclass);
910843e1988Sjohnlev 	}
911843e1988Sjohnlev 	return (XEN_INVAL);
912843e1988Sjohnlev }
913843e1988Sjohnlev 
914843e1988Sjohnlev int
915843e1988Sjohnlev xendev_devclass_ipl(xendev_devclass_t devclass)
916843e1988Sjohnlev {
917843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
918843e1988Sjohnlev 
919843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(devclass);
920843e1988Sjohnlev 	ASSERT(xdcp != NULL);
921843e1988Sjohnlev 
922843e1988Sjohnlev 	return (xdcp->xd_ipl);
923843e1988Sjohnlev }
924843e1988Sjohnlev 
925843e1988Sjohnlev /*
926843e1988Sjohnlev  * Determine if a devinfo instance exists of a particular device
927843e1988Sjohnlev  * class, domain and xenstore virtual device number.
928843e1988Sjohnlev  */
929843e1988Sjohnlev dev_info_t *
930843e1988Sjohnlev xvdi_find_dev(dev_info_t *parent, xendev_devclass_t devclass,
931843e1988Sjohnlev     domid_t dom, int vdev)
932843e1988Sjohnlev {
933843e1988Sjohnlev 	dev_info_t *dip;
934843e1988Sjohnlev 
935843e1988Sjohnlev 	ASSERT(DEVI_BUSY_OWNED(parent));
936843e1988Sjohnlev 
937843e1988Sjohnlev 	switch (devclass) {
938843e1988Sjohnlev 	case XEN_CONSOLE:
939843e1988Sjohnlev 	case XEN_XENBUS:
940843e1988Sjohnlev 	case XEN_DOMCAPS:
941843e1988Sjohnlev 	case XEN_BALLOON:
942843e1988Sjohnlev 	case XEN_EVTCHN:
943843e1988Sjohnlev 	case XEN_PRIVCMD:
944843e1988Sjohnlev 		/* Console and soft devices have no vdev. */
945*1ca30e39Sjohnlev 		vdev = VDEV_NOXS;
946843e1988Sjohnlev 		break;
947843e1988Sjohnlev 	default:
948843e1988Sjohnlev 		break;
949843e1988Sjohnlev 	}
950843e1988Sjohnlev 
951843e1988Sjohnlev 	for (dip = ddi_get_child(parent); dip != NULL;
952843e1988Sjohnlev 	    dip = ddi_get_next_sibling(dip)) {
953843e1988Sjohnlev 		int *vdevnump, *domidp, *devclsp, vdevnum;
954843e1988Sjohnlev 		uint_t ndomid, nvdevnum, ndevcls;
955843e1988Sjohnlev 		xendev_devclass_t devcls;
956843e1988Sjohnlev 		domid_t domid;
957843e1988Sjohnlev 		struct xendev_ppd *pdp = ddi_get_parent_data(dip);
958843e1988Sjohnlev 
959843e1988Sjohnlev 		if (pdp == NULL) {
960843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
961843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "domain", &domidp, &ndomid) !=
962843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
963843e1988Sjohnlev 				continue;
964843e1988Sjohnlev 			ASSERT(ndomid == 1);
965843e1988Sjohnlev 			domid = (domid_t)*domidp;
966843e1988Sjohnlev 			ddi_prop_free(domidp);
967843e1988Sjohnlev 
968843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
969843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "vdev", &vdevnump, &nvdevnum) !=
970843e1988Sjohnlev 			    DDI_PROP_SUCCESS)
971843e1988Sjohnlev 				continue;
972843e1988Sjohnlev 			ASSERT(nvdevnum == 1);
973843e1988Sjohnlev 			vdevnum = *vdevnump;
974843e1988Sjohnlev 			ddi_prop_free(vdevnump);
975843e1988Sjohnlev 
976843e1988Sjohnlev 			if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
977843e1988Sjohnlev 			    DDI_PROP_DONTPASS, "devclass", &devclsp,
978843e1988Sjohnlev 			    &ndevcls) != DDI_PROP_SUCCESS)
979843e1988Sjohnlev 				continue;
980843e1988Sjohnlev 			ASSERT(ndevcls == 1);
981843e1988Sjohnlev 			devcls = (xendev_devclass_t)*devclsp;
982843e1988Sjohnlev 			ddi_prop_free(devclsp);
983843e1988Sjohnlev 		} else {
984843e1988Sjohnlev 			domid = pdp->xd_domain;
985843e1988Sjohnlev 			vdevnum = pdp->xd_vdevnum;
986843e1988Sjohnlev 			devcls = pdp->xd_devclass;
987843e1988Sjohnlev 		}
988843e1988Sjohnlev 
989843e1988Sjohnlev 		if ((domid == dom) && (vdevnum == vdev) && (devcls == devclass))
990843e1988Sjohnlev 			return (dip);
991843e1988Sjohnlev 	}
992843e1988Sjohnlev 	return (NULL);
993843e1988Sjohnlev }
994843e1988Sjohnlev 
995843e1988Sjohnlev int
996843e1988Sjohnlev xvdi_get_evtchn(dev_info_t *xdip)
997843e1988Sjohnlev {
998843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
999843e1988Sjohnlev 
1000843e1988Sjohnlev 	ASSERT(pdp != NULL);
1001843e1988Sjohnlev 	return (pdp->xd_evtchn);
1002843e1988Sjohnlev }
1003843e1988Sjohnlev 
1004843e1988Sjohnlev int
1005843e1988Sjohnlev xvdi_get_vdevnum(dev_info_t *xdip)
1006843e1988Sjohnlev {
1007843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1008843e1988Sjohnlev 
1009843e1988Sjohnlev 	ASSERT(pdp != NULL);
1010843e1988Sjohnlev 	return (pdp->xd_vdevnum);
1011843e1988Sjohnlev }
1012843e1988Sjohnlev 
1013843e1988Sjohnlev char *
1014843e1988Sjohnlev xvdi_get_xsname(dev_info_t *xdip)
1015843e1988Sjohnlev {
1016843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1017843e1988Sjohnlev 
1018843e1988Sjohnlev 	ASSERT(pdp != NULL);
1019843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.nodename));
1020843e1988Sjohnlev }
1021843e1988Sjohnlev 
1022843e1988Sjohnlev char *
1023843e1988Sjohnlev xvdi_get_oename(dev_info_t *xdip)
1024843e1988Sjohnlev {
1025843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1026843e1988Sjohnlev 
1027843e1988Sjohnlev 	ASSERT(pdp != NULL);
1028843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1029843e1988Sjohnlev 		return (NULL);
1030843e1988Sjohnlev 	return ((char *)(pdp->xd_xsdev.otherend));
1031843e1988Sjohnlev }
1032843e1988Sjohnlev 
1033843e1988Sjohnlev struct xenbus_device *
1034843e1988Sjohnlev xvdi_get_xsd(dev_info_t *xdip)
1035843e1988Sjohnlev {
1036843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1037843e1988Sjohnlev 
1038843e1988Sjohnlev 	ASSERT(pdp != NULL);
1039843e1988Sjohnlev 	return (&pdp->xd_xsdev);
1040843e1988Sjohnlev }
1041843e1988Sjohnlev 
1042843e1988Sjohnlev domid_t
1043843e1988Sjohnlev xvdi_get_oeid(dev_info_t *xdip)
1044843e1988Sjohnlev {
1045843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(xdip);
1046843e1988Sjohnlev 
1047843e1988Sjohnlev 	ASSERT(pdp != NULL);
1048843e1988Sjohnlev 	if (pdp->xd_devclass == XEN_CONSOLE)
1049843e1988Sjohnlev 		return ((domid_t)-1);
1050843e1988Sjohnlev 	return ((domid_t)(pdp->xd_xsdev.otherend_id));
1051843e1988Sjohnlev }
1052843e1988Sjohnlev 
1053843e1988Sjohnlev void
1054843e1988Sjohnlev xvdi_dev_error(dev_info_t *dip, int errno, char *errstr)
1055843e1988Sjohnlev {
1056843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1057843e1988Sjohnlev 
1058843e1988Sjohnlev 	ASSERT(pdp != NULL);
1059843e1988Sjohnlev 	xenbus_dev_error(&pdp->xd_xsdev, errno, errstr);
1060843e1988Sjohnlev }
1061843e1988Sjohnlev 
1062843e1988Sjohnlev void
1063843e1988Sjohnlev xvdi_fatal_error(dev_info_t *dip, int errno, char *errstr)
1064843e1988Sjohnlev {
1065843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1066843e1988Sjohnlev 
1067843e1988Sjohnlev 	ASSERT(pdp != NULL);
1068843e1988Sjohnlev 	xenbus_dev_fatal(&pdp->xd_xsdev, errno, errstr);
1069843e1988Sjohnlev }
1070843e1988Sjohnlev 
1071843e1988Sjohnlev static void
1072843e1988Sjohnlev i_xvdi_oestate_handler(void *arg)
1073843e1988Sjohnlev {
1074843e1988Sjohnlev 	dev_info_t *dip = arg;
1075843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1076843e1988Sjohnlev 	XenbusState oestate = pdp->xd_xsdev.otherend_state;
1077843e1988Sjohnlev 	ddi_eventcookie_t evc;
1078843e1988Sjohnlev 
1079843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1080843e1988Sjohnlev 
1081843e1988Sjohnlev 	if (pdp->xd_oe_ehid != NULL) {
1082843e1988Sjohnlev 		/* send notification to driver */
1083843e1988Sjohnlev 		if (ddi_get_eventcookie(dip, XS_OE_STATE,
1084843e1988Sjohnlev 		    &evc) == DDI_SUCCESS) {
1085843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
1086843e1988Sjohnlev 			(void) ndi_post_event(dip, dip, evc, &oestate);
1087843e1988Sjohnlev 			mutex_enter(&pdp->xd_lk);
1088843e1988Sjohnlev 		}
1089843e1988Sjohnlev 	} else {
1090843e1988Sjohnlev 		/*
1091843e1988Sjohnlev 		 * take default action, if driver hasn't registered its
1092843e1988Sjohnlev 		 * event handler yet
1093843e1988Sjohnlev 		 */
1094843e1988Sjohnlev 		if (oestate == XenbusStateClosing) {
1095843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1096843e1988Sjohnlev 			    XenbusStateClosed);
1097843e1988Sjohnlev 		} else if (oestate == XenbusStateClosed) {
1098843e1988Sjohnlev 			(void) xvdi_switch_state(dip, XBT_NULL,
1099843e1988Sjohnlev 			    XenbusStateClosed);
1100843e1988Sjohnlev 			(void) xvdi_post_event(dip, XEN_HP_REMOVE);
1101843e1988Sjohnlev 		}
1102843e1988Sjohnlev 	}
1103843e1988Sjohnlev 
1104843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1105843e1988Sjohnlev 
1106843e1988Sjohnlev 	/*
1107843e1988Sjohnlev 	 * We'll try to remove the devinfo node of this device if the
1108843e1988Sjohnlev 	 * other end has closed.
1109843e1988Sjohnlev 	 */
1110843e1988Sjohnlev 	if (oestate == XenbusStateClosed)
1111843e1988Sjohnlev 		(void) ddi_taskq_dispatch(DEVI(ddi_get_parent(dip))->devi_taskq,
1112843e1988Sjohnlev 		    xendev_offline_device, dip, DDI_SLEEP);
1113843e1988Sjohnlev }
1114843e1988Sjohnlev 
1115843e1988Sjohnlev static void
1116843e1988Sjohnlev i_xvdi_hpstate_handler(void *arg)
1117843e1988Sjohnlev {
1118843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
1119843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1120843e1988Sjohnlev 	ddi_eventcookie_t evc;
1121843e1988Sjohnlev 	char *hp_status;
1122843e1988Sjohnlev 	unsigned int hpl;
1123843e1988Sjohnlev 
1124843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1125843e1988Sjohnlev 	if ((ddi_get_eventcookie(dip, XS_HP_STATE, &evc) == DDI_SUCCESS) &&
1126843e1988Sjohnlev 	    (xenbus_read(XBT_NULL, pdp->xd_hp_watch.node, "",
1127843e1988Sjohnlev 	    (void *)&hp_status, &hpl) == 0)) {
1128843e1988Sjohnlev 
1129843e1988Sjohnlev 		xendev_hotplug_state_t new_state = Unrecognized;
1130843e1988Sjohnlev 
1131843e1988Sjohnlev 		if (strcmp(hp_status, "connected") == 0)
1132843e1988Sjohnlev 			new_state = Connected;
1133843e1988Sjohnlev 
1134843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1135843e1988Sjohnlev 
1136843e1988Sjohnlev 		(void) ndi_post_event(dip, dip, evc, &new_state);
1137843e1988Sjohnlev 		kmem_free(hp_status, hpl);
1138843e1988Sjohnlev 		return;
1139843e1988Sjohnlev 	}
1140843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1141843e1988Sjohnlev }
1142843e1988Sjohnlev 
1143843e1988Sjohnlev void
1144843e1988Sjohnlev xvdi_notify_oe(dev_info_t *dip)
1145843e1988Sjohnlev {
1146843e1988Sjohnlev 	struct xendev_ppd *pdp;
1147843e1988Sjohnlev 
1148843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1149843e1988Sjohnlev 	ASSERT(pdp->xd_evtchn != INVALID_EVTCHN);
1150843e1988Sjohnlev 	ec_notify_via_evtchn(pdp->xd_evtchn);
1151843e1988Sjohnlev }
1152843e1988Sjohnlev 
1153843e1988Sjohnlev static void
1154843e1988Sjohnlev i_xvdi_bepath_cb(struct xenbus_watch *w, const char **vec, unsigned int len)
1155843e1988Sjohnlev {
1156843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
1157843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1158843e1988Sjohnlev 	char *be = NULL;
1159843e1988Sjohnlev 	unsigned int bel;
1160843e1988Sjohnlev 
1161843e1988Sjohnlev 	ASSERT(len > XS_WATCH_PATH);
1162843e1988Sjohnlev 	ASSERT(vec[XS_WATCH_PATH] != NULL);
1163843e1988Sjohnlev 
1164843e1988Sjohnlev 	/*
1165843e1988Sjohnlev 	 * If the backend is not the same as that we already stored,
1166843e1988Sjohnlev 	 * re-set our watch for its' state.
1167843e1988Sjohnlev 	 */
1168843e1988Sjohnlev 	if ((xenbus_read(XBT_NULL, "", vec[XS_WATCH_PATH], (void *)be, &bel)
1169843e1988Sjohnlev 	    == 0) && (strcmp(be, pdp->xd_xsdev.otherend) != 0))
1170843e1988Sjohnlev 		(void) i_xvdi_add_watch_oestate(dip);
1171843e1988Sjohnlev 
1172843e1988Sjohnlev 	if (be != NULL) {
1173843e1988Sjohnlev 		ASSERT(bel > 0);
1174843e1988Sjohnlev 		kmem_free(be, bel);
1175843e1988Sjohnlev 	}
1176843e1988Sjohnlev }
1177843e1988Sjohnlev 
1178843e1988Sjohnlev static int
1179843e1988Sjohnlev i_xvdi_add_watch_oestate(dev_info_t *dip)
1180843e1988Sjohnlev {
1181843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1182843e1988Sjohnlev 
1183843e1988Sjohnlev 	ASSERT(pdp != NULL);
1184843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.nodename != NULL);
1185843e1988Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
1186843e1988Sjohnlev 
1187843e1988Sjohnlev 	/*
1188843e1988Sjohnlev 	 * Create taskq for delivering other end state change event to
1189843e1988Sjohnlev 	 * this device later.
1190843e1988Sjohnlev 	 *
1191843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1192843e1988Sjohnlev 	 * in order.
1193843e1988Sjohnlev 	 *
1194843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1195843e1988Sjohnlev 	 * xenstore change under the path that it is watching. If two
1196843e1988Sjohnlev 	 * changes happen consecutively in a very short amount of
1197843e1988Sjohnlev 	 * time, it is likely that the driver will see only the last
1198843e1988Sjohnlev 	 * one.
1199843e1988Sjohnlev 	 */
1200843e1988Sjohnlev 	if (pdp->xd_oe_taskq == NULL)
1201843e1988Sjohnlev 		if ((pdp->xd_oe_taskq = ddi_taskq_create(dip,
1202843e1988Sjohnlev 		    "xendev_oe_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1203843e1988Sjohnlev 			return (DDI_FAILURE);
1204843e1988Sjohnlev 
1205843e1988Sjohnlev 	/*
1206843e1988Sjohnlev 	 * Watch for changes to the XenbusState of otherend.
1207843e1988Sjohnlev 	 */
1208843e1988Sjohnlev 	pdp->xd_xsdev.otherend_state = XenbusStateUnknown;
1209843e1988Sjohnlev 	pdp->xd_xsdev.otherend_changed = i_xvdi_oestate_cb;
1210843e1988Sjohnlev 
1211843e1988Sjohnlev 	if (talk_to_otherend(&pdp->xd_xsdev) != 0) {
1212843e1988Sjohnlev 		i_xvdi_rem_watch_oestate(dip);
1213843e1988Sjohnlev 		return (DDI_FAILURE);
1214843e1988Sjohnlev 	}
1215843e1988Sjohnlev 
1216843e1988Sjohnlev 	return (DDI_SUCCESS);
1217843e1988Sjohnlev }
1218843e1988Sjohnlev 
1219843e1988Sjohnlev static void
1220843e1988Sjohnlev i_xvdi_rem_watch_oestate(dev_info_t *dip)
1221843e1988Sjohnlev {
1222843e1988Sjohnlev 	struct xendev_ppd *pdp;
1223843e1988Sjohnlev 	struct xenbus_device *dev;
1224843e1988Sjohnlev 
1225843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1226843e1988Sjohnlev 	ASSERT(pdp != NULL);
1227843e1988Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
1228843e1988Sjohnlev 
1229843e1988Sjohnlev 	dev = &pdp->xd_xsdev;
1230843e1988Sjohnlev 
1231843e1988Sjohnlev 	/* Unwatch for changes to XenbusState of otherend */
1232843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL) {
1233843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1234843e1988Sjohnlev 		unregister_xenbus_watch(&dev->otherend_watch);
1235843e1988Sjohnlev 		mutex_enter(&pdp->xd_lk);
1236843e1988Sjohnlev 	}
1237843e1988Sjohnlev 
1238843e1988Sjohnlev 	/* make sure no event handler is running */
1239843e1988Sjohnlev 	if (pdp->xd_oe_taskq != NULL) {
1240843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1241843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_oe_taskq);
1242843e1988Sjohnlev 		mutex_enter(&pdp->xd_lk);
1243843e1988Sjohnlev 		pdp->xd_oe_taskq = NULL;
1244843e1988Sjohnlev 	}
1245843e1988Sjohnlev 
1246843e1988Sjohnlev 	/* clean up */
1247843e1988Sjohnlev 	dev->otherend_state = XenbusStateUnknown;
1248843e1988Sjohnlev 	dev->otherend_id = (domid_t)-1;
1249843e1988Sjohnlev 	if (dev->otherend_watch.node != NULL)
1250843e1988Sjohnlev 		kmem_free((void *)dev->otherend_watch.node,
1251843e1988Sjohnlev 		    strlen(dev->otherend_watch.node) + 1);
1252843e1988Sjohnlev 	dev->otherend_watch.node = NULL;
1253843e1988Sjohnlev 	if (dev->otherend != NULL)
1254843e1988Sjohnlev 		kmem_free((void *)dev->otherend, strlen(dev->otherend) + 1);
1255843e1988Sjohnlev 	dev->otherend = NULL;
1256843e1988Sjohnlev }
1257843e1988Sjohnlev 
1258843e1988Sjohnlev static int
1259843e1988Sjohnlev i_xvdi_add_watch_hpstate(dev_info_t *dip)
1260843e1988Sjohnlev {
1261843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1262843e1988Sjohnlev 
1263843e1988Sjohnlev 	ASSERT(pdp != NULL);
1264843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
1265843e1988Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
1266843e1988Sjohnlev 
1267843e1988Sjohnlev 	/*
1268843e1988Sjohnlev 	 * Create taskq for delivering hotplug status change event to
1269843e1988Sjohnlev 	 * this device later.
1270843e1988Sjohnlev 	 *
1271843e1988Sjohnlev 	 * Set nthreads to 1 to make sure that events can be delivered
1272843e1988Sjohnlev 	 * in order.
1273843e1988Sjohnlev 	 *
1274843e1988Sjohnlev 	 * Note: It is _not_ guaranteed that driver can see every
1275843e1988Sjohnlev 	 * hotplug status change under the path that it is
1276843e1988Sjohnlev 	 * watching. If two changes happen consecutively in a very
1277843e1988Sjohnlev 	 * short amount of time, it is likely that the driver only
1278843e1988Sjohnlev 	 * sees the last one.
1279843e1988Sjohnlev 	 */
1280843e1988Sjohnlev 	if (pdp->xd_hp_taskq == NULL)
1281843e1988Sjohnlev 		if ((pdp->xd_hp_taskq = ddi_taskq_create(dip,
1282843e1988Sjohnlev 		    "xendev_hp_taskq", 1, TASKQ_DEFAULTPRI, 0)) == NULL)
1283843e1988Sjohnlev 			return (DDI_FAILURE);
1284843e1988Sjohnlev 
1285843e1988Sjohnlev 	if (pdp->xd_hp_watch.node == NULL) {
1286843e1988Sjohnlev 		size_t len;
1287843e1988Sjohnlev 		char *path;
1288843e1988Sjohnlev 
1289843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1290843e1988Sjohnlev 
1291843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) +
1292843e1988Sjohnlev 		    strlen("/hotplug-status") + 1;
1293843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1294843e1988Sjohnlev 		(void) snprintf(path, len, "%s/hotplug-status",
1295843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1296843e1988Sjohnlev 
1297843e1988Sjohnlev 		pdp->xd_hp_watch.node = path;
1298843e1988Sjohnlev 		pdp->xd_hp_watch.callback = i_xvdi_hpstate_cb;
1299843e1988Sjohnlev 		pdp->xd_hp_watch.dev = (struct xenbus_device *)dip; /* yuck! */
1300843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_hp_watch) != 0) {
1301843e1988Sjohnlev 			i_xvdi_rem_watch_hpstate(dip);
1302843e1988Sjohnlev 			return (DDI_FAILURE);
1303843e1988Sjohnlev 		}
1304843e1988Sjohnlev 	}
1305843e1988Sjohnlev 
1306843e1988Sjohnlev 	return (DDI_SUCCESS);
1307843e1988Sjohnlev }
1308843e1988Sjohnlev 
1309843e1988Sjohnlev static void
1310843e1988Sjohnlev i_xvdi_rem_watch_hpstate(dev_info_t *dip)
1311843e1988Sjohnlev {
1312843e1988Sjohnlev 	struct xendev_ppd *pdp;
1313843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1314843e1988Sjohnlev 
1315843e1988Sjohnlev 	ASSERT(pdp != NULL);
1316843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 0);
1317843e1988Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
1318843e1988Sjohnlev 
1319843e1988Sjohnlev 	/* Unwatch for changes to "hotplug-status" node for backend device. */
1320843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
1321843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1322843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_hp_watch);
1323843e1988Sjohnlev 		mutex_enter(&pdp->xd_lk);
1324843e1988Sjohnlev 	}
1325843e1988Sjohnlev 
1326843e1988Sjohnlev 	/* Make sure no event handler is running. */
1327843e1988Sjohnlev 	if (pdp->xd_hp_taskq != NULL) {
1328843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1329843e1988Sjohnlev 		ddi_taskq_destroy(pdp->xd_hp_taskq);
1330843e1988Sjohnlev 		mutex_enter(&pdp->xd_lk);
1331843e1988Sjohnlev 		pdp->xd_hp_taskq = NULL;
1332843e1988Sjohnlev 	}
1333843e1988Sjohnlev 
1334843e1988Sjohnlev 	/* Clean up. */
1335843e1988Sjohnlev 	if (pdp->xd_hp_watch.node != NULL) {
1336843e1988Sjohnlev 		kmem_free((void *)pdp->xd_hp_watch.node,
1337843e1988Sjohnlev 		    strlen(pdp->xd_hp_watch.node) + 1);
1338843e1988Sjohnlev 		pdp->xd_hp_watch.node = NULL;
1339843e1988Sjohnlev 	}
1340843e1988Sjohnlev }
1341843e1988Sjohnlev 
1342843e1988Sjohnlev static int
1343843e1988Sjohnlev i_xvdi_add_watches(dev_info_t *dip)
1344843e1988Sjohnlev {
1345843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1346843e1988Sjohnlev 
1347843e1988Sjohnlev 	ASSERT(pdp != NULL);
1348843e1988Sjohnlev 
1349843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1350843e1988Sjohnlev 
1351843e1988Sjohnlev 	if (i_xvdi_add_watch_oestate(dip) != DDI_SUCCESS) {
1352843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1353843e1988Sjohnlev 		return (DDI_FAILURE);
1354843e1988Sjohnlev 	}
1355843e1988Sjohnlev 
1356843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1) {
1357843e1988Sjohnlev 		/*
1358843e1988Sjohnlev 		 * Frontend devices must watch for the backend path
1359843e1988Sjohnlev 		 * changing.
1360843e1988Sjohnlev 		 */
1361843e1988Sjohnlev 		if (i_xvdi_add_watch_bepath(dip) != DDI_SUCCESS)
1362843e1988Sjohnlev 			goto unwatch_and_fail;
1363843e1988Sjohnlev 	} else {
1364843e1988Sjohnlev 		/*
1365843e1988Sjohnlev 		 * Backend devices must watch for hotplug events.
1366843e1988Sjohnlev 		 */
1367843e1988Sjohnlev 		if (i_xvdi_add_watch_hpstate(dip) != DDI_SUCCESS)
1368843e1988Sjohnlev 			goto unwatch_and_fail;
1369843e1988Sjohnlev 	}
1370843e1988Sjohnlev 
1371843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1372843e1988Sjohnlev 
1373843e1988Sjohnlev 	return (DDI_SUCCESS);
1374843e1988Sjohnlev 
1375843e1988Sjohnlev unwatch_and_fail:
1376843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
1377843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1378843e1988Sjohnlev 
1379843e1988Sjohnlev 	return (DDI_FAILURE);
1380843e1988Sjohnlev }
1381843e1988Sjohnlev 
1382843e1988Sjohnlev static void
1383843e1988Sjohnlev i_xvdi_rem_watches(dev_info_t *dip)
1384843e1988Sjohnlev {
1385843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1386843e1988Sjohnlev 
1387843e1988Sjohnlev 	ASSERT(pdp != NULL);
1388843e1988Sjohnlev 
1389843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1390843e1988Sjohnlev 
1391843e1988Sjohnlev 	i_xvdi_rem_watch_oestate(dip);
1392843e1988Sjohnlev 
1393843e1988Sjohnlev 	if (pdp->xd_xsdev.frontend == 1)
1394843e1988Sjohnlev 		i_xvdi_rem_watch_bepath(dip);
1395843e1988Sjohnlev 	else
1396843e1988Sjohnlev 		i_xvdi_rem_watch_hpstate(dip);
1397843e1988Sjohnlev 
1398843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1399843e1988Sjohnlev }
1400843e1988Sjohnlev 
1401843e1988Sjohnlev static int
1402843e1988Sjohnlev i_xvdi_add_watch_bepath(dev_info_t *dip)
1403843e1988Sjohnlev {
1404843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1405843e1988Sjohnlev 
1406843e1988Sjohnlev 	ASSERT(pdp != NULL);
1407843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
1408843e1988Sjohnlev 
1409843e1988Sjohnlev 	/*
1410843e1988Sjohnlev 	 * Frontend devices need to watch for the backend path changing.
1411843e1988Sjohnlev 	 */
1412843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node == NULL) {
1413843e1988Sjohnlev 		size_t len;
1414843e1988Sjohnlev 		char *path;
1415843e1988Sjohnlev 
1416843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.nodename != NULL);
1417843e1988Sjohnlev 
1418843e1988Sjohnlev 		len = strlen(pdp->xd_xsdev.nodename) + strlen("/backend") + 1;
1419843e1988Sjohnlev 		path = kmem_alloc(len, KM_SLEEP);
1420843e1988Sjohnlev 		(void) snprintf(path, len, "%s/backend",
1421843e1988Sjohnlev 		    pdp->xd_xsdev.nodename);
1422843e1988Sjohnlev 
1423843e1988Sjohnlev 		pdp->xd_bepath_watch.node = path;
1424843e1988Sjohnlev 		pdp->xd_bepath_watch.callback = i_xvdi_bepath_cb;
1425843e1988Sjohnlev 		pdp->xd_bepath_watch.dev = (struct xenbus_device *)dip;
1426843e1988Sjohnlev 		if (register_xenbus_watch(&pdp->xd_bepath_watch) != 0) {
1427843e1988Sjohnlev 			kmem_free(path, len);
1428843e1988Sjohnlev 			pdp->xd_bepath_watch.node = NULL;
1429843e1988Sjohnlev 			return (DDI_FAILURE);
1430843e1988Sjohnlev 		}
1431843e1988Sjohnlev 	}
1432843e1988Sjohnlev 
1433843e1988Sjohnlev 	return (DDI_SUCCESS);
1434843e1988Sjohnlev }
1435843e1988Sjohnlev 
1436843e1988Sjohnlev static void
1437843e1988Sjohnlev i_xvdi_rem_watch_bepath(dev_info_t *dip)
1438843e1988Sjohnlev {
1439843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1440843e1988Sjohnlev 
1441843e1988Sjohnlev 	ASSERT(pdp != NULL);
1442843e1988Sjohnlev 	ASSERT(pdp->xd_xsdev.frontend == 1);
1443843e1988Sjohnlev 	ASSERT(mutex_owned(&pdp->xd_lk));
1444843e1988Sjohnlev 
1445843e1988Sjohnlev 	if (pdp->xd_bepath_watch.node != NULL) {
1446843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1447843e1988Sjohnlev 		unregister_xenbus_watch(&pdp->xd_bepath_watch);
1448843e1988Sjohnlev 		mutex_enter(&pdp->xd_lk);
1449843e1988Sjohnlev 
1450843e1988Sjohnlev 		kmem_free((void *)(pdp->xd_bepath_watch.node),
1451843e1988Sjohnlev 		    strlen(pdp->xd_bepath_watch.node) + 1);
1452843e1988Sjohnlev 		pdp->xd_bepath_watch.node = NULL;
1453843e1988Sjohnlev 	}
1454843e1988Sjohnlev }
1455843e1988Sjohnlev 
1456843e1988Sjohnlev int
1457843e1988Sjohnlev xvdi_switch_state(dev_info_t *dip, xenbus_transaction_t xbt,
1458843e1988Sjohnlev     XenbusState newState)
1459843e1988Sjohnlev {
1460843e1988Sjohnlev 	int rv;
1461843e1988Sjohnlev 	struct xendev_ppd *pdp;
1462843e1988Sjohnlev 
1463843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1464843e1988Sjohnlev 	ASSERT(pdp != NULL);
1465843e1988Sjohnlev 
1466843e1988Sjohnlev 	XVDI_DPRINTF(XVDI_DBG_STATE,
1467843e1988Sjohnlev 	    "xvdi_switch_state: dip 0x%p moves to %d",
1468843e1988Sjohnlev 	    (void *)dip, newState);
1469843e1988Sjohnlev 
1470843e1988Sjohnlev 	rv = xenbus_switch_state(&pdp->xd_xsdev, xbt, newState);
1471843e1988Sjohnlev 	if (rv > 0)
1472843e1988Sjohnlev 		cmn_err(CE_WARN, "xvdi_switch_state: change state failed");
1473843e1988Sjohnlev 
1474843e1988Sjohnlev 	return (rv);
1475843e1988Sjohnlev }
1476843e1988Sjohnlev 
1477843e1988Sjohnlev /*
1478843e1988Sjohnlev  * Notify hotplug script running in userland
1479843e1988Sjohnlev  */
1480843e1988Sjohnlev int
1481843e1988Sjohnlev xvdi_post_event(dev_info_t *dip, xendev_hotplug_cmd_t hpc)
1482843e1988Sjohnlev {
1483843e1988Sjohnlev 	struct xendev_ppd *pdp;
1484843e1988Sjohnlev 	nvlist_t *attr_list = NULL;
1485843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
1486843e1988Sjohnlev 	sysevent_id_t eid;
1487843e1988Sjohnlev 	int err;
1488843e1988Sjohnlev 	char devname[256]; /* XXPV dme: ? */
1489843e1988Sjohnlev 
1490843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1491843e1988Sjohnlev 	ASSERT(pdp != NULL);
1492843e1988Sjohnlev 
1493843e1988Sjohnlev 	xdcp = i_xvdi_devclass2cfg(pdp->xd_devclass);
1494843e1988Sjohnlev 	ASSERT(xdcp != NULL);
1495843e1988Sjohnlev 
1496843e1988Sjohnlev 	(void) snprintf(devname, sizeof (devname) - 1, "%s%d",
1497843e1988Sjohnlev 	    ddi_driver_name(dip),  ddi_get_instance(dip));
1498843e1988Sjohnlev 
1499843e1988Sjohnlev 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME, KM_NOSLEEP);
1500843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1501843e1988Sjohnlev 		goto failure;
1502843e1988Sjohnlev 
1503843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "domain", pdp->xd_domain);
1504843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1505843e1988Sjohnlev 		goto failure;
1506843e1988Sjohnlev 	err = nvlist_add_int32(attr_list, "vdev", pdp->xd_vdevnum);
1507843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1508843e1988Sjohnlev 		goto failure;
1509843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "devclass", xdcp->xsdev);
1510843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1511843e1988Sjohnlev 		goto failure;
1512843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "device", devname);
1513843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1514843e1988Sjohnlev 		goto failure;
1515843e1988Sjohnlev 	err = nvlist_add_string(attr_list, "fob",
1516843e1988Sjohnlev 	    ((pdp->xd_xsdev.frontend == 1) ? "frontend" : "backend"));
1517843e1988Sjohnlev 	if (err != DDI_SUCCESS)
1518843e1988Sjohnlev 		goto failure;
1519843e1988Sjohnlev 
1520843e1988Sjohnlev 	switch (hpc) {
1521843e1988Sjohnlev 	case XEN_HP_ADD:
1522843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1523843e1988Sjohnlev 		    "add", attr_list, &eid, DDI_NOSLEEP);
1524843e1988Sjohnlev 		break;
1525843e1988Sjohnlev 	case XEN_HP_REMOVE:
1526843e1988Sjohnlev 		err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, "EC_xendev",
1527843e1988Sjohnlev 		    "remove", attr_list, &eid, DDI_NOSLEEP);
1528843e1988Sjohnlev 		break;
1529843e1988Sjohnlev 	default:
1530843e1988Sjohnlev 		err = DDI_FAILURE;
1531843e1988Sjohnlev 		goto failure;
1532843e1988Sjohnlev 	}
1533843e1988Sjohnlev 
1534843e1988Sjohnlev failure:
1535843e1988Sjohnlev 	if (attr_list != NULL)
1536843e1988Sjohnlev 		nvlist_free(attr_list);
1537843e1988Sjohnlev 
1538843e1988Sjohnlev 	return (err);
1539843e1988Sjohnlev }
1540843e1988Sjohnlev 
1541843e1988Sjohnlev /* ARGSUSED */
1542843e1988Sjohnlev static void
1543843e1988Sjohnlev i_xvdi_probe_path_cb(struct xenbus_watch *w, const char **vec,
1544843e1988Sjohnlev     unsigned int len)
1545843e1988Sjohnlev {
1546843e1988Sjohnlev 	char *path;
1547843e1988Sjohnlev 
1548843e1988Sjohnlev 	if (xendev_dip == NULL)
1549843e1988Sjohnlev 		xendev_dip = ddi_find_devinfo("xpvd", -1, 0);
1550843e1988Sjohnlev 
1551843e1988Sjohnlev 	path = i_ddi_strdup((char *)vec[XS_WATCH_PATH], KM_SLEEP);
1552843e1988Sjohnlev 
1553843e1988Sjohnlev 	(void) ddi_taskq_dispatch(DEVI(xendev_dip)->devi_taskq,
1554843e1988Sjohnlev 	    i_xvdi_probe_path_handler, (void *)path, DDI_SLEEP);
1555843e1988Sjohnlev }
1556843e1988Sjohnlev 
1557843e1988Sjohnlev static void
1558843e1988Sjohnlev i_xvdi_watch_device(char *path)
1559843e1988Sjohnlev {
1560843e1988Sjohnlev 	struct xenbus_watch *w;
1561843e1988Sjohnlev 
1562843e1988Sjohnlev 	ASSERT(path != NULL);
1563843e1988Sjohnlev 
1564843e1988Sjohnlev 	w = kmem_zalloc(sizeof (*w), KM_SLEEP);
1565843e1988Sjohnlev 	w->node = path;
1566843e1988Sjohnlev 	w->callback = &i_xvdi_probe_path_cb;
1567843e1988Sjohnlev 	w->dev = NULL;
1568843e1988Sjohnlev 
1569843e1988Sjohnlev 	if (register_xenbus_watch(w) != 0) {
1570843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_watch_device: "
1571843e1988Sjohnlev 		    "cannot set watch on %s", path);
1572843e1988Sjohnlev 		kmem_free(w, sizeof (*w));
1573843e1988Sjohnlev 		return;
1574843e1988Sjohnlev 	}
1575843e1988Sjohnlev }
1576843e1988Sjohnlev 
1577843e1988Sjohnlev void
1578843e1988Sjohnlev xvdi_watch_devices(int newstate)
1579843e1988Sjohnlev {
1580843e1988Sjohnlev 	int devclass;
1581843e1988Sjohnlev 
1582843e1988Sjohnlev 	/*
1583843e1988Sjohnlev 	 * Watch for devices being created in the store.
1584843e1988Sjohnlev 	 */
1585843e1988Sjohnlev 	if (newstate == XENSTORE_DOWN)
1586843e1988Sjohnlev 		return;
1587843e1988Sjohnlev 	for (devclass = 0; devclass < NXDC; devclass++) {
1588843e1988Sjohnlev 		if (xdci[devclass].xs_path_fe != NULL)
1589843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_fe);
1590843e1988Sjohnlev 		if (xdci[devclass].xs_path_be != NULL)
1591843e1988Sjohnlev 			i_xvdi_watch_device(xdci[devclass].xs_path_be);
1592843e1988Sjohnlev 	}
1593843e1988Sjohnlev }
1594843e1988Sjohnlev 
1595843e1988Sjohnlev /*
1596843e1988Sjohnlev  * Iterate over the store looking for backend devices to create.
1597843e1988Sjohnlev  */
1598843e1988Sjohnlev static void
1599843e1988Sjohnlev i_xvdi_enum_be(dev_info_t *parent, i_xd_cfg_t *xdcp)
1600843e1988Sjohnlev {
1601843e1988Sjohnlev 	char **domains;
1602843e1988Sjohnlev 	unsigned int ndomains;
1603843e1988Sjohnlev 	int ldomains, i;
1604843e1988Sjohnlev 
1605843e1988Sjohnlev 	if ((domains = xenbus_directory(XBT_NULL, xdcp->xs_path_be, "",
1606843e1988Sjohnlev 	    &ndomains)) == NULL)
1607843e1988Sjohnlev 		return;
1608843e1988Sjohnlev 
1609843e1988Sjohnlev 	for (i = 0, ldomains = 0; i < ndomains; i++) {
1610843e1988Sjohnlev 		ldomains += strlen(domains[i]) + 1 + sizeof (char *);
1611843e1988Sjohnlev 
1612843e1988Sjohnlev 		i_xvdi_enum_worker(parent, xdcp, domains[i]);
1613843e1988Sjohnlev 	}
1614843e1988Sjohnlev 	kmem_free(domains, ldomains);
1615843e1988Sjohnlev }
1616843e1988Sjohnlev 
1617843e1988Sjohnlev /*
1618843e1988Sjohnlev  * Iterate over the store looking for frontend devices to create.
1619843e1988Sjohnlev  */
1620843e1988Sjohnlev static void
1621843e1988Sjohnlev i_xvdi_enum_fe(dev_info_t *parent, i_xd_cfg_t *xdcp)
1622843e1988Sjohnlev {
1623843e1988Sjohnlev 	i_xvdi_enum_worker(parent, xdcp, NULL);
1624843e1988Sjohnlev }
1625843e1988Sjohnlev 
1626843e1988Sjohnlev static void
1627843e1988Sjohnlev i_xvdi_enum_worker(dev_info_t *parent, i_xd_cfg_t *xdcp,
1628843e1988Sjohnlev     char *domain)
1629843e1988Sjohnlev {
1630843e1988Sjohnlev 	char *path, *domain_path, *ep;
1631843e1988Sjohnlev 	char **devices;
1632843e1988Sjohnlev 	unsigned int ndevices;
1633843e1988Sjohnlev 	int ldevices, j, circ;
1634843e1988Sjohnlev 	domid_t dom;
1635843e1988Sjohnlev 
1636843e1988Sjohnlev 	if (domain == NULL) {
1637843e1988Sjohnlev 		dom = DOMID_SELF;
1638843e1988Sjohnlev 		path = xdcp->xs_path_fe;
1639843e1988Sjohnlev 		domain_path = "";
1640843e1988Sjohnlev 	} else {
1641843e1988Sjohnlev 		(void) ddi_strtol(domain, &ep, 0, (long *)&dom);
1642843e1988Sjohnlev 		path = xdcp->xs_path_be;
1643843e1988Sjohnlev 		domain_path = domain;
1644843e1988Sjohnlev 	}
1645843e1988Sjohnlev 
1646843e1988Sjohnlev 	if ((devices = xenbus_directory(XBT_NULL, path, domain_path,
1647843e1988Sjohnlev 	    &ndevices)) == NULL)
1648843e1988Sjohnlev 		return;
1649843e1988Sjohnlev 
1650843e1988Sjohnlev 	for (j = 0, ldevices = 0; j < ndevices; j++) {
1651843e1988Sjohnlev 		int vdev;
1652843e1988Sjohnlev 
1653843e1988Sjohnlev 		ldevices += strlen(devices[j]) + 1 + sizeof (char *);
1654843e1988Sjohnlev 		(void) ddi_strtol(devices[j], &ep, 0, (long *)&vdev);
1655843e1988Sjohnlev 
1656843e1988Sjohnlev 		ndi_devi_enter(parent, &circ);
1657843e1988Sjohnlev 
1658843e1988Sjohnlev 		if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev)
1659843e1988Sjohnlev 		    == NULL)
1660843e1988Sjohnlev 			(void) xvdi_create_dev(parent, xdcp->devclass,
1661843e1988Sjohnlev 			    dom, vdev);
1662843e1988Sjohnlev 
1663843e1988Sjohnlev 		ndi_devi_exit(parent, circ);
1664843e1988Sjohnlev 	}
1665843e1988Sjohnlev 	kmem_free(devices, ldevices);
1666843e1988Sjohnlev }
1667843e1988Sjohnlev 
1668843e1988Sjohnlev /*
1669843e1988Sjohnlev  * Leaf drivers should call this in their detach() routine during suspend.
1670843e1988Sjohnlev  */
1671843e1988Sjohnlev void
1672843e1988Sjohnlev xvdi_suspend(dev_info_t *dip)
1673843e1988Sjohnlev {
1674843e1988Sjohnlev 	i_xvdi_rem_watches(dip);
1675843e1988Sjohnlev }
1676843e1988Sjohnlev 
1677843e1988Sjohnlev /*
1678843e1988Sjohnlev  * Leaf drivers should call this in their attach() routine during resume.
1679843e1988Sjohnlev  */
1680843e1988Sjohnlev int
1681843e1988Sjohnlev xvdi_resume(dev_info_t *dip)
1682843e1988Sjohnlev {
1683843e1988Sjohnlev 	return (i_xvdi_add_watches(dip));
1684843e1988Sjohnlev }
1685843e1988Sjohnlev 
1686843e1988Sjohnlev /*
1687843e1988Sjohnlev  * Add event handler for the leaf driver
1688843e1988Sjohnlev  * to handle event triggered by the change in xenstore
1689843e1988Sjohnlev  */
1690843e1988Sjohnlev int
1691843e1988Sjohnlev xvdi_add_event_handler(dev_info_t *dip, char *name,
1692843e1988Sjohnlev     void (*evthandler)(dev_info_t *, ddi_eventcookie_t, void *, void *))
1693843e1988Sjohnlev {
1694843e1988Sjohnlev 	ddi_eventcookie_t ecv;
1695843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
1696843e1988Sjohnlev 	ddi_callback_id_t *cbid;
1697843e1988Sjohnlev 
1698843e1988Sjohnlev 	ASSERT(pdp != NULL);
1699843e1988Sjohnlev 
1700843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1701843e1988Sjohnlev 
1702843e1988Sjohnlev 	if (strcmp(name, XS_OE_STATE) == 0) {
1703843e1988Sjohnlev 		ASSERT(pdp->xd_xsdev.otherend != NULL);
1704843e1988Sjohnlev 
1705843e1988Sjohnlev 		cbid = &pdp->xd_oe_ehid;
1706843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
1707843e1988Sjohnlev 		if (pdp->xd_xsdev.frontend == 1) {
1708843e1988Sjohnlev 			mutex_exit(&pdp->xd_lk);
1709843e1988Sjohnlev 			return (DDI_FAILURE);
1710843e1988Sjohnlev 		}
1711843e1988Sjohnlev 
1712843e1988Sjohnlev 		ASSERT(pdp->xd_hp_watch.node != NULL);
1713843e1988Sjohnlev 
1714843e1988Sjohnlev 		cbid = &pdp->xd_hp_ehid;
1715843e1988Sjohnlev 	} else {
1716843e1988Sjohnlev 		/* Unsupported watch. */
1717843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1718843e1988Sjohnlev 		return (DDI_FAILURE);
1719843e1988Sjohnlev 	}
1720843e1988Sjohnlev 
1721843e1988Sjohnlev 	/*
1722843e1988Sjohnlev 	 * No event handler provided, take default action to handle
1723843e1988Sjohnlev 	 * event.
1724843e1988Sjohnlev 	 */
1725843e1988Sjohnlev 	if (evthandler == NULL) {
1726843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1727843e1988Sjohnlev 		return (DDI_SUCCESS);
1728843e1988Sjohnlev 	}
1729843e1988Sjohnlev 
1730843e1988Sjohnlev 	ASSERT(*cbid == NULL);
1731843e1988Sjohnlev 
1732843e1988Sjohnlev 	if (ddi_get_eventcookie(dip, name, &ecv) != DDI_SUCCESS) {
1733843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to find %s cookie for %s@%s",
1734843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
1735843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1736843e1988Sjohnlev 		return (DDI_FAILURE);
1737843e1988Sjohnlev 	}
1738843e1988Sjohnlev 	if (ddi_add_event_handler(dip, ecv, evthandler, NULL, cbid)
1739843e1988Sjohnlev 	    != DDI_SUCCESS) {
1740843e1988Sjohnlev 		cmn_err(CE_WARN, "failed to add %s event handler for %s@%s",
1741843e1988Sjohnlev 		    name, ddi_get_name(dip), ddi_get_name_addr(dip));
1742843e1988Sjohnlev 		*cbid = NULL;
1743843e1988Sjohnlev 		mutex_exit(&pdp->xd_lk);
1744843e1988Sjohnlev 		return (DDI_FAILURE);
1745843e1988Sjohnlev 	}
1746843e1988Sjohnlev 
1747843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1748843e1988Sjohnlev 
1749843e1988Sjohnlev 	return (DDI_SUCCESS);
1750843e1988Sjohnlev }
1751843e1988Sjohnlev 
1752843e1988Sjohnlev /*
1753843e1988Sjohnlev  * Remove event handler for the leaf driver and unwatch xenstore
1754843e1988Sjohnlev  * so, driver will not be notified when xenstore entry changed later
1755843e1988Sjohnlev  */
1756843e1988Sjohnlev void
1757843e1988Sjohnlev xvdi_remove_event_handler(dev_info_t *dip, char *name)
1758843e1988Sjohnlev {
1759843e1988Sjohnlev 	struct xendev_ppd *pdp;
1760843e1988Sjohnlev 	boolean_t rem_oe = B_FALSE, rem_hp = B_FALSE;
1761843e1988Sjohnlev 	ddi_callback_id_t oeid = NULL, hpid = NULL;
1762843e1988Sjohnlev 
1763843e1988Sjohnlev 	pdp = ddi_get_parent_data(dip);
1764843e1988Sjohnlev 	ASSERT(pdp != NULL);
1765843e1988Sjohnlev 
1766843e1988Sjohnlev 	if (name == NULL) {
1767843e1988Sjohnlev 		rem_oe = B_TRUE;
1768843e1988Sjohnlev 		rem_hp = B_TRUE;
1769843e1988Sjohnlev 	} else if (strcmp(name, XS_OE_STATE) == 0) {
1770843e1988Sjohnlev 		rem_oe = B_TRUE;
1771843e1988Sjohnlev 	} else if (strcmp(name, XS_HP_STATE) == 0) {
1772843e1988Sjohnlev 		rem_hp = B_TRUE;
1773843e1988Sjohnlev 	} else {
1774843e1988Sjohnlev 		cmn_err(CE_WARN, "event %s not supported, cannot remove", name);
1775843e1988Sjohnlev 		return;
1776843e1988Sjohnlev 	}
1777843e1988Sjohnlev 
1778843e1988Sjohnlev 	mutex_enter(&pdp->xd_lk);
1779843e1988Sjohnlev 
1780843e1988Sjohnlev 	if (rem_oe && (pdp->xd_oe_ehid != NULL)) {
1781843e1988Sjohnlev 		oeid = pdp->xd_oe_ehid;
1782843e1988Sjohnlev 		pdp->xd_oe_ehid = NULL;
1783843e1988Sjohnlev 	}
1784843e1988Sjohnlev 
1785843e1988Sjohnlev 	if (rem_hp && (pdp->xd_hp_ehid != NULL)) {
1786843e1988Sjohnlev 		hpid = pdp->xd_hp_ehid;
1787843e1988Sjohnlev 		pdp->xd_hp_ehid = NULL;
1788843e1988Sjohnlev 	}
1789843e1988Sjohnlev 
1790843e1988Sjohnlev 	mutex_exit(&pdp->xd_lk);
1791843e1988Sjohnlev 
1792843e1988Sjohnlev 	if (oeid != NULL)
1793843e1988Sjohnlev 		(void) ddi_remove_event_handler(oeid);
1794843e1988Sjohnlev 	if (hpid != NULL)
1795843e1988Sjohnlev 		(void) ddi_remove_event_handler(hpid);
1796843e1988Sjohnlev }
1797843e1988Sjohnlev 
1798843e1988Sjohnlev 
1799843e1988Sjohnlev /*
1800843e1988Sjohnlev  * common ring interfaces
1801843e1988Sjohnlev  */
1802843e1988Sjohnlev 
1803843e1988Sjohnlev #define	FRONT_RING(_ringp)	(&(_ringp)->xr_sring.fr)
1804843e1988Sjohnlev #define	BACK_RING(_ringp)	(&(_ringp)->xr_sring.br)
1805843e1988Sjohnlev #define	GET_RING_SIZE(_ringp)	RING_SIZE(FRONT_RING(ringp))
1806843e1988Sjohnlev #define	GET_RING_ENTRY_FE(_ringp, _idx)		\
1807843e1988Sjohnlev 	(FRONT_RING(_ringp)->sring->ring +	\
1808843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
1809843e1988Sjohnlev #define	GET_RING_ENTRY_BE(_ringp, _idx)		\
1810843e1988Sjohnlev 	(BACK_RING(_ringp)->sring->ring +	\
1811843e1988Sjohnlev 	(_ringp)->xr_entry_size * ((_idx) & (GET_RING_SIZE(_ringp) - 1)))
1812843e1988Sjohnlev 
1813843e1988Sjohnlev unsigned int
1814843e1988Sjohnlev xvdi_ring_avail_slots(xendev_ring_t *ringp)
1815843e1988Sjohnlev {
1816843e1988Sjohnlev 	comif_ring_fe_t *frp;
1817843e1988Sjohnlev 	comif_ring_be_t *brp;
1818843e1988Sjohnlev 
1819843e1988Sjohnlev 	if (ringp->xr_frontend) {
1820843e1988Sjohnlev 		frp = FRONT_RING(ringp);
1821843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
1822843e1988Sjohnlev 		    (frp->req_prod_pvt - frp->rsp_cons));
1823843e1988Sjohnlev 	} else {
1824843e1988Sjohnlev 		brp = BACK_RING(ringp);
1825843e1988Sjohnlev 		return (GET_RING_SIZE(ringp) -
1826843e1988Sjohnlev 		    (brp->rsp_prod_pvt - brp->req_cons));
1827843e1988Sjohnlev 	}
1828843e1988Sjohnlev }
1829843e1988Sjohnlev 
1830843e1988Sjohnlev int
1831843e1988Sjohnlev xvdi_ring_has_unconsumed_requests(xendev_ring_t *ringp)
1832843e1988Sjohnlev {
1833843e1988Sjohnlev 	comif_ring_be_t *brp;
1834843e1988Sjohnlev 
1835843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
1836843e1988Sjohnlev 	brp = BACK_RING(ringp);
1837843e1988Sjohnlev 	return ((brp->req_cons !=
1838843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &brp->sring->req_prod)) &&
1839843e1988Sjohnlev 	    ((brp->req_cons - brp->rsp_prod_pvt) != RING_SIZE(brp)));
1840843e1988Sjohnlev }
1841843e1988Sjohnlev 
1842843e1988Sjohnlev int
1843843e1988Sjohnlev xvdi_ring_has_incomp_request(xendev_ring_t *ringp)
1844843e1988Sjohnlev {
1845843e1988Sjohnlev 	comif_ring_fe_t *frp;
1846843e1988Sjohnlev 
1847843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
1848843e1988Sjohnlev 	frp = FRONT_RING(ringp);
1849843e1988Sjohnlev 	return (frp->req_prod_pvt !=
1850843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
1851843e1988Sjohnlev }
1852843e1988Sjohnlev 
1853843e1988Sjohnlev int
1854843e1988Sjohnlev xvdi_ring_has_unconsumed_responses(xendev_ring_t *ringp)
1855843e1988Sjohnlev {
1856843e1988Sjohnlev 	comif_ring_fe_t *frp;
1857843e1988Sjohnlev 
1858843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
1859843e1988Sjohnlev 	frp = FRONT_RING(ringp);
1860843e1988Sjohnlev 	return (frp->rsp_cons !=
1861843e1988Sjohnlev 	    ddi_get32(ringp->xr_acc_hdl, &frp->sring->rsp_prod));
1862843e1988Sjohnlev }
1863843e1988Sjohnlev 
1864843e1988Sjohnlev /* NOTE: req_event will be increased as needed */
1865843e1988Sjohnlev void *
1866843e1988Sjohnlev xvdi_ring_get_request(xendev_ring_t *ringp)
1867843e1988Sjohnlev {
1868843e1988Sjohnlev 	comif_ring_fe_t *frp;
1869843e1988Sjohnlev 	comif_ring_be_t *brp;
1870843e1988Sjohnlev 
1871843e1988Sjohnlev 	if (ringp->xr_frontend) {
1872843e1988Sjohnlev 		/* for frontend ring */
1873843e1988Sjohnlev 		frp = FRONT_RING(ringp);
1874843e1988Sjohnlev 		if (!RING_FULL(frp))
1875843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->req_prod_pvt++));
1876843e1988Sjohnlev 		else
1877843e1988Sjohnlev 			return (NULL);
1878843e1988Sjohnlev 	} else {
1879843e1988Sjohnlev 		/* for backend ring */
1880843e1988Sjohnlev 		brp = BACK_RING(ringp);
1881843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_REQUESTS() */
1882843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_requests(ringp))
1883843e1988Sjohnlev 			return (GET_RING_ENTRY_BE(ringp, brp->req_cons++));
1884843e1988Sjohnlev 		else {
1885843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &brp->sring->req_event,
1886843e1988Sjohnlev 			    brp->req_cons + 1);
1887843e1988Sjohnlev 			membar_enter();
1888843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_requests(ringp))
1889843e1988Sjohnlev 				return (GET_RING_ENTRY_BE(ringp,
1890843e1988Sjohnlev 				    brp->req_cons++));
1891843e1988Sjohnlev 			else
1892843e1988Sjohnlev 				return (NULL);
1893843e1988Sjohnlev 		}
1894843e1988Sjohnlev 	}
1895843e1988Sjohnlev }
1896843e1988Sjohnlev 
1897843e1988Sjohnlev int
1898843e1988Sjohnlev xvdi_ring_push_request(xendev_ring_t *ringp)
1899843e1988Sjohnlev {
1900843e1988Sjohnlev 	RING_IDX old, new, reqevt;
1901843e1988Sjohnlev 	comif_ring_fe_t *frp;
1902843e1988Sjohnlev 
1903843e1988Sjohnlev 	/* only frontend should be able to push request */
1904843e1988Sjohnlev 	ASSERT(ringp->xr_frontend);
1905843e1988Sjohnlev 
1906843e1988Sjohnlev 	/* RING_PUSH_REQUEST_AND_CHECK_NOTIFY() */
1907843e1988Sjohnlev 	frp = FRONT_RING(ringp);
1908843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_prod);
1909843e1988Sjohnlev 	new = frp->req_prod_pvt;
1910843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &frp->sring->req_prod, new);
1911843e1988Sjohnlev 	membar_enter();
1912843e1988Sjohnlev 	reqevt = ddi_get32(ringp->xr_acc_hdl, &frp->sring->req_event);
1913843e1988Sjohnlev 	return ((RING_IDX)(new - reqevt) < (RING_IDX)(new - old));
1914843e1988Sjohnlev }
1915843e1988Sjohnlev 
1916843e1988Sjohnlev /* NOTE: rsp_event will be increased as needed */
1917843e1988Sjohnlev void *
1918843e1988Sjohnlev xvdi_ring_get_response(xendev_ring_t *ringp)
1919843e1988Sjohnlev {
1920843e1988Sjohnlev 	comif_ring_fe_t *frp;
1921843e1988Sjohnlev 	comif_ring_be_t *brp;
1922843e1988Sjohnlev 
1923843e1988Sjohnlev 	if (!ringp->xr_frontend) {
1924843e1988Sjohnlev 		/* for backend ring */
1925843e1988Sjohnlev 		brp = BACK_RING(ringp);
1926843e1988Sjohnlev 		return (GET_RING_ENTRY_BE(ringp, brp->rsp_prod_pvt++));
1927843e1988Sjohnlev 	} else {
1928843e1988Sjohnlev 		/* for frontend ring */
1929843e1988Sjohnlev 		frp = FRONT_RING(ringp);
1930843e1988Sjohnlev 		/* RING_FINAL_CHECK_FOR_RESPONSES() */
1931843e1988Sjohnlev 		if (xvdi_ring_has_unconsumed_responses(ringp))
1932843e1988Sjohnlev 			return (GET_RING_ENTRY_FE(ringp, frp->rsp_cons++));
1933843e1988Sjohnlev 		else {
1934843e1988Sjohnlev 			ddi_put32(ringp->xr_acc_hdl, &frp->sring->rsp_event,
1935843e1988Sjohnlev 			    frp->rsp_cons + 1);
1936843e1988Sjohnlev 			membar_enter();
1937843e1988Sjohnlev 			if (xvdi_ring_has_unconsumed_responses(ringp))
1938843e1988Sjohnlev 				return (GET_RING_ENTRY_FE(ringp,
1939843e1988Sjohnlev 				    frp->rsp_cons++));
1940843e1988Sjohnlev 			else
1941843e1988Sjohnlev 				return (NULL);
1942843e1988Sjohnlev 		}
1943843e1988Sjohnlev 	}
1944843e1988Sjohnlev }
1945843e1988Sjohnlev 
1946843e1988Sjohnlev int
1947843e1988Sjohnlev xvdi_ring_push_response(xendev_ring_t *ringp)
1948843e1988Sjohnlev {
1949843e1988Sjohnlev 	RING_IDX old, new, rspevt;
1950843e1988Sjohnlev 	comif_ring_be_t *brp;
1951843e1988Sjohnlev 
1952843e1988Sjohnlev 	/* only backend should be able to push response */
1953843e1988Sjohnlev 	ASSERT(!ringp->xr_frontend);
1954843e1988Sjohnlev 
1955843e1988Sjohnlev 	/* RING_PUSH_RESPONSE_AND_CHECK_NOTIFY() */
1956843e1988Sjohnlev 	brp = BACK_RING(ringp);
1957843e1988Sjohnlev 	old = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_prod);
1958843e1988Sjohnlev 	new = brp->rsp_prod_pvt;
1959843e1988Sjohnlev 	ddi_put32(ringp->xr_acc_hdl, &brp->sring->rsp_prod, new);
1960843e1988Sjohnlev 	membar_enter();
1961843e1988Sjohnlev 	rspevt = ddi_get32(ringp->xr_acc_hdl, &brp->sring->rsp_event);
1962843e1988Sjohnlev 	return ((RING_IDX)(new - rspevt) < (RING_IDX)(new - old));
1963843e1988Sjohnlev }
1964843e1988Sjohnlev 
1965843e1988Sjohnlev static void
1966843e1988Sjohnlev xvdi_ring_init_sring(xendev_ring_t *ringp)
1967843e1988Sjohnlev {
1968843e1988Sjohnlev 	ddi_acc_handle_t acchdl;
1969843e1988Sjohnlev 	comif_sring_t *xsrp;
1970843e1988Sjohnlev 	int i;
1971843e1988Sjohnlev 
1972843e1988Sjohnlev 	xsrp = (comif_sring_t *)ringp->xr_vaddr;
1973843e1988Sjohnlev 	acchdl = ringp->xr_acc_hdl;
1974843e1988Sjohnlev 
1975843e1988Sjohnlev 	/* shared ring initialization */
1976843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_prod, 0);
1977843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_prod, 0);
1978843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->req_event, 1);
1979843e1988Sjohnlev 	ddi_put32(acchdl, &xsrp->rsp_event, 1);
1980843e1988Sjohnlev 	for (i = 0; i < sizeof (xsrp->pad); i++)
1981843e1988Sjohnlev 		ddi_put8(acchdl, xsrp->pad + i, 0);
1982843e1988Sjohnlev }
1983843e1988Sjohnlev 
1984843e1988Sjohnlev static void
1985843e1988Sjohnlev xvdi_ring_init_front_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
1986843e1988Sjohnlev {
1987843e1988Sjohnlev 	comif_ring_fe_t *xfrp;
1988843e1988Sjohnlev 
1989843e1988Sjohnlev 	xfrp = &ringp->xr_sring.fr;
1990843e1988Sjohnlev 	xfrp->req_prod_pvt = 0;
1991843e1988Sjohnlev 	xfrp->rsp_cons = 0;
1992843e1988Sjohnlev 	xfrp->nr_ents = nentry;
1993843e1988Sjohnlev 	xfrp->sring = (comif_sring_t *)ringp->xr_vaddr;
1994843e1988Sjohnlev 
1995843e1988Sjohnlev 	ringp->xr_frontend = 1;
1996843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
1997843e1988Sjohnlev }
1998843e1988Sjohnlev 
1999551bc2a6Smrj #ifndef XPV_HVM_DRIVER
2000843e1988Sjohnlev static void
2001843e1988Sjohnlev xvdi_ring_init_back_ring(xendev_ring_t *ringp, size_t nentry, size_t entrysize)
2002843e1988Sjohnlev {
2003843e1988Sjohnlev 	comif_ring_be_t *xbrp;
2004843e1988Sjohnlev 
2005843e1988Sjohnlev 	xbrp = &ringp->xr_sring.br;
2006843e1988Sjohnlev 	xbrp->rsp_prod_pvt = 0;
2007843e1988Sjohnlev 	xbrp->req_cons = 0;
2008843e1988Sjohnlev 	xbrp->nr_ents = nentry;
2009843e1988Sjohnlev 	xbrp->sring = (comif_sring_t *)ringp->xr_vaddr;
2010843e1988Sjohnlev 
2011843e1988Sjohnlev 	ringp->xr_frontend = 0;
2012843e1988Sjohnlev 	ringp->xr_entry_size = entrysize;
2013843e1988Sjohnlev }
2014551bc2a6Smrj #endif /* XPV_HVM_DRIVER */
2015843e1988Sjohnlev 
2016843e1988Sjohnlev static void
2017843e1988Sjohnlev xendev_offline_device(void *arg)
2018843e1988Sjohnlev {
2019843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)arg;
2020843e1988Sjohnlev 	char devname[MAXNAMELEN] = {0};
2021843e1988Sjohnlev 
2022843e1988Sjohnlev 	/*
2023843e1988Sjohnlev 	 * This is currently the only chance to delete a devinfo node, which
2024843e1988Sjohnlev 	 * is _not_ always successful.
2025843e1988Sjohnlev 	 */
2026843e1988Sjohnlev 	(void) ddi_deviname(dip, devname);
2027843e1988Sjohnlev 	(void) devfs_clean(ddi_get_parent(dip), devname + 1, DV_CLEAN_FORCE);
2028843e1988Sjohnlev 	(void) ndi_devi_offline(dip, NDI_DEVI_REMOVE);
2029843e1988Sjohnlev }
2030843e1988Sjohnlev 
2031843e1988Sjohnlev static void
2032843e1988Sjohnlev i_xvdi_oestate_cb(struct xenbus_device *dev, XenbusState oestate)
2033843e1988Sjohnlev {
2034843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)dev->data;
2035843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2036843e1988Sjohnlev 
2037843e1988Sjohnlev 	/*
2038843e1988Sjohnlev 	 * Don't trigger two consecutive ndi_devi_offline on the same
2039843e1988Sjohnlev 	 * dip.
2040843e1988Sjohnlev 	 */
2041843e1988Sjohnlev 	if ((oestate == XenbusStateClosed) &&
2042843e1988Sjohnlev 	    (dev->otherend_state == XenbusStateClosed))
2043843e1988Sjohnlev 		return;
2044843e1988Sjohnlev 
2045843e1988Sjohnlev 	dev->otherend_state = oestate;
2046843e1988Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_oe_taskq,
2047843e1988Sjohnlev 	    i_xvdi_oestate_handler, (void *)dip, DDI_SLEEP);
2048843e1988Sjohnlev }
2049843e1988Sjohnlev 
2050843e1988Sjohnlev /*ARGSUSED*/
2051843e1988Sjohnlev static void
2052843e1988Sjohnlev i_xvdi_hpstate_cb(struct xenbus_watch *w, const char **vec,
2053843e1988Sjohnlev     unsigned int len)
2054843e1988Sjohnlev {
2055843e1988Sjohnlev 	dev_info_t *dip = (dev_info_t *)w->dev;
2056843e1988Sjohnlev 	struct xendev_ppd *pdp = ddi_get_parent_data(dip);
2057843e1988Sjohnlev 
2058843e1988Sjohnlev 	(void) ddi_taskq_dispatch(pdp->xd_hp_taskq,
2059843e1988Sjohnlev 	    i_xvdi_hpstate_handler, (void *)dip, DDI_SLEEP);
2060843e1988Sjohnlev }
2061843e1988Sjohnlev 
2062843e1988Sjohnlev static void
2063843e1988Sjohnlev i_xvdi_probe_path_handler(void *arg)
2064843e1988Sjohnlev {
2065843e1988Sjohnlev 	dev_info_t *parent;
2066843e1988Sjohnlev 	char *path = arg, *p = NULL;
2067843e1988Sjohnlev 	int i, vdev, circ;
2068843e1988Sjohnlev 	i_xd_cfg_t *xdcp;
2069843e1988Sjohnlev 	boolean_t frontend;
2070843e1988Sjohnlev 	domid_t dom;
2071843e1988Sjohnlev 
2072843e1988Sjohnlev 	for (i = 0, xdcp = &xdci[0]; i < NXDC; i++, xdcp++) {
2073843e1988Sjohnlev 
2074843e1988Sjohnlev 		if ((xdcp->xs_path_fe != NULL) &&
2075843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_fe, strlen(xdcp->xs_path_fe))
2076843e1988Sjohnlev 		    == 0)) {
2077843e1988Sjohnlev 
2078843e1988Sjohnlev 			frontend = B_TRUE;
2079843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_fe);
2080843e1988Sjohnlev 			break;
2081843e1988Sjohnlev 		}
2082843e1988Sjohnlev 
2083843e1988Sjohnlev 		if ((xdcp->xs_path_be != NULL) &&
2084843e1988Sjohnlev 		    (strncmp(path, xdcp->xs_path_be, strlen(xdcp->xs_path_be))
2085843e1988Sjohnlev 		    == 0)) {
2086843e1988Sjohnlev 
2087843e1988Sjohnlev 			frontend = B_FALSE;
2088843e1988Sjohnlev 			p = path + strlen(xdcp->xs_path_be);
2089843e1988Sjohnlev 			break;
2090843e1988Sjohnlev 		}
2091843e1988Sjohnlev 
2092843e1988Sjohnlev 	}
2093843e1988Sjohnlev 
2094843e1988Sjohnlev 	if (p == NULL) {
2095843e1988Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
2096843e1988Sjohnlev 		    "unexpected path prefix in %s", path);
2097843e1988Sjohnlev 		goto done;
2098843e1988Sjohnlev 	}
2099843e1988Sjohnlev 
2100843e1988Sjohnlev 	if (frontend) {
2101843e1988Sjohnlev 		dom = DOMID_SELF;
2102843e1988Sjohnlev 		if (sscanf(p, "/%d/", &vdev) != 1) {
2103843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2104843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2105843e1988Sjohnlev 			    "cannot parse frontend path %s",
2106843e1988Sjohnlev 			    path);
2107843e1988Sjohnlev 			goto done;
2108843e1988Sjohnlev 		}
2109843e1988Sjohnlev 	} else {
2110843e1988Sjohnlev 		if (sscanf(p, "/%d/%d/", &dom, &vdev) != 2) {
2111843e1988Sjohnlev 			XVDI_DPRINTF(XVDI_DBG_PROBE,
2112843e1988Sjohnlev 			    "i_xvdi_probe_path_handler: "
2113843e1988Sjohnlev 			    "cannot parse backend path %s",
2114843e1988Sjohnlev 			    path);
2115843e1988Sjohnlev 			goto done;
2116843e1988Sjohnlev 		}
2117843e1988Sjohnlev 	}
2118843e1988Sjohnlev 
2119*1ca30e39Sjohnlev 	/*
2120*1ca30e39Sjohnlev 	 * This is an oxymoron, so indicates a bogus configuration we
2121*1ca30e39Sjohnlev 	 * must check for.
2122*1ca30e39Sjohnlev 	 */
2123*1ca30e39Sjohnlev 	if (vdev == VDEV_NOXS) {
2124*1ca30e39Sjohnlev 		cmn_err(CE_WARN, "i_xvdi_probe_path_handler: "
2125*1ca30e39Sjohnlev 		    "invalid path %s", path);
2126*1ca30e39Sjohnlev 		goto done;
2127*1ca30e39Sjohnlev 	}
2128*1ca30e39Sjohnlev 
2129843e1988Sjohnlev 	parent = xendev_dip;
2130843e1988Sjohnlev 	ASSERT(parent != NULL);
2131843e1988Sjohnlev 
2132843e1988Sjohnlev 	ndi_devi_enter(parent, &circ);
2133843e1988Sjohnlev 
2134843e1988Sjohnlev 	if (xvdi_find_dev(parent, xdcp->devclass, dom, vdev) == NULL) {
2135843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2136843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: create for %s", path);
2137843e1988Sjohnlev 		(void) xvdi_create_dev(parent, xdcp->devclass, dom, vdev);
2138843e1988Sjohnlev 	} else {
2139843e1988Sjohnlev 		XVDI_DPRINTF(XVDI_DBG_PROBE,
2140843e1988Sjohnlev 		    "i_xvdi_probe_path_handler: %s already exists", path);
2141843e1988Sjohnlev 	}
2142843e1988Sjohnlev 
2143843e1988Sjohnlev 	ndi_devi_exit(parent, circ);
2144843e1988Sjohnlev 
2145843e1988Sjohnlev done:
2146843e1988Sjohnlev 	kmem_free(path, strlen(path) + 1);
2147843e1988Sjohnlev }
2148