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