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