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 * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1 94 * otherwise. 95 */ 96 int 97 mem_unum_burst(const char *pat, char ***dimmsp, size_t *ndimmsp) 98 { 99 const bank_dimm_t *bd; 100 char **dimms = NULL, **newdimms; 101 size_t ndimms = 0; 102 const char *c; 103 104 for (bd = bank_dimm; bd->bd_pat != NULL; bd++) { 105 int replace, start, matched; 106 char dimmname[64]; 107 108 replace = start = matched = -1; 109 (void) sscanf(pat, bd->bd_pat, &replace, &start, &matched); 110 if (matched == -1) 111 continue; 112 113 (void) strlcpy(dimmname, pat, sizeof (dimmname)); 114 if (bd->bd_subst != NULL) { 115 (void) strlcpy(dimmname+replace, bd->bd_subst, 116 sizeof (dimmname) - strlen(bd->bd_subst)); 117 replace += strlen(bd->bd_subst); 118 } 119 120 c = pat + start; 121 while (*c != '\0') { 122 int dimmlen = -1; 123 124 (void) sscanf(c, bd->bd_reppat, &dimmlen); 125 if (dimmlen == -1) 126 break; 127 128 while (*c == ' ') { 129 c++; 130 dimmlen--; 131 } 132 133 if (dimmlen > sizeof (dimmname) - replace) 134 break; 135 136 (void) strlcpy(dimmname + replace, c, dimmlen + 1); 137 138 newdimms = fmd_fmri_alloc(sizeof (char *) * 139 (ndimms + 1)); 140 if (ndimms != 0) { 141 bcopy(dimms, newdimms, sizeof (char *) * 142 ndimms); 143 fmd_fmri_free(dimms, sizeof (char *) * ndimms); 144 } 145 newdimms[ndimms++] = fmd_fmri_strdup(dimmname); 146 dimms = newdimms; 147 148 c += dimmlen; 149 150 if (*c != ' ' && *c != '\0') 151 break; 152 } 153 154 if (*c != '\0') 155 break; 156 157 *dimmsp = dimms; 158 *ndimmsp = ndimms; 159 160 return (0); 161 } 162 163 mem_strarray_free(dimms, ndimms); 164 return (fmd_fmri_set_errno(EINVAL)); 165 } 166 167 /* 168 * The unum containership operation is designed to tell the caller whether a 169 * given FMRI contains another. In the case of this plugin, we tell the caller 170 * whether a given memory FMRI (usually a bank) contains another (usually a 171 * DIMM). We do this in one of two ways, depending on the platform. For most 172 * platforms, we can use the bursting routine to generate the list of member 173 * unums from the container unum. Membership can then be determined by 174 * searching the bursted list for the containee's unum. 175 * 176 * Some platforms, however, cannot be bursted, as their bank unums do not 177 * contain all of the information needed to generate the complete list of 178 * member DIMM unums. For these unums, we must make do with a substring 179 * comparison. 180 */ 181 182 static int 183 unum_contains_bypat(const char *erunum, const char *eeunum) 184 { 185 char **ernms, **eenms; 186 uint_t nernms, neenms; 187 int i, j, rv = 1; 188 189 if (mem_unum_burst(erunum, &ernms, &nernms) < 0) 190 return (fmd_fmri_set_errno(EINVAL)); 191 if (mem_unum_burst(eeunum, &eenms, &neenms) < 0) { 192 mem_strarray_free(ernms, nernms); 193 return (fmd_fmri_set_errno(EINVAL)); 194 } 195 196 for (i = 0; i < neenms; i++) { 197 for (j = 0; j < nernms; j++) { 198 if (strcmp(eenms[i], ernms[j]) == 0) 199 break; 200 } 201 202 if (j == nernms) { 203 /* 204 * This DIMM was not found in the container. 205 */ 206 rv = 0; 207 break; 208 } 209 } 210 211 mem_strarray_free(ernms, nernms); 212 mem_strarray_free(eenms, neenms); 213 214 return (rv); 215 } 216 217 static int 218 unum_strip_one_jnum(const char *unum, uint_t *endp) 219 { 220 char *c; 221 int i; 222 223 if ((c = strrchr(unum, 'J')) == NULL) 224 return (0); 225 226 while (c > unum && isspace(c[-1])) 227 c--; 228 229 (void) sscanf(c, " J%*[0-9] %n", &i); 230 if (i == 0 || (uintptr_t)(c - unum) + i != strlen(unum)) 231 return (0); 232 233 *endp = (uint_t)(c - unum); 234 return (1); 235 } 236 237 238 static int 239 unum_contains_bysubstr(const char *erunum, const char *eeunum) 240 { 241 uint_t erlen, eelen; 242 243 /* 244 * This comparison method is only known to work on specific types of 245 * unums. Check for those types here. 246 */ 247 if ((strncmp(erunum, "/N", 2) != 0 && strncmp(erunum, "/IO", 3) != 0 && 248 strncmp(erunum, "/SB", 3) != 0) || 249 (strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 && 250 strncmp(eeunum, "/SB", 3) != 0)) 251 return (fmd_fmri_set_errno(EINVAL)); 252 253 erlen = unum_strip_one_jnum(erunum, &erlen) ? erlen : strlen(erunum); 254 eelen = unum_strip_one_jnum(eeunum, &eelen) ? eelen : strlen(eeunum); 255 256 return (strncmp(erunum, eeunum, MIN(erlen, eelen)) == 0); 257 } 258 259 typedef int unum_cmptor_f(const char *, const char *); 260 261 static unum_cmptor_f *const unum_cmptors[] = { 262 unum_contains_bypat, 263 unum_contains_bysubstr 264 }; 265 266 int 267 mem_unum_contains(const char *erunum, const char *eeunum) 268 { 269 static int cmptor = 0; 270 int rc; 271 272 while (isspace(*erunum)) 273 erunum++; 274 while (isspace(*eeunum)) 275 eeunum++; 276 277 if ((rc = unum_cmptors[cmptor](erunum, eeunum)) >= 0) 278 return (rc); 279 280 if ((rc = unum_cmptors[cmptor == 0](erunum, eeunum)) >= 0) { 281 /* 282 * We succeeded with the non-default comparator. Change the 283 * default so we use the correct one next time. 284 */ 285 cmptor = (cmptor == 0); 286 } 287 288 return (rc); 289 } 290