xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/mem.c (revision 12967:ab9ae749152f)
11414Scindi /*
21414Scindi  * CDDL HEADER START
31414Scindi  *
41414Scindi  * The contents of this file are subject to the terms of the
51493Sgavinm  * Common Development and Distribution License (the "License").
61493Sgavinm  * 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 /*
23*12967Sgavin.maltby@oracle.com  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241414Scindi  */
251414Scindi 
265204Sstephh #include <ctype.h>
271414Scindi #include <errno.h>
281414Scindi #include <kstat.h>
291414Scindi #include <limits.h>
301414Scindi #include <strings.h>
311414Scindi #include <unistd.h>
32*12967Sgavin.maltby@oracle.com #include <zone.h>
336828Stsien #include <topo_error.h>
341414Scindi #include <fm/topo_mod.h>
351414Scindi #include <sys/fm/protocol.h>
361414Scindi 
373062Scindi #include <topo_method.h>
383062Scindi #include <mem.h>
391414Scindi 
406828Stsien /*
416828Stsien  * platform specific mem module
426828Stsien  */
436828Stsien #define	PLATFORM_MEM_VERSION	MEM_VERSION
446828Stsien #define	PLATFORM_MEM_NAME	"platform-mem"
456828Stsien 
461414Scindi static int mem_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
473062Scindi     topo_instance_t, void *, void *);
481414Scindi static void mem_release(topo_mod_t *, tnode_t *);
491414Scindi static int mem_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
501414Scindi     nvlist_t **);
513062Scindi static int mem_fmri_create(topo_mod_t *, tnode_t *, topo_version_t,
523062Scindi     nvlist_t *, nvlist_t **);
531414Scindi 
541414Scindi static const topo_method_t mem_methods[] = {
551414Scindi 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
561414Scindi 	    TOPO_STABILITY_INTERNAL, mem_nvl2str },
573062Scindi 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
583062Scindi 	    TOPO_STABILITY_INTERNAL, mem_fmri_create },
591414Scindi 	{ NULL }
601414Scindi };
611414Scindi 
623062Scindi static const topo_modops_t mem_ops =
633062Scindi 	{ mem_enum, mem_release };
641414Scindi static const topo_modinfo_t mem_info =
653062Scindi 	{ "mem", FM_FMRI_SCHEME_MEM, MEM_VERSION, &mem_ops };
661414Scindi 
673062Scindi int
mem_init(topo_mod_t * mod,topo_version_t version)683062Scindi mem_init(topo_mod_t *mod, topo_version_t version)
691414Scindi {
701414Scindi 
713062Scindi 	topo_mod_setdebug(mod);
721414Scindi 	topo_mod_dprintf(mod, "initializing mem builtin\n");
731414Scindi 
743062Scindi 	if (version != MEM_VERSION)
753062Scindi 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
763062Scindi 
773062Scindi 	if (topo_mod_register(mod, &mem_info, TOPO_VERSION) != 0) {
781414Scindi 		topo_mod_dprintf(mod, "failed to register mem_info: "
791414Scindi 		    "%s\n", topo_mod_errmsg(mod));
803062Scindi 		return (-1); /* mod errno already set */
811414Scindi 	}
823062Scindi 
833062Scindi 	return (0);
841414Scindi }
851414Scindi 
861414Scindi void
mem_fini(topo_mod_t * mod)871414Scindi mem_fini(topo_mod_t *mod)
881414Scindi {
891414Scindi 	topo_mod_unregister(mod);
901414Scindi }
911414Scindi 
921414Scindi /*ARGSUSED*/
931414Scindi static int
mem_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)941414Scindi mem_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
953062Scindi     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
961414Scindi {
97*12967Sgavin.maltby@oracle.com 	int isglobal = (getzoneid() == GLOBAL_ZONEID);
986828Stsien 	topo_mod_t *nmp;
996828Stsien 
100*12967Sgavin.maltby@oracle.com 	if (isglobal && (nmp = topo_mod_load(mod, PLATFORM_MEM_NAME,
1016828Stsien 	    PLATFORM_MEM_VERSION)) == NULL) {
1026828Stsien 		if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
1036828Stsien 			/*
1046828Stsien 			 * There is no platform specific mem module.
1056828Stsien 			 */
1066828Stsien 			(void) topo_method_register(mod, pnode, mem_methods);
1076828Stsien 			return (0);
1086828Stsien 		} else {
1096828Stsien 			/* Fail to load the module */
1106828Stsien 			topo_mod_dprintf(mod, "Failed to load module %s: %s",
1116828Stsien 			    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
1126828Stsien 			return (-1);
1136828Stsien 		}
1146828Stsien 	}
1156828Stsien 
116*12967Sgavin.maltby@oracle.com 	if (isglobal && topo_mod_enumerate(nmp, pnode, PLATFORM_MEM_NAME, name,
1176828Stsien 	    min, max, NULL) < 0) {
1186828Stsien 		topo_mod_dprintf(mod, "%s failed to enumerate: %s",
1196828Stsien 		    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
1206828Stsien 		return (-1);
1216828Stsien 	}
1221414Scindi 	(void) topo_method_register(mod, pnode, mem_methods);
1231414Scindi 
1241414Scindi 	return (0);
1251414Scindi }
1261414Scindi 
1271414Scindi static void
mem_release(topo_mod_t * mod,tnode_t * node)1281414Scindi mem_release(topo_mod_t *mod, tnode_t *node)
1291414Scindi {
1301414Scindi 	topo_method_unregister_all(mod, node);
1311414Scindi }
1321414Scindi 
1335204Sstephh /*
1345204Sstephh  * Convert an input string to a URI escaped string and return the new string.
1355204Sstephh  * RFC2396 Section 2.4 says that data must be escaped if it does not have a
1365204Sstephh  * representation using an unreserved character, where an unreserved character
1375204Sstephh  * is one that is either alphanumeric or one of the marks defined in S2.3.
1385204Sstephh  */
1395204Sstephh static size_t
mem_fmri_uriescape(const char * s,const char * xmark,char * buf,size_t len)1405204Sstephh mem_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len)
1415204Sstephh {
1425204Sstephh 	static const char rfc2396_mark[] = "-_.!~*'()";
1435204Sstephh 	static const char hex_digits[] = "0123456789ABCDEF";
1445204Sstephh 	static const char empty_str[] = "";
1455204Sstephh 
1465204Sstephh 	const char *p;
1475204Sstephh 	char c, *q;
1485204Sstephh 	size_t n = 0;
1495204Sstephh 
1505204Sstephh 	if (s == NULL)
1515204Sstephh 		s = empty_str;
1525204Sstephh 
1535204Sstephh 	if (xmark == NULL)
1545204Sstephh 		xmark = empty_str;
1555204Sstephh 
1565204Sstephh 	for (p = s; (c = *p) != '\0'; p++) {
1575204Sstephh 		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c))
1585204Sstephh 			n++;    /* represent c as itself */
1595204Sstephh 		else
1605204Sstephh 			n += 3; /* represent c as escape */
1615204Sstephh 	}
1625204Sstephh 
1635204Sstephh 	if (buf == NULL)
1645204Sstephh 		return (n);
1655204Sstephh 
1665204Sstephh 	for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) {
1675204Sstephh 		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) {
1685204Sstephh 			*q++ = c;
1695204Sstephh 		} else {
1705204Sstephh 			*q++ = '%';
1715204Sstephh 			*q++ = hex_digits[((uchar_t)c & 0xf0) >> 4];
1725204Sstephh 			*q++ = hex_digits[(uchar_t)c & 0xf];
1735204Sstephh 		}
1745204Sstephh 	}
1755204Sstephh 
1765204Sstephh 	if (q == buf + len)
1775204Sstephh 		q--; /* len is too small: truncate output string */
1785204Sstephh 
1795204Sstephh 	*q = '\0';
1805204Sstephh 	return (n);
1815204Sstephh }
1825204Sstephh 
1831414Scindi /*ARGSUSED*/
1841414Scindi static int
mem_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)1851414Scindi mem_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
1861414Scindi     nvlist_t *in, nvlist_t **out)
1871414Scindi {
1881414Scindi 	const char *format;
1891414Scindi 	nvlist_t *nvl;
1901414Scindi 	uint64_t val;
1911414Scindi 	char *buf, *unum;
1921414Scindi 	size_t len;
1931414Scindi 	int err;
1945204Sstephh 	char *preunum, *escunum, *prefix;
1955204Sstephh 	ssize_t presz;
1965204Sstephh 	int i;
1971414Scindi 
1981414Scindi 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
1991414Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2001414Scindi 
2011414Scindi 	if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0) {
2021414Scindi 		nvlist_free(nvl);
2031414Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2041414Scindi 	}
2051414Scindi 
2061414Scindi 	/*
2071414Scindi 	 * If we have a DIMM offset, include it in the string.  If we have a
2081414Scindi 	 * PA then use that.  Otherwise just format the unum element.
2091414Scindi 	 */
2103323Scindi 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &val) == 0) {
2115204Sstephh 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/"
2125204Sstephh 		    FM_FMRI_MEM_OFFSET "=%3$llx";
2133323Scindi 	} else if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
2145204Sstephh 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/"
2155204Sstephh 		    FM_FMRI_MEM_PHYSADDR "=%3$llx";
2161414Scindi 	} else
2175204Sstephh 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s";
2181414Scindi 
2192869Sgavinm 	/*
2203062Scindi 	 * If we have a well-formed unum we step over the hc:// and
2213062Scindi 	 * authority prefix
2222869Sgavinm 	 */
2233062Scindi 	if (strncmp(unum, "hc://", 5) == 0) {
2243062Scindi 		unum += 5;
2253062Scindi 		unum = strchr(unum, '/');
2263062Scindi 		++unum;
2275204Sstephh 		prefix = "";
2285204Sstephh 		escunum = unum;
2295204Sstephh 	} else {
2305204Sstephh 		prefix = FM_FMRI_MEM_UNUM "=";
2315204Sstephh 		preunum = topo_mod_strdup(mod, unum);
2325204Sstephh 		presz = strlen(preunum) + 1;
2335204Sstephh 
2345204Sstephh 		for (i = 0; i < presz - 1; i++) {
2355204Sstephh 			if (preunum[i] == ':' && preunum[i + 1] == ' ') {
2365204Sstephh 				bcopy(preunum + i + 2, preunum + i + 1,
2375204Sstephh 				    presz - (i + 2));
2385204Sstephh 			} else if (preunum[i] == ' ') {
2395204Sstephh 				preunum[i] = ',';
2405204Sstephh 			}
2415204Sstephh 		}
2425204Sstephh 
2435204Sstephh 		i = mem_fmri_uriescape(preunum, ":,/", NULL, 0);
2445204Sstephh 		escunum = topo_mod_alloc(mod, i + 1);
2455204Sstephh 		(void) mem_fmri_uriescape(preunum, ":,/", escunum, i + 1);
2465204Sstephh 		topo_mod_free(mod, preunum, presz);
2473062Scindi 	}
2482869Sgavinm 
2495204Sstephh 	len = snprintf(NULL, 0, format, prefix, escunum, val) + 1;
2501414Scindi 	buf = topo_mod_zalloc(mod, len);
2511414Scindi 
2521414Scindi 	if (buf == NULL) {
2531414Scindi 		nvlist_free(nvl);
2541414Scindi 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2551414Scindi 	}
2561414Scindi 
2575204Sstephh 	(void) snprintf(buf, len, format, prefix, escunum, val);
2585204Sstephh 	if (escunum != unum)
2595204Sstephh 		topo_mod_strfree(mod, escunum);
2601414Scindi 	err = nvlist_add_string(nvl, "fmri-string", buf);
2611414Scindi 	topo_mod_free(mod, buf, len);
2621414Scindi 
2631414Scindi 	if (err != 0) {
2641414Scindi 		nvlist_free(nvl);
2651414Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2661414Scindi 	}
2671414Scindi 
2681414Scindi 	*out = nvl;
2691414Scindi 	return (0);
2701414Scindi }
2711414Scindi 
2723062Scindi static nvlist_t *
mem_fmri(topo_mod_t * mod,uint64_t pa,uint64_t offset,char * unum,int flags)2733062Scindi mem_fmri(topo_mod_t *mod, uint64_t pa, uint64_t offset, char *unum, int flags)
2741414Scindi {
2753062Scindi 	int err;
2763062Scindi 	nvlist_t *asru;
2773062Scindi 
2783062Scindi 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
2793062Scindi 		return (NULL);
2803062Scindi 
2813062Scindi 	/*
2823062Scindi 	 * If we have a well-formed unum we step over the hc:/// and
2833062Scindi 	 * authority prefix
2843062Scindi 	 */
2853062Scindi 	if (strncmp(unum, "hc://", 5) == 0) {
2863062Scindi 		char *tstr;
2871414Scindi 
2883062Scindi 		tstr = strchr(unum, '/');
2893062Scindi 		unum = ++tstr;
2903062Scindi 	}
2913062Scindi 
2923062Scindi 	err = nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION);
2933062Scindi 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM);
2943062Scindi 	err |= nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum);
2953062Scindi 	if (flags & TOPO_MEMFMRI_PA)
2963062Scindi 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa);
2973062Scindi 	if (flags & TOPO_MEMFMRI_OFFSET)
2983062Scindi 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset);
2993062Scindi 
3003062Scindi 	if (err != 0) {
3013062Scindi 		nvlist_free(asru);
3023062Scindi 		return (NULL);
3033062Scindi 	}
3043062Scindi 
3053062Scindi 	return (asru);
3061414Scindi }
3071414Scindi 
3081414Scindi /*ARGSUSED*/
3091414Scindi static int
mem_fmri_create(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)3103062Scindi mem_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3111414Scindi     nvlist_t *in, nvlist_t **out)
3121414Scindi {
3133062Scindi 	uint64_t pa = 0, offset = 0;
3143062Scindi 	int flags = 0;
3153062Scindi 	nvlist_t *asru;
3163062Scindi 	char *unum;
3171414Scindi 
3183062Scindi 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &pa) == 0)
3193062Scindi 		flags |= TOPO_MEMFMRI_PA;
3203062Scindi 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &offset) == 0)
3213062Scindi 		flags |= TOPO_MEMFMRI_OFFSET;
3223062Scindi 	if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0)
3233062Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
3243062Scindi 
3253062Scindi 	asru = mem_fmri(mod, pa, offset, unum, flags);
3263062Scindi 
3273062Scindi 	if (asru == NULL)
3283062Scindi 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3293062Scindi 
3303062Scindi 	*out = asru;
3313062Scindi 
3323062Scindi 	return (0);
3331414Scindi }
334