Home | History | Annotate | Download | only in cpumem-diagnosis
      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