Home | History | Annotate | Download | only in dimm
      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