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 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 26 /* 27 * Support routines for DIMMs. 28 */ 29 30 #include <gmem_mem.h> 31 #include <gmem_dimm.h> 32 #include <gmem.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #include <fm/fmd_api.h> 40 #include <fm/libtopo.h> 41 #include <sys/fm/protocol.h> 42 #include <sys/mem.h> 43 #include <sys/nvpair.h> 44 45 nvlist_t *dimm_nvl; 46 47 typedef struct dimmid { 48 char serial[100]; 49 int type; 50 } dimmid_t; 51 52 nvlist_t * 53 gmem_dimm_fru(gmem_dimm_t *dimm) 54 { 55 return (dimm->dimm_asru_nvl); 56 } 57 58 static void 59 gmem_dimm_free(fmd_hdl_t *hdl, gmem_dimm_t *dimm, int destroy) 60 { 61 gmem_case_t *cc = &dimm->dimm_case; 62 int i; 63 gmem_mq_t *q; 64 tstamp_t *tsp, *next; 65 66 if (cc->cc_cp != NULL) { 67 gmem_case_fini(hdl, cc->cc_cp, destroy); 68 if (cc->cc_serdnm != NULL) { 69 if (fmd_serd_exists(hdl, cc->cc_serdnm) && 70 destroy) 71 fmd_serd_destroy(hdl, cc->cc_serdnm); 72 fmd_hdl_strfree(hdl, cc->cc_serdnm); 73 } 74 } 75 76 gmem_fmri_fini(hdl, &dimm->dimm_asru, destroy); 77 78 for (i = 0; i < GMEM_MAX_CKWDS; i++) { 79 while ((q = gmem_list_next(&dimm->mq_root[i])) != NULL) { 80 if (q->mq_serdnm != NULL) { 81 if (fmd_serd_exists(hdl, q->mq_serdnm)) 82 fmd_serd_destroy(hdl, q->mq_serdnm); 83 fmd_hdl_strfree(hdl, q->mq_serdnm); 84 q->mq_serdnm = NULL; 85 } 86 87 for (tsp = gmem_list_next(&q->mq_dupce_tstamp); 88 tsp != NULL; tsp = next) { 89 next = gmem_list_next(tsp); 90 gmem_list_delete(&q->mq_dupce_tstamp, 91 &tsp->ts_l); 92 fmd_hdl_free(hdl, tsp, sizeof (tstamp_t)); 93 } 94 95 gmem_list_delete(&dimm->mq_root[i], q); 96 fmd_hdl_free(hdl, q, sizeof (gmem_mq_t)); 97 } 98 } 99 100 if (destroy) 101 fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname); 102 103 gmem_list_delete(&gmem.gm_dimms, dimm); 104 fmd_hdl_free(hdl, dimm, sizeof (gmem_dimm_t)); 105 } 106 107 void 108 gmem_dimm_destroy(fmd_hdl_t *hdl, gmem_dimm_t *dimm) 109 { 110 fmd_stat_destroy(hdl, 1, &(dimm->dimm_retstat)); 111 gmem_dimm_free(hdl, dimm, FMD_B_TRUE); 112 } 113 114 static gmem_dimm_t * 115 dimm_lookup_by_serial(const char *serial) 116 { 117 gmem_dimm_t *dimm; 118 119 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; 120 dimm = gmem_list_next(dimm)) { 121 if (strcmp(dimm->dimm_serial, serial) == 0) 122 return (dimm); 123 } 124 125 return (NULL); 126 } 127 128 gmem_dimm_t * 129 gmem_dimm_create(fmd_hdl_t *hdl, nvlist_t *asru) 130 { 131 gmem_dimm_t *dimm; 132 nvlist_t *fmri; 133 char *serial; 134 135 if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial) != 0) { 136 fmd_hdl_debug(hdl, "Unable to get dimm serial\n"); 137 return (NULL); 138 } 139 140 if (nvlist_dup(asru, &fmri, 0) != 0) { 141 fmd_hdl_debug(hdl, "dimm create nvlist dup failed"); 142 return (NULL); 143 } 144 145 fmd_hdl_debug(hdl, "dimm_create: creating new DIMM serial=%s\n", 146 serial); 147 GMEM_STAT_BUMP(dimm_creat); 148 149 dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP); 150 dimm->dimm_nodetype = GMEM_NT_DIMM; 151 dimm->dimm_version = GMEM_DIMM_VERSION; 152 dimm->dimm_phys_addr_low = ULLONG_MAX; 153 dimm->dimm_phys_addr_hi = 0; 154 dimm->dimm_syl_error = USHRT_MAX; 155 156 gmem_bufname(dimm->dimm_bufname, sizeof (dimm->dimm_bufname), "dimm_%s", 157 serial); 158 gmem_fmri_init(hdl, &dimm->dimm_asru, fmri, "dimm_asru_%s", serial); 159 160 nvlist_free(fmri); 161 162 (void) nvlist_lookup_string(dimm->dimm_asru_nvl, FM_FMRI_HC_SERIAL_ID, 163 (char **)&dimm->dimm_serial); 164 165 gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat, dimm->dimm_serial, 166 0, GMEM_DIMM_STAT_PREFIX); 167 168 gmem_list_append(&gmem.gm_dimms, dimm); 169 gmem_dimm_dirty(hdl, dimm); 170 171 return (dimm); 172 } 173 174 gmem_dimm_t * 175 gmem_dimm_lookup(fmd_hdl_t *hdl, nvlist_t *asru) 176 { 177 gmem_dimm_t *dimm; 178 char *serial; 179 int err; 180 181 err = nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial); 182 183 if (err != 0) { 184 fmd_hdl_debug(hdl, "Can't get dimm serial number\n"); 185 GMEM_STAT_BUMP(bad_mem_resource); 186 return (NULL); 187 } 188 189 dimm = dimm_lookup_by_serial(serial); 190 return (dimm); 191 } 192 193 194 static gmem_dimm_t * 195 gmem_dimm_v0tov1(fmd_hdl_t *hdl, gmem_dimm_0_t *old, size_t oldsz) 196 { 197 gmem_dimm_t *new; 198 if (oldsz != sizeof (gmem_dimm_0_t)) { 199 fmd_hdl_abort(hdl, "size of state doesn't match size of " 200 "version 0 state (%u bytes).\n", sizeof (gmem_dimm_0_t)); 201 } 202 203 new = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP); 204 new->dimm_header = old->dimm0_header; 205 new->dimm_version = GMEM_DIMM_VERSION; 206 new->dimm_asru = old->dimm0_asru; 207 new->dimm_nretired = old->dimm0_nretired; 208 new->dimm_phys_addr_hi = 0; 209 new->dimm_phys_addr_low = ULLONG_MAX; 210 211 fmd_hdl_free(hdl, old, oldsz); 212 return (new); 213 } 214 215 static gmem_dimm_t * 216 gmem_dimm_wrapv1(fmd_hdl_t *hdl, gmem_dimm_pers_t *pers, size_t psz) 217 { 218 gmem_dimm_t *dimm; 219 220 if (psz != sizeof (gmem_dimm_pers_t)) { 221 fmd_hdl_abort(hdl, "size of state doesn't match size of " 222 "version 0 state (%u bytes).\n", sizeof (gmem_dimm_pers_t)); 223 } 224 225 dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP); 226 bcopy(pers, dimm, sizeof (gmem_dimm_pers_t)); 227 fmd_hdl_free(hdl, pers, psz); 228 return (dimm); 229 } 230 231 void * 232 gmem_dimm_restore(fmd_hdl_t *hdl, fmd_case_t *cp, gmem_case_ptr_t *ptr) 233 { 234 gmem_dimm_t *dimm; 235 236 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; 237 dimm = gmem_list_next(dimm)) { 238 if (strcmp(dimm->dimm_bufname, ptr->ptr_name) == 0) 239 break; 240 } 241 242 if (dimm == NULL) { 243 int migrated = 0; 244 size_t dimmsz; 245 246 fmd_hdl_debug(hdl, "restoring dimm from %s\n", ptr->ptr_name); 247 248 if ((dimmsz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) { 249 fmd_hdl_abort(hdl, "dimm referenced by case %s does " 250 "not exist in saved state\n", 251 fmd_case_uuid(hdl, cp)); 252 } else if (dimmsz > GMEM_DIMM_MAXSIZE || 253 dimmsz < GMEM_DIMM_MINSIZE) { 254 fmd_hdl_abort(hdl, "dimm buffer referenced by case %s " 255 "is out of bounds (is %u bytes, max %u, min %u)\n", 256 fmd_case_uuid(hdl, cp), dimmsz, 257 GMEM_DIMM_MAXSIZE, GMEM_DIMM_MINSIZE); 258 } 259 260 if ((dimm = gmem_buf_read(hdl, NULL, ptr->ptr_name, 261 dimmsz)) == NULL) { 262 fmd_hdl_abort(hdl, "failed to read dimm buf %s", 263 ptr->ptr_name); 264 } 265 266 fmd_hdl_debug(hdl, "found %d in version field\n", 267 dimm->dimm_version); 268 269 if (GMEM_DIMM_VERSIONED(dimm)) { 270 271 switch (dimm->dimm_version) { 272 case GMEM_DIMM_VERSION_1: 273 dimm = gmem_dimm_wrapv1(hdl, 274 (gmem_dimm_pers_t *)dimm, dimmsz); 275 break; 276 default: 277 fmd_hdl_abort(hdl, "unknown version (found %d) " 278 "for dimm state referenced by case %s.\n", 279 dimm->dimm_version, fmd_case_uuid(hdl, cp)); 280 break; 281 } 282 } else { 283 dimm = gmem_dimm_v0tov1(hdl, (gmem_dimm_0_t *)dimm, 284 dimmsz); 285 migrated = 1; 286 } 287 288 if (migrated) { 289 GMEM_STAT_BUMP(dimm_migrat); 290 gmem_dimm_dirty(hdl, dimm); 291 } 292 293 gmem_fmri_restore(hdl, &dimm->dimm_asru); 294 295 if ((errno = nvlist_lookup_string(dimm->dimm_asru_nvl, 296 FM_FMRI_HC_SERIAL_ID, (char **)&dimm->dimm_serial)) != 0) 297 fmd_hdl_abort(hdl, 298 "failed to retrieve serial from asru"); 299 300 301 gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat, 302 dimm->dimm_serial, dimm->dimm_nretired, 303 GMEM_DIMM_STAT_PREFIX); 304 305 gmem_list_append(&gmem.gm_dimms, dimm); 306 } 307 308 switch (ptr->ptr_subtype) { 309 case GMEM_PTR_DIMM_CASE: 310 gmem_mem_case_restore(hdl, &dimm->dimm_case, cp, "dimm", 311 dimm->dimm_serial); 312 break; 313 default: 314 fmd_hdl_abort(hdl, "invalid %s subtype %d\n", 315 ptr->ptr_name, ptr->ptr_subtype); 316 } 317 318 return (dimm); 319 } 320 321 void 322 gmem_dimm_validate(fmd_hdl_t *hdl) 323 { 324 gmem_dimm_t *dimm, *next; 325 326 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; dimm = next) { 327 next = gmem_list_next(dimm); 328 329 if (!gmem_dimm_present(hdl, dimm->dimm_asru_nvl)) 330 gmem_dimm_destroy(hdl, dimm); 331 } 332 } 333 334 void 335 gmem_dimm_dirty(fmd_hdl_t *hdl, gmem_dimm_t *dimm) 336 { 337 if (fmd_buf_size(hdl, NULL, dimm->dimm_bufname) != 338 sizeof (gmem_dimm_pers_t)) 339 fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname); 340 341 /* No need to rewrite the FMRIs in the dimm - they don't change */ 342 fmd_buf_write(hdl, NULL, dimm->dimm_bufname, &dimm->dimm_pers, 343 sizeof (gmem_dimm_pers_t)); 344 } 345 346 void 347 gmem_dimm_gc(fmd_hdl_t *hdl) 348 { 349 gmem_dimm_validate(hdl); 350 } 351 352 void 353 gmem_dimm_fini(fmd_hdl_t *hdl) 354 { 355 gmem_dimm_t *dimm; 356 357 while ((dimm = gmem_list_next(&gmem.gm_dimms)) != NULL) 358 gmem_dimm_free(hdl, dimm, FMD_B_FALSE); 359 } 360 361 362 /*ARGSUSED*/ 363 static int 364 find_dimm_hc_fmri(topo_hdl_t *thp, tnode_t *node, void *arg) 365 { 366 367 char *topo_sn; 368 dimmid_t *dimmid = (dimmid_t *)arg; 369 nvlist_t *fru = NULL; 370 nvlist_t *rsc = NULL; 371 nvlist_t *asru = NULL; 372 int err; 373 374 if (topo_node_fru(node, &fru, NULL, &err) < 0) 375 return (TOPO_WALK_NEXT); 376 377 err = nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &topo_sn); 378 if (err != 0) { 379 nvlist_free(fru); 380 return (TOPO_WALK_NEXT); 381 } 382 383 if (strcmp(dimmid->serial, topo_sn) != 0) { 384 nvlist_free(fru); 385 return (TOPO_WALK_NEXT); 386 } 387 388 switch (dimmid->type) { 389 case FINDFRU: 390 (void) nvlist_dup(fru, &dimm_nvl, NV_UNIQUE_NAME); 391 break; 392 case FINDRSC: 393 (void) topo_node_resource(node, &rsc, &err); 394 if (rsc != NULL) { 395 (void) nvlist_dup(rsc, &dimm_nvl, 396 NV_UNIQUE_NAME); 397 nvlist_free(rsc); 398 } 399 break; 400 case FINDASRU: 401 (void) topo_node_asru(node, &asru, NULL, &err); 402 if (asru != NULL) { 403 (void) nvlist_dup(asru, &dimm_nvl, 404 NV_UNIQUE_NAME); 405 nvlist_free(asru); 406 } 407 break; 408 default: 409 break; 410 } 411 nvlist_free(fru); 412 return (TOPO_WALK_TERMINATE); 413 } 414 415 nvlist_t * 416 gmem_find_dimm_by_sn(fmd_hdl_t *hdl, dimmid_t *dimmid) { 417 topo_hdl_t *thp; 418 topo_walk_t *twp; 419 int err; 420 dimm_nvl = NULL; 421 422 if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL) 423 return (NULL); 424 425 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, 426 find_dimm_hc_fmri, dimmid, &err)) == NULL) { 427 fmd_hdl_topo_rele(hdl, thp); 428 return (NULL); 429 } 430 431 (void) topo_walk_step(twp, TOPO_WALK_CHILD); 432 topo_walk_fini(twp); 433 fmd_hdl_topo_rele(hdl, thp); 434 return (dimm_nvl); 435 } 436 437 nvlist_t * 438 gmem_find_dimm_fru(fmd_hdl_t *hdl, char *sn) 439 { 440 dimmid_t fru; 441 (void) strcpy(fru.serial, sn); 442 fru.type = FINDFRU; 443 return (gmem_find_dimm_by_sn(hdl, &fru)); 444 } 445 446 nvlist_t * 447 gmem_find_dimm_rsc(fmd_hdl_t *hdl, char *sn) 448 { 449 dimmid_t rsc; 450 (void) strcpy(rsc.serial, sn); 451 rsc.type = FINDRSC; 452 return (gmem_find_dimm_by_sn(hdl, &rsc)); 453 } 454 455 nvlist_t * 456 gmem_find_dimm_asru(fmd_hdl_t *hdl, char *sn) 457 { 458 dimmid_t asru; 459 (void) strcpy(asru.serial, sn); 460 asru.type = FINDASRU; 461 return (gmem_find_dimm_by_sn(hdl, &asru)); 462 } 463 464 int 465 gmem_dimm_present(fmd_hdl_t *hdl, nvlist_t *asru) 466 { 467 char *sn; 468 nvlist_t *dimm = NULL; 469 470 if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &sn) != 0) { 471 fmd_hdl_debug(hdl, "Unable to get dimm serial\n"); 472 return (0); 473 } 474 dimm = gmem_find_dimm_fru(hdl, sn); 475 if (dimm == NULL) { 476 fmd_hdl_debug(hdl, "Dimm sn=%s is not present\n", sn); 477 return (0); 478 } 479 if (dimm != NULL) 480 nvlist_free(dimm); 481 return (1); 482 } 483 484 static int 485 gmem_find_dimm_chip(nvlist_t *nvl, uint32_t *chip) 486 { 487 488 char *name, *id, *end; 489 nvlist_t **hcl; 490 uint_t n; 491 int i; 492 int rc = 0; 493 494 if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &n) < 0) 495 return (0); 496 for (i = 0; i < n; i++) { 497 (void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name); 498 (void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &id); 499 500 if (strcmp(name, "chip") == 0) { 501 *chip = (uint32_t)strtoul(id, &end, 10); 502 rc = 1; 503 break; 504 } 505 } 506 return (rc); 507 } 508 509 int 510 gmem_same_datapath_dimms(fmd_hdl_t *hdl, gmem_dimm_t *d1, gmem_dimm_t *d2) 511 { 512 nvlist_t *rsrc1, *rsrc2; 513 uint32_t chip1, chip2; 514 515 rsrc1 = gmem_find_dimm_rsc(hdl, d1->dimm_serial); 516 rsrc2 = gmem_find_dimm_rsc(hdl, d2->dimm_serial); 517 518 if (rsrc1 == NULL || rsrc2 == NULL) 519 return (0); 520 521 if (gmem_find_dimm_chip(rsrc1, &chip1) && 522 gmem_find_dimm_chip(rsrc2, &chip2)) { 523 if (chip1 == chip2) { 524 nvlist_free(rsrc1); 525 nvlist_free(rsrc2); 526 return (1); 527 } 528 } 529 530 nvlist_free(rsrc1); 531 nvlist_free(rsrc2); 532 return (0); 533 } 534 535 int 536 gmem_check_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos) 537 { 538 gmem_dimm_t *dimm = NULL, *next = NULL; 539 540 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; 541 dimm = next) { 542 next = gmem_list_next(dimm); 543 if (gmem_same_datapath_dimms(hdl, dimm, d) && 544 dimm->dimm_syl_error == upos) 545 return (1); 546 } 547 return (0); 548 } 549 550 void 551 gmem_save_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos) 552 { 553 gmem_dimm_t *dimm = NULL, *next = NULL; 554 555 for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; 556 dimm = next) { 557 next = gmem_list_next(dimm); 558 if (gmem_same_datapath_dimms(hdl, dimm, d)) 559 dimm->dimm_syl_error = upos; 560 } 561 } 562