xref: /titanic_41/usr/src/uts/sun4v/io/dr_io.c (revision 99c7e85592b5d673f1469b2767f876710004dc23)
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
dr_io_check_node(dev_info_t * dip,void * arg)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 *
dr_io_find_node(char * name,uint64_t devid)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
dr_io_find_node_md(md_t * mdp,char * name,uint64_t id,mde_cookie_t * listp)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
new_dev_node(dev_info_t * new_node,void * arg,uint_t flags)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
dr_vio_find_parent_md(md_t * mdp,mde_cookie_t node)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
dr_io_configure(dr_vio_req_t * req,dr_vio_res_t * res)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
dr_io_unconfigure(dr_vio_req_t * req,dr_vio_res_t * res)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
dr_vio_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)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
dr_vio_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)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
dr_vio_unreg_handler(ds_cb_arg_t arg)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
dr_io_init(void)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
dr_io_fini(void)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
_init(void)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
_info(struct modinfo * modinfop)908 _info(struct modinfo *modinfop)
909 {
910 	return (mod_info(&modlinkage, modinfop));
911 }
912 
913 int dr_io_allow_unload = 0;
914 
915 int
_fini(void)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