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