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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 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 #ifdef sparc 37 #include <sys/fm/ldom.h> 38 ldom_hdl_t *mem_scheme_lhp; 39 #endif /* sparc */ 40 41 mem_t mem; 42 43 static int 44 mem_fmri_get_unum(nvlist_t *nvl, char **unump) 45 { 46 uint8_t version; 47 char *unum; 48 49 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 50 version > FM_MEM_SCHEME_VERSION || 51 nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0) 52 return (fmd_fmri_set_errno(EINVAL)); 53 54 *unump = unum; 55 56 return (0); 57 } 58 59 ssize_t 60 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 61 { 62 char format[64]; 63 ssize_t size, presz; 64 char *rawunum, *preunum, *escunum, *prefix; 65 uint64_t val; 66 int i; 67 68 if (mem_fmri_get_unum(nvl, &rawunum) < 0) 69 return (-1); /* errno is set for us */ 70 71 /* 72 * If we have a well-formed unum (hc-FMRI), use the string verbatim 73 * to form the initial mem:/// components. Otherwise use unum=%s. 74 */ 75 if (strncmp(rawunum, "hc://", 5) != 0) 76 prefix = FM_FMRI_MEM_UNUM "="; 77 else 78 prefix = ""; 79 80 /* 81 * If we have a DIMM offset, include it in the string. If we have a PA 82 * then use that. Otherwise just format the unum element. 83 */ 84 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) { 85 (void) snprintf(format, sizeof (format), 86 "%s:///%s%%1$s/%s=%%2$llx", 87 FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET); 88 } else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) { 89 (void) snprintf(format, sizeof (format), 90 "%s:///%s%%1$s/%s=%%2$llx", 91 FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_PHYSADDR); 92 } else { 93 (void) snprintf(format, sizeof (format), 94 "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix); 95 } 96 97 /* 98 * If we have a well-formed unum (hc-FMRI), we skip over the 99 * the scheme and authority prefix. 100 * Otherwise, the spaces and colons will be escaped, 101 * rendering the resulting FMRI pretty much unreadable. 102 * We're therefore going to do some escaping of our own first. 103 */ 104 if (strncmp(rawunum, "hc://", 5) == 0) { 105 rawunum += 5; 106 rawunum = strchr(rawunum, '/'); 107 ++rawunum; 108 /* LINTED: variable format specifier */ 109 size = snprintf(buf, buflen, format, rawunum, val); 110 } else { 111 preunum = fmd_fmri_strdup(rawunum); 112 presz = strlen(preunum) + 1; 113 114 for (i = 0; i < presz - 1; i++) { 115 if (preunum[i] == ':' && preunum[i + 1] == ' ') { 116 bcopy(preunum + i + 2, preunum + i + 1, 117 presz - (i + 2)); 118 } else if (preunum[i] == ' ') { 119 preunum[i] = ','; 120 } 121 } 122 123 escunum = fmd_fmri_strescape(preunum); 124 fmd_fmri_free(preunum, presz); 125 126 /* LINTED: variable format specifier */ 127 size = snprintf(buf, buflen, format, escunum, val); 128 fmd_fmri_strfree(escunum); 129 } 130 131 return (size); 132 } 133 134 int 135 fmd_fmri_expand(nvlist_t *nvl) 136 { 137 char *unum, **serids; 138 uint_t nnvlserids; 139 size_t nserids; 140 int rc; 141 142 if ((mem_fmri_get_unum(nvl, &unum) < 0) || (*unum == '\0')) 143 return (fmd_fmri_set_errno(EINVAL)); 144 145 if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, 146 &serids, &nnvlserids)) == 0) { /* already have serial #s */ 147 mem_expand_opt(nvl, unum, serids); 148 return (0); 149 } else if (rc != ENOENT) 150 return (fmd_fmri_set_errno(EINVAL)); 151 152 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 153 /* errno is set for us */ 154 if (errno == ENOTSUP) 155 return (0); /* nothing to add - no s/n support */ 156 else 157 return (-1); 158 } 159 160 rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids, 161 nserids); 162 mem_expand_opt(nvl, unum, serids); 163 164 mem_strarray_free(serids, nserids); 165 166 if (rc != 0) 167 return (fmd_fmri_set_errno(EINVAL)); 168 else 169 return (0); 170 } 171 172 static int 173 serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2) 174 { 175 int i; 176 177 if (nserids1 != nserids2) 178 return (0); 179 180 for (i = 0; i < nserids1; i++) { 181 if (strcmp(serids1[i], serids2[i]) != 0) 182 return (0); 183 } 184 185 return (1); 186 } 187 188 int 189 fmd_fmri_present(nvlist_t *nvl) 190 { 191 char *unum, **nvlserids, **serids; 192 uint_t nnvlserids; 193 size_t nserids; 194 uint64_t memconfig; 195 int rc; 196 197 if (mem_fmri_get_unum(nvl, &unum) < 0) 198 return (-1); /* errno is set for us */ 199 200 if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, 201 &nnvlserids) != 0) { 202 /* 203 * Some mem scheme FMRIs don't have serial ids because 204 * either the platform does not support them, or because 205 * the FMRI was created before support for serial ids was 206 * introduced. If this is the case, assume it is there. 207 */ 208 if (mem.mem_dm == NULL) 209 return (1); 210 else 211 return (fmd_fmri_set_errno(EINVAL)); 212 } 213 214 /* 215 * Hypervisor will change the memconfig value when the mapping of 216 * pages to DIMMs changes, e.g. for change in DIMM size or interleave. 217 * If we detect such a change, we discard ereports associated with a 218 * previous memconfig value as invalid. 219 * 220 * The test (mem.mem_memconfig != 0) means we run on a system that 221 * actually suplies a memconfig value. 222 */ 223 224 if ((nvlist_lookup_uint64(nvl, FM_FMRI_MEM_MEMCONFIG, 225 &memconfig) == 0) && (mem.mem_memconfig != 0) && 226 (memconfig != mem.mem_memconfig)) 227 return (0); 228 229 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 230 if (errno == ENOTSUP) 231 return (1); /* assume it's there, no s/n support here */ 232 if (errno != ENOENT) { 233 /* 234 * Errors are only signalled to the caller if they're 235 * the caller's fault. This isn't - it's a failure on 236 * our part to burst or read the serial numbers. We'll 237 * whine about it, and tell the caller the named 238 * module(s) isn't/aren't there. 239 */ 240 fmd_fmri_warn("failed to retrieve serial number for " 241 "unum %s", unum); 242 } 243 return (0); 244 } 245 246 rc = serids_eq(serids, nserids, nvlserids, nnvlserids); 247 248 mem_strarray_free(serids, nserids); 249 250 return (rc); 251 } 252 253 int 254 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) 255 { 256 char *erunum, *eeunum; 257 uint64_t erval = 0, eeval = 0; 258 259 if (mem_fmri_get_unum(er, &erunum) < 0 || 260 mem_fmri_get_unum(ee, &eeunum) < 0) 261 return (-1); /* errno is set for us */ 262 263 if (mem_unum_contains(erunum, eeunum) <= 0) 264 return (0); /* can't parse/match, so assume no containment */ 265 266 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) { 267 return (nvlist_lookup_uint64(ee, 268 FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval); 269 } 270 271 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) { 272 return (nvlist_lookup_uint64(ee, 273 FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval); 274 } 275 276 return (1); 277 } 278 279 /* 280 * We can only make a usable/unusable determination for pages. Mem FMRIs 281 * without page addresses will be reported as usable since Solaris has no 282 * way at present to dynamically disable an entire DIMM or DIMM pair. 283 */ 284 int 285 fmd_fmri_unusable(nvlist_t *nvl) 286 { 287 uint64_t val; 288 uint8_t version; 289 int rc, err1, err2; 290 nvlist_t *nvlcp = NULL; 291 int retval; 292 293 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 294 version > FM_MEM_SCHEME_VERSION) 295 return (fmd_fmri_set_errno(EINVAL)); 296 297 err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val); 298 err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val); 299 300 if (err1 == ENOENT && err2 == ENOENT) 301 return (0); /* no page, so assume it's still usable */ 302 303 if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT)) 304 return (fmd_fmri_set_errno(EINVAL)); 305 306 if ((err1 = mem_unum_rewrite(nvl, &nvlcp)) != 0) 307 return (fmd_fmri_set_errno(err1)); 308 309 /* 310 * Ask the kernel if the page is retired, using either the rewritten 311 * hc FMRI or the original mem FMRI with the specified offset or PA. 312 * Refer to the kernel's page_retire_check() for the error codes. 313 */ 314 rc = mem_page_cmd(MEM_PAGE_FMRI_ISRETIRED, nvlcp ? nvlcp : nvl); 315 316 if (rc == -1 && errno == EIO) { 317 /* 318 * The page is not retired and is not scheduled for retirement 319 * (i.e. no request pending and has not seen any errors) 320 */ 321 retval = 0; 322 } else if (rc == 0 || errno == EAGAIN || errno == EINVAL) { 323 /* 324 * The page has been retired, is in the process of being 325 * retired, or doesn't exist. The latter is valid if the page 326 * existed in the past but has been DR'd out. 327 */ 328 retval = 1; 329 } else { 330 /* 331 * Errors are only signalled to the caller if they're the 332 * caller's fault. This isn't - it's a failure of the 333 * retirement-check code. We'll whine about it and tell 334 * the caller the page is unusable. 335 */ 336 fmd_fmri_warn("failed to determine page %s=%llx usability: " 337 "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET : 338 FM_FMRI_MEM_PHYSADDR, (u_longlong_t)val, rc, errno); 339 retval = 1; 340 } 341 342 if (nvlcp) 343 nvlist_free(nvlcp); 344 345 return (retval); 346 } 347 348 int 349 fmd_fmri_init(void) 350 { 351 #ifdef sparc 352 mem_scheme_lhp = ldom_init(fmd_fmri_alloc, fmd_fmri_free); 353 #endif /* sparc */ 354 return (mem_discover()); 355 } 356 357 void 358 fmd_fmri_fini(void) 359 { 360 mem_dimm_map_t *dm, *em; 361 mem_seg_map_t *sm, *tm; 362 363 for (dm = mem.mem_dm; dm != NULL; dm = em) { 364 em = dm->dm_next; 365 fmd_fmri_strfree(dm->dm_label); 366 fmd_fmri_strfree(dm->dm_part); 367 fmd_fmri_strfree(dm->dm_device); 368 fmd_fmri_free(dm, sizeof (mem_dimm_map_t)); 369 } 370 for (sm = mem.mem_seg; sm != NULL; sm = tm) { 371 tm = sm->sm_next; 372 fmd_fmri_free(sm, sizeof (mem_seg_map_t)); 373 } 374 #ifdef sparc 375 ldom_fini(mem_scheme_lhp); 376 #endif /* sparc */ 377 } 378