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 * Copyright 2019 Joyent, Inc.
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 *
pci_label_physlot_lookup(topo_mod_t * mod,char * platform,did_t * dp)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 (strcasecmp(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 *
pci_label_slotname_lookup(topo_mod_t * mod,char * platform,const char * label,did_t * dp)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 (strcasecmp(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,
121 rw.srw_new ? rw.srw_new : NULL);
122 /*
123 * If a test function is specified then call
124 * it to do an additional check.
125 */
126 if (rw.srw_test != NULL) {
127 topo_mod_dprintf(mod,
128 "%s: calling test function=%p\n",
129 __func__, rw.srw_test);
130 if ((ret = rw.srw_test(mod, dp)) != 0)
131 rlabel = rw.srw_new;
132 topo_mod_dprintf(mod,
133 "%s: test function return=%d\n",
134 __func__, ret);
135 } else {
136 rlabel = rw.srw_new;
137 }
138 break;
139 }
140 }
141 break;
142 }
143 topo_mod_dprintf(mod, "%s: returning label=%s\n", __func__,
144 rlabel ? rlabel : "NULL");
145 return (rlabel);
146 }
147
148 /*
149 * Do a platform specific label lookup based on bus, dev, etc.
150 */
151 static const char *
pci_label_missing_lookup(topo_mod_t * mod,char * platform,did_t * dp)152 pci_label_missing_lookup(topo_mod_t *mod, char *platform, did_t *dp)
153 {
154 const char *rlabel = NULL;
155 int board, bridge, rc, bus, dev;
156 int p, i, ret;
157
158 if (Missing_Names == NULL || platform == NULL)
159 return (NULL);
160
161 bridge = did_bridge(dp);
162 board = did_board(dp);
163 rc = did_rc(dp);
164 did_BDF(dp, &bus, &dev, NULL);
165
166 topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s, "
167 "board=%d, bridge=%d, rc=%d, bus=%d, dev=%d\n",
168 __func__, platform, board, bridge, rc, bus, dev);
169
170 for (p = 0; p < Missing_Names->mn_nplats; p++) {
171 topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
172 __func__, Missing_Names->mn_names[p].pdl_platform);
173 if (strcasecmp(Missing_Names->mn_names[p].pdl_platform,
174 platform) != 0)
175 continue;
176 topo_mod_dprintf(mod, "%s: found lookup table for this "
177 "platform\n", __func__);
178 for (i = 0; i < Missing_Names->mn_names[p].pdl_nnames; i++) {
179 devlab_t m;
180 m = Missing_Names->mn_names[p].pdl_names[i];
181 if (m.dl_board == board && m.dl_bridge == bridge &&
182 m.dl_rc == rc &&
183 (m.dl_bus == -1 || m.dl_bus == bus) &&
184 (m.dl_dev == -1 || m.dl_dev == dev)) {
185 topo_mod_dprintf(mod, "%s: matched entry=%d, "
186 "label=%s\n", __func__, i, m.dl_label);
187 /*
188 * If a test function is specified then call
189 * it to do an additional test.
190 */
191 if (m.dl_test != NULL) {
192 topo_mod_dprintf(mod,
193 "%s: calling test function=%p\n",
194 __func__, m.dl_test);
195 if ((ret = m.dl_test(mod, dp)) != 0)
196 rlabel = m.dl_label;
197 topo_mod_dprintf(mod,
198 "%s: test function return=%d\n",
199 __func__, ret);
200 if (ret)
201 break;
202 } else {
203 rlabel = m.dl_label;
204 break;
205 }
206 }
207 }
208 break;
209 }
210 if (rlabel != NULL) {
211 topo_mod_dprintf(mod, "%s: match found, label=%s\n",
212 __func__, rlabel);
213 }
214 return (rlabel);
215 }
216
217 /*
218 * Do an overall slot label lookup for the device node.
219 */
220 char *
pci_slot_label_lookup(topo_mod_t * mod,tnode_t * node,did_t * dp,did_t * pdp)221 pci_slot_label_lookup(topo_mod_t *mod, tnode_t *node, did_t *dp, did_t *pdp)
222 {
223 tnode_t *anode, *apnode;
224 did_t *adp, *apdp;
225 char *plat, *pp, *l = NULL, *ancestor_l = NULL, *new_l = NULL;
226 int err, b, d, f, done = 0;
227 size_t len;
228
229 did_BDF(dp, &b, &d, &f);
230
231 topo_mod_dprintf(mod, "%s: entry: node=%p, node_name=%s, "
232 "node_inst=%" PRIu64 ", dp=%p, dp_bdf=%d/%d/%d, pdp=%p\n",
233 __func__, node, topo_node_name(node), topo_node_instance(node),
234 dp, b, d, f, pdp);
235
236 /*
237 * If this device has a physical slot number then check if
238 * an ancestor also has a slot label.
239 *
240 * If an ancestor has a slot label, then this node's label
241 * is generated by concatenating a default label onto the
242 * ancestor's label.
243 *
244 * We grab pairs of ancestors (parent and child) as we go up
245 * the tree because the parent is checked for the presence
246 * of a slot while the child contains the label.
247 *
248 * Note that this algorithm only applies to nodes which have
249 * a physical slot number. (i.e. PCIE devices or PCI/PCIX
250 * devices off of a PCIE to PCIX switch)
251 */
252 if (did_physlot(pdp) >= 0) {
253
254 topo_mod_dprintf(mod, "%s: node=%p: node has a physical "
255 "slot=%d, checking ancestors for slots\n",
256 __func__, node, did_physlot(pdp));
257
258 /*
259 * Get this device's physical slot name.
260 */
261 l = (char *)did_physlot_name(pdp, d);
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[%" PRIu64 "], "
296 "anode_bdf=%d/%d/%d\n", __func__, node,
297 topo_node_name(anode),
298 topo_node_instance(anode), b, d, f);
299 }
300 if ((apnode != NULL) && (apdp != NULL)) {
301 did_BDF(apdp, &b, &d, &f);
302 topo_mod_dprintf(mod, "%s: node=%p: "
303 "apnode_name=%s[%" PRIu64 "], "
304 "apnode_bdf=%d/%d/%d\n",
305 __func__, node, topo_node_name(apnode),
306 topo_node_instance(apnode), b, d, f);
307 }
308
309 /*
310 * If the ancestors do not exist or are not pci
311 * devices then we're done searching.
312 *
313 * Otherwise, if the ancestor has a physical slot,
314 * and it is a different slot than the one we
315 * started with then lookup the ancestor label,
316 * and we're done.
317 */
318 if ((anode == NULL) || (adp == NULL) ||
319 (apnode == NULL) || (apdp == NULL)) {
320 done++;
321 } else if (did_physlot_exists(apdp) &&
322 (apdp != pdp)) {
323 if (topo_node_label(anode, &ancestor_l,
324 &err) != 0) {
325 topo_mod_dprintf(mod,
326 "%s: node=%p: topo_node_label() "
327 "FAILED!", __func__, node);
328 (void) topo_mod_seterrno(mod, err);
329 return (NULL);
330 }
331 done++;
332 topo_mod_dprintf(mod, "%s: node=%p: found "
333 "ancestor with a slot, label=%s ",
334 __func__, node, ancestor_l);
335 }
336 }
337 if (ancestor_l == NULL) {
338 topo_mod_dprintf(mod, "%s: node=%p: no ancestor "
339 "slot found\n", __func__, node);
340 }
341 }
342
343 /*
344 * If we found an ancestor with a slot label, and this node has
345 * a physical slot number label then concatenate the two to form
346 * this node's label. Otherwise, do a full slot label lookup.
347 */
348 if (ancestor_l && l) {
349 topo_mod_dprintf(mod, "%s: node=%p: concatenating "
350 "ancestor_l=%s and l=%s\n",
351 __func__, node, ancestor_l, l);
352 len = strlen(ancestor_l) + strlen(l) + 2;
353 new_l = alloca(len);
354 (void) snprintf(new_l, len, "%s/%s", ancestor_l, l);
355 l = new_l;
356 } else {
357 /*
358 * Get platform name used for lookups.
359 */
360 if (topo_prop_get_string(node, FM_FMRI_AUTHORITY,
361 FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) {
362 (void) topo_mod_seterrno(mod, err);
363 return (NULL);
364 }
365 /*
366 * Trim SUNW, from the platform name
367 */
368 pp = strchr(plat, ',');
369 if (pp == NULL)
370 pp = plat;
371 else
372 ++pp;
373 /*
374 * Get device number used for lookup.
375 */
376 did_BDF(dp, NULL, &d, NULL);
377
378 /*
379 * The slot label is determined in the following order:
380 * - Platform specific lookup based on physical slot #.
381 * - Platform specific lookup based on default label string.
382 * - Platform specific lookup based on device number.
383 * - Default label.
384 * The default label is based on the slot names property
385 * if it exists, else it is a generic name derived from
386 * the slot #.
387 */
388 if ((l = (char *)pci_label_physlot_lookup(mod, pp, pdp))
389 == NULL) {
390 if ((l = (char *)did_physlot_name(dp, d)) != NULL) {
391 l = (char *)
392 pci_label_slotname_lookup(mod, pp, l, dp);
393 }
394 if (l == NULL) {
395 l = (char *)
396 pci_label_missing_lookup(mod, pp, dp);
397 }
398 }
399 topo_mod_strfree(mod, plat);
400 }
401
402 /*
403 * If we calculated a slot label, then save it in the
404 * node's data structure so we can free it later.
405 */
406 if (l) {
407 if (did_slot_label_get(dp) != NULL)
408 topo_mod_strfree(mod, did_slot_label_get(dp));
409 l = topo_mod_strdup(mod, l);
410 did_slot_label_set(dp, l);
411 }
412
413 topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n",
414 __func__, node, (l ? l : "NULL"));
415
416 return (l);
417 }
418
419 int
pci_label_cmn(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)420 pci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
421 {
422 uint64_t ptr;
423 char *l;
424 did_t *dp, *pdp;
425 tnode_t *pnode;
426 char *nm;
427 int err;
428
429 /*
430 * If it's not a device or a PCI-express bus (which could potentially
431 * represent a slot, and therefore we might need to capture its slot
432 * name information), just inherit any label from our parent
433 */
434 *out = NULL;
435 nm = topo_node_name(node);
436 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
437 strcmp(nm, PCIEX_BUS) != 0) {
438 if (topo_node_label_set(node, NULL, &err) < 0)
439 if (err != ETOPO_PROP_NOENT)
440 return (topo_mod_seterrno(mod, err));
441 return (0);
442 }
443
444 if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) {
445 topo_mod_dprintf(mod,
446 "%s: label method argument not found.\n", __func__);
447 return (-1);
448 }
449 dp = (did_t *)(uintptr_t)ptr;
450 pnode = did_gettnode(dp);
451 pdp = did_find(mod, topo_node_getspecific(pnode));
452
453 /*
454 * Is there a slot label associated with the device?
455 */
456 if ((l = pci_slot_label_lookup(mod, node, dp, pdp)) != NULL) {
457 nvlist_t *rnvl;
458
459 if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 ||
460 nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0)
461 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
462 *out = rnvl;
463 return (0);
464 } else {
465 if (topo_node_label_set(node, NULL, &err) < 0)
466 if (err != ETOPO_PROP_NOENT)
467 return (topo_mod_seterrno(mod, err));
468 return (0);
469 }
470 }
471
472 int
pci_fru_cmn(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)473 pci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
474 {
475 int err = 0;
476 uint64_t ptr;
477 did_t *dp, *pdp;
478 tnode_t *pnode;
479 char *nm;
480
481 *out = NULL;
482 nm = topo_node_name(node);
483 if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
484 strcmp(nm, PCIEX_BUS) != 0)
485 return (0);
486
487 if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) {
488 topo_mod_dprintf(mod,
489 "%s: label method argument not found.\n", __func__);
490 return (-1);
491 }
492 dp = (did_t *)(uintptr_t)ptr;
493 pnode = did_gettnode(dp);
494 pdp = did_find(mod, topo_node_getspecific(pnode));
495
496 /*
497 * Is there a slot label associated with the device?
498 */
499 if (pci_slot_label_lookup(mod, pnode, dp, pdp) != NULL) {
500 nvlist_t *rnvl;
501
502 if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) {
503 topo_mod_dprintf(mod, "%s: error: %s\n",
504 __func__, topo_strerror(topo_mod_errno(mod)));
505 return (topo_mod_seterrno(mod, err));
506 }
507 *out = rnvl;
508 }
509 return (0);
510 }
511