11529f529SScott Davenport /*
21529f529SScott Davenport  * CDDL HEADER START
31529f529SScott Davenport  *
41529f529SScott Davenport  * The contents of this file are subject to the terms of the
51529f529SScott Davenport  * Common Development and Distribution License (the "License").
61529f529SScott Davenport  * You may not use this file except in compliance with the License.
71529f529SScott Davenport  *
81529f529SScott Davenport  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91529f529SScott Davenport  * or http://www.opensolaris.org/os/licensing.
101529f529SScott Davenport  * See the License for the specific language governing permissions
111529f529SScott Davenport  * and limitations under the License.
121529f529SScott Davenport  *
131529f529SScott Davenport  * When distributing Covered Code, include this CDDL HEADER in each
141529f529SScott Davenport  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151529f529SScott Davenport  * If applicable, add the following below this CDDL HEADER, with the
161529f529SScott Davenport  * fields enclosed by brackets "[]" replaced with your own identifying
171529f529SScott Davenport  * information: Portions Copyright [yyyy] [name of copyright owner]
181529f529SScott Davenport  *
191529f529SScott Davenport  * CDDL HEADER END
201529f529SScott Davenport  */
211529f529SScott Davenport /*
22d4ac42a1STrang Do  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
231529f529SScott Davenport  */
241529f529SScott Davenport 
251529f529SScott Davenport 
261529f529SScott Davenport /*
271529f529SScott Davenport  * Support routines for DIMMs.
281529f529SScott Davenport  */
291529f529SScott Davenport 
301529f529SScott Davenport #include <gmem_mem.h>
311529f529SScott Davenport #include <gmem_dimm.h>
321529f529SScott Davenport #include <gmem.h>
331529f529SScott Davenport #include <errno.h>
34d4ac42a1STrang Do #include <limits.h>
351529f529SScott Davenport #include <string.h>
361529f529SScott Davenport #include <strings.h>
371529f529SScott Davenport #include <fcntl.h>
381529f529SScott Davenport #include <unistd.h>
391529f529SScott Davenport #include <fm/fmd_api.h>
401529f529SScott Davenport #include <fm/libtopo.h>
411529f529SScott Davenport #include <sys/fm/protocol.h>
421529f529SScott Davenport #include <sys/mem.h>
431529f529SScott Davenport #include <sys/nvpair.h>
441529f529SScott Davenport 
451529f529SScott Davenport nvlist_t *dimm_nvl;
461529f529SScott Davenport 
471529f529SScott Davenport typedef struct dimmid {
481529f529SScott Davenport 	char serial[100];
491529f529SScott Davenport 	int type;
501529f529SScott Davenport } dimmid_t;
511529f529SScott Davenport 
528f665889STrang Do static int gmem_find_dimm_chip(nvlist_t *, uint32_t *);
538f665889STrang Do 
541529f529SScott Davenport nvlist_t *
gmem_dimm_fru(gmem_dimm_t * dimm)551529f529SScott Davenport gmem_dimm_fru(gmem_dimm_t *dimm)
561529f529SScott Davenport {
571529f529SScott Davenport 	return (dimm->dimm_asru_nvl);
581529f529SScott Davenport }
591529f529SScott Davenport 
601529f529SScott Davenport static void
gmem_dimm_free(fmd_hdl_t * hdl,gmem_dimm_t * dimm,int destroy)611529f529SScott Davenport gmem_dimm_free(fmd_hdl_t *hdl, gmem_dimm_t *dimm, int destroy)
621529f529SScott Davenport {
631529f529SScott Davenport 	gmem_case_t *cc = &dimm->dimm_case;
641529f529SScott Davenport 	int i;
651529f529SScott Davenport 	gmem_mq_t *q;
66d4ac42a1STrang Do 	tstamp_t *tsp, *next;
671529f529SScott Davenport 
681529f529SScott Davenport 	if (cc->cc_cp != NULL) {
691529f529SScott Davenport 		gmem_case_fini(hdl, cc->cc_cp, destroy);
701529f529SScott Davenport 		if (cc->cc_serdnm != NULL) {
711529f529SScott Davenport 			if (fmd_serd_exists(hdl, cc->cc_serdnm) &&
721529f529SScott Davenport 			    destroy)
731529f529SScott Davenport 				fmd_serd_destroy(hdl, cc->cc_serdnm);
741529f529SScott Davenport 			fmd_hdl_strfree(hdl, cc->cc_serdnm);
751529f529SScott Davenport 		}
761529f529SScott Davenport 	}
771529f529SScott Davenport 
781529f529SScott Davenport 	gmem_fmri_fini(hdl, &dimm->dimm_asru, destroy);
791529f529SScott Davenport 
801529f529SScott Davenport 	for (i = 0; i < GMEM_MAX_CKWDS; i++) {
811529f529SScott Davenport 		while ((q = gmem_list_next(&dimm->mq_root[i])) != NULL) {
821529f529SScott Davenport 			if (q->mq_serdnm != NULL) {
831529f529SScott Davenport 				if (fmd_serd_exists(hdl, q->mq_serdnm))
841529f529SScott Davenport 					fmd_serd_destroy(hdl, q->mq_serdnm);
851529f529SScott Davenport 				fmd_hdl_strfree(hdl, q->mq_serdnm);
861529f529SScott Davenport 				q->mq_serdnm = NULL;
871529f529SScott Davenport 			}
88d4ac42a1STrang Do 
89d4ac42a1STrang Do 			for (tsp = gmem_list_next(&q->mq_dupce_tstamp);
90d4ac42a1STrang Do 			    tsp != NULL; tsp = next) {
91d4ac42a1STrang Do 				next = gmem_list_next(tsp);
92d4ac42a1STrang Do 				gmem_list_delete(&q->mq_dupce_tstamp,
93d4ac42a1STrang Do 				    &tsp->ts_l);
94d4ac42a1STrang Do 				fmd_hdl_free(hdl, tsp, sizeof (tstamp_t));
95d4ac42a1STrang Do 			}
96d4ac42a1STrang Do 
971529f529SScott Davenport 			gmem_list_delete(&dimm->mq_root[i], q);
981529f529SScott Davenport 			fmd_hdl_free(hdl, q, sizeof (gmem_mq_t));
991529f529SScott Davenport 		}
1001529f529SScott Davenport 	}
1011529f529SScott Davenport 
1021529f529SScott Davenport 	if (destroy)
1031529f529SScott Davenport 		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
1041529f529SScott Davenport 
1051529f529SScott Davenport 	gmem_list_delete(&gmem.gm_dimms, dimm);
1061529f529SScott Davenport 	fmd_hdl_free(hdl, dimm, sizeof (gmem_dimm_t));
1071529f529SScott Davenport }
1081529f529SScott Davenport 
1091529f529SScott Davenport void
gmem_dimm_destroy(fmd_hdl_t * hdl,gmem_dimm_t * dimm)1101529f529SScott Davenport gmem_dimm_destroy(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
1111529f529SScott Davenport {
1121529f529SScott Davenport 	fmd_stat_destroy(hdl, 1, &(dimm->dimm_retstat));
1131529f529SScott Davenport 	gmem_dimm_free(hdl, dimm, FMD_B_TRUE);
1141529f529SScott Davenport }
1151529f529SScott Davenport 
1161529f529SScott Davenport static gmem_dimm_t *
dimm_lookup_by_serial(const char * serial)1171529f529SScott Davenport dimm_lookup_by_serial(const char *serial)
1181529f529SScott Davenport {
1191529f529SScott Davenport 	gmem_dimm_t *dimm;
1201529f529SScott Davenport 
1211529f529SScott Davenport 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
1221529f529SScott Davenport 	    dimm = gmem_list_next(dimm)) {
1231529f529SScott Davenport 		if (strcmp(dimm->dimm_serial, serial) == 0)
1241529f529SScott Davenport 			return (dimm);
1251529f529SScott Davenport 	}
1261529f529SScott Davenport 
1271529f529SScott Davenport 	return (NULL);
1281529f529SScott Davenport }
1291529f529SScott Davenport 
1301529f529SScott Davenport gmem_dimm_t *
gmem_dimm_create(fmd_hdl_t * hdl,nvlist_t * asru,nvlist_t * det)1318f665889STrang Do gmem_dimm_create(fmd_hdl_t *hdl, nvlist_t *asru, nvlist_t *det)
1321529f529SScott Davenport {
1331529f529SScott Davenport 	gmem_dimm_t *dimm;
1341529f529SScott Davenport 	nvlist_t *fmri;
1351529f529SScott Davenport 	char *serial;
1368f665889STrang Do 	uint32_t chip_id;
1371529f529SScott Davenport 
1381529f529SScott Davenport 	if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial) != 0) {
1391529f529SScott Davenport 		fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
1401529f529SScott Davenport 		return (NULL);
1411529f529SScott Davenport 	}
1421529f529SScott Davenport 
1431529f529SScott Davenport 	if (nvlist_dup(asru, &fmri, 0) != 0) {
1441529f529SScott Davenport 		fmd_hdl_debug(hdl, "dimm create nvlist dup failed");
1451529f529SScott Davenport 		return (NULL);
1461529f529SScott Davenport 	}
1471529f529SScott Davenport 
1488f665889STrang Do 	(void) gmem_find_dimm_chip(det, &chip_id);
1498f665889STrang Do 
1501529f529SScott Davenport 	fmd_hdl_debug(hdl, "dimm_create: creating new DIMM serial=%s\n",
1511529f529SScott Davenport 	    serial);
1521529f529SScott Davenport 	GMEM_STAT_BUMP(dimm_creat);
1531529f529SScott Davenport 
1541529f529SScott Davenport 	dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
1551529f529SScott Davenport 	dimm->dimm_nodetype = GMEM_NT_DIMM;
1561529f529SScott Davenport 	dimm->dimm_version = GMEM_DIMM_VERSION;
157d4ac42a1STrang Do 	dimm->dimm_phys_addr_low = ULLONG_MAX;
158d4ac42a1STrang Do 	dimm->dimm_phys_addr_hi = 0;
159d4ac42a1STrang Do 	dimm->dimm_syl_error = USHRT_MAX;
1608f665889STrang Do 	dimm->dimm_chipid = chip_id;
1611529f529SScott Davenport 
1621529f529SScott Davenport 	gmem_bufname(dimm->dimm_bufname, sizeof (dimm->dimm_bufname), "dimm_%s",
1631529f529SScott Davenport 	    serial);
1641529f529SScott Davenport 	gmem_fmri_init(hdl, &dimm->dimm_asru, fmri, "dimm_asru_%s", serial);
1651529f529SScott Davenport 
1661529f529SScott Davenport 	nvlist_free(fmri);
1671529f529SScott Davenport 
1681529f529SScott Davenport 	(void) nvlist_lookup_string(dimm->dimm_asru_nvl, FM_FMRI_HC_SERIAL_ID,
1691529f529SScott Davenport 	    (char **)&dimm->dimm_serial);
1701529f529SScott Davenport 
1711529f529SScott Davenport 	gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat, dimm->dimm_serial,
1721529f529SScott Davenport 	    0, GMEM_DIMM_STAT_PREFIX);
1731529f529SScott Davenport 
1741529f529SScott Davenport 	gmem_list_append(&gmem.gm_dimms, dimm);
1751529f529SScott Davenport 	gmem_dimm_dirty(hdl, dimm);
1761529f529SScott Davenport 
1771529f529SScott Davenport 	return (dimm);
1781529f529SScott Davenport }
1791529f529SScott Davenport 
1801529f529SScott Davenport gmem_dimm_t *
gmem_dimm_lookup(fmd_hdl_t * hdl,nvlist_t * asru)1811529f529SScott Davenport gmem_dimm_lookup(fmd_hdl_t *hdl, nvlist_t *asru)
1821529f529SScott Davenport {
1831529f529SScott Davenport 	gmem_dimm_t *dimm;
1841529f529SScott Davenport 	char *serial;
1851529f529SScott Davenport 	int err;
1861529f529SScott Davenport 
1871529f529SScott Davenport 	err = nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial);
1881529f529SScott Davenport 
1891529f529SScott Davenport 	if (err != 0) {
1901529f529SScott Davenport 		fmd_hdl_debug(hdl, "Can't get dimm serial number\n");
1911529f529SScott Davenport 		GMEM_STAT_BUMP(bad_mem_resource);
1921529f529SScott Davenport 		return (NULL);
1931529f529SScott Davenport 	}
1941529f529SScott Davenport 
1951529f529SScott Davenport 	dimm = dimm_lookup_by_serial(serial);
1961529f529SScott Davenport 	return (dimm);
1971529f529SScott Davenport }
1981529f529SScott Davenport 
199d4ac42a1STrang Do 
2001529f529SScott Davenport static gmem_dimm_t *
gmem_dimm_v0tov1(fmd_hdl_t * hdl,gmem_dimm_0_t * old,size_t oldsz)201d4ac42a1STrang Do gmem_dimm_v0tov1(fmd_hdl_t *hdl, gmem_dimm_0_t *old, size_t oldsz)
202d4ac42a1STrang Do {
203d4ac42a1STrang Do 	gmem_dimm_t *new;
204d4ac42a1STrang Do 	if (oldsz != sizeof (gmem_dimm_0_t)) {
205d4ac42a1STrang Do 		fmd_hdl_abort(hdl, "size of state doesn't match size of "
206d4ac42a1STrang Do 		    "version 0 state (%u bytes).\n", sizeof (gmem_dimm_0_t));
207d4ac42a1STrang Do 	}
208d4ac42a1STrang Do 
209d4ac42a1STrang Do 	new = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
210d4ac42a1STrang Do 	new->dimm_header = old->dimm0_header;
211d4ac42a1STrang Do 	new->dimm_version = GMEM_DIMM_VERSION;
212d4ac42a1STrang Do 	new->dimm_asru = old->dimm0_asru;
213d4ac42a1STrang Do 	new->dimm_nretired = old->dimm0_nretired;
214d4ac42a1STrang Do 	new->dimm_phys_addr_hi = 0;
215d4ac42a1STrang Do 	new->dimm_phys_addr_low = ULLONG_MAX;
216d4ac42a1STrang Do 
217d4ac42a1STrang Do 	fmd_hdl_free(hdl, old, oldsz);
218d4ac42a1STrang Do 	return (new);
219d4ac42a1STrang Do }
220d4ac42a1STrang Do 
221d4ac42a1STrang Do static gmem_dimm_t *
gmem_dimm_wrapv1(fmd_hdl_t * hdl,gmem_dimm_pers_t * pers,size_t psz)222d4ac42a1STrang Do gmem_dimm_wrapv1(fmd_hdl_t *hdl, gmem_dimm_pers_t *pers, size_t psz)
2231529f529SScott Davenport {
2241529f529SScott Davenport 	gmem_dimm_t *dimm;
2251529f529SScott Davenport 
2261529f529SScott Davenport 	if (psz != sizeof (gmem_dimm_pers_t)) {
2271529f529SScott Davenport 		fmd_hdl_abort(hdl, "size of state doesn't match size of "
2281529f529SScott Davenport 		    "version 0 state (%u bytes).\n", sizeof (gmem_dimm_pers_t));
2291529f529SScott Davenport 	}
2301529f529SScott Davenport 
2311529f529SScott Davenport 	dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
2321529f529SScott Davenport 	bcopy(pers, dimm, sizeof (gmem_dimm_pers_t));
2331529f529SScott Davenport 	fmd_hdl_free(hdl, pers, psz);
2341529f529SScott Davenport 	return (dimm);
2351529f529SScott Davenport }
2361529f529SScott Davenport 
2371529f529SScott Davenport void *
gmem_dimm_restore(fmd_hdl_t * hdl,fmd_case_t * cp,gmem_case_ptr_t * ptr)2381529f529SScott Davenport gmem_dimm_restore(fmd_hdl_t *hdl, fmd_case_t *cp, gmem_case_ptr_t *ptr)
2391529f529SScott Davenport {
2401529f529SScott Davenport 	gmem_dimm_t *dimm;
2411529f529SScott Davenport 
2421529f529SScott Davenport 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
2431529f529SScott Davenport 	    dimm = gmem_list_next(dimm)) {
2441529f529SScott Davenport 		if (strcmp(dimm->dimm_bufname, ptr->ptr_name) == 0)
2451529f529SScott Davenport 			break;
2461529f529SScott Davenport 	}
2471529f529SScott Davenport 
2481529f529SScott Davenport 	if (dimm == NULL) {
249d4ac42a1STrang Do 		int migrated = 0;
2501529f529SScott Davenport 		size_t dimmsz;
2511529f529SScott Davenport 
2521529f529SScott Davenport 		fmd_hdl_debug(hdl, "restoring dimm from %s\n", ptr->ptr_name);
2531529f529SScott Davenport 
2541529f529SScott Davenport 		if ((dimmsz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) {
2551529f529SScott Davenport 			fmd_hdl_abort(hdl, "dimm referenced by case %s does "
2561529f529SScott Davenport 			    "not exist in saved state\n",
2571529f529SScott Davenport 			    fmd_case_uuid(hdl, cp));
2581529f529SScott Davenport 		} else if (dimmsz > GMEM_DIMM_MAXSIZE ||
2591529f529SScott Davenport 		    dimmsz < GMEM_DIMM_MINSIZE) {
2601529f529SScott Davenport 			fmd_hdl_abort(hdl, "dimm buffer referenced by case %s "
2611529f529SScott Davenport 			    "is out of bounds (is %u bytes, max %u, min %u)\n",
2621529f529SScott Davenport 			    fmd_case_uuid(hdl, cp), dimmsz,
2631529f529SScott Davenport 			    GMEM_DIMM_MAXSIZE, GMEM_DIMM_MINSIZE);
2641529f529SScott Davenport 		}
2651529f529SScott Davenport 
2661529f529SScott Davenport 		if ((dimm = gmem_buf_read(hdl, NULL, ptr->ptr_name,
2671529f529SScott Davenport 		    dimmsz)) == NULL) {
2681529f529SScott Davenport 			fmd_hdl_abort(hdl, "failed to read dimm buf %s",
2691529f529SScott Davenport 			    ptr->ptr_name);
2701529f529SScott Davenport 		}
2711529f529SScott Davenport 
2721529f529SScott Davenport 		fmd_hdl_debug(hdl, "found %d in version field\n",
2731529f529SScott Davenport 		    dimm->dimm_version);
2741529f529SScott Davenport 
275d4ac42a1STrang Do 		if (GMEM_DIMM_VERSIONED(dimm)) {
276d4ac42a1STrang Do 
277d4ac42a1STrang Do 			switch (dimm->dimm_version) {
278d4ac42a1STrang Do 			case GMEM_DIMM_VERSION_1:
279d4ac42a1STrang Do 				dimm = gmem_dimm_wrapv1(hdl,
280d4ac42a1STrang Do 				    (gmem_dimm_pers_t *)dimm, dimmsz);
281d4ac42a1STrang Do 				break;
282d4ac42a1STrang Do 			default:
283d4ac42a1STrang Do 				fmd_hdl_abort(hdl, "unknown version (found %d) "
284d4ac42a1STrang Do 				    "for dimm state referenced by case %s.\n",
285d4ac42a1STrang Do 				    dimm->dimm_version, fmd_case_uuid(hdl, cp));
286d4ac42a1STrang Do 				break;
287d4ac42a1STrang Do 			}
288d4ac42a1STrang Do 		} else {
289d4ac42a1STrang Do 			dimm = gmem_dimm_v0tov1(hdl, (gmem_dimm_0_t *)dimm,
2901529f529SScott Davenport 			    dimmsz);
291d4ac42a1STrang Do 			migrated = 1;
292d4ac42a1STrang Do 		}
293d4ac42a1STrang Do 
294d4ac42a1STrang Do 		if (migrated) {
295d4ac42a1STrang Do 			GMEM_STAT_BUMP(dimm_migrat);
296d4ac42a1STrang Do 			gmem_dimm_dirty(hdl, dimm);
2971529f529SScott Davenport 		}
2981529f529SScott Davenport 
2991529f529SScott Davenport 		gmem_fmri_restore(hdl, &dimm->dimm_asru);
3001529f529SScott Davenport 
3011529f529SScott Davenport 		if ((errno = nvlist_lookup_string(dimm->dimm_asru_nvl,
3021529f529SScott Davenport 		    FM_FMRI_HC_SERIAL_ID, (char **)&dimm->dimm_serial)) != 0)
3031529f529SScott Davenport 			fmd_hdl_abort(hdl,
3041529f529SScott Davenport 			    "failed to retrieve serial from asru");
3051529f529SScott Davenport 
3061529f529SScott Davenport 
3071529f529SScott Davenport 		gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat,
3081529f529SScott Davenport 		    dimm->dimm_serial, dimm->dimm_nretired,
3091529f529SScott Davenport 		    GMEM_DIMM_STAT_PREFIX);
3101529f529SScott Davenport 
3111529f529SScott Davenport 		gmem_list_append(&gmem.gm_dimms, dimm);
3121529f529SScott Davenport 	}
3131529f529SScott Davenport 
3141529f529SScott Davenport 	switch (ptr->ptr_subtype) {
3151529f529SScott Davenport 	case GMEM_PTR_DIMM_CASE:
3161529f529SScott Davenport 		gmem_mem_case_restore(hdl, &dimm->dimm_case, cp, "dimm",
3171529f529SScott Davenport 		    dimm->dimm_serial);
3181529f529SScott Davenport 		break;
3191529f529SScott Davenport 	default:
3201529f529SScott Davenport 		fmd_hdl_abort(hdl, "invalid %s subtype %d\n",
3211529f529SScott Davenport 		    ptr->ptr_name, ptr->ptr_subtype);
3221529f529SScott Davenport 	}
3231529f529SScott Davenport 
3241529f529SScott Davenport 	return (dimm);
3251529f529SScott Davenport }
3261529f529SScott Davenport 
3271529f529SScott Davenport void
gmem_dimm_validate(fmd_hdl_t * hdl)3281529f529SScott Davenport gmem_dimm_validate(fmd_hdl_t *hdl)
3291529f529SScott Davenport {
3301529f529SScott Davenport 	gmem_dimm_t *dimm, *next;
3311529f529SScott Davenport 
3321529f529SScott Davenport 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; dimm = next) {
3331529f529SScott Davenport 		next = gmem_list_next(dimm);
3341529f529SScott Davenport 
3351529f529SScott Davenport 		if (!gmem_dimm_present(hdl, dimm->dimm_asru_nvl))
3361529f529SScott Davenport 			gmem_dimm_destroy(hdl, dimm);
3371529f529SScott Davenport 	}
3381529f529SScott Davenport }
3391529f529SScott Davenport 
3401529f529SScott Davenport void
gmem_dimm_dirty(fmd_hdl_t * hdl,gmem_dimm_t * dimm)3411529f529SScott Davenport gmem_dimm_dirty(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
3421529f529SScott Davenport {
3431529f529SScott Davenport 	if (fmd_buf_size(hdl, NULL, dimm->dimm_bufname) !=
3441529f529SScott Davenport 	    sizeof (gmem_dimm_pers_t))
3451529f529SScott Davenport 		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
3461529f529SScott Davenport 
3471529f529SScott Davenport 	/* No need to rewrite the FMRIs in the dimm - they don't change */
3481529f529SScott Davenport 	fmd_buf_write(hdl, NULL, dimm->dimm_bufname, &dimm->dimm_pers,
3491529f529SScott Davenport 	    sizeof (gmem_dimm_pers_t));
3501529f529SScott Davenport }
3511529f529SScott Davenport 
3521529f529SScott Davenport void
gmem_dimm_gc(fmd_hdl_t * hdl)3531529f529SScott Davenport gmem_dimm_gc(fmd_hdl_t *hdl)
3541529f529SScott Davenport {
3551529f529SScott Davenport 	gmem_dimm_validate(hdl);
3561529f529SScott Davenport }
3571529f529SScott Davenport 
3581529f529SScott Davenport void
gmem_dimm_fini(fmd_hdl_t * hdl)3591529f529SScott Davenport gmem_dimm_fini(fmd_hdl_t *hdl)
3601529f529SScott Davenport {
3611529f529SScott Davenport 	gmem_dimm_t *dimm;
3621529f529SScott Davenport 
3631529f529SScott Davenport 	while ((dimm = gmem_list_next(&gmem.gm_dimms)) != NULL)
3641529f529SScott Davenport 		gmem_dimm_free(hdl, dimm, FMD_B_FALSE);
3651529f529SScott Davenport }
3661529f529SScott Davenport 
3671529f529SScott Davenport 
3681529f529SScott Davenport /*ARGSUSED*/
3691529f529SScott Davenport static int
find_dimm_hc_fmri(topo_hdl_t * thp,tnode_t * node,void * arg)3701529f529SScott Davenport find_dimm_hc_fmri(topo_hdl_t *thp, tnode_t *node, void *arg)
3711529f529SScott Davenport {
3721529f529SScott Davenport 
3731529f529SScott Davenport 	char *topo_sn;
3741529f529SScott Davenport 	dimmid_t *dimmid = (dimmid_t *)arg;
3751529f529SScott Davenport 	nvlist_t *fru = NULL;
3761529f529SScott Davenport 	nvlist_t *rsc = NULL;
3771529f529SScott Davenport 	nvlist_t *asru = NULL;
3781529f529SScott Davenport 	int err;
3791529f529SScott Davenport 
3801529f529SScott Davenport 	if (topo_node_fru(node, &fru, NULL, &err) < 0)
3811529f529SScott Davenport 		return (TOPO_WALK_NEXT);
3821529f529SScott Davenport 
3831529f529SScott Davenport 	err = nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &topo_sn);
3841529f529SScott Davenport 	if (err != 0) {
3851529f529SScott Davenport 		nvlist_free(fru);
3861529f529SScott Davenport 		return (TOPO_WALK_NEXT);
3871529f529SScott Davenport 	}
3881529f529SScott Davenport 
3891529f529SScott Davenport 	if (strcmp(dimmid->serial, topo_sn) != 0) {
3901529f529SScott Davenport 		nvlist_free(fru);
3911529f529SScott Davenport 		return (TOPO_WALK_NEXT);
3921529f529SScott Davenport 	}
3931529f529SScott Davenport 
3941529f529SScott Davenport 	switch (dimmid->type) {
3951529f529SScott Davenport 		case FINDFRU:
3961529f529SScott Davenport 			(void) nvlist_dup(fru, &dimm_nvl, NV_UNIQUE_NAME);
3971529f529SScott Davenport 			break;
3981529f529SScott Davenport 		case FINDRSC:
3991529f529SScott Davenport 			(void) topo_node_resource(node, &rsc, &err);
4001529f529SScott Davenport 			if (rsc != NULL) {
4011529f529SScott Davenport 				(void) nvlist_dup(rsc, &dimm_nvl,
4021529f529SScott Davenport 				    NV_UNIQUE_NAME);
4031529f529SScott Davenport 				nvlist_free(rsc);
4041529f529SScott Davenport 			}
4051529f529SScott Davenport 			break;
4061529f529SScott Davenport 		case FINDASRU:
4071529f529SScott Davenport 			(void) topo_node_asru(node, &asru, NULL, &err);
4081529f529SScott Davenport 			if (asru != NULL) {
4091529f529SScott Davenport 				(void) nvlist_dup(asru, &dimm_nvl,
4101529f529SScott Davenport 				    NV_UNIQUE_NAME);
4111529f529SScott Davenport 				nvlist_free(asru);
4121529f529SScott Davenport 			}
4131529f529SScott Davenport 			break;
4141529f529SScott Davenport 		default:
4151529f529SScott Davenport 			break;
4161529f529SScott Davenport 	}
4171529f529SScott Davenport 	nvlist_free(fru);
4181529f529SScott Davenport 	return (TOPO_WALK_TERMINATE);
4191529f529SScott Davenport }
4201529f529SScott Davenport 
4211529f529SScott Davenport nvlist_t *
gmem_find_dimm_by_sn(fmd_hdl_t * hdl,dimmid_t * dimmid)4221529f529SScott Davenport gmem_find_dimm_by_sn(fmd_hdl_t *hdl, dimmid_t *dimmid) {
4231529f529SScott Davenport 	topo_hdl_t *thp;
4241529f529SScott Davenport 	topo_walk_t *twp;
4251529f529SScott Davenport 	int err;
4261529f529SScott Davenport 	dimm_nvl = NULL;
4271529f529SScott Davenport 
4281529f529SScott Davenport 	if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
4291529f529SScott Davenport 		return (NULL);
4301529f529SScott Davenport 
4311529f529SScott Davenport 	if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
4321529f529SScott Davenport 	    find_dimm_hc_fmri, dimmid, &err)) == NULL) {
4331529f529SScott Davenport 		fmd_hdl_topo_rele(hdl, thp);
4341529f529SScott Davenport 		return (NULL);
4351529f529SScott Davenport 	}
4361529f529SScott Davenport 
4371529f529SScott Davenport 	(void) topo_walk_step(twp, TOPO_WALK_CHILD);
4381529f529SScott Davenport 	topo_walk_fini(twp);
4391529f529SScott Davenport 	fmd_hdl_topo_rele(hdl, thp);
4401529f529SScott Davenport 	return (dimm_nvl);
4411529f529SScott Davenport }
4421529f529SScott Davenport 
4431529f529SScott Davenport nvlist_t *
gmem_find_dimm_fru(fmd_hdl_t * hdl,char * sn)4441529f529SScott Davenport gmem_find_dimm_fru(fmd_hdl_t *hdl, char *sn)
4451529f529SScott Davenport {
4461529f529SScott Davenport 	dimmid_t fru;
4471529f529SScott Davenport 	(void) strcpy(fru.serial, sn);
4481529f529SScott Davenport 	fru.type = FINDFRU;
4491529f529SScott Davenport 	return (gmem_find_dimm_by_sn(hdl, &fru));
4501529f529SScott Davenport }
4511529f529SScott Davenport 
4521529f529SScott Davenport nvlist_t *
gmem_find_dimm_rsc(fmd_hdl_t * hdl,char * sn)4531529f529SScott Davenport gmem_find_dimm_rsc(fmd_hdl_t *hdl, char *sn)
4541529f529SScott Davenport {
4551529f529SScott Davenport 	dimmid_t rsc;
4561529f529SScott Davenport 	(void) strcpy(rsc.serial, sn);
4571529f529SScott Davenport 	rsc.type = FINDRSC;
4581529f529SScott Davenport 	return (gmem_find_dimm_by_sn(hdl, &rsc));
4591529f529SScott Davenport }
4601529f529SScott Davenport 
4611529f529SScott Davenport nvlist_t *
gmem_find_dimm_asru(fmd_hdl_t * hdl,char * sn)4621529f529SScott Davenport gmem_find_dimm_asru(fmd_hdl_t *hdl, char *sn)
4631529f529SScott Davenport {
4641529f529SScott Davenport 	dimmid_t asru;
4651529f529SScott Davenport 	(void) strcpy(asru.serial, sn);
4661529f529SScott Davenport 	asru.type = FINDASRU;
4671529f529SScott Davenport 	return (gmem_find_dimm_by_sn(hdl, &asru));
4681529f529SScott Davenport }
4691529f529SScott Davenport 
4701529f529SScott Davenport int
gmem_dimm_present(fmd_hdl_t * hdl,nvlist_t * asru)4711529f529SScott Davenport gmem_dimm_present(fmd_hdl_t *hdl, nvlist_t *asru)
4721529f529SScott Davenport {
4731529f529SScott Davenport 	char *sn;
4741529f529SScott Davenport 	nvlist_t *dimm = NULL;
4751529f529SScott Davenport 
4761529f529SScott Davenport 	if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &sn) != 0) {
4771529f529SScott Davenport 		fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
4781529f529SScott Davenport 		return (0);
4791529f529SScott Davenport 	}
4801529f529SScott Davenport 	dimm = gmem_find_dimm_fru(hdl, sn);
4811529f529SScott Davenport 	if (dimm == NULL) {
4821529f529SScott Davenport 		fmd_hdl_debug(hdl, "Dimm sn=%s is not present\n", sn);
4831529f529SScott Davenport 		return (0);
4841529f529SScott Davenport 	}
485*aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(dimm);
4861529f529SScott Davenport 	return (1);
4871529f529SScott Davenport }
488d4ac42a1STrang Do 
489d4ac42a1STrang Do static int
gmem_find_dimm_chip(nvlist_t * nvl,uint32_t * chip)490d4ac42a1STrang Do gmem_find_dimm_chip(nvlist_t *nvl, uint32_t *chip)
491d4ac42a1STrang Do {
492d4ac42a1STrang Do 
493d4ac42a1STrang Do 	char *name, *id, *end;
494d4ac42a1STrang Do 	nvlist_t **hcl;
495d4ac42a1STrang Do 	uint_t n;
496d4ac42a1STrang Do 	int i;
497d4ac42a1STrang Do 	int rc = 0;
4988f665889STrang Do 	*chip = ULONG_MAX;
499d4ac42a1STrang Do 
500d4ac42a1STrang Do 	if (nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &n) < 0)
501d4ac42a1STrang Do 		return (0);
502d4ac42a1STrang Do 	for (i = 0; i < n; i++) {
503d4ac42a1STrang Do 		(void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name);
504d4ac42a1STrang Do 		(void) nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &id);
505d4ac42a1STrang Do 
506d4ac42a1STrang Do 		if (strcmp(name, "chip") == 0) {
507d4ac42a1STrang Do 			*chip = (uint32_t)strtoul(id, &end, 10);
508d4ac42a1STrang Do 			rc = 1;
509d4ac42a1STrang Do 			break;
510d4ac42a1STrang Do 		}
511d4ac42a1STrang Do 	}
512d4ac42a1STrang Do 	return (rc);
513d4ac42a1STrang Do }
514d4ac42a1STrang Do 
5158f665889STrang Do /*ARGSUSED*/
516d4ac42a1STrang Do int
gmem_same_datapath_dimms(fmd_hdl_t * hdl,gmem_dimm_t * d1,gmem_dimm_t * d2)517d4ac42a1STrang Do gmem_same_datapath_dimms(fmd_hdl_t *hdl, gmem_dimm_t *d1, gmem_dimm_t *d2)
518d4ac42a1STrang Do {
519d4ac42a1STrang Do 
5208f665889STrang Do 	if (d1->dimm_chipid == ULONG_MAX || d2->dimm_chipid == ULONG_MAX)
521d4ac42a1STrang Do 		return (0);
522d4ac42a1STrang Do 
5238f665889STrang Do 	if (d1->dimm_chipid == d2->dimm_chipid)
5248f665889STrang Do 		return (1);
525d4ac42a1STrang Do 
526d4ac42a1STrang Do 	return (0);
527d4ac42a1STrang Do }
528d4ac42a1STrang Do 
529d4ac42a1STrang Do int
gmem_check_symbol_error(fmd_hdl_t * hdl,gmem_dimm_t * d,uint16_t upos)530d4ac42a1STrang Do gmem_check_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos)
531d4ac42a1STrang Do {
532d4ac42a1STrang Do 	gmem_dimm_t *dimm = NULL, *next = NULL;
533d4ac42a1STrang Do 
534d4ac42a1STrang Do 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
535d4ac42a1STrang Do 	    dimm = next) {
536d4ac42a1STrang Do 		next = gmem_list_next(dimm);
537d4ac42a1STrang Do 		if (gmem_same_datapath_dimms(hdl, dimm, d) &&
538d4ac42a1STrang Do 		    dimm->dimm_syl_error == upos)
539d4ac42a1STrang Do 			return (1);
540d4ac42a1STrang Do 	}
541d4ac42a1STrang Do 	return (0);
542d4ac42a1STrang Do }
543d4ac42a1STrang Do 
544d4ac42a1STrang Do void
gmem_save_symbol_error(fmd_hdl_t * hdl,gmem_dimm_t * d,uint16_t upos)545d4ac42a1STrang Do gmem_save_symbol_error(fmd_hdl_t *hdl, gmem_dimm_t *d, uint16_t upos)
546d4ac42a1STrang Do {
547d4ac42a1STrang Do 	gmem_dimm_t *dimm = NULL, *next = NULL;
548d4ac42a1STrang Do 
549d4ac42a1STrang Do 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
550d4ac42a1STrang Do 	    dimm = next) {
551d4ac42a1STrang Do 		next = gmem_list_next(dimm);
552d4ac42a1STrang Do 		if (gmem_same_datapath_dimms(hdl, dimm, d))
553d4ac42a1STrang Do 			dimm->dimm_syl_error = upos;
554d4ac42a1STrang Do 	}
555d4ac42a1STrang Do }