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