# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # include ../../../Makefile.cmd # Hammerhead: amd64-only SUBDIRS = $(MACH64) include ../../Makefile.subdirs # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # include ../../Makefile.com SRCS = \ mem.c \ mem_read.c \ mem_unum.c \ mem_util.c # Hammerhead: mem_disc.c removed - was in intel/ ISA subdir, not in tree # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include $(SRC)/Makefile.master.64 include ../../Makefile.targ # Hammerhead: 64-bit only - flatten FM library paths LDLIBS += -L$(ROOTLIB)/fm -lfmd_agent -ltopo LDFLAGS += -R/usr/lib/fm install: all $(ROOTPROG64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include mem_t mem; static int mem_fmri_get_unum(nvlist_t *nvl, char **unump) { uint8_t version; char *unum; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_MEM_SCHEME_VERSION || nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0) return (fmd_fmri_set_errno(EINVAL)); *unump = unum; return (0); } static int page_isretired(nvlist_t *fmri, int *errp) { fmd_agent_hdl_t *hdl; int rc, err; if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) return (-1); rc = fmd_agent_page_isretired(hdl, fmri); err = fmd_agent_errno(hdl); fmd_agent_close(hdl); if (errp != NULL) *errp = err; return (rc); } ssize_t fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) { char format[64]; ssize_t size, presz; char *rawunum, *preunum, *escunum, *prefix; uint64_t val; int i; if (mem_fmri_get_unum(nvl, &rawunum) < 0) return (-1); /* errno is set for us */ /* * If we have a well-formed unum (hc-FMRI), use the string verbatim * to form the initial mem:/// components. Otherwise use unum=%s. */ if (strncmp(rawunum, "hc://", 5) != 0) prefix = FM_FMRI_MEM_UNUM "="; else prefix = ""; /* * If we have a DIMM offset, include it in the string. If we have a PA * then use that. Otherwise just format the unum element. */ if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) { (void) snprintf(format, sizeof (format), "%s:///%s%%1$s/%s=%%2$llx", FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET); } else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) { (void) snprintf(format, sizeof (format), "%s:///%s%%1$s/%s=%%2$llx", FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_PHYSADDR); } else { (void) snprintf(format, sizeof (format), "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix); } /* * If we have a well-formed unum (hc-FMRI), we skip over the * the scheme and authority prefix. * Otherwise, the spaces and colons will be escaped, * rendering the resulting FMRI pretty much unreadable. * We're therefore going to do some escaping of our own first. */ if (strncmp(rawunum, "hc://", 5) == 0) { rawunum += 5; rawunum = strchr(rawunum, '/'); ++rawunum; /* LINTED: variable format specifier */ size = snprintf(buf, buflen, format, rawunum, val); } else { preunum = fmd_fmri_strdup(rawunum); presz = strlen(preunum) + 1; for (i = 0; i < presz - 1; i++) { if (preunum[i] == ':' && preunum[i + 1] == ' ') { bcopy(preunum + i + 2, preunum + i + 1, presz - (i + 2)); } else if (preunum[i] == ' ') { preunum[i] = ','; } } escunum = fmd_fmri_strescape(preunum); fmd_fmri_free(preunum, presz); /* LINTED: variable format specifier */ size = snprintf(buf, buflen, format, escunum, val); fmd_fmri_strfree(escunum); } return (size); } int fmd_fmri_expand(nvlist_t *nvl) { char *unum, **serids; uint_t nnvlserids; size_t nserids; int rc, err = 0; topo_hdl_t *thp; if ((mem_fmri_get_unum(nvl, &unum) < 0) || (*unum == '\0')) return (fmd_fmri_set_errno(EINVAL)); /* * If the mem-scheme topology exports this method expand(), invoke it. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); rc = topo_fmri_expand(thp, nvl, &err); fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &serids, &nnvlserids)) == 0) { /* already have serial #s */ return (0); } else if (rc != ENOENT) return (fmd_fmri_set_errno(EINVAL)); if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { /* errno is set for us */ if (errno == ENOTSUP) return (0); /* nothing to add - no s/n support */ else return (-1); } rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids, nserids); mem_strarray_free(serids, nserids); if (rc != 0) return (fmd_fmri_set_errno(EINVAL)); else return (0); } #ifdef sparc static int serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2) { int i; if (nserids1 != nserids2) return (0); for (i = 0; i < nserids1; i++) { if (strcmp(serids1[i], serids2[i]) != 0) return (0); } return (1); } #endif /* sparc */ int fmd_fmri_present(nvlist_t *nvl) { char *unum = NULL; int rc, err = 0; struct topo_hdl *thp; #ifdef sparc char **nvlserids, **serids; uint_t nnvlserids; size_t nserids; #else nvlist_t *unum_nvl; nvlist_t *nvlcp = NULL; uint64_t val; #endif /* sparc */ if (mem_fmri_get_unum(nvl, &unum) < 0) return (-1); /* errno is set for us */ #ifdef sparc /* * If the mem-scheme topology exports this method present(), invoke it. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); rc = topo_fmri_present(thp, nvl, &err); fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, &nnvlserids) != 0) { /* * Some mem scheme FMRIs don't have serial ids because * either the platform does not support them, or because * the FMRI was created before support for serial ids was * introduced. If this is the case, assume it is there. */ if (mem.mem_dm == NULL) return (1); else return (fmd_fmri_set_errno(EINVAL)); } if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { if (errno == ENOTSUP) return (1); /* assume it's there, no s/n support here */ if (errno != ENOENT) { /* * Errors are only signalled to the caller if they're * the caller's fault. This isn't - it's a failure on * our part to burst or read the serial numbers. We'll * whine about it, and tell the caller the named * module(s) isn't/aren't there. */ fmd_fmri_warn("failed to retrieve serial number for " "unum %s", unum); } return (0); } rc = serids_eq(serids, nserids, nvlserids, nnvlserids); mem_strarray_free(serids, nserids); #else /* * On X86 we will invoke the topo is_present method passing in the * unum, which is in hc scheme. The libtopo hc-scheme is_present method * will invoke the node-specific is_present method, which is implemented * by the chip enumerator for rank nodes. The rank node's is_present * method will compare the serial number in the unum with the current * serial to determine if the same DIMM is present. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) { fmd_fmri_warn("failed to get handle to topology"); return (-1); } if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) { rc = topo_fmri_present(thp, unum_nvl, &err); nvlist_free(unum_nvl); } else rc = fmd_fmri_set_errno(EINVAL); fmd_fmri_topo_rele(thp); /* * Need to check if this is a valid page too. if "isretired" returns * EINVAL, assume page invalid and return not_present. */ if (rc == 1 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 && mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) { int page_err, rval = page_isretired(nvlcp, &page_err); if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL) rc = 0; nvlist_free(nvlcp); } #endif /* sparc */ return (rc); } int fmd_fmri_replaced(nvlist_t *nvl) { char *unum = NULL; int rc, err = 0; struct topo_hdl *thp; #ifdef sparc char **nvlserids, **serids; uint_t nnvlserids; size_t nserids; #else nvlist_t *unum_nvl; nvlist_t *nvlcp = NULL; uint64_t val; #endif /* sparc */ if (mem_fmri_get_unum(nvl, &unum) < 0) return (-1); /* errno is set for us */ #ifdef sparc /* * If the mem-scheme topology exports this method replaced(), invoke it. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); rc = topo_fmri_replaced(thp, nvl, &err); fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids, &nnvlserids) != 0) { /* * Some mem scheme FMRIs don't have serial ids because * either the platform does not support them, or because * the FMRI was created before support for serial ids was * introduced. If this is the case, assume it is there. */ if (mem.mem_dm == NULL) return (FMD_OBJ_STATE_UNKNOWN); else return (fmd_fmri_set_errno(EINVAL)); } if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) { if (errno == ENOTSUP) return (FMD_OBJ_STATE_UNKNOWN); if (errno != ENOENT) { /* * Errors are only signalled to the caller if they're * the caller's fault. This isn't - it's a failure on * our part to burst or read the serial numbers. We'll * whine about it, and tell the caller the named * module(s) isn't/aren't there. */ fmd_fmri_warn("failed to retrieve serial number for " "unum %s", unum); } return (FMD_OBJ_STATE_NOT_PRESENT); } rc = serids_eq(serids, nserids, nvlserids, nnvlserids) ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED; mem_strarray_free(serids, nserids); #else /* * On X86 we will invoke the topo is_replaced method passing in the * unum, which is in hc scheme. The libtopo hc-scheme is_replaced * method will invoke the node-specific is_replaced method, which is * implemented by the chip enumerator for rank nodes. The rank node's * is_replaced method will compare the serial number in the unum with * the current serial to determine if the same DIMM is replaced. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) { fmd_fmri_warn("failed to get handle to topology"); return (-1); } if (topo_fmri_str2nvl(thp, unum, &unum_nvl, &err) == 0) { rc = topo_fmri_replaced(thp, unum_nvl, &err); nvlist_free(unum_nvl); } else rc = fmd_fmri_set_errno(EINVAL); fmd_fmri_topo_rele(thp); /* * Need to check if this is a valid page too. if "isretired" returns * EINVAL, assume page invalid and return not_present. */ if ((rc == FMD_OBJ_STATE_STILL_PRESENT || rc == FMD_OBJ_STATE_UNKNOWN) && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0 && nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0 && mem_unum_rewrite(nvl, &nvlcp) == 0 && nvlcp != NULL) { int page_err, rval = page_isretired(nvlcp, &page_err); if (rval == FMD_AGENT_RETIRE_DONE && page_err == EINVAL) rc = FMD_OBJ_STATE_NOT_PRESENT; nvlist_free(nvlcp); } #endif /* sparc */ return (rc); } int fmd_fmri_contains(nvlist_t *er, nvlist_t *ee) { int rc, err = 0; struct topo_hdl *thp; char *erunum, *eeunum; uint64_t erval = 0, eeval = 0; /* * If the mem-scheme topology exports this method contains(), invoke it. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); rc = topo_fmri_contains(thp, er, ee, &err); fmd_fmri_topo_rele(thp); if (err != ETOPO_METHOD_NOTSUP) return (rc); if (mem_fmri_get_unum(er, &erunum) < 0 || mem_fmri_get_unum(ee, &eeunum) < 0) return (-1); /* errno is set for us */ if (mem_unum_contains(erunum, eeunum) <= 0) return (0); /* can't parse/match, so assume no containment */ if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) { return (nvlist_lookup_uint64(ee, FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval); } if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) { return (nvlist_lookup_uint64(ee, FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval); } return (1); } /* * We can only make a usable/unusable determination for pages. Mem FMRIs * without page addresses will be reported as usable since Solaris has no * way at present to dynamically disable an entire DIMM or DIMM pair. */ int fmd_fmri_unusable(nvlist_t *nvl) { uint64_t val1, val2; uint8_t version; int rc, err1 = 0, err2; nvlist_t *nvlcp = NULL; int retval; topo_hdl_t *thp; if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || version > FM_MEM_SCHEME_VERSION) return (fmd_fmri_set_errno(EINVAL)); /* * If the mem-scheme topology exports this method unusable(), invoke it. */ if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (fmd_fmri_set_errno(EINVAL)); rc = topo_fmri_unusable(thp, nvl, &err1); fmd_fmri_topo_rele(thp); if (err1 != ETOPO_METHOD_NOTSUP) return (rc); err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val1); err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val2); if (err1 == ENOENT && err2 == ENOENT) return (0); /* no page, so assume it's still usable */ if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT)) return (fmd_fmri_set_errno(EINVAL)); if ((rc = mem_unum_rewrite(nvl, &nvlcp)) != 0) return (fmd_fmri_set_errno(rc)); /* * Ask the kernel if the page is retired, using either the rewritten * hc FMRI or the original mem FMRI with the specified offset or PA. * Refer to the kernel's page_retire_check() for the error codes. */ rc = page_isretired(nvlcp ? nvlcp : nvl, NULL); if (rc == FMD_AGENT_RETIRE_FAIL) { /* * The page is not retired and is not scheduled for retirement * (i.e. no request pending and has not seen any errors) */ retval = 0; } else if (rc == FMD_AGENT_RETIRE_DONE || rc == FMD_AGENT_RETIRE_ASYNC) { /* * The page has been retired, is in the process of being * retired, or doesn't exist. The latter is valid if the page * existed in the past but has been DR'd out. */ retval = 1; } else { /* * Errors are only signalled to the caller if they're the * caller's fault. This isn't - it's a failure of the * retirement-check code. We'll whine about it and tell * the caller the page is unusable. */ fmd_fmri_warn("failed to determine page %s=%llx usability: " "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET : FM_FMRI_MEM_PHYSADDR, err1 == 0 ? (u_longlong_t)val1 : (u_longlong_t)val2, rc, errno); retval = 1; } nvlist_free(nvlcp); return (retval); } int fmd_fmri_init(void) { return (mem_discover()); } void fmd_fmri_fini(void) { mem_dimm_map_t *dm, *em; mem_bank_map_t *bm, *cm; mem_grp_t *gm, *hm; mem_seg_map_t *sm, *tm; for (dm = mem.mem_dm; dm != NULL; dm = em) { em = dm->dm_next; fmd_fmri_strfree(dm->dm_label); fmd_fmri_strfree(dm->dm_part); fmd_fmri_strfree(dm->dm_device); fmd_fmri_free(dm, sizeof (mem_dimm_map_t)); } for (bm = mem.mem_bank; bm != NULL; bm = cm) { cm = bm->bm_next; fmd_fmri_free(bm, sizeof (mem_bank_map_t)); } for (gm = mem.mem_group; gm != NULL; gm = hm) { hm = gm->mg_next; fmd_fmri_free(gm, sizeof (mem_grp_t)); } for (sm = mem.mem_seg; sm != NULL; sm = tm) { tm = sm->sm_next; fmd_fmri_free(sm, sizeof (mem_seg_map_t)); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MEM_H #define _MEM_H #include #include #ifdef __cplusplus extern "C" { #endif /* * FMRI plugin for the `mem' scheme. * * The mem scheme can be used to name individual memory modules, as well as * groups of memory modules, also known as banks. The name `dimm' is used as a * synonym for individual memory modules, for no good reason. Mem FMRIs can * be further refined with the addition of a member which identifies a * particular physical page within the bank or DIMM. The named page is as * viewed by the VM system, and may thus span multiple memory modules. It will, * however, be at least partially contained by the named bank or DIMM. * * Memory modules are identified by two things - their physical position, or * slot, in the machine, and their serial number. DIMMs are identified by this * tuple on platforms which support the retrieval of serial numbers. Platforms * which don't have this support rely on the slot number, with the corresponding * degradation in their ability to detect hardware changees. * * The physical location is embodied by the unum, which is highly specific to * each platform, and bears a passing resemblance to the name of the slot, as * printed on the actual hardware. The unum is mapped to a DIMM-specific * device, which is then read to determine the serial number. See mem_disc.c * for details of the process by which unums are mapped to devices, and * mem_read.c for the code which actually retrieves the serial number from the * device. * * Banks are also identified by unums, which must be broken apart into the * unums which identify each constituent memory module. Serial numbers are * retrieved for banks - one per member module - in the same way as for * individual modules. See mem_unum.c for the code which bursts bank unums. * * Serial number retrieval, on platforms which support it, is very expensive * (on the order of several tenths of a second, which adds up in a hurry on * larger machines). So, while we pre-generate the list of DIMM device paths, * we only read their serial numbers when requested by plugin consumers. To * further reduce the perceived cost, we don't re-read until/unless we detect * that a DR operation has taken place. * * Using the facilities described above, the plugin implements the following * entry points: (see mem.c) * * - nvl2str: The printed representation of the named bank or DIMM is * generated. No attempt is made to determine whether or not the named * item is still present in the system. * * - expand: For platforms which do not include bank or DIMM * serial numbers in their ereports, this entry point will read the * serial number(s) for the named item, and will add it/them to the passed * FMRI. Errors will be returned if the FMRI (unum) was unparseable, or if * the serial number could not be retrieved. * * - present: Given a mem-schemed FMRI with a serial number, this entry * point will attempt to determine whether the bank or module named in the * FMRI is still present in the system at the same location. Programmer * errors (invalid FMRIs) will be signalled to the caller. Warnings will * be emitted for otherwise-valid FMRIs whose serial numbers could not be * read, with the caller told that the FMRI is not present. * * - contains: Used to determine whether a given bank contains a given DIMM. * No attempt is made to determine whether the module named by the FMRIs are * actually present in the system. Programmer errors (invalidd FMRIs) will * be returned to the caller. Warnings will be emitted for otherwise-valid * FMRIs whose relationship could not be determined, with the caller told * that there is no relationship. */ /* * 18+nul for SPD, 6+nul for SEEPROM, 15+nul max for Serengeti, Starcat, LW8. * 18 for Sun Partnumber, 18 partner partnumber, 12 serialnumber for OPL. */ #define MEM_SERID_MAXLEN 64 #define MAX_DIMMS_PER_BANK 4 typedef struct mem_dimm_map { struct mem_dimm_map *dm_next; /* The next DIMM map */ char *dm_label; /* The UNUM for this DIMM */ char *dm_device; /* Path to I2C device for DIMM */ char dm_serid[MEM_SERID_MAXLEN]; /* Cached serial number */ char *dm_part; /* DIMM part number */ uint64_t dm_drgen; /* DR gen count for cached S/N */ } mem_dimm_map_t; typedef struct mem_bank_map { struct mem_bank_map *bm_next; /* the next bank map overall */ struct mem_bank_map *bm_grp; /* next bank map in group */ uint64_t bm_mask; uint64_t bm_match; uint16_t bm_shift; /* dimms-per-reference shift */ mem_dimm_map_t *bm_dimm[MAX_DIMMS_PER_BANK]; } mem_bank_map_t; typedef struct mem_grp { struct mem_grp *mg_next; size_t mg_size; mem_bank_map_t *mg_bank; } mem_grp_t; typedef struct mem_seg_map { struct mem_seg_map *sm_next; /* the next segment map */ uint64_t sm_base; /* base address for this segment */ uint64_t sm_size; /* size for this segment */ mem_grp_t *sm_grp; } mem_seg_map_t; typedef struct mem { mem_dimm_map_t *mem_dm; /* List supported DIMMs */ uint64_t mem_memconfig; /* HV memory-configuration-id# */ mem_seg_map_t *mem_seg; /* list of defined segments */ mem_bank_map_t *mem_bank; mem_grp_t *mem_group; /* groups of banks for a segment */ } mem_t; extern int mem_discover(void); extern int mem_get_serid(const char *, char *, size_t); extern int mem_get_serids_by_unum(const char *, char ***, size_t *); extern void mem_expand_opt(nvlist_t *, char *, char **); extern int mem_unum_burst(const char *, char ***, size_t *); extern int mem_unum_contains(const char *, const char *); extern int mem_unum_rewrite(nvlist_t *, nvlist_t **); extern void mem_strarray_free(char **, size_t); extern mem_t mem; #ifdef __cplusplus } #endif #endif /* _MEM_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Retrieval of the DIMM serial number from data encoded in the SPD and * SEEPROM formats. */ #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZ_SPD 256 #define BUFSIZ_SEEPROM 8192 /* * SEEPROMs are composed of self-describing containers. The first container, * starting at offset 0, is r/w. The second container, starting at 0x1800, is * r/o, and contains identification information supplied by the manufacturer. */ #define SEEPROM_OFFSET_RO 0x1800 static int mem_get_spd_serid(const char *buf, size_t bufsz, char *serid, size_t seridsz) { static const char hex_digits[] = "0123456789ABCDEF"; spd_data_t *spd = (spd_data_t *)buf; char *c; int i; if (bufsz < sizeof (spd_data_t)) return (fmd_fmri_set_errno(EINVAL)); if (seridsz < sizeof (spd->asmb_serial_no) * 2 + 1) return (fmd_fmri_set_errno(EINVAL)); for (c = serid, i = 0; i < sizeof (spd->asmb_serial_no); i++) { *c++ = hex_digits[spd->asmb_serial_no[i] >> 4]; *c++ = hex_digits[spd->asmb_serial_no[i] & 0xf]; } *c = '\0'; return (0); } static void * seeprom_seg_lookup(const char *buf, size_t bufsz, char *segname, size_t *segszp) { seeprom_container_t *sc; seeprom_seg_t *segp, seg; int sidx; if (strlen(segname) != sizeof (seg.sees_name)) return (NULL); sc = (seeprom_container_t *)(buf + SEEPROM_OFFSET_RO); /* Validate sc size then dereference it */ if (bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) || bufsz < SEEPROM_OFFSET_RO + sizeof (seeprom_container_t) + sc->seec_contsz) return (NULL); if (sc->seec_tag == 0 || sc->seec_contsz == 0 || sc->seec_nsegs == 0) return (NULL); for (sidx = 0; sidx < sc->seec_nsegs; sidx++) { /* LINTED - pointer alignment */ segp = ((seeprom_seg_t *)(sc + 1)) + sidx; bcopy(segp, &seg, sizeof (seeprom_seg_t)); seg.sees_segoff = ntohs(seg.sees_segoff); seg.sees_seglen = ntohs(seg.sees_seglen); if (bufsz < seg.sees_segoff + seg.sees_seglen) return (NULL); if (strncmp(segname, seg.sees_name, sizeof (seg.sees_name)) == 0) { *segszp = seg.sees_seglen; return ((void *)(buf + seg.sees_segoff)); } } return (NULL); } static int mem_get_seeprom_serid(const char *buf, size_t bufsz, char *serid, size_t seridsz) { seeprom_seg_sd_t *sd; size_t segsz; if (seridsz < sizeof (sd->seesd_sun_sno) + 1) return (fmd_fmri_set_errno(EINVAL)); if ((sd = seeprom_seg_lookup(buf, bufsz, "SD", &segsz)) == NULL) return (fmd_fmri_set_errno(EINVAL)); if (segsz < sizeof (seeprom_seg_sd_t)) return (fmd_fmri_set_errno(EINVAL)); bcopy(sd->seesd_sun_sno, serid, sizeof (sd->seesd_sun_sno)); serid[sizeof (sd->seesd_sun_sno)] = '\0'; return (0); } int mem_get_serid(const char *device, char *serid, size_t seridsz) { char buf[8192]; int fd; ssize_t sz; if ((fd = open(device, O_RDONLY)) < 0) return (-1); /* errno is set for us */ if ((sz = read(fd, buf, sizeof (buf))) < 0) { int err = errno; (void) close(fd); return (fmd_fmri_set_errno(err)); } (void) close(fd); switch (sz) { case BUFSIZ_SPD: return (mem_get_spd_serid(buf, BUFSIZ_SPD, serid, seridsz)); case BUFSIZ_SEEPROM: return (mem_get_seeprom_serid(buf, BUFSIZ_SEEPROM, serid, seridsz)); default: return (fmd_fmri_set_errno(EINVAL)); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MEM_SEEPROM_H #define _MEM_SEEPROM_H /* * Layout of SEEPROM data * * XXX need cite */ #include #ifdef __cplusplus extern "C" { #endif /* * Care must be taken when accessing these structures, as the SEEPROM source * data does not have an alignment requirement. */ typedef struct seeprom_seg_sd { uint32_t seesd_header; uint32_t seesd_tstamp; char seesd_frudesc[80]; char seesd_mfgloc[64]; char seesd_sun_pno[7]; char seesd_sun_sno[6]; uint8_t seesd_vendorhi; uint8_t seesd_vendorlo; char seesd_hwdash[2]; char seesd_hwrev[2]; char seesd_fruname[16]; } seeprom_seg_sd_t; typedef struct seeprom_seg { char sees_name[2]; uint16_t sees_prothi; uint16_t sees_protlo; uint16_t sees_segoff; uint16_t sees_seglen; } seeprom_seg_t; typedef struct seeprom_container { uint8_t seec_tag; uint8_t seec_verhi; uint8_t seec_verlo; uint8_t seec_contsz; uint8_t seec_crc8; uint8_t seec_nsegs; } seeprom_container_t; #ifdef __cplusplus } #endif #endif /* _MEM_SEEPROM_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MEM_SPD_H #define _MEM_SPD_H /* * Layout of SPD-format data, as per PICL. */ #include #ifdef __cplusplus extern "C" { #endif typedef struct spd_data { uint8_t spd_len; /* bytes written by manufacturer */ uint8_t spd_max_len; /* total available prom space */ uint8_t memory_type; /* e.g. SDRAM DDR = 0x07 */ uint8_t n_rows; /* row address bits */ uint8_t n_cols; /* column address bits */ uint8_t n_mod_rows; /* number of module rows */ uint8_t ls_data_width; /* e.g. 72 bits */ uint8_t ms_data_width; uint8_t vddq_if; /* e.g. SSTL 2.5V = 0x04 */ uint8_t cycle_time25; /* cycle time at CAS latency 2.5 */ uint8_t access_time25; uint8_t config; /* e.g. ECC = 0x02 */ uint8_t refresh; /* e.g. 7.8uS & self refresh = 0x82 */ uint8_t primary_width; uint8_t err_chk_width; uint8_t tCCD; uint8_t burst_lengths; /* e.g. 2,4,8 = 0x0e */ uint8_t n_banks; uint8_t cas_lat; uint8_t cs_lat; uint8_t we_lat; uint8_t mod_attrs; uint8_t dev_attrs; uint8_t cycle_time20; /* cycle time at CAS latency 2.0 */ uint8_t access_time20; uint8_t cycle_time15; uint8_t access_time15; uint8_t tRP; uint8_t tRRD; uint8_t tRCD; uint8_t tRAS; uint8_t mod_row_density; uint8_t addr_ip_setup; uint8_t addr_ip_hold; uint8_t data_ip_setup; uint8_t data_ip_hold; uint8_t superset[62 - 36]; uint8_t spd_rev; uint8_t chksum_0_62; uint8_t jedec[8]; uint8_t manu_loc; uint8_t manu_part_no[91 - 73]; uint8_t manu_rev_pcb; uint8_t manu_rev_comp; uint8_t manu_year; uint8_t manu_week; uint8_t asmb_serial_no[4]; uint8_t manu_specific[128 - 99]; } spd_data_t; #ifdef __cplusplus } #endif #endif /* _MEM_SPD_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2019 Peter Tribble. */ #include #include #include #include #include #include #define ISHCUNUM(unum) (strncmp(unum, "hc:/", 4) == 0) /* * Given a DIMM or bank unum, mem_unum_burst will break it apart into individual * DIMM names. If it's a DIMM, one name will be returned. If it's a bank, the * unums for the individual DIMMs will be returned. * * Plain J-number DIMM and bank unums are simple. J DIMMs have one J number. J * banks have multiple whitespace-separated J numbers. * * The others are more complex, and consist of a common portion c, a colon, and * a DIMM-specific portion d. DIMMs are of the form "c: d", while banks are of * the form "c: d d ...". The patterns are designed to handle the complex case, * but also handle the simple ones as an afterthought. bd_pat is used to * match specific styles of unum. In bd_pat, the first %n indicates the end of * the common portion ("c" above). The second %n marks the beginning of the * repetitive portion ("d" above). The third %n is used to determine whether or * not the entire pattern matched. bd_reppat is used to match instances of the * repetitive part. * * sscanf is your disturbingly powerful friend. * * The "bd_subst" element of the bank_dimm structure was added for Ontario * in order to accommodate its bank string names. Previously, to convert * from a bank representation ... * we concatenated the common piece with each dimm-specific piece in turn, * possibly deleting some characters in between. Ontario is the first * platform which requires that characters be substituted (like a vi s/1/2/) * in place of characters deleted. "bd_subst" represents the character(s) * to be substituted between the common piece and each dimm-specific piece * as part of the bursting. For prior platforms, this value is skipped. * * Example: * input: "MB/CMP0/CH3: R1/D0/J1901 R1/D1/J2001" * outputs: "MB/CMP0/CH3/R1/D0/J1901", "MB/CMP0/CH3/R1/D1/J2001" */ typedef struct bank_dimm { const char *bd_pat; const char *bd_reppat; const char *bd_subst; } bank_dimm_t; static const bank_dimm_t bank_dimm[] = { { "%n%nJ%*4d%n", " J%*4d%n" }, { "MB/P%*d/%nB%*d:%n%n", " B%*d/D%*d%n" }, { "MB/P%*d/%nB%*d/D%*d:%n%n", " B%*d/D%*d%n" }, { "C%*d/P%*d/%nB%*d:%n%n", " B%*d/D%*d%n" }, { "C%*d/P%*d/%nB%*d/D%*d:%n%n", " B%*d/D%*d%n" }, { "Slot %*c: %n%nJ%*4d%n", " J%*4d%n" }, { "%n%nDIMM%*d%n", " DIMM%*d%n" }, { "MB/%nDIMM%*d MB/DIMM%*d: %n%n", " DIMM%*d%n" }, { "MB/%nDIMM%*d:%n%n", " DIMM%*d%n" }, { "MB/CMP%*d/CH%*d%n:%n%n", " R%*d/D%*d/J%*4d%n", "/" }, { "MB/CMP%*d/CH%*d%n%n%n", "/R%*d/D%*d/J%*4d%n" }, { "MB/C%*d/P%*d/%nB%*d:%n%n", " B%*d/D%*d%n" }, { "MB/C%*d/P%*d/%nB%*d/D%*d:%n%n", " B%*d/D%*d%n" }, { "/MBU_A/MEMB%*d/%n%nMEM%*d%*1c%n", " MEM%*d%*1c%n" }, { "/MBU_B/MEMB%*d/%n%nMEM%*d%*1c%n", " MEM%*d%*1c%n" }, { "/MBU_A/%n%nMEM%*d%*1c%n", " MEM%*d%*1c%n" }, { "/CMU%*2d/%n%nMEM%*2d%*1c%n", " MEM%*2d%*1c%n" }, { "MB/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/" }, { "%n%nMB/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", "MB/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, { "%n%nMB/CMP%*d/BR%*d/CH%*d/D%*d%n", "MB/CMP%*d/BR%*d/CH%*d/D%*d%n" }, { "MB/CPU%*d/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/"}, { "MB/MEM%*d/CMP%*d/BR%*d%n:%n%n", " CH%*d/D%*d/J%*4d%n", "/"}, { "%n%nMB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", "MB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, { "%n%nMB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n", "MB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d/J%*4d%n" }, { "%n%nMB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d%n", "MB/MEM%*d/CMP%*d/BR%*d/CH%*d/D%*d%n" }, { "%n%nMB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d%n", "MB/CPU%*d/CMP%*d/BR%*d/CH%*d/D%*d%n" }, { NULL } }; /* * Burst Serengeti-style unums. * A DIMM unum string is expected to be in this form: * "[/N0/]SB12/P0/B0/D2 [J13500]" * A bank unum string is expected to be in this form: * "[/N0/]SB12/P0/B0 [J13500, ...]" */ static int mem_unum_burst_sgsc(const char *pat, char ***dimmsp, size_t *ndimmsp) { char buf[64]; char **dimms; char *base; const char *c; char *copy; size_t copysz; int i; /* * No expansion is required for a DIMM unum */ if (strchr(pat, 'D') != NULL) { dimms = fmd_fmri_alloc(sizeof (char *)); dimms[0] = fmd_fmri_strdup(pat); *dimmsp = dimms; *ndimmsp = 1; return (0); } /* * strtok is destructive so we need to work with * a copy and keep track of the size allocated. */ copysz = strlen(pat) + 1; copy = fmd_fmri_alloc(copysz); (void) strcpy(copy, pat); base = strtok(copy, " "); /* There are four DIMMs in a bank */ dimms = fmd_fmri_alloc(sizeof (char *) * 4); for (i = 0; i < 4; i++) { (void) snprintf(buf, sizeof (buf), "%s/D%d", base, i); if ((c = strtok(NULL, " ")) != NULL) { size_t len = strlen(buf); (void) snprintf(buf + len, sizeof (buf) - len, " %s", c); } dimms[i] = fmd_fmri_strdup(buf); } fmd_fmri_free(copy, copysz); *dimmsp = dimms; *ndimmsp = 4; return (0); } /* * Returns 0 (with dimmsp and ndimmsp set) if the unum could be bursted, -1 * otherwise. */ static int mem_unum_burst_pattern(const char *pat, char ***dimmsp, size_t *ndimmsp) { const bank_dimm_t *bd; char **dimms = NULL, **newdimms; size_t ndimms = 0; const char *c; for (bd = bank_dimm; bd->bd_pat != NULL; bd++) { int replace, start, matched; char dimmname[64]; replace = start = matched = -1; (void) sscanf(pat, bd->bd_pat, &replace, &start, &matched); if (matched == -1) continue; (void) strlcpy(dimmname, pat, sizeof (dimmname)); if (bd->bd_subst != NULL) { (void) strlcpy(dimmname+replace, bd->bd_subst, sizeof (dimmname) - strlen(bd->bd_subst)); replace += strlen(bd->bd_subst); } c = pat + start; while (*c != '\0') { int dimmlen = -1; (void) sscanf(c, bd->bd_reppat, &dimmlen); if (dimmlen == -1) break; while (*c == ' ') { c++; dimmlen--; } if (dimmlen > sizeof (dimmname) - replace) break; (void) strlcpy(dimmname + replace, c, dimmlen + 1); newdimms = fmd_fmri_alloc(sizeof (char *) * (ndimms + 1)); if (ndimms != 0) { bcopy(dimms, newdimms, sizeof (char *) * ndimms); fmd_fmri_free(dimms, sizeof (char *) * ndimms); } newdimms[ndimms++] = fmd_fmri_strdup(dimmname); dimms = newdimms; c += dimmlen; if (*c != ' ' && *c != '\0') break; } if (*c != '\0') break; *dimmsp = dimms; *ndimmsp = ndimms; return (0); } mem_strarray_free(dimms, ndimms); /* * Set errno to ENOTSUP and return -1. This allows support for DIMMs * with unknown unum strings and/or serial numbers. The only consumer * of mem_unum_burst_pattern() that cares/checks for the returned * errno is fmd_fmri_expand(). */ return (fmd_fmri_set_errno(ENOTSUP)); } int mem_unum_burst(const char *pat, char ***dimmsp, size_t *ndimmsp) { const char *platform = fmd_fmri_get_platform(); /* * Call mem_unum_burst_sgsc() for Serengeti and * Lightweight 8 platforms. Call mem_unum_burst_pattern() * for all other platforms. */ if (strcmp(platform, "SUNW,Sun-Fire") == 0 || strcmp(platform, "SUNW,Netra-T12") == 0) return (mem_unum_burst_sgsc(pat, dimmsp, ndimmsp)); else return (mem_unum_burst_pattern(pat, dimmsp, ndimmsp)); } /* * The unum containership operation is designed to tell the caller whether a * given FMRI contains another. In the case of this plugin, we tell the caller * whether a given memory FMRI (usually a bank) contains another (usually a * DIMM). We do this in one of two ways, depending on the platform. For most * platforms, we can use the bursting routine to generate the list of member * unums from the container unum. Membership can then be determined by * searching the bursted list for the containee's unum. * * Some platforms, however, cannot be bursted, as their bank unums do not * contain all of the information needed to generate the complete list of * member DIMM unums. For these unums, we must make do with a substring * comparison. */ static int unum_contains_bypat(const char *erunum, const char *eeunum) { char **ernms, **eenms; size_t nernms, neenms; int i, j, rv = 1; if (mem_unum_burst(erunum, &ernms, &nernms) < 0) return (fmd_fmri_set_errno(EINVAL)); if (mem_unum_burst(eeunum, &eenms, &neenms) < 0) { mem_strarray_free(ernms, nernms); return (fmd_fmri_set_errno(EINVAL)); } for (i = 0; i < neenms; i++) { for (j = 0; j < nernms; j++) { if (strcmp(eenms[i], ernms[j]) == 0) break; } if (j == nernms) { /* * This DIMM was not found in the container. */ rv = 0; break; } } mem_strarray_free(ernms, nernms); mem_strarray_free(eenms, neenms); return (rv); } static int unum_strip_one_jnum(const char *unum, uint_t *endp) { char *c; int i; if ((c = strrchr(unum, 'J')) == NULL) return (0); while (c > unum && isspace(c[-1])) c--; (void) sscanf(c, " J%*[0-9] %n", &i); if (i == 0 || (uintptr_t)(c - unum) + i != strlen(unum)) return (0); *endp = (uint_t)(c - unum); return (1); } static int unum_contains_bysubstr(const char *erunum, const char *eeunum) { uint_t erlen, eelen; int nojnumstrip = 0; /* * This comparison method is only known to work on specific types of * unums. Check for those types here. */ if ((strncmp(erunum, "/N", 2) != 0 && strncmp(erunum, "/IO", 3) != 0 && strncmp(erunum, "/SB", 3) != 0) || (strncmp(eeunum, "/N", 2) != 0 && strncmp(eeunum, "/IO", 3) != 0 && strncmp(eeunum, "/SB", 3) != 0)) { if (ISHCUNUM(erunum) && ISHCUNUM(eeunum)) { nojnumstrip = 1; erlen = strlen(erunum); eelen = strlen(eeunum); } else { return (fmd_fmri_set_errno(EINVAL)); } } if (!nojnumstrip) { erlen = unum_strip_one_jnum(erunum, &erlen) ? erlen : strlen(erunum); eelen = unum_strip_one_jnum(eeunum, &eelen) ? eelen : strlen(eeunum); } return (strncmp(erunum, eeunum, MIN(erlen, eelen)) == 0); } typedef int unum_cmptor_f(const char *, const char *); static unum_cmptor_f *const unum_cmptors[] = { unum_contains_bypat, unum_contains_bysubstr }; int mem_unum_contains(const char *erunum, const char *eeunum) { static int cmptor = 0; int rc; while (isspace(*erunum)) erunum++; while (isspace(*eeunum)) eeunum++; if ((rc = unum_cmptors[cmptor](erunum, eeunum)) >= 0) return (rc); if ((rc = unum_cmptors[cmptor == 0](erunum, eeunum)) >= 0) { /* * We succeeded with the non-default comparator. Change the * default so we use the correct one next time. */ cmptor = (cmptor == 0); } return (rc); } /* * If an asru has a unum string that is an hc path string then return * a new nvl (to be freed by the caller) that is a duplicate of the * original but with an additional member of a reconstituted hc fmri. */ int mem_unum_rewrite(nvlist_t *nvl, nvlist_t **rnvl) { int err; char *unumstr; nvlist_t *unum; struct topo_hdl *thp; if (nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unumstr) != 0 || !ISHCUNUM(unumstr)) return (0); if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) return (EINVAL); if (topo_fmri_str2nvl(thp, unumstr, &unum, &err) != 0) { fmd_fmri_topo_rele(thp); return (EINVAL); } fmd_fmri_topo_rele(thp); if ((err = nvlist_dup(nvl, rnvl, 0)) != 0) { nvlist_free(unum); return (err); } err = nvlist_add_nvlist(*rnvl, FM_FMRI_MEM_UNUM "-fmri", unum); nvlist_free(unum); if (err != 0) nvlist_free(*rnvl); return (err); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include void mem_strarray_free(char **arr, size_t dim) { int i; for (i = 0; i < dim; i++) { if (arr[i] != NULL) fmd_fmri_strfree(arr[i]); } fmd_fmri_free(arr, sizeof (char *) * dim); }