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