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