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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 { NULL } 90 }; 91 92 /* 93 * Burst Serengeti and Starcat-style unums. 94 * A DIMM unum string is expected to be in this form: 95 * "[/N0/]SB12/P0/B0/D2 [J13500]" 96 * A bank unum string is expected to be in this form: 97 * "[/N0/]SB12/P0/B0 [J13500, ...]" 98 */ 99 static int 100 mem_unum_burst_sgsc(const char *pat, char ***dimmsp, size_t *ndimmsp) 101 { 102 char buf[64]; 103 char **dimms; 104 char *base; 105 const char *c; 106 char *copy; 107 size_t copysz; 108 int i; 109 110 /* 111 * No expansion is required for a DIMM unum 112 */ 113 if (strchr(pat, 'D') != NULL) { 114 dimms = fmd_fmri_alloc(sizeof (char *)); 115 dimms[0] = fmd_fmri_strdup(pat); 116 *dimmsp = dimms; 117 *ndimmsp = 1; 118 return (0); 119 } 120 121 /* 122 * strtok is destructive so we need to work with 123 * a copy and keep track of the size allocated. 124 */ 125 copysz = strlen(pat) + 1; 126 copy = fmd_fmri_alloc(copysz); 127 (void) strcpy(copy, pat); 128 129 base = strtok(copy, " "); 130 131 /* There are four DIMMs in a bank */ 132 dimms = fmd_fmri_alloc(sizeof (char *) * 4); 133 134 for (i = 0; i < 4; i++) { 135 (void) snprintf(buf, sizeof (buf), "%s/D%d", base, i); 136 137 if ((c = strtok(NULL, " ")) != NULL) 138 (void) snprintf(buf, sizeof (buf), "%s %s", buf, c); 139 140 dimms[i] = fmd_fmri_strdup(buf); 141 } 142 143 fmd_fmri_free(copy, copysz); 144 145 *dimmsp = dimms; 146 *ndimmsp = 4; 147 return (0); 148 } 149 150 151 /* 152 * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1 153 * otherwise. 154 */ 155 static int 156 mem_unum_burst_pattern(const char *pat, char ***dimmsp, size_t *ndimmsp) 157 { 158 const bank_dimm_t *bd; 159 char **dimms = NULL, **newdimms; 160 size_t ndimms = 0; 161 const char *c; 162 163 164 for (bd = bank_dimm; bd->bd_pat != NULL; bd++) { 165 int replace, start, matched; 166 char dimmname[64]; 167 168 replace = start = matched = -1; 169 (void) sscanf(pat, bd->bd_pat, &replace, &start, &matched); 170 if (matched == -1) 171 continue; 172 173 (void) strlcpy(dimmname, pat, sizeof (dimmname)); 174 if (bd->bd_subst != NULL) { 175 (void) strlcpy(dimmname+replace, bd->bd_subst, 176 sizeof (dimmname) - strlen(bd->bd_subst)); 177 replace += strlen(bd->bd_subst); 178 } 179 180 c = pat + start; 181 while (*c != '\0') { 182 int dimmlen = -1; 183 184 (void) sscanf(c, bd->bd_reppat, &dimmlen); 185 if (dimmlen == -1) 186 break; 187 188 while (*c == ' ') { 189 c++; 190 dimmlen--; 191 } 192 193 if (dimmlen > sizeof (dimmname) - replace) 194 break; 195 196 (void) strlcpy(dimmname + replace, c, dimmlen + 1); 197 198 newdimms = fmd_fmri_alloc(sizeof (char *) * 199 (ndimms + 1)); 200 if (ndimms != 0) { 201 bcopy(dimms, newdimms, sizeof (char *) * 202 ndimms); 203 fmd_fmri_free(dimms, sizeof (char *) * ndimms); 204 } 205 newdimms[ndimms++] = fmd_fmri_strdup(dimmname); 206 dimms = newdimms; 207 208 c += dimmlen; 209 210 if (*c != ' ' && *c != '\0') 211 break; 212 } 213 214 if (*c != '\0') 215 break; 216 217 *dimmsp = dimms; 218 *ndimmsp = ndimms; 219 220 return (0); 221 } 222 223 mem_strarray_free(dimms, ndimms); 224 225 return (fmd_fmri_set_errno(EINVAL)); 226 } 227 228 int 229 mem_unum_burst(const char *pat, char ***dimmsp, size_t *ndimmsp) 230 { 231 const char *platform = fmd_fmri_get_platform(); 232 233 /* 234 * Call mem_unum_burst_sgsc() for Starcat, Serengeti, and 235 * Lightweight 8 platforms. Call mem_unum_burst_pattern() 236 * for all other platforms. 237 */ 238 if (strcmp(platform, "SUNW,Sun-Fire-15000") == 0 || 239 strcmp(platform, "SUNW,Sun-Fire") == 0 || 240 strcmp(platform, "SUNW,Netra-T12") == 0) 241 return (mem_unum_burst_sgsc(pat, dimmsp, ndimmsp)); 242 else 243 return (mem_unum_burst_pattern(pat, dimmsp, ndimmsp)); 244 } 245 246 /* 247 * The unum containership operation is designed to tell the caller whether a 248 * given FMRI contains another. In the case of this plugin, we tell the caller 249 * whether a given memory FMRI (usually a bank) contains another (usually a 250 * DIMM). We do this in one of two ways, depending on the platform. For most 251 * platforms, we can use the bursting routine to generate the list of member 252 * unums from the container unum. Membership can then be determined by 253 * searching the bursted list for the containee's unum. 254 * 255 * Some platforms, however, cannot be bursted, as their bank unums do not 256 * contain all of the information needed to generate the complete list of 257 * member DIMM unums. For these unums, we must make do with a substring 258 * comparison. 259 */ 260 261 static int 262 unum_contains_bypat(const char *erunum, const char *eeunum) 263 { 264 char **ernms, **eenms; 265 uint_t nernms, neenms; 266 int i, j, rv = 1; 267 268 if (mem_unum_burst(erunum, &ernms, &nernms) < 0) 269 return (fmd_fmri_set_errno(EINVAL)); 270 if (mem_unum_burst(eeunum, &eenms, &neenms) < 0) { 271 mem_strarray_free(ernms, nernms); 272 return (fmd_fmri_set_errno(EINVAL)); 273 } 274 275 for (i = 0; i < neenms; i++) { 276 for (j = 0; j < nernms; j++) { 277 if (strcmp(eenms[i], ernms[j]) == 0) 278 break; 279 } 280 281 if (j == nernms) { 282 /* 283 * This DIMM was not found in the container. 284 */ 285 rv = 0; 286 break; 287 } 288 } 289 290 mem_strarray_free(ernms, nernms); 291 mem_strarray_free(eenms, neenms); 292 293 return (rv); 294 } 295 296 static int 297 unum_strip_one_jnum(const char *unum, uint_t *endp) 298 { 299 char *c; 300 int i; 301 302 if ((c = strrchr(unum, 'J')) == NULL) 303 return (0); 304 305 while (c > unum && isspace(c[-1])) 306 c--; 307 308 (void) sscanf(c, " J%*[0-9] %n", &i); 309 if (i == 0 || (uintptr_t)(c - unum) + i != strlen(unum)) 310 return (0); 311 312 *endp = (uint_t)(c - unum); 313 return (1); 314 } 315 316 317 static int 318 unum_contains_bysubstr(const char *erunum, const char *eeunum) 319 { 320 uint_t erlen, eelen; 321 322 /* 323 * This comparison method is only known to work on specific types of 324 * unums. Check for those types here. 325 */ 326 if ((strncmp(erunum, "/N", 2) != 0 && strncmp(erunum, "/IO", 3) != 0 && 327 strncmp(erunum, "/SB", 3) != 0) || 328 (strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 && 329 strncmp(eeunum, "/SB", 3) != 0)) 330 return (fmd_fmri_set_errno(EINVAL)); 331 332 erlen = unum_strip_one_jnum(erunum, &erlen) ? erlen : strlen(erunum); 333 eelen = unum_strip_one_jnum(eeunum, &eelen) ? eelen : strlen(eeunum); 334 335 return (strncmp(erunum, eeunum, MIN(erlen, eelen)) == 0); 336 } 337 338 typedef int unum_cmptor_f(const char *, const char *); 339 340 static unum_cmptor_f *const unum_cmptors[] = { 341 unum_contains_bypat, 342 unum_contains_bysubstr 343 }; 344 345 int 346 mem_unum_contains(const char *erunum, const char *eeunum) 347 { 348 static int cmptor = 0; 349 int rc; 350 351 while (isspace(*erunum)) 352 erunum++; 353 while (isspace(*eeunum)) 354 eeunum++; 355 356 if ((rc = unum_cmptors[cmptor](erunum, eeunum)) >= 0) 357 return (rc); 358 359 if ((rc = unum_cmptors[cmptor == 0](erunum, eeunum)) >= 0) { 360 /* 361 * We succeeded with the non-default comparator. Change the 362 * default so we use the correct one next time. 363 */ 364 cmptor = (cmptor == 0); 365 } 366 367 return (rc); 368 } 369