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