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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <sys/types.h> 31 #include <fm/topo_mod.h> 32 #include <sys/fm/protocol.h> 33 34 #include <unistd.h> 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <umem.h> 39 40 #include <mem_mdesc.h> 41 42 /* 43 * Enumerates the DIMMS in a system. For each DIMM found, the necessary nodes 44 * are also constructed. 45 */ 46 47 #define DIMM_VERSION TOPO_VERSION 48 #define DIMM_NODE_NAME "dimm" 49 50 extern topo_method_t pi_mem_methods[]; 51 52 /* Forward declaration */ 53 static int dimm_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 54 topo_instance_t, void *, void *); 55 static void dimm_release(topo_mod_t *, tnode_t *); 56 57 static const topo_modops_t dimm_ops = 58 { dimm_enum, dimm_release }; 59 static const topo_modinfo_t dimm_info = 60 { "dimm", FM_FMRI_SCHEME_HC, DIMM_VERSION, &dimm_ops }; 61 62 static const topo_pgroup_info_t mem_auth_pgroup = { 63 FM_FMRI_AUTHORITY, 64 TOPO_STABILITY_PRIVATE, 65 TOPO_STABILITY_PRIVATE, 66 1 67 }; 68 69 int 70 _topo_init(topo_mod_t *mod) 71 { 72 md_mem_info_t *mem; 73 74 if (getenv("TOPOMEMDBG")) 75 topo_mod_setdebug(mod); 76 topo_mod_dprintf(mod, "initializing mem enumerator\n"); 77 78 if ((mem = topo_mod_zalloc(mod, sizeof (md_mem_info_t))) == NULL) 79 return (-1); 80 81 if (mem_mdesc_init(mod, mem) != 0) { 82 topo_mod_dprintf(mod, "failed to get dimms from the PRI/MD\n"); 83 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 84 return (-1); 85 } 86 87 topo_mod_setspecific(mod, (void *)mem); 88 89 if (topo_mod_register(mod, &dimm_info, TOPO_VERSION) != 0) { 90 topo_mod_dprintf(mod, "failed to register hc: " 91 "%s\n", topo_mod_errmsg(mod)); 92 mem_mdesc_fini(mod, mem); 93 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 94 return (-1); 95 } 96 97 topo_mod_dprintf(mod, "mem enumerator inited\n"); 98 99 return (0); 100 } 101 102 void 103 _topo_fini(topo_mod_t *mod) 104 { 105 md_mem_info_t *mem; 106 107 mem = (md_mem_info_t *)topo_mod_getspecific(mod); 108 109 mem_mdesc_fini(mod, mem); 110 111 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 112 113 topo_mod_unregister(mod); 114 } 115 116 static tnode_t * 117 mem_tnode_create(topo_mod_t *mod, tnode_t *parent, 118 const char *name, topo_instance_t i, char *serial, 119 nvlist_t *fru, char *label, void *priv) 120 { 121 int err; 122 nvlist_t *fmri; 123 tnode_t *ntn; 124 nvlist_t *auth = topo_mod_auth(mod, parent); 125 126 fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i, 127 NULL, auth, NULL, NULL, serial); 128 nvlist_free(auth); 129 if (fmri == NULL) { 130 topo_mod_dprintf(mod, 131 "Unable to make nvlist for %s bind: %s.\n", 132 name, topo_mod_errmsg(mod)); 133 return (NULL); 134 } 135 136 ntn = topo_node_bind(mod, parent, name, i, fmri); 137 if (ntn == NULL) { 138 topo_mod_dprintf(mod, 139 "topo_node_bind (%s%d/%s%d) failed: %s\n", 140 topo_node_name(parent), topo_node_instance(parent), 141 name, i, 142 topo_strerror(topo_mod_errno(mod))); 143 nvlist_free(fmri); 144 return (NULL); 145 } 146 nvlist_free(fmri); 147 topo_node_setspecific(ntn, priv); 148 149 if (topo_pgroup_create(ntn, &mem_auth_pgroup, &err) == 0) { 150 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY, 151 FM_FMRI_AUTH_PRODUCT, &err); 152 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY, 153 FM_FMRI_AUTH_PRODUCT_SN, &err); 154 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY, 155 FM_FMRI_AUTH_CHASSIS, &err); 156 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY, 157 FM_FMRI_AUTH_SERVER, &err); 158 } 159 160 (void) topo_node_label_set(ntn, label, &err); 161 (void) topo_node_fru_set(ntn, fru, 0, &err); 162 163 return (ntn); 164 } 165 166 typedef struct { 167 const char *nh_name; 168 const char *nh_sscan; 169 } nac_hc_t; 170 171 static const nac_hc_t nac_mem_tbl[] = { 172 {"branch", "BR%d" }, 173 {"dram-channel", "CH%d" }, 174 {"rank", "R%d" }, 175 {"dimm", "D%d" }, 176 {"memboard", "MR%d" }, 177 {"memboard", "MEM%d" }, 178 {"chip", "CMP%d" } 179 }; 180 181 static const char * 182 nac2hc(const char *s, int *inst) 183 { 184 int i; 185 186 if (s == NULL) 187 return (NULL); 188 189 for (i = 0; i < sizeof (nac_mem_tbl) / sizeof (nac_hc_t); i++) { 190 if (sscanf(s, nac_mem_tbl[i].nh_sscan, inst) == 1) 191 return (nac_mem_tbl[i].nh_name); 192 } 193 return (NULL); 194 } 195 196 static int 197 create_one_dimm(topo_mod_t *mod, tnode_t *pnode, int inst, mem_dimm_map_t *dp) 198 { 199 tnode_t *cnode; 200 nvlist_t *rsrc, *fru; 201 int nerr = 0, err; 202 nvlist_t *auth = NULL; 203 204 /* 205 * Because mem_tnode_create will fill in a "FRU" value by default, 206 * but not an "ASRU" value, we have to compute the desired "FRU" 207 * value -before- calling mem_tnode_create, but it's ok to 208 * topo_mod_asru_set() the ASRU value after the topo_node is 209 * created. 210 */ 211 212 auth = topo_mod_auth(mod, pnode); 213 if ((fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, "dimm", 214 inst, NULL, auth, dp->dm_part, NULL, dp->dm_serid)) == NULL) 215 nerr++; 216 nvlist_free(auth); 217 218 cnode = mem_tnode_create(mod, pnode, "dimm", inst, 219 dp->dm_serid, fru, dp->dm_label, NULL); 220 nvlist_free(fru); 221 if (cnode == NULL) 222 return (++nerr); 223 224 rsrc = NULL; 225 /* ASRU will be computed by topo method */ 226 if (topo_node_resource(cnode, &rsrc, &err) < 0 || 227 topo_method_register(mod, cnode, pi_mem_methods) < 0 || 228 topo_node_asru_set(cnode, rsrc, TOPO_ASRU_COMPUTE, &err) < 0) 229 nerr++; 230 nvlist_free(rsrc); 231 232 return (nerr); 233 } 234 235 int 236 slashorend(const char *s, int start) 237 { 238 const char *t = s + start; 239 240 if ((t = strchr(t, '/')) == NULL) 241 return (strlen(s)); /* end */ 242 else 243 return (t - s); /* next slash */ 244 } 245 246 /* 247 * mem_range_create and mem_inst_create are mutually recursive routines which 248 * together create the node hierarchy for one dimm and its siblings. 249 * mem_range_create is called when creating the first instance of a given node 250 * type as child of a parent instance, because it is then, and only then, 251 * that a topo range must be created. It calls mem_inst_create for its first 252 * and subsequent instances. The recursion always starts with 253 * mem_range_create, so it performs the up-front sanity checks. 254 * 255 * Note: the list of mem_dimm_map_t's pointed at by dp must be sorted 256 * alphabetically by *dm_label. 257 */ 258 259 static int mem_range_create(topo_mod_t *, tnode_t *, int, mem_dimm_map_t *); 260 261 static int 262 mem_inst_create(topo_mod_t *mod, tnode_t *pnode, int pflen, mem_dimm_map_t *dp) 263 { 264 int inst, pfnext; 265 const char *nodename; 266 tnode_t *cnode; 267 mem_dimm_map_t *d; 268 nvlist_t *fru; 269 int nerr = 0; 270 271 pfnext = slashorend(dp->dm_label, pflen); 272 nodename = nac2hc((dp->dm_label) + pflen, &inst); 273 d = dp; 274 if (strcmp(nodename, "dimm") == 0) { 275 return (create_one_dimm(mod, pnode, inst, dp)); 276 } else if (*(d->dm_label + pfnext) == '\0') { /* this node has a fru */ 277 fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, 278 nodename, inst, NULL, NULL, dp->dm_part, NULL, 279 dp->dm_serid); 280 cnode = mem_tnode_create(mod, pnode, nodename, inst, 281 dp->dm_serid, fru, dp->dm_label, NULL); 282 nvlist_free(fru); 283 d = dp->dm_next; /* next mem_dimm_map_t could be child */ 284 } else { 285 cnode = mem_tnode_create(mod, pnode, nodename, inst, 286 NULL, NULL, NULL, NULL); 287 } 288 if ((d != NULL) && 289 strncmp(dp->dm_label, d->dm_label, pfnext) == 0) 290 nerr += mem_range_create(mod, cnode, pfnext+1, d); 291 return (nerr); 292 } 293 294 int 295 mem_range_create(topo_mod_t *mod, tnode_t *pnode, int pflen, 296 mem_dimm_map_t *dp) 297 { 298 int inst, pfnext; 299 const char *nodename; 300 mem_dimm_map_t *d; 301 int nerr = 0; 302 303 if (pnode == NULL) 304 return (1); /* definitely an error */ 305 if (*(dp->dm_label + pflen) == '\0') 306 return (1); /* recursed too far */ 307 308 pfnext = slashorend(dp->dm_label, pflen); 309 nodename = nac2hc(dp->dm_label + pflen, &inst); 310 311 if (nodename != NULL) { 312 if (topo_node_range_create(mod, pnode, nodename, 0, 313 MEM_DIMM_MAX) < 0) { 314 topo_mod_dprintf(mod, "failed to create " 315 "DIMM range %s error %s\n", nodename, 316 topo_mod_errmsg(mod)); 317 return (-1); 318 } 319 } else { 320 /* 321 * Skip over NAC elements other than those listed 322 * above. These elements will appear 323 * in the DIMM's unum, but not in hc: scheme hierarchy. 324 */ 325 326 return (mem_range_create(mod, pnode, pfnext+1, dp)); 327 } 328 329 nerr += mem_inst_create(mod, pnode, pflen, dp); 330 331 for (d = dp->dm_next; d != NULL; d = d->dm_next) { 332 if (strncmp(dp->dm_label, d->dm_label, pfnext) == 0) 333 continue; /* child of 1st instance -- already done */ 334 else if (strncmp(dp->dm_label, d->dm_label, 335 pflen) == 0) { /* child of same parent */ 336 if (nodename == nac2hc((d->dm_label)+pflen, &inst)) { 337 /* 338 * Same nodename as sibling. Don't create 339 * new range, or the enumeration will die. 340 */ 341 nerr += mem_inst_create(mod, pnode, pflen, d); 342 dp = d; 343 } else { 344 nodename = nac2hc((d->dm_label)+pflen, &inst); 345 nerr += mem_range_create(mod, pnode, pflen, d); 346 dp = d; 347 } 348 } 349 else 350 return (nerr); /* finished all children of my parent */ 351 } 352 return (nerr); /* reached end of mem_dimm_map_t list */ 353 } 354 static int 355 mem_create(topo_mod_t *mod, tnode_t *rnode, md_mem_info_t *cm) 356 { 357 int l, nerrs; 358 char nodename[10]; /* allows up to 10^6 chips in system */ 359 char *p; 360 mem_dimm_map_t *dp; 361 362 if (strcmp(topo_node_name(rnode), "chip") == 0) { 363 364 (void) snprintf(nodename, 10, "CMP%d", 365 topo_node_instance(rnode)); 366 367 for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) { 368 p = strstr(dp->dm_label, nodename); 369 if (p != NULL && (p = strchr(p, '/')) != NULL) { 370 l = p - (dp->dm_label) + 1; 371 break; 372 } 373 } 374 } else if (strcmp(topo_node_name(rnode), "motherboard") == 0) { 375 for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) { 376 p = strstr(dp->dm_label, "MB/MEM"); 377 if (p != NULL) { 378 l = 3; /* start with MEM */ 379 break; 380 } 381 } 382 } else { 383 return (1); 384 } 385 386 if (dp != NULL) 387 nerrs = mem_range_create(mod, rnode, l, dp); 388 else 389 nerrs = 1; 390 return (nerrs); 391 } 392 393 394 /* 395 * The hc-scheme memory enumerator is invoked from within a platform 396 * toplogy file. Make sure that the invocation is either 397 * 1) a child of the chip enumerator, which will cause the argument "rnode" 398 * below to be a chip node, and the dimm structures specific for that chip can 399 * then be built from its specific node, or 400 * 2) a child of the motherboard enumerator -- for Batoka and similar machines 401 * with cpu-boards. 402 */ 403 404 /*ARGSUSED*/ 405 static int 406 dimm_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 407 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 408 { 409 md_mem_info_t *mem = (md_mem_info_t *)arg; 410 411 if (strcmp(name, DIMM_NODE_NAME) == 0) 412 return (mem_create(mod, rnode, mem)); 413 414 return (-1); 415 } 416 417 /*ARGSUSED*/ 418 static void 419 dimm_release(topo_mod_t *mp, tnode_t *node) 420 { 421 } 422