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