Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * sun4v VIO DR Module
     29  */
     30 
     31 #include <sys/modctl.h>
     32 #include <sys/sunddi.h>
     33 #include <sys/sunndi.h>
     34 #include <sys/note.h>
     35 #include <sys/sysevent/dr.h>
     36 #include <sys/hypervisor_api.h>
     37 #include <sys/mach_descrip.h>
     38 #include <sys/mdesc.h>
     39 #include <sys/mdesc_impl.h>
     40 #include <sys/ds.h>
     41 #include <sys/drctl.h>
     42 #include <sys/dr_util.h>
     43 #include <sys/dr_io.h>
     44 #include <sys/promif.h>
     45 #include <sys/machsystm.h>
     46 #include <sys/ethernet.h>
     47 #include <sys/hotplug/pci/pcicfg.h>
     48 
     49 
     50 static struct modlmisc modlmisc = {
     51 	&mod_miscops,
     52 	"sun4v VIO DR"
     53 };
     54 
     55 static struct modlinkage modlinkage = {
     56 	MODREV_1,
     57 	(void *)&modlmisc,
     58 	NULL
     59 };
     60 
     61 
     62 /*
     63  * VIO DS Interface
     64  */
     65 
     66 /*
     67  * Global DS Handle
     68  */
     69 static ds_svc_hdl_t ds_vio_handle;
     70 
     71 /*
     72  * Supported DS Capability Versions
     73  */
     74 static ds_ver_t		dr_vio_vers[] = { { 1, 0 } };
     75 #define	DR_VIO_NVERS	(sizeof (dr_vio_vers) / sizeof (dr_vio_vers[0]))
     76 
     77 /*
     78  * DS Capability Description
     79  */
     80 static ds_capability_t dr_vio_cap = {
     81 	DR_VIO_DS_ID,		/* svc_id */
     82 	dr_vio_vers,		/* vers */
     83 	DR_VIO_NVERS		/* nvers */
     84 };
     85 
     86 /*
     87  * DS Callbacks
     88  */
     89 static void dr_vio_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
     90 static void dr_vio_unreg_handler(ds_cb_arg_t arg);
     91 static void dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
     92 
     93 /*
     94  * DS Client Ops Vector
     95  */
     96 static ds_clnt_ops_t dr_vio_ops = {
     97 	dr_vio_reg_handler,	/* ds_reg_cb */
     98 	dr_vio_unreg_handler,	/* ds_unreg_cb */
     99 	dr_vio_data_handler,	/* ds_data_cb */
    100 	NULL			/* cb_arg */
    101 };
    102 
    103 
    104 typedef struct {
    105 	char		*name;
    106 	uint64_t	devid;
    107 	dev_info_t	*dip;
    108 } dr_search_arg_t;
    109 
    110 static int
    111 dr_io_check_node(dev_info_t *dip, void *arg)
    112 {
    113 	char 		*name;
    114 	uint64_t	devid;
    115 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
    116 
    117 	name = ddi_node_name(dip);
    118 
    119 	if (strcmp(name, sarg->name) != 0)
    120 		return (DDI_WALK_CONTINUE);
    121 
    122 	devid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
    123 	    "reg", -1);
    124 
    125 	DR_DBG_IO("%s: found devid=%ld, looking for %ld\n",
    126 	    __func__, devid, sarg->devid);
    127 
    128 	if (devid == sarg->devid) {
    129 		DR_DBG_IO("%s: matched", __func__);
    130 
    131 		/* matching node must be returned held */
    132 		if (!e_ddi_branch_held(dip))
    133 			e_ddi_branch_hold(dip);
    134 
    135 		sarg->dip = dip;
    136 		return (DDI_WALK_TERMINATE);
    137 	}
    138 
    139 	return (DDI_WALK_CONTINUE);
    140 }
    141 
    142 /*
    143  * Walk the device tree to find the dip corresponding to the devid
    144  * passed in. If present, the dip is returned held. The caller must
    145  * release the hold on the dip once it is no longer required. If no
    146  * matching node if found, NULL is returned.
    147  */
    148 static dev_info_t *
    149 dr_io_find_node(char *name, uint64_t devid)
    150 {
    151 	dr_search_arg_t	arg;
    152 
    153 	DR_DBG_IO("dr_io_find_node...\n");
    154 
    155 	arg.name = name;
    156 	arg.devid = devid;
    157 	arg.dip = NULL;
    158 
    159 	ddi_walk_devs(ddi_root_node(), dr_io_check_node, &arg);
    160 
    161 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
    162 
    163 	return ((arg.dip) ? arg.dip : NULL);
    164 }
    165 
    166 /*
    167  * Look up a particular IO node in the MD. Returns the mde_cookie_t
    168  * representing that IO node if present, and MDE_INVAL_ELEM_COOKIE otherwise.
    169  * It is assumed the scratch array has already been allocated so that
    170  * it can accommodate the worst case scenario, every node in the MD.
    171  */
    172 static mde_cookie_t
    173 dr_io_find_node_md(md_t *mdp, char *name, uint64_t id, mde_cookie_t *listp)
    174 {
    175 	int		i;
    176 	int		nnodes;
    177 	char		*devnm;
    178 	uint64_t	devid;
    179 	mde_cookie_t	rootnode;
    180 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
    181 
    182 	DR_DBG_IO("%s: %s@%ld\n", __func__, name, id);
    183 
    184 	rootnode = md_root_node(mdp);
    185 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
    186 
    187 	/*
    188 	 * Scan the DAG for all candidate nodes.
    189 	 */
    190 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "virtual-device"),
    191 	    md_find_name(mdp, "fwd"), listp);
    192 
    193 	if (nnodes < 0) {
    194 		DR_DBG_IO("%s: scan for "
    195 		    "'virtual-device' nodes failed\n", __func__);
    196 		return (result);
    197 	}
    198 
    199 	DR_DBG_IO("%s: found %d nodes in the MD\n", __func__, nnodes);
    200 
    201 	/*
    202 	 * Find the node of interest
    203 	 */
    204 	for (i = 0; i < nnodes; i++) {
    205 
    206 		if (md_get_prop_str(mdp, listp[i], "name", &devnm)) {
    207 			DR_DBG_IO("%s: missing 'name' property for"
    208 			    " IO node %d\n", __func__, i);
    209 			return (DDI_WALK_ERROR);
    210 		}
    211 
    212 		if (strcmp(devnm, name) != 0)
    213 			continue;
    214 
    215 		if (md_get_prop_val(mdp, listp[i], "cfg-handle", &devid)) {
    216 			DR_DBG_IO("%s: missing 'cfg-handle' property for"
    217 			    " IO node %d\n", __func__, i);
    218 			break;
    219 		}
    220 
    221 		if (devid == id) {
    222 			/* found a match */
    223 			DR_DBG_IO("%s: found IO node %s@%ld "
    224 			    "in MD\n", __func__, name, id);
    225 			result = listp[i];
    226 			break;
    227 		}
    228 	}
    229 
    230 	if (result == MDE_INVAL_ELEM_COOKIE)
    231 		DR_DBG_IO("%s: IO node %ld not in MD\n", __func__, id);
    232 
    233 	return (result);
    234 }
    235 
    236 typedef struct {
    237 	md_t		*mdp;
    238 	mde_cookie_t	node;
    239 	dev_info_t	*dip;
    240 } cb_arg_t;
    241 
    242 #define	STR_ARR_LEN	5
    243 
    244 static int
    245 new_dev_node(dev_info_t *new_node, void *arg, uint_t flags)
    246 {
    247 	_NOTE(ARGUNUSED(flags))
    248 
    249 	cb_arg_t	*cba;
    250 	char		*devnm, *devtype;
    251 	char		*compat;
    252 	uint64_t	devid;
    253 	int		len = 0;
    254 	char		*curr;
    255 	int		i = 0;
    256 	char		*str_arr[STR_ARR_LEN];
    257 
    258 	cba = (cb_arg_t *)arg;
    259 
    260 	/*
    261 	 * Add 'name' property
    262 	 */
    263 	if (md_get_prop_str(cba->mdp, cba->node, "name", &devnm)) {
    264 		DR_DBG_IO("%s: failed to read 'name' prop from MD\n", __func__);
    265 		return (DDI_WALK_ERROR);
    266 	}
    267 	DR_DBG_IO("%s: device name is %s\n", __func__, devnm);
    268 
    269 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
    270 	    "name", devnm) != DDI_SUCCESS) {
    271 		DR_DBG_IO("%s: failed to create 'name' prop\n", __func__);
    272 		return (DDI_WALK_ERROR);
    273 	}
    274 
    275 	/*
    276 	 * Add 'compatible' property
    277 	 */
    278 	if (md_get_prop_data(cba->mdp, cba->node, "compatible",
    279 	    (uint8_t **)&compat, &len)) {
    280 		DR_DBG_IO("%s: failed to read "
    281 		    "'compatible' prop from MD\n", __func__);
    282 		return (DDI_WALK_ERROR);
    283 	}
    284 
    285 	/* parse the MD string array */
    286 	curr = compat;
    287 	while (curr < (compat + len)) {
    288 
    289 		DR_DBG_IO("%s: adding '%s' to "
    290 		    "'compatible' prop\n", __func__, curr);
    291 
    292 		str_arr[i++] = curr;
    293 		curr += strlen(curr) + 1;
    294 
    295 		if (i == STR_ARR_LEN) {
    296 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
    297 			break;
    298 		}
    299 	}
    300 
    301 
    302 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
    303 	    "compatible", str_arr, i) != DDI_SUCCESS) {
    304 		DR_DBG_IO("%s: cannot create 'compatible' prop\n", __func__);
    305 		return (DDI_WALK_ERROR);
    306 	}
    307 
    308 	/*
    309 	 * Add 'device_type' property
    310 	 */
    311 	if (md_get_prop_str(cba->mdp, cba->node, "device-type", &devtype)) {
    312 		DR_DBG_IO("%s: failed to read "
    313 		    "'device-type' prop from MD\n", __func__);
    314 		return (DDI_WALK_ERROR);
    315 	}
    316 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
    317 	    "device_type", devtype) != DDI_SUCCESS) {
    318 		DR_DBG_IO("%s: failed to create "
    319 		    "'device-type' prop\n", __func__);
    320 		return (DDI_WALK_ERROR);
    321 	}
    322 
    323 	DR_DBG_IO("%s: device type is %s\n", __func__, devtype);
    324 
    325 	/*
    326 	 * Add 'reg' (cfg-handle) property
    327 	 */
    328 	if (md_get_prop_val(cba->mdp, cba->node, "cfg-handle", &devid)) {
    329 		DR_DBG_IO("%s: failed to read "
    330 		    "'cfg-handle' prop from MD\n", __func__);
    331 		return (DDI_WALK_ERROR);
    332 	}
    333 
    334 	DR_DBG_IO("%s: new device is %s@%ld\n", __func__, devnm, devid);
    335 
    336 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node, "reg", devid)
    337 	    != DDI_SUCCESS) {
    338 		DR_DBG_IO("%s: failed to create 'reg' prop\n", __func__);
    339 		return (DDI_WALK_ERROR);
    340 	}
    341 
    342 	/* if vnet/vswitch, probe and add mac-address and mtu properties */
    343 	if (strcmp(devnm, "vsw") == 0 || strcmp(devnm, "network") == 0) {
    344 
    345 		int i, j;
    346 		uint64_t mtu, macaddr;
    347 		uchar_t maddr_arr[ETHERADDRL];
    348 
    349 		if (md_get_prop_val(cba->mdp, cba->node, "local-mac-address",
    350 		    &macaddr)) {
    351 			DR_DBG_IO("%s: failed to read "
    352 			    "'local-mac-address' prop from MD\n", __func__);
    353 			return (DDI_WALK_ERROR);
    354 		}
    355 
    356 		for (i = 0, j = (ETHERADDRL - 1); i < ETHERADDRL; i++, j--)
    357 			maddr_arr[j] = (macaddr >> (i * 8)) & 0xff;
    358 
    359 		if (ndi_prop_update_byte_array(DDI_DEV_T_NONE, new_node,
    360 		    "local-mac-address", maddr_arr, ETHERADDRL)
    361 		    != DDI_SUCCESS) {
    362 			DR_DBG_IO("%s: failed to create "
    363 			    "'local-mac-address' prop\n", __func__);
    364 			return (DDI_WALK_ERROR);
    365 		}
    366 
    367 		if (md_get_prop_val(cba->mdp, cba->node, "mtu", &mtu)) {
    368 			DR_DBG_IO("%s: failed to read "
    369 			    "'mtu' prop from MD\n", __func__);
    370 			return (DDI_WALK_ERROR);
    371 		}
    372 
    373 		if (ndi_prop_update_int64(DDI_DEV_T_NONE, new_node, "mtu",
    374 		    mtu) != DDI_SUCCESS) {
    375 			DR_DBG_IO("%s: failed to "
    376 			    "create 'mtu' prop\n", __func__);
    377 			return (DDI_WALK_ERROR);
    378 		}
    379 
    380 		DR_DBG_IO("%s: Added properties for %s@%ld, "
    381 		    "mac=%ld, mtu=%ld\n", __func__, devnm, devid, macaddr, mtu);
    382 	}
    383 
    384 	cba->dip = new_node;
    385 
    386 	return (DDI_WALK_TERMINATE);
    387 }
    388 
    389 /*
    390  * Find the parent node of the argument virtual device node in
    391  * the MD.  For virtual devices, the parent is always
    392  * "channel-devices", so scan the MD using the "back" arcs
    393  * looking for a node with that name.
    394  */
    395 static mde_cookie_t
    396 dr_vio_find_parent_md(md_t *mdp, mde_cookie_t node)
    397 {
    398 	int		max_nodes;
    399 	int		num_nodes;
    400 	int		listsz;
    401 	mde_cookie_t    *listp;
    402 	mde_cookie_t	pnode = MDE_INVAL_ELEM_COOKIE;
    403 
    404 	max_nodes = md_node_count(mdp);
    405 	listsz = max_nodes * sizeof (mde_cookie_t);
    406 	listp = kmem_zalloc(listsz, KM_SLEEP);
    407 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
    408 	    __func__, (void *)listp, listsz);
    409 
    410 	num_nodes = md_scan_dag(mdp, node,
    411 	    md_find_name(mdp, "channel-devices"),
    412 	    md_find_name(mdp, "back"), listp);
    413 
    414 	ASSERT(num_nodes == 1);
    415 
    416 	if (num_nodes == 1)
    417 		pnode = listp[0];
    418 
    419 	DR_DBG_KMEM("%s: free addr %p size %d\n",
    420 	    __func__, (void *)listp, listsz);
    421 	kmem_free(listp, listsz);
    422 
    423 	return (pnode);
    424 }
    425 
    426 static int
    427 dr_io_configure(dr_vio_req_t *req, dr_vio_res_t *res)
    428 {
    429 	int		rv = ENXIO;
    430 	int		listsz;
    431 	int		nnodes;
    432 	uint64_t	devid = req->dev_id;
    433 	uint64_t	pdevid;
    434 	char		*name = req->name;
    435 	char		*pname;
    436 	md_t		*mdp = NULL;
    437 	mde_cookie_t	*listp = NULL;
    438 	mde_cookie_t	node;
    439 	mde_cookie_t	pnode;
    440 	dev_info_t	*pdip = NULL;
    441 	dev_info_t	*dip;
    442 	devi_branch_t	br;
    443 	cb_arg_t	cba;
    444 	int		drctl_cmd;
    445 	int		drctl_flags = 0;
    446 	drctl_rsrc_t	*drctl_req;
    447 	size_t		drctl_req_len;
    448 	drctl_rsrc_t	*drctl_rsrc = NULL;
    449 	drctl_cookie_t	drctl_res_ck;
    450 	char		*p;
    451 	drctl_resp_t	*drctl_resp;
    452 	size_t		drctl_resp_len = 0;
    453 
    454 	res->result = DR_VIO_RES_FAILURE;
    455 
    456 	if ((dip = dr_io_find_node(name, devid)) != NULL) {
    457 		DR_DBG_IO("%s: %s@%ld already configured\n",
    458 		    __func__, name, devid);
    459 
    460 		/* Return success if resources is already there. */
    461 		res->result = DR_VIO_RES_OK;
    462 		res->status = DR_VIO_STAT_CONFIGURED;
    463 		e_ddi_branch_rele(dip);
    464 		return (0);
    465 	}
    466 
    467 	/* Assume we fail to find the node to be added. */
    468 	res->status = DR_VIO_STAT_NOT_PRESENT;
    469 
    470 	if ((mdp = md_get_handle()) == NULL) {
    471 		DR_DBG_IO("%s: unable to initialize MD\n", __func__);
    472 		return (ENXIO);
    473 	}
    474 
    475 	nnodes = md_node_count(mdp);
    476 	ASSERT(nnodes > 0);
    477 
    478 	listsz = nnodes * sizeof (mde_cookie_t);
    479 	listp = kmem_zalloc(listsz, KM_SLEEP);
    480 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
    481 	    __func__, (void *)listp, listsz);
    482 
    483 	/*
    484 	 * Get the MD device node.
    485 	 */
    486 	node = dr_io_find_node_md(mdp, name, devid, listp);
    487 
    488 	if (node == MDE_INVAL_ELEM_COOKIE) {
    489 		DR_DBG_IO("%s: scan for %s name node failed\n", __func__, name);
    490 		res->result = DR_VIO_RES_NOT_IN_MD;
    491 		goto done;
    492 	}
    493 
    494 	/*
    495 	 * Get the MD parent node.
    496 	 */
    497 	pnode = dr_vio_find_parent_md(mdp, node);
    498 	if (pnode == MDE_INVAL_ELEM_COOKIE) {
    499 		DR_DBG_IO("%s: failed to find MD parent of %lx\n",
    500 		    __func__, pnode);
    501 		goto done;
    502 	}
    503 
    504 	if (md_get_prop_str(mdp, pnode, "name", &pname)) {
    505 		DR_DBG_IO("%s: failed to read "
    506 		    "'name' for pnode %lx from MD\n", __func__, pnode);
    507 		goto done;
    508 	}
    509 
    510 	if (md_get_prop_val(mdp, pnode, "cfg-handle", &pdevid)) {
    511 		DR_DBG_IO("%s: failed to read 'cfg-handle' "
    512 		    "for pnode '%s' from MD\n", __func__, pname);
    513 		goto done;
    514 	}
    515 
    516 	DR_DBG_IO("%s: parent device %s@%lx\n", __func__, pname, pdevid);
    517 
    518 	/*
    519 	 * Get the devinfo parent node.
    520 	 */
    521 	if ((pdip = dr_io_find_node(pname, pdevid)) == NULL) {
    522 		DR_DBG_IO("%s: parent device %s@%ld not found\n",
    523 		    __func__, pname, pdevid);
    524 		goto done;
    525 	}
    526 
    527 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
    528 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
    529 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
    530 	    __func__, (void *)drctl_req, drctl_req_len);
    531 	drctl_req->status = DRCTL_STATUS_INIT;
    532 
    533 	drctl_cmd = DRCTL_IO_CONFIG_REQUEST;
    534 
    535 	/*
    536 	 * Construct the path of the device as it will be if it
    537 	 * is successfully added.
    538 	 */
    539 	p = drctl_req->res_dev_path;
    540 	(void) sprintf(p, "/devices");
    541 	(void) ddi_pathname(pdip, p + strlen(p));
    542 	(void) sprintf(p + strlen(p), "/%s@%ld", name, devid);
    543 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
    544 
    545 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
    546 	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
    547 
    548 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
    549 
    550 	drctl_rsrc = drctl_resp->resp_resources;
    551 
    552 	if (rv != 0) {
    553 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
    554 
    555 		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
    556 
    557 		(void) strlcpy(res->reason,
    558 		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
    559 
    560 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
    561 
    562 		goto done;
    563 
    564 	}
    565 
    566 	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
    567 
    568 	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
    569 
    570 		res->result = DR_VIO_RES_BLOCKED;
    571 
    572 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
    573 		p = (char *)drctl_rsrc + drctl_rsrc->offset;
    574 
    575 		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
    576 
    577 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
    578 
    579 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
    580 
    581 		rv = EPERM;
    582 	} else {
    583 		cba.mdp = mdp;
    584 		cba.node = node;
    585 
    586 		br.arg = (void *)&cba;
    587 		br.type = DEVI_BRANCH_SID;
    588 		br.create.sid_branch_create = new_dev_node;
    589 		br.devi_branch_callback = NULL;
    590 
    591 		rv = e_ddi_branch_create(pdip,
    592 		    &br, NULL, DEVI_BRANCH_CONFIGURE);
    593 
    594 		drctl_req->status = (rv == 0) ?
    595 		    DRCTL_STATUS_CONFIG_SUCCESS : DRCTL_STATUS_CONFIG_FAILURE;
    596 
    597 		DR_DBG_IO("%s: %s@%ld = %d\n", __func__, name, devid, rv);
    598 	}
    599 
    600 	if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
    601 		DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
    602 
    603 done:
    604 	if (listp) {
    605 		DR_DBG_KMEM("%s: free addr %p size %d\n",
    606 		    __func__, (void *)listp, listsz);
    607 		kmem_free(listp, listsz);
    608 	}
    609 
    610 	if (mdp)
    611 		(void) md_fini_handle(mdp);
    612 
    613 	if (pdip)
    614 		e_ddi_branch_rele(pdip);
    615 
    616 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
    617 	    __func__, (void *)drctl_req, drctl_req_len);
    618 	kmem_free(drctl_req, drctl_req_len);
    619 
    620 	if (drctl_resp) {
    621 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
    622 		    __func__, (void *)drctl_resp, drctl_resp_len);
    623 		kmem_free(drctl_resp, drctl_resp_len);
    624 	}
    625 
    626 	if (rv == 0) {
    627 		res->result = DR_VIO_RES_OK;
    628 		res->status = DR_VIO_STAT_CONFIGURED;
    629 
    630 		/* notify interested parties about the operation */
    631 		dr_generate_event(DR_TYPE_VIO, SE_HINT_INSERT);
    632 	} else {
    633 		res->status = DR_VIO_STAT_UNCONFIGURED;
    634 	}
    635 
    636 	return (rv);
    637 }
    638 
    639 static int
    640 dr_io_unconfigure(dr_vio_req_t *req, dr_vio_res_t *res)
    641 {
    642 	int		rv;
    643 	char		*name = req->name;
    644 	char		*p;
    645 	uint64_t	devid = req->dev_id;
    646 	dev_info_t	*dip;
    647 	dev_info_t	*fdip = NULL;
    648 	int		drctl_cmd;
    649 	int		drctl_flags = 0;
    650 	drctl_rsrc_t	*drctl_req;
    651 	size_t		drctl_req_len;
    652 	drctl_rsrc_t	*drctl_rsrc = NULL;
    653 	drctl_cookie_t	drctl_res_ck;
    654 	drctl_resp_t	*drctl_resp;
    655 	size_t		drctl_resp_len;
    656 
    657 	if ((dip = dr_io_find_node(name, devid)) == NULL) {
    658 		DR_DBG_IO("%s: %s@%ld already unconfigured\n",
    659 		    __func__, name, devid);
    660 		res->result = DR_VIO_RES_OK;
    661 		res->status = DR_VIO_STAT_NOT_PRESENT;
    662 		return (0);
    663 	}
    664 
    665 	res->result = DR_VIO_RES_FAILURE;
    666 
    667 	ASSERT(e_ddi_branch_held(dip));
    668 
    669 	/* Assume we fail to unconfigure the resource. */
    670 	res->status = DR_VIO_STAT_CONFIGURED;
    671 
    672 	drctl_req_len = sizeof (drctl_rsrc_t) + MAXPATHLEN;
    673 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
    674 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
    675 	    __func__, (void *)drctl_req, drctl_req_len);
    676 	drctl_req->status = DRCTL_STATUS_INIT;
    677 
    678 	drctl_cmd = DRCTL_IO_UNCONFIG_REQUEST;
    679 
    680 	if (req->msg_type == DR_VIO_FORCE_UNCONFIG)
    681 		drctl_flags = DRCTL_FLAG_FORCE;
    682 
    683 	p = drctl_req->res_dev_path;
    684 	(void) sprintf(p, "/devices");
    685 	(void) ddi_pathname(dip, p + strlen(p));
    686 	DR_DBG_IO("%s: devpath=%s\n", __func__, drctl_req->res_dev_path);
    687 
    688 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
    689 	    1, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
    690 
    691 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
    692 
    693 	drctl_rsrc = drctl_resp->resp_resources;
    694 
    695 	if (rv != 0) {
    696 
    697 		DR_DBG_IO("%s: drctl_config_init failed: %d\n", __func__, rv);
    698 
    699 		ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
    700 
    701 		(void) strlcpy(res->reason,
    702 		    drctl_resp->resp_err_msg, DR_VIO_MAXREASONLEN);
    703 
    704 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
    705 
    706 		goto done;
    707 	}
    708 
    709 	if (drctl_rsrc->status == DRCTL_STATUS_DENY) {
    710 		res->result = DR_VIO_RES_BLOCKED;
    711 
    712 		DR_DBG_IO("%s: drctl_config_init denied\n", __func__);
    713 		p = (char *)drctl_rsrc + drctl_rsrc->offset;
    714 
    715 		(void) strlcpy(res->reason, p, DR_VIO_MAXREASONLEN);
    716 
    717 		DR_DBG_IO("%s: %s\n", __func__, res->reason);
    718 
    719 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
    720 
    721 		rv = EPERM;
    722 	} else if (rv = e_ddi_branch_destroy(dip, &fdip, 0)) {
    723 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
    724 
    725 		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
    726 		    __func__, (void *)path, MAXPATHLEN);
    727 		/*
    728 		 * If non-NULL, fdip is held and must be released.
    729 		 */
    730 		if (fdip != NULL) {
    731 			(void) ddi_pathname(fdip, path);
    732 			ddi_release_devi(fdip);
    733 		} else {
    734 			(void) ddi_pathname(dip, path);
    735 		}
    736 
    737 		DR_DBG_IO("%s: node removal failed: %s (%p)",
    738 		    __func__, path, (fdip) ? (void *)fdip : (void *)dip);
    739 
    740 		drctl_req->status = DRCTL_STATUS_CONFIG_FAILURE;
    741 
    742 		DR_DBG_KMEM("%s: free addr %p size %d\n",
    743 		    __func__, (void *)path, MAXPATHLEN);
    744 		kmem_free(path, MAXPATHLEN);
    745 	} else {
    746 		drctl_req->status = DRCTL_STATUS_CONFIG_SUCCESS;
    747 	}
    748 
    749 	if (drctl_config_fini(&drctl_res_ck, drctl_req, 1) != 0)
    750 		DR_DBG_IO("%s: drctl_config_fini returned: %d\n", __func__, rv);
    751 
    752 	DR_DBG_IO("%s: (%s@%ld) = %d\n", __func__, name, devid, rv);
    753 
    754 	if (rv == 0) {
    755 		res->result = DR_VIO_RES_OK;
    756 		res->status = DR_VIO_STAT_UNCONFIGURED;
    757 
    758 		/* Notify interested parties about the operation. */
    759 		dr_generate_event(DR_TYPE_VIO, SE_HINT_REMOVE);
    760 	}
    761 done:
    762 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
    763 	    __func__, (void *)drctl_req, drctl_req_len);
    764 	kmem_free(drctl_req, drctl_req_len);
    765 
    766 	if (drctl_resp) {
    767 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
    768 		    __func__, (void *)drctl_resp, drctl_resp_len);
    769 		kmem_free(drctl_resp, drctl_resp_len);
    770 	}
    771 
    772 	return (rv);
    773 }
    774 
    775 static void
    776 dr_vio_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
    777 {
    778 	_NOTE(ARGUNUSED(arg))
    779 
    780 	size_t		res_len;
    781 	dr_vio_res_t	*res;
    782 	dr_vio_req_t	*req;
    783 
    784 	/*
    785 	 * Allocate a response buffer, because we always want to
    786 	 * send back a response message.
    787 	 */
    788 	res_len = sizeof (dr_vio_res_t) + DR_VIO_MAXREASONLEN;
    789 	res = kmem_zalloc(res_len, KM_SLEEP);
    790 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
    791 	    __func__, (void *)res, res_len);
    792 	res->result = DR_VIO_RES_FAILURE;
    793 
    794 	/*
    795 	 * Sanity check the message
    796 	 */
    797 	if (buf == NULL) {
    798 		DR_DBG_IO("empty message: expected at least %ld bytes\n",
    799 		    sizeof (dr_vio_req_t));
    800 		goto done;
    801 	}
    802 	if (buflen < sizeof (dr_vio_req_t)) {
    803 		DR_DBG_IO("incoming message short: expected at least %ld "
    804 		    "bytes, received %ld\n", sizeof (dr_vio_req_t), buflen);
    805 		goto done;
    806 	}
    807 
    808 	DR_DBG_TRANS("incoming request:\n");
    809 	DR_DBG_DUMP_MSG(buf, buflen);
    810 
    811 	req = buf;
    812 	switch (req->msg_type) {
    813 	case DR_VIO_CONFIGURE:
    814 		(void) dr_io_configure(req, res);
    815 		break;
    816 	case DR_VIO_FORCE_UNCONFIG:
    817 	case DR_VIO_UNCONFIGURE:
    818 		(void) dr_io_unconfigure(req, res);
    819 		break;
    820 	default:
    821 		cmn_err(CE_NOTE, "bad msg_type %d\n", req->msg_type);
    822 		break;
    823 	}
    824 done:
    825 	res->req_num = (req) ? req->req_num : 0;
    826 
    827 	DR_DBG_TRANS("outgoing response:\n");
    828 	DR_DBG_DUMP_MSG(res, res_len);
    829 
    830 	/* send back the response */
    831 	if (ds_cap_send(ds_vio_handle, res, res_len) != 0)
    832 		DR_DBG_IO("ds_send failed\n");
    833 
    834 	if (res) {
    835 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
    836 		    __func__, (void *)res, res_len);
    837 		kmem_free(res, res_len);
    838 	}
    839 }
    840 
    841 static void
    842 dr_vio_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
    843 {
    844 	DR_DBG_IO("vio_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
    845 	    arg, ver->major, ver->minor, hdl);
    846 
    847 	ds_vio_handle = hdl;
    848 }
    849 
    850 static void
    851 dr_vio_unreg_handler(ds_cb_arg_t arg)
    852 {
    853 	DR_DBG_IO("vio_unreg_handler: arg=0x%p\n", arg);
    854 
    855 	ds_vio_handle = DS_INVALID_HDL;
    856 }
    857 
    858 static int
    859 dr_io_init(void)
    860 {
    861 	int	rv;
    862 
    863 	if ((rv = ds_cap_init(&dr_vio_cap, &dr_vio_ops)) != 0) {
    864 		cmn_err(CE_NOTE, "ds_cap_init vio failed: %d", rv);
    865 		return (-1);
    866 	}
    867 
    868 	return (0);
    869 }
    870 
    871 static int
    872 dr_io_fini(void)
    873 {
    874 	int	rv;
    875 
    876 	if ((rv = ds_cap_fini(&dr_vio_cap)) != 0) {
    877 		cmn_err(CE_NOTE, "ds_cap_fini vio failed: %d", rv);
    878 		return (-1);
    879 	}
    880 
    881 	return (0);
    882 }
    883 
    884 int
    885 _init(void)
    886 {
    887 	int	status;
    888 
    889 	/* check that IO DR is enabled */
    890 	if (dr_is_disabled(DR_TYPE_VIO)) {
    891 		cmn_err(CE_CONT, "!VIO DR is disabled\n");
    892 		return (-1);
    893 	}
    894 
    895 	if ((status = dr_io_init()) != 0) {
    896 		cmn_err(CE_NOTE, "VIO DR initialization failed");
    897 		return (status);
    898 	}
    899 
    900 	if ((status = mod_install(&modlinkage)) != 0) {
    901 		(void) dr_io_fini();
    902 	}
    903 
    904 	return (status);
    905 }
    906 
    907 int
    908 _info(struct modinfo *modinfop)
    909 {
    910 	return (mod_info(&modlinkage, modinfop));
    911 }
    912 
    913 int dr_io_allow_unload = 0;
    914 
    915 int
    916 _fini(void)
    917 {
    918 	int	status;
    919 
    920 	if (dr_io_allow_unload == 0)
    921 		return (EBUSY);
    922 
    923 	if ((status = mod_remove(&modlinkage)) == 0) {
    924 		(void) dr_io_fini();
    925 	}
    926 
    927 	return (status);
    928 }
    929