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