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 } 510