xref: /onnv-gate/usr/src/uts/sun4v/io/dr_io.c (revision 7899:9ae13a31f010)
16441Sjm22469 /*
26441Sjm22469  * CDDL HEADER START
36441Sjm22469  *
46441Sjm22469  * The contents of this file are subject to the terms of the
56441Sjm22469  * Common Development and Distribution License (the "License").
66441Sjm22469  * You may not use this file except in compliance with the License.
76441Sjm22469  *
86441Sjm22469  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96441Sjm22469  * or http://www.opensolaris.org/os/licensing.
106441Sjm22469  * See the License for the specific language governing permissions
116441Sjm22469  * and limitations under the License.
126441Sjm22469  *
136441Sjm22469  * When distributing Covered Code, include this CDDL HEADER in each
146441Sjm22469  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156441Sjm22469  * If applicable, add the following below this CDDL HEADER, with the
166441Sjm22469  * fields enclosed by brackets "[]" replaced with your own identifying
176441Sjm22469  * information: Portions Copyright [yyyy] [name of copyright owner]
186441Sjm22469  *
196441Sjm22469  * CDDL HEADER END
206441Sjm22469  */
216441Sjm22469 
226441Sjm22469 /*
236441Sjm22469  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
246441Sjm22469  * Use is subject to license terms.
256441Sjm22469  */
266441Sjm22469 
276441Sjm22469 /*
286441Sjm22469  * sun4v VIO DR Module
296441Sjm22469  */
306441Sjm22469 
316441Sjm22469 #include <sys/modctl.h>
326441Sjm22469 #include <sys/sunddi.h>
336441Sjm22469 #include <sys/sunndi.h>
346441Sjm22469 #include <sys/note.h>
356441Sjm22469 #include <sys/sysevent/dr.h>
366441Sjm22469 #include <sys/hypervisor_api.h>
376441Sjm22469 #include <sys/mach_descrip.h>
386441Sjm22469 #include <sys/mdesc.h>
396441Sjm22469 #include <sys/mdesc_impl.h>
406441Sjm22469 #include <sys/ds.h>
416441Sjm22469 #include <sys/drctl.h>
426441Sjm22469 #include <sys/dr_util.h>
436441Sjm22469 #include <sys/dr_io.h>
446441Sjm22469 #include <sys/promif.h>
456441Sjm22469 #include <sys/machsystm.h>
466441Sjm22469 #include <sys/ethernet.h>
476441Sjm22469 #include <sys/hotplug/pci/pcicfg.h>
486441Sjm22469 
496441Sjm22469 
506441Sjm22469 static struct modlmisc modlmisc = {
516441Sjm22469 	&mod_miscops,
527799SRichard.Bean@Sun.COM 	"sun4v VIO DR"
536441Sjm22469 };
546441Sjm22469 
556441Sjm22469 static struct modlinkage modlinkage = {
566441Sjm22469 	MODREV_1,
576441Sjm22469 	(void *)&modlmisc,
586441Sjm22469 	NULL
596441Sjm22469 };
606441Sjm22469 
616441Sjm22469 
626441Sjm22469 /*
636441Sjm22469  * VIO DS Interface
646441Sjm22469  */
656441Sjm22469 
666441Sjm22469 /*
676441Sjm22469  * Global DS Handle
686441Sjm22469  */
696441Sjm22469 static ds_svc_hdl_t ds_vio_handle;
706441Sjm22469 
716441Sjm22469 /*
726441Sjm22469  * Supported DS Capability Versions
736441Sjm22469  */
746441Sjm22469 static ds_ver_t		dr_vio_vers[] = { { 1, 0 } };
756441Sjm22469 #define	DR_VIO_NVERS	(sizeof (dr_vio_vers) / sizeof (dr_vio_vers[0]))
766441Sjm22469 
776441Sjm22469 /*
786441Sjm22469  * DS Capability Description
796441Sjm22469  */
806441Sjm22469 static ds_capability_t dr_vio_cap = {
816441Sjm22469 	DR_VIO_DS_ID,		/* svc_id */
826441Sjm22469 	dr_vio_vers,		/* vers */
836441Sjm22469 	DR_VIO_NVERS		/* nvers */
846441Sjm22469 };
856441Sjm22469 
866441Sjm22469 /*
876441Sjm22469  * DS Callbacks
886441Sjm22469  */
896441Sjm22469 static void dr_vio_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
906441Sjm22469 static void dr_vio_unreg_handler(ds_cb_arg_t arg);
916441Sjm22469 static void dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
926441Sjm22469 
936441Sjm22469 /*
946441Sjm22469  * DS Client Ops Vector
956441Sjm22469  */
966441Sjm22469 static ds_clnt_ops_t dr_vio_ops = {
976441Sjm22469 	dr_vio_reg_handler,	/* ds_reg_cb */
986441Sjm22469 	dr_vio_unreg_handler,	/* ds_unreg_cb */
996441Sjm22469 	dr_vio_data_handler,	/* ds_data_cb */
1006441Sjm22469 	NULL			/* cb_arg */
1016441Sjm22469 };
1026441Sjm22469 
1036441Sjm22469 
1046441Sjm22469 typedef struct {
1056441Sjm22469 	char		*name;
1066441Sjm22469 	uint64_t	devid;
1076441Sjm22469 	dev_info_t	*dip;
1086441Sjm22469 } dr_search_arg_t;
1096441Sjm22469 
1106441Sjm22469 static int
dr_io_check_node(dev_info_t * dip,void * arg)1116441Sjm22469 dr_io_check_node(dev_info_t *dip, void *arg)
1126441Sjm22469 {
1136441Sjm22469 	char 		*name;
1146441Sjm22469 	uint64_t	devid;
1156441Sjm22469 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
1166441Sjm22469 
1176441Sjm22469 	name = ddi_node_name(dip);
1186441Sjm22469 
1196441Sjm22469 	if (strcmp(name, sarg->name) != 0)
1206441Sjm22469 		return (DDI_WALK_CONTINUE);
1216441Sjm22469 
1226441Sjm22469 	devid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1236441Sjm22469 	    "reg", -1);
1246441Sjm22469 
1256441Sjm22469 	DR_DBG_IO("%s: found devid=%ld, looking for %ld\n",
1266441Sjm22469 	    __func__, devid, sarg->devid);
1276441Sjm22469 
1286441Sjm22469 	if (devid == sarg->devid) {
1296441Sjm22469 		DR_DBG_IO("%s: matched", __func__);
1306441Sjm22469 
1316441Sjm22469 		/* matching node must be returned held */
1326441Sjm22469 		if (!e_ddi_branch_held(dip))
1336441Sjm22469 			e_ddi_branch_hold(dip);
1346441Sjm22469 
1356441Sjm22469 		sarg->dip = dip;
1366441Sjm22469 		return (DDI_WALK_TERMINATE);
1376441Sjm22469 	}
1386441Sjm22469 
1396441Sjm22469 	return (DDI_WALK_CONTINUE);
1406441Sjm22469 }
1416441Sjm22469 
1426441Sjm22469 /*
1436441Sjm22469  * Walk the device tree to find the dip corresponding to the devid
1446441Sjm22469  * passed in. If present, the dip is returned held. The caller must
1456441Sjm22469  * release the hold on the dip once it is no longer required. If no
1466441Sjm22469  * matching node if found, NULL is returned.
1476441Sjm22469  */
1486441Sjm22469 static dev_info_t *
dr_io_find_node(char * name,uint64_t devid)1496441Sjm22469 dr_io_find_node(char *name, uint64_t devid)
1506441Sjm22469 {
1516441Sjm22469 	dr_search_arg_t	arg;
1526441Sjm22469 
1536441Sjm22469 	DR_DBG_IO("dr_io_find_node...\n");
1546441Sjm22469 
1556441Sjm22469 	arg.name = name;
1566441Sjm22469 	arg.devid = devid;
1576441Sjm22469 	arg.dip = NULL;
1586441Sjm22469 
1596441Sjm22469 	ddi_walk_devs(ddi_root_node(), dr_io_check_node, &arg);
1606441Sjm22469 
1616441Sjm22469 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
1626441Sjm22469 
1636441Sjm22469 	return ((arg.dip) ? arg.dip : NULL);
1646441Sjm22469 }
1656441Sjm22469 
1666441Sjm22469 /*
1676441Sjm22469  * Look up a particular IO node in the MD. Returns the mde_cookie_t
1686441Sjm22469  * representing that IO node if present, and MDE_INVAL_ELEM_COOKIE otherwise.
1696441Sjm22469  * It is assumed the scratch array has already been allocated so that
1706441Sjm22469  * it can accommodate the worst case scenario, every node in the MD.
1716441Sjm22469  */
1726441Sjm22469 static mde_cookie_t
dr_io_find_node_md(md_t * mdp,char * name,uint64_t id,mde_cookie_t * listp)1736441Sjm22469 dr_io_find_node_md(md_t *mdp, char *name, uint64_t id, mde_cookie_t *listp)
1746441Sjm22469 {
1756441Sjm22469 	int		i;
1766441Sjm22469 	int		nnodes;
1776441Sjm22469 	char		*devnm;
1786441Sjm22469 	uint64_t	devid;
1796441Sjm22469 	mde_cookie_t	rootnode;
1806441Sjm22469 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
1816441Sjm22469 
1826441Sjm22469 	DR_DBG_IO("%s: %s@%ld\n", __func__, name, id);
1836441Sjm22469 
1846441Sjm22469 	rootnode = md_root_node(mdp);
1856441Sjm22469 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1866441Sjm22469 
1876441Sjm22469 	/*
1886441Sjm22469 	 * Scan the DAG for all candidate nodes.
1896441Sjm22469 	 */
1906441Sjm22469 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "virtual-device"),
1916441Sjm22469 	    md_find_name(mdp, "fwd"), listp);
1926441Sjm22469 
1936441Sjm22469 	if (nnodes < 0) {
1946441Sjm22469 		DR_DBG_IO("%s: scan for "
1956441Sjm22469 		    "'virtual-device' nodes failed\n", __func__);
1966441Sjm22469 		return (result);
1976441Sjm22469 	}
1986441Sjm22469 
1996441Sjm22469 	DR_DBG_IO("%s: found %d nodes in the MD\n", __func__, nnodes);
2006441Sjm22469 
2016441Sjm22469 	/*
2026441Sjm22469 	 * Find the node of interest
2036441Sjm22469 	 */
2046441Sjm22469 	for (i = 0; i < nnodes; i++) {
2056441Sjm22469 
2066441Sjm22469 		if (md_get_prop_str(mdp, listp[i], "name", &devnm)) {
2076441Sjm22469 			DR_DBG_IO("%s: missing 'name' property for"
2086441Sjm22469 			    " IO node %d\n", __func__, i);
2096441Sjm22469 			return (DDI_WALK_ERROR);
2106441Sjm22469 		}
2116441Sjm22469 
2126441Sjm22469 		if (strcmp(devnm, name) != 0)
2136441Sjm22469 			continue;
2146441Sjm22469 
2156441Sjm22469 		if (md_get_prop_val(mdp, listp[i], "cfg-handle", &devid)) {
2166441Sjm22469 			DR_DBG_IO("%s: missing 'cfg-handle' property for"
2176441Sjm22469 			    " IO node %d\n", __func__, i);
2186441Sjm22469 			break;
2196441Sjm22469 		}
2206441Sjm22469 
2216441Sjm22469 		if (devid == id) {
2226441Sjm22469 			/* found a match */
2236441Sjm22469 			DR_DBG_IO("%s: found IO node %s@%ld "
2246441Sjm22469 			    "in MD\n", __func__, name, id);
2256441Sjm22469 			result = listp[i];
2266441Sjm22469 			break;
2276441Sjm22469 		}
2286441Sjm22469 	}
2296441Sjm22469 
2306441Sjm22469 	if (result == MDE_INVAL_ELEM_COOKIE)
2316441Sjm22469 		DR_DBG_IO("%s: IO node %ld not in MD\n", __func__, id);
2326441Sjm22469 
2336441Sjm22469 	return (result);
2346441Sjm22469 }
2356441Sjm22469 
2366441Sjm22469 typedef struct {
2376441Sjm22469 	md_t		*mdp;
2386441Sjm22469 	mde_cookie_t	node;
2396441Sjm22469 	dev_info_t	*dip;
2406441Sjm22469 } cb_arg_t;
2416441Sjm22469 
2426441Sjm22469 #define	STR_ARR_LEN	5
2436441Sjm22469 
2446441Sjm22469 static int
new_dev_node(dev_info_t * new_node,void * arg,uint_t flags)2456441Sjm22469 new_dev_node(dev_info_t *new_node, void *arg, uint_t flags)
2466441Sjm22469 {
2476441Sjm22469 	_NOTE(ARGUNUSED(flags))
2486441Sjm22469 
2496441Sjm22469 	cb_arg_t	*cba;
2506441Sjm22469 	char		*devnm, *devtype;
2516441Sjm22469 	char		*compat;
2526441Sjm22469 	uint64_t	devid;
2536441Sjm22469 	int		len = 0;
2546441Sjm22469 	char		*curr;
2556441Sjm22469 	int		i = 0;
2566441Sjm22469 	char		*str_arr[STR_ARR_LEN];
2576441Sjm22469 
2586441Sjm22469 	cba = (cb_arg_t *)arg;
2596441Sjm22469 
2606441Sjm22469 	/*
2616441Sjm22469 	 * Add 'name' property
2626441Sjm22469 	 */
2636441Sjm22469 	if (md_get_prop_str(cba->mdp, cba->node, "name", &devnm)) {
2646441Sjm22469 		DR_DBG_IO("%s: failed to read 'name' prop from MD\n", __func__);
2656441Sjm22469 		return (DDI_WALK_ERROR);
2666441Sjm22469 	}
2676441Sjm22469 	DR_DBG_IO("%s: device name is %s\n", __func__, devnm);
2686441Sjm22469 
2696441Sjm22469 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
2706441Sjm22469 	    "name", devnm) != DDI_SUCCESS) {
2716441Sjm22469 		DR_DBG_IO("%s: failed to create 'name' prop\n", __func__);
2726441Sjm22469 		return (DDI_WALK_ERROR);
2736441Sjm22469 	}
2746441Sjm22469 
2756441Sjm22469 	/*
2766441Sjm22469 	 * Add 'compatible' property
2776441Sjm22469 	 */
2786441Sjm22469 	if (md_get_prop_data(cba->mdp, cba->node, "compatible",
2796441Sjm22469 	    (uint8_t **)&compat, &len)) {
2806441Sjm22469 		DR_DBG_IO("%s: failed to read "
2816441Sjm22469 		    "'compatible' prop from MD\n", __func__);
2826441Sjm22469 		return (DDI_WALK_ERROR);
2836441Sjm22469 	}
2846441Sjm22469 
2856441Sjm22469 	/* parse the MD string array */
2866441Sjm22469 	curr = compat;
2876441Sjm22469 	while (curr < (compat + len)) {
2886441Sjm22469 
2896441Sjm22469 		DR_DBG_IO("%s: adding '%s' to "
2906441Sjm22469 		    "'compatible' prop\n", __func__, curr);
2916441Sjm22469 
2926441Sjm22469 		str_arr[i++] = curr;
2936441Sjm22469 		curr += strlen(curr) + 1;
2946441Sjm22469 
2956441Sjm22469 		if (i == STR_ARR_LEN) {
2966441Sjm22469 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
2976441Sjm22469 			break;
2986441Sjm22469 		}
2996441Sjm22469 	}
3006441Sjm22469 
3016441Sjm22469 
3026441Sjm22469 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
3036441Sjm22469 	    "compatible", str_arr, i) != DDI_SUCCESS) {
3046441Sjm22469 		DR_DBG_IO("%s: cannot create 'compatible' prop\n", __func__);
3056441Sjm22469 		return (DDI_WALK_ERROR);
3066441Sjm22469 	}
3076441Sjm22469 
3086441Sjm22469 	/*
3096441Sjm22469 	 * Add 'device_type' property
3106441Sjm22469 	 */
3116441Sjm22469 	if (md_get_prop_str(cba->mdp, cba->node, "device-type", &devtype)) {
3126441Sjm22469 		DR_DBG_IO("%s: failed to read "
3136441Sjm22469 		    "'device-type' prop from MD\n", __func__);
3146441Sjm22469 		return (DDI_WALK_ERROR);
3156441Sjm22469 	}
3166441Sjm22469 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
3176441Sjm22469 	    "device_type", devtype) != DDI_SUCCESS) {
3186441Sjm22469 		DR_DBG_IO("%s: failed to create "
3196441Sjm22469 		    "'device-type' prop\n", __func__);
3206441Sjm22469 		return (DDI_WALK_ERROR);
3216441Sjm22469 	}
3226441Sjm22469 
3236441Sjm22469 	DR_DBG_IO("%s: device type is %s\n", __func__, devtype);
3246441Sjm22469 
3256441Sjm22469 	/*
3266441Sjm22469 	 * Add 'reg' (cfg-handle) property
3276441Sjm22469 	 */
3286441Sjm22469 	if (md_get_prop_val(cba->mdp, cba->node, "cfg-handle", &devid)) {
3296441Sjm22469 		DR_DBG_IO("%s: failed to read "
3306441Sjm22469 		    "'cfg-handle' prop from MD\n", __func__);
3316441Sjm22469 		return (DDI_WALK_ERROR);
3326441Sjm22469 	}
3336441Sjm22469 
3346441Sjm22469 	DR_DBG_IO("%s: new device is %s@%ld\n", __func__, devnm, devid);
3356441Sjm22469 
3366441Sjm22469 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node, "reg", devid)
3376441Sjm22469 	    != DDI_SUCCESS) {
3386441Sjm22469 		DR_DBG_IO("%s: failed to create 'reg' prop\n", __func__);
3396441Sjm22469 		return (DDI_WALK_ERROR);
3406441Sjm22469 	}
3416441Sjm22469 
3426441Sjm22469 	/* if vnet/vswitch, probe and add mac-address and mtu properties */
3436441Sjm22469 	if (strcmp(devnm, "vsw") == 0 || strcmp(devnm, "network") == 0) {
3446441Sjm22469 
3456441Sjm22469 		int i, j;
3466441Sjm22469 		uint64_t mtu, macaddr;
3476441Sjm22469 		uchar_t maddr_arr[ETHERADDRL];
3486441Sjm22469 
3496441Sjm22469 		if (md_get_prop_val(cba->mdp, cba->node, "local-mac-address",
3506441Sjm22469 		    &macaddr)) {
3516441Sjm22469 			DR_DBG_IO("%s: failed to read "
3526441Sjm22469 			    "'local-mac-address' prop from MD\n", __func__);
3536441Sjm22469 			return (DDI_WALK_ERROR);
3546441Sjm22469 		}
3556441Sjm22469 
3566441Sjm22469 		for (i = 0, j = (ETHERADDRL - 1); i < ETHERADDRL; i++, j--)
3576441Sjm22469 			maddr_arr[j] = (macaddr >> (i * 8)) & 0xff;
3586441Sjm22469 
3596441Sjm22469 		if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, new_node,
3606441Sjm22469 		    "local-mac-address", maddr_arr, ETHERADDRL)
3616441Sjm22469 		    != DDI_SUCCESS) {
3626441Sjm22469 			DR_DBG_IO("%s: failed to create "
3636441Sjm22469 			    "'local-mac-address' prop\n", __func__);
3646441Sjm22469 			return (DDI_WALK_ERROR);
3656441Sjm22469 		}
3666441Sjm22469 
3676441Sjm22469 		if (md_get_prop_val(cba->mdp, cba->node, "mtu", &mtu)) {
3686441Sjm22469 			DR_DBG_IO("%s: failed to read "
3696441Sjm22469 			    "'mtu' prop from MD\n", __func__);
3706441Sjm22469 			return (DDI_WALK_ERROR);
3716441Sjm22469 		}
3726441Sjm22469 
3736441Sjm22469 		if (ndi_prop_update_int64(DDI_DEV_T_NONE, new_node, "mtu",
3746441Sjm22469 		    mtu) != DDI_SUCCESS) {
3756441Sjm22469 			DR_DBG_IO("%s: failed to "
3766441Sjm22469 			    "create 'mtu' prop\n", __func__);
3776441Sjm22469 			return (DDI_WALK_ERROR);
3786441Sjm22469 		}
3796441Sjm22469 
3806441Sjm22469 		DR_DBG_IO("%s: Added properties for %s@%ld, "
3816441Sjm22469 		    "mac=%ld, mtu=%ld\n", __func__, devnm, devid, macaddr, mtu);
3826441Sjm22469 	}
3836441Sjm22469 
3846441Sjm22469 	cba->dip = new_node;
3856441Sjm22469 
3866441Sjm22469 	return (DDI_WALK_TERMINATE);
3876441Sjm22469 }
3886441Sjm22469 
3896441Sjm22469 /*
3906441Sjm22469  * Find the parent node of the argument virtual device node in
3916441Sjm22469  * the MD.  For virtual devices, the parent is always
3926441Sjm22469  * "channel-devices", so scan the MD using the "back" arcs
3936441Sjm22469  * looking for a node with that name.
3946441Sjm22469  */
3956441Sjm22469 static mde_cookie_t
dr_vio_find_parent_md(md_t * mdp,mde_cookie_t node)3966441Sjm22469 dr_vio_find_parent_md(md_t *mdp, mde_cookie_t node)
3976441Sjm22469 {
3986441Sjm22469 	int		max_nodes;
3996441Sjm22469 	int		num_nodes;
4006441Sjm22469 	int		listsz;
4016441Sjm22469 	mde_cookie_t    *listp;
4026441Sjm22469 	mde_cookie_t	pnode = MDE_INVAL_ELEM_COOKIE;
4036441Sjm22469 
4046441Sjm22469 	max_nodes = md_node_count(mdp);
4056441Sjm22469 	listsz = max_nodes * sizeof (mde_cookie_t);
4066441Sjm22469 	listp = kmem_zalloc(listsz, KM_SLEEP);
407*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
408*7899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
4096441Sjm22469 
4106441Sjm22469 	num_nodes = md_scan_dag(mdp, node,
4116441Sjm22469 	    md_find_name(mdp, "channel-devices"),
4126441Sjm22469 	    md_find_name(mdp, "back"), listp);
4136441Sjm22469 
4146441Sjm22469 	ASSERT(num_nodes == 1);
4156441Sjm22469 
4166441Sjm22469 	if (num_nodes == 1)
4176441Sjm22469 		pnode = listp[0];
4186441Sjm22469 
419*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %d\n",
420*7899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
4216441Sjm22469 	kmem_free(listp, listsz);
4226441Sjm22469 
4236441Sjm22469 	return (pnode);
4246441Sjm22469 }
4256441Sjm22469 
4266441Sjm22469 static int
dr_io_configure(dr_vio_req_t * req,dr_vio_res_t * res)4276441Sjm22469 dr_io_configure(dr_vio_req_t *req, dr_vio_res_t *res)
4286441Sjm22469 {
4296441Sjm22469 	int		rv = ENXIO;
4306441Sjm22469 	int		listsz;
4316441Sjm22469 	int		nnodes;
4326441Sjm22469 	uint64_t	devid = req->dev_id;
4336441Sjm22469 	uint64_t	pdevid;
4346441Sjm22469 	char		*name = req->name;
4356441Sjm22469 	char		*pname;
4366441Sjm22469 	md_t		*mdp = NULL;
4376441Sjm22469 	mde_cookie_t	*listp = NULL;
4386441Sjm22469 	mde_cookie_t	node;
4396441Sjm22469 	mde_cookie_t	pnode;
4406441Sjm22469 	dev_info_t	*pdip = NULL;
4416441Sjm22469 	dev_info_t	*dip;
4426441Sjm22469 	devi_branch_t	br;
4436441Sjm22469 	cb_arg_t	cba;
4446441Sjm22469 	int		drctl_cmd;
4456441Sjm22469 	int		drctl_flags = 0;
4466441Sjm22469 	drctl_rsrc_t	*drctl_req;
4476441Sjm22469 	size_t		drctl_req_len;
448*7899SJames.Marks@Sun.COM 	drctl_rsrc_t	*drctl_rsrc = NULL;
4496441Sjm22469 	drctl_cookie_t	drctl_res_ck;
4506441Sjm22469 	char		*p;
451*7899SJames.Marks@Sun.COM 	drctl_resp_t	*drctl_resp;
452*7899SJames.Marks@Sun.COM 	size_t		drctl_resp_len = 0;
4536441Sjm22469 
4546441Sjm22469 	res->result = DR_VIO_RES_FAILURE;
4556441Sjm22469 
4566441Sjm22469 	if ((dip = dr_io_find_node(name, devid)) != NULL) {
4576441Sjm22469 		DR_DBG_IO("%s: %s@%ld already configured\n",
4586441Sjm22469 		    __func__, name, devid);
4596441Sjm22469 
4606441Sjm22469 		/* Return success if resources is already there. */
4616441Sjm22469 		res->result = DR_VIO_RES_OK;
4626441Sjm22469 		res->status = DR_VIO_STAT_CONFIGURED;
4636441Sjm22469 		e_ddi_branch_rele(dip);
4646441Sjm22469 		return (0);
4656441Sjm22469 	}
4666441Sjm22469 
4676441Sjm22469 	/* Assume we fail to find the node to be added. */
4686441Sjm22469 	res->status = DR_VIO_STAT_NOT_PRESENT;
4696441Sjm22469 
4706441Sjm22469 	if ((mdp = md_get_handle()) == NULL) {
4716441Sjm22469 		DR_DBG_IO("%s: unable to initialize MD\n", __func__);
4726441Sjm22469 		return (ENXIO);
4736441Sjm22469 	}
4746441Sjm22469 
4756441Sjm22469 	nnodes = md_node_count(mdp);
4766441Sjm22469 	ASSERT(nnodes > 0);
4776441Sjm22469 
4786441Sjm22469 	listsz = nnodes * sizeof (mde_cookie_t);
4796441Sjm22469 	listp = kmem_zalloc(listsz, KM_SLEEP);
480*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
481*7899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
4826441Sjm22469 
4836441Sjm22469 	/*
4846441Sjm22469 	 * Get the MD device node.
4856441Sjm22469 	 */
4866441Sjm22469 	node = dr_io_find_node_md(mdp, name, devid, listp);
4876441Sjm22469 
4886441Sjm22469 	if (node == MDE_INVAL_ELEM_COOKIE) {
4896441Sjm22469 		DR_DBG_IO("%s: scan for %s name node failed\n", __func__, name);
4906441Sjm22469 		res->result = DR_VIO_RES_NOT_IN_MD;
4916441Sjm22469 		goto done;
4926441Sjm22469 	}
4936441Sjm22469 
4946441Sjm22469 	/*
4956441Sjm22469 	 * Get the MD parent node.
4966441Sjm22469 	 */
4976441Sjm22469 	pnode = dr_vio_find_parent_md(mdp, node);
4986441Sjm22469 	if (pnode == MDE_INVAL_ELEM_COOKIE) {
4996441Sjm22469 		DR_DBG_IO("%s: failed to find MD parent of %lx\n",
5006441Sjm22469 		    __func__, pnode);
5016441Sjm22469 		goto done;
5026441Sjm22469 	}
5036441Sjm22469 
5046441Sjm22469 	if (md_get_prop_str(mdp, pnode, "name", &pname)) {
5056441Sjm22469 		DR_DBG_IO("%s: failed to read "
5066441Sjm22469 		    "'name' for pnode %lx from MD\n", __func__, pnode);
5076441Sjm22469 		goto done;
5086441Sjm22469 	}
5096441Sjm22469 
5106441Sjm22469 	if (md_get_prop_val(mdp, pnode, "cfg-handle", &pdevid)) {
5116441Sjm22469 		DR_DBG_IO("%s: failed to read 'cfg-handle' "
5126441Sjm22469 		    "for pnode '%s' from MD\n", __func__, pname);
5136441Sjm22469 		goto done;
5146441Sjm22469 	}
5156441Sjm22469 
5166441Sjm22469 	DR_DBG_IO("%s: parent device %s@%lx\n", __func__, pname, pdevid);
5176441Sjm22469 
5186441Sjm22469 	/*
5196441Sjm22469 	 * Get the devinfo parent node.
5206441Sjm22469 	 */
5216441Sjm22469 	if ((pdip = dr_io_find_node(pname, pdevid)) == NULL) {
5226441Sjm22469 		DR_DBG_IO("%s: parent device %s@%ld not found\n",
5236441Sjm22469 		    __func__, pname, pdevid);
5246441Sjm22469 		goto done;
5256441Sjm22469 	}
5266441Sjm22469 
5276441Sjm22469 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
5286441Sjm22469 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
529*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
530*7899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
5316441Sjm22469 	drctl_req->status = DRCTL_STATUS_INIT;
5326441Sjm22469 
5336441Sjm22469 	drctl_cmd = DRCTL_IO_CONFIG_REQUEST;
5346441Sjm22469 
5356441Sjm22469 	/*
5366441Sjm22469 	 * Construct the path of the device as it will be if it
5376441Sjm22469 	 * is successfully added.
5386441Sjm22469 	 */
5396441Sjm22469 	p = drctl_req->res_dev_path;
5406441Sjm22469 	(void) sprintf(p, "/devices");
5416441Sjm22469 	(void) ddi_pathname(pdip, p + strlen(p));
5426441Sjm22469 	(void) sprintf(p + strlen(p), "/%s@%ld", name, devid);
5436441Sjm22469 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
5446441Sjm22469 
545*7899SJames.Marks@Sun.COM 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
546*7899SJames.Marks@Sun.COM 	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
547*7899SJames.Marks@Sun.COM 
548*7899SJames.Marks@Sun.COM 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
549*7899SJames.Marks@Sun.COM 
550*7899SJames.Marks@Sun.COM 	drctl_rsrc = drctl_resp->resp_resources;
5516441Sjm22469 
552*7899SJames.Marks@Sun.COM 	if (rv != 0) {
5536441Sjm22469 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
554*7899SJames.Marks@Sun.COM 
555*7899SJames.Marks@Sun.COM 		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
556*7899SJames.Marks@Sun.COM 
557*7899SJames.Marks@Sun.COM 		(void) strlcpy(res->reason,
558*7899SJames.Marks@Sun.COM 		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
559*7899SJames.Marks@Sun.COM 
560*7899SJames.Marks@Sun.COM 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
561*7899SJames.Marks@Sun.COM 
5626441Sjm22469 		goto done;
5636441Sjm22469 
564*7899SJames.Marks@Sun.COM 	}
565*7899SJames.Marks@Sun.COM 
566*7899SJames.Marks@Sun.COM 	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
567*7899SJames.Marks@Sun.COM 
568*7899SJames.Marks@Sun.COM 	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
569*7899SJames.Marks@Sun.COM 
5706441Sjm22469 		res->result = DR_VIO_RES_BLOCKED;
5716441Sjm22469 
5726441Sjm22469 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
573*7899SJames.Marks@Sun.COM 		p = (char *)drctl_rsrc + drctl_rsrc->offset;
5746441Sjm22469 
575*7899SJames.Marks@Sun.COM 		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
5766441Sjm22469 
5776441Sjm22469 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
5786441Sjm22469 
5796441Sjm22469 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
5806441Sjm22469 
5816441Sjm22469 		rv = EPERM;
5826441Sjm22469 	} else {
5836441Sjm22469 		cba.mdp = mdp;
5846441Sjm22469 		cba.node = node;
5856441Sjm22469 
5866441Sjm22469 		br.arg = (void *)&cba;
5876441Sjm22469 		br.type = DEVI_BRANCH_SID;
5886441Sjm22469 		br.create.sid_branch_create = new_dev_node;
5896441Sjm22469 		br.devi_branch_callback = NULL;
5906441Sjm22469 
5916441Sjm22469 		rv = e_ddi_branch_create(pdip,
5926441Sjm22469 		    &br, NULL, DEVI_BRANCH_CONFIGURE);
5936441Sjm22469 
5946441Sjm22469 		drctl_req->status = (rv == 0) ?
5956441Sjm22469 		    DRCTL_STATUS_CONFIG_SUCCESS : DRCTL_STATUS_CONFIG_FAILURE;
5966441Sjm22469 
5976441Sjm22469 		DR_DBG_IO("%s: %s@%ld = %d\n", __func__, name, devid, rv);
5986441Sjm22469 	}
5996441Sjm22469 
6006441Sjm22469 	if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
6016441Sjm22469 		DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
6026441Sjm22469 
6036441Sjm22469 done:
604*7899SJames.Marks@Sun.COM 	if (listp) {
605*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %d\n",
606*7899SJames.Marks@Sun.COM 		    __func__, (void *)listp, listsz);
6076441Sjm22469 		kmem_free(listp, listsz);
608*7899SJames.Marks@Sun.COM 	}
6096441Sjm22469 
6106441Sjm22469 	if (mdp)
6116441Sjm22469 		(void) md_fini_handle(mdp);
6126441Sjm22469 
6136441Sjm22469 	if (pdip)
6146441Sjm22469 		e_ddi_branch_rele(pdip);
6156441Sjm22469 
616*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
617*7899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
6186441Sjm22469 	kmem_free(drctl_req, drctl_req_len);
619*7899SJames.Marks@Sun.COM 
620*7899SJames.Marks@Sun.COM 	if (drctl_resp) {
621*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
622*7899SJames.Marks@Sun.COM 		    __func__, (void *)drctl_resp, drctl_resp_len);
623*7899SJames.Marks@Sun.COM 		kmem_free(drctl_resp, drctl_resp_len);
624*7899SJames.Marks@Sun.COM 	}
6256441Sjm22469 
6266441Sjm22469 	if (rv == 0) {
6276441Sjm22469 		res->result = DR_VIO_RES_OK;
6286441Sjm22469 		res->status = DR_VIO_STAT_CONFIGURED;
6296441Sjm22469 
6306441Sjm22469 		/* notify interested parties about the operation */
6316441Sjm22469 		dr_generate_event(DR_TYPE_VIO, SE_HINT_INSERT);
6326441Sjm22469 	} else {
6336441Sjm22469 		res->status = DR_VIO_STAT_UNCONFIGURED;
6346441Sjm22469 	}
6356441Sjm22469 
6366441Sjm22469 	return (rv);
6376441Sjm22469 }
6386441Sjm22469 
6396441Sjm22469 static int
dr_io_unconfigure(dr_vio_req_t * req,dr_vio_res_t * res)6406441Sjm22469 dr_io_unconfigure(dr_vio_req_t *req, dr_vio_res_t *res)
6416441Sjm22469 {
6426441Sjm22469 	int		rv;
6436441Sjm22469 	char		*name = req->name;
6446441Sjm22469 	char		*p;
6456441Sjm22469 	uint64_t	devid = req->dev_id;
6466441Sjm22469 	dev_info_t	*dip;
6476441Sjm22469 	dev_info_t	*fdip = NULL;
6486441Sjm22469 	int		drctl_cmd;
6496441Sjm22469 	int		drctl_flags = 0;
6506441Sjm22469 	drctl_rsrc_t	*drctl_req;
6516441Sjm22469 	size_t		drctl_req_len;
652*7899SJames.Marks@Sun.COM 	drctl_rsrc_t	*drctl_rsrc = NULL;
6536441Sjm22469 	drctl_cookie_t	drctl_res_ck;
654*7899SJames.Marks@Sun.COM 	drctl_resp_t	*drctl_resp;
655*7899SJames.Marks@Sun.COM 	size_t		drctl_resp_len;
6566441Sjm22469 
6576441Sjm22469 	if ((dip = dr_io_find_node(name, devid)) == NULL) {
6586441Sjm22469 		DR_DBG_IO("%s: %s@%ld already unconfigured\n",
6596441Sjm22469 		    __func__, name, devid);
6606441Sjm22469 		res->result = DR_VIO_RES_OK;
6616441Sjm22469 		res->status = DR_VIO_STAT_NOT_PRESENT;
6626441Sjm22469 		return (0);
6636441Sjm22469 	}
6646441Sjm22469 
6656441Sjm22469 	res->result = DR_VIO_RES_FAILURE;
6666441Sjm22469 
6676441Sjm22469 	ASSERT(e_ddi_branch_held(dip));
6686441Sjm22469 
6696441Sjm22469 	/* Assume we fail to unconfigure the resource. */
6706441Sjm22469 	res->status = DR_VIO_STAT_CONFIGURED;
6716441Sjm22469 
6726441Sjm22469 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
6736441Sjm22469 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
674*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
675*7899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
6766441Sjm22469 	drctl_req->status = DRCTL_STATUS_INIT;
6776441Sjm22469 
6786441Sjm22469 	drctl_cmd = DRCTL_IO_UNCONFIG_REQUEST;
6796441Sjm22469 
6806441Sjm22469 	if (req->msg_type == DR_VIO_FORCE_UNCONFIG)
6816441Sjm22469 		drctl_flags = DRCTL_FLAG_FORCE;
6826441Sjm22469 
6836441Sjm22469 	p = drctl_req->res_dev_path;
6846441Sjm22469 	(void) sprintf(p, "/devices");
6856441Sjm22469 	(void) ddi_pathname(dip, p + strlen(p));
6866441Sjm22469 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
6876441Sjm22469 
688*7899SJames.Marks@Sun.COM 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
689*7899SJames.Marks@Sun.COM 	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
690*7899SJames.Marks@Sun.COM 
691*7899SJames.Marks@Sun.COM 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
692*7899SJames.Marks@Sun.COM 
693*7899SJames.Marks@Sun.COM 	drctl_rsrc = drctl_resp->resp_resources;
694*7899SJames.Marks@Sun.COM 
695*7899SJames.Marks@Sun.COM 	if (rv != 0) {
6966441Sjm22469 
6976441Sjm22469 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
698*7899SJames.Marks@Sun.COM 
699*7899SJames.Marks@Sun.COM 		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
700*7899SJames.Marks@Sun.COM 
701*7899SJames.Marks@Sun.COM 		(void) strlcpy(res->reason,
702*7899SJames.Marks@Sun.COM 		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
7036441Sjm22469 
704*7899SJames.Marks@Sun.COM 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
705*7899SJames.Marks@Sun.COM 
706*7899SJames.Marks@Sun.COM 		goto done;
707*7899SJames.Marks@Sun.COM 	}
708*7899SJames.Marks@Sun.COM 
709*7899SJames.Marks@Sun.COM 	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
7106441Sjm22469 		res->result = DR_VIO_RES_BLOCKED;
7116441Sjm22469 
7126441Sjm22469 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
713*7899SJames.Marks@Sun.COM 		p = (char *)drctl_rsrc + drctl_rsrc->offset;
7146441Sjm22469 
715*7899SJames.Marks@Sun.COM 		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
7166441Sjm22469 
7176441Sjm22469 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
7186441Sjm22469 
7196441Sjm22469 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
7206441Sjm22469 
7216441Sjm22469 		rv = EPERM;
7226441Sjm22469 	} else if (rv = e_ddi_branch_destroy(dip, &fdip, 0)) {
7236441Sjm22469 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7246441Sjm22469 
725*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
726*7899SJames.Marks@Sun.COM 		    __func__, (void *)path, MAXPATHLEN);
7276441Sjm22469 		/*
7286441Sjm22469 		 * If non-NULL, fdip is held and must be released.
7296441Sjm22469 		 */
7306441Sjm22469 		if (fdip != NULL) {
7316441Sjm22469 			(void) ddi_pathname(fdip, path);
7326441Sjm22469 			ddi_release_devi(fdip);
7336441Sjm22469 		} else {
7346441Sjm22469 			(void) ddi_pathname(dip, path);
7356441Sjm22469 		}
7366441Sjm22469 
7376441Sjm22469 		DR_DBG_IO("%s: node removal failed: %s (%p)",
7386441Sjm22469 		    __func__, path, (fdip) ? (void *)fdip : (void *)dip);
7396441Sjm22469 
7406441Sjm22469 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
7416441Sjm22469 
742*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %d\n",
743*7899SJames.Marks@Sun.COM 		    __func__, (void *)path, MAXPATHLEN);
7446441Sjm22469 		kmem_free(path, MAXPATHLEN);
7456441Sjm22469 	} else {
7466441Sjm22469 		drctl_req->status = DRCTL_STATUS_CONFIG_SUCCESS;
7476441Sjm22469 	}
7486441Sjm22469 
7496441Sjm22469 	if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
7506441Sjm22469 		DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
7516441Sjm22469 
7526441Sjm22469 	DR_DBG_IO("%s: (%s@%ld) = %d\n", __func__, name, devid, rv);
7536441Sjm22469 
7546441Sjm22469 	if (rv == 0) {
7556441Sjm22469 		res->result = DR_VIO_RES_OK;
7566441Sjm22469 		res->status = DR_VIO_STAT_UNCONFIGURED;
7576441Sjm22469 
7586441Sjm22469 		/* Notify interested parties about the operation. */
7596441Sjm22469 		dr_generate_event(DR_TYPE_VIO, SE_HINT_REMOVE);
7606441Sjm22469 	}
7616441Sjm22469 done:
762*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
763*7899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
7646441Sjm22469 	kmem_free(drctl_req, drctl_req_len);
7656441Sjm22469 
766*7899SJames.Marks@Sun.COM 	if (drctl_resp) {
767*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
768*7899SJames.Marks@Sun.COM 		    __func__, (void *)drctl_resp, drctl_resp_len);
769*7899SJames.Marks@Sun.COM 		kmem_free(drctl_resp, drctl_resp_len);
770*7899SJames.Marks@Sun.COM 	}
7716441Sjm22469 
7726441Sjm22469 	return (rv);
7736441Sjm22469 }
7746441Sjm22469 
7756441Sjm22469 static void
dr_vio_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)7766441Sjm22469 dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
7776441Sjm22469 {
7786441Sjm22469 	_NOTE(ARGUNUSED(arg))
7796441Sjm22469 
7806441Sjm22469 	size_t		res_len;
7816441Sjm22469 	dr_vio_res_t	*res;
7826441Sjm22469 	dr_vio_req_t	*req;
7836441Sjm22469 
7846441Sjm22469 	/*
7856441Sjm22469 	 * Allocate a response buffer, because we always want to
7866441Sjm22469 	 * send back a response message.
7876441Sjm22469 	 */
7886441Sjm22469 	res_len = sizeof (dr_vio_res_t) + DR_VIO_MAXREASONLEN;
7896441Sjm22469 	res = kmem_zalloc(res_len, KM_SLEEP);
790*7899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
791*7899SJames.Marks@Sun.COM 	    __func__, (void *)res, res_len);
7926441Sjm22469 	res->result = DR_VIO_RES_FAILURE;
7936441Sjm22469 
7946441Sjm22469 	/*
7956441Sjm22469 	 * Sanity check the message
7966441Sjm22469 	 */
7976441Sjm22469 	if (buf == NULL) {
7986441Sjm22469 		DR_DBG_IO("empty message: expected at least %ld bytes\n",
7996441Sjm22469 		    sizeof (dr_vio_req_t));
8006441Sjm22469 		goto done;
8016441Sjm22469 	}
8026441Sjm22469 	if (buflen < sizeof (dr_vio_req_t)) {
8036441Sjm22469 		DR_DBG_IO("incoming message short: expected at least %ld "
8046441Sjm22469 		    "bytes, received %ld\n", sizeof (dr_vio_req_t), buflen);
8056441Sjm22469 		goto done;
8066441Sjm22469 	}
8076441Sjm22469 
8086441Sjm22469 	DR_DBG_TRANS("incoming request:\n");
8096441Sjm22469 	DR_DBG_DUMP_MSG(buf, buflen);
8106441Sjm22469 
8116441Sjm22469 	req = buf;
8126441Sjm22469 	switch (req->msg_type) {
8136441Sjm22469 	case DR_VIO_CONFIGURE:
8146441Sjm22469 		(void) dr_io_configure(req, res);
8156441Sjm22469 		break;
8166441Sjm22469 	case DR_VIO_FORCE_UNCONFIG:
8176441Sjm22469 	case DR_VIO_UNCONFIGURE:
8186441Sjm22469 		(void) dr_io_unconfigure(req, res);
8196441Sjm22469 		break;
8206441Sjm22469 	default:
8216441Sjm22469 		cmn_err(CE_NOTE, "bad msg_type %d\n", req->msg_type);
8226441Sjm22469 		break;
8236441Sjm22469 	}
8246441Sjm22469 done:
8256441Sjm22469 	res->req_num = (req) ? req->req_num : 0;
8266441Sjm22469 
8276441Sjm22469 	DR_DBG_TRANS("outgoing response:\n");
8286441Sjm22469 	DR_DBG_DUMP_MSG(res, res_len);
8296441Sjm22469 
8306441Sjm22469 	/* send back the response */
8316441Sjm22469 	if (ds_cap_send(ds_vio_handle, res, res_len) != 0)
8326441Sjm22469 		DR_DBG_IO("ds_send failed\n");
8336441Sjm22469 
834*7899SJames.Marks@Sun.COM 	if (res) {
835*7899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
836*7899SJames.Marks@Sun.COM 		    __func__, (void *)res, res_len);
8376441Sjm22469 		kmem_free(res, res_len);
838*7899SJames.Marks@Sun.COM 	}
8396441Sjm22469 }
8406441Sjm22469 
8416441Sjm22469 static void
dr_vio_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)8426441Sjm22469 dr_vio_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
8436441Sjm22469 {
8446441Sjm22469 	DR_DBG_IO("vio_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
8456441Sjm22469 	    arg, ver->major, ver->minor, hdl);
8466441Sjm22469 
8476441Sjm22469 	ds_vio_handle = hdl;
8486441Sjm22469 }
8496441Sjm22469 
8506441Sjm22469 static void
dr_vio_unreg_handler(ds_cb_arg_t arg)8516441Sjm22469 dr_vio_unreg_handler(ds_cb_arg_t arg)
8526441Sjm22469 {
8536441Sjm22469 	DR_DBG_IO("vio_unreg_handler: arg=0x%p\n", arg);
8546441Sjm22469 
8556441Sjm22469 	ds_vio_handle = DS_INVALID_HDL;
8566441Sjm22469 }
8576441Sjm22469 
8586441Sjm22469 static int
dr_io_init(void)8596441Sjm22469 dr_io_init(void)
8606441Sjm22469 {
8616441Sjm22469 	int	rv;
8626441Sjm22469 
8636441Sjm22469 	if ((rv = ds_cap_init(&dr_vio_cap, &dr_vio_ops)) != 0) {
8646441Sjm22469 		cmn_err(CE_NOTE, "ds_cap_init vio failed: %d", rv);
8656441Sjm22469 		return (-1);
8666441Sjm22469 	}
8676441Sjm22469 
8686441Sjm22469 	return (0);
8696441Sjm22469 }
8706441Sjm22469 
8716441Sjm22469 static int
dr_io_fini(void)8726441Sjm22469 dr_io_fini(void)
8736441Sjm22469 {
8746441Sjm22469 	int	rv;
8756441Sjm22469 
8766441Sjm22469 	if ((rv = ds_cap_fini(&dr_vio_cap)) != 0) {
8776441Sjm22469 		cmn_err(CE_NOTE, "ds_cap_fini vio failed: %d", rv);
8786441Sjm22469 		return (-1);
8796441Sjm22469 	}
8806441Sjm22469 
8816441Sjm22469 	return (0);
8826441Sjm22469 }
8836441Sjm22469 
8846441Sjm22469 int
_init(void)8856441Sjm22469 _init(void)
8866441Sjm22469 {
8876441Sjm22469 	int	status;
8886441Sjm22469 
8896441Sjm22469 	/* check that IO DR is enabled */
8906441Sjm22469 	if (dr_is_disabled(DR_TYPE_VIO)) {
8916441Sjm22469 		cmn_err(CE_CONT, "!VIO DR is disabled\n");
8926441Sjm22469 		return (-1);
8936441Sjm22469 	}
8946441Sjm22469 
8956441Sjm22469 	if ((status = dr_io_init()) != 0) {
8966441Sjm22469 		cmn_err(CE_NOTE, "VIO DR initialization failed");
8976441Sjm22469 		return (status);
8986441Sjm22469 	}
8996441Sjm22469 
9006441Sjm22469 	if ((status = mod_install(&modlinkage)) != 0) {
9016441Sjm22469 		(void) dr_io_fini();
9026441Sjm22469 	}
9036441Sjm22469 
9046441Sjm22469 	return (status);
9056441Sjm22469 }
9066441Sjm22469 
9076441Sjm22469 int
_info(struct modinfo * modinfop)9086441Sjm22469 _info(struct modinfo *modinfop)
9096441Sjm22469 {
9106441Sjm22469 	return (mod_info(&modlinkage, modinfop));
9116441Sjm22469 }
9126441Sjm22469 
9136441Sjm22469 int dr_io_allow_unload = 0;
9146441Sjm22469 
9156441Sjm22469 int
_fini(void)9166441Sjm22469 _fini(void)
9176441Sjm22469 {
9186441Sjm22469 	int	status;
9196441Sjm22469 
9206441Sjm22469 	if (dr_io_allow_unload == 0)
9216441Sjm22469 		return (EBUSY);
9226441Sjm22469 
9236441Sjm22469 	if ((status = mod_remove(&modlinkage)) == 0) {
9246441Sjm22469 		(void) dr_io_fini();
9256441Sjm22469 	}
9266441Sjm22469 
9276441Sjm22469 	return (status);
9286441Sjm22469 }
929