xref: /illumos-gate/usr/src/cmd/fm/schemes/mem/mem.c (revision aab83bb8)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5749f21d3Swesolows  * Common Development and Distribution License (the "License").
6749f21d3Swesolows  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21749f21d3Swesolows 
227c478bd9Sstevel@tonic-gate /*
23*705dd6c2SJakub Jermar  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <mem.h>
287c478bd9Sstevel@tonic-gate #include <fm/fmd_fmri.h>
2970818f58Stsien #include <fm/libtopo.h>
30e4b86885SCheng Sean Ye #include <fm/fmd_agent.h>
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <strings.h>
347c478bd9Sstevel@tonic-gate #include <sys/mem.h>
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate mem_t mem;
377c478bd9Sstevel@tonic-gate 
38d00f0155Sayznaga static int
mem_fmri_get_unum(nvlist_t * nvl,char ** unump)397c478bd9Sstevel@tonic-gate mem_fmri_get_unum(nvlist_t *nvl, char **unump)
407c478bd9Sstevel@tonic-gate {
417c478bd9Sstevel@tonic-gate 	uint8_t version;
427c478bd9Sstevel@tonic-gate 	char *unum;
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
457c478bd9Sstevel@tonic-gate 	    version > FM_MEM_SCHEME_VERSION ||
467c478bd9Sstevel@tonic-gate 	    nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0)
477c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate 	*unump = unum;
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 	return (0);
527c478bd9Sstevel@tonic-gate }
537c478bd9Sstevel@tonic-gate 
54e4b86885SCheng Sean Ye static int
page_isretired(nvlist_t * fmri,int * errp)55e4b86885SCheng Sean Ye page_isretired(nvlist_t *fmri, int *errp)
56e4b86885SCheng Sean Ye {
57e4b86885SCheng Sean Ye 	fmd_agent_hdl_t *hdl;
58e4b86885SCheng Sean Ye 	int rc, err;
59e4b86885SCheng Sean Ye 
60e4b86885SCheng Sean Ye 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
61e4b86885SCheng Sean Ye 		return (-1);
62e4b86885SCheng Sean Ye 	rc = fmd_agent_page_isretired(hdl, fmri);
63e4b86885SCheng Sean Ye 	err = fmd_agent_errno(hdl);
64e4b86885SCheng Sean Ye 	fmd_agent_close(hdl);
65e4b86885SCheng Sean Ye 
66e4b86885SCheng Sean Ye 	if (errp != NULL)
67e4b86885SCheng Sean Ye 		*errp = err;
68e4b86885SCheng Sean Ye 	return (rc);
69e4b86885SCheng Sean Ye }
70e4b86885SCheng Sean Ye 
717c478bd9Sstevel@tonic-gate ssize_t
fmd_fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)727c478bd9Sstevel@tonic-gate fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
737c478bd9Sstevel@tonic-gate {
747aec1d6eScindi 	char format[64];
757c478bd9Sstevel@tonic-gate 	ssize_t size, presz;
767aec1d6eScindi 	char *rawunum, *preunum, *escunum, *prefix;
777aec1d6eScindi 	uint64_t val;
787c478bd9Sstevel@tonic-gate 	int i;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	if (mem_fmri_get_unum(nvl, &rawunum) < 0)
817c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
827c478bd9Sstevel@tonic-gate 
837aec1d6eScindi 	/*
847aec1d6eScindi 	 * If we have a well-formed unum (hc-FMRI), use the string verbatim
857aec1d6eScindi 	 * to form the initial mem:/// components.  Otherwise use unum=%s.
867aec1d6eScindi 	 */
870eb822a1Scindi 	if (strncmp(rawunum, "hc://", 5) != 0)
887aec1d6eScindi 		prefix = FM_FMRI_MEM_UNUM "=";
897aec1d6eScindi 	else
907aec1d6eScindi 		prefix = "";
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	/*
937aec1d6eScindi 	 * If we have a DIMM offset, include it in the string.  If we have a PA
947aec1d6eScindi 	 * then use that.  Otherwise just format the unum element.
957c478bd9Sstevel@tonic-gate 	 */
967aec1d6eScindi 	if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) {
977aec1d6eScindi 		(void) snprintf(format, sizeof (format),
987aec1d6eScindi 		    "%s:///%s%%1$s/%s=%%2$llx",
997aec1d6eScindi 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET);
1007aec1d6eScindi 	} else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
1017aec1d6eScindi 		(void) snprintf(format, sizeof (format),
1027aec1d6eScindi 		    "%s:///%s%%1$s/%s=%%2$llx",
1037aec1d6eScindi 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_PHYSADDR);
1047aec1d6eScindi 	} else {
1057aec1d6eScindi 		(void) snprintf(format, sizeof (format),
1067aec1d6eScindi 		    "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix);
1077aec1d6eScindi 	}
1087aec1d6eScindi 
1097aec1d6eScindi 	/*
1100eb822a1Scindi 	 * If we have a well-formed unum (hc-FMRI), we skip over the
1110eb822a1Scindi 	 * the scheme and authority prefix.
1127aec1d6eScindi 	 * Otherwise, the spaces and colons will be escaped,
1137aec1d6eScindi 	 * rendering the resulting FMRI pretty much unreadable.
1147aec1d6eScindi 	 * We're therefore going to do some escaping of our own first.
1157aec1d6eScindi 	 */
1160eb822a1Scindi 	if (strncmp(rawunum, "hc://", 5) == 0) {
1170eb822a1Scindi 		rawunum += 5;
1180eb822a1Scindi 		rawunum = strchr(rawunum, '/');
1190eb822a1Scindi 		++rawunum;
1207aec1d6eScindi 		/* LINTED: variable format specifier */
1210eb822a1Scindi 		size = snprintf(buf, buflen, format, rawunum, val);
1227aec1d6eScindi 	} else {
1237c478bd9Sstevel@tonic-gate 		preunum = fmd_fmri_strdup(rawunum);
1247c478bd9Sstevel@tonic-gate 		presz = strlen(preunum) + 1;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 		for (i = 0; i < presz - 1; i++) {
1277c478bd9Sstevel@tonic-gate 			if (preunum[i] == ':' && preunum[i + 1] == ' ') {
1287c478bd9Sstevel@tonic-gate 				bcopy(preunum + i + 2, preunum + i + 1,
1297c478bd9Sstevel@tonic-gate 				    presz - (i + 2));
1307c478bd9Sstevel@tonic-gate 			} else if (preunum[i] == ' ') {
1317c478bd9Sstevel@tonic-gate 				preunum[i] = ',';
1327c478bd9Sstevel@tonic-gate 			}
1337c478bd9Sstevel@tonic-gate 		}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 		escunum = fmd_fmri_strescape(preunum);
1367c478bd9Sstevel@tonic-gate 		fmd_fmri_free(preunum, presz);
1377c478bd9Sstevel@tonic-gate 
1387aec1d6eScindi 		/* LINTED: variable format specifier */
1397aec1d6eScindi 		size = snprintf(buf, buflen, format, escunum, val);
1407c478bd9Sstevel@tonic-gate 		fmd_fmri_strfree(escunum);
1417aec1d6eScindi 	}
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	return (size);
1447c478bd9Sstevel@tonic-gate }
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate int
fmd_fmri_expand(nvlist_t * nvl)1477c478bd9Sstevel@tonic-gate fmd_fmri_expand(nvlist_t *nvl)
1487c478bd9Sstevel@tonic-gate {
1497c478bd9Sstevel@tonic-gate 	char *unum, **serids;
150749f21d3Swesolows 	uint_t nnvlserids;
151749f21d3Swesolows 	size_t nserids;
15270818f58Stsien 	int rc, err = 0;
15370818f58Stsien 	topo_hdl_t *thp;
1547c478bd9Sstevel@tonic-gate 
155822f48a6Skd93003 	if ((mem_fmri_get_unum(nvl, &unum) < 0) || (*unum == '\0'))
1567c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1577c478bd9Sstevel@tonic-gate 
15870818f58Stsien 	/*
15970818f58Stsien 	 * If the mem-scheme topology exports this method expand(), invoke it.
16070818f58Stsien 	 */
16170818f58Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
16270818f58Stsien 		return (fmd_fmri_set_errno(EINVAL));
16370818f58Stsien 	rc = topo_fmri_expand(thp, nvl, &err);
16470818f58Stsien 	fmd_fmri_topo_rele(thp);
16570818f58Stsien 	if (err != ETOPO_METHOD_NOTSUP)
16670818f58Stsien 		return (rc);
16770818f58Stsien 
1687c478bd9Sstevel@tonic-gate 	if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID,
169fbd1c0daSsd77468 	    &serids, &nnvlserids)) == 0) { /* already have serial #s */
170fbd1c0daSsd77468 		return (0);
171fbd1c0daSsd77468 	} else if (rc != ENOENT)
1727c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1737c478bd9Sstevel@tonic-gate 
174d00f0155Sayznaga 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
175d00f0155Sayznaga 		/* errno is set for us */
176d00f0155Sayznaga 		if (errno == ENOTSUP)
177d00f0155Sayznaga 			return (0); /* nothing to add - no s/n support */
178d00f0155Sayznaga 		else
179d00f0155Sayznaga 			return (-1);
180d00f0155Sayznaga 	}
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids,
1837c478bd9Sstevel@tonic-gate 	    nserids);
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	mem_strarray_free(serids, nserids);
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate 	if (rc != 0)
1887c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
189fbd1c0daSsd77468 	else
1907c478bd9Sstevel@tonic-gate 		return (0);
1917c478bd9Sstevel@tonic-gate }
1927c478bd9Sstevel@tonic-gate 
1932cb5535aSrobj #ifdef sparc
1947c478bd9Sstevel@tonic-gate static int
serids_eq(char ** serids1,uint_t nserids1,char ** serids2,uint_t nserids2)1957c478bd9Sstevel@tonic-gate serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2)
1967c478bd9Sstevel@tonic-gate {
1977c478bd9Sstevel@tonic-gate 	int i;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if (nserids1 != nserids2)
2007c478bd9Sstevel@tonic-gate 		return (0);
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	for (i = 0; i < nserids1; i++) {
2037c478bd9Sstevel@tonic-gate 		if (strcmp(serids1[i], serids2[i]) != 0)
2047c478bd9Sstevel@tonic-gate 			return (0);
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	return (1);
2087c478bd9Sstevel@tonic-gate }
2092cb5535aSrobj #endif /* sparc */
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate int
fmd_fmri_present(nvlist_t * nvl)2127c478bd9Sstevel@tonic-gate fmd_fmri_present(nvlist_t *nvl)
2137c478bd9Sstevel@tonic-gate {
2142cb5535aSrobj 	char *unum = NULL;
21570818f58Stsien 	int rc, err = 0;
21670818f58Stsien 	struct topo_hdl *thp;
2172cb5535aSrobj #ifdef sparc
2182cb5535aSrobj 	char **nvlserids, **serids;
219749f21d3Swesolows 	uint_t nnvlserids;
220749f21d3Swesolows 	size_t nserids;
2212cb5535aSrobj #else
2222cb5535aSrobj 	nvlist_t *unum_nvl;
22325c6ff4bSstephh 	nvlist_t *nvlcp = NULL;
22425c6ff4bSstephh 	uint64_t val;
2252cb5535aSrobj #endif /* sparc */
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	if (mem_fmri_get_unum(nvl, &unum) < 0)
2287c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
2297c478bd9Sstevel@tonic-gate 
2302cb5535aSrobj #ifdef sparc
23170818f58Stsien 	/*
23270818f58Stsien 	 * If the mem-scheme topology exports this method present(), invoke it.
23370818f58Stsien 	 */
23470818f58Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
23570818f58Stsien 		return (fmd_fmri_set_errno(EINVAL));
23670818f58Stsien 	rc = topo_fmri_present(thp, nvl, &err);
23770818f58Stsien 	fmd_fmri_topo_rele(thp);
23870818f58Stsien 	if (err != ETOPO_METHOD_NOTSUP)
23970818f58Stsien 		return (rc);
24070818f58Stsien 
2417c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids,
242d00f0155Sayznaga 	    &nnvlserids) != 0) {
243d00f0155Sayznaga 		/*
244d00f0155Sayznaga 		 * Some mem scheme FMRIs don't have serial ids because
245d00f0155Sayznaga 		 * either the platform does not support them, or because
246d00f0155Sayznaga 		 * the FMRI was created before support for serial ids was
247d00f0155Sayznaga 		 * introduced.  If this is the case, assume it is there.
248d00f0155Sayznaga 		 */
249d00f0155Sayznaga 		if (mem.mem_dm == NULL)
250d00f0155Sayznaga 			return (1);
251d00f0155Sayznaga 		else
2527c478bd9Sstevel@tonic-gate 			return (fmd_fmri_set_errno(EINVAL));
253d00f0155Sayznaga 	}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
256d00f0155Sayznaga 		if (errno == ENOTSUP)
257d00f0155Sayznaga 			return (1); /* assume it's there, no s/n support here */
2587c478bd9Sstevel@tonic-gate 		if (errno != ENOENT) {
2597c478bd9Sstevel@tonic-gate 			/*
2607c478bd9Sstevel@tonic-gate 			 * Errors are only signalled to the caller if they're
2617c478bd9Sstevel@tonic-gate 			 * the caller's fault.  This isn't - it's a failure on
2627c478bd9Sstevel@tonic-gate 			 * our part to burst or read the serial numbers.  We'll
2637c478bd9Sstevel@tonic-gate 			 * whine about it, and tell the caller the named
2647c478bd9Sstevel@tonic-gate 			 * module(s) isn't/aren't there.
2657c478bd9Sstevel@tonic-gate 			 */
2667c478bd9Sstevel@tonic-gate 			fmd_fmri_warn("failed to retrieve serial number for "
2677c478bd9Sstevel@tonic-gate 			    "unum %s", unum);
2687c478bd9Sstevel@tonic-gate 		}
2697c478bd9Sstevel@tonic-gate 		return (0);
2707c478bd9Sstevel@tonic-gate 	}
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	rc = serids_eq(serids, nserids, nvlserids, nnvlserids);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	mem_strarray_free(serids, nserids);
2752cb5535aSrobj #else
2762cb5535aSrobj 	/*
2772cb5535aSrobj 	 * On X86 we will invoke the topo is_present method passing in the
2782cb5535aSrobj 	 * unum, which is in hc scheme.  The libtopo hc-scheme is_present method
2792cb5535aSrobj 	 * will invoke the node-specific is_present method, which is implemented
2802cb5535aSrobj 	 * by the chip enumerator for rank nodes.  The rank node's is_present
2812cb5535aSrobj 	 * method will compare the serial number in the unum with the current
2822cb5535aSrobj 	 * serial to determine if the same DIMM is present.
2832cb5535aSrobj 	 */
2842cb5535aSrobj 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) {
2852cb5535aSrobj 		fmd_fmri_warn("failed to get handle to topology");
2862cb5535aSrobj 		return (-1);
2872cb5535aSrobj 	}
2882cb5535aSrobj 	if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) {
2892cb5535aSrobj 		rc = topo_fmri_present(thp, unum_nvl, &err);
2902cb5535aSrobj 		nvlist_free(unum_nvl);
2912cb5535aSrobj 	} else
2922cb5535aSrobj 		rc = fmd_fmri_set_errno(EINVAL);
2932cb5535aSrobj 	fmd_fmri_topo_rele(thp);
2947c478bd9Sstevel@tonic-gate 
29525c6ff4bSstephh 	/*
29625c6ff4bSstephh 	 * Need to check if this is a valid page too. if "isretired" returns
29725c6ff4bSstephh 	 * EINVAL, assume page invalid and return not_present.
29825c6ff4bSstephh 	 */
29925c6ff4bSstephh 	if (rc == 1 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) ==
30025c6ff4bSstephh 	    0 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 &&
30125c6ff4bSstephh 	    mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) {
302e4b86885SCheng Sean Ye 		int page_err, rval = page_isretired(nvlcp, &page_err);
303e4b86885SCheng Sean Ye 		if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL)
30425c6ff4bSstephh 			rc = 0;
30525c6ff4bSstephh 		nvlist_free(nvlcp);
30625c6ff4bSstephh 	}
30725c6ff4bSstephh #endif	/* sparc */
30825c6ff4bSstephh 	return (rc);
30925c6ff4bSstephh }
31025c6ff4bSstephh 
31125c6ff4bSstephh int
fmd_fmri_replaced(nvlist_t * nvl)31225c6ff4bSstephh fmd_fmri_replaced(nvlist_t *nvl)
31325c6ff4bSstephh {
31425c6ff4bSstephh 	char *unum = NULL;
31525c6ff4bSstephh 	int rc, err = 0;
31625c6ff4bSstephh 	struct topo_hdl *thp;
31725c6ff4bSstephh #ifdef sparc
31825c6ff4bSstephh 	char **nvlserids, **serids;
31925c6ff4bSstephh 	uint_t nnvlserids;
32025c6ff4bSstephh 	size_t nserids;
32125c6ff4bSstephh #else
32225c6ff4bSstephh 	nvlist_t *unum_nvl;
32325c6ff4bSstephh 	nvlist_t *nvlcp = NULL;
32425c6ff4bSstephh 	uint64_t val;
32525c6ff4bSstephh #endif /* sparc */
32625c6ff4bSstephh 
32725c6ff4bSstephh 	if (mem_fmri_get_unum(nvl, &unum) < 0)
32825c6ff4bSstephh 		return (-1); /* errno is set for us */
32925c6ff4bSstephh 
33025c6ff4bSstephh #ifdef sparc
33125c6ff4bSstephh 	/*
33225c6ff4bSstephh 	 * If the mem-scheme topology exports this method replaced(), invoke it.
33325c6ff4bSstephh 	 */
33425c6ff4bSstephh 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
33525c6ff4bSstephh 		return (fmd_fmri_set_errno(EINVAL));
33625c6ff4bSstephh 	rc = topo_fmri_replaced(thp, nvl, &err);
33725c6ff4bSstephh 	fmd_fmri_topo_rele(thp);
33825c6ff4bSstephh 	if (err != ETOPO_METHOD_NOTSUP)
33925c6ff4bSstephh 		return (rc);
34025c6ff4bSstephh 
34125c6ff4bSstephh 	if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids,
34225c6ff4bSstephh 	    &nnvlserids) != 0) {
34325c6ff4bSstephh 		/*
34425c6ff4bSstephh 		 * Some mem scheme FMRIs don't have serial ids because
34525c6ff4bSstephh 		 * either the platform does not support them, or because
34625c6ff4bSstephh 		 * the FMRI was created before support for serial ids was
34725c6ff4bSstephh 		 * introduced.  If this is the case, assume it is there.
34825c6ff4bSstephh 		 */
34925c6ff4bSstephh 		if (mem.mem_dm == NULL)
35025c6ff4bSstephh 			return (FMD_OBJ_STATE_UNKNOWN);
35125c6ff4bSstephh 		else
35225c6ff4bSstephh 			return (fmd_fmri_set_errno(EINVAL));
35325c6ff4bSstephh 	}
35425c6ff4bSstephh 
35525c6ff4bSstephh 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
35625c6ff4bSstephh 		if (errno == ENOTSUP)
35725c6ff4bSstephh 			return (FMD_OBJ_STATE_UNKNOWN);
35825c6ff4bSstephh 		if (errno != ENOENT) {
35925c6ff4bSstephh 			/*
36025c6ff4bSstephh 			 * Errors are only signalled to the caller if they're
36125c6ff4bSstephh 			 * the caller's fault.  This isn't - it's a failure on
36225c6ff4bSstephh 			 * our part to burst or read the serial numbers.  We'll
36325c6ff4bSstephh 			 * whine about it, and tell the caller the named
36425c6ff4bSstephh 			 * module(s) isn't/aren't there.
36525c6ff4bSstephh 			 */
36625c6ff4bSstephh 			fmd_fmri_warn("failed to retrieve serial number for "
36725c6ff4bSstephh 			    "unum %s", unum);
36825c6ff4bSstephh 		}
36925c6ff4bSstephh 		return (FMD_OBJ_STATE_NOT_PRESENT);
37025c6ff4bSstephh 	}
37125c6ff4bSstephh 
37225c6ff4bSstephh 	rc = serids_eq(serids, nserids, nvlserids, nnvlserids) ?
37325c6ff4bSstephh 	    FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED;
37425c6ff4bSstephh 
37525c6ff4bSstephh 	mem_strarray_free(serids, nserids);
37625c6ff4bSstephh #else
37725c6ff4bSstephh 	/*
37825c6ff4bSstephh 	 * On X86 we will invoke the topo is_replaced method passing in the
37925c6ff4bSstephh 	 * unum, which is in hc scheme.  The libtopo hc-scheme is_replaced
38025c6ff4bSstephh 	 * method will invoke the node-specific is_replaced method, which is
38125c6ff4bSstephh 	 * implemented by the chip enumerator for rank nodes.  The rank node's
38225c6ff4bSstephh 	 * is_replaced method will compare the serial number in the unum with
38325c6ff4bSstephh 	 * the current serial to determine if the same DIMM is replaced.
38425c6ff4bSstephh 	 */
38525c6ff4bSstephh 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) {
38625c6ff4bSstephh 		fmd_fmri_warn("failed to get handle to topology");
38725c6ff4bSstephh 		return (-1);
38825c6ff4bSstephh 	}
38925c6ff4bSstephh 	if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) {
39025c6ff4bSstephh 		rc = topo_fmri_replaced(thp, unum_nvl, &err);
39125c6ff4bSstephh 		nvlist_free(unum_nvl);
39225c6ff4bSstephh 	} else
39325c6ff4bSstephh 		rc = fmd_fmri_set_errno(EINVAL);
39425c6ff4bSstephh 	fmd_fmri_topo_rele(thp);
39525c6ff4bSstephh 
39625c6ff4bSstephh 	/*
39725c6ff4bSstephh 	 * Need to check if this is a valid page too. if "isretired" returns
39825c6ff4bSstephh 	 * EINVAL, assume page invalid and return not_present.
39925c6ff4bSstephh 	 */
40025c6ff4bSstephh 	if ((rc == FMD_OBJ_STATE_STILL_PRESENT ||
40125c6ff4bSstephh 	    rc == FMD_OBJ_STATE_UNKNOWN) &&
40225c6ff4bSstephh 	    nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0 &&
40325c6ff4bSstephh 	    nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 &&
40425c6ff4bSstephh 	    mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) {
405e4b86885SCheng Sean Ye 		int page_err, rval = page_isretired(nvlcp, &page_err);
406e4b86885SCheng Sean Ye 		if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL)
40725c6ff4bSstephh 			rc = FMD_OBJ_STATE_NOT_PRESENT;
40825c6ff4bSstephh 		nvlist_free(nvlcp);
40925c6ff4bSstephh 	}
4102cb5535aSrobj #endif	/* sparc */
4117c478bd9Sstevel@tonic-gate 	return (rc);
4127c478bd9Sstevel@tonic-gate }
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate int
fmd_fmri_contains(nvlist_t * er,nvlist_t * ee)4157c478bd9Sstevel@tonic-gate fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
4167c478bd9Sstevel@tonic-gate {
41770818f58Stsien 	int rc, err = 0;
41870818f58Stsien 	struct topo_hdl *thp;
4197c478bd9Sstevel@tonic-gate 	char *erunum, *eeunum;
4207aec1d6eScindi 	uint64_t erval = 0, eeval = 0;
4217c478bd9Sstevel@tonic-gate 
42270818f58Stsien 	/*
42370818f58Stsien 	 * If the mem-scheme topology exports this method contains(), invoke it.
42470818f58Stsien 	 */
42570818f58Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
42670818f58Stsien 		return (fmd_fmri_set_errno(EINVAL));
42770818f58Stsien 	rc = topo_fmri_contains(thp, er, ee, &err);
42870818f58Stsien 	fmd_fmri_topo_rele(thp);
42970818f58Stsien 	if (err != ETOPO_METHOD_NOTSUP)
43070818f58Stsien 		return (rc);
43170818f58Stsien 
4327c478bd9Sstevel@tonic-gate 	if (mem_fmri_get_unum(er, &erunum) < 0 ||
4337c478bd9Sstevel@tonic-gate 	    mem_fmri_get_unum(ee, &eeunum) < 0)
4347c478bd9Sstevel@tonic-gate 		return (-1); /* errno is set for us */
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	if (mem_unum_contains(erunum, eeunum) <= 0)
4377c478bd9Sstevel@tonic-gate 		return (0); /* can't parse/match, so assume no containment */
4387c478bd9Sstevel@tonic-gate 
4397aec1d6eScindi 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) {
4407aec1d6eScindi 		return (nvlist_lookup_uint64(ee,
4417aec1d6eScindi 		    FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval);
4427aec1d6eScindi 	}
4437aec1d6eScindi 
4447aec1d6eScindi 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) {
4457aec1d6eScindi 		return (nvlist_lookup_uint64(ee,
4467aec1d6eScindi 		    FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval);
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	return (1);
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
4527aec1d6eScindi /*
4537aec1d6eScindi  * We can only make a usable/unusable determination for pages.  Mem FMRIs
4547aec1d6eScindi  * without page addresses will be reported as usable since Solaris has no
4557aec1d6eScindi  * way at present to dynamically disable an entire DIMM or DIMM pair.
4567aec1d6eScindi  */
4577c478bd9Sstevel@tonic-gate int
fmd_fmri_unusable(nvlist_t * nvl)4587c478bd9Sstevel@tonic-gate fmd_fmri_unusable(nvlist_t *nvl)
4597c478bd9Sstevel@tonic-gate {
460*705dd6c2SJakub Jermar 	uint64_t val1, val2;
4617c478bd9Sstevel@tonic-gate 	uint8_t version;
46270818f58Stsien 	int rc, err1 = 0, err2;
4637aec1d6eScindi 	nvlist_t *nvlcp = NULL;
4647aec1d6eScindi 	int retval;
46570818f58Stsien 	topo_hdl_t *thp;
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
4687c478bd9Sstevel@tonic-gate 	    version > FM_MEM_SCHEME_VERSION)
4697c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
4707c478bd9Sstevel@tonic-gate 
47170818f58Stsien 	/*
47270818f58Stsien 	 * If the mem-scheme topology exports this method unusable(), invoke it.
47370818f58Stsien 	 */
47470818f58Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
47570818f58Stsien 		return (fmd_fmri_set_errno(EINVAL));
47670818f58Stsien 	rc = topo_fmri_unusable(thp, nvl, &err1);
47770818f58Stsien 	fmd_fmri_topo_rele(thp);
47870818f58Stsien 	if (err1 != ETOPO_METHOD_NOTSUP)
47970818f58Stsien 		return (rc);
48070818f58Stsien 
481*705dd6c2SJakub Jermar 	err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val1);
482*705dd6c2SJakub Jermar 	err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val2);
4837aec1d6eScindi 
4847aec1d6eScindi 	if (err1 == ENOENT && err2 == ENOENT)
4857c478bd9Sstevel@tonic-gate 		return (0); /* no page, so assume it's still usable */
4867aec1d6eScindi 
4877aec1d6eScindi 	if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT))
4887c478bd9Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
4897c478bd9Sstevel@tonic-gate 
490*705dd6c2SJakub Jermar 	if ((rc = mem_unum_rewrite(nvl, &nvlcp)) != 0)
491*705dd6c2SJakub Jermar 		return (fmd_fmri_set_errno(rc));
4927aec1d6eScindi 
4937aec1d6eScindi 	/*
4947aec1d6eScindi 	 * Ask the kernel if the page is retired, using either the rewritten
4957aec1d6eScindi 	 * hc FMRI or the original mem FMRI with the specified offset or PA.
4967aec1d6eScindi 	 * Refer to the kernel's page_retire_check() for the error codes.
4977aec1d6eScindi 	 */
498e4b86885SCheng Sean Ye 	rc = page_isretired(nvlcp ? nvlcp : nvl, NULL);
4997aec1d6eScindi 
500e4b86885SCheng Sean Ye 	if (rc == FMD_AGENT_RETIRE_FAIL) {
5017aec1d6eScindi 		/*
5027aec1d6eScindi 		 * The page is not retired and is not scheduled for retirement
5037aec1d6eScindi 		 * (i.e. no request pending and has not seen any errors)
5047aec1d6eScindi 		 */
5057aec1d6eScindi 		retval = 0;
506e4b86885SCheng Sean Ye 	} else if (rc == FMD_AGENT_RETIRE_DONE ||
507e4b86885SCheng Sean Ye 	    rc == FMD_AGENT_RETIRE_ASYNC) {
5087c478bd9Sstevel@tonic-gate 		/*
5097c478bd9Sstevel@tonic-gate 		 * The page has been retired, is in the process of being
5107c478bd9Sstevel@tonic-gate 		 * retired, or doesn't exist.  The latter is valid if the page
5117c478bd9Sstevel@tonic-gate 		 * existed in the past but has been DR'd out.
5127c478bd9Sstevel@tonic-gate 		 */
5137aec1d6eScindi 		retval = 1;
5147c478bd9Sstevel@tonic-gate 	} else {
5157c478bd9Sstevel@tonic-gate 		/*
5167c478bd9Sstevel@tonic-gate 		 * Errors are only signalled to the caller if they're the
5177c478bd9Sstevel@tonic-gate 		 * caller's fault.  This isn't - it's a failure of the
5187c478bd9Sstevel@tonic-gate 		 * retirement-check code.  We'll whine about it and tell
5197c478bd9Sstevel@tonic-gate 		 * the caller the page is unusable.
5207c478bd9Sstevel@tonic-gate 		 */
5217aec1d6eScindi 		fmd_fmri_warn("failed to determine page %s=%llx usability: "
5227aec1d6eScindi 		    "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET :
523*705dd6c2SJakub Jermar 		    FM_FMRI_MEM_PHYSADDR, err1 == 0 ? (u_longlong_t)val1 :
524*705dd6c2SJakub Jermar 		    (u_longlong_t)val2, rc, errno);
5257aec1d6eScindi 		retval = 1;
5267c478bd9Sstevel@tonic-gate 	}
5277aec1d6eScindi 
5287aec1d6eScindi 	nvlist_free(nvlcp);
5297aec1d6eScindi 
5307aec1d6eScindi 	return (retval);
5317c478bd9Sstevel@tonic-gate }
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate int
fmd_fmri_init(void)5347c478bd9Sstevel@tonic-gate fmd_fmri_init(void)
5357c478bd9Sstevel@tonic-gate {
5367c478bd9Sstevel@tonic-gate 	return (mem_discover());
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate void
fmd_fmri_fini(void)5407c478bd9Sstevel@tonic-gate fmd_fmri_fini(void)
5417c478bd9Sstevel@tonic-gate {
5427aec1d6eScindi 	mem_dimm_map_t *dm, *em;
543d30c532dStsien 	mem_bank_map_t *bm, *cm;
544d30c532dStsien 	mem_grp_t *gm, *hm;
545fbd1c0daSsd77468 	mem_seg_map_t *sm, *tm;
5467aec1d6eScindi 
5477aec1d6eScindi 	for (dm = mem.mem_dm; dm != NULL; dm = em) {
5487aec1d6eScindi 		em = dm->dm_next;
5497aec1d6eScindi 		fmd_fmri_strfree(dm->dm_label);
550fbd1c0daSsd77468 		fmd_fmri_strfree(dm->dm_part);
5517aec1d6eScindi 		fmd_fmri_strfree(dm->dm_device);
5527aec1d6eScindi 		fmd_fmri_free(dm, sizeof (mem_dimm_map_t));
5537aec1d6eScindi 	}
554d30c532dStsien 	for (bm = mem.mem_bank; bm != NULL; bm = cm) {
555d30c532dStsien 		cm = bm->bm_next;
556d30c532dStsien 		fmd_fmri_free(bm, sizeof (mem_bank_map_t));
557d30c532dStsien 	}
558d30c532dStsien 	for (gm = mem.mem_group; gm != NULL; gm = hm) {
559d30c532dStsien 		hm = gm->mg_next;
560d30c532dStsien 		fmd_fmri_free(gm, sizeof (mem_grp_t));
561d30c532dStsien 	}
562fbd1c0daSsd77468 	for (sm = mem.mem_seg; sm != NULL; sm = tm) {
563fbd1c0daSsd77468 		tm = sm->sm_next;
564fbd1c0daSsd77468 		fmd_fmri_free(sm, sizeof (mem_seg_map_t));
565fbd1c0daSsd77468 	}
5667c478bd9Sstevel@tonic-gate }
567