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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <libnvpair.h>
30 #include <fm/topo_mod.h>
31 #include <assert.h>
32 #include <string.h>
33 #include <sys/fm/protocol.h>
34 
35 #include <did.h>
36 #include <pcibus.h>
37 #include <pcibus_labels.h>
38 
39 extern slotnm_rewrite_t *Slot_Rewrites;
40 extern physlot_names_t *Physlot_Names;
41 extern missing_names_t *Missing_Names;
42 
43 static const char *
44 pci_physslot_name_lookup(char *platform, did_t *dp)
45 {
46 	const char *rlabel = NULL;
47 	int n, p, i;
48 
49 	if ((n = did_physslot(dp)) < 0 || Physlot_Names == NULL ||
50 	    platform == NULL)
51 		return (NULL);
52 
53 	for (p = 0; p < Physlot_Names->psn_nplats; p++) {
54 		if (strcmp(Physlot_Names->psn_names[p].pnm_platform,
55 		    platform) != 0)
56 			continue;
57 		for (i = 0; i < Physlot_Names->psn_names[p].pnm_nnames; i++) {
58 			physnm_t ps;
59 			ps = Physlot_Names->psn_names[p].pnm_names[i];
60 			if (ps.ps_num == n) {
61 				rlabel = ps.ps_label;
62 				break;
63 			}
64 		}
65 		break;
66 	}
67 	return (rlabel);
68 }
69 
70 static const char *
71 pci_slotname_rewrite(char *platform, const char *label)
72 {
73 	const char *rlabel = label;
74 	int s, i;
75 
76 	if (Slot_Rewrites == NULL)
77 		return (rlabel);
78 
79 	for (s = 0; s < Slot_Rewrites->srw_nplats; s++) {
80 		if (strcmp(Slot_Rewrites->srw_platrewrites[s].prw_platform,
81 		    platform) != 0)
82 			continue;
83 		for (i = 0;
84 		    i < Slot_Rewrites->srw_platrewrites[s].prw_nrewrites;
85 		    i++) {
86 			slot_rwd_t rw;
87 			rw = Slot_Rewrites->srw_platrewrites[s].prw_rewrites[i];
88 			if (strcmp(rw.srw_obp, label) == 0) {
89 				rlabel = rw.srw_new;
90 				break;
91 			}
92 		}
93 		break;
94 	}
95 	assert(rlabel != NULL);
96 	return (rlabel);
97 }
98 
99 static const char *
100 pci_missing_match(topo_mod_t *mod, char *platform, did_t *dp)
101 {
102 	const char *rlabel = NULL;
103 	int board, bridge, rc, bus, dev;
104 	int p, i;
105 
106 	if (Missing_Names == NULL)
107 		return (NULL);
108 	bridge = did_bridge(dp);
109 	board = did_board(dp);
110 	rc = did_rc(dp);
111 	did_BDF(dp, &bus, &dev, NULL);
112 
113 	topo_mod_dprintf(mod, "Missing a name for %d, %d, %d, %d, %d ?\n",
114 	    board, bridge, rc, bus, dev);
115 
116 	for (p = 0; p < Missing_Names->mn_nplats; p++) {
117 		if (strcmp(Missing_Names->mn_names[p].pdl_platform,
118 		    platform) != 0)
119 			continue;
120 		for (i = 0; i < Missing_Names->mn_names[p].pdl_nnames; i++) {
121 			devlab_t m;
122 			m = Missing_Names->mn_names[p].pdl_names[i];
123 			if (m.dl_board == board && m.dl_bridge == bridge &&
124 			    m.dl_rc == rc && m.dl_bus == bus &&
125 			    m.dl_dev == dev) {
126 				rlabel = m.dl_label;
127 				break;
128 			}
129 		}
130 		break;
131 	}
132 	return (rlabel);
133 }
134 
135 const char *
136 pci_slotname_lookup(topo_mod_t *mod, tnode_t *node, did_t *dp, did_t *pdp)
137 {
138 	const char *l;
139 	char *plat, *pp;
140 	int err;
141 	int d;
142 
143 	if (topo_prop_get_string(node,
144 	    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT, &plat, &err) < 0) {
145 		(void) topo_mod_seterrno(mod, err);
146 		return (NULL);
147 	}
148 
149 	/*
150 	 * Trim SUNW, from the platform name
151 	 */
152 	pp = strchr(plat, ',');
153 	if (pp == NULL)
154 		pp = plat;
155 	else
156 		++pp;
157 
158 	did_BDF(dp, NULL, &d, NULL);
159 	if ((l = pci_physslot_name_lookup(pp, pdp)) == NULL)
160 		if ((l = did_label(pdp, d)) != NULL) {
161 			l = pci_slotname_rewrite(pp, l);
162 		} else {
163 			l = pci_missing_match(mod, pp, dp);
164 		}
165 	topo_mod_strfree(mod, plat);
166 	return (l);
167 }
168 
169 int
170 pci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
171 {
172 	uint64_t ptr;
173 	const char *l;
174 	did_t *dp, *pdp;
175 	tnode_t *pnode;
176 	char *nm;
177 	int err;
178 
179 	/*
180 	 * If it's not a device or a PCI-express bus (which could potentially
181 	 * represent a slot, and therefore we might need to capture its slot
182 	 * name information), just inherit any label from our parent
183 	 */
184 	*out = NULL;
185 	nm = topo_node_name(node);
186 	if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
187 	    strcmp(nm, PCIEX_BUS) != 0) {
188 		if (topo_node_label_set(node, NULL, &err) < 0)
189 			if (err != ETOPO_PROP_NOENT)
190 				return (topo_mod_seterrno(mod, err));
191 		return (0);
192 	}
193 
194 	if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) {
195 		topo_mod_dprintf(mod,
196 		    "label method argument not found.\n");
197 		return (-1);
198 	}
199 	dp = (did_t *)(uintptr_t)ptr;
200 	pnode = did_gettnode(dp);
201 	pdp = did_find(mod, topo_node_getspecific(pnode));
202 
203 	/*
204 	 * Is there a slotname associated with the device?
205 	 */
206 	if ((l = pci_slotname_lookup(mod, node, dp, pdp)) != NULL) {
207 		nvlist_t *rnvl;
208 
209 		if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 ||
210 		    nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0)
211 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
212 		*out = rnvl;
213 		return (0);
214 	} else {
215 		if (topo_node_label_set(node, NULL, &err) < 0)
216 			if (err != ETOPO_PROP_NOENT)
217 				return (topo_mod_seterrno(mod, err));
218 		return (0);
219 	}
220 }
221 
222 int
223 pci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
224 {
225 	int err = 0;
226 	uint64_t ptr;
227 	did_t *dp, *pdp;
228 	tnode_t *pnode;
229 	char *nm;
230 
231 	*out = NULL;
232 	nm = topo_node_name(node);
233 	if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
234 	    strcmp(nm, PCIEX_BUS) != 0)
235 		return (0);
236 
237 	if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) {
238 		topo_mod_dprintf(mod,
239 		    "label method argument not found.\n");
240 		return (-1);
241 	}
242 	dp = (did_t *)(uintptr_t)ptr;
243 	pnode = did_gettnode(dp);
244 	pdp = did_find(mod, topo_node_getspecific(pnode));
245 
246 	/*
247 	 * Is there a slotname associated with the device?
248 	 */
249 	if (pci_slotname_lookup(mod, pnode, dp, pdp) != NULL) {
250 		nvlist_t *rnvl;
251 
252 		if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) {
253 			topo_mod_dprintf(mod, "pci_fru_compute error: %s\n",
254 			    topo_strerror(topo_mod_errno(mod)));
255 			return (topo_mod_seterrno(mod, err));
256 		}
257 		*out = rnvl;
258 	}
259 	return (0);
260 }
261