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