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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <fm/fmd_api.h>
29 #include <fm/libtopo.h>
30 #include <sys/fm/protocol.h>
31 #include <cmd.h>
32 #include <string.h>
33 #include <cmd_hc_sun4v.h>
34 
35 nvlist_t *mb_nvl;
36 
37 nvlist_t *
38 cmd_fault_add_location(fmd_hdl_t *hdl, nvlist_t *flt, const char *locstr) {
39 
40 	char *t, *s;
41 
42 	if (nvlist_lookup_string(flt, FM_FAULT_LOCATION, &t) == 0)
43 		return (flt); /* already has location value */
44 
45 	/* Replace occurrence of ": " with "/" to avoid confusing ILOM. */
46 	t = fmd_hdl_zalloc(hdl, strlen(locstr) + 1, FMD_SLEEP);
47 	s = strstr(locstr, ": ");
48 	if (s != NULL) {
49 		(void) strncpy(t, locstr, s - locstr);
50 		(void) strcat(t, "/");
51 		(void) strcat(t, s + 2);
52 	} else {
53 		(void) strcpy(t, locstr);
54 	}
55 
56 	/* Also, remove any J number from end of this string. */
57 	s = strstr(t, "/J");
58 	if (s != NULL)
59 		*s = '\0';
60 
61 	if (nvlist_add_string(flt, FM_FAULT_LOCATION, t) != 0)
62 		fmd_hdl_error(hdl, "unable to alloc location for fault\n");
63 	fmd_hdl_free(hdl, t, strlen(locstr) + 1);
64 	return (flt);
65 }
66 
67 typedef struct tr_ent {
68 	const char *nac_component;
69 	const char *hc_component;
70 } tr_ent_t;
71 
72 static tr_ent_t tr_tbl[] = {
73 	{ "MB",		"motherboard" },
74 	{ "CPU",	"cpuboard" },
75 	{ "MEM",	"memboard" },
76 	{ "CMP",	"chip" },
77 	{ "BR",		"branch" },
78 	{ "CH",		"dram-channel" },
79 	{ "R",		"rank" },
80 	{ "D",		"dimm" }
81 };
82 
83 #define	tr_tbl_n	sizeof (tr_tbl) / sizeof (tr_ent_t)
84 
85 int
86 map_name(const char *p) {
87 	int i;
88 
89 	for (i = 0; i < tr_tbl_n; i++) {
90 		if (strncmp(p, tr_tbl[i].nac_component,
91 		    strlen(tr_tbl[i].nac_component)) == 0)
92 			return (i);
93 	}
94 	return (-1);
95 }
96 
97 int
98 cmd_count_components(const char *str, char sep)
99 {
100 	int num = 0;
101 	const char *cptr = str;
102 
103 	if (*cptr == sep) cptr++;		/* skip initial sep */
104 	if (strlen(cptr) > 0) num = 1;
105 	while ((cptr = strchr(cptr, sep)) != NULL) {
106 		cptr++;
107 		if (cptr == NULL || strcmp(cptr, "") == 0) break;
108 		if (map_name(cptr) >= 0) num++;
109 	}
110 	return (num);
111 }
112 
113 /*
114  * This version of breakup_components assumes that all component names which
115  * it sees are of the form:  <nonnumeric piece><numeric piece>
116  * i.e. no embedded numerals in component name which have to be spelled out.
117  */
118 
119 int
120 cmd_breakup_components(char *str, char *sep, nvlist_t **hc_nvl)
121 {
122 	char namebuf[64], instbuf[64];
123 	char *token, *tokbuf;
124 	int i, j, namelen, instlen;
125 
126 	i = 0;
127 	for (token = strtok_r(str, sep, &tokbuf);
128 	    token != NULL;
129 	    token = strtok_r(NULL, sep, &tokbuf)) {
130 		namelen = strcspn(token, "0123456789");
131 		instlen = strspn(token+namelen, "0123456789");
132 		(void) strncpy(namebuf, token, namelen);
133 		namebuf[namelen] = '\0';
134 
135 		if ((j = map_name(namebuf)) < 0)
136 			continue; /* skip names that don't map */
137 
138 		if (instlen == 0) {
139 			(void) strncpy(instbuf, "0", 2);
140 		} else {
141 			(void) strncpy(instbuf, token+namelen, instlen);
142 			instbuf[instlen] = '\0';
143 		}
144 		if (nvlist_add_string(hc_nvl[i], FM_FMRI_HC_NAME,
145 		    tr_tbl[j].hc_component) != 0 ||
146 		    nvlist_add_string(hc_nvl[i], FM_FMRI_HC_ID, instbuf) != 0)
147 			return (-1);
148 		i++;
149 	}
150 	return (1);
151 }
152 
153 char *
154 cmd_getfru_loc(fmd_hdl_t *hdl, nvlist_t *asru) {
155 
156 	char *fru_loc, *cpufru;
157 	if (nvlist_lookup_string(asru, FM_FMRI_CPU_CPUFRU, &cpufru) == 0) {
158 		fru_loc = strstr(cpufru, "MB");
159 		if (fru_loc != NULL) {
160 			fmd_hdl_debug(hdl, "cmd_getfru_loc: fruloc=%s\n",
161 			    fru_loc);
162 			return (fmd_hdl_strdup(hdl, fru_loc, FMD_SLEEP));
163 		}
164 	}
165 	fmd_hdl_debug(hdl, "cmd_getfru_loc: Default fruloc=empty string\n");
166 	return (fmd_hdl_strdup(hdl, EMPTY_STR, FMD_SLEEP));
167 }
168 
169 nvlist_t *
170 cmd_mkboard_fru(fmd_hdl_t *hdl, char *frustr, char *serialstr, char *partstr) {
171 
172 	char *nac, *nac_name;
173 	int n, i, len;
174 	nvlist_t *fru, **hc_list;
175 
176 	if (frustr == NULL)
177 		return (NULL);
178 
179 	if ((nac_name = strstr(frustr, "MB")) == NULL)
180 		return (NULL);
181 
182 	len = strlen(nac_name) + 1;
183 
184 	nac = fmd_hdl_zalloc(hdl, len, FMD_SLEEP);
185 	(void) strcpy(nac, nac_name);
186 
187 	n = cmd_count_components(nac, '/');
188 
189 	fmd_hdl_debug(hdl, "cmd_mkboard_fru: nac=%s components=%d\n", nac, n);
190 
191 	hc_list = fmd_hdl_zalloc(hdl, sizeof (nvlist_t *)*n, FMD_SLEEP);
192 
193 	for (i = 0; i < n; i++) {
194 		(void) nvlist_alloc(&hc_list[i],
195 		    NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE, 0);
196 	}
197 
198 	if (cmd_breakup_components(nac, "/", hc_list) < 0) {
199 		for (i = 0; i < n; i++) {
200 			if (hc_list[i] != NULL)
201 			    nvlist_free(hc_list[i]);
202 		}
203 		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
204 		fmd_hdl_free(hdl, nac, len);
205 		return (NULL);
206 	}
207 
208 	if (nvlist_alloc(&fru, NV_UNIQUE_NAME, 0) != 0) {
209 		for (i = 0; i < n; i++) {
210 			if (hc_list[i] != NULL)
211 			    nvlist_free(hc_list[i]);
212 		}
213 		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
214 		fmd_hdl_free(hdl, nac, len);
215 		return (NULL);
216 	}
217 
218 	if (nvlist_add_uint8(fru, FM_VERSION, FM_HC_SCHEME_VERSION) != 0 ||
219 	    nvlist_add_string(fru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC) != 0 ||
220 	    nvlist_add_string(fru, FM_FMRI_HC_ROOT, "") != 0 ||
221 	    nvlist_add_uint32(fru, FM_FMRI_HC_LIST_SZ, n) != 0 ||
222 	    nvlist_add_nvlist_array(fru, FM_FMRI_HC_LIST, hc_list, n) != 0) {
223 		for (i = 0; i < n; i++) {
224 			if (hc_list[i] != NULL)
225 			    nvlist_free(hc_list[i]);
226 		}
227 		fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
228 		fmd_hdl_free(hdl, nac, len);
229 		nvlist_free(fru);
230 		return (NULL);
231 	}
232 
233 	for (i = 0; i < n; i++) {
234 		if (hc_list[i] != NULL)
235 		    nvlist_free(hc_list[i]);
236 	}
237 	fmd_hdl_free(hdl, hc_list, sizeof (nvlist_t *)*n);
238 	fmd_hdl_free(hdl, nac, len);
239 
240 	if ((serialstr != NULL &&
241 	    nvlist_add_string(fru, FM_FMRI_HC_SERIAL_ID, serialstr) != 0) ||
242 	    (partstr != NULL &&
243 	    nvlist_add_string(fru, FM_FMRI_HC_PART, partstr) != 0)) {
244 		nvlist_free(fru);
245 		return (NULL);
246 	}
247 
248 	return (fru);
249 }
250 
251 nvlist_t *
252 cmd_boardfru_create_fault(fmd_hdl_t *hdl, nvlist_t *asru, const char *fltnm,
253     uint_t cert, char *loc)
254 {
255 	nvlist_t *flt, *nvlfru;
256 	char *serialstr, *partstr;
257 
258 	if ((loc == NULL) || (strcmp(loc, EMPTY_STR) == 0))
259 		return (NULL);
260 
261 	if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serialstr) != 0)
262 		serialstr = NULL;
263 	if (nvlist_lookup_string(asru, FM_FMRI_HC_PART, &partstr) != 0)
264 		partstr = NULL;
265 
266 	nvlfru = cmd_mkboard_fru(hdl, loc, serialstr, partstr);
267 	if (nvlfru == NULL)
268 		return (NULL);
269 
270 	flt = cmd_nvl_create_fault(hdl, fltnm, cert, nvlfru, nvlfru, NULL);
271 	flt = cmd_fault_add_location(hdl, flt, loc);
272 	if (nvlfru != NULL)
273 		nvlist_free(nvlfru);
274 	return (flt);
275 }
276 
277 /* find_mb -- find hardware platform motherboard within libtopo */
278 
279 /* ARGSUSED */
280 static int
281 find_mb(topo_hdl_t *thp, tnode_t *node, void *arg)
282 {
283 	int err;
284 	nvlist_t *rsrc, **hcl;
285 	char *name;
286 	uint_t n;
287 
288 	if (topo_node_resource(node, &rsrc, &err) < 0) {
289 		return (TOPO_WALK_NEXT);	/* no resource, try next */
290 	}
291 
292 	if (nvlist_lookup_nvlist_array(rsrc, FM_FMRI_HC_LIST, &hcl, &n) < 0) {
293 		nvlist_free(rsrc);
294 		return (TOPO_WALK_NEXT);
295 	}
296 
297 	if (nvlist_lookup_string(hcl[0], FM_FMRI_HC_NAME, &name) != 0) {
298 		nvlist_free(rsrc);
299 		return (TOPO_WALK_NEXT);
300 	}
301 
302 	if (strcmp(name, "motherboard") != 0) {
303 		nvlist_free(rsrc);
304 		return (TOPO_WALK_NEXT); /* not MB hc list, try next */
305 	}
306 
307 	(void) nvlist_dup(rsrc, &mb_nvl, NV_UNIQUE_NAME);
308 
309 	nvlist_free(rsrc);
310 	return (TOPO_WALK_TERMINATE);	/* if no space, give up */
311 }
312 
313 /* init_mb -- read hardware platform motherboard from libtopo */
314 
315 nvlist_t *
316 init_mb(fmd_hdl_t *hdl)
317 {
318 	topo_hdl_t *thp;
319 	topo_walk_t *twp;
320 	int err;
321 
322 	if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
323 		return (NULL);
324 	if ((twp = topo_walk_init(thp,
325 	    FM_FMRI_SCHEME_HC, find_mb, NULL, &err))
326 	    == NULL) {
327 		fmd_hdl_topo_rele(hdl, thp);
328 		return (NULL);
329 	}
330 	(void) topo_walk_step(twp, TOPO_WALK_CHILD);
331 	topo_walk_fini(twp);
332 	fmd_hdl_topo_rele(hdl, thp);
333 	return (mb_nvl);
334 }
335