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
platform_pci_label(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)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
platform_pci_fru(topo_mod_t * mod,tnode_t * node,nvlist_t * in,nvlist_t ** out)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
platform_pci_fru_location(topo_mod_t * mod,tnode_t * node,uchar_t * loc,int locsiz)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
platform_pci_fru_cb(topo_mod_t * mod,tnode_t * node,void * private)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