xref: /onnv-gate/usr/src/cmd/fm/schemes/mem/sparc/mem_disc.c (revision 6828:8532ff8de343)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
51414Scindi  * Common Development and Distribution License (the "License").
61414Scindi  * You may not use this file except in compliance with the License.
71414Scindi  *
81414Scindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91414Scindi  * or http://www.opensolaris.org/os/licensing.
101414Scindi  * See the License for the specific language governing permissions
111414Scindi  * and limitations under the License.
121414Scindi  *
131414Scindi  * When distributing Covered Code, include this CDDL HEADER in each
141414Scindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151414Scindi  * If applicable, add the following below this CDDL HEADER, with the
161414Scindi  * fields enclosed by brackets "[]" replaced with your own identifying
171414Scindi  * information: Portions Copyright [yyyy] [name of copyright owner]
181414Scindi  *
191414Scindi  * CDDL HEADER END
201414Scindi  */
211414Scindi 
221414Scindi /*
236198Stsien  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241414Scindi  * Use is subject to license terms.
251414Scindi  */
261414Scindi 
271414Scindi #pragma ident	"%Z%%M%	%I%	%E% SMI"
281414Scindi 
291414Scindi /*
301414Scindi  * DIMM unum/device map construction
311414Scindi  *
321414Scindi  * The map is constructed from PICL configuration files, which contain a map
331414Scindi  * between a form of the unum and the device to be used for serial number
341414Scindi  * retrieval.  We massage the PICL unum into a form that matches the one used
351414Scindi  * by mem FMRIs, creating a map entry from the munged version.  As described
361414Scindi  * below, two configuration files must be correlated to determine the correct
371414Scindi  * device path, and thus to build the mem_dimm_map_t list.  While platforms
381414Scindi  * without PICL configuration files are acceptable (some platforms, like
391414Scindi  * Serengeti and Starcat, don't have configuration files as of this writing),
401414Scindi  * platforms with only one or the other aren't.
411414Scindi  *
421414Scindi  * On Sun4v platforms, we read the 'mdesc' machine description file in order
431414Scindi  * to obtain the mapping between dimm unum+jnum strings (which denote slot
441414Scindi  * names) and the serial numbers of the dimms occupying those slots.
451414Scindi  */
461414Scindi 
471414Scindi #include <sys/param.h>
481414Scindi #include <sys/mdesc.h>
491414Scindi 
501414Scindi #include <mem.h>
511414Scindi #include <fm/fmd_fmri.h>
521414Scindi 
534759Ssd77468 #include <fcntl.h>
544759Ssd77468 #include <unistd.h>
551414Scindi #include <stdio.h>
561414Scindi #include <stdlib.h>
571414Scindi #include <string.h>
581414Scindi #include <strings.h>
591414Scindi #include <errno.h>
604759Ssd77468 #include <time.h>
614759Ssd77468 #include <sys/mem.h>
622072Svn83148 #include <sys/fm/ldom.h>
631414Scindi 
642072Svn83148 extern ldom_hdl_t *mem_scheme_lhp;
651414Scindi 
661414Scindi #define	PICL_FRUTREE_PATH \
671414Scindi 	"%s/usr/platform/%s/lib/picl/plugins/piclfrutree.conf"
681414Scindi 
691414Scindi #define	PICL_FRUDATA_PATH \
701414Scindi 	"%s/usr/platform/%s/lib/picl/plugins/libpiclfrudata.conf"
711414Scindi 
721414Scindi typedef struct mem_path_map {
731414Scindi 	struct mem_path_map *pm_next;
741414Scindi 	char *pm_path;
751414Scindi 	char *pm_fullpath;
761414Scindi } mem_path_map_t;
771414Scindi 
781414Scindi typedef struct label_xlators {
791414Scindi 	const char *lx_infmt;
801414Scindi 	uint_t lx_matches;
811414Scindi 	const char *lx_outfmt;
821414Scindi } label_xlators_t;
831414Scindi 
841414Scindi /*
851414Scindi  * PICL configuration files use a different format for the DIMM name (unum)
861414Scindi  * than that used in mem FMRIs.  The following patterns and routine are used
871414Scindi  * to convert between the PICL and unum formats.
881414Scindi  */
891414Scindi static const label_xlators_t label_xlators[] = {
901414Scindi 	{ "/system-board/mem-slot?Label=J%4d%5$n", 1,
911414Scindi 	    "J%04d" },
921414Scindi 	{ "/system-board/mem-slot?Label=DIMM%1d%5$n", 1,
931414Scindi 	    "DIMM%d" },
941414Scindi 	{ "/system-board/cpu-mem-slot?Label=%4$c/mem-slot?Label=J%1$4d%5$n", 2,
951414Scindi 	    "Slot %4$c: J%1$4d" },
961414Scindi 	{ "/MB/system-board/mem-slot?Label=DIMM%1d%5$n", 1,
971414Scindi 	    "DIMM%d" },
981414Scindi 	{ "/MB/system-board/P%1d/cpu/B%1d/bank/D%1d%5$n", 3,
991414Scindi 	    "MB/P%d/B%d/D%d" },
1001414Scindi 	{ "/MB/system-board/C%1d/cpu-module/P0/cpu/B%1d/bank/D%1d%5$n", 3,
1011414Scindi 	    "MB/C%d/P0/B%d/D%d" },
1021414Scindi 	{ "/MB/system-board/DIMM%1d%5$n", 1,
1031414Scindi 	    "MB/DIMM%d" },
1041414Scindi 	{ "/C%1d/system-board/P0/cpu/B%1d/bank/D%1d%5$n", 3,
1051414Scindi 	    "C%d/P0/B%d/D%d" },
1061414Scindi 	{ NULL }
1071414Scindi };
1081414Scindi 
1091414Scindi static int
label_xlate(char * buf)1101414Scindi label_xlate(char *buf)
1111414Scindi {
1121414Scindi 	const label_xlators_t *xlator;
1131414Scindi 
1141414Scindi 	if (strncmp(buf, "/frutree/chassis", 16) != 0)
1151414Scindi 		return (0);
1161414Scindi 
1171414Scindi 	for (xlator = label_xlators; xlator->lx_infmt != NULL; xlator++) {
1181414Scindi 		uint_t len, a1, a2, a3;
1191414Scindi 		char a4;
1201414Scindi 
1211414Scindi 		if (sscanf(buf + 16, xlator->lx_infmt, &a1, &a2, &a3, &a4,
1221414Scindi 		    &len) == xlator->lx_matches && len == strlen(buf + 16)) {
1231414Scindi 			(void) sprintf(buf, xlator->lx_outfmt, a1, a2, a3, a4);
1241414Scindi 			return (0);
1251414Scindi 		}
1261414Scindi 	}
1271414Scindi 
1281414Scindi 	return (fmd_fmri_set_errno(EINVAL));
1291414Scindi }
1301414Scindi 
1311414Scindi /*
1321414Scindi  * Match two paths taken from picl files.  This is a normal component-based path
1331414Scindi  * comparison, but for the fact that components `foo' and `foo@1,2' are assumed
1341414Scindi  * to be equal.  `foo@1,2' and `foo@3,4', however, are not assumed to be equal.
1351414Scindi  */
1361414Scindi static int
picl_path_eq(const char * p1,const char * p2)1371414Scindi picl_path_eq(const char *p1, const char *p2)
1381414Scindi {
1391414Scindi 	for (;;) {
1401414Scindi 		if (*p1 == *p2) {
1411414Scindi 			if (*p1 == '\0')
1421414Scindi 				return (1);
1431414Scindi 			else {
1441414Scindi 				p1++;
1451414Scindi 				p2++;
1461414Scindi 				continue;
1471414Scindi 			}
1481414Scindi 		}
1491414Scindi 
1501414Scindi 		if (*p1 == '@' && (*p2 == '/' || *p2 == '\0')) {
1511414Scindi 			while (*p1 != '/' && *p1 != '\0')
1521414Scindi 				p1++;
1531414Scindi 			continue;
1541414Scindi 		}
1551414Scindi 
1561414Scindi 		if ((*p1 == '/' || *p1 == '\0') && *p2 == '@') {
1571414Scindi 			while (*p2 != '/' && *p2 != '\0')
1581414Scindi 				p2++;
1591414Scindi 			continue;
1601414Scindi 		}
1611414Scindi 
1621414Scindi 		return (0);
1631414Scindi 	}
1641414Scindi }
1651414Scindi 
1661414Scindi /*
1671414Scindi  * PICL paths begin with `/platform' instead of `/devices', as they are
1681414Scindi  * intended to reference points in the PICL tree, rather than places in the
1691414Scindi  * device tree.  Furthermore, some paths use the construct `?UnitAddress=a,b'
1701414Scindi  * instead of `@a,b' to indicate unit number and address.  This routine
1711414Scindi  * replaces both constructs with forms more appropriate for /devices path
1721414Scindi  * lookup.
1731414Scindi  */
1741414Scindi static void
path_depicl(char * path)1751414Scindi path_depicl(char *path)
1761414Scindi {
1771414Scindi 	char *c;
1781414Scindi 
1791414Scindi 	if (strncmp(path, "name:", 4) == 0)
1801414Scindi 		bcopy(path + 5, path, strlen(path + 5) + 1);
1811414Scindi 
1821414Scindi 	for (c = path; (c = strstr(c, "?UnitAddress=")) != NULL; c++) {
1831414Scindi 		uint_t len = 0;
1841414Scindi 
1851414Scindi 		(void) sscanf(c + 13, "%*x,%*x%n", &len);
1861414Scindi 		if (len == 0)
1871414Scindi 			continue;
1881414Scindi 
1891414Scindi 		*c = '@';
1901414Scindi 		bcopy(c + 13, c + 1, strlen(c + 13) + 1);
1911414Scindi 	}
1921414Scindi }
1931414Scindi 
1941414Scindi /*
1951414Scindi  * The libpiclfrudata configuration file contains a map between the generic
1961414Scindi  * (minor-less) device and the specific device to be used for SPD/SEEPROM
1971414Scindi  * data access.
1981414Scindi  *
1991414Scindi  * Entries are of the form:
2001414Scindi  *
2011414Scindi  * name:/platform/generic-path
2021414Scindi  * PROP FRUDevicePath string r 0 "full-path"
2031414Scindi  *
2041414Scindi  * Where `generic-path' is the path, sans minor name, to be used for DIMM
2051414Scindi  * data access, and `full-path' is the path with the minor name.
2061414Scindi  */
2071414Scindi static int
picl_frudata_parse(char * buf,char * path,void * arg)2081414Scindi picl_frudata_parse(char *buf, char *path, void *arg)
2091414Scindi {
2101414Scindi 	mem_path_map_t **mapp = arg;
2111414Scindi 	mem_path_map_t *pm = NULL;
2121414Scindi 	char fullpath[BUFSIZ];
2131414Scindi 	uint_t len;
2141414Scindi 
2151414Scindi 	if (sscanf(buf, " PROP FRUDevicePath string r 0 \"%[^\"]\" \n%n",
2161414Scindi 	    fullpath, &len) != 1 || fullpath[0] == '\0' || len != strlen(buf))
2171414Scindi 		return (0);
2181414Scindi 
2191414Scindi 	path_depicl(path);
2201414Scindi 
2211414Scindi 	pm = fmd_fmri_alloc(sizeof (mem_path_map_t));
2221414Scindi 	pm->pm_path = fmd_fmri_strdup(path);
2231414Scindi 	pm->pm_fullpath = fmd_fmri_strdup(fullpath);
2241414Scindi 
2251414Scindi 	pm->pm_next = *mapp;
2261414Scindi 	*mapp = pm;
2271414Scindi 
2281414Scindi 	return (1);
2291414Scindi }
2301414Scindi 
2311414Scindi /*
2321414Scindi  * The piclfrutree configuration file contains a map between a form of the
2331414Scindi  * DIMM's unum and the generic (minor-less) device used for SPD/SEEPROM data
2341414Scindi  * access.
2351414Scindi  *
2361414Scindi  * Entries are of the form:
2371414Scindi  *
2381414Scindi  * name:/frutree/chassis/picl-unum
2391414Scindi  * REFNODE mem-module fru WITH /platform/generic-path
2401414Scindi  *
2411414Scindi  * Where `picl-unum' is the PICL form of the unum, which we'll massage into
2421414Scindi  * the form compatible with FMRIs (see label_xlate), and `generic-path' is
2431414Scindi  * the minor-less path into the PICL tree for the device used to access the
2441414Scindi  * DIMM.  It is this path that will be used as the key in the frudata
2451414Scindi  * configuration file to determine the proper /devices path.
2461414Scindi  */
2471414Scindi typedef struct dimm_map_arg {
2481414Scindi 	mem_path_map_t *dma_pm;
2491414Scindi 	mem_dimm_map_t *dma_dm;
2501414Scindi } dimm_map_arg_t;
2511414Scindi 
2521414Scindi static int
picl_frutree_parse(char * buf,char * label,void * arg)2531414Scindi picl_frutree_parse(char *buf, char *label, void *arg)
2541414Scindi {
2551414Scindi 	dimm_map_arg_t *dma = arg;
2561414Scindi 	mem_dimm_map_t *dm = NULL;
2571414Scindi 	mem_path_map_t *pm;
2581414Scindi 	char path[BUFSIZ];
2591414Scindi 	uint_t len;
2601414Scindi 
2611414Scindi 	/* LINTED - sscanf cannot exceed sizeof (path) */
2621414Scindi 	if (sscanf(buf, " REFNODE mem-module fru WITH %s \n%n",
2631414Scindi 	    path, &len) != 1 || path[0] == '\0' || len != strlen(buf))
2641414Scindi 		return (0);
2651414Scindi 
2661414Scindi 	if (label_xlate(label) < 0)
2671414Scindi 		return (-1); /* errno is set for us */
2681414Scindi 
2691414Scindi 	path_depicl(path);
2701414Scindi 
2711414Scindi 	for (pm = dma->dma_pm; pm != NULL; pm = pm->pm_next) {
2721414Scindi 		if (picl_path_eq(pm->pm_path, path)) {
2731414Scindi 			(void) strcpy(path, pm->pm_fullpath);
2741414Scindi 			break;
2751414Scindi 		}
2761414Scindi 	}
2771414Scindi 
2781414Scindi 	dm = fmd_fmri_zalloc(sizeof (mem_dimm_map_t));
2791414Scindi 	dm->dm_label = fmd_fmri_strdup(label);
2801414Scindi 	dm->dm_device = fmd_fmri_strdup(path);
2811414Scindi 
2821414Scindi 	dm->dm_next = dma->dma_dm;
2831414Scindi 	dma->dma_dm = dm;
2841414Scindi 
2851414Scindi 	return (1);
2861414Scindi }
2871414Scindi 
2881414Scindi /*
2891414Scindi  * Both configuration files use the same format, thus allowing us to use the
2901414Scindi  * same parser to process them.
2911414Scindi  */
2921414Scindi static int
picl_conf_parse(const char * pathpat,int (* func)(char *,char *,void *),void * arg)2931414Scindi picl_conf_parse(const char *pathpat, int (*func)(char *, char *, void *),
2941414Scindi     void *arg)
2951414Scindi {
2961414Scindi 	char confpath[MAXPATHLEN];
2971414Scindi 	char buf[BUFSIZ], label[BUFSIZ];
2981414Scindi 	int line, len, rc;
2991414Scindi 	FILE *fp;
3001414Scindi 
3011414Scindi 	(void) snprintf(confpath, sizeof (confpath), pathpat,
3021414Scindi 	    fmd_fmri_get_rootdir(), fmd_fmri_get_platform());
3031414Scindi 
3041414Scindi 	if ((fp = fopen(confpath, "r")) == NULL)
3051414Scindi 		return (-1); /* errno is set for us */
3061414Scindi 
3071414Scindi 	label[0] = '\0';
3081414Scindi 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
3091414Scindi 		if (buf[0] == '#')
3101414Scindi 			continue;
3111414Scindi 
3121414Scindi 		if (buf[0] == '\n') {
3131414Scindi 			label[0] = '\0';
3141414Scindi 			continue;
3151414Scindi 		}
3161414Scindi 
3171414Scindi 		/* LINTED - label length cannot exceed length of buf */
3181414Scindi 		if (sscanf(buf, " name:%s \n%n", label, &len) == 1 &&
3191414Scindi 		    label[0] != '\0' && len == strlen(buf))
3201414Scindi 			continue;
3211414Scindi 
3221414Scindi 		if (label[0] != '\0') {
3231414Scindi 			if ((rc = func(buf, label, arg)) < 0) {
3241414Scindi 				int err = errno;
3251414Scindi 				(void) fclose(fp);
3261414Scindi 				return (fmd_fmri_set_errno(err));
3271414Scindi 			} else if (rc != 0) {
3281414Scindi 				label[0] = '\0';
3291414Scindi 			}
3301414Scindi 		}
3311414Scindi 	}
3321414Scindi 
3331414Scindi 	(void) fclose(fp);
3341414Scindi 	return (0);
3351414Scindi }
3361414Scindi 
3371414Scindi static void
path_map_destroy(mem_path_map_t * pm)3381414Scindi path_map_destroy(mem_path_map_t *pm)
3391414Scindi {
3401414Scindi 	mem_path_map_t *next;
3411414Scindi 
3421414Scindi 	for (/* */; pm != NULL; pm = next) {
3431414Scindi 		next = pm->pm_next;
3441414Scindi 
3451414Scindi 		fmd_fmri_strfree(pm->pm_path);
3461414Scindi 		fmd_fmri_strfree(pm->pm_fullpath);
3471414Scindi 		fmd_fmri_free(pm, sizeof (mem_path_map_t));
3481414Scindi 	}
3491414Scindi }
3501414Scindi 
3514759Ssd77468 int
mem_discover(void)352*6828Stsien mem_discover(void)
3531414Scindi {
3541414Scindi 	mem_path_map_t *path_map = NULL;
3551414Scindi 	dimm_map_arg_t dma;
3561414Scindi 	int rc;
3571414Scindi 
3581414Scindi 	if (picl_conf_parse(PICL_FRUDATA_PATH, picl_frudata_parse,
3591414Scindi 	    &path_map) < 0 && errno != ENOENT)
3601414Scindi 		return (-1); /* errno is set for us */
3611414Scindi 
3621414Scindi 	dma.dma_pm = path_map;
3631414Scindi 	dma.dma_dm = NULL;
3641414Scindi 
3651414Scindi 	if ((rc = picl_conf_parse(PICL_FRUTREE_PATH, picl_frutree_parse,
3661414Scindi 	    &dma)) < 0 && errno == ENOENT && path_map == NULL) {
3671414Scindi 		/*
3681414Scindi 		 * This platform doesn't support serial number retrieval via
3691414Scindi 		 * PICL mapping files.  Unfortunate, but not an error.
3701414Scindi 		 */
3711414Scindi 		return (0);
3721414Scindi 	}
3731414Scindi 
3741414Scindi 	path_map_destroy(path_map);
3751414Scindi 
3761414Scindi 	if (rc < 0)
3771414Scindi 		return (-1); /* errno is set for us */
3781414Scindi 
3791414Scindi 	if (dma.dma_dm == NULL) {
3801414Scindi 		/*
3811414Scindi 		 * This platform should support DIMM serial numbers, but we
3821414Scindi 		 * weren't able to derive the paths.  Return an error.
3831414Scindi 		 */
3841414Scindi 		return (fmd_fmri_set_errno(EIO));
3851414Scindi 	}
3861414Scindi 
3871414Scindi 	mem.mem_dm = dma.dma_dm;
3881414Scindi 	return (0);
3891414Scindi }
3901414Scindi 
3911414Scindi /*
3924759Ssd77468  * Retry values for handling the case where the kernel is not yet ready
3934759Ssd77468  * to provide DIMM serial ids.  Some platforms acquire DIMM serial id
3944759Ssd77468  * information from their System Controller via a mailbox interface.
3954759Ssd77468  * The values chosen are for 10 retries 3 seconds apart to approximate the
3964759Ssd77468  * possible 30 second timeout length of a mailbox message request.
3974759Ssd77468  */
3984759Ssd77468 #define	MAX_MEM_SID_RETRIES	10
3994759Ssd77468 #define	MEM_SID_RETRY_WAIT	3
4004759Ssd77468 
4014759Ssd77468 /*
4024759Ssd77468  * The comparison is asymmetric. It compares up to the length of the
4034759Ssd77468  * argument unum.
4044759Ssd77468  */
4054759Ssd77468 static mem_dimm_map_t *
dm_lookup(const char * name)4064759Ssd77468 dm_lookup(const char *name)
4074759Ssd77468 {
4084759Ssd77468 	mem_dimm_map_t *dm;
4094759Ssd77468 
4104759Ssd77468 	for (dm = mem.mem_dm; dm != NULL; dm = dm->dm_next) {
4114759Ssd77468 		if (strncmp(name, dm->dm_label, strlen(name)) == 0)
4124759Ssd77468 			return (dm);
4134759Ssd77468 	}
4144759Ssd77468 
4154759Ssd77468 	return (NULL);
4164759Ssd77468 }
4174759Ssd77468 
4184759Ssd77468 /*
4194759Ssd77468  * Returns 0 with serial numbers if found, -1 (with errno set) for errors.  If
4204759Ssd77468  * the unum (or a component of same) wasn't found, -1 is returned with errno
4214759Ssd77468  * set to ENOENT.  If the kernel doesn't have support for serial numbers,
4224759Ssd77468  * -1 is returned with errno set to ENOTSUP.
4234759Ssd77468  */
4244759Ssd77468 static int
mem_get_serids_from_kernel(const char * unum,char *** seridsp,size_t * nseridsp)4254759Ssd77468 mem_get_serids_from_kernel(const char *unum, char ***seridsp, size_t *nseridsp)
4264759Ssd77468 {
4274759Ssd77468 	char **dimms, **serids;
4284759Ssd77468 	size_t ndimms, nserids;
4294759Ssd77468 	int i, rc = 0;
4304759Ssd77468 	int fd;
4314759Ssd77468 	int retries = MAX_MEM_SID_RETRIES;
4324759Ssd77468 	mem_name_t mn;
4334759Ssd77468 	struct timespec rqt;
4344759Ssd77468 
4354759Ssd77468 	if ((fd = open("/dev/mem", O_RDONLY)) < 0)
4364759Ssd77468 		return (-1);
4374759Ssd77468 
4384759Ssd77468 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0) {
4394759Ssd77468 		(void) close(fd);
4404759Ssd77468 		return (-1); /* errno is set for us */
4414759Ssd77468 	}
4424759Ssd77468 
4434759Ssd77468 	serids = fmd_fmri_zalloc(sizeof (char *) * ndimms);
4444759Ssd77468 	nserids = ndimms;
4454759Ssd77468 
4464759Ssd77468 	bzero(&mn, sizeof (mn));
4474759Ssd77468 
4484759Ssd77468 	for (i = 0; i < ndimms; i++) {
4494759Ssd77468 		mn.m_namelen = strlen(dimms[i]) + 1;
4504759Ssd77468 		mn.m_sidlen = MEM_SERID_MAXLEN;
4514759Ssd77468 
4524759Ssd77468 		mn.m_name = fmd_fmri_alloc(mn.m_namelen);
4534759Ssd77468 		mn.m_sid = fmd_fmri_alloc(mn.m_sidlen);
4544759Ssd77468 
4554759Ssd77468 		(void) strcpy(mn.m_name, dimms[i]);
4564759Ssd77468 
4574759Ssd77468 		do {
4584759Ssd77468 			rc = ioctl(fd, MEM_SID, &mn);
4594759Ssd77468 
4604759Ssd77468 			if (rc >= 0 || errno != EAGAIN)
4614759Ssd77468 				break;
4624759Ssd77468 
4634759Ssd77468 			if (retries == 0) {
4644759Ssd77468 				errno = ETIMEDOUT;
4654759Ssd77468 				break;
4664759Ssd77468 			}
4674759Ssd77468 
4684759Ssd77468 			/*
4694759Ssd77468 			 * EAGAIN indicates the kernel is
4704759Ssd77468 			 * not ready to provide DIMM serial
4714759Ssd77468 			 * ids.  Sleep MEM_SID_RETRY_WAIT seconds
4724759Ssd77468 			 * and try again.
4734759Ssd77468 			 * nanosleep() is used instead of sleep()
4744759Ssd77468 			 * to avoid interfering with fmd timers.
4754759Ssd77468 			 */
4764759Ssd77468 			rqt.tv_sec = MEM_SID_RETRY_WAIT;
4774759Ssd77468 			rqt.tv_nsec = 0;
4784759Ssd77468 			(void) nanosleep(&rqt, NULL);
4794759Ssd77468 
4804759Ssd77468 		} while (retries--);
4814759Ssd77468 
4824759Ssd77468 		if (rc < 0) {
4834759Ssd77468 			/*
4844759Ssd77468 			 * ENXIO can happen if the kernel memory driver
4854759Ssd77468 			 * doesn't have the MEM_SID ioctl (e.g. if the
4864759Ssd77468 			 * kernel hasn't been patched to provide the
4874759Ssd77468 			 * support).
4884759Ssd77468 			 *
4894759Ssd77468 			 * If the MEM_SID ioctl is available but the
4904759Ssd77468 			 * particular platform doesn't support providing
4914759Ssd77468 			 * serial ids, ENOTSUP will be returned by the ioctl.
4924759Ssd77468 			 */
4934759Ssd77468 			if (errno == ENXIO)
4944759Ssd77468 				errno = ENOTSUP;
4954759Ssd77468 			fmd_fmri_free(mn.m_name, mn.m_namelen);
4964759Ssd77468 			fmd_fmri_free(mn.m_sid, mn.m_sidlen);
4974759Ssd77468 			mem_strarray_free(serids, nserids);
4984759Ssd77468 			mem_strarray_free(dimms, ndimms);
4994759Ssd77468 			(void) close(fd);
5004759Ssd77468 			return (-1);
5014759Ssd77468 		}
5024759Ssd77468 
5034759Ssd77468 		serids[i] = fmd_fmri_strdup(mn.m_sid);
5044759Ssd77468 
5054759Ssd77468 		fmd_fmri_free(mn.m_name, mn.m_namelen);
5064759Ssd77468 		fmd_fmri_free(mn.m_sid, mn.m_sidlen);
5074759Ssd77468 	}
5084759Ssd77468 
5094759Ssd77468 	mem_strarray_free(dimms, ndimms);
5104759Ssd77468 
5114759Ssd77468 	(void) close(fd);
5124759Ssd77468 
5134759Ssd77468 	*seridsp = serids;
5144759Ssd77468 	*nseridsp = nserids;
5154759Ssd77468 
5164759Ssd77468 	return (0);
5174759Ssd77468 }
5184759Ssd77468 
5194759Ssd77468 /*
5204759Ssd77468  * Returns 0 with serial numbers if found, -1 (with errno set) for errors.  If
5214759Ssd77468  * the unum (or a component of same) wasn't found, -1 is returned with errno
5224759Ssd77468  * set to ENOENT.
5234759Ssd77468  */
5244759Ssd77468 static int
mem_get_serids_from_cache(const char * unum,char *** seridsp,size_t * nseridsp)5254759Ssd77468 mem_get_serids_from_cache(const char *unum, char ***seridsp, size_t *nseridsp)
5264759Ssd77468 {
5274759Ssd77468 	uint64_t drgen = fmd_fmri_get_drgen();
5284759Ssd77468 	char **dimms, **serids;
5294759Ssd77468 	size_t ndimms, nserids;
5304759Ssd77468 	mem_dimm_map_t *dm;
5314759Ssd77468 	int i, rc = 0;
5324759Ssd77468 
5334759Ssd77468 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0)
5344759Ssd77468 		return (-1); /* errno is set for us */
5354759Ssd77468 
5364759Ssd77468 	serids = fmd_fmri_zalloc(sizeof (char *) * ndimms);
5374759Ssd77468 	nserids = ndimms;
5384759Ssd77468 
5394759Ssd77468 	for (i = 0; i < ndimms; i++) {
5404759Ssd77468 		if ((dm = dm_lookup(dimms[i])) == NULL) {
5414759Ssd77468 			rc = fmd_fmri_set_errno(EINVAL);
5424759Ssd77468 			break;
5434759Ssd77468 		}
5444759Ssd77468 
5454759Ssd77468 		if (*dm->dm_serid == '\0' || dm->dm_drgen != drgen) {
5464759Ssd77468 			/*
5474759Ssd77468 			 * We don't have a cached copy, or the copy we've got is
5484759Ssd77468 			 * out of date.  Look it up again.
5494759Ssd77468 			 */
5504759Ssd77468 			if (mem_get_serid(dm->dm_device, dm->dm_serid,
5514759Ssd77468 			    sizeof (dm->dm_serid)) < 0) {
5524759Ssd77468 				rc = -1; /* errno is set for us */
5534759Ssd77468 				break;
5544759Ssd77468 			}
5554759Ssd77468 
5564759Ssd77468 			dm->dm_drgen = drgen;
5574759Ssd77468 		}
5584759Ssd77468 
5594759Ssd77468 		serids[i] = fmd_fmri_strdup(dm->dm_serid);
5604759Ssd77468 	}
5614759Ssd77468 
5624759Ssd77468 	mem_strarray_free(dimms, ndimms);
5634759Ssd77468 
5644759Ssd77468 	if (rc == 0) {
5654759Ssd77468 		*seridsp = serids;
5664759Ssd77468 		*nseridsp = nserids;
5674759Ssd77468 	} else {
5684759Ssd77468 		mem_strarray_free(serids, nserids);
5694759Ssd77468 	}
5704759Ssd77468 
5714759Ssd77468 	return (rc);
5724759Ssd77468 }
5734759Ssd77468 
5744759Ssd77468 int
mem_get_serids_by_unum(const char * unum,char *** seridsp,size_t * nseridsp)5754759Ssd77468 mem_get_serids_by_unum(const char *unum, char ***seridsp, size_t *nseridsp)
5764759Ssd77468 {
5774759Ssd77468 	/*
5784759Ssd77468 	 * Some platforms do not support the caching of serial ids by the
5794759Ssd77468 	 * mem scheme plugin but instead support making serial ids available
5804759Ssd77468 	 * via the kernel.
5814759Ssd77468 	 */
5824759Ssd77468 	if (mem.mem_dm == NULL)
5834759Ssd77468 		return (mem_get_serids_from_kernel(unum, seridsp, nseridsp));
5844759Ssd77468 	else
5854759Ssd77468 		return (mem_get_serids_from_cache(unum, seridsp, nseridsp));
5864759Ssd77468 }
587