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