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 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * Copyright 2023 Oxide Computer Company 27 */ 28 29 #include <sys/conf.h> 30 #include <sys/stat.h> 31 #include <sys/modctl.h> 32 #include <sys/taskq.h> 33 #include <sys/mdi_impldefs.h> 34 #include <sys/sunmdi.h> 35 #include <sys/sunpm.h> 36 #include <sys/ib/mgt/ibdm/ibdm_impl.h> 37 #include <sys/ib/ibnex/ibnex.h> 38 #include <sys/ib/ibnex/ibnex_devctl.h> 39 #include <sys/ib/ibtl/ibti.h> 40 #include <sys/ib/ibtl/impl/ibtl_ibnex.h> 41 #include <sys/file.h> 42 #include <sys/hwconf.h> 43 #include <sys/fs/dv_node.h> 44 45 void ibnex_handle_hca_attach(void *); 46 static int ibnex_hca_bus_config_one(dev_info_t *, void *, 47 ddi_bus_config_op_t, uint_t *, dev_info_t **); 48 49 static ibnex_node_data_t *ibnex_get_cdip_info(dev_info_t *, char *, 50 dev_info_t **, ibnex_node_type_t *); 51 static int ibnex_prom_devname_to_pkey_n_portnum( 52 char *, ib_pkey_t *, uint8_t *); 53 static dev_info_t *ibnex_config_obp_args(dev_info_t *, char *); 54 55 extern int ibnex_busctl(dev_info_t *, 56 dev_info_t *, ddi_ctl_enum_t, void *, void *); 57 extern int ibnex_map_fault(dev_info_t *, 58 dev_info_t *, struct hat *, struct seg *, 59 caddr_t, struct devpage *, pfn_t, uint_t, uint_t); 60 static int ibnex_hca_bus_config(dev_info_t *, uint_t, 61 ddi_bus_config_op_t, void *, dev_info_t **); 62 static int ibnex_hca_bus_unconfig(dev_info_t *, 63 uint_t, ddi_bus_config_op_t, void *); 64 extern dev_info_t *ibnex_config_port_node(dev_info_t *, char *); 65 extern dev_info_t *ibnex_config_obp_args(dev_info_t *, char *); 66 extern int ibnex_ioc_bus_config_one(dev_info_t **, uint_t, 67 ddi_bus_config_op_t, void *, dev_info_t **, int *); 68 extern int ibnex_pseudo_config_one( 69 ibnex_node_data_t *, char *, dev_info_t *); 70 extern void ibnex_config_all_children(dev_info_t *); 71 extern void ibnex_pseudo_initnodes(void); 72 73 extern int ibnex_pseudo_mdi_config_one(int, void *, dev_info_t **, 74 char *, char *); 75 extern int ibnex_get_dip_from_guid(ib_guid_t, int, 76 ib_pkey_t, dev_info_t **); 77 extern dev_info_t *ibnex_commsvc_initnode(dev_info_t *, 78 ibdm_port_attr_t *, int, int, ib_pkey_t, int *, 79 int); 80 extern uint64_t ibnex_str2hex(char *, int, int *); 81 extern int ibnex_str2int(char *, int, int *); 82 extern void ibnex_create_hcasvc_nodes( 83 dev_info_t *, ibdm_port_attr_t *); 84 extern void ibnex_create_port_nodes( 85 dev_info_t *, ibdm_port_attr_t *); 86 extern void ibnex_create_vppa_nodes( 87 dev_info_t *, ibdm_port_attr_t *); 88 extern int ibnex_get_pkey_commsvc_index_portnum( 89 char *, int *, ib_pkey_t *, uint8_t *); 90 91 extern ibnex_t ibnex; 92 extern int ibnex_port_settling_time; 93 94 /* 95 * The bus_ops structure defines the capabilities of HCA nexus driver. 96 */ 97 struct bus_ops ibnex_ci_busops = { 98 BUSO_REV, 99 nullbusmap, /* bus_map */ 100 NULL, /* bus_get_intrspec */ 101 NULL, /* bus_add_intrspec */ 102 NULL, /* bus_remove_intrspec */ 103 ibnex_map_fault, /* Map Fault */ 104 ddi_no_dma_map, /* DMA related entry points */ 105 NULL, 106 NULL, 107 NULL, 108 NULL, 109 NULL, 110 NULL, 111 NULL, 112 ibnex_busctl, /* bus_ctl */ 113 ddi_bus_prop_op, /* bus_prop_op */ 114 NULL, /* bus_get_eventcookie */ 115 NULL, /* bus_add_eventcall */ 116 NULL, /* bus_remove_eventcall */ 117 NULL, /* bus_post_event */ 118 NULL, 119 ibnex_hca_bus_config, /* bus config */ 120 ibnex_hca_bus_unconfig /* bus unconfig */ 121 }; 122 123 /* 124 * ibnex_hca_bus_config() 125 * 126 * BUS_CONFIG_ONE: 127 * Enumerate the exact instance of the driver. Use the device node name 128 * to locate the exact instance. 129 * Query IBDM to find whether the hardware exits for the instance of the 130 * driver. If exists, create a device node and return NDI_SUCCESS. 131 * 132 * BUS_CONFIG_ALL: 133 * Enumerate all the instances of all the possible children (seen before 134 * and never seen before). 135 * 136 * BUS_CONFIG_DRIVER: 137 * Enumerate all the instances of a particular driver. 138 */ 139 static int 140 ibnex_hca_bus_config(dev_info_t *parent, uint_t flag, 141 ddi_bus_config_op_t op, void *devname, dev_info_t **child) 142 { 143 int ret = IBNEX_SUCCESS; 144 boolean_t enteredv; 145 char *srvname, nameaddr[MAXNAMELEN]; 146 dev_info_t *cdip; 147 ibnex_node_data_t *node_data; 148 ibnex_port_node_t *port_node; 149 150 /* 151 * In a normal case HCA is setup as a phci. 152 * If an HCA is in maintenance mode, its phci is not set up 153 * but the driver is attached to update the firmware. In this 154 * case, do not configure the MPxIO clients. 155 */ 156 if (mdi_component_is_phci(parent, NULL) == MDI_FAILURE) { 157 if (op == BUS_CONFIG_ALL || op == BUS_CONFIG_DRIVER) 158 return (NDI_SUCCESS); 159 else 160 return (NDI_FAILURE); 161 } 162 163 switch (op) { 164 case BUS_CONFIG_ONE: 165 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: CONFIG_ONE, " 166 "parent %p", parent); 167 ret = ibnex_hca_bus_config_one( 168 parent, devname, op, &flag, child); 169 break; 170 171 case BUS_CONFIG_OBP_ARGS: 172 mdi_devi_enter(parent, &enteredv); 173 cdip = ibnex_config_obp_args(parent, devname); 174 if (cdip) { 175 /* 176 * Boot case. 177 * Special handling because the "devname" 178 * format for the enumerated device is 179 * different. 180 */ 181 node_data = ddi_get_parent_data(cdip); 182 port_node = &node_data->node_data.port_node; 183 if (node_data->node_type == 184 IBNEX_VPPA_COMMSVC_NODE) { 185 srvname = 186 ibnex.ibnex_vppa_comm_svc_names[ 187 port_node->port_commsvc_idx]; 188 (void) snprintf(nameaddr, MAXNAMELEN, 189 "ibport@%x,%x,%s", 190 port_node->port_num, 191 port_node->port_pkey, srvname); 192 } 193 devname = (void *)nameaddr; 194 } else { 195 IBTF_DPRINTF_L2("ibnex", "\thca_bus_config: " 196 "CONFIG_OBP_ARGS : invalid state!!"); 197 198 ret = IBNEX_FAILURE; 199 } 200 mdi_devi_exit(parent, enteredv); 201 break; 202 203 case BUS_CONFIG_ALL: 204 IBTF_DPRINTF_L4("ibnex", 205 "\thca_bus_config: CONFIG_ALL parent %p", parent); 206 ibnex_config_all_children(parent); 207 break; 208 209 case BUS_CONFIG_DRIVER: 210 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: " 211 "CONFIG_DRIVER parent %p", parent); 212 ibnex_config_all_children(parent); 213 break; 214 215 default: 216 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config: error"); 217 ret = IBNEX_FAILURE; 218 break; 219 } 220 221 222 if (ret == IBNEX_SUCCESS) { 223 if (op == BUS_CONFIG_OBP_ARGS) 224 op = BUS_CONFIG_ONE; 225 226 ret = ndi_busop_bus_config( 227 parent, flag, op, devname, child, 0); 228 IBTF_DPRINTF_L4("ibnex", "\thca_bus_config:" 229 "ndi_busop_bus_config : retval %d", ret); 230 return (ret); 231 } 232 233 return (NDI_FAILURE); 234 } 235 236 /* 237 * ibnex_hca_bus_unconfig() 238 * 239 * Unconfigure a particular device node or all instance of a device 240 * driver device or all children of IBnex 241 */ 242 static int 243 ibnex_hca_bus_unconfig(dev_info_t *parent, 244 uint_t flag, ddi_bus_config_op_t op, void *device_name) 245 { 246 247 if (ndi_busop_bus_unconfig(parent, flag, op, device_name) != 248 DDI_SUCCESS) 249 return (DDI_FAILURE); 250 251 if ((op == BUS_UNCONFIG_ALL || op == BUS_UNCONFIG_DRIVER) && 252 (flag & NDI_UNCONFIG)) { 253 ibnex_node_data_t *ndp; 254 dev_info_t *dip = NULL; 255 major_t major = (major_t)(uintptr_t)device_name; 256 257 mutex_enter(&ibnex.ibnex_mutex); 258 259 if (major == -1) { 260 /* 261 * HCA dip. When major number is -1 HCA is 262 * going away cleanup all the port nodes. 263 */ 264 for (ndp = ibnex.ibnex_port_node_head; 265 ndp; ndp = ndp->node_next) { 266 ibnex_port_node_t *port_node; 267 268 port_node = &ndp->node_data.port_node; 269 if (port_node->port_pdip == parent) { 270 port_node->port_pdip = NULL; 271 ndp->node_dip = NULL; 272 ndp->node_state = 273 IBNEX_CFGADM_UNCONFIGURED; 274 } 275 } 276 } else { 277 /* 278 * HCA dip. Cleanup only the port nodes that 279 * match the major number. 280 */ 281 for (ndp = ibnex.ibnex_port_node_head; 282 ndp; ndp = ndp->node_next) { 283 ibnex_port_node_t *port_node; 284 285 port_node = &ndp->node_data.port_node; 286 dip = ndp->node_dip; 287 if (dip && (ddi_driver_major(dip) == 288 major) && port_node->port_pdip == 289 parent) { 290 port_node->port_pdip = NULL; 291 ndp->node_dip = NULL; 292 ndp->node_state = 293 IBNEX_CFGADM_UNCONFIGURED; 294 } 295 } 296 } 297 mutex_exit(&ibnex.ibnex_mutex); 298 } 299 return (DDI_SUCCESS); 300 } 301 302 /* 303 * ibnex_config_obp_args() 304 * Configures a particular port node for a IP over IB communication 305 * service. 306 * The format of the input string "devname" is 307 * port=x,pkey=y,protocol=ip 308 * Thr format of the node name created here is 309 * ibport@<Port#>,<pkey>,<service name> 310 * where pkey = 0 for port communication service nodes 311 * Returns "dev_info_t" of the "child" node just created 312 * NULL when failed to enumerate the child node 313 * 314 */ 315 static dev_info_t * 316 ibnex_config_obp_args(dev_info_t *parent, char *devname) 317 { 318 int ii, index; 319 int rval, iter = 0; 320 char *temp; 321 uint8_t port_num; 322 ib_guid_t hca_guid, port_guid; 323 ib_pkey_t pkey; 324 dev_info_t *cdip; 325 boolean_t displayed = B_FALSE; 326 ibdm_port_attr_t *port_attr; 327 328 IBTF_DPRINTF_L4("ibnex", "\tconfig_obp_args: %s", devname); 329 330 /* Is this OBP node for IPoIB ? */ 331 temp = devname; 332 do { 333 temp = strstr(temp, ",protocol=ip"); 334 if (temp == NULL) 335 break; 336 337 if (strlen(devname) > (int)((temp - devname) + 12)) { 338 if (temp[12] == ',') 339 break; 340 } else { 341 break; 342 } 343 temp++; 344 } while (temp); 345 346 if (temp == NULL) 347 return (NULL); 348 if (ibnex_prom_devname_to_pkey_n_portnum( 349 devname, &pkey, &port_num) != IBNEX_SUCCESS) { 350 return (NULL); 351 } 352 for (index = 0; index < ibnex.ibnex_nvppa_comm_svcs; index++) { 353 if (strcmp(ibnex.ibnex_vppa_comm_svc_names[index], 354 "ipib") == 0) { 355 break; 356 } 357 } 358 359 hca_guid = ibtl_ibnex_hcadip2guid(parent); 360 if ((port_attr = ibdm_ibnex_probe_hcaport( 361 hca_guid, port_num)) == NULL) { 362 IBTF_DPRINTF_L2("ibnex", 363 "\tconfig_port_node: Port does not exist"); 364 return (NULL); 365 } 366 367 /* Wait until "port is up" */ 368 while (port_attr->pa_state != IBT_PORT_ACTIVE) { 369 ibdm_ibnex_free_port_attr(port_attr); 370 delay(drv_usectohz(10000)); 371 if ((port_attr = ibdm_ibnex_probe_hcaport( 372 hca_guid, port_num)) == NULL) { 373 return (NULL); 374 } 375 if (iter++ == 400) { 376 if (displayed == B_FALSE) { 377 cmn_err(CE_NOTE, "\tWaiting for Port %d " 378 "initialization", port_attr->pa_port_num); 379 displayed = B_TRUE; 380 } 381 } 382 } 383 IBTF_DPRINTF_L4("ibnex", "\tPort is initialized"); 384 385 mutex_enter(&ibnex.ibnex_mutex); 386 port_guid = port_attr->pa_port_guid; 387 rval = ibnex_get_dip_from_guid(port_guid, index, pkey, &cdip); 388 if (rval == IBNEX_SUCCESS && cdip != NULL) { 389 IBTF_DPRINTF_L4("ibnex", "\tconfig_port_node: Node exists"); 390 mutex_exit(&ibnex.ibnex_mutex); 391 ibdm_ibnex_free_port_attr(port_attr); 392 return (cdip); 393 } 394 for (ii = 0; ii < port_attr->pa_npkeys; ii++) { 395 if (pkey == port_attr->pa_pkey_tbl[ii].pt_pkey) { 396 cdip = ibnex_commsvc_initnode(parent, port_attr, 397 index, IBNEX_VPPA_COMMSVC_NODE, pkey, &rval, 398 IBNEX_CFGADM_ENUMERATE); 399 IBTF_DPRINTF_L5("ibnex", 400 "\t ibnex_commsvc_initnode rval %x", rval); 401 break; 402 } 403 } 404 mutex_exit(&ibnex.ibnex_mutex); 405 406 ibdm_ibnex_free_port_attr(port_attr); 407 return (cdip); 408 } 409 410 411 /* 412 * ibnex_prom_devname_to_pkey_n_portnum() 413 * Parses the device node name and extracts "PKEY" and "port#" 414 * Returns IBNEX_SUCCESS/IBNEX_FAILURE 415 */ 416 static int 417 ibnex_prom_devname_to_pkey_n_portnum( 418 char *devname, ib_pkey_t *pkey, uint8_t *port) 419 { 420 int ret = IBNEX_SUCCESS; 421 char *tmp, *tmp1; 422 423 if ((tmp = strstr(devname, "port=")) != NULL) { 424 if ((tmp = strchr(++tmp, '=')) != NULL) 425 if ((tmp1 = strchr(++tmp, ',')) != NULL) 426 *port = ibnex_str2int(tmp, (tmp1 - tmp), &ret); 427 } else 428 ret = IBNEX_FAILURE; 429 430 if ((ret == IBNEX_SUCCESS) && 431 (tmp = strstr(devname, "pkey=")) != NULL) { 432 if ((tmp = strchr(++tmp, '=')) != NULL) 433 if ((tmp1 = strchr(++tmp, ',')) != NULL) 434 *pkey = ibnex_str2hex(tmp, (tmp1 - tmp), &ret); 435 } else 436 ret = IBNEX_FAILURE; 437 438 return (ret); 439 } 440 441 static ibnex_node_data_t * 442 ibnex_get_cdip_info(dev_info_t *parent, 443 char *devname, dev_info_t **cdip, ibnex_node_type_t *type) 444 { 445 char *device_name, *cname = NULL, *caddr = NULL; 446 int len; 447 ibnex_node_data_t *node_data = NULL; 448 449 len = strlen((char *)devname) + 1; 450 device_name = i_ddi_strdup(devname, KM_SLEEP); 451 i_ddi_parse_name(device_name, &cname, &caddr, NULL); 452 453 IBTF_DPRINTF_L4("ibnex", 454 "\tfind_child_dip: cname %s addr %s", cname, caddr); 455 456 if (strncmp(cname, IBNEX_IOC_CNAME, 3) == 0) 457 *type = IBNEX_IOC_NODE; 458 else if (strncmp(cname, IBNEX_IBPORT_CNAME, 3) == 0) 459 *type = IBNEX_HCA_CHILD_NODE; 460 else 461 *type = IBNEX_PSEUDO_NODE; 462 463 *cdip = ndi_devi_findchild(parent, devname); 464 465 IBTF_DPRINTF_L4("ibnex", 466 "\tfind_child_dip: cdip %p type %x", *cdip, *type); 467 468 if (*cdip) 469 node_data = ddi_get_parent_data(*cdip); 470 kmem_free(device_name, len); 471 472 return (node_data); 473 } 474 475 static int 476 ibnex_hca_bus_config_one(dev_info_t *parent, void *devname, 477 ddi_bus_config_op_t op, uint_t *flag, dev_info_t **child) 478 { 479 int ret = IBNEX_SUCCESS, len, need_bus_config; 480 char *device_name, *caddr, *cname; 481 dev_info_t *cdip; 482 ibnex_node_data_t *node_data; 483 ibnex_node_type_t node_type; 484 int index; 485 uint8_t port_num; 486 ib_pkey_t pkey; 487 boolean_t enteredv; 488 489 len = strlen((char *)devname) + 1; 490 device_name = i_ddi_strdup(devname, KM_SLEEP); 491 i_ddi_parse_name(device_name, &cname, &caddr, NULL); 492 493 if (caddr == NULL || (strlen(caddr) == 0)) { 494 IBTF_DPRINTF_L2("ibnex", 495 "\thca_bus_config: Invalid device node address"); 496 kmem_free(device_name, len); 497 return (IBNEX_FAILURE); 498 } 499 500 ndi_devi_enter(parent); 501 node_data = ibnex_get_cdip_info( 502 parent, devname, &cdip, &node_type); 503 ndi_devi_exit(parent); 504 505 if (cdip) { 506 if ((node_data) && (node_data->node_type == 507 IBNEX_PORT_COMMSVC_NODE)) { 508 if (node_data->node_dip == NULL) { 509 node_data->node_dip = cdip; 510 node_data->node_data.port_node.port_pdip = 511 parent; 512 } 513 } 514 } 515 516 /* 517 * If child dip is present, just return 518 * from here. 519 */ 520 if (cdip != NULL || (node_data != NULL && 521 node_data->node_dip != NULL)) { 522 goto end; 523 } 524 525 switch (node_type) { 526 527 case IBNEX_IOC_NODE: 528 ret = ibnex_ioc_bus_config_one(&parent, *flag, 529 op, devname, child, &need_bus_config); 530 if (!need_bus_config) { 531 kmem_free(device_name, len); 532 return (ret); 533 } 534 break; 535 536 case IBNEX_PSEUDO_NODE: 537 ret = IBNEX_SUCCESS; 538 mdi_devi_enter(parent, &enteredv); 539 ibnex_pseudo_initnodes(); 540 mutex_enter(&ibnex.ibnex_mutex); 541 ret = ibnex_pseudo_config_one(NULL, 542 caddr, parent); 543 mutex_exit(&ibnex.ibnex_mutex); 544 mdi_devi_exit(parent, enteredv); 545 break; 546 547 default: 548 if (ibnex_get_pkey_commsvc_index_portnum(devname, 549 &index, &pkey, &port_num) != IBNEX_SUCCESS) { 550 IBTF_DPRINTF_L2("ibnex", 551 "\tconfig_port_node: Invalid Service Name"); 552 kmem_free(device_name, len); 553 return (IBNEX_FAILURE); 554 } 555 556 if ((pkey != 0) && (port_num != 0)) { 557 if (strcmp("ipib", 558 ibnex.ibnex_vppa_comm_svc_names[index]) == 0) { 559 IBTF_DPRINTF_L2("ibnex", 560 "Skipping IBD devices... "); 561 break; 562 } 563 } 564 565 ndi_devi_enter(parent); 566 cdip = ibnex_config_port_node(parent, devname); 567 if (cdip) 568 ret = IBNEX_SUCCESS; 569 else 570 ret = IBNEX_FAILURE; 571 ndi_devi_exit(parent); 572 break; 573 } 574 end: 575 if (node_type == IBNEX_HCA_CHILD_NODE) { 576 /* Allows enumeration under PHCI */ 577 *flag |= NDI_MDI_FALLBACK; 578 } 579 kmem_free(device_name, len); 580 return (ret); 581 } 582 583 void 584 ibnex_handle_hca_attach(void *cb_arg) 585 { 586 ib_guid_t hca_guid = *((ib_guid_t *)cb_arg); 587 dev_info_t *phci; 588 int ii; 589 ibdm_hca_list_t *hca_list; 590 591 IBTF_DPRINTF_L4("ibnex", "handle_hca_attach(%llx)", hca_guid); 592 593 phci = ibtl_ibnex_hcaguid2dip(hca_guid); 594 595 /* 596 * Enumerate children of this HCA, port nodes, 597 * VPPA & HCA_SVC nodes. Use ndi_devi_enter() for 598 * locking. IB Nexus is enumerating the children 599 * of HCA, not MPXIO clients. 600 */ 601 ndi_devi_enter(phci); 602 ibdm_ibnex_port_settle_wait(hca_guid, ibnex_port_settling_time); 603 hca_list = ibdm_ibnex_get_hca_info_by_guid(hca_guid); 604 if (hca_list == NULL) { 605 ndi_devi_exit(phci); 606 kmem_free(cb_arg, sizeof (ib_guid_t)); 607 return; 608 } 609 ibnex_create_hcasvc_nodes(phci, hca_list->hl_hca_port_attr); 610 for (ii = 0; ii < hca_list->hl_nports; ii++) { 611 ibnex_create_port_nodes(phci, &hca_list->hl_port_attr[ii]); 612 ibnex_create_vppa_nodes(phci, &hca_list->hl_port_attr[ii]); 613 } 614 ibdm_ibnex_free_hca_list(hca_list); 615 ndi_devi_exit(phci); 616 kmem_free(cb_arg, sizeof (ib_guid_t)); 617 } 618