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