xref: /illumos-gate/usr/src/cmd/fm/schemes/mem/mem_unum.c (revision 65a89a64c60f3061bbe2381edaacc81660af9a95)
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 2006 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 <mem.h>
30 #include <fm/fmd_fmri.h>
31 #include <fm/libtopo.h>
32 
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 
37 /*
38  * Given a DIMM or bank unum, mem_unum_burst will break it apart into individual
39  * DIMM names.  If it's a DIMM, one name will be returned.  If it's a bank, the
40  * unums for the individual DIMMs will be returned.
41  *
42  * Plain J-number DIMM and bank unums are simple.  J DIMMs have one J number.  J
43  * banks have multiple whitespace-separated J numbers.
44  *
45  * The others are more complex, and consist of a common portion c, a colon, and
46  * a DIMM-specific portion d.  DIMMs are of the form "c: d", while banks are of
47  * the form "c: d d ...".  The patterns are designed to handle the complex case,
48  * but also handle the simple ones as an afterthought.  bd_pat is used to
49  * match specific styles of unum.  In bd_pat, the first %n indicates the end of
50  * the common portion ("c" above).  The second %n marks the beginning of the
51  * repetitive portion ("d" above).  The third %n is used to determine whether or
52  * not the entire pattern matched.  bd_reppat is used to match instances of the
53  * repetitive part.
54  *
55  * sscanf is your disturbingly powerful friend.
56  *
57  * The "bd_subst" element of the bank_dimm structure was added for Ontario
58  * in order to accommodate its bank string names.  Previously, to convert
59  * from a bank representation <common piece> <dimm1> <dimm2> ...
60  * we concatenated the common piece with each dimm-specific piece in turn,
61  * possibly deleting some characters in between.  Ontario is the first
62  * platform which requires that characters be substituted (like a vi s/1/2/)
63  * in place of characters deleted.  "bd_subst" represents the character(s)
64  * to be substituted between the common piece and each dimm-specific piece
65  * as part of the bursting.  For prior platforms, this value is skipped.
66  *
67  * Example:
68  * input: "MB/CMP0/CH3: R1/D0/J1901 R1/D1/J2001"
69  * outputs: "MB/CMP0/CH3/R1/D0/J1901", "MB/CMP0/CH3/R1/D1/J2001"
70  */
71 
72 typedef struct bank_dimm {
73 	const char *bd_pat;
74 	const char *bd_reppat;
75 	const char *bd_subst;
76 } bank_dimm_t;
77 
78 static const bank_dimm_t bank_dimm[] = {
79 	{ "%n%nJ%*4d%n",			" J%*4d%n" },
80 	{ "MB/P%*d/%nB%*d:%n%n",		" B%*d/D%*d%n" },
81 	{ "MB/P%*d/%nB%*d/D%*d:%n%n",		" B%*d/D%*d%n" },
82 	{ "C%*d/P%*d/%nB%*d:%n%n",		" B%*d/D%*d%n" },
83 	{ "C%*d/P%*d/%nB%*d/D%*d:%n%n",		" B%*d/D%*d%n" },
84 	{ "Slot %*c: %n%nJ%*4d%n",		" J%*4d%n" },
85 	{ "%n%nDIMM%*d%n",			" DIMM%*d%n" },
86 	{ "MB/%nDIMM%*d MB/DIMM%*d: %n%n",	" DIMM%*d%n" },
87 	{ "MB/%nDIMM%*d:%n%n",			" DIMM%*d%n" },
88 	{ "MB/CMP%*d/CH%*d%n:%n%n",		" R%*d/D%*d/J%*4d%n",	"/" },
89 	{ "MB/CMP%*d/CH%*d%n%n%n",		"/R%*d/D%*d/J%*4d%n" },
90 	{ "MB/C%*d/P%*d/%nB%*d:%n%n",		" B%*d/D%*d%n" },
91 	{ "MB/C%*d/P%*d/%nB%*d/D%*d:%n%n",	" B%*d/D%*d%n" },
92 	{ NULL }
93 };
94 
95 /*
96  * Burst Serengeti and Starcat-style unums.
97  * A DIMM unum string is expected to be in this form:
98  * "[/N0/]SB12/P0/B0/D2 [J13500]"
99  * A bank unum string is expected to be in this form:
100  * "[/N0/]SB12/P0/B0 [J13500, ...]"
101  */
102 static int
103 mem_unum_burst_sgsc(const char *pat, char ***dimmsp, size_t *ndimmsp)
104 {
105 	char buf[64];
106 	char **dimms;
107 	char *base;
108 	const char *c;
109 	char *copy;
110 	size_t copysz;
111 	int i;
112 
113 	/*
114 	 * No expansion is required for a DIMM unum
115 	 */
116 	if (strchr(pat, 'D') != NULL) {
117 		dimms = fmd_fmri_alloc(sizeof (char *));
118 		dimms[0] = fmd_fmri_strdup(pat);
119 		*dimmsp = dimms;
120 		*ndimmsp = 1;
121 		return (0);
122 	}
123 
124 	/*
125 	 * strtok is destructive so we need to work with
126 	 * a copy and keep track of the size allocated.
127 	 */
128 	copysz = strlen(pat) + 1;
129 	copy = fmd_fmri_alloc(copysz);
130 	(void) strcpy(copy, pat);
131 
132 	base = strtok(copy, " ");
133 
134 	/* There are four DIMMs in a bank */
135 	dimms = fmd_fmri_alloc(sizeof (char *) * 4);
136 
137 	for (i = 0; i < 4; i++) {
138 		(void) snprintf(buf, sizeof (buf), "%s/D%d", base, i);
139 
140 		if ((c = strtok(NULL, " ")) != NULL)
141 			(void) snprintf(buf, sizeof (buf), "%s %s", buf, c);
142 
143 		dimms[i] = fmd_fmri_strdup(buf);
144 	}
145 
146 	fmd_fmri_free(copy, copysz);
147 
148 	*dimmsp = dimms;
149 	*ndimmsp = 4;
150 	return (0);
151 }
152 
153 
154 /*
155  * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1
156  * otherwise.
157  */
158 static int
159 mem_unum_burst_pattern(const char *pat, char ***dimmsp, size_t *ndimmsp)
160 {
161 	const bank_dimm_t *bd;
162 	char **dimms = NULL, **newdimms;
163 	size_t ndimms = 0;
164 	const char *c;
165 
166 
167 	for (bd = bank_dimm; bd->bd_pat != NULL; bd++) {
168 		int replace, start, matched;
169 		char dimmname[64];
170 
171 		replace = start = matched = -1;
172 		(void) sscanf(pat, bd->bd_pat, &replace, &start, &matched);
173 		if (matched == -1)
174 			continue;
175 
176 		(void) strlcpy(dimmname, pat, sizeof (dimmname));
177 		if (bd->bd_subst != NULL) {
178 			(void) strlcpy(dimmname+replace, bd->bd_subst,
179 			    sizeof (dimmname) - strlen(bd->bd_subst));
180 			replace += strlen(bd->bd_subst);
181 		}
182 
183 		c = pat + start;
184 		while (*c != '\0') {
185 			int dimmlen = -1;
186 
187 			(void) sscanf(c, bd->bd_reppat, &dimmlen);
188 			if (dimmlen == -1)
189 				break;
190 
191 			while (*c == ' ') {
192 				c++;
193 				dimmlen--;
194 			}
195 
196 			if (dimmlen > sizeof (dimmname) - replace)
197 				break;
198 
199 			(void) strlcpy(dimmname + replace, c, dimmlen + 1);
200 
201 			newdimms = fmd_fmri_alloc(sizeof (char *) *
202 			    (ndimms + 1));
203 			if (ndimms != 0) {
204 				bcopy(dimms, newdimms, sizeof (char *) *
205 				    ndimms);
206 				fmd_fmri_free(dimms, sizeof (char *) * ndimms);
207 			}
208 			newdimms[ndimms++] = fmd_fmri_strdup(dimmname);
209 			dimms = newdimms;
210 
211 			c += dimmlen;
212 
213 			if (*c != ' ' && *c != '\0')
214 				break;
215 		}
216 
217 		if (*c != '\0')
218 			break;
219 
220 		*dimmsp = dimms;
221 		*ndimmsp = ndimms;
222 
223 		return (0);
224 	}
225 
226 	mem_strarray_free(dimms, ndimms);
227 
228 	return (fmd_fmri_set_errno(EINVAL));
229 }
230 
231 int
232 mem_unum_burst(const char *pat, char ***dimmsp, size_t *ndimmsp)
233 {
234 	const char *platform = fmd_fmri_get_platform();
235 
236 	/*
237 	 * Call mem_unum_burst_sgsc() for Starcat, Serengeti, and
238 	 * Lightweight 8 platforms.  Call mem_unum_burst_pattern()
239 	 * for all other platforms.
240 	 */
241 	if (strcmp(platform, "SUNW,Sun-Fire-15000") == 0 ||
242 	    strcmp(platform, "SUNW,Sun-Fire") == 0 ||
243 	    strcmp(platform, "SUNW,Netra-T12") == 0)
244 		return (mem_unum_burst_sgsc(pat, dimmsp, ndimmsp));
245 	else
246 		return (mem_unum_burst_pattern(pat, dimmsp, ndimmsp));
247 }
248 
249 /*
250  * The unum containership operation is designed to tell the caller whether a
251  * given FMRI contains another.  In the case of this plugin, we tell the caller
252  * whether a given memory FMRI (usually a bank) contains another (usually a
253  * DIMM).  We do this in one of two ways, depending on the platform.  For most
254  * platforms, we can use the bursting routine to generate the list of member
255  * unums from the container unum.  Membership can then be determined by
256  * searching the bursted list for the containee's unum.
257  *
258  * Some platforms, however, cannot be bursted, as their bank unums do not
259  * contain all of the information needed to generate the complete list of
260  * member DIMM unums.  For these unums, we must make do with a substring
261  * comparison.
262  */
263 
264 static int
265 unum_contains_bypat(const char *erunum, const char *eeunum)
266 {
267 	char **ernms, **eenms;
268 	size_t nernms, neenms;
269 	int i, j, rv = 1;
270 
271 	if (mem_unum_burst(erunum, &ernms, &nernms) < 0)
272 		return (fmd_fmri_set_errno(EINVAL));
273 	if (mem_unum_burst(eeunum, &eenms, &neenms) < 0) {
274 		mem_strarray_free(ernms, nernms);
275 		return (fmd_fmri_set_errno(EINVAL));
276 	}
277 
278 	for (i = 0; i < neenms; i++) {
279 		for (j = 0; j < nernms; j++) {
280 			if (strcmp(eenms[i], ernms[j]) == 0)
281 				break;
282 		}
283 
284 		if (j == nernms) {
285 			/*
286 			 * This DIMM was not found in the container.
287 			 */
288 			rv = 0;
289 			break;
290 		}
291 	}
292 
293 	mem_strarray_free(ernms, nernms);
294 	mem_strarray_free(eenms, neenms);
295 
296 	return (rv);
297 }
298 
299 static int
300 unum_strip_one_jnum(const char *unum, uint_t *endp)
301 {
302 	char *c;
303 	int i;
304 
305 	if ((c = strrchr(unum, 'J')) == NULL)
306 		return (0);
307 
308 	while (c > unum && isspace(c[-1]))
309 		c--;
310 
311 	(void) sscanf(c, " J%*[0-9] %n", &i);
312 	if (i == 0 || (uintptr_t)(c - unum) + i != strlen(unum))
313 		return (0);
314 
315 	*endp = (uint_t)(c - unum);
316 	return (1);
317 }
318 
319 
320 static int
321 unum_contains_bysubstr(const char *erunum, const char *eeunum)
322 {
323 	uint_t erlen, eelen;
324 
325 	/*
326 	 * This comparison method is only known to work on specific types of
327 	 * unums.  Check for those types here.
328 	 */
329 	if ((strncmp(erunum, "/N", 2) != 0 && strncmp(erunum, "/IO", 3) != 0 &&
330 	    strncmp(erunum, "/SB", 3) != 0) ||
331 	    (strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 &&
332 	    strncmp(eeunum, "/SB", 3) != 0))
333 		return (fmd_fmri_set_errno(EINVAL));
334 
335 	erlen = unum_strip_one_jnum(erunum, &erlen) ? erlen : strlen(erunum);
336 	eelen = unum_strip_one_jnum(eeunum, &eelen) ? eelen : strlen(eeunum);
337 
338 	return (strncmp(erunum, eeunum, MIN(erlen, eelen)) == 0);
339 }
340 
341 typedef int unum_cmptor_f(const char *, const char *);
342 
343 static unum_cmptor_f *const unum_cmptors[] = {
344 	unum_contains_bypat,
345 	unum_contains_bysubstr
346 };
347 
348 int
349 mem_unum_contains(const char *erunum, const char *eeunum)
350 {
351 	static int cmptor = 0;
352 	int rc;
353 
354 	while (isspace(*erunum))
355 		erunum++;
356 	while (isspace(*eeunum))
357 		eeunum++;
358 
359 	if ((rc = unum_cmptors[cmptor](erunum, eeunum)) >= 0)
360 		return (rc);
361 
362 	if ((rc = unum_cmptors[cmptor == 0](erunum, eeunum)) >= 0) {
363 		/*
364 		 * We succeeded with the non-default comparator.  Change the
365 		 * default so we use the correct one next time.
366 		 */
367 		cmptor = (cmptor == 0);
368 	}
369 
370 	return (rc);
371 }
372 
373 /*
374  * If an asru has a unum string that is an hc path string then return
375  * a new nvl (to be freed by the caller) that is a duplicate of the
376  * original but with an additional member of a reconstituted hc fmri.
377  */
378 int
379 mem_unum_rewrite(nvlist_t *nvl, nvlist_t **rnvl)
380 {
381 	int err;
382 	char *unumstr;
383 	nvlist_t *unum;
384 	struct topo_hdl *thp;
385 
386 	if (nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unumstr) != 0 ||
387 	    strncmp(unumstr, "hc:/", 4) != 0)
388 		return (0);
389 
390 	thp = fmd_fmri_topology(TOPO_VERSION);
391 
392 	if (topo_fmri_str2nvl(thp, unumstr, &unum, &err) != 0)
393 		return (EINVAL);
394 
395 	if ((err = nvlist_dup(nvl, rnvl, 0)) != 0) {
396 		nvlist_free(unum);
397 		return (err);
398 	}
399 
400 	err = nvlist_add_nvlist(*rnvl, FM_FMRI_MEM_UNUM "-fmri", unum);
401 	nvlist_free(unum);
402 
403 	if (err != 0)
404 		nvlist_free(*rnvl);
405 
406 	return (err);
407 }
408