Home | History | Annotate | Download | only in mem
      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
    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
    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
    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
    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
    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
    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
    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
    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