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 2007 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 { "/CMU%*2d/%n%nMEM%*2d%*1c%n", " MEM%*2d%*1c%n" }, 97 { "MB/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/" }, 98 { "%n%nMB/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", 99 "MB/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, 100 { "%n%nMB/CMP%*d/BR%*d/CH%*d/D%*d%n", "MB/CMP%*d/BR%*d/CH%*d/D%*d%n" }, 101 { "MB/CPU%*d/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/"}, 102 { "MB/MEM%*d/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/"}, 103 { "%n%nMB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", 104 "MB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, 105 { "%n%nMB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", 106 "MB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, 107 { "%n%nMB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d%n", 108 "MB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d%n" }, 109 { "%n%nMB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d%n", 110 "MB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d%n" }, 111 { NULL } 112 }; 113 114 /* 115 * Burst Serengeti and Starcat-style unums. 116 * A DIMM unum string is expected to be in this form: 117 * "[/N0/]SB12/P0/B0/D2 [J13500]" 118 * A bank unum string is expected to be in this form: 119 * "[/N0/]SB12/P0/B0 [J13500, ...]" 120 */ 121 static int 122 mem_unum_burst_sgsc(const char *pat, char ***dimmsp, size_t *ndimmsp) 123 { 124 char buf[64]; 125 char **dimms; 126 char *base; 127 const char *c; 128 char *copy; 129 size_t copysz; 130 int i; 131 132 /* 133 * No expansion is required for a DIMM unum 134 */ 135 if (strchr(pat, 'D') != NULL) { 136 dimms = fmd_fmri_alloc(sizeof (char *)); 137 dimms[0] = fmd_fmri_strdup(pat); 138 *dimmsp = dimms; 139 *ndimmsp = 1; 140 return (0); 141 } 142 143 /* 144 * strtok is destructive so we need to work with 145 * a copy and keep track of the size allocated. 146 */ 147 copysz = strlen(pat) + 1; 148 copy = fmd_fmri_alloc(copysz); 149 (void) strcpy(copy, pat); 150 151 base = strtok(copy, " "); 152 153 /* There are four DIMMs in a bank */ 154 dimms = fmd_fmri_alloc(sizeof (char *) * 4); 155 156 for (i = 0; i < 4; i++) { 157 (void) snprintf(buf, sizeof (buf), "%s/D%d", base, i); 158 159 if ((c = strtok(NULL, " ")) != NULL) 160 (void) snprintf(buf, sizeof (buf), "%s %s", buf, c); 161 162 dimms[i] = fmd_fmri_strdup(buf); 163 } 164 165 fmd_fmri_free(copy, copysz); 166 167 *dimmsp = dimms; 168 *ndimmsp = 4; 169 return (0); 170 } 171 172 173 /* 174 * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1 175 * otherwise. 176 */ 177 static int 178 mem_unum_burst_pattern(const char *pat, char ***dimmsp, size_t *ndimmsp) 179 { 180 const bank_dimm_t *bd; 181 char **dimms = NULL, **newdimms; 182 size_t ndimms = 0; 183 const char *c; 184 185 186 for (bd = bank_dimm; bd->bd_pat != NULL; bd++) { 187 int replace, start, matched; 188 char dimmname[64]; 189 190 replace = start = matched = -1; 191 (void) sscanf(pat, bd->bd_pat, &replace, &start, &matched); 192 if (matched == -1) 193 continue; 194 (void) strlcpy(dimmname, pat, sizeof (dimmname)); 195 if (bd->bd_subst != NULL) { 196 (void) strlcpy(dimmname+replace, bd->bd_subst, 197 sizeof (dimmname) - strlen(bd->bd_subst)); 198 replace += strlen(bd->bd_subst); 199 } 200 201 c = pat + start; 202 while (*c != '\0') { 203 int dimmlen = -1; 204 205 (void) sscanf(c, bd->bd_reppat, &dimmlen); 206 if (dimmlen == -1) 207 break; 208 209 while (*c == ' ') { 210 c++; 211 dimmlen--; 212 } 213 214 if (dimmlen > sizeof (dimmname) - replace) 215 break; 216 217 (void) strlcpy(dimmname + replace, c, dimmlen + 1); 218 219 newdimms = fmd_fmri_alloc(sizeof (char *) * 220 (ndimms + 1)); 221 if (ndimms != 0) { 222 bcopy(dimms, newdimms, sizeof (char *) * 223 ndimms); 224 fmd_fmri_free(dimms, sizeof (char *) * ndimms); 225 } 226 newdimms[ndimms++] = fmd_fmri_strdup(dimmname); 227 dimms = newdimms; 228 229 c += dimmlen; 230 231 if (*c != ' ' && *c != '\0') 232 break; 233 } 234 235 if (*c != '\0') 236 break; 237 238 *dimmsp = dimms; 239 *ndimmsp = ndimms; 240 241 return (0); 242 } 243 244 mem_strarray_free(dimms, ndimms); 245 246 return (fmd_fmri_set_errno(EINVAL)); 247 } 248 249 int 250 mem_unum_burst(const char *pat, char ***dimmsp, size_t *ndimmsp) 251 { 252 const char *platform = fmd_fmri_get_platform(); 253 254 /* 255 * Call mem_unum_burst_sgsc() for Starcat, Serengeti, and 256 * Lightweight 8 platforms. Call mem_unum_burst_pattern() 257 * for all other platforms. 258 */ 259 if (strcmp(platform, "SUNW,Sun-Fire-15000") == 0 || 260 strcmp(platform, "SUNW,Sun-Fire") == 0 || 261 strcmp(platform, "SUNW,Netra-T12") == 0) 262 return (mem_unum_burst_sgsc(pat, dimmsp, ndimmsp)); 263 else 264 return (mem_unum_burst_pattern(pat, dimmsp, ndimmsp)); 265 } 266 267 /* 268 * The unum containership operation is designed to tell the caller whether a 269 * given FMRI contains another. In the case of this plugin, we tell the caller 270 * whether a given memory FMRI (usually a bank) contains another (usually a 271 * DIMM). We do this in one of two ways, depending on the platform. For most 272 * platforms, we can use the bursting routine to generate the list of member 273 * unums from the container unum. Membership can then be determined by 274 * searching the bursted list for the containee's unum. 275 * 276 * Some platforms, however, cannot be bursted, as their bank unums do not 277 * contain all of the information needed to generate the complete list of 278 * member DIMM unums. For these unums, we must make do with a substring 279 * comparison. 280 */ 281 282 static int 283 unum_contains_bypat(const char *erunum, const char *eeunum) 284 { 285 char **ernms, **eenms; 286 size_t nernms, neenms; 287 int i, j, rv = 1; 288 289 if (mem_unum_burst(erunum, &ernms, &nernms) < 0) 290 return (fmd_fmri_set_errno(EINVAL)); 291 if (mem_unum_burst(eeunum, &eenms, &neenms) < 0) { 292 mem_strarray_free(ernms, nernms); 293 return (fmd_fmri_set_errno(EINVAL)); 294 } 295 296 for (i = 0; i < neenms; i++) { 297 for (j = 0; j < nernms; j++) { 298 if (strcmp(eenms[i], ernms[j]) == 0) 299 break; 300 } 301 302 if (j == nernms) { 303 /* 304 * This DIMM was not found in the container. 305 */ 306 rv = 0; 307 break; 308 } 309 } 310 311 mem_strarray_free(ernms, nernms); 312 mem_strarray_free(eenms, neenms); 313 314 return (rv); 315 } 316 317 static int 318 unum_strip_one_jnum(const char *unum, uint_t *endp) 319 { 320 char *c; 321 int i; 322 323 if ((c = strrchr(unum, 'J')) == NULL) 324 return (0); 325 326 while (c > unum && isspace(c[-1])) 327 c--; 328 329 (void) sscanf(c, " J%*[0-9] %n", &i); 330 if (i == 0 || (uintptr_t)(c - unum) + i != strlen(unum)) 331 return (0); 332 333 *endp = (uint_t)(c - unum); 334 return (1); 335 } 336 337 338 static int 339 unum_contains_bysubstr(const char *erunum, const char *eeunum) 340 { 341 uint_t erlen, eelen; 342 int nojnumstrip = 0; 343 344 /* 345 * This comparison method is only known to work on specific types of 346 * unums. Check for those types here. 347 */ 348 if ((strncmp(erunum, "/N", 2) != 0 && strncmp(erunum, "/IO", 3) != 0 && 349 strncmp(erunum, "/SB", 3) != 0) || 350 (strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 && 351 strncmp(eeunum, "/SB", 3) != 0)) { 352 if (ISHCUNUM(erunum) && ISHCUNUM(eeunum)) { 353 nojnumstrip = 1; 354 erlen = strlen(erunum); 355 eelen = strlen(eeunum); 356 } else { 357 return (fmd_fmri_set_errno(EINVAL)); 358 } 359 } 360 361 if (!nojnumstrip) { 362 erlen = unum_strip_one_jnum(erunum, &erlen) ? 363 erlen : strlen(erunum); 364 eelen = unum_strip_one_jnum(eeunum, &eelen) ? 365 eelen : strlen(eeunum); 366 } 367 368 return (strncmp(erunum, eeunum, MIN(erlen, eelen)) == 0); 369 } 370 371 typedef int unum_cmptor_f(const char *, const char *); 372 373 static unum_cmptor_f *const unum_cmptors[] = { 374 unum_contains_bypat, 375 unum_contains_bysubstr 376 }; 377 378 int 379 mem_unum_contains(const char *erunum, const char *eeunum) 380 { 381 static int cmptor = 0; 382 int rc; 383 384 while (isspace(*erunum)) 385 erunum++; 386 while (isspace(*eeunum)) 387 eeunum++; 388 389 if ((rc = unum_cmptors[cmptor](erunum, eeunum)) >= 0) 390 return (rc); 391 392 if ((rc = unum_cmptors[cmptor == 0](erunum, eeunum)) >= 0) { 393 /* 394 * We succeeded with the non-default comparator. Change the 395 * default so we use the correct one next time. 396 */ 397 cmptor = (cmptor == 0); 398 } 399 400 return (rc); 401 } 402 403 /* 404 * If an asru has a unum string that is an hc path string then return 405 * a new nvl (to be freed by the caller) that is a duplicate of the 406 * original but with an additional member of a reconstituted hc fmri. 407 */ 408 int 409 mem_unum_rewrite(nvlist_t *nvl, nvlist_t **rnvl) 410 { 411 int err; 412 char *unumstr; 413 nvlist_t *unum; 414 struct topo_hdl *thp; 415 416 if (nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unumstr) != 0 || 417 !ISHCUNUM(unumstr)) 418 return (0); 419 420 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 421 return (EINVAL); 422 423 if (topo_fmri_str2nvl(thp, unumstr, &unum, &err) != 0) { 424 fmd_fmri_topo_rele(thp); 425 return (EINVAL); 426 } 427 428 fmd_fmri_topo_rele(thp); 429 430 if ((err = nvlist_dup(nvl, rnvl, 0)) != 0) { 431 nvlist_free(unum); 432 return (err); 433 } 434 435 err = nvlist_add_nvlist(*rnvl, FM_FMRI_MEM_UNUM "-fmri", unum); 436 nvlist_free(unum); 437 438 if (err != 0) 439 nvlist_free(*rnvl); 440 441 return (err); 442 } 443