Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <ctype.h>
     30 #include <errno.h>
     31 #include <kstat.h>
     32 #include <limits.h>
     33 #include <strings.h>
     34 #include <unistd.h>
     35 #include <topo_error.h>
     36 #include <fm/topo_mod.h>
     37 #include <sys/fm/protocol.h>
     38 
     39 #include <topo_method.h>
     40 #include <mem.h>
     41 
     42 /*
     43  * platform specific mem module
     44  */
     45 #define	PLATFORM_MEM_VERSION	MEM_VERSION
     46 #define	PLATFORM_MEM_NAME	"platform-mem"
     47 
     48 static int mem_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
     49     topo_instance_t, void *, void *);
     50 static void mem_release(topo_mod_t *, tnode_t *);
     51 static int mem_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
     52     nvlist_t **);
     53 static int mem_fmri_create(topo_mod_t *, tnode_t *, topo_version_t,
     54     nvlist_t *, nvlist_t **);
     55 
     56 static const topo_method_t mem_methods[] = {
     57 	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
     58 	    TOPO_STABILITY_INTERNAL, mem_nvl2str },
     59 	{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
     60 	    TOPO_STABILITY_INTERNAL, mem_fmri_create },
     61 	{ NULL }
     62 };
     63 
     64 static const topo_modops_t mem_ops =
     65 	{ mem_enum, mem_release };
     66 static const topo_modinfo_t mem_info =
     67 	{ "mem", FM_FMRI_SCHEME_MEM, MEM_VERSION, &mem_ops };
     68 
     69 int
     70 mem_init(topo_mod_t *mod, topo_version_t version)
     71 {
     72 
     73 	topo_mod_setdebug(mod);
     74 	topo_mod_dprintf(mod, "initializing mem builtin\n");
     75 
     76 	if (version != MEM_VERSION)
     77 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
     78 
     79 	if (topo_mod_register(mod, &mem_info, TOPO_VERSION) != 0) {
     80 		topo_mod_dprintf(mod, "failed to register mem_info: "
     81 		    "%s\n", topo_mod_errmsg(mod));
     82 		return (-1); /* mod errno already set */
     83 	}
     84 
     85 	return (0);
     86 }
     87 
     88 void
     89 mem_fini(topo_mod_t *mod)
     90 {
     91 	topo_mod_unregister(mod);
     92 }
     93 
     94 /*ARGSUSED*/
     95 static int
     96 mem_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
     97     topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
     98 {
     99 	topo_mod_t *nmp;
    100 
    101 	if ((nmp = topo_mod_load(mod, PLATFORM_MEM_NAME,
    102 	    PLATFORM_MEM_VERSION)) == NULL) {
    103 		if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) {
    104 			/*
    105 			 * There is no platform specific mem module.
    106 			 */
    107 			(void) topo_method_register(mod, pnode, mem_methods);
    108 			return (0);
    109 		} else {
    110 			/* Fail to load the module */
    111 			topo_mod_dprintf(mod, "Failed to load module %s: %s",
    112 			    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
    113 			return (-1);
    114 		}
    115 	}
    116 
    117 	if (topo_mod_enumerate(nmp, pnode, PLATFORM_MEM_NAME, name,
    118 	    min, max, NULL) < 0) {
    119 		topo_mod_dprintf(mod, "%s failed to enumerate: %s",
    120 		    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
    121 		return (-1);
    122 	}
    123 	(void) topo_method_register(mod, pnode, mem_methods);
    124 
    125 	return (0);
    126 }
    127 
    128 static void
    129 mem_release(topo_mod_t *mod, tnode_t *node)
    130 {
    131 	topo_method_unregister_all(mod, node);
    132 }
    133 
    134 /*
    135  * Convert an input string to a URI escaped string and return the new string.
    136  * RFC2396 Section 2.4 says that data must be escaped if it does not have a
    137  * representation using an unreserved character, where an unreserved character
    138  * is one that is either alphanumeric or one of the marks defined in S2.3.
    139  */
    140 static size_t
    141 mem_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len)
    142 {
    143 	static const char rfc2396_mark[] = "-_.!~*'()";
    144 	static const char hex_digits[] = "0123456789ABCDEF";
    145 	static const char empty_str[] = "";
    146 
    147 	const char *p;
    148 	char c, *q;
    149 	size_t n = 0;
    150 
    151 	if (s == NULL)
    152 		s = empty_str;
    153 
    154 	if (xmark == NULL)
    155 		xmark = empty_str;
    156 
    157 	for (p = s; (c = *p) != '\0'; p++) {
    158 		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c))
    159 			n++;    /* represent c as itself */
    160 		else
    161 			n += 3; /* represent c as escape */
    162 	}
    163 
    164 	if (buf == NULL)
    165 		return (n);
    166 
    167 	for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) {
    168 		if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) {
    169 			*q++ = c;
    170 		} else {
    171 			*q++ = '%';
    172 			*q++ = hex_digits[((uchar_t)c & 0xf0) >> 4];
    173 			*q++ = hex_digits[(uchar_t)c & 0xf];
    174 		}
    175 	}
    176 
    177 	if (q == buf + len)
    178 		q--; /* len is too small: truncate output string */
    179 
    180 	*q = '\0';
    181 	return (n);
    182 }
    183 
    184 /*ARGSUSED*/
    185 static int
    186 mem_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    187     nvlist_t *in, nvlist_t **out)
    188 {
    189 	const char *format;
    190 	nvlist_t *nvl;
    191 	uint64_t val;
    192 	char *buf, *unum;
    193 	size_t len;
    194 	int err;
    195 	char *preunum, *escunum, *prefix;
    196 	ssize_t presz;
    197 	int i;
    198 
    199 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
    200 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    201 
    202 	if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0) {
    203 		nvlist_free(nvl);
    204 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    205 	}
    206 
    207 	/*
    208 	 * If we have a DIMM offset, include it in the string.  If we have a
    209 	 * PA then use that.  Otherwise just format the unum element.
    210 	 */
    211 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &val) == 0) {
    212 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/"
    213 		    FM_FMRI_MEM_OFFSET "=%3$llx";
    214 	} else if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
    215 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/"
    216 		    FM_FMRI_MEM_PHYSADDR "=%3$llx";
    217 	} else
    218 		format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s";
    219 
    220 	/*
    221 	 * If we have a well-formed unum we step over the hc:// and
    222 	 * authority prefix
    223 	 */
    224 	if (strncmp(unum, "hc://", 5) == 0) {
    225 		unum += 5;
    226 		unum = strchr(unum, '/');
    227 		++unum;
    228 		prefix = "";
    229 		escunum = unum;
    230 	} else {
    231 		prefix = FM_FMRI_MEM_UNUM "=";
    232 		preunum = topo_mod_strdup(mod, unum);
    233 		presz = strlen(preunum) + 1;
    234 
    235 		for (i = 0; i < presz - 1; i++) {
    236 			if (preunum[i] == ':' && preunum[i + 1] == ' ') {
    237 				bcopy(preunum + i + 2, preunum + i + 1,
    238 				    presz - (i + 2));
    239 			} else if (preunum[i] == ' ') {
    240 				preunum[i] = ',';
    241 			}
    242 		}
    243 
    244 		i = mem_fmri_uriescape(preunum, ":,/", NULL, 0);
    245 		escunum = topo_mod_alloc(mod, i + 1);
    246 		(void) mem_fmri_uriescape(preunum, ":,/", escunum, i + 1);
    247 		topo_mod_free(mod, preunum, presz);
    248 	}
    249 
    250 	len = snprintf(NULL, 0, format, prefix, escunum, val) + 1;
    251 	buf = topo_mod_zalloc(mod, len);
    252 
    253 	if (buf == NULL) {
    254 		nvlist_free(nvl);
    255 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    256 	}
    257 
    258 	(void) snprintf(buf, len, format, prefix, escunum, val);
    259 	if (escunum != unum)
    260 		topo_mod_strfree(mod, escunum);
    261 	err = nvlist_add_string(nvl, "fmri-string", buf);
    262 	topo_mod_free(mod, buf, len);
    263 
    264 	if (err != 0) {
    265 		nvlist_free(nvl);
    266 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    267 	}
    268 
    269 	*out = nvl;
    270 	return (0);
    271 }
    272 
    273 static nvlist_t *
    274 mem_fmri(topo_mod_t *mod, uint64_t pa, uint64_t offset, char *unum, int flags)
    275 {
    276 	int err;
    277 	nvlist_t *asru;
    278 
    279 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
    280 		return (NULL);
    281 
    282 	/*
    283 	 * If we have a well-formed unum we step over the hc:/// and
    284 	 * authority prefix
    285 	 */
    286 	if (strncmp(unum, "hc://", 5) == 0) {
    287 		char *tstr;
    288 
    289 		tstr = strchr(unum, '/');
    290 		unum = ++tstr;
    291 	}
    292 
    293 	err = nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION);
    294 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM);
    295 	err |= nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum);
    296 	if (flags & TOPO_MEMFMRI_PA)
    297 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa);
    298 	if (flags & TOPO_MEMFMRI_OFFSET)
    299 		err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset);
    300 
    301 	if (err != 0) {
    302 		nvlist_free(asru);
    303 		return (NULL);
    304 	}
    305 
    306 	return (asru);
    307 }
    308 
    309 /*ARGSUSED*/
    310 static int
    311 mem_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    312     nvlist_t *in, nvlist_t **out)
    313 {
    314 	uint64_t pa = 0, offset = 0;
    315 	int flags = 0;
    316 	nvlist_t *asru;
    317 	char *unum;
    318 
    319 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &pa) == 0)
    320 		flags |= TOPO_MEMFMRI_PA;
    321 	if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &offset) == 0)
    322 		flags |= TOPO_MEMFMRI_OFFSET;
    323 	if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0)
    324 		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
    325 
    326 	asru = mem_fmri(mod, pa, offset, unum, flags);
    327 
    328 	if (asru == NULL)
    329 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    330 
    331 	*out = asru;
    332 
    333 	return (0);
    334 }
    335