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