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