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 *
pci_label_physlot_lookup(topo_mod_t * mod,char * platform,did_t * dp)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 *
pci_label_slotname_lookup(topo_mod_t * mod,char * platform,const char * label,did_t * dp)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 *
pci_label_missing_lookup(topo_mod_t * mod,char * platform,did_t * dp)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 *
pci_slot_label_lookup(topo_mod_t * mod,tnode_t * node,did_t * dp,did_t * pdp)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
pci_label_cmn(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)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
pci_fru_cmn(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)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