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