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