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