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