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