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