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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2019 Joyent, Inc. 26 */ 27 28 /* 29 * This file contains the common hotplug code that is used by Standard 30 * PCIe and PCI HotPlug Controller code. 31 * 32 * NOTE: This file is compiled and delivered through misc/pcie module. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/conf.h> 37 #include <sys/kmem.h> 38 #include <sys/debug.h> 39 #include <sys/vtrace.h> 40 #include <sys/autoconf.h> 41 #include <sys/varargs.h> 42 #include <sys/ddi_impldefs.h> 43 #include <sys/time.h> 44 #include <sys/note.h> 45 #include <sys/callb.h> 46 #include <sys/ddi.h> 47 #include <sys/sunddi.h> 48 #include <sys/sunndi.h> 49 #include <sys/sysevent.h> 50 #include <sys/sysevent/eventdefs.h> 51 #include <sys/sysevent/dr.h> 52 #include <sys/pci_impl.h> 53 #include <sys/pci_cap.h> 54 #include <sys/hotplug/pci/pcicfg.h> 55 #include <sys/hotplug/pci/pcie_hp.h> 56 #include <sys/hotplug/pci/pciehpc.h> 57 #include <sys/hotplug/pci/pcishpc.h> 58 #include <io/pciex/pcieb.h> 59 60 /* Local functions prototype */ 61 static int pcie_hp_list_occupants(dev_info_t *dip, void *arg); 62 static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, 63 char *cn_name); 64 static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num); 65 static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg); 66 static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg); 67 static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl); 68 static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num); 69 static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, 70 int *func_num); 71 static int pcie_hp_create_port_name_num(dev_info_t *dip, 72 ddi_hp_cn_info_t *cn_info); 73 static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, 74 int func_num); 75 76 /* 77 * Global functions (called by other drivers/modules) 78 */ 79 80 /* 81 * return description text for led state 82 */ 83 char * 84 pcie_led_state_text(pcie_hp_led_state_t state) 85 { 86 switch (state) { 87 case PCIE_HP_LED_ON: 88 return (PCIEHPC_PROP_VALUE_ON); 89 case PCIE_HP_LED_OFF: 90 return (PCIEHPC_PROP_VALUE_OFF); 91 case PCIE_HP_LED_BLINK: 92 default: 93 return (PCIEHPC_PROP_VALUE_BLINK); 94 } 95 } 96 97 /* 98 * return description text for slot condition 99 */ 100 char * 101 pcie_slot_condition_text(ap_condition_t condition) 102 { 103 switch (condition) { 104 case AP_COND_UNKNOWN: 105 return (PCIEHPC_PROP_VALUE_UNKNOWN); 106 case AP_COND_OK: 107 return (PCIEHPC_PROP_VALUE_OK); 108 case AP_COND_FAILING: 109 return (PCIEHPC_PROP_VALUE_FAILING); 110 case AP_COND_FAILED: 111 return (PCIEHPC_PROP_VALUE_FAILED); 112 case AP_COND_UNUSABLE: 113 return (PCIEHPC_PROP_VALUE_UNUSABLE); 114 default: 115 return (PCIEHPC_PROP_VALUE_UNKNOWN); 116 } 117 } 118 119 /* 120 * routine to copy in a nvlist from userland 121 */ 122 int 123 pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp) 124 { 125 int ret = DDI_SUCCESS; 126 char *packed; 127 nvlist_t *dest = NULL; 128 129 if (packed_buf == NULL || packed_sz == 0) 130 return (DDI_EINVAL); 131 132 /* copyin packed nvlist */ 133 if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL) 134 return (DDI_ENOMEM); 135 136 if (copyin(packed_buf, packed, packed_sz) != 0) { 137 cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n"); 138 ret = DDI_FAILURE; 139 goto copyin_cleanup; 140 } 141 142 /* unpack packed nvlist */ 143 if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) { 144 cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack " 145 "failed with err %d\n", ret); 146 switch (ret) { 147 case EINVAL: 148 case ENOTSUP: 149 ret = DDI_EINVAL; 150 goto copyin_cleanup; 151 case ENOMEM: 152 ret = DDI_ENOMEM; 153 goto copyin_cleanup; 154 default: 155 ret = DDI_FAILURE; 156 goto copyin_cleanup; 157 } 158 } 159 *nvlp = dest; 160 copyin_cleanup: 161 kmem_free(packed, packed_sz); 162 return (ret); 163 } 164 165 /* 166 * routine to copy out a nvlist to userland 167 */ 168 int 169 pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz) 170 { 171 int err = 0; 172 char *buf = NULL; 173 size_t packed_sz; 174 175 if (nvl == NULL || packed_buf == NULL || buf_sz == NULL) 176 return (DDI_EINVAL); 177 178 /* pack nvlist, the library will allocate memory */ 179 if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0)) 180 != 0) { 181 cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack " 182 "failed with err %d\n", err); 183 switch (err) { 184 case EINVAL: 185 case ENOTSUP: 186 return (DDI_EINVAL); 187 case ENOMEM: 188 return (DDI_ENOMEM); 189 default: 190 return (DDI_FAILURE); 191 } 192 } 193 if (packed_sz > *buf_sz) { 194 return (DDI_EINVAL); 195 } 196 197 /* copyout packed nvlist */ 198 if (copyout(buf, packed_buf, packed_sz) != 0) { 199 cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n"); 200 kmem_free(buf, packed_sz); 201 return (DDI_FAILURE); 202 } 203 204 *buf_sz = packed_sz; 205 kmem_free(buf, packed_sz); 206 return (DDI_SUCCESS); 207 } 208 209 /* 210 * init bus_hp_op entry and init hotpluggable slots & virtual ports 211 */ 212 int 213 pcie_hp_init(dev_info_t *dip, caddr_t arg) 214 { 215 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 216 int ret = DDI_SUCCESS, count; 217 dev_info_t *cdip; 218 219 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 220 /* Init hotplug controller */ 221 ret = pciehpc_init(dip, arg); 222 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 223 ret = pcishpc_init(dip); 224 } 225 226 if (ret != DDI_SUCCESS) { 227 PCIE_DBG("pcie_hp_init: initialize hotplug " 228 "controller failed with %d\n", ret); 229 return (ret); 230 } 231 232 ndi_devi_enter(dip, &count); 233 234 /* Create port for the first level children */ 235 cdip = ddi_get_child(dip); 236 while (cdip != NULL) { 237 if ((ret = pcie_hp_register_port(cdip, dip, NULL)) 238 != DDI_SUCCESS) { 239 /* stop and cleanup */ 240 break; 241 } 242 cdip = ddi_get_next_sibling(cdip); 243 } 244 ndi_devi_exit(dip, count); 245 if (ret != DDI_SUCCESS) { 246 cmn_err(CE_WARN, "pcie_hp_init: initialize virtual " 247 "hotplug port failed with %d\n", ret); 248 (void) pcie_hp_uninit(dip); 249 250 return (ret); 251 } 252 253 return (DDI_SUCCESS); 254 } 255 256 /* 257 * uninit the hotpluggable slots and virtual ports 258 */ 259 int 260 pcie_hp_uninit(dev_info_t *dip) 261 { 262 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 263 pcie_hp_unreg_port_t arg; 264 265 /* 266 * Must set arg.rv to NDI_SUCCESS so that if there's no port 267 * under this dip, we still return success thus the bridge 268 * driver can be successfully detached. 269 * 270 * Note that during the probe PCI configurator calls 271 * ndi_devi_offline() to detach driver for a new probed bridge, 272 * so that it can reprogram the resources for the bridge, 273 * ndi_devi_offline() calls into pcieb_detach() which in turn 274 * calls into this function. In this case there are no ports 275 * created under a new probe bridge dip, as ports are only 276 * created after the configurator finishing probing, thus the 277 * ndi_hp_walk_cn() will see no ports when this is called 278 * from the PCI configurtor. 279 */ 280 arg.nexus_dip = dip; 281 arg.connector_num = DDI_HP_CN_NUM_NONE; 282 arg.rv = NDI_SUCCESS; 283 284 /* tear down all virtual hotplug handles */ 285 ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 286 287 if (arg.rv != NDI_SUCCESS) 288 return (DDI_FAILURE); 289 290 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 291 (void) pciehpc_uninit(dip); 292 else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 293 (void) pcishpc_uninit(dip); 294 295 return (DDI_SUCCESS); 296 } 297 298 /* 299 * interrupt handler 300 */ 301 int 302 pcie_hp_intr(dev_info_t *dip) 303 { 304 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 305 int ret = DDI_INTR_UNCLAIMED; 306 307 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 308 ret = pciehpc_intr(dip); 309 else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 310 ret = pcishpc_intr(dip); 311 312 return (ret); 313 } 314 315 /* 316 * Probe the given PCIe/PCI Hotplug Connection (CN). 317 */ 318 /*ARGSUSED*/ 319 int 320 pcie_hp_probe(pcie_hp_slot_t *slot_p) 321 { 322 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 323 dev_info_t *dip = ctrl_p->hc_dip; 324 325 /* 326 * Call the configurator to probe a given PCI hotplug 327 * Hotplug Connection (CN). 328 */ 329 if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 330 != PCICFG_SUCCESS) { 331 PCIE_DBG("pcie_hp_probe() failed\n"); 332 return (DDI_FAILURE); 333 } 334 slot_p->hs_condition = AP_COND_OK; 335 pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip), 336 slot_p->hs_minor), slot_p->hs_device_num); 337 338 /* 339 * Create ports for the newly probed devices. 340 * Note, this is only for the first level children because the 341 * descendants' ports will be created during bridge driver attach. 342 */ 343 return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num)); 344 } 345 346 /* 347 * Unprobe the given PCIe/PCI Hotplug Connection (CN): 348 * 1. remove all child device nodes 349 * 2. unregister all dependent ports 350 */ 351 /*ARGSUSED*/ 352 int 353 pcie_hp_unprobe(pcie_hp_slot_t *slot_p) 354 { 355 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 356 dev_info_t *dip = ctrl_p->hc_dip; 357 pcie_hp_unreg_port_t arg; 358 359 /* 360 * Call the configurator to unprobe a given PCI hotplug 361 * Hotplug Connection (CN). 362 */ 363 if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 364 != PCICFG_SUCCESS) { 365 PCIE_DBG("pcie_hp_unprobe() failed\n"); 366 return (DDI_FAILURE); 367 } 368 slot_p->hs_condition = AP_COND_UNKNOWN; 369 pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip), 370 slot_p->hs_minor)); 371 372 /* 373 * Remove ports for the unprobed devices. 374 * Note, this is only for the first level children because the 375 * descendants' ports were already removed during bridge driver dettach. 376 */ 377 arg.nexus_dip = dip; 378 arg.connector_num = slot_p->hs_info.cn_num; 379 arg.rv = NDI_SUCCESS; 380 ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 381 382 return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE); 383 } 384 385 /* Read-only probe: no hardware register programming. */ 386 int 387 pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip) 388 { 389 long dev, func; 390 int ret; 391 char *sp; 392 dev_info_t *cdip; 393 394 *pcdip = NULL; 395 /* 396 * Parse the string of a pci Port name and get the device number 397 * and function number. 398 */ 399 if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 400 return (DDI_EINVAL); 401 if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 402 return (DDI_EINVAL); 403 404 ret = pcicfg_configure(dip, (int)dev, (int)func, 405 PCICFG_FLAG_READ_ONLY); 406 if (ret == PCICFG_SUCCESS) { 407 cdip = pcie_hp_devi_find(dip, (int)dev, (int)func); 408 *pcdip = cdip; 409 } 410 return (ret); 411 } 412 413 /* Read-only unprobe: no hardware register programming. */ 414 int 415 pcie_read_only_unprobe(dev_info_t *dip, char *cn_name) 416 { 417 long dev, func; 418 int ret; 419 char *sp; 420 421 /* 422 * Parse the string of a pci Port name and get the device number 423 * and function number. 424 */ 425 if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 426 return (DDI_EINVAL); 427 if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 428 return (DDI_EINVAL); 429 430 ret = pcicfg_unconfigure(dip, (int)dev, (int)func, 431 PCICFG_FLAG_READ_ONLY); 432 433 return (ret); 434 } 435 436 /* Control structure used to find a device in the devinfo tree */ 437 struct pcie_hp_find_ctrl { 438 uint_t device; 439 uint_t function; 440 dev_info_t *dip; 441 }; 442 443 /* 444 * find a devinfo node with specified device and function number 445 * in the device tree under 'dip' 446 */ 447 dev_info_t * 448 pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function) 449 { 450 struct pcie_hp_find_ctrl ctrl; 451 int count; 452 453 ctrl.device = device; 454 ctrl.function = function; 455 ctrl.dip = NULL; 456 457 ndi_devi_enter(dip, &count); 458 ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func, 459 (void *)&ctrl); 460 ndi_devi_exit(dip, count); 461 462 return (ctrl.dip); 463 } 464 465 /* 466 * routine to create 'pci-occupant' property for a hotplug slot 467 */ 468 void 469 pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev) 470 { 471 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 472 pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl; 473 pcie_hp_slot_t *slotp = NULL; 474 pcie_hp_cn_cfg_t cn_cfg; 475 pcie_hp_occupant_info_t *occupant; 476 int circular, i; 477 478 ndi_devi_enter(dip, &circular); 479 480 if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) { 481 slotp = (ctrl_p && (pci_dev == 0)) ? 482 ctrl_p->hc_slots[pci_dev] : NULL; 483 } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) { 484 if (ctrl_p) { 485 int slot_num; 486 487 slot_num = (ctrl_p->hc_device_increases) ? 488 (pci_dev - ctrl_p->hc_device_start) : 489 (pci_dev + ctrl_p->hc_device_start); 490 491 slotp = ctrl_p->hc_slots[slot_num]; 492 } else { 493 slotp = NULL; 494 } 495 } 496 497 if (slotp == NULL) 498 return; 499 500 occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP); 501 occupant->i = 0; 502 503 cn_cfg.flag = B_FALSE; 504 cn_cfg.rv = NDI_SUCCESS; 505 cn_cfg.dip = NULL; 506 cn_cfg.slotp = (void *)slotp; 507 cn_cfg.cn_private = (void *)occupant; 508 509 ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants, 510 (void *)&cn_cfg); 511 512 if (occupant->i == 0) { 513 /* no occupants right now, need to create stub property */ 514 char *c[] = { "" }; 515 (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 516 c, 1); 517 } else { 518 (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 519 occupant->id, occupant->i); 520 } 521 522 for (i = 0; i < occupant->i; i++) 523 kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN])); 524 525 kmem_free(occupant, sizeof (pcie_hp_occupant_info_t)); 526 527 ndi_devi_exit(dip, circular); 528 } 529 530 /* 531 * routine to remove 'pci-occupant' property for a hotplug slot 532 */ 533 void 534 pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev) 535 { 536 (void) ddi_prop_remove(dev, dip, "pci-occupant"); 537 } 538 539 /* 540 * general code to create a minor node, called from hotplug controller 541 * drivers. 542 */ 543 int 544 pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 545 { 546 dev_info_t *dip = ctrl_p->hc_dip; 547 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot]; 548 ddi_hp_cn_info_t *info_p = &slot_p->hs_info; 549 550 if (ddi_create_minor_node(dip, info_p->cn_name, 551 S_IFCHR, slot_p->hs_minor, 552 DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 553 return (DDI_FAILURE); 554 } 555 556 (void) ddi_prop_update_int(DDI_DEV_T_NONE, 557 dip, "ap-names", 1 << slot_p->hs_device_num); 558 559 return (DDI_SUCCESS); 560 } 561 562 /* 563 * general code to remove a minor node, called from hotplug controller 564 * drivers. 565 */ 566 void 567 pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 568 { 569 ddi_remove_minor_node(ctrl_p->hc_dip, 570 ctrl_p->hc_slots[slot]->hs_info.cn_name); 571 } 572 573 /* 574 * Local functions (called within this file) 575 */ 576 577 /* 578 * Register ports for all the children with device number device_num 579 */ 580 static int 581 pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num) 582 { 583 dev_info_t *cdip; 584 int rv; 585 586 for (cdip = ddi_get_child(dip); cdip; 587 cdip = ddi_get_next_sibling(cdip)) { 588 if (pcie_hp_match_dev(cdip, device_num)) { 589 /* 590 * Found the newly probed device under the 591 * current slot. Register a port for it. 592 */ 593 if ((rv = pcie_hp_register_port(cdip, dip, NULL)) 594 != DDI_SUCCESS) 595 return (rv); 596 } else { 597 continue; 598 } 599 } 600 601 return (DDI_SUCCESS); 602 } 603 604 /* 605 * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn() 606 * 607 * If connector_num is specified, then unregister the slot's dependent ports 608 * only; Otherwise, unregister all ports of a pci bridge dip. 609 */ 610 static int 611 pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg) 612 { 613 pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg; 614 dev_info_t *dip = unreg_arg->nexus_dip; 615 int rv = NDI_SUCCESS; 616 617 if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) { 618 unreg_arg->rv = rv; 619 return (DDI_WALK_CONTINUE); 620 } 621 622 if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) { 623 /* Unregister ports for all unprobed devices under a slot. */ 624 if (unreg_arg->connector_num == info->cn_num_dpd_on) { 625 626 rv = ndi_hp_unregister(dip, info->cn_name); 627 } 628 } else { 629 630 /* Unregister all ports of a pci bridge dip. */ 631 rv = ndi_hp_unregister(dip, info->cn_name); 632 } 633 634 unreg_arg->rv = rv; 635 if (rv == NDI_SUCCESS) 636 return (DDI_WALK_CONTINUE); 637 else 638 return (DDI_WALK_TERMINATE); 639 } 640 641 /* 642 * Find a port according to cn_name and get the port's state. 643 */ 644 static int 645 pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg) 646 { 647 pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg; 648 649 if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) 650 return (DDI_WALK_CONTINUE); 651 652 if (strcmp(info->cn_name, port->cn_name) == 0) { 653 /* Matched. */ 654 port->cn_state = info->cn_state; 655 port->rv = DDI_SUCCESS; 656 657 return (DDI_WALK_TERMINATE); 658 } 659 660 return (DDI_WALK_CONTINUE); 661 } 662 663 /* 664 * Find the physical slot with the given device number; 665 * return the slot if found. 666 */ 667 static pcie_hp_slot_t * 668 pcie_find_physical_slot(dev_info_t *dip, int dev_num) 669 { 670 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 671 pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip); 672 673 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 674 /* PCIe has only one slot */ 675 return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL); 676 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 677 for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) { 678 if (ctrl->hc_slots[slot]->hs_device_num == dev_num) { 679 /* found */ 680 return (ctrl->hc_slots[slot]); 681 } 682 } 683 } 684 685 return (NULL); 686 } 687 688 /* 689 * setup slot name/slot-number info for the port which is being registered. 690 */ 691 static int 692 pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info) 693 { 694 int ret, dev_num, func_num, name_len; 695 dev_info_t *pdip = ddi_get_parent(dip); 696 pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip); 697 pcie_hp_slot_t *slot; 698 pcie_req_id_t bdf; 699 char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN]; 700 701 ret = pcie_get_bdf_from_dip(dip, &bdf); 702 if (ret != DDI_SUCCESS) { 703 return (ret); 704 } 705 if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) || 706 PCIE_IS_PCI2PCIE(bus_p)) { 707 /* 708 * It is under a PCIe device, devcie number is always 0; 709 * function number might > 8 in ARI supported case. 710 */ 711 dev_num = 0; 712 func_num = (bdf & ((~PCI_REG_BUS_M) >> 8)); 713 } else { 714 dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3; 715 func_num = bdf & (PCI_REG_FUNC_M >> 8); 716 } 717 /* 718 * The string length of dev_num and func_num must be no longer than 4 719 * including the string end mark. (With ARI case considered, e.g., 720 * dev_num=0x0, func_num=0xff.) 721 */ 722 (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x", 723 dev_num, func_num); 724 /* 725 * Calculate the length of cn_name. 726 * The format of pci port name is: pci.d,f 727 * d stands for dev_num, f stands for func_num. So the length of the 728 * name string can be calculated as following. 729 */ 730 name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1; 731 732 cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP); 733 (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x", 734 dev_num, func_num); 735 cn_info->cn_num = (dev_num << 8) | func_num; 736 slot = pcie_find_physical_slot(pdip, dev_num); 737 738 cn_info->cn_num_dpd_on = slot ? 739 slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE; 740 741 return (DDI_SUCCESS); 742 } 743 744 /* 745 * Extract device and function number from port name, whose format is 746 * something like 'pci.1,0' 747 */ 748 static int 749 pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num) 750 { 751 int name_len, ret; 752 long d, f; 753 char *sp; 754 755 /* some checks for the input name */ 756 name_len = strlen(cn_name); 757 if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) || 758 (name_len > (PCIE_HP_PORT_NAME_STRING_LEN + 759 PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) || 760 (strncmp("pci.", cn_name, 4) != 0)) { 761 return (DDI_EINVAL); 762 } 763 ret = ddi_strtol(cn_name + 4, &sp, 10, &d); 764 if (ret != DDI_SUCCESS) 765 return (ret); 766 767 if (strncmp(",", sp, 1) != 0) 768 return (DDI_EINVAL); 769 770 ret = ddi_strtol(sp + 1, NULL, 10, &f); 771 if (ret != DDI_SUCCESS) 772 return (ret); 773 *dev_num = (int)d; 774 *func_num = (int)f; 775 776 return (ret); 777 } 778 779 /* 780 * Check/copy cn_name and set connection numbers. 781 * If it is a valid name, then setup cn_info for the newly created port. 782 */ 783 static int 784 pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name, 785 ddi_hp_cn_info_t *cn_info) 786 { 787 int dev_num, func_num, ret; 788 pcie_hp_slot_t *slot; 789 790 if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num)) 791 != DDI_SUCCESS) 792 return (ret); 793 794 if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) == 795 DDI_SUCCESS) { 796 cn_info->cn_state = DDI_HP_CN_STATE_PRESENT; 797 } else { 798 cn_info->cn_state = DDI_HP_CN_STATE_EMPTY; 799 } 800 801 cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP); 802 cn_info->cn_num = (dev_num << 8) | func_num; 803 804 slot = pcie_find_physical_slot(pdip, dev_num); 805 if (slot) { 806 cn_info->cn_num_dpd_on = slot->hs_info.cn_num; 807 } else { 808 cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE; 809 } 810 return (DDI_SUCCESS); 811 } 812 813 static int 814 ndi2ddi(int n) 815 { 816 int ret; 817 818 switch (n) { 819 case NDI_SUCCESS: 820 ret = DDI_SUCCESS; 821 break; 822 case NDI_NOMEM: 823 ret = DDI_ENOMEM; 824 break; 825 case NDI_BUSY: 826 ret = DDI_EBUSY; 827 break; 828 case NDI_EINVAL: 829 ret = DDI_EINVAL; 830 break; 831 case NDI_ENOTSUP: 832 ret = DDI_ENOTSUP; 833 break; 834 case NDI_FAILURE: 835 default: 836 ret = DDI_FAILURE; 837 break; 838 } 839 return (ret); 840 } 841 842 /* 843 * Common routine to create and register a new port 844 * 845 * Create an empty port if dip is NULL, and cn_name needs to be specified in 846 * this case. Otherwise, create a port mapping to the specified dip, and cn_name 847 * is not needed in this case. 848 */ 849 static int 850 pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name) 851 { 852 ddi_hp_cn_info_t *cn_info; 853 int ret; 854 855 ASSERT((dip == NULL) != (cn_name == NULL)); 856 cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP); 857 if (dip != NULL) 858 ret = pcie_hp_create_port_name_num(dip, cn_info); 859 else 860 ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info); 861 862 if (ret != DDI_SUCCESS) { 863 kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 864 return (ret); 865 } 866 867 cn_info->cn_child = dip; 868 cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT; 869 cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT; 870 871 ret = ndi_hp_register(pdip, cn_info); 872 873 kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1); 874 kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 875 876 return (ndi2ddi(ret)); 877 } 878 879 /* Check if there is a piece of hardware exist corresponding to the cn_name */ 880 static int 881 pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num) 882 { 883 884 /* 885 * VHPTODO: 886 * According to device and function number, check if there is a hardware 887 * device exists. Currently, this function can not be reached before 888 * we enable state transition to or from "Port-Empty" or "Port-Present" 889 * states. When the pci device type project is integrated, we are going 890 * to call the pci config space access interfaces introduced by it. 891 */ 892 _NOTE(ARGUNUSED(dip, dev_num, func_num)); 893 894 return (DDI_SUCCESS); 895 } 896 897 /* 898 * Dispatch hotplug commands to different hotplug controller drivers, including 899 * physical and virtual hotplug operations. 900 */ 901 /* ARGSUSED */ 902 int 903 pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, 904 void *arg, void *result) 905 { 906 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 907 int ret = DDI_SUCCESS; 908 909 PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n", 910 dip, cn_name, op, arg); 911 912 switch (op) { 913 case DDI_HPOP_CN_CREATE_PORT: 914 { 915 /* create an empty port */ 916 return (pcie_hp_register_port(NULL, dip, cn_name)); 917 } 918 case DDI_HPOP_CN_CHANGE_STATE: 919 { 920 ddi_hp_cn_state_t curr_state; 921 ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg; 922 pcie_hp_port_state_t state_arg; 923 924 if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) { 925 /* this is for physical slot state change */ 926 break; 927 } 928 PCIE_DBG("pcie_hp_common_ops: change port state" 929 " dip=%p cn_name=%s" 930 " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 931 932 state_arg.rv = DDI_FAILURE; 933 state_arg.cn_name = cn_name; 934 ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg); 935 if (state_arg.rv != DDI_SUCCESS) { 936 /* can not find the port */ 937 return (DDI_EINVAL); 938 } 939 curr_state = state_arg.cn_state; 940 /* 941 * Check if this is for changing port's state: change to/from 942 * PORT_EMPTY/PRESENT states. 943 */ 944 if (curr_state < target_state) { 945 /* Upgrade state */ 946 switch (curr_state) { 947 case DDI_HP_CN_STATE_PORT_EMPTY: 948 if (target_state == 949 DDI_HP_CN_STATE_PORT_PRESENT) { 950 int dev_num, func_num; 951 952 ret = pcie_hp_get_df_from_port_name( 953 cn_name, &dev_num, &func_num); 954 if (ret != DDI_SUCCESS) 955 goto port_state_done; 956 957 ret = pcie_hp_check_hardware_existence( 958 dip, dev_num, func_num); 959 } else if (target_state == 960 DDI_HP_CN_STATE_OFFLINE) { 961 ret = pcie_read_only_probe(dip, 962 cn_name, (dev_info_t **)result); 963 } else 964 ret = DDI_EINVAL; 965 966 goto port_state_done; 967 case DDI_HP_CN_STATE_PORT_PRESENT: 968 if (target_state == 969 DDI_HP_CN_STATE_OFFLINE) 970 ret = pcie_read_only_probe(dip, 971 cn_name, (dev_info_t **)result); 972 else 973 ret = DDI_EINVAL; 974 975 goto port_state_done; 976 default: 977 ASSERT("unexpected state"); 978 } 979 } else { 980 /* Downgrade state */ 981 switch (curr_state) { 982 case DDI_HP_CN_STATE_PORT_PRESENT: 983 { 984 int dev_num, func_num; 985 986 ret = pcie_hp_get_df_from_port_name(cn_name, 987 &dev_num, &func_num); 988 if (ret != DDI_SUCCESS) 989 goto port_state_done; 990 991 ret = pcie_hp_check_hardware_existence(dip, 992 dev_num, func_num); 993 994 goto port_state_done; 995 } 996 case DDI_HP_CN_STATE_OFFLINE: 997 ret = pcie_read_only_unprobe(dip, cn_name); 998 999 goto port_state_done; 1000 default: 1001 ASSERT("unexpected state"); 1002 } 1003 } 1004 port_state_done: 1005 *(ddi_hp_cn_state_t *)result = curr_state; 1006 return (ret); 1007 } 1008 default: 1009 break; 1010 } 1011 1012 if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 1013 /* PCIe hotplug */ 1014 ret = pciehpc_hp_ops(dip, cn_name, op, arg, result); 1015 } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 1016 /* PCI SHPC hotplug */ 1017 ret = pcishpc_hp_ops(dip, cn_name, op, arg, result); 1018 } else { 1019 cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported." 1020 " dip=%p cn_name=%s" 1021 " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 1022 ret = DDI_ENOTSUP; 1023 } 1024 1025 #if defined(__x86) 1026 /* 1027 * like in attach, since hotplugging can change error registers, 1028 * we need to ensure that the proper bits are set on this port 1029 * after a configure operation 1030 */ 1031 if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) && 1032 (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED)) 1033 pcieb_intel_error_workaround(dip); 1034 #endif 1035 1036 return (ret); 1037 } 1038 1039 /* 1040 * pcie_hp_match_dev_func: 1041 * Match dip's PCI device number and function number with input ones. 1042 */ 1043 static int 1044 pcie_hp_match_dev_func(dev_info_t *dip, void *hdl) 1045 { 1046 struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl; 1047 pci_regspec_t *pci_rp; 1048 int length; 1049 int pci_dev, pci_func; 1050 1051 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1052 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1053 ctrl->dip = NULL; 1054 return (DDI_WALK_TERMINATE); 1055 } 1056 1057 /* get the PCI device address info */ 1058 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1059 pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 1060 1061 /* 1062 * free the memory allocated by ddi_prop_lookup_int_array 1063 */ 1064 ddi_prop_free(pci_rp); 1065 1066 if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { 1067 /* found the match for the specified device address */ 1068 ctrl->dip = dip; 1069 return (DDI_WALK_TERMINATE); 1070 } 1071 1072 /* 1073 * continue the walk to the next sibling to look for a match. 1074 */ 1075 return (DDI_WALK_PRUNECHILD); 1076 } 1077 1078 /* 1079 * pcie_hp_match_dev: 1080 * Match the dip's pci device number with the input dev_num 1081 */ 1082 static boolean_t 1083 pcie_hp_match_dev(dev_info_t *dip, int dev_num) 1084 { 1085 pci_regspec_t *pci_rp; 1086 int length; 1087 int pci_dev; 1088 1089 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1090 "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1091 return (B_FALSE); 1092 } 1093 1094 /* get the PCI device address info */ 1095 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1096 1097 /* 1098 * free the memory allocated by ddi_prop_lookup_int_array 1099 */ 1100 ddi_prop_free(pci_rp); 1101 1102 if (pci_dev == dev_num) { 1103 /* found the match for the specified device address */ 1104 return (B_TRUE); 1105 } 1106 1107 return (B_FALSE); 1108 } 1109 1110 /* 1111 * Callback function to match with device number in order to list 1112 * occupants under a specific slot 1113 */ 1114 static int 1115 pcie_hp_list_occupants(dev_info_t *dip, void *arg) 1116 { 1117 pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg; 1118 pcie_hp_occupant_info_t *occupant = 1119 (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private; 1120 pcie_hp_slot_t *slot_p = 1121 (pcie_hp_slot_t *)cn_cfg_p->slotp; 1122 int pci_dev; 1123 pci_regspec_t *pci_rp; 1124 int length; 1125 major_t major; 1126 1127 /* 1128 * Get the PCI device number information from the devinfo 1129 * node. Since the node may not have the address field 1130 * setup (this is done in the DDI_INITCHILD of the parent) 1131 * we look up the 'reg' property to decode that information. 1132 */ 1133 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 1134 DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, 1135 (uint_t *)&length) != DDI_PROP_SUCCESS) { 1136 cn_cfg_p->rv = DDI_FAILURE; 1137 cn_cfg_p->dip = dip; 1138 return (DDI_WALK_TERMINATE); 1139 } 1140 1141 /* get the pci device id information */ 1142 pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1143 1144 /* 1145 * free the memory allocated by ddi_prop_lookup_int_array 1146 */ 1147 ddi_prop_free(pci_rp); 1148 1149 /* 1150 * Match the node for the device number of the slot. 1151 */ 1152 if (pci_dev == slot_p->hs_device_num) { 1153 1154 major = ddi_driver_major(dip); 1155 1156 /* 1157 * If the node is not yet attached, then don't list it 1158 * as an occupant. This is valid, since nothing can be 1159 * consuming it until it is attached, and cfgadm will 1160 * ask for the property explicitly which will cause it 1161 * to be re-freshed right before checking with rcm. 1162 */ 1163 if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip)) 1164 return (DDI_WALK_PRUNECHILD); 1165 1166 /* 1167 * If we have used all our occupants then print mesage 1168 * and terminate walk. 1169 */ 1170 if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) { 1171 cmn_err(CE_WARN, 1172 "pcie (%s%d): unable to list all occupants", 1173 ddi_driver_name(ddi_get_parent(dip)), 1174 ddi_get_instance(ddi_get_parent(dip))); 1175 return (DDI_WALK_TERMINATE); 1176 } 1177 1178 /* 1179 * No need to hold the dip as ddi_walk_devs 1180 * has already arranged that for us. 1181 */ 1182 occupant->id[occupant->i] = 1183 kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP); 1184 (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]); 1185 occupant->i++; 1186 } 1187 1188 /* 1189 * continue the walk to the next sibling to look for a match 1190 * or to find other nodes if this card is a multi-function card. 1191 */ 1192 return (DDI_WALK_PRUNECHILD); 1193 } 1194 1195 /* 1196 * Generate the System Event for ESC_DR_REQ. 1197 * One of the consumers is pcidr, it calls to libcfgadm to perform a 1198 * configure or unconfigure operation to the AP. 1199 */ 1200 void 1201 pcie_hp_gen_sysevent_req(char *slot_name, int hint, 1202 dev_info_t *self, int kmflag) 1203 { 1204 sysevent_id_t eid; 1205 nvlist_t *ev_attr_list = NULL; 1206 char cn_path[MAXPATHLEN]; 1207 char *ap_id; 1208 int err, ap_id_len; 1209 1210 /* 1211 * Minor device name (AP) will be bus path 1212 * concatenated with slot name 1213 */ 1214 (void) strcpy(cn_path, "/devices"); 1215 (void) ddi_pathname(self, cn_path + strlen("/devices")); 1216 1217 ap_id_len = strlen(cn_path) + strlen(":") + 1218 strlen(slot_name) + 1; 1219 ap_id = kmem_zalloc(ap_id_len, kmflag); 1220 if (ap_id == NULL) { 1221 cmn_err(CE_WARN, 1222 "%s%d: Failed to allocate memory for AP ID: %s:%s", 1223 ddi_driver_name(self), ddi_get_instance(self), 1224 cn_path, slot_name); 1225 1226 return; 1227 } 1228 1229 (void) strcpy(ap_id, cn_path); 1230 (void) strcat(ap_id, ":"); 1231 (void) strcat(ap_id, slot_name); 1232 1233 err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag); 1234 if (err != 0) { 1235 cmn_err(CE_WARN, 1236 "%s%d: Failed to allocate memory " 1237 "for event attributes%s", ddi_driver_name(self), 1238 ddi_get_instance(self), ESC_DR_REQ); 1239 1240 kmem_free(ap_id, ap_id_len); 1241 return; 1242 } 1243 1244 switch (hint) { 1245 1246 case SE_INVESTIGATE_RES: /* fall through */ 1247 case SE_INCOMING_RES: /* fall through */ 1248 case SE_OUTGOING_RES: /* fall through */ 1249 1250 err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE, 1251 SE_REQ2STR(hint)); 1252 1253 if (err != 0) { 1254 cmn_err(CE_WARN, 1255 "%s%d: Failed to add attr [%s] " 1256 "for %s event", ddi_driver_name(self), 1257 ddi_get_instance(self), 1258 DR_REQ_TYPE, ESC_DR_REQ); 1259 1260 goto done; 1261 } 1262 break; 1263 1264 default: 1265 cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent", 1266 ddi_driver_name(self), ddi_get_instance(self)); 1267 1268 goto done; 1269 } 1270 1271 /* 1272 * Add attachment point as attribute (common attribute) 1273 */ 1274 1275 err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id); 1276 1277 if (err != 0) { 1278 cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event", 1279 ddi_driver_name(self), ddi_get_instance(self), 1280 DR_AP_ID, EC_DR); 1281 1282 goto done; 1283 } 1284 1285 1286 /* 1287 * Log this event with sysevent framework. 1288 */ 1289 1290 err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR, 1291 ESC_DR_REQ, ev_attr_list, &eid, 1292 ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP)); 1293 if (err != 0) { 1294 cmn_err(CE_WARN, "%s%d: Failed to log %s event", 1295 ddi_driver_name(self), ddi_get_instance(self), EC_DR); 1296 } 1297 1298 done: 1299 nvlist_free(ev_attr_list); 1300 kmem_free(ap_id, ap_id_len); 1301 } 1302