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