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 <sys/mem.h> 35 36 /* 37 * The scheme plugin for mem FMRIs. 38 */ 39 40 mem_t mem; 41 42 static mem_dimm_map_t * 43 dm_lookup(const char *name) 44 { 45 mem_dimm_map_t *dm; 46 47 for (dm = mem.mem_dm; dm != NULL; dm = dm->dm_next) { 48 if (strcmp(name, dm->dm_label) == 0) 49 return (dm); 50 } 51 52 return (NULL); 53 } 54 55 /* 56 * Returns 0 with serial numbers if found, -1 (with errno set) for errors. If 57 * the unum (or a component of same) wasn't found, -1 is returned with errno 58 * set to ENOENT. 59 */ 60 static int 61 mem_get_serids_by_unum(const char *unum, char ***seridsp, size_t *nseridsp) 62 { 63 uint64_t drgen = fmd_fmri_get_drgen(); 64 char **dimms, **serids; 65 size_t ndimms, nserids; 66 mem_dimm_map_t *dm; 67 int i, rc = 0; 68 69 if (mem_unum_burst(unum, &dimms, &ndimms) < 0) 70 return (-1); /* errno is set for us */ 71 72 serids = fmd_fmri_zalloc(sizeof (char *) * ndimms); 73 nserids = ndimms; 74 75 for (i = 0; i < ndimms; i++) { 76 if ((dm = dm_lookup(dimms[i])) == NULL) { 77 rc = fmd_fmri_set_errno(EINVAL); 78 break; 79 } 80 81 if (*dm->dm_serid == '\0' || dm->dm_drgen != drgen) { 82 /* 83 * We don't have a cached copy, or the copy we've got is 84 * out of date. Look it up again. 85 */ 86 if (mem_get_serid(dm->dm_device, dm->dm_serid, 87 sizeof (dm->dm_serid)) < 0) { 88 rc = -1; /* errno is set for us */ 89 break; 90 } 91 92 dm->dm_drgen = drgen; 93 } 94 95 serids[i] = fmd_fmri_strdup(dm->dm_serid); 96 } 97 98 mem_strarray_free(dimms, ndimms); 99 100 if (i == ndimms) { 101 *seridsp = serids; 102 *nseridsp = nserids; 103 } else { 104 mem_strarray_free(serids, nserids); 105 } 106 107 return (rc); 108 } 109 110 static int 111 mem_fmri_get_unum(nvlist_t *nvl, char **unump) 112 { 113 uint8_t version; 114 char *unum; 115 116 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 117 version > FM_MEM_SCHEME_VERSION || 118 nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0) 119 return (fmd_fmri_set_errno(EINVAL)); 120 121 *unump = unum; 122 123 return (0); 124 } 125 126 ssize_t 127 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 128 { 129 const char *fmt = "mem:///component=%1$s"; 130 ssize_t size, presz; 131 uint64_t pa; 132 char *rawunum, *preunum, *escunum; 133 int i; 134 135 if (mem_fmri_get_unum(nvl, &rawunum) < 0) 136 return (-1); /* errno is set for us */ 137 138 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &pa) == 0) 139 fmt = "mem:///pa=%2$llx/component=%1$s"; 140 141 /* 142 * If we leave the unum as-is, the spaces and colons will be escaped, 143 * rendering the resulting FMRI pretty much unreadable. We're therefore 144 * going to do some escaping of our own first. 145 */ 146 preunum = fmd_fmri_strdup(rawunum); 147 presz = strlen(preunum) + 1; 148 149 for (i = 0; i < presz - 1; i++) { 150 if (preunum[i] == ':' && preunum[i + 1] == ' ') { 151 bcopy(preunum + i + 2, preunum + i + 1, 152 presz - (i + 2)); 153 } else if (preunum[i] == ' ') { 154 preunum[i] = ','; 155 } 156 } 157 158 escunum = fmd_fmri_strescape(preunum); 159 fmd_fmri_free(preunum, presz); 160 161 size = snprintf(buf, buflen, fmt, escunum, (u_longlong_t)pa); 162 fmd_fmri_strfree(escunum); 163 164 return (size); 165 } 166 167 int 168 fmd_fmri_expand(nvlist_t *nvl) 169 { 170 char *unum, **serids; 171 uint_t nserids; 172 int rc; 173 174 if (mem.mem_dm == NULL) 175 return (0); /* nothing to add - no s/n support here */ 176 177 if (mem_fmri_get_unum(nvl, &unum) < 0) 178 return (fmd_fmri_set_errno(EINVAL)); 179 180 if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, 181 &serids, &nserids)) == 0) 182 return (0); /* fmri is already expanded */ 183 else if (rc != ENOENT) 184 return (fmd_fmri_set_errno(EINVAL)); 185 186 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) 187 return (-1); /* errno is set for us */ 188 189 rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids, 190 nserids); 191 192 mem_strarray_free(serids, nserids); 193 194 if (rc != 0) 195 return (fmd_fmri_set_errno(EINVAL)); 196 197 return (0); 198 } 199 200 static int 201 serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2) 202 { 203 int i; 204 205 if (nserids1 != nserids2) 206 return (0); 207 208 for (i = 0; i < nserids1; i++) { 209 if (strcmp(serids1[i], serids2[i]) != 0) 210 return (0); 211 } 212 213 return (1); 214 } 215 216 int 217 fmd_fmri_present(nvlist_t *nvl) 218 { 219 char *unum, **nvlserids, **serids; 220 uint_t nnvlserids, nserids; 221 int rc; 222 223 if (mem.mem_dm == NULL) 224 return (1); /* assume it's there - no s/n support here */ 225 226 if (mem_fmri_get_unum(nvl, &unum) < 0) 227 return (-1); /* errno is set for us */ 228 229 if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, 230 &nnvlserids) != 0) 231 return (fmd_fmri_set_errno(EINVAL)); 232 233 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 234 if (errno != ENOENT) { 235 /* 236 * Errors are only signalled to the caller if they're 237 * the caller's fault. This isn't - it's a failure on 238 * our part to burst or read the serial numbers. We'll 239 * whine about it, and tell the caller the named 240 * module(s) isn't/aren't there. 241 */ 242 fmd_fmri_warn("failed to retrieve serial number for " 243 "unum %s", unum); 244 } 245 return (0); 246 } 247 248 rc = serids_eq(serids, nserids, nvlserids, nnvlserids); 249 250 mem_strarray_free(serids, nserids); 251 252 return (rc); 253 } 254 255 int 256 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) 257 { 258 char *erunum, *eeunum; 259 uint64_t erpa = 0, eepa = 0; 260 261 if (mem_fmri_get_unum(er, &erunum) < 0 || 262 mem_fmri_get_unum(ee, &eeunum) < 0) 263 return (-1); /* errno is set for us */ 264 265 if (mem_unum_contains(erunum, eeunum) <= 0) 266 return (0); /* can't parse/match, so assume no containment */ 267 268 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erpa) == 0) { 269 /* container has a PA; only match if containee has same PA */ 270 return (nvlist_lookup_uint64(ee, FM_FMRI_MEM_PHYSADDR, 271 &eepa) == 0 && erpa == eepa); 272 } 273 274 return (1); 275 } 276 277 int 278 fmd_fmri_unusable(nvlist_t *nvl) 279 { 280 uint64_t pageaddr; 281 uint8_t version; 282 int rc, err; 283 284 /* 285 * We can only make a usable/unusable determination for pages. FMRIs 286 * without page addresses will be reported as usable. 287 */ 288 289 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 290 version > FM_MEM_SCHEME_VERSION) 291 return (fmd_fmri_set_errno(EINVAL)); 292 293 if ((err = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 294 &pageaddr)) == ENOENT) 295 return (0); /* no page, so assume it's still usable */ 296 else if (err != 0) 297 return (fmd_fmri_set_errno(EINVAL)); 298 299 if ((rc = mem_page_cmd(MEM_PAGE_ISRETIRED, pageaddr)) < 0 && 300 errno == EIO) { 301 return (0); /* the page wonders, "why all the fuss?" */ 302 } else if (rc == 0 || errno == EAGAIN || errno == EINVAL) { 303 /* 304 * The page has been retired, is in the process of being 305 * retired, or doesn't exist. The latter is valid if the page 306 * existed in the past but has been DR'd out. 307 */ 308 return (1); 309 } else { 310 /* 311 * Errors are only signalled to the caller if they're the 312 * caller's fault. This isn't - it's a failure of the 313 * retirement-check code. We'll whine about it and tell 314 * the caller the page is unusable. 315 */ 316 fmd_fmri_warn("failed to determine usability of page %llx", 317 pageaddr); 318 return (1); 319 } 320 } 321 322 int 323 fmd_fmri_init(void) 324 { 325 bzero(&mem, sizeof (mem_t)); 326 return (mem_discover()); 327 } 328 329 void 330 fmd_fmri_fini(void) 331 { 332 mem_destroy(); 333 } 334