xref: /onnv-gate/usr/src/cmd/fm/schemes/mem/mem.c (revision 10825:7fd761ba0fb4)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51303Swesolows  * Common Development and Distribution License (the "License").
61303Swesolows  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211303Swesolows 
220Sstevel@tonic-gate /*
23*10825SJakub.Jermar@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <mem.h>
280Sstevel@tonic-gate #include <fm/fmd_fmri.h>
296828Stsien #include <fm/libtopo.h>
307532SSean.Ye@Sun.COM #include <fm/fmd_agent.h>
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <string.h>
330Sstevel@tonic-gate #include <strings.h>
340Sstevel@tonic-gate #include <sys/mem.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate mem_t mem;
370Sstevel@tonic-gate 
381186Sayznaga static int
mem_fmri_get_unum(nvlist_t * nvl,char ** unump)390Sstevel@tonic-gate mem_fmri_get_unum(nvlist_t *nvl, char **unump)
400Sstevel@tonic-gate {
410Sstevel@tonic-gate 	uint8_t version;
420Sstevel@tonic-gate 	char *unum;
430Sstevel@tonic-gate 
440Sstevel@tonic-gate 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
450Sstevel@tonic-gate 	    version > FM_MEM_SCHEME_VERSION ||
460Sstevel@tonic-gate 	    nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0)
470Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
480Sstevel@tonic-gate 
490Sstevel@tonic-gate 	*unump = unum;
500Sstevel@tonic-gate 
510Sstevel@tonic-gate 	return (0);
520Sstevel@tonic-gate }
530Sstevel@tonic-gate 
547532SSean.Ye@Sun.COM static int
page_isretired(nvlist_t * fmri,int * errp)557532SSean.Ye@Sun.COM page_isretired(nvlist_t *fmri, int *errp)
567532SSean.Ye@Sun.COM {
577532SSean.Ye@Sun.COM 	fmd_agent_hdl_t *hdl;
587532SSean.Ye@Sun.COM 	int rc, err;
597532SSean.Ye@Sun.COM 
607532SSean.Ye@Sun.COM 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
617532SSean.Ye@Sun.COM 		return (-1);
627532SSean.Ye@Sun.COM 	rc = fmd_agent_page_isretired(hdl, fmri);
637532SSean.Ye@Sun.COM 	err = fmd_agent_errno(hdl);
647532SSean.Ye@Sun.COM 	fmd_agent_close(hdl);
657532SSean.Ye@Sun.COM 
667532SSean.Ye@Sun.COM 	if (errp != NULL)
677532SSean.Ye@Sun.COM 		*errp = err;
687532SSean.Ye@Sun.COM 	return (rc);
697532SSean.Ye@Sun.COM }
707532SSean.Ye@Sun.COM 
710Sstevel@tonic-gate ssize_t
fmd_fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)720Sstevel@tonic-gate fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
730Sstevel@tonic-gate {
741414Scindi 	char format[64];
750Sstevel@tonic-gate 	ssize_t size, presz;
761414Scindi 	char *rawunum, *preunum, *escunum, *prefix;
771414Scindi 	uint64_t val;
780Sstevel@tonic-gate 	int i;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	if (mem_fmri_get_unum(nvl, &rawunum) < 0)
810Sstevel@tonic-gate 		return (-1); /* errno is set for us */
820Sstevel@tonic-gate 
831414Scindi 	/*
841414Scindi 	 * If we have a well-formed unum (hc-FMRI), use the string verbatim
851414Scindi 	 * to form the initial mem:/// components.  Otherwise use unum=%s.
861414Scindi 	 */
873062Scindi 	if (strncmp(rawunum, "hc://", 5) != 0)
881414Scindi 		prefix = FM_FMRI_MEM_UNUM "=";
891414Scindi 	else
901414Scindi 		prefix = "";
911414Scindi 
921414Scindi 	/*
931414Scindi 	 * If we have a DIMM offset, include it in the string.  If we have a PA
941414Scindi 	 * then use that.  Otherwise just format the unum element.
951414Scindi 	 */
961414Scindi 	if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) {
971414Scindi 		(void) snprintf(format, sizeof (format),
981414Scindi 		    "%s:///%s%%1$s/%s=%%2$llx",
991414Scindi 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET);
1001414Scindi 	} else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
1011414Scindi 		(void) snprintf(format, sizeof (format),
1021414Scindi 		    "%s:///%s%%1$s/%s=%%2$llx",
1031414Scindi 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_PHYSADDR);
1041414Scindi 	} else {
1051414Scindi 		(void) snprintf(format, sizeof (format),
1061414Scindi 		    "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix);
1071414Scindi 	}
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	/*
1103062Scindi 	 * If we have a well-formed unum (hc-FMRI), we skip over the
1113062Scindi 	 * the scheme and authority prefix.
1121414Scindi 	 * Otherwise, the spaces and colons will be escaped,
1131414Scindi 	 * rendering the resulting FMRI pretty much unreadable.
1141414Scindi 	 * We're therefore going to do some escaping of our own first.
1150Sstevel@tonic-gate 	 */
1163062Scindi 	if (strncmp(rawunum, "hc://", 5) == 0) {
1173062Scindi 		rawunum += 5;
1183062Scindi 		rawunum = strchr(rawunum, '/');
1193062Scindi 		++rawunum;
1201414Scindi 		/* LINTED: variable format specifier */
1213062Scindi 		size = snprintf(buf, buflen, format, rawunum, val);
1221414Scindi 	} else {
1231414Scindi 		preunum = fmd_fmri_strdup(rawunum);
1241414Scindi 		presz = strlen(preunum) + 1;
1250Sstevel@tonic-gate 
1261414Scindi 		for (i = 0; i < presz - 1; i++) {
1271414Scindi 			if (preunum[i] == ':' && preunum[i + 1] == ' ') {
1281414Scindi 				bcopy(preunum + i + 2, preunum + i + 1,
1291414Scindi 				    presz - (i + 2));
1301414Scindi 			} else if (preunum[i] == ' ') {
1311414Scindi 				preunum[i] = ',';
1321414Scindi 			}
1330Sstevel@tonic-gate 		}
1341414Scindi 
1351414Scindi 		escunum = fmd_fmri_strescape(preunum);
1361414Scindi 		fmd_fmri_free(preunum, presz);
1370Sstevel@tonic-gate 
1381414Scindi 		/* LINTED: variable format specifier */
1391414Scindi 		size = snprintf(buf, buflen, format, escunum, val);
1401414Scindi 		fmd_fmri_strfree(escunum);
1411414Scindi 	}
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	return (size);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate int
fmd_fmri_expand(nvlist_t * nvl)1470Sstevel@tonic-gate fmd_fmri_expand(nvlist_t *nvl)
1480Sstevel@tonic-gate {
1490Sstevel@tonic-gate 	char *unum, **serids;
1501303Swesolows 	uint_t nnvlserids;
1511303Swesolows 	size_t nserids;
1526828Stsien 	int rc, err = 0;
1536828Stsien 	topo_hdl_t *thp;
1540Sstevel@tonic-gate 
1554512Skd93003 	if ((mem_fmri_get_unum(nvl, &unum) < 0) || (*unum == '\0'))
1560Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1570Sstevel@tonic-gate 
1586828Stsien 	/*
1596828Stsien 	 * If the mem-scheme topology exports this method expand(), invoke it.
1606828Stsien 	 */
1616828Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
1626828Stsien 		return (fmd_fmri_set_errno(EINVAL));
1636828Stsien 	rc = topo_fmri_expand(thp, nvl, &err);
1646828Stsien 	fmd_fmri_topo_rele(thp);
1656828Stsien 	if (err != ETOPO_METHOD_NOTSUP)
1666828Stsien 		return (rc);
1676828Stsien 
1680Sstevel@tonic-gate 	if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID,
1694759Ssd77468 	    &serids, &nnvlserids)) == 0) { /* already have serial #s */
1704759Ssd77468 		return (0);
1714759Ssd77468 	} else if (rc != ENOENT)
1720Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1730Sstevel@tonic-gate 
1741186Sayznaga 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
1751186Sayznaga 		/* errno is set for us */
1761186Sayznaga 		if (errno == ENOTSUP)
1771186Sayznaga 			return (0); /* nothing to add - no s/n support */
1781186Sayznaga 		else
1791186Sayznaga 			return (-1);
1801186Sayznaga 	}
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 	rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids,
1830Sstevel@tonic-gate 	    nserids);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	mem_strarray_free(serids, nserids);
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	if (rc != 0)
1880Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
1894759Ssd77468 	else
1903838Std122701 		return (0);
1910Sstevel@tonic-gate }
1920Sstevel@tonic-gate 
1936292Srobj #ifdef sparc
1940Sstevel@tonic-gate static int
serids_eq(char ** serids1,uint_t nserids1,char ** serids2,uint_t nserids2)1950Sstevel@tonic-gate serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2)
1960Sstevel@tonic-gate {
1970Sstevel@tonic-gate 	int i;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if (nserids1 != nserids2)
2000Sstevel@tonic-gate 		return (0);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	for (i = 0; i < nserids1; i++) {
2030Sstevel@tonic-gate 		if (strcmp(serids1[i], serids2[i]) != 0)
2040Sstevel@tonic-gate 			return (0);
2050Sstevel@tonic-gate 	}
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	return (1);
2080Sstevel@tonic-gate }
2096292Srobj #endif /* sparc */
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate int
fmd_fmri_present(nvlist_t * nvl)2120Sstevel@tonic-gate fmd_fmri_present(nvlist_t *nvl)
2130Sstevel@tonic-gate {
2146292Srobj 	char *unum = NULL;
2156828Stsien 	int rc, err = 0;
2166828Stsien 	struct topo_hdl *thp;
2176292Srobj #ifdef sparc
2186292Srobj 	char **nvlserids, **serids;
2191303Swesolows 	uint_t nnvlserids;
2201303Swesolows 	size_t nserids;
2216292Srobj #else
2226292Srobj 	nvlist_t *unum_nvl;
2237275Sstephh 	nvlist_t *nvlcp = NULL;
2247275Sstephh 	uint64_t val;
2256292Srobj #endif /* sparc */
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	if (mem_fmri_get_unum(nvl, &unum) < 0)
2280Sstevel@tonic-gate 		return (-1); /* errno is set for us */
2290Sstevel@tonic-gate 
2306292Srobj #ifdef sparc
2316828Stsien 	/*
2326828Stsien 	 * If the mem-scheme topology exports this method present(), invoke it.
2336828Stsien 	 */
2346828Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
2356828Stsien 		return (fmd_fmri_set_errno(EINVAL));
2366828Stsien 	rc = topo_fmri_present(thp, nvl, &err);
2376828Stsien 	fmd_fmri_topo_rele(thp);
2386828Stsien 	if (err != ETOPO_METHOD_NOTSUP)
2396828Stsien 		return (rc);
2406828Stsien 
2410Sstevel@tonic-gate 	if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids,
2421186Sayznaga 	    &nnvlserids) != 0) {
2431186Sayznaga 		/*
2441186Sayznaga 		 * Some mem scheme FMRIs don't have serial ids because
2451186Sayznaga 		 * either the platform does not support them, or because
2461186Sayznaga 		 * the FMRI was created before support for serial ids was
2471186Sayznaga 		 * introduced.  If this is the case, assume it is there.
2481186Sayznaga 		 */
2491186Sayznaga 		if (mem.mem_dm == NULL)
2501186Sayznaga 			return (1);
2511186Sayznaga 		else
2521186Sayznaga 			return (fmd_fmri_set_errno(EINVAL));
2531186Sayznaga 	}
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
2561186Sayznaga 		if (errno == ENOTSUP)
2571186Sayznaga 			return (1); /* assume it's there, no s/n support here */
2580Sstevel@tonic-gate 		if (errno != ENOENT) {
2590Sstevel@tonic-gate 			/*
2600Sstevel@tonic-gate 			 * Errors are only signalled to the caller if they're
2610Sstevel@tonic-gate 			 * the caller's fault.  This isn't - it's a failure on
2620Sstevel@tonic-gate 			 * our part to burst or read the serial numbers.  We'll
2630Sstevel@tonic-gate 			 * whine about it, and tell the caller the named
2640Sstevel@tonic-gate 			 * module(s) isn't/aren't there.
2650Sstevel@tonic-gate 			 */
2660Sstevel@tonic-gate 			fmd_fmri_warn("failed to retrieve serial number for "
2670Sstevel@tonic-gate 			    "unum %s", unum);
2680Sstevel@tonic-gate 		}
2690Sstevel@tonic-gate 		return (0);
2700Sstevel@tonic-gate 	}
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	rc = serids_eq(serids, nserids, nvlserids, nnvlserids);
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	mem_strarray_free(serids, nserids);
2756292Srobj #else
2766292Srobj 	/*
2776292Srobj 	 * On X86 we will invoke the topo is_present method passing in the
2786292Srobj 	 * unum, which is in hc scheme.  The libtopo hc-scheme is_present method
2796292Srobj 	 * will invoke the node-specific is_present method, which is implemented
2806292Srobj 	 * by the chip enumerator for rank nodes.  The rank node's is_present
2816292Srobj 	 * method will compare the serial number in the unum with the current
2826292Srobj 	 * serial to determine if the same DIMM is present.
2836292Srobj 	 */
2846292Srobj 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) {
2856292Srobj 		fmd_fmri_warn("failed to get handle to topology");
2866292Srobj 		return (-1);
2876292Srobj 	}
2886292Srobj 	if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) {
2896292Srobj 		rc = topo_fmri_present(thp, unum_nvl, &err);
2906292Srobj 		nvlist_free(unum_nvl);
2916292Srobj 	} else
2926292Srobj 		rc = fmd_fmri_set_errno(EINVAL);
2936292Srobj 	fmd_fmri_topo_rele(thp);
2940Sstevel@tonic-gate 
2957275Sstephh 	/*
2967275Sstephh 	 * Need to check if this is a valid page too. if "isretired" returns
2977275Sstephh 	 * EINVAL, assume page invalid and return not_present.
2987275Sstephh 	 */
2997275Sstephh 	if (rc == 1 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) ==
3007275Sstephh 	    0 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 &&
3017275Sstephh 	    mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) {
3027532SSean.Ye@Sun.COM 		int page_err, rval = page_isretired(nvlcp, &page_err);
3037532SSean.Ye@Sun.COM 		if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL)
3047275Sstephh 			rc = 0;
3057275Sstephh 		nvlist_free(nvlcp);
3067275Sstephh 	}
3077275Sstephh #endif	/* sparc */
3087275Sstephh 	return (rc);
3097275Sstephh }
3107275Sstephh 
3117275Sstephh int
fmd_fmri_replaced(nvlist_t * nvl)3127275Sstephh fmd_fmri_replaced(nvlist_t *nvl)
3137275Sstephh {
3147275Sstephh 	char *unum = NULL;
3157275Sstephh 	int rc, err = 0;
3167275Sstephh 	struct topo_hdl *thp;
3177275Sstephh #ifdef sparc
3187275Sstephh 	char **nvlserids, **serids;
3197275Sstephh 	uint_t nnvlserids;
3207275Sstephh 	size_t nserids;
3217275Sstephh #else
3227275Sstephh 	nvlist_t *unum_nvl;
3237275Sstephh 	nvlist_t *nvlcp = NULL;
3247275Sstephh 	uint64_t val;
3257275Sstephh #endif /* sparc */
3267275Sstephh 
3277275Sstephh 	if (mem_fmri_get_unum(nvl, &unum) < 0)
3287275Sstephh 		return (-1); /* errno is set for us */
3297275Sstephh 
3307275Sstephh #ifdef sparc
3317275Sstephh 	/*
3327275Sstephh 	 * If the mem-scheme topology exports this method replaced(), invoke it.
3337275Sstephh 	 */
3347275Sstephh 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
3357275Sstephh 		return (fmd_fmri_set_errno(EINVAL));
3367275Sstephh 	rc = topo_fmri_replaced(thp, nvl, &err);
3377275Sstephh 	fmd_fmri_topo_rele(thp);
3387275Sstephh 	if (err != ETOPO_METHOD_NOTSUP)
3397275Sstephh 		return (rc);
3407275Sstephh 
3417275Sstephh 	if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids,
3427275Sstephh 	    &nnvlserids) != 0) {
3437275Sstephh 		/*
3447275Sstephh 		 * Some mem scheme FMRIs don't have serial ids because
3457275Sstephh 		 * either the platform does not support them, or because
3467275Sstephh 		 * the FMRI was created before support for serial ids was
3477275Sstephh 		 * introduced.  If this is the case, assume it is there.
3487275Sstephh 		 */
3497275Sstephh 		if (mem.mem_dm == NULL)
3507275Sstephh 			return (FMD_OBJ_STATE_UNKNOWN);
3517275Sstephh 		else
3527275Sstephh 			return (fmd_fmri_set_errno(EINVAL));
3537275Sstephh 	}
3547275Sstephh 
3557275Sstephh 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
3567275Sstephh 		if (errno == ENOTSUP)
3577275Sstephh 			return (FMD_OBJ_STATE_UNKNOWN);
3587275Sstephh 		if (errno != ENOENT) {
3597275Sstephh 			/*
3607275Sstephh 			 * Errors are only signalled to the caller if they're
3617275Sstephh 			 * the caller's fault.  This isn't - it's a failure on
3627275Sstephh 			 * our part to burst or read the serial numbers.  We'll
3637275Sstephh 			 * whine about it, and tell the caller the named
3647275Sstephh 			 * module(s) isn't/aren't there.
3657275Sstephh 			 */
3667275Sstephh 			fmd_fmri_warn("failed to retrieve serial number for "
3677275Sstephh 			    "unum %s", unum);
3687275Sstephh 		}
3697275Sstephh 		return (FMD_OBJ_STATE_NOT_PRESENT);
3707275Sstephh 	}
3717275Sstephh 
3727275Sstephh 	rc = serids_eq(serids, nserids, nvlserids, nnvlserids) ?
3737275Sstephh 	    FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED;
3747275Sstephh 
3757275Sstephh 	mem_strarray_free(serids, nserids);
3767275Sstephh #else
3777275Sstephh 	/*
3787275Sstephh 	 * On X86 we will invoke the topo is_replaced method passing in the
3797275Sstephh 	 * unum, which is in hc scheme.  The libtopo hc-scheme is_replaced
3807275Sstephh 	 * method will invoke the node-specific is_replaced method, which is
3817275Sstephh 	 * implemented by the chip enumerator for rank nodes.  The rank node's
3827275Sstephh 	 * is_replaced method will compare the serial number in the unum with
3837275Sstephh 	 * the current serial to determine if the same DIMM is replaced.
3847275Sstephh 	 */
3857275Sstephh 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) {
3867275Sstephh 		fmd_fmri_warn("failed to get handle to topology");
3877275Sstephh 		return (-1);
3887275Sstephh 	}
3897275Sstephh 	if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) {
3907275Sstephh 		rc = topo_fmri_replaced(thp, unum_nvl, &err);
3917275Sstephh 		nvlist_free(unum_nvl);
3927275Sstephh 	} else
3937275Sstephh 		rc = fmd_fmri_set_errno(EINVAL);
3947275Sstephh 	fmd_fmri_topo_rele(thp);
3957275Sstephh 
3967275Sstephh 	/*
3977275Sstephh 	 * Need to check if this is a valid page too. if "isretired" returns
3987275Sstephh 	 * EINVAL, assume page invalid and return not_present.
3997275Sstephh 	 */
4007275Sstephh 	if ((rc == FMD_OBJ_STATE_STILL_PRESENT ||
4017275Sstephh 	    rc == FMD_OBJ_STATE_UNKNOWN) &&
4027275Sstephh 	    nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0 &&
4037275Sstephh 	    nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 &&
4047275Sstephh 	    mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) {
4057532SSean.Ye@Sun.COM 		int page_err, rval = page_isretired(nvlcp, &page_err);
4067532SSean.Ye@Sun.COM 		if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL)
4077275Sstephh 			rc = FMD_OBJ_STATE_NOT_PRESENT;
4087275Sstephh 		nvlist_free(nvlcp);
4097275Sstephh 	}
4106292Srobj #endif	/* sparc */
4110Sstevel@tonic-gate 	return (rc);
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate int
fmd_fmri_contains(nvlist_t * er,nvlist_t * ee)4150Sstevel@tonic-gate fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
4160Sstevel@tonic-gate {
4176828Stsien 	int rc, err = 0;
4186828Stsien 	struct topo_hdl *thp;
4190Sstevel@tonic-gate 	char *erunum, *eeunum;
4201414Scindi 	uint64_t erval = 0, eeval = 0;
4210Sstevel@tonic-gate 
4226828Stsien 	/*
4236828Stsien 	 * If the mem-scheme topology exports this method contains(), invoke it.
4246828Stsien 	 */
4256828Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
4266828Stsien 		return (fmd_fmri_set_errno(EINVAL));
4276828Stsien 	rc = topo_fmri_contains(thp, er, ee, &err);
4286828Stsien 	fmd_fmri_topo_rele(thp);
4296828Stsien 	if (err != ETOPO_METHOD_NOTSUP)
4306828Stsien 		return (rc);
4316828Stsien 
4320Sstevel@tonic-gate 	if (mem_fmri_get_unum(er, &erunum) < 0 ||
4330Sstevel@tonic-gate 	    mem_fmri_get_unum(ee, &eeunum) < 0)
4340Sstevel@tonic-gate 		return (-1); /* errno is set for us */
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	if (mem_unum_contains(erunum, eeunum) <= 0)
4370Sstevel@tonic-gate 		return (0); /* can't parse/match, so assume no containment */
4380Sstevel@tonic-gate 
4391414Scindi 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) {
4401414Scindi 		return (nvlist_lookup_uint64(ee,
4411414Scindi 		    FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval);
4421414Scindi 	}
4431414Scindi 
4441414Scindi 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) {
4451414Scindi 		return (nvlist_lookup_uint64(ee,
4461414Scindi 		    FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval);
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	return (1);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate 
4521414Scindi /*
4531414Scindi  * We can only make a usable/unusable determination for pages.  Mem FMRIs
4541414Scindi  * without page addresses will be reported as usable since Solaris has no
4551414Scindi  * way at present to dynamically disable an entire DIMM or DIMM pair.
4561414Scindi  */
4570Sstevel@tonic-gate int
fmd_fmri_unusable(nvlist_t * nvl)4580Sstevel@tonic-gate fmd_fmri_unusable(nvlist_t *nvl)
4590Sstevel@tonic-gate {
460*10825SJakub.Jermar@Sun.COM 	uint64_t val1, val2;
4610Sstevel@tonic-gate 	uint8_t version;
4626828Stsien 	int rc, err1 = 0, err2;
4631414Scindi 	nvlist_t *nvlcp = NULL;
4641414Scindi 	int retval;
4656828Stsien 	topo_hdl_t *thp;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
4680Sstevel@tonic-gate 	    version > FM_MEM_SCHEME_VERSION)
4690Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
4700Sstevel@tonic-gate 
4716828Stsien 	/*
4726828Stsien 	 * If the mem-scheme topology exports this method unusable(), invoke it.
4736828Stsien 	 */
4746828Stsien 	if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL)
4756828Stsien 		return (fmd_fmri_set_errno(EINVAL));
4766828Stsien 	rc = topo_fmri_unusable(thp, nvl, &err1);
4776828Stsien 	fmd_fmri_topo_rele(thp);
4786828Stsien 	if (err1 != ETOPO_METHOD_NOTSUP)
4796828Stsien 		return (rc);
4806828Stsien 
481*10825SJakub.Jermar@Sun.COM 	err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val1);
482*10825SJakub.Jermar@Sun.COM 	err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val2);
4831414Scindi 
4841414Scindi 	if (err1 == ENOENT && err2 == ENOENT)
4850Sstevel@tonic-gate 		return (0); /* no page, so assume it's still usable */
4861414Scindi 
4871414Scindi 	if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT))
4880Sstevel@tonic-gate 		return (fmd_fmri_set_errno(EINVAL));
4890Sstevel@tonic-gate 
490*10825SJakub.Jermar@Sun.COM 	if ((rc = mem_unum_rewrite(nvl, &nvlcp)) != 0)
491*10825SJakub.Jermar@Sun.COM 		return (fmd_fmri_set_errno(rc));
4921414Scindi 
4931414Scindi 	/*
4941414Scindi 	 * Ask the kernel if the page is retired, using either the rewritten
4951414Scindi 	 * hc FMRI or the original mem FMRI with the specified offset or PA.
4961414Scindi 	 * Refer to the kernel's page_retire_check() for the error codes.
4971414Scindi 	 */
4987532SSean.Ye@Sun.COM 	rc = page_isretired(nvlcp ? nvlcp : nvl, NULL);
4991414Scindi 
5007532SSean.Ye@Sun.COM 	if (rc == FMD_AGENT_RETIRE_FAIL) {
5011414Scindi 		/*
5021414Scindi 		 * The page is not retired and is not scheduled for retirement
5031414Scindi 		 * (i.e. no request pending and has not seen any errors)
5041414Scindi 		 */
5051414Scindi 		retval = 0;
5067532SSean.Ye@Sun.COM 	} else if (rc == FMD_AGENT_RETIRE_DONE ||
5077532SSean.Ye@Sun.COM 	    rc == FMD_AGENT_RETIRE_ASYNC) {
5080Sstevel@tonic-gate 		/*
5090Sstevel@tonic-gate 		 * The page has been retired, is in the process of being
5100Sstevel@tonic-gate 		 * retired, or doesn't exist.  The latter is valid if the page
5110Sstevel@tonic-gate 		 * existed in the past but has been DR'd out.
5120Sstevel@tonic-gate 		 */
5131414Scindi 		retval = 1;
5140Sstevel@tonic-gate 	} else {
5150Sstevel@tonic-gate 		/*
5160Sstevel@tonic-gate 		 * Errors are only signalled to the caller if they're the
5170Sstevel@tonic-gate 		 * caller's fault.  This isn't - it's a failure of the
5180Sstevel@tonic-gate 		 * retirement-check code.  We'll whine about it and tell
5190Sstevel@tonic-gate 		 * the caller the page is unusable.
5200Sstevel@tonic-gate 		 */
5211414Scindi 		fmd_fmri_warn("failed to determine page %s=%llx usability: "
5221414Scindi 		    "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET :
523*10825SJakub.Jermar@Sun.COM 		    FM_FMRI_MEM_PHYSADDR, err1 == 0 ? (u_longlong_t)val1 :
524*10825SJakub.Jermar@Sun.COM 		    (u_longlong_t)val2, rc, errno);
5251414Scindi 		retval = 1;
5260Sstevel@tonic-gate 	}
5271414Scindi 
5281414Scindi 	if (nvlcp)
5291414Scindi 		nvlist_free(nvlcp);
5301414Scindi 
5311414Scindi 	return (retval);
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate int
fmd_fmri_init(void)5350Sstevel@tonic-gate fmd_fmri_init(void)
5360Sstevel@tonic-gate {
5370Sstevel@tonic-gate 	return (mem_discover());
5380Sstevel@tonic-gate }
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate void
fmd_fmri_fini(void)5410Sstevel@tonic-gate fmd_fmri_fini(void)
5420Sstevel@tonic-gate {
5431414Scindi 	mem_dimm_map_t *dm, *em;
5446434Stsien 	mem_bank_map_t *bm, *cm;
5456434Stsien 	mem_grp_t *gm, *hm;
5464759Ssd77468 	mem_seg_map_t *sm, *tm;
5471414Scindi 
5481414Scindi 	for (dm = mem.mem_dm; dm != NULL; dm = em) {
5491414Scindi 		em = dm->dm_next;
5501414Scindi 		fmd_fmri_strfree(dm->dm_label);
5514759Ssd77468 		fmd_fmri_strfree(dm->dm_part);
5521414Scindi 		fmd_fmri_strfree(dm->dm_device);
5531414Scindi 		fmd_fmri_free(dm, sizeof (mem_dimm_map_t));
5541414Scindi 	}
5556434Stsien 	for (bm = mem.mem_bank; bm != NULL; bm = cm) {
5566434Stsien 		cm = bm->bm_next;
5576434Stsien 		fmd_fmri_free(bm, sizeof (mem_bank_map_t));
5586434Stsien 	}
5596434Stsien 	for (gm = mem.mem_group; gm != NULL; gm = hm) {
5606434Stsien 		hm = gm->mg_next;
5616434Stsien 		fmd_fmri_free(gm, sizeof (mem_grp_t));
5626434Stsien 	}
5634759Ssd77468 	for (sm = mem.mem_seg; sm != NULL; sm = tm) {
5644759Ssd77468 		tm = sm->sm_next;
5654759Ssd77468 		fmd_fmri_free(sm, sizeof (mem_seg_map_t));
5664759Ssd77468 	}
5670Sstevel@tonic-gate }
568