17aec1d6eScindi /* 27aec1d6eScindi * CDDL HEADER START 37aec1d6eScindi * 47aec1d6eScindi * The contents of this file are subject to the terms of the 55f25dc2aSgavinm * Common Development and Distribution License (the "License"). 65f25dc2aSgavinm * You may not use this file except in compliance with the License. 77aec1d6eScindi * 87aec1d6eScindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97aec1d6eScindi * or http://www.opensolaris.org/os/licensing. 107aec1d6eScindi * See the License for the specific language governing permissions 117aec1d6eScindi * and limitations under the License. 127aec1d6eScindi * 137aec1d6eScindi * When distributing Covered Code, include this CDDL HEADER in each 147aec1d6eScindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157aec1d6eScindi * If applicable, add the following below this CDDL HEADER, with the 167aec1d6eScindi * fields enclosed by brackets "[]" replaced with your own identifying 177aec1d6eScindi * information: Portions Copyright [yyyy] [name of copyright owner] 187aec1d6eScindi * 197aec1d6eScindi * CDDL HEADER END 207aec1d6eScindi */ 217aec1d6eScindi 227aec1d6eScindi /* 23*f6e214c7SGavin Maltby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 247aec1d6eScindi */ 257aec1d6eScindi 26e5ba14ffSstephh #include <ctype.h> 277aec1d6eScindi #include <errno.h> 287aec1d6eScindi #include <kstat.h> 297aec1d6eScindi #include <limits.h> 307aec1d6eScindi #include <strings.h> 317aec1d6eScindi #include <unistd.h> 32*f6e214c7SGavin Maltby #include <zone.h> 3370818f58Stsien #include <topo_error.h> 347aec1d6eScindi #include <fm/topo_mod.h> 357aec1d6eScindi #include <sys/fm/protocol.h> 367aec1d6eScindi 370eb822a1Scindi #include <topo_method.h> 380eb822a1Scindi #include <mem.h> 397aec1d6eScindi 4070818f58Stsien /* 4170818f58Stsien * platform specific mem module 4270818f58Stsien */ 4370818f58Stsien #define PLATFORM_MEM_VERSION MEM_VERSION 4470818f58Stsien #define PLATFORM_MEM_NAME "platform-mem" 4570818f58Stsien 467aec1d6eScindi static int mem_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 470eb822a1Scindi topo_instance_t, void *, void *); 487aec1d6eScindi static void mem_release(topo_mod_t *, tnode_t *); 497aec1d6eScindi static int mem_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 507aec1d6eScindi nvlist_t **); 510eb822a1Scindi static int mem_fmri_create(topo_mod_t *, tnode_t *, topo_version_t, 520eb822a1Scindi nvlist_t *, nvlist_t **); 537aec1d6eScindi 547aec1d6eScindi static const topo_method_t mem_methods[] = { 557aec1d6eScindi { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 567aec1d6eScindi TOPO_STABILITY_INTERNAL, mem_nvl2str }, 570eb822a1Scindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 580eb822a1Scindi TOPO_STABILITY_INTERNAL, mem_fmri_create }, 597aec1d6eScindi { NULL } 607aec1d6eScindi }; 617aec1d6eScindi 620eb822a1Scindi static const topo_modops_t mem_ops = 630eb822a1Scindi { mem_enum, mem_release }; 647aec1d6eScindi static const topo_modinfo_t mem_info = 650eb822a1Scindi { "mem", FM_FMRI_SCHEME_MEM, MEM_VERSION, &mem_ops }; 667aec1d6eScindi 670eb822a1Scindi int 680eb822a1Scindi mem_init(topo_mod_t *mod, topo_version_t version) 697aec1d6eScindi { 707aec1d6eScindi 710eb822a1Scindi topo_mod_setdebug(mod); 727aec1d6eScindi topo_mod_dprintf(mod, "initializing mem builtin\n"); 737aec1d6eScindi 740eb822a1Scindi if (version != MEM_VERSION) 750eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 760eb822a1Scindi 770eb822a1Scindi if (topo_mod_register(mod, &mem_info, TOPO_VERSION) != 0) { 787aec1d6eScindi topo_mod_dprintf(mod, "failed to register mem_info: " 797aec1d6eScindi "%s\n", topo_mod_errmsg(mod)); 800eb822a1Scindi return (-1); /* mod errno already set */ 817aec1d6eScindi } 820eb822a1Scindi 830eb822a1Scindi return (0); 847aec1d6eScindi } 857aec1d6eScindi 867aec1d6eScindi void 877aec1d6eScindi mem_fini(topo_mod_t *mod) 887aec1d6eScindi { 897aec1d6eScindi topo_mod_unregister(mod); 907aec1d6eScindi } 917aec1d6eScindi 927aec1d6eScindi /*ARGSUSED*/ 937aec1d6eScindi static int 947aec1d6eScindi mem_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 950eb822a1Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 967aec1d6eScindi { 97*f6e214c7SGavin Maltby int isglobal = (getzoneid() == GLOBAL_ZONEID); 9870818f58Stsien topo_mod_t *nmp; 9970818f58Stsien 100*f6e214c7SGavin Maltby if (isglobal && (nmp = topo_mod_load(mod, PLATFORM_MEM_NAME, 10170818f58Stsien PLATFORM_MEM_VERSION)) == NULL) { 10270818f58Stsien if (topo_mod_errno(mod) == ETOPO_MOD_NOENT) { 10370818f58Stsien /* 10470818f58Stsien * There is no platform specific mem module. 10570818f58Stsien */ 10670818f58Stsien (void) topo_method_register(mod, pnode, mem_methods); 10770818f58Stsien return (0); 10870818f58Stsien } else { 10970818f58Stsien /* Fail to load the module */ 11070818f58Stsien topo_mod_dprintf(mod, "Failed to load module %s: %s", 11170818f58Stsien PLATFORM_MEM_NAME, topo_mod_errmsg(mod)); 11270818f58Stsien return (-1); 11370818f58Stsien } 11470818f58Stsien } 11570818f58Stsien 116*f6e214c7SGavin Maltby if (isglobal && topo_mod_enumerate(nmp, pnode, PLATFORM_MEM_NAME, name, 11770818f58Stsien min, max, NULL) < 0) { 11870818f58Stsien topo_mod_dprintf(mod, "%s failed to enumerate: %s", 11970818f58Stsien PLATFORM_MEM_NAME, topo_mod_errmsg(mod)); 12070818f58Stsien return (-1); 12170818f58Stsien } 1227aec1d6eScindi (void) topo_method_register(mod, pnode, mem_methods); 1237aec1d6eScindi 1247aec1d6eScindi return (0); 1257aec1d6eScindi } 1267aec1d6eScindi 1277aec1d6eScindi static void 1287aec1d6eScindi mem_release(topo_mod_t *mod, tnode_t *node) 1297aec1d6eScindi { 1307aec1d6eScindi topo_method_unregister_all(mod, node); 1317aec1d6eScindi } 1327aec1d6eScindi 133e5ba14ffSstephh /* 134e5ba14ffSstephh * Convert an input string to a URI escaped string and return the new string. 135e5ba14ffSstephh * RFC2396 Section 2.4 says that data must be escaped if it does not have a 136e5ba14ffSstephh * representation using an unreserved character, where an unreserved character 137e5ba14ffSstephh * is one that is either alphanumeric or one of the marks defined in S2.3. 138e5ba14ffSstephh */ 139e5ba14ffSstephh static size_t 140e5ba14ffSstephh mem_fmri_uriescape(const char *s, const char *xmark, char *buf, size_t len) 141e5ba14ffSstephh { 142e5ba14ffSstephh static const char rfc2396_mark[] = "-_.!~*'()"; 143e5ba14ffSstephh static const char hex_digits[] = "0123456789ABCDEF"; 144e5ba14ffSstephh static const char empty_str[] = ""; 145e5ba14ffSstephh 146e5ba14ffSstephh const char *p; 147e5ba14ffSstephh char c, *q; 148e5ba14ffSstephh size_t n = 0; 149e5ba14ffSstephh 150e5ba14ffSstephh if (s == NULL) 151e5ba14ffSstephh s = empty_str; 152e5ba14ffSstephh 153e5ba14ffSstephh if (xmark == NULL) 154e5ba14ffSstephh xmark = empty_str; 155e5ba14ffSstephh 156e5ba14ffSstephh for (p = s; (c = *p) != '\0'; p++) { 157e5ba14ffSstephh if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) 158e5ba14ffSstephh n++; /* represent c as itself */ 159e5ba14ffSstephh else 160e5ba14ffSstephh n += 3; /* represent c as escape */ 161e5ba14ffSstephh } 162e5ba14ffSstephh 163e5ba14ffSstephh if (buf == NULL) 164e5ba14ffSstephh return (n); 165e5ba14ffSstephh 166e5ba14ffSstephh for (p = s, q = buf; (c = *p) != '\0' && q < buf + len; p++) { 167e5ba14ffSstephh if (isalnum(c) || strchr(rfc2396_mark, c) || strchr(xmark, c)) { 168e5ba14ffSstephh *q++ = c; 169e5ba14ffSstephh } else { 170e5ba14ffSstephh *q++ = '%'; 171e5ba14ffSstephh *q++ = hex_digits[((uchar_t)c & 0xf0) >> 4]; 172e5ba14ffSstephh *q++ = hex_digits[(uchar_t)c & 0xf]; 173e5ba14ffSstephh } 174e5ba14ffSstephh } 175e5ba14ffSstephh 176e5ba14ffSstephh if (q == buf + len) 177e5ba14ffSstephh q--; /* len is too small: truncate output string */ 178e5ba14ffSstephh 179e5ba14ffSstephh *q = '\0'; 180e5ba14ffSstephh return (n); 181e5ba14ffSstephh } 182e5ba14ffSstephh 1837aec1d6eScindi /*ARGSUSED*/ 1847aec1d6eScindi static int 1857aec1d6eScindi mem_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 1867aec1d6eScindi nvlist_t *in, nvlist_t **out) 1877aec1d6eScindi { 1887aec1d6eScindi const char *format; 1897aec1d6eScindi nvlist_t *nvl; 1907aec1d6eScindi uint64_t val; 1917aec1d6eScindi char *buf, *unum; 1927aec1d6eScindi size_t len; 1937aec1d6eScindi int err; 194e5ba14ffSstephh char *preunum, *escunum, *prefix; 195e5ba14ffSstephh ssize_t presz; 196e5ba14ffSstephh int i; 1977aec1d6eScindi 1987aec1d6eScindi if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) 1997aec1d6eScindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 2007aec1d6eScindi 2017aec1d6eScindi if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0) { 2027aec1d6eScindi nvlist_free(nvl); 2037aec1d6eScindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 2047aec1d6eScindi } 2057aec1d6eScindi 2067aec1d6eScindi /* 2077aec1d6eScindi * If we have a DIMM offset, include it in the string. If we have a 2087aec1d6eScindi * PA then use that. Otherwise just format the unum element. 2097aec1d6eScindi */ 2109dd0f810Scindi if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &val) == 0) { 211e5ba14ffSstephh format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/" 212e5ba14ffSstephh FM_FMRI_MEM_OFFSET "=%3$llx"; 2139dd0f810Scindi } else if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &val) == 0) { 214e5ba14ffSstephh format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s/" 215e5ba14ffSstephh FM_FMRI_MEM_PHYSADDR "=%3$llx"; 2167aec1d6eScindi } else 217e5ba14ffSstephh format = FM_FMRI_SCHEME_MEM ":///%1$s%2$s"; 2187aec1d6eScindi 2198a40a695Sgavinm /* 2200eb822a1Scindi * If we have a well-formed unum we step over the hc:// and 2210eb822a1Scindi * authority prefix 2228a40a695Sgavinm */ 2230eb822a1Scindi if (strncmp(unum, "hc://", 5) == 0) { 2240eb822a1Scindi unum += 5; 2250eb822a1Scindi unum = strchr(unum, '/'); 2260eb822a1Scindi ++unum; 227e5ba14ffSstephh prefix = ""; 228e5ba14ffSstephh escunum = unum; 229e5ba14ffSstephh } else { 230e5ba14ffSstephh prefix = FM_FMRI_MEM_UNUM "="; 231e5ba14ffSstephh preunum = topo_mod_strdup(mod, unum); 232e5ba14ffSstephh presz = strlen(preunum) + 1; 233e5ba14ffSstephh 234e5ba14ffSstephh for (i = 0; i < presz - 1; i++) { 235e5ba14ffSstephh if (preunum[i] == ':' && preunum[i + 1] == ' ') { 236e5ba14ffSstephh bcopy(preunum + i + 2, preunum + i + 1, 237e5ba14ffSstephh presz - (i + 2)); 238e5ba14ffSstephh } else if (preunum[i] == ' ') { 239e5ba14ffSstephh preunum[i] = ','; 240e5ba14ffSstephh } 2410eb822a1Scindi } 2428a40a695Sgavinm 243e5ba14ffSstephh i = mem_fmri_uriescape(preunum, ":,/", NULL, 0); 244e5ba14ffSstephh escunum = topo_mod_alloc(mod, i + 1); 245e5ba14ffSstephh (void) mem_fmri_uriescape(preunum, ":,/", escunum, i + 1); 246e5ba14ffSstephh topo_mod_free(mod, preunum, presz); 247e5ba14ffSstephh } 248e5ba14ffSstephh 249e5ba14ffSstephh len = snprintf(NULL, 0, format, prefix, escunum, val) + 1; 2507aec1d6eScindi buf = topo_mod_zalloc(mod, len); 2517aec1d6eScindi 2527aec1d6eScindi if (buf == NULL) { 2537aec1d6eScindi nvlist_free(nvl); 2547aec1d6eScindi return (topo_mod_seterrno(mod, EMOD_NOMEM)); 2557aec1d6eScindi } 2567aec1d6eScindi 257e5ba14ffSstephh (void) snprintf(buf, len, format, prefix, escunum, val); 258e5ba14ffSstephh if (escunum != unum) 259e5ba14ffSstephh topo_mod_strfree(mod, escunum); 2607aec1d6eScindi err = nvlist_add_string(nvl, "fmri-string", buf); 2617aec1d6eScindi topo_mod_free(mod, buf, len); 2627aec1d6eScindi 2637aec1d6eScindi if (err != 0) { 2647aec1d6eScindi nvlist_free(nvl); 2657aec1d6eScindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 2667aec1d6eScindi } 2677aec1d6eScindi 2687aec1d6eScindi *out = nvl; 2697aec1d6eScindi return (0); 2707aec1d6eScindi } 2717aec1d6eScindi 2720eb822a1Scindi static nvlist_t * 2730eb822a1Scindi mem_fmri(topo_mod_t *mod, uint64_t pa, uint64_t offset, char *unum, int flags) 2747aec1d6eScindi { 2750eb822a1Scindi int err; 2760eb822a1Scindi nvlist_t *asru; 2770eb822a1Scindi 2780eb822a1Scindi if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 2790eb822a1Scindi return (NULL); 2800eb822a1Scindi 2810eb822a1Scindi /* 2820eb822a1Scindi * If we have a well-formed unum we step over the hc:/// and 2830eb822a1Scindi * authority prefix 2840eb822a1Scindi */ 2850eb822a1Scindi if (strncmp(unum, "hc://", 5) == 0) { 2860eb822a1Scindi char *tstr; 2870eb822a1Scindi 2880eb822a1Scindi tstr = strchr(unum, '/'); 2890eb822a1Scindi unum = ++tstr; 2900eb822a1Scindi } 2910eb822a1Scindi 2920eb822a1Scindi err = nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION); 2930eb822a1Scindi err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM); 2940eb822a1Scindi err |= nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum); 2950eb822a1Scindi if (flags & TOPO_MEMFMRI_PA) 2960eb822a1Scindi err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa); 2970eb822a1Scindi if (flags & TOPO_MEMFMRI_OFFSET) 2980eb822a1Scindi err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset); 2990eb822a1Scindi 3000eb822a1Scindi if (err != 0) { 3010eb822a1Scindi nvlist_free(asru); 3020eb822a1Scindi return (NULL); 3030eb822a1Scindi } 3040eb822a1Scindi 3050eb822a1Scindi return (asru); 3067aec1d6eScindi } 3077aec1d6eScindi 3087aec1d6eScindi /*ARGSUSED*/ 3097aec1d6eScindi static int 3100eb822a1Scindi mem_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version, 3117aec1d6eScindi nvlist_t *in, nvlist_t **out) 3127aec1d6eScindi { 3130eb822a1Scindi uint64_t pa = 0, offset = 0; 3140eb822a1Scindi int flags = 0; 3150eb822a1Scindi nvlist_t *asru; 3160eb822a1Scindi char *unum; 3177aec1d6eScindi 3180eb822a1Scindi if (nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &pa) == 0) 3190eb822a1Scindi flags |= TOPO_MEMFMRI_PA; 3200eb822a1Scindi if (nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &offset) == 0) 3210eb822a1Scindi flags |= TOPO_MEMFMRI_OFFSET; 3220eb822a1Scindi if (nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0) 3230eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 3247aec1d6eScindi 3250eb822a1Scindi asru = mem_fmri(mod, pa, offset, unum, flags); 3267aec1d6eScindi 3270eb822a1Scindi if (asru == NULL) 3280eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 3290eb822a1Scindi 3300eb822a1Scindi *out = asru; 3310eb822a1Scindi 3320eb822a1Scindi return (0); 3337aec1d6eScindi } 334