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 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 87 mem_fini(topo_mod_t *mod) 88 { 89 topo_mod_unregister(mod); 90 } 91 92 /*ARGSUSED*/ 93 static int 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 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 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 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 * 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 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