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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <alloca.h>
  27 #include <assert.h>
  28 #include <fm/topo_mod.h>
  29 #include <libnvpair.h>
  30 #include <string.h>
  31 #include <sys/fm/protocol.h>
  32 
  33 #include <did.h>
  34 #include <pcibus.h>
  35 #include <pcibus_labels.h>
  36 
  37 extern slotnm_rewrite_t *Slot_Rewrites;
  38 extern physlot_names_t *Physlot_Names;
  39 extern missing_names_t *Missing_Names;
  40 
  41 /*
  42  * Do a platform specific label lookup based on physical slot number.
  43  */
  44 static const char *
  45 pci_label_physlot_lookup(topo_mod_t *mod, char *platform, did_t *dp)
  46 {
  47         const char *rlabel = NULL;
  48         int n, p, i;
  49 
  50         topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n",
  51             __func__, platform);
  52 
  53         if ((n = did_physlot(dp)) < 0 || Physlot_Names == NULL ||
  54             platform == NULL)
  55                 return (NULL);
  56 
  57         topo_mod_dprintf(mod, "%s: doing a lookup for physlot=%d\n",
  58             __func__, n);
  59 
  60         for (p = 0; p < Physlot_Names->psn_nplats; p++) {
  61                 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
  62                     __func__, Physlot_Names->psn_names[p].pnm_platform);
  63                 if (strcasecmp(Physlot_Names->psn_names[p].pnm_platform,
  64                     platform) != 0)
  65                         continue;
  66                 topo_mod_dprintf(mod, "%s: found lookup table for this "
  67                     "platform\n", __func__);
  68                 for (i = 0; i < Physlot_Names->psn_names[p].pnm_nnames; i++) {
  69                         physnm_t ps;
  70                         ps = Physlot_Names->psn_names[p].pnm_names[i];
  71                         if (ps.ps_num == n) {
  72                                 topo_mod_dprintf(mod, "%s: matched entry=%d, "
  73                                     "label=%s\n", __func__, i, ps.ps_label);
  74                                 rlabel = ps.ps_label;
  75                                 break;
  76                         }
  77                 }
  78                 break;
  79         }
  80         if (rlabel != NULL) {
  81                 topo_mod_dprintf(mod, "%s: returning label=%s\n",
  82                     __func__, rlabel);
  83         }
  84         return (rlabel);
  85 }
  86 
  87 /*
  88  * Do a platform specific label lookup based on slot name.
  89  */
  90 static const char *
  91 pci_label_slotname_lookup(topo_mod_t *mod, char *platform,
  92     const char *label, did_t *dp)
  93 {
  94         const char *rlabel = label;
  95         int s, i, ret;
  96 
  97         if (Slot_Rewrites == NULL || platform == NULL)
  98                 return (rlabel);
  99 
 100         topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n",
 101             __func__, platform);
 102 
 103         for (s = 0; s < Slot_Rewrites->srw_nplats; s++) {
 104                 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
 105                     __func__, Slot_Rewrites->srw_platrewrites[s].prw_platform);
 106                 if (strcasecmp(Slot_Rewrites->srw_platrewrites[s].prw_platform,
 107                     platform) != 0)
 108                         continue;
 109                 topo_mod_dprintf(mod, "%s: found lookup table for this "
 110                     "platform\n", __func__);
 111                 for (i = 0;
 112                     i < Slot_Rewrites->srw_platrewrites[s].prw_nrewrites;
 113                     i++) {
 114                         slot_rwd_t rw;
 115                         rw = Slot_Rewrites->srw_platrewrites[s].prw_rewrites[i];
 116                         if (strcmp(rw.srw_obp, label) == 0) {
 117                                 topo_mod_dprintf(mod, "%s: matched entry=%d, "
 118                                     "old_label=%s, new_label=%s\n",
 119                                     __func__, i, rw.srw_obp,
 120                                     rw.srw_new ? rw.srw_new : NULL);
 121                                 /*
 122                                  * If a test function is specified then call
 123                                  * it to do an additional check.
 124                                  */
 125                                 if (rw.srw_test != NULL) {
 126                                         topo_mod_dprintf(mod,
 127                                             "%s: calling test function=%p\n",
 128                                             __func__, rw.srw_test);
 129                                         if (ret = rw.srw_test(mod, dp))
 130                                                 rlabel = rw.srw_new;
 131                                         topo_mod_dprintf(mod,
 132                                             "%s: test function return=%d\n",
 133                                             __func__, ret);
 134                                 } else {
 135                                         rlabel = rw.srw_new;
 136                                 }
 137                                 break;
 138                         }
 139                 }
 140                 break;
 141         }
 142         topo_mod_dprintf(mod, "%s: returning label=%s\n", __func__,
 143             rlabel ? rlabel : "NULL");
 144         return (rlabel);
 145 }
 146 
 147 /*
 148  * Do a platform specific label lookup based on bus, dev, etc.
 149  */
 150 static const char *
 151 pci_label_missing_lookup(topo_mod_t *mod, char *platform, did_t *dp)
 152 {
 153         const char *rlabel = NULL;
 154         int board, bridge, rc, bus, dev;
 155         int p, i, ret;
 156 
 157         if (Missing_Names == NULL || platform == NULL)
 158                 return (NULL);
 159 
 160         bridge = did_bridge(dp);
 161         board = did_board(dp);
 162         rc = did_rc(dp);
 163         did_BDF(dp, &bus, &dev, NULL);
 164 
 165         topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s, "
 166             "board=%d, bridge=%d, rc=%d, bus=%d, dev=%d\n",
 167             __func__, platform, board, bridge, rc, bus, dev);
 168 
 169         for (p = 0; p < Missing_Names->mn_nplats; p++) {
 170                 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
 171                     __func__, Missing_Names->mn_names[p].pdl_platform);
 172                 if (strcasecmp(Missing_Names->mn_names[p].pdl_platform,
 173                     platform) != 0)
 174                         continue;
 175                 topo_mod_dprintf(mod, "%s: found lookup table for this "
 176                     "platform\n", __func__);
 177                 for (i = 0; i < Missing_Names->mn_names[p].pdl_nnames; i++) {
 178                         devlab_t m;
 179                         m = Missing_Names->mn_names[p].pdl_names[i];
 180                         if (m.dl_board == board && m.dl_bridge == bridge &&
 181                             m.dl_rc == rc &&
 182                             (m.dl_bus == -1 || m.dl_bus == bus) &&
 183                             (m.dl_dev == -1 || m.dl_dev == dev)) {
 184                                 topo_mod_dprintf(mod, "%s: matched entry=%d, "
 185                                     "label=%s\n", __func__, i, m.dl_label);
 186                                 /*
 187                                  * If a test function is specified then call
 188                                  * it to do an additional test.
 189                                  */
 190                                 if (m.dl_test != NULL) {
 191                                         topo_mod_dprintf(mod,
 192                                             "%s: calling test function=%p\n",
 193                                             __func__, m.dl_test);
 194                                         if (ret = m.dl_test(mod, dp))
 195                                                 rlabel = m.dl_label;
 196                                         topo_mod_dprintf(mod,
 197                                             "%s: test function return=%d\n",
 198                                             __func__, ret);
 199                                         if (ret)
 200                                                 break;
 201                                 } else {
 202                                         rlabel = m.dl_label;
 203                                         break;
 204                                 }
 205                         }
 206                 }
 207                 break;
 208         }
 209         if (rlabel != NULL) {
 210                 topo_mod_dprintf(mod, "%s: match found, label=%s\n",
 211                     __func__, rlabel);
 212         }
 213         return (rlabel);
 214 }
 215 
 216 /*
 217  * Do an overall slot label lookup for the device node.
 218  */
 219 char *
 220 pci_slot_label_lookup(topo_mod_t *mod, tnode_t *node, did_t *dp, did_t *pdp)
 221 {
 222         tnode_t *anode, *apnode;
 223         did_t *adp, *apdp;
 224         char *plat, *pp, *l, *ancestor_l = NULL, *new_l = NULL;
 225         int err, b, d, f, done = 0;
 226         size_t len;
 227 
 228         did_BDF(dp, &b, &d, &f);
 229 
 230         topo_mod_dprintf(mod, "%s: entry: node=%p, node_name=%s, "
 231             "node_inst=%d, dp=%p, dp_bdf=%d/%d/%d, pdp=%p\n",
 232             __func__, node, topo_node_name(node), topo_node_instance(node),
 233             dp, b, d, f, pdp);
 234 
 235         /*
 236          * If this device has a physical slot number then check if
 237          * an ancestor also has a slot label.
 238          *
 239          * If an ancestor has a slot label, then this node's label
 240          * is generated by concatenating a default label onto the
 241          * ancestor's label.
 242          *
 243          * We grab pairs of ancestors (parent and child) as we go up
 244          * the tree because the parent is checked for the presence
 245          * of a slot while the child contains the label.
 246          *
 247          * Note that this algorithm only applies to nodes which have
 248          * a physcal slot number. (i.e. PCIE devices or PCI/PCIX
 249          * devices off of a PCIE to PCIX switch)
 250          */
 251         if (did_physlot(pdp) >= 0) {
 252 
 253                 topo_mod_dprintf(mod, "%s: node=%p: node has a physical "
 254                     "slot=%d, checking ancestors for slots\n",
 255                     __func__, node, did_physlot(pdp));
 256 
 257                 /*
 258                  * Get this device's physical slot name.
 259                  */
 260                 l = (char *)did_physlot_name(pdp, d);
 261 
 262                 anode = topo_node_parent(node);
 263 
 264                 /*
 265                  * Check ancestors for a slot label until we
 266                  * either find one or hit a non-pci device.
 267                  */
 268                 while (!done) {
 269 
 270                         /*
 271                          * Get next ancestor node and data pointers.
 272                          */
 273                         anode = topo_node_parent(anode);
 274                         if (anode != NULL) {
 275                                 adp = did_find(mod,
 276                                     topo_node_getspecific(anode));
 277                                 apnode = topo_node_parent(anode);
 278                                 if (apnode != NULL)
 279                                         apdp = did_find(mod,
 280                                             topo_node_getspecific(apnode));
 281                                 else
 282                                         apdp = NULL;
 283                         } else {
 284                                 apnode = NULL;
 285                                 apdp = adp = NULL;
 286                         }
 287 
 288                         topo_mod_dprintf(mod, "%s: node=%p: checking next "
 289                             "two ancestors: anode=%p, adp=%p "
 290                             "apnode=%p, apdp=%p\n",
 291                             __func__, node, anode, adp, apnode, apdp);
 292                         if ((anode != NULL) && (adp != NULL)) {
 293                                 did_BDF(adp, &b, &d, &f);
 294                                 topo_mod_dprintf(mod, "%s: node=%p: "
 295                                     "anode_name=%s[%d], anode_bdf=%d/%d/%d\n",
 296                                     __func__, node, topo_node_name(anode),
 297                                     topo_node_instance(anode), b, d, f);
 298                         }
 299                         if ((apnode != NULL) && (apdp != NULL)) {
 300                                 did_BDF(apdp, &b, &d, &f);
 301                                 topo_mod_dprintf(mod, "%s: node=%p: "
 302                                     "apnode_name=%s[%d], "
 303                                     "apnode_bdf=%d/%d/%d\n",
 304                                     __func__, node, topo_node_name(apnode),
 305                                     topo_node_instance(apnode), b, d, f);
 306                         }
 307 
 308                         /*
 309                          * If the ancestors do not exist or are not pci
 310                          * devices then we're done searching.
 311                          *
 312                          * Otherwise, if the ancestor has a physical slot,
 313                          * and it is a different slot than the one we
 314                          * started with then lookup the ancestor label,
 315                          * and we're done.
 316                          */
 317                         if ((anode == NULL) || (adp == NULL) ||
 318                             (apnode == NULL) || (apdp == NULL)) {
 319                                 done++;
 320                         } else if (did_physlot_exists(apdp) &&
 321                             (apdp != pdp)) {
 322                                 if (topo_node_label(anode, &ancestor_l,
 323                                     &err) != 0) {
 324                                         topo_mod_dprintf(mod,
 325                                             "%s: node=%p: topo_node_label() "
 326                                             "FAILED!", __func__, node);
 327                                         (void) topo_mod_seterrno(mod, err);
 328                                         return (NULL);
 329                                 }
 330                                 done++;
 331                                 topo_mod_dprintf(mod, "%s: node=%p: found "
 332                                     "ancestor with a slot, label=%s ",
 333                                     __func__, node, ancestor_l);
 334                         }
 335                 }
 336                 if (ancestor_l == NULL) {
 337                         topo_mod_dprintf(mod, "%s: node=%p: no ancestor "
 338                             "slot found\n", __func__, node);
 339                 }
 340         }
 341 
 342         /*
 343          * If we found an ancestor with a slot label, and this node has
 344          * a physical slot number label then concatenate the two to form
 345          * this node's label. Otherwise, do a full slot label lookup.
 346          */
 347         if (ancestor_l && l) {
 348                 topo_mod_dprintf(mod, "%s: node=%p: concatenating "
 349                     "ancestor_l=%s and l=%s\n",
 350                     __func__, node, ancestor_l, l);
 351                 len = strlen(ancestor_l) + strlen(l) + 2;
 352                 new_l = alloca(len);
 353                 (void) snprintf(new_l, len, "%s/%s", ancestor_l, l);
 354                 l = new_l;
 355         } else {
 356                 /*
 357                  * Get platform name used for lookups.
 358                  */
 359                 if (topo_prop_get_string(node, FM_FMRI_AUTHORITY,
 360                     FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) {
 361                         (void) topo_mod_seterrno(mod, err);
 362                         return (NULL);
 363                 }
 364                 /*
 365                  * Trim SUNW, from the platform name
 366                  */
 367                 pp = strchr(plat, ',');
 368                 if (pp == NULL)
 369                         pp = plat;
 370                 else
 371                         ++pp;
 372                 /*
 373                  * Get device number used for lookup.
 374                  */
 375                 did_BDF(dp, NULL, &d, NULL);
 376 
 377                 /*
 378                  * The slot label is determined in the following order:
 379                  * - Platform specific lookup based on physical slot #.
 380                  * - Platform specific lookup based on default label string.
 381                  * - Platform specific lookup based on device number.
 382                  * - Default label.
 383                  *   The default label is based on the slot names property
 384                  *   if it exists, else it is a generic name derived from
 385                  *   the slot #.
 386                  */
 387                 if ((l = (char *)pci_label_physlot_lookup(mod, pp, pdp))
 388                     == NULL) {
 389                         if ((l = (char *)did_physlot_name(pdp, d)) != NULL) {
 390                                 l = (char *)
 391                                     pci_label_slotname_lookup(mod, pp, l, dp);
 392                         }
 393                         if (l == NULL) {
 394                                 l = (char *)
 395                                     pci_label_missing_lookup(mod, pp, dp);
 396                         }
 397                 }
 398                 topo_mod_strfree(mod, plat);
 399         }
 400 
 401         /*
 402          * If we calculated a slot label,  then save it in the
 403          * node's data structure so we can free it later.
 404          */
 405         if (l) {
 406                 if (did_slot_label_get(dp) != NULL)
 407                         topo_mod_strfree(mod, did_slot_label_get(dp));
 408                 l = topo_mod_strdup(mod, l);
 409                 did_slot_label_set(dp, l);
 410         }
 411 
 412         topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n",
 413             __func__, node, (l ? l : "NULL"));
 414 
 415         return (l);
 416 }
 417 
 418 int
 419 pci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
 420 {
 421         uint64_t ptr;
 422         char *l;
 423         did_t *dp, *pdp;
 424         tnode_t *pnode;
 425         char *nm;
 426         int err;
 427 
 428         /*
 429          * If it's not a device or a PCI-express bus (which could potentially
 430          * represent a slot, and therefore we might need to capture its slot
 431          * name information), just inherit any label from our parent
 432          */
 433         *out = NULL;
 434         nm = topo_node_name(node);
 435         if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
 436             strcmp(nm, PCIEX_BUS) != 0) {
 437                 if (topo_node_label_set(node, NULL, &err) < 0)
 438                         if (err != ETOPO_PROP_NOENT)
 439                                 return (topo_mod_seterrno(mod, err));
 440                 return (0);
 441         }
 442 
 443         if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) {
 444                 topo_mod_dprintf(mod,
 445                     "%s: label method argument not found.\n", __func__);
 446                 return (-1);
 447         }
 448         dp = (did_t *)(uintptr_t)ptr;
 449         pnode = did_gettnode(dp);
 450         pdp = did_find(mod, topo_node_getspecific(pnode));
 451 
 452         /*
 453          * Is there a slot label associated with the device?
 454          */
 455         if ((l = pci_slot_label_lookup(mod, node, dp, pdp)) != NULL) {
 456                 nvlist_t *rnvl;
 457 
 458                 if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 ||
 459                     nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0)
 460                         return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
 461                 *out = rnvl;
 462                 return (0);
 463         } else {
 464                 if (topo_node_label_set(node, NULL, &err) < 0)
 465                         if (err != ETOPO_PROP_NOENT)
 466                                 return (topo_mod_seterrno(mod, err));
 467                 return (0);
 468         }
 469 }
 470 
 471 int
 472 pci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
 473 {
 474         int err = 0;
 475         uint64_t ptr;
 476         did_t *dp, *pdp;
 477         tnode_t *pnode;
 478         char *nm;
 479 
 480         *out = NULL;
 481         nm = topo_node_name(node);
 482         if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
 483             strcmp(nm, PCIEX_BUS) != 0)
 484                 return (0);
 485 
 486         if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) {
 487                 topo_mod_dprintf(mod,
 488                     "%s: label method argument not found.\n", __func__);
 489                 return (-1);
 490         }
 491         dp = (did_t *)(uintptr_t)ptr;
 492         pnode = did_gettnode(dp);
 493         pdp = did_find(mod, topo_node_getspecific(pnode));
 494 
 495         /*
 496          * Is there a slot label associated with the device?
 497          */
 498         if (pci_slot_label_lookup(mod, pnode, dp, pdp) != NULL) {
 499                 nvlist_t *rnvl;
 500 
 501                 if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) {
 502                         topo_mod_dprintf(mod, "%s: error: %s\n",
 503                             __func__, topo_strerror(topo_mod_errno(mod)));
 504                         return (topo_mod_seterrno(mod, err));
 505                 }
 506                 *out = rnvl;
 507         }
 508         return (0);
 509 }