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 uint64_t memconfig; 222 int rc; 223 224 if (mem.mem_dm == NULL) 225 return (1); /* assume it's there - no s/n support here */ 226 227 if (mem_fmri_get_unum(nvl, &unum) < 0) 228 return (-1); /* errno is set for us */ 229 230 if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, 231 &nnvlserids) != 0) 232 return (fmd_fmri_set_errno(EINVAL)); 233 234 /* 235 * Hypervisor will change the memconfig value when the mapping of 236 * pages to DIMMs changes, e.g. for change in DIMM size or interleave. 237 * If we detect such a change, we discard ereports associated with a 238 * previous memconfig value as invalid. 239 */ 240 241 if ((nvlist_lookup_uint64(nvl, FM_FMRI_MEM_MEMCONFIG, 242 &memconfig) == 0) && memconfig != mem.mem_memconfig) 243 return (0); 244 245 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 246 if (errno != ENOENT) { 247 /* 248 * Errors are only signalled to the caller if they're 249 * the caller's fault. This isn't - it's a failure on 250 * our part to burst or read the serial numbers. We'll 251 * whine about it, and tell the caller the named 252 * module(s) isn't/aren't there. 253 */ 254 fmd_fmri_warn("failed to retrieve serial number for " 255 "unum %s", unum); 256 } 257 return (0); 258 } 259 260 rc = serids_eq(serids, nserids, nvlserids, nnvlserids); 261 262 mem_strarray_free(serids, nserids); 263 264 return (rc); 265 } 266 267 int 268 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) 269 { 270 char *erunum, *eeunum; 271 uint64_t erpa = 0, eepa = 0; 272 273 if (mem_fmri_get_unum(er, &erunum) < 0 || 274 mem_fmri_get_unum(ee, &eeunum) < 0) 275 return (-1); /* errno is set for us */ 276 277 if (mem_unum_contains(erunum, eeunum) <= 0) 278 return (0); /* can't parse/match, so assume no containment */ 279 280 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erpa) == 0) { 281 /* container has a PA; only match if containee has same PA */ 282 return (nvlist_lookup_uint64(ee, FM_FMRI_MEM_PHYSADDR, 283 &eepa) == 0 && erpa == eepa); 284 } 285 286 return (1); 287 } 288 289 int 290 fmd_fmri_unusable(nvlist_t *nvl) 291 { 292 uint64_t pageaddr; 293 uint8_t version; 294 int rc, err; 295 296 /* 297 * We can only make a usable/unusable determination for pages. FMRIs 298 * without page addresses will be reported as usable. 299 */ 300 301 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 302 version > FM_MEM_SCHEME_VERSION) 303 return (fmd_fmri_set_errno(EINVAL)); 304 305 if ((err = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, 306 &pageaddr)) == ENOENT) 307 return (0); /* no page, so assume it's still usable */ 308 else if (err != 0) 309 return (fmd_fmri_set_errno(EINVAL)); 310 311 if ((rc = mem_page_cmd(MEM_PAGE_ISRETIRED, pageaddr)) < 0 && 312 errno == EIO) { 313 return (0); /* the page wonders, "why all the fuss?" */ 314 } else if (rc == 0 || errno == EAGAIN || errno == EINVAL) { 315 /* 316 * The page has been retired, is in the process of being 317 * retired, or doesn't exist. The latter is valid if the page 318 * existed in the past but has been DR'd out. 319 */ 320 return (1); 321 } else { 322 /* 323 * Errors are only signalled to the caller if they're the 324 * caller's fault. This isn't - it's a failure of the 325 * retirement-check code. We'll whine about it and tell 326 * the caller the page is unusable. 327 */ 328 fmd_fmri_warn("failed to determine usability of page %llx", 329 pageaddr); 330 return (1); 331 } 332 } 333 334 int 335 fmd_fmri_init(void) 336 { 337 bzero(&mem, sizeof (mem_t)); 338 return (mem_discover()); 339 } 340 341 void 342 fmd_fmri_fini(void) 343 { 344 mem_destroy(); 345 } 346