xref: /titanic_52/usr/src/lib/fm/topo/modules/sun4v/pcibus/pci_sun4v.c (revision 186d582bd9dbcd38e0aeea49036d47d3426a3536)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <fm/topo_mod.h>
28 #include <sys/fm/protocol.h>
29 #include <string.h>
30 #include <alloca.h>
31 #include <libdevinfo.h>
32 #include <did_props.h>
33 
34 /*
35  * Including the following file gives us definitions of the three
36  * global arrays used to adjust labels, Slot_Rewrites, Physlot_Names,
37  * and Missing_Names.  With those defined we can use the common labeling
38  * routines for pci.
39  */
40 #include "pci_sun4v.h"
41 #include "pci_sun4.h"
42 
43 #define	PI_PROP_CHASSIS_LOCATION_NAME		"chassis-location-name"
44 
45 typedef struct _pci_fru {
46 	tnode_t	*node;
47 	char	*location;
48 	int	locsiz;
49 } _pci_fru_t;
50 
51 
52 static int platform_pci_fru_location(topo_mod_t *, tnode_t *, uchar_t *, int);
53 static int platform_pci_fru_cb(topo_mod_t *, tnode_t *, void *);
54 
55 
56 int
57 platform_pci_label(topo_mod_t *mod, tnode_t *node, nvlist_t *in,
58     nvlist_t **out)
59 {
60 	int	result;
61 	int	err;
62 	int	locsiz = 0;
63 	uchar_t	*loc = NULL;
64 	char	*nac = NULL;
65 
66 	topo_mod_dprintf(mod, "entering platform_pci_label\n");
67 
68 	*out = NULL;
69 	result = di_bytes_get(mod, topo_node_getspecific(node),
70 	    PI_PROP_CHASSIS_LOCATION_NAME, &locsiz, &loc);
71 	if (result == -1 || locsiz < 0) {
72 		topo_mod_dprintf(mod, "platform_pci_label: %s not found (%s)\n",
73 		    PI_PROP_CHASSIS_LOCATION_NAME, strerror(errno));
74 
75 		/* Invoke the generic label generator for this node */
76 		return (pci_label_cmn(mod, node, in, out));
77 	}
78 
79 	/*
80 	 * We have crossed a FRU boundary.  Use the value in the
81 	 * chassis-location-name property as the node label.
82 	 */
83 	nac = alloca(locsiz+1);
84 	(void) memset(nac, 0, locsiz+1);
85 	(void) memcpy(nac, loc, locsiz);
86 	result = topo_node_label_set(node, nac, &err);
87 	if (result < 0) {
88 		if (err != ETOPO_PROP_NOENT) {
89 			return (topo_mod_seterrno(mod, err));
90 		}
91 	}
92 
93 	return (0);
94 }
95 
96 
97 int
98 platform_pci_fru(topo_mod_t *mod, tnode_t *node, nvlist_t *in,
99     nvlist_t **out)
100 {
101 	int err = 0;
102 	uint64_t ptr;
103 	did_t *dp, *pdp;
104 	tnode_t *pnode;
105 	char *nm, *plat, *pp, **cp;
106 	char *label;
107 	int found_t1plat = 0;
108 	uchar_t *loc;
109 	int locsiz;
110 
111 	topo_mod_dprintf(mod, "entering platform_pci_fru\n");
112 
113 	if (topo_prop_get_string(node, FM_FMRI_AUTHORITY,
114 	    FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) {
115 		(void) topo_mod_seterrno(mod, err);
116 		return (-1);
117 	}
118 	/* Delete the "SUNW," */
119 	pp = strchr(plat, ',');
120 	if (pp == NULL)
121 		pp = plat;
122 	else
123 		++pp;
124 
125 	/* Is this an UltraSPARC-T1 platform? */
126 	cp = usT1_plats;
127 	while ((*cp != NULL) && (found_t1plat == 0)) {
128 		if (strcmp(pp, *cp) == 0)
129 			found_t1plat = 1;
130 		cp++;
131 	}
132 
133 	topo_mod_strfree(mod, plat);
134 
135 	/*
136 	 * On UltraSPARC-T1 systems, use the legacy hc scheme on
137 	 * the adapter slots to ensure ALOM on the SP can interpret
138 	 * the FRU correctly. For everything else, follow the normal
139 	 * code flow
140 	 */
141 	if (found_t1plat) {
142 		*out = NULL;
143 		nm = topo_node_name(node);
144 		if (strcmp(nm, PCI_DEVICE) != 0 &&
145 		    strcmp(nm, PCIEX_DEVICE) != 0 &&
146 		    strcmp(nm, PCIEX_BUS) != 0)
147 			return (0);
148 
149 		if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) {
150 			topo_mod_dprintf(mod, "label method argument "
151 			    "not found.\n");
152 			return (-1);
153 		}
154 		dp = (did_t *)(uintptr_t)ptr;
155 		pnode = did_gettnode(dp);
156 		pdp = did_find(mod, topo_node_getspecific(pnode));
157 
158 		/*
159 		 * Is there a slotname associated with the device?
160 		 */
161 		if ((label = pci_slot_label_lookup(mod, pnode, dp, pdp))
162 		    != NULL) {
163 			nvlist_t *rnvl;
164 			char buf[PATH_MAX];
165 
166 			(void) snprintf(buf, PATH_MAX, "hc:///component=%s",
167 			    label);
168 			if (topo_mod_str2nvl(mod, buf, &rnvl) < 0)
169 				return (-1);
170 			*out = rnvl;
171 		}
172 		return (0);
173 	} else if (di_bytes_get(mod, topo_node_getspecific(node),
174 	    PI_PROP_CHASSIS_LOCATION_NAME, &locsiz, &loc) == 0 && locsiz > 0) {
175 		/*
176 		 * We have crossed a FRU boundary and need to find the parent
177 		 * node with this location and set our FMRI to that value.
178 		 */
179 		return (platform_pci_fru_location(mod, node, loc, locsiz));
180 	} else {
181 		return (pci_fru_compute(mod, node, in, out));
182 	}
183 }
184 
185 
186 static int
187 platform_pci_fru_location(topo_mod_t *mod, tnode_t *node, uchar_t *loc,
188     int locsiz)
189 {
190 	int		err;
191 	tnode_t		*parent;
192 	tnode_t		*top;
193 	topo_walk_t	*wp;
194 	_pci_fru_t	walkdata;
195 
196 	topo_mod_dprintf(mod, "entering platform_pci_fru_location\n");
197 
198 	/* Find the root node */
199 	top = node;
200 	while ((parent = topo_node_parent(top)) != NULL) {
201 		top = parent;
202 	}
203 	walkdata.node = node;
204 	walkdata.locsiz = locsiz;
205 	walkdata.location = alloca(locsiz+1);
206 	(void) memset(walkdata.location, 0, locsiz+1);
207 	(void) memcpy(walkdata.location, loc, locsiz);
208 
209 	/* Create a walker starting at the root node */
210 	wp = topo_mod_walk_init(mod, top, platform_pci_fru_cb, &walkdata, &err);
211 	if (wp == NULL) {
212 		return (topo_mod_seterrno(mod, err));
213 	}
214 
215 	/*
216 	 * Walk the tree breadth first to hopefully avoid visiting too many
217 	 * nodes while searching for the node with the appropriate FMRI.
218 	 */
219 	(void) topo_walk_step(wp, TOPO_WALK_SIBLING);
220 	topo_walk_fini(wp);
221 
222 	return (0);
223 }
224 
225 
226 static int
227 platform_pci_fru_cb(topo_mod_t *mod, tnode_t *node, void *private)
228 {
229 	int		err;
230 	_pci_fru_t	*walkdata = (_pci_fru_t *)private;
231 	nvlist_t	*fmri;
232 	char		*location;
233 	int 		result, rc;
234 
235 	if (node == walkdata->node) {
236 		/* This is the starting node.  Do not check the location */
237 		return (TOPO_WALK_NEXT);
238 	}
239 
240 	if (topo_node_label(node, &location, &err) != 0) {
241 		/* This node has no location property.  Continue the walk */
242 		return (TOPO_WALK_NEXT);
243 	}
244 
245 	result = TOPO_WALK_NEXT;
246 	if (strncmp(location, walkdata->location, walkdata->locsiz) == 0) {
247 		/*
248 		 * We have a match.  Set the node's FRU FMRI to this nodes
249 		 * FRU FMRI
250 		 */
251 		rc = topo_node_fru(node, &fmri, NULL, &err);
252 		if (rc == 0) {
253 			rc = topo_node_fru_set(walkdata->node, fmri, 0, &err);
254 			nvlist_free(fmri);
255 		}
256 		if (rc != 0) {
257 			result = TOPO_WALK_TERMINATE;
258 			(void) topo_mod_seterrno(mod, err);
259 		}
260 	}
261 	topo_mod_strfree(mod, location);
262 	return (result);
263 }
264