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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <fm/fmd_fmri.h>
31 #include <fm/libtopo.h>
37 #define ISHCUNUM(unum) (strncmp(unum, "hc:/", 4) == 0)
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.
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.
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
57 * sscanf is your disturbingly powerful friend.
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.
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"
74 typedef struct bank_dimm
{
76 const char *bd_reppat
;
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" },
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, ...]"
123 mem_unum_burst_sgsc(const char *pat
, char ***dimmsp
, size_t *ndimmsp
)
134 * No expansion is required for a DIMM unum
136 if (strchr(pat
, 'D') != NULL
) {
137 dimms
= fmd_fmri_alloc(sizeof (char *));
138 dimms
[0] = fmd_fmri_strdup(pat
);
145 * strtok is destructive so we need to work with
146 * a copy and keep track of the size allocated.
148 copysz
= strlen(pat
) + 1;
149 copy
= fmd_fmri_alloc(copysz
);
150 (void) strcpy(copy
, pat
);
152 base
= strtok(copy
, " ");
154 /* There are four DIMMs in a bank */
155 dimms
= fmd_fmri_alloc(sizeof (char *) * 4);
157 for (i
= 0; i
< 4; i
++) {
158 (void) snprintf(buf
, sizeof (buf
), "%s/D%d", base
, i
);
160 if ((c
= strtok(NULL
, " ")) != NULL
)
161 (void) snprintf(buf
, sizeof (buf
), "%s %s", buf
, c
);
163 dimms
[i
] = fmd_fmri_strdup(buf
);
166 fmd_fmri_free(copy
, copysz
);
175 * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1
179 mem_unum_burst_pattern(const char *pat
, char ***dimmsp
, size_t *ndimmsp
)
181 const bank_dimm_t
*bd
;
182 char **dimms
= NULL
, **newdimms
;
187 for (bd
= bank_dimm
; bd
->bd_pat
!= NULL
; bd
++) {
188 int replace
, start
, matched
;
191 replace
= start
= matched
= -1;
192 (void) sscanf(pat
, bd
->bd_pat
, &replace
, &start
, &matched
);
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
);
206 (void) sscanf(c
, bd
->bd_reppat
, &dimmlen
);
215 if (dimmlen
> sizeof (dimmname
) - replace
)
218 (void) strlcpy(dimmname
+ replace
, c
, dimmlen
+ 1);
220 newdimms
= fmd_fmri_alloc(sizeof (char *) *
223 bcopy(dimms
, newdimms
, sizeof (char *) *
225 fmd_fmri_free(dimms
, sizeof (char *) * ndimms
);
227 newdimms
[ndimms
++] = fmd_fmri_strdup(dimmname
);
232 if (*c
!= ' ' && *c
!= '\0')
245 mem_strarray_free(dimms
, ndimms
);
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().
253 return (fmd_fmri_set_errno(ENOTSUP
));
257 mem_unum_burst(const char *pat
, char ***dimmsp
, size_t *ndimmsp
)
259 const char *platform
= fmd_fmri_get_platform();
262 * Call mem_unum_burst_sgsc() for Starcat, Serengeti, and
263 * Lightweight 8 platforms. Call mem_unum_burst_pattern()
264 * for all other platforms.
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
));
271 return (mem_unum_burst_pattern(pat
, dimmsp
, ndimmsp
));
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.
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
290 unum_contains_bypat(const char *erunum
, const char *eeunum
)
292 char **ernms
, **eenms
;
293 size_t nernms
, neenms
;
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
));
303 for (i
= 0; i
< neenms
; i
++) {
304 for (j
= 0; j
< nernms
; j
++) {
305 if (strcmp(eenms
[i
], ernms
[j
]) == 0)
311 * This DIMM was not found in the container.
318 mem_strarray_free(ernms
, nernms
);
319 mem_strarray_free(eenms
, neenms
);
325 unum_strip_one_jnum(const char *unum
, uint_t
*endp
)
330 if ((c
= strrchr(unum
, 'J')) == NULL
)
333 while (c
> unum
&& isspace(c
[-1]))
336 (void) sscanf(c
, " J%*[0-9] %n", &i
);
337 if (i
== 0 || (uintptr_t)(c
- unum
) + i
!= strlen(unum
))
340 *endp
= (uint_t
)(c
- unum
);
346 unum_contains_bysubstr(const char *erunum
, const char *eeunum
)
352 * This comparison method is only known to work on specific types of
353 * unums. Check for those types here.
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
)) {
361 erlen
= strlen(erunum
);
362 eelen
= strlen(eeunum
);
364 return (fmd_fmri_set_errno(EINVAL
));
369 erlen
= unum_strip_one_jnum(erunum
, &erlen
) ?
370 erlen
: strlen(erunum
);
371 eelen
= unum_strip_one_jnum(eeunum
, &eelen
) ?
372 eelen
: strlen(eeunum
);
375 return (strncmp(erunum
, eeunum
, MIN(erlen
, eelen
)) == 0);
378 typedef int unum_cmptor_f(const char *, const char *);
380 static unum_cmptor_f
*const unum_cmptors
[] = {
382 unum_contains_bysubstr
386 mem_unum_contains(const char *erunum
, const char *eeunum
)
388 static int cmptor
= 0;
391 while (isspace(*erunum
))
393 while (isspace(*eeunum
))
396 if ((rc
= unum_cmptors
[cmptor
](erunum
, eeunum
)) >= 0)
399 if ((rc
= unum_cmptors
[cmptor
== 0](erunum
, eeunum
)) >= 0) {
401 * We succeeded with the non-default comparator. Change the
402 * default so we use the correct one next time.
404 cmptor
= (cmptor
== 0);
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.
416 mem_unum_rewrite(nvlist_t
*nvl
, nvlist_t
**rnvl
)
421 struct topo_hdl
*thp
;
423 if (nvlist_lookup_string(nvl
, FM_FMRI_MEM_UNUM
, &unumstr
) != 0 ||
427 if ((thp
= fmd_fmri_topo_hold(TOPO_VERSION
)) == NULL
)
430 if (topo_fmri_str2nvl(thp
, unumstr
, &unum
, &err
) != 0) {
431 fmd_fmri_topo_rele(thp
);
435 fmd_fmri_topo_rele(thp
);
437 if ((err
= nvlist_dup(nvl
, rnvl
, 0)) != 0) {
442 err
= nvlist_add_nvlist(*rnvl
, FM_FMRI_MEM_UNUM
"-fmri", unum
);