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 2008 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 #include <fm/libtopo.h> 32 33 #include <string.h> 34 #include <strings.h> 35 #include <sys/mem.h> 36 37 mem_t mem; 38 39 static int 40 mem_fmri_get_unum(nvlist_t *nvl, char **unump) 41 { 42 uint8_t version; 43 char *unum; 44 45 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 46 version > FM_MEM_SCHEME_VERSION || 47 nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0) 48 return (fmd_fmri_set_errno(EINVAL)); 49 50 *unump = unum; 51 52 return (0); 53 } 54 55 ssize_t 56 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 57 { 58 char format[64]; 59 ssize_t size, presz; 60 char *rawunum, *preunum, *escunum, *prefix; 61 uint64_t val; 62 int i; 63 64 if (mem_fmri_get_unum(nvl, &rawunum) < 0) 65 return (-1); /* errno is set for us */ 66 67 /* 68 * If we have a well-formed unum (hc-FMRI), use the string verbatim 69 * to form the initial mem:/// components. Otherwise use unum=%s. 70 */ 71 if (strncmp(rawunum, "hc://", 5) != 0) 72 prefix = FM_FMRI_MEM_UNUM "="; 73 else 74 prefix = ""; 75 76 /* 77 * If we have a DIMM offset, include it in the string. If we have a PA 78 * then use that. Otherwise just format the unum element. 79 */ 80 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) { 81 (void) snprintf(format, sizeof (format), 82 "%s:///%s%%1$s/%s=%%2$llx", 83 FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET); 84 } else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &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_PHYSADDR); 88 } else { 89 (void) snprintf(format, sizeof (format), 90 "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix); 91 } 92 93 /* 94 * If we have a well-formed unum (hc-FMRI), we skip over the 95 * the scheme and authority prefix. 96 * Otherwise, the spaces and colons will be escaped, 97 * rendering the resulting FMRI pretty much unreadable. 98 * We're therefore going to do some escaping of our own first. 99 */ 100 if (strncmp(rawunum, "hc://", 5) == 0) { 101 rawunum += 5; 102 rawunum = strchr(rawunum, '/'); 103 ++rawunum; 104 /* LINTED: variable format specifier */ 105 size = snprintf(buf, buflen, format, rawunum, val); 106 } else { 107 preunum = fmd_fmri_strdup(rawunum); 108 presz = strlen(preunum) + 1; 109 110 for (i = 0; i < presz - 1; i++) { 111 if (preunum[i] == ':' && preunum[i + 1] == ' ') { 112 bcopy(preunum + i + 2, preunum + i + 1, 113 presz - (i + 2)); 114 } else if (preunum[i] == ' ') { 115 preunum[i] = ','; 116 } 117 } 118 119 escunum = fmd_fmri_strescape(preunum); 120 fmd_fmri_free(preunum, presz); 121 122 /* LINTED: variable format specifier */ 123 size = snprintf(buf, buflen, format, escunum, val); 124 fmd_fmri_strfree(escunum); 125 } 126 127 return (size); 128 } 129 130 int 131 fmd_fmri_expand(nvlist_t *nvl) 132 { 133 char *unum, **serids; 134 uint_t nnvlserids; 135 size_t nserids; 136 int rc, err = 0; 137 topo_hdl_t *thp; 138 139 if ((mem_fmri_get_unum(nvl, &unum) < 0) || (*unum == '\0')) 140 return (fmd_fmri_set_errno(EINVAL)); 141 142 /* 143 * If the mem-scheme topology exports this method expand(), invoke it. 144 */ 145 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 146 return (fmd_fmri_set_errno(EINVAL)); 147 rc = topo_fmri_expand(thp, nvl, &err); 148 fmd_fmri_topo_rele(thp); 149 if (err != ETOPO_METHOD_NOTSUP) 150 return (rc); 151 152 if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, 153 &serids, &nnvlserids)) == 0) { /* already have serial #s */ 154 return (0); 155 } else if (rc != ENOENT) 156 return (fmd_fmri_set_errno(EINVAL)); 157 158 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 159 /* errno is set for us */ 160 if (errno == ENOTSUP) 161 return (0); /* nothing to add - no s/n support */ 162 else 163 return (-1); 164 } 165 166 rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids, 167 nserids); 168 169 mem_strarray_free(serids, nserids); 170 171 if (rc != 0) 172 return (fmd_fmri_set_errno(EINVAL)); 173 else 174 return (0); 175 } 176 177 #ifdef sparc 178 static int 179 serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2) 180 { 181 int i; 182 183 if (nserids1 != nserids2) 184 return (0); 185 186 for (i = 0; i < nserids1; i++) { 187 if (strcmp(serids1[i], serids2[i]) != 0) 188 return (0); 189 } 190 191 return (1); 192 } 193 #endif /* sparc */ 194 195 int 196 fmd_fmri_present(nvlist_t *nvl) 197 { 198 char *unum = NULL; 199 int rc, err = 0; 200 struct topo_hdl *thp; 201 #ifdef sparc 202 char **nvlserids, **serids; 203 uint_t nnvlserids; 204 size_t nserids; 205 #else 206 nvlist_t *unum_nvl; 207 #endif /* sparc */ 208 209 if (mem_fmri_get_unum(nvl, &unum) < 0) 210 return (-1); /* errno is set for us */ 211 212 #ifdef sparc 213 /* 214 * If the mem-scheme topology exports this method present(), invoke it. 215 */ 216 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 217 return (fmd_fmri_set_errno(EINVAL)); 218 rc = topo_fmri_present(thp, nvl, &err); 219 fmd_fmri_topo_rele(thp); 220 if (err != ETOPO_METHOD_NOTSUP) 221 return (rc); 222 223 if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, 224 &nnvlserids) != 0) { 225 /* 226 * Some mem scheme FMRIs don't have serial ids because 227 * either the platform does not support them, or because 228 * the FMRI was created before support for serial ids was 229 * introduced. If this is the case, assume it is there. 230 */ 231 if (mem.mem_dm == NULL) 232 return (1); 233 else 234 return (fmd_fmri_set_errno(EINVAL)); 235 } 236 237 if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { 238 if (errno == ENOTSUP) 239 return (1); /* assume it's there, no s/n support here */ 240 if (errno != ENOENT) { 241 /* 242 * Errors are only signalled to the caller if they're 243 * the caller's fault. This isn't - it's a failure on 244 * our part to burst or read the serial numbers. We'll 245 * whine about it, and tell the caller the named 246 * module(s) isn't/aren't there. 247 */ 248 fmd_fmri_warn("failed to retrieve serial number for " 249 "unum %s", unum); 250 } 251 return (0); 252 } 253 254 rc = serids_eq(serids, nserids, nvlserids, nnvlserids); 255 256 mem_strarray_free(serids, nserids); 257 #else 258 /* 259 * On X86 we will invoke the topo is_present method passing in the 260 * unum, which is in hc scheme. The libtopo hc-scheme is_present method 261 * will invoke the node-specific is_present method, which is implemented 262 * by the chip enumerator for rank nodes. The rank node's is_present 263 * method will compare the serial number in the unum with the current 264 * serial to determine if the same DIMM is present. 265 */ 266 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) { 267 fmd_fmri_warn("failed to get handle to topology"); 268 return (-1); 269 } 270 if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) { 271 rc = topo_fmri_present(thp, unum_nvl, &err); 272 nvlist_free(unum_nvl); 273 } else 274 rc = fmd_fmri_set_errno(EINVAL); 275 fmd_fmri_topo_rele(thp); 276 277 #endif /* sparc */ 278 return (rc); 279 } 280 281 int 282 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) 283 { 284 int rc, err = 0; 285 struct topo_hdl *thp; 286 char *erunum, *eeunum; 287 uint64_t erval = 0, eeval = 0; 288 289 /* 290 * If the mem-scheme topology exports this method contains(), invoke it. 291 */ 292 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 293 return (fmd_fmri_set_errno(EINVAL)); 294 rc = topo_fmri_contains(thp, er, ee, &err); 295 fmd_fmri_topo_rele(thp); 296 if (err != ETOPO_METHOD_NOTSUP) 297 return (rc); 298 299 if (mem_fmri_get_unum(er, &erunum) < 0 || 300 mem_fmri_get_unum(ee, &eeunum) < 0) 301 return (-1); /* errno is set for us */ 302 303 if (mem_unum_contains(erunum, eeunum) <= 0) 304 return (0); /* can't parse/match, so assume no containment */ 305 306 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) { 307 return (nvlist_lookup_uint64(ee, 308 FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval); 309 } 310 311 if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) { 312 return (nvlist_lookup_uint64(ee, 313 FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval); 314 } 315 316 return (1); 317 } 318 319 /* 320 * We can only make a usable/unusable determination for pages. Mem FMRIs 321 * without page addresses will be reported as usable since Solaris has no 322 * way at present to dynamically disable an entire DIMM or DIMM pair. 323 */ 324 int 325 fmd_fmri_unusable(nvlist_t *nvl) 326 { 327 uint64_t val; 328 uint8_t version; 329 int rc, err1 = 0, err2; 330 nvlist_t *nvlcp = NULL; 331 int retval; 332 topo_hdl_t *thp; 333 334 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 335 version > FM_MEM_SCHEME_VERSION) 336 return (fmd_fmri_set_errno(EINVAL)); 337 338 /* 339 * If the mem-scheme topology exports this method unusable(), invoke it. 340 */ 341 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 342 return (fmd_fmri_set_errno(EINVAL)); 343 rc = topo_fmri_unusable(thp, nvl, &err1); 344 fmd_fmri_topo_rele(thp); 345 if (err1 != ETOPO_METHOD_NOTSUP) 346 return (rc); 347 348 err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val); 349 err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val); 350 351 if (err1 == ENOENT && err2 == ENOENT) 352 return (0); /* no page, so assume it's still usable */ 353 354 if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT)) 355 return (fmd_fmri_set_errno(EINVAL)); 356 357 if ((err1 = mem_unum_rewrite(nvl, &nvlcp)) != 0) 358 return (fmd_fmri_set_errno(err1)); 359 360 /* 361 * Ask the kernel if the page is retired, using either the rewritten 362 * hc FMRI or the original mem FMRI with the specified offset or PA. 363 * Refer to the kernel's page_retire_check() for the error codes. 364 */ 365 rc = mem_page_cmd(MEM_PAGE_FMRI_ISRETIRED, nvlcp ? nvlcp : nvl); 366 367 if (rc == -1 && errno == EIO) { 368 /* 369 * The page is not retired and is not scheduled for retirement 370 * (i.e. no request pending and has not seen any errors) 371 */ 372 retval = 0; 373 } else if (rc == 0 || errno == EAGAIN || errno == EINVAL) { 374 /* 375 * The page has been retired, is in the process of being 376 * retired, or doesn't exist. The latter is valid if the page 377 * existed in the past but has been DR'd out. 378 */ 379 retval = 1; 380 } else { 381 /* 382 * Errors are only signalled to the caller if they're the 383 * caller's fault. This isn't - it's a failure of the 384 * retirement-check code. We'll whine about it and tell 385 * the caller the page is unusable. 386 */ 387 fmd_fmri_warn("failed to determine page %s=%llx usability: " 388 "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET : 389 FM_FMRI_MEM_PHYSADDR, (u_longlong_t)val, rc, errno); 390 retval = 1; 391 } 392 393 if (nvlcp) 394 nvlist_free(nvlcp); 395 396 return (retval); 397 } 398 399 int 400 fmd_fmri_init(void) 401 { 402 return (mem_discover()); 403 } 404 405 void 406 fmd_fmri_fini(void) 407 { 408 mem_dimm_map_t *dm, *em; 409 mem_bank_map_t *bm, *cm; 410 mem_grp_t *gm, *hm; 411 mem_seg_map_t *sm, *tm; 412 413 for (dm = mem.mem_dm; dm != NULL; dm = em) { 414 em = dm->dm_next; 415 fmd_fmri_strfree(dm->dm_label); 416 fmd_fmri_strfree(dm->dm_part); 417 fmd_fmri_strfree(dm->dm_device); 418 fmd_fmri_free(dm, sizeof (mem_dimm_map_t)); 419 } 420 for (bm = mem.mem_bank; bm != NULL; bm = cm) { 421 cm = bm->bm_next; 422 fmd_fmri_free(bm, sizeof (mem_bank_map_t)); 423 } 424 for (gm = mem.mem_group; gm != NULL; gm = hm) { 425 hm = gm->mg_next; 426 fmd_fmri_free(gm, sizeof (mem_grp_t)); 427 } 428 for (sm = mem.mem_seg; sm != NULL; sm = tm) { 429 tm = sm->sm_next; 430 fmd_fmri_free(sm, sizeof (mem_seg_map_t)); 431 } 432 } 433