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