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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Ereport-handling routines for memory errors 28 */ 29 30 #include <cmd_mem.h> 31 #include <cmd_dimm.h> 32 #include <cmd_bank.h> 33 #include <cmd_page.h> 34 #include <cmd_cpu.h> 35 #include <cmd_branch.h> 36 #include <cmd_state.h> 37 #include <cmd.h> 38 #include <cmd_hc_sun4v.h> 39 40 #include <assert.h> 41 #include <strings.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <unistd.h> 45 #include <fm/fmd_api.h> 46 #include <sys/fm/ldom.h> 47 #include <sys/fm/protocol.h> 48 49 #include <sys/fm/cpu/UltraSPARC-T1.h> 50 #include <sys/mdesc.h> 51 #include <sys/async.h> 52 #include <sys/errclassify.h> 53 #include <sys/niagararegs.h> 54 #include <sys/fm/ldom.h> 55 #include <ctype.h> 56 57 #define VF_TS3_FCR 0x000000000000FFFFULL 58 #define VF_L2ESYR_C2C 0x8000000000000000ULL 59 #define UTS2_CPUS_PER_CHIP 64 60 #define FBR_ERROR ".fbr" 61 #define DSU_ERROR ".dsu" 62 #define FERG_INVALID ".invalid" 63 #define DBU_ERROR ".dbu" 64 65 extern ldom_hdl_t *cpumem_diagnosis_lhp; 66 67 static fmd_hdl_t *cpumem_hdl = NULL; 68 69 #define ERR_CLASS(x, y) (strcmp(strrchr(x, '.'), y)) 70 71 static void * 72 cpumem_alloc(size_t size) 73 { 74 assert(cpumem_hdl != NULL); 75 76 return (fmd_hdl_alloc(cpumem_hdl, size, FMD_SLEEP)); 77 } 78 79 static void 80 cpumem_free(void *addr, size_t size) 81 { 82 assert(cpumem_hdl != NULL); 83 84 fmd_hdl_free(cpumem_hdl, addr, size); 85 } 86 87 /*ARGSUSED*/ 88 cmd_evdisp_t 89 cmd_mem_synd_check(fmd_hdl_t *hdl, uint64_t afar, uint8_t afar_status, 90 uint16_t synd, uint8_t synd_status, cmd_cpu_t *cpu) 91 { 92 /* 93 * Niagara writebacks from L2 containing UEs are placed in memory 94 * with the poison syndrome NI_DRAM_POISON_SYND_FROM_LDWU. 95 * Memory UE ereports showing this syndrome are dropped because they 96 * indicate an L2 problem, which should be diagnosed from the 97 * corresponding L2 cache ereport. 98 */ 99 switch (cpu->cpu_type) { 100 case CPU_ULTRASPARC_T1: 101 if (synd == NI_DRAM_POISON_SYND_FROM_LDWU) { 102 fmd_hdl_debug(hdl, 103 "discarding UE due to magic syndrome %x\n", 104 synd); 105 return (CMD_EVD_UNUSED); 106 } 107 break; 108 case CPU_ULTRASPARC_T2: 109 case CPU_ULTRASPARC_T2plus: 110 if (synd == N2_DRAM_POISON_SYND_FROM_LDWU) { 111 fmd_hdl_debug(hdl, 112 "discarding UE due to magic syndrome %x\n", 113 synd); 114 return (CMD_EVD_UNUSED); 115 } 116 break; 117 default: 118 break; 119 } 120 return (CMD_EVD_OK); 121 } 122 123 static int 124 cpu_present(fmd_hdl_t *hdl, nvlist_t *asru, uint32_t *cpuid) 125 { 126 nvlist_t *cp_asru; 127 uint32_t i; 128 129 if (nvlist_dup(asru, &cp_asru, 0) != 0) { 130 fmd_hdl_debug(hdl, "unable to alloc asru for thread\n"); 131 return (-1); 132 } 133 134 for (i = *cpuid; i < *cpuid + UTS2_CPUS_PER_CHIP; i++) { 135 136 (void) nvlist_remove_all(cp_asru, FM_FMRI_CPU_ID); 137 138 if (nvlist_add_uint32(cp_asru, FM_FMRI_CPU_ID, i) == 0) { 139 if (fmd_nvl_fmri_present(hdl, cp_asru) && 140 !fmd_nvl_fmri_unusable(hdl, cp_asru)) { 141 nvlist_free(cp_asru); 142 *cpuid = i; 143 return (0); 144 } 145 } 146 } 147 nvlist_free(cp_asru); 148 return (-1); 149 } 150 151 /*ARGSUSED*/ 152 cmd_evdisp_t 153 cmd_c2c(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 154 cmd_errcl_t clcode) 155 { 156 uint32_t cpuid; 157 nvlist_t *det; 158 int rc; 159 160 (void) nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &det); 161 if (nvlist_lookup_uint32(det, FM_FMRI_CPU_ID, &cpuid) == 0) { 162 163 /* 164 * If the c2c bit is set, the sending cache of the 165 * cpu must be faulted instead of the memory. 166 * If the detector is chip0, the cache of the chip1 167 * is faulted and vice versa. 168 */ 169 if (cpuid < UTS2_CPUS_PER_CHIP) 170 cpuid = UTS2_CPUS_PER_CHIP; 171 else 172 cpuid = 0; 173 174 rc = cpu_present(hdl, det, &cpuid); 175 176 if (rc != -1) { 177 (void) nvlist_remove(det, FM_FMRI_CPU_ID, 178 DATA_TYPE_UINT32); 179 if (nvlist_add_uint32(det, 180 FM_FMRI_CPU_ID, cpuid) == 0) { 181 clcode |= CMD_CPU_LEVEL_CHIP; 182 return (cmd_l2u(hdl, ep, nvl, class, clcode)); 183 } 184 185 } 186 } 187 fmd_hdl_debug(hdl, "cmd_c2c: no cpuid discarding C2C error"); 188 return (CMD_EVD_BAD); 189 } 190 191 /* 192 * sun4v's xe_common routine has an extra argument, clcode, compared 193 * to routine of same name in sun4u. 194 */ 195 196 static cmd_evdisp_t 197 xe_common(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, 198 const char *class, cmd_errcl_t clcode, cmd_xe_handler_f *hdlr) 199 { 200 uint64_t afar, l2_afar, dram_afar; 201 uint64_t l2_afsr, dram_afsr, l2_esyr; 202 uint16_t synd; 203 uint8_t afar_status, synd_status; 204 nvlist_t *rsrc; 205 char *typenm; 206 uint64_t disp = 0; 207 int minorvers = 1; 208 209 if (nvlist_lookup_uint64(nvl, 210 FM_EREPORT_PAYLOAD_NAME_L2_AFSR, &l2_afsr) != 0 && 211 nvlist_lookup_uint64(nvl, 212 FM_EREPORT_PAYLOAD_NAME_L2_ESR, &l2_afsr) != 0) 213 return (CMD_EVD_BAD); 214 215 if (nvlist_lookup_uint64(nvl, 216 FM_EREPORT_PAYLOAD_NAME_DRAM_AFSR, &dram_afsr) != 0 && 217 nvlist_lookup_uint64(nvl, 218 FM_EREPORT_PAYLOAD_NAME_DRAM_ESR, &dram_afsr) != 0) 219 return (CMD_EVD_BAD); 220 221 if (nvlist_lookup_uint64(nvl, 222 FM_EREPORT_PAYLOAD_NAME_L2_AFAR, &l2_afar) != 0 && 223 nvlist_lookup_uint64(nvl, 224 FM_EREPORT_PAYLOAD_NAME_L2_EAR, &l2_afar) != 0) 225 return (CMD_EVD_BAD); 226 227 if (nvlist_lookup_uint64(nvl, 228 FM_EREPORT_PAYLOAD_NAME_DRAM_AFAR, &dram_afar) != 0 && 229 nvlist_lookup_uint64(nvl, 230 FM_EREPORT_PAYLOAD_NAME_DRAM_EAR, &dram_afar) != 0) 231 return (CMD_EVD_BAD); 232 233 if (nvlist_lookup_pairs(nvl, 0, 234 FM_EREPORT_PAYLOAD_NAME_ERR_TYPE, DATA_TYPE_STRING, &typenm, 235 FM_EREPORT_PAYLOAD_NAME_RESOURCE, DATA_TYPE_NVLIST, &rsrc, 236 NULL) != 0) 237 return (CMD_EVD_BAD); 238 239 synd = dram_afsr; 240 241 /* 242 * Niagara afar and synd validity. 243 * For a given set of error registers, the payload value is valid if 244 * no higher priority error status bit is set. See UltraSPARC-T1.h for 245 * error status bit values and priority settings. Note that for DAC 246 * and DAU, afar value is taken from l2 error registers, syndrome 247 * from dram error * registers; for DSC and DSU, both afar and 248 * syndrome are taken from dram * error registers. DSU afar and 249 * syndrome are always valid because no 250 * higher priority error will override. 251 */ 252 switch (clcode) { 253 case CMD_ERRCL_DAC: 254 afar = l2_afar; 255 afar_status = ((l2_afsr & NI_L2AFSR_P10) == 0) ? 256 AFLT_STAT_VALID : AFLT_STAT_INVALID; 257 synd_status = ((dram_afsr & NI_DMAFSR_P01) == 0) ? 258 AFLT_STAT_VALID : AFLT_STAT_INVALID; 259 break; 260 case CMD_ERRCL_DSC: 261 afar = dram_afar; 262 afar_status = ((dram_afsr & NI_DMAFSR_P01) == 0) ? 263 AFLT_STAT_VALID : AFLT_STAT_INVALID; 264 synd_status = afar_status; 265 break; 266 case CMD_ERRCL_DAU: 267 afar = l2_afar; 268 afar_status = ((l2_afsr & NI_L2AFSR_P05) == 0) ? 269 AFLT_STAT_VALID : AFLT_STAT_INVALID; 270 synd_status = AFLT_STAT_VALID; 271 272 if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_NAME_L2_ESYR, 273 &l2_esyr) == 0) { 274 if (l2_esyr & VF_L2ESYR_C2C) { 275 return (cmd_c2c(hdl, ep, nvl, class, clcode)); 276 } 277 } 278 break; 279 case CMD_ERRCL_DSU: 280 afar = dram_afar; 281 afar_status = synd_status = AFLT_STAT_VALID; 282 break; 283 default: 284 fmd_hdl_debug(hdl, "Niagara unrecognized mem error %llx\n", 285 clcode); 286 return (CMD_EVD_UNUSED); 287 } 288 289 return (hdlr(hdl, ep, nvl, class, afar, afar_status, synd, 290 synd_status, cmd_mem_name2type(typenm, minorvers), disp, rsrc)); 291 } 292 293 294 /*ARGSUSED*/ 295 cmd_evdisp_t 296 cmd_ce(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 297 cmd_errcl_t clcode) 298 { 299 if (strcmp(class, "ereport.cpu.ultraSPARC-T2plus.dsc") == 0) 300 return (CMD_EVD_UNUSED); /* drop VF dsc's */ 301 else 302 return (xe_common(hdl, ep, nvl, class, clcode, cmd_ce_common)); 303 } 304 305 /*ARGSUSED*/ 306 cmd_evdisp_t 307 cmd_ue_train(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 308 cmd_errcl_t clcode) 309 { 310 cmd_evdisp_t rc, rc1; 311 312 /* 313 * The DAU is cause of the DAU->DCDP/ICDP train: 314 * - process the cause of the event. 315 * - register the error to the nop event train, so the effected errors 316 * (DCDP/ICDP) will be dropped. 317 */ 318 rc = xe_common(hdl, ep, nvl, class, clcode, cmd_ue_common); 319 320 rc1 = cmd_xxcu_initial(hdl, ep, nvl, class, clcode, CMD_XR_HDLR_NOP); 321 if (rc1 != 0) 322 fmd_hdl_debug(hdl, 323 "Fail to add error (%llx) to the train, rc = %d", 324 clcode, rc1); 325 326 return (rc); 327 } 328 329 /*ARGSUSED*/ 330 cmd_evdisp_t 331 cmd_ue(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 332 cmd_errcl_t clcode) 333 { 334 if (strcmp(class, "ereport.cpu.ultraSPARC-T2plus.dsu") == 0) 335 /* 336 * VF dsu's need to be treated like branch errors, 337 * because we can't localize to a single DIMM or pair of 338 * DIMMs given missing/invalid parts of the dram-ear. 339 */ 340 return (cmd_fb(hdl, ep, nvl, class, clcode)); 341 else 342 return (xe_common(hdl, ep, nvl, class, clcode, cmd_ue_common)); 343 } 344 345 /*ARGSUSED*/ 346 cmd_evdisp_t 347 cmd_frx(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 348 cmd_errcl_t clcode) 349 { 350 return (CMD_EVD_UNUSED); 351 } 352 353 354 /*ARGSUSED*/ 355 cmd_evdisp_t 356 cmd_fb(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 357 cmd_errcl_t clcode) 358 { 359 cmd_branch_t *branch; 360 const char *uuid; 361 nvlist_t *asru, *det; 362 uint64_t ts3_fcr; 363 364 if (nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &asru) < 0) { 365 CMD_STAT_BUMP(bad_mem_asru); 366 return (NULL); 367 } 368 369 if (nvlist_lookup_nvlist(nvl, FM_EREPORT_DETECTOR, &det) < 0) { 370 CMD_STAT_BUMP(bad_mem_asru); 371 return (NULL); 372 } 373 374 if (fmd_nvl_fmri_expand(hdl, det) < 0) { 375 fmd_hdl_debug(hdl, "Failed to expand detector"); 376 return (NULL); 377 } 378 379 branch = cmd_branch_lookup(hdl, asru); 380 if (branch == NULL) { 381 if ((branch = cmd_branch_create(hdl, asru)) == NULL) 382 return (CMD_EVD_UNUSED); 383 } 384 385 if (branch->branch_case.cc_cp != NULL && 386 fmd_case_solved(hdl, branch->branch_case.cc_cp)) { 387 fmd_hdl_debug(hdl, "Case solved\n"); 388 return (CMD_EVD_REDUND); 389 } 390 391 if (branch->branch_case.cc_cp == NULL) { 392 branch->branch_case.cc_cp = cmd_case_create(hdl, 393 &branch->branch_header, CMD_PTR_BRANCH_CASE, &uuid); 394 } 395 396 if (ERR_CLASS(class, FBR_ERROR) == 0) { 397 if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_NAME_TS3_FCR, 398 &ts3_fcr) == 0 && (ts3_fcr != VF_TS3_FCR)) { 399 fmd_hdl_debug(hdl, 400 "Processing fbr with lane failover\n"); 401 cmd_branch_create_fault(hdl, branch, 402 "fault.memory.link-f", det); 403 404 } else { 405 fmd_hdl_debug(hdl, "Adding fbr event to serd engine\n"); 406 if (branch->branch_case.cc_serdnm == NULL) { 407 branch->branch_case.cc_serdnm = 408 cmd_mem_serdnm_create(hdl, 409 "branch", branch->branch_unum); 410 411 fmd_serd_create(hdl, 412 branch->branch_case.cc_serdnm, 413 fmd_prop_get_int32(hdl, "fbr_n"), 414 fmd_prop_get_int64(hdl, "fbr_t")); 415 } 416 417 if (fmd_serd_record(hdl, 418 branch->branch_case.cc_serdnm, ep) == FMD_B_FALSE) 419 return (CMD_EVD_OK); /* engine hasn't fired */ 420 421 fmd_hdl_debug(hdl, "fbr serd fired\n"); 422 423 fmd_case_add_serd(hdl, branch->branch_case.cc_cp, 424 branch->branch_case.cc_serdnm); 425 426 cmd_branch_create_fault(hdl, branch, 427 "fault.memory.link-c", det); 428 } 429 } else if (ERR_CLASS(class, DSU_ERROR) == 0) { 430 fmd_hdl_debug(hdl, "Processing dsu event"); 431 cmd_branch_create_fault(hdl, branch, "fault.memory.bank", det); 432 } else { 433 fmd_hdl_debug(hdl, "Processing fbu event"); 434 cmd_branch_create_fault(hdl, branch, "fault.memory.link-u", 435 det); 436 } 437 438 branch->branch_flags |= CMD_MEM_F_FAULTING; 439 440 if (branch->branch_case.cc_serdnm != NULL) { 441 fmd_serd_destroy(hdl, branch->branch_case.cc_serdnm); 442 fmd_hdl_strfree(hdl, branch->branch_case.cc_serdnm); 443 branch->branch_case.cc_serdnm = NULL; 444 } 445 446 fmd_case_add_ereport(hdl, branch->branch_case.cc_cp, ep); 447 fmd_case_solve(hdl, branch->branch_case.cc_cp); 448 cmd_branch_dirty(hdl, branch); 449 450 return (CMD_EVD_OK); 451 } 452 453 /*ARGSUSED*/ 454 cmd_evdisp_t 455 cmd_fb_train(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 456 cmd_errcl_t clcode) 457 { 458 cmd_evdisp_t rc, rc1; 459 460 /* 461 * The FBU is cause of the FBU->DCDP/ICDP train: 462 * - process the cause of the event. 463 * - register the error to the nop event train, so the effected errors 464 * (DCDP/ICDP) will be dropped. 465 */ 466 rc = cmd_fb(hdl, ep, nvl, class, clcode); 467 468 rc1 = cmd_xxcu_initial(hdl, ep, nvl, class, clcode, CMD_XR_HDLR_NOP); 469 if (rc1 != 0) 470 fmd_hdl_debug(hdl, 471 "Fail to add error (%llx) to the train, rc = %d", 472 clcode, rc1); 473 474 return (rc); 475 } 476 477 478 /*ARGSUSED*/ 479 cmd_evdisp_t 480 cmd_fw_defect(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class, 481 cmd_errcl_t clcode) 482 { 483 const char *fltclass = NULL; 484 nvlist_t *rsc = NULL; 485 int solve = 0; 486 487 if ((rsc = init_mb(hdl)) == NULL) 488 return (CMD_EVD_UNUSED); 489 490 if (ERR_CLASS(class, FERG_INVALID) == 0) { 491 fltclass = "defect.fw.generic-sparc.erpt-gen"; 492 } else if (ERR_CLASS(class, DBU_ERROR) == 0) { 493 cmd_evdisp_t rc; 494 fltclass = "defect.fw.generic-sparc.addr-oob"; 495 /* 496 * add dbu to nop error train 497 */ 498 rc = cmd_xxcu_initial(hdl, ep, nvl, class, clcode, 499 CMD_XR_HDLR_NOP); 500 if (rc != 0) 501 fmd_hdl_debug(hdl, 502 "Failed to add error (%llx) to the train, rc = %d", 503 clcode, rc); 504 } else { 505 fmd_hdl_debug(hdl, "Unexpected fw defect event %s", class); 506 } 507 508 if (fltclass) { 509 fmd_case_t *cp = NULL; 510 nvlist_t *fault = NULL; 511 512 fault = fmd_nvl_create_fault(hdl, fltclass, 100, NULL, 513 NULL, rsc); 514 if (fault != NULL) { 515 cp = fmd_case_open(hdl, NULL); 516 fmd_case_add_ereport(hdl, cp, ep); 517 fmd_case_add_suspect(hdl, cp, fault); 518 fmd_case_solve(hdl, cp); 519 solve = 1; 520 } 521 } 522 523 if (rsc) 524 nvlist_free(rsc); 525 526 return (solve ? CMD_EVD_OK : CMD_EVD_UNUSED); 527 } 528 529 void 530 cmd_branch_close(fmd_hdl_t *hdl, void *arg) 531 { 532 cmd_branch_destroy(hdl, arg); 533 } 534 535 536 /*ARGSUSED*/ 537 ulong_t 538 cmd_mem_get_phys_pages(fmd_hdl_t *hdl) 539 { 540 /* 541 * Compute and return the total physical memory in pages from the 542 * MD/PRI. 543 * Cache its value. 544 */ 545 static ulong_t npage = 0; 546 md_t *mdp; 547 mde_cookie_t *listp; 548 uint64_t bmem, physmem = 0; 549 ssize_t bufsiz = 0; 550 uint64_t *bufp; 551 int num_nodes, nmblocks, i; 552 553 if (npage > 0) { 554 return (npage); 555 } 556 557 if (cpumem_hdl == NULL) { 558 cpumem_hdl = hdl; 559 } 560 561 if ((bufsiz = ldom_get_core_md(cpumem_diagnosis_lhp, &bufp)) <= 0) { 562 return (0); 563 } 564 if ((mdp = md_init_intern(bufp, cpumem_alloc, cpumem_free)) == NULL || 565 (num_nodes = md_node_count(mdp)) <= 0) { 566 cpumem_free(bufp, (size_t)bufsiz); 567 return (0); 568 } 569 570 listp = (mde_cookie_t *)cpumem_alloc(sizeof (mde_cookie_t) * 571 num_nodes); 572 nmblocks = md_scan_dag(mdp, MDE_INVAL_ELEM_COOKIE, 573 md_find_name(mdp, "mblock"), 574 md_find_name(mdp, "fwd"), listp); 575 for (i = 0; i < nmblocks; i++) { 576 if (md_get_prop_val(mdp, listp[i], "size", &bmem) < 0) { 577 physmem = 0; 578 break; 579 } 580 physmem += bmem; 581 } 582 npage = (ulong_t)(physmem / cmd.cmd_pagesize); 583 584 cpumem_free(listp, sizeof (mde_cookie_t) * num_nodes); 585 cpumem_free(bufp, (size_t)bufsiz); 586 (void) md_fini(mdp); 587 588 return (npage); 589 } 590 591 static int galois_mul[16][16] = { 592 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 593 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 0 */ 594 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, /* 1 */ 595 { 0, 2, 4, 6, 8, 10, 12, 14, 3, 1, 7, 5, 11, 9, 15, 13}, /* 2 */ 596 { 0, 3, 6, 5, 12, 15, 10, 9, 11, 8, 13, 14, 7, 4, 1, 2}, /* 3 */ 597 { 0, 4, 8, 12, 3, 7, 11, 15, 6, 2, 14, 10, 5, 1, 13, 9}, /* 4 */ 598 { 0, 5, 10, 15, 7, 2, 13, 8, 14, 11, 4, 1, 9, 12, 3, 6}, /* 5 */ 599 { 0, 6, 12, 10, 11, 13, 7, 1, 5, 3, 9, 15, 14, 8, 2, 4}, /* 6 */ 600 { 0, 7, 14, 9, 15, 8, 1, 6, 13, 10, 3, 4, 2, 5, 12, 11}, /* 7 */ 601 { 0, 8, 3, 11, 6, 14, 5, 13, 12, 4, 15, 7, 10, 2, 9, 1}, /* 8 */ 602 { 0, 9, 1, 8, 2, 11, 3, 10, 4, 13, 5, 12, 6, 15, 7, 14}, /* 9 */ 603 { 0, 10, 7, 13, 14, 4, 9, 3, 15, 5, 8, 2, 1, 11, 6, 12}, /* A */ 604 { 0, 11, 5, 14, 10, 1, 15, 4, 7, 12, 2, 9, 13, 6, 8, 3}, /* B */ 605 { 0, 12, 11, 7, 5, 9, 14, 2, 10, 6, 1, 13, 15, 3, 4, 8}, /* C */ 606 { 0, 13, 9, 4, 1, 12, 8, 5, 2, 15, 11, 6, 3, 14, 10, 7}, /* D */ 607 { 0, 14, 15, 1, 13, 3, 2, 12, 9, 7, 6, 8, 4, 10, 11, 5}, /* E */ 608 { 0, 15, 13, 2, 9, 6, 4, 11, 1, 14, 12, 3, 8, 7, 5, 10} /* F */ 609 }; 610 611 static int 612 galois_div(int num, int denom) { 613 int i; 614 615 for (i = 0; i < 16; i++) { 616 if (galois_mul[denom][i] == num) 617 return (i); 618 } 619 return (-1); 620 } 621 622 /* 623 * Data nibbles N0-N31 => 0-31 624 * check nibbles C0-3 => 32-35 625 */ 626 627 int 628 cmd_synd2upos(uint16_t syndrome) { 629 630 uint16_t s0, s1, s2, s3; 631 632 if (syndrome == 0) 633 return (-1); /* clean syndrome, not a CE */ 634 635 s0 = syndrome & 0xF; 636 s1 = (syndrome >> 4) & 0xF; 637 s2 = (syndrome >> 8) & 0xF; 638 s3 = (syndrome >> 12) & 0xF; 639 640 if (s3 == 0) { 641 if (s2 == 0 && s1 == 0) 642 return (32); /* 0 0 0 e => C0 */ 643 if (s2 == 0 && s0 == 0) 644 return (33); /* 0 0 e 0 => C1 */ 645 if (s1 == 0 && s0 == 0) 646 return (34); /* 0 e 0 0 => C2 */ 647 if (s2 == s1 && s1 == s0) 648 return (31); /* 0 d d d => N31 */ 649 return (-1); /* multibit error */ 650 } else if (s2 == 0) { 651 if (s1 == 0 && s0 == 0) 652 return (35); /* e 0 0 0 => C4 */ 653 if (s1 == 0 || s0 == 0) 654 return (-1); /* not a 0 b c */ 655 if (s3 != galois_div(galois_mul[s1][s1], s0)) 656 return (-1); /* check nibble not valid */ 657 return (galois_div(s0, s1) - 1); /* N0 - N14 */ 658 } else if (s1 == 0) { 659 if (s2 == 0 || s0 == 0) 660 return (-1); /* not a b 0 c */ 661 if (s3 != galois_div(galois_mul[s2][s2], s0)) 662 return (-1); /* check nibble not valid */ 663 return (galois_div(s0, s2) + 14); /* N15 - N29 */ 664 } else if (s0 == 0) { 665 if (s3 == s2 && s2 == s1) 666 return (30); /* d d d 0 => N30 */ 667 return (-1); 668 } else return (-1); 669 } 670 671 int 672 cmd_upos2dram(uint16_t upos) { 673 674 /* 675 * If and/or when x8 DIMMs are used on sun4v systems, this 676 * function will become more complicated. 677 */ 678 679 return ((int)upos); 680 681 } 682 683 nvlist_t * 684 cmd_mem2hc(fmd_hdl_t *hdl, nvlist_t *mem_fmri) { 685 686 char **snp; 687 uint_t n; 688 689 if (nvlist_lookup_string_array(mem_fmri, FM_FMRI_HC_SERIAL_ID, 690 &snp, &n) != 0) 691 return (NULL); /* doesn't have serial id */ 692 693 return (cmd_find_dimm_by_sn(hdl, FM_FMRI_SCHEME_HC, *snp)); 694 } 695