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