2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <sys/types.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <sys/fm/protocol.h>
2N/A
2N/A#include <unistd.h>
2N/A#include <sys/param.h>
2N/A#include <sys/stat.h>
2N/A#include <fcntl.h>
2N/A#include <umem.h>
2N/A
2N/A#include <mem_mdesc.h>
2N/A
2N/A/*
2N/A * Enumerates the DIMMS in a system. For each DIMM found, the necessary nodes
2N/A * are also constructed.
2N/A */
2N/A
2N/A#define DIMM_VERSION TOPO_VERSION
2N/A#define DIMM_NODE_NAME "dimm"
2N/A
2N/Aextern topo_method_t pi_mem_methods[];
2N/A
2N/A/* Forward declaration */
2N/Astatic int dimm_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
2N/A topo_instance_t, void *, void *);
2N/Astatic void dimm_release(topo_mod_t *, tnode_t *);
2N/A
2N/Astatic const topo_modops_t dimm_ops =
2N/A { dimm_enum, dimm_release };
2N/Astatic const topo_modinfo_t dimm_info =
2N/A { "dimm", FM_FMRI_SCHEME_HC, DIMM_VERSION, &dimm_ops };
2N/A
2N/Astatic const topo_pgroup_info_t mem_auth_pgroup = {
2N/A FM_FMRI_AUTHORITY,
2N/A TOPO_STABILITY_PRIVATE,
2N/A TOPO_STABILITY_PRIVATE,
2N/A 1
2N/A};
2N/A
2N/Aint
2N/A_topo_init(topo_mod_t *mod)
2N/A{
2N/A md_mem_info_t *mem;
2N/A
2N/A if (getenv("TOPOMEMDBG"))
2N/A topo_mod_setdebug(mod);
2N/A topo_mod_dprintf(mod, "initializing mem enumerator\n");
2N/A
2N/A if ((mem = topo_mod_zalloc(mod, sizeof (md_mem_info_t))) == NULL)
2N/A return (-1);
2N/A
2N/A if (mem_mdesc_init(mod, mem) != 0) {
2N/A topo_mod_dprintf(mod, "failed to get dimms from the PRI/MD\n");
2N/A topo_mod_free(mod, mem, sizeof (md_mem_info_t));
2N/A return (-1);
2N/A }
2N/A
2N/A topo_mod_setspecific(mod, (void *)mem);
2N/A
2N/A if (topo_mod_register(mod, &dimm_info, TOPO_VERSION) != 0) {
2N/A topo_mod_dprintf(mod, "failed to register hc: "
2N/A "%s\n", topo_mod_errmsg(mod));
2N/A mem_mdesc_fini(mod, mem);
2N/A topo_mod_free(mod, mem, sizeof (md_mem_info_t));
2N/A return (-1);
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "mem enumerator inited\n");
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/A_topo_fini(topo_mod_t *mod)
2N/A{
2N/A md_mem_info_t *mem;
2N/A
2N/A mem = (md_mem_info_t *)topo_mod_getspecific(mod);
2N/A
2N/A mem_mdesc_fini(mod, mem);
2N/A
2N/A topo_mod_free(mod, mem, sizeof (md_mem_info_t));
2N/A
2N/A topo_mod_unregister(mod);
2N/A}
2N/A
2N/Astatic tnode_t *
2N/Amem_tnode_create(topo_mod_t *mod, tnode_t *parent,
2N/A const char *name, topo_instance_t i, char *serial,
2N/A nvlist_t *fru, char *label, void *priv)
2N/A{
2N/A int err;
2N/A nvlist_t *fmri;
2N/A tnode_t *ntn;
2N/A nvlist_t *auth = topo_mod_auth(mod, parent);
2N/A
2N/A fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
2N/A NULL, auth, NULL, NULL, serial);
2N/A nvlist_free(auth);
2N/A if (fmri == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "Unable to make nvlist for %s bind: %s.\n",
2N/A name, topo_mod_errmsg(mod));
2N/A return (NULL);
2N/A }
2N/A
2N/A ntn = topo_node_bind(mod, parent, name, i, fmri);
2N/A if (ntn == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "topo_node_bind (%s%d/%s%d) failed: %s\n",
2N/A topo_node_name(parent), topo_node_instance(parent),
2N/A name, i,
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A nvlist_free(fmri);
2N/A return (NULL);
2N/A }
2N/A nvlist_free(fmri);
2N/A topo_node_setspecific(ntn, priv);
2N/A
2N/A if (topo_pgroup_create(ntn, &mem_auth_pgroup, &err) == 0) {
2N/A /* chassis */
2N/A (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_MFG, &err);
2N/A (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_NM, &err);
2N/A (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_PN, &err);
2N/A (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_SN, &err);
2N/A (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_ALIAS, &err);
2N/A }
2N/A
2N/A (void) topo_node_label_set(ntn, label, &err);
2N/A (void) topo_node_fru_set(ntn, fru, 0, &err);
2N/A
2N/A return (ntn);
2N/A}
2N/A
2N/Atypedef struct {
2N/A const char *nh_name;
2N/A const char *nh_sscan;
2N/A} nac_hc_t;
2N/A
2N/Astatic const nac_hc_t nac_mem_tbl[] = {
2N/A {"branch", "BR%d" },
2N/A {"dram-channel", "CH%d" },
2N/A {"rank", "R%d" },
2N/A {"dimm", "D%d" },
2N/A {"memboard", "MR%d" },
2N/A {"memboard", "MEM%d" },
2N/A {"chip", "CMP%d" }
2N/A};
2N/A
2N/Astatic const char *
2N/Anac2hc(const char *s, int *inst)
2N/A{
2N/A int i;
2N/A
2N/A if (s == NULL)
2N/A return (NULL);
2N/A
2N/A for (i = 0; i < sizeof (nac_mem_tbl) / sizeof (nac_hc_t); i++) {
2N/A if (sscanf(s, nac_mem_tbl[i].nh_sscan, inst) == 1)
2N/A return (nac_mem_tbl[i].nh_name);
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic int
2N/Acreate_one_dimm(topo_mod_t *mod, tnode_t *pnode, int inst, mem_dimm_map_t *dp)
2N/A{
2N/A tnode_t *cnode;
2N/A nvlist_t *rsrc, *fru;
2N/A int nerr = 0, err;
2N/A nvlist_t *auth = NULL;
2N/A
2N/A /*
2N/A * Because mem_tnode_create will fill in a "FRU" value by default,
2N/A * but not an "ASRU" value, we have to compute the desired "FRU"
2N/A * value -before- calling mem_tnode_create, but it's ok to
2N/A * topo_mod_asru_set() the ASRU value after the topo_node is
2N/A * created.
2N/A */
2N/A
2N/A auth = topo_mod_auth(mod, pnode);
2N/A if ((fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, "dimm",
2N/A inst, NULL, auth, dp->dm_part, NULL, dp->dm_serid)) == NULL)
2N/A nerr++;
2N/A nvlist_free(auth);
2N/A
2N/A cnode = mem_tnode_create(mod, pnode, "dimm", inst,
2N/A dp->dm_serid, fru, dp->dm_label, NULL);
2N/A nvlist_free(fru);
2N/A if (cnode == NULL)
2N/A return (++nerr);
2N/A
2N/A rsrc = NULL;
2N/A /* ASRU will be computed by topo method */
2N/A if (topo_node_resource(cnode, &rsrc, &err) < 0 ||
2N/A topo_method_register(mod, cnode, pi_mem_methods) < 0 ||
2N/A topo_node_asru_set(cnode, rsrc, TOPO_ASRU_COMPUTE, &err) < 0)
2N/A nerr++;
2N/A nvlist_free(rsrc);
2N/A
2N/A return (nerr);
2N/A}
2N/A
2N/Aint
2N/Aslashorend(const char *s, int start)
2N/A{
2N/A const char *t = s + start;
2N/A
2N/A if ((t = strchr(t, '/')) == NULL)
2N/A return (strlen(s)); /* end */
2N/A else
2N/A return (t - s); /* next slash */
2N/A}
2N/A
2N/A/*
2N/A * mem_range_create and mem_inst_create are mutually recursive routines which
2N/A * together create the node hierarchy for one dimm and its siblings.
2N/A * mem_range_create is called when creating the first instance of a given node
2N/A * type as child of a parent instance, because it is then, and only then,
2N/A * that a topo range must be created. It calls mem_inst_create for its first
2N/A * and subsequent instances. The recursion always starts with
2N/A * mem_range_create, so it performs the up-front sanity checks.
2N/A *
2N/A * Note: the list of mem_dimm_map_t's pointed at by dp must be sorted
2N/A * alphabetically by *dm_label.
2N/A */
2N/A
2N/Astatic int mem_range_create(topo_mod_t *, tnode_t *, int, mem_dimm_map_t *);
2N/A
2N/Astatic int
2N/Amem_inst_create(topo_mod_t *mod, tnode_t *pnode, int pflen, mem_dimm_map_t *dp)
2N/A{
2N/A int inst, pfnext;
2N/A const char *nodename;
2N/A tnode_t *cnode;
2N/A mem_dimm_map_t *d;
2N/A nvlist_t *fru;
2N/A int nerr = 0;
2N/A
2N/A pfnext = slashorend(dp->dm_label, pflen);
2N/A nodename = nac2hc((dp->dm_label) + pflen, &inst);
2N/A d = dp;
2N/A if (strcmp(nodename, "dimm") == 0) {
2N/A return (create_one_dimm(mod, pnode, inst, dp));
2N/A } else if (*(d->dm_label + pfnext) == '\0') { /* this node has a fru */
2N/A fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
2N/A nodename, inst, NULL, NULL, dp->dm_part, NULL,
2N/A dp->dm_serid);
2N/A cnode = mem_tnode_create(mod, pnode, nodename, inst,
2N/A dp->dm_serid, fru, dp->dm_label, NULL);
2N/A nvlist_free(fru);
2N/A d = dp->dm_next; /* next mem_dimm_map_t could be child */
2N/A } else {
2N/A cnode = mem_tnode_create(mod, pnode, nodename, inst,
2N/A NULL, NULL, NULL, NULL);
2N/A }
2N/A if ((d != NULL) &&
2N/A strncmp(dp->dm_label, d->dm_label, pfnext) == 0)
2N/A nerr += mem_range_create(mod, cnode, pfnext+1, d);
2N/A return (nerr);
2N/A}
2N/A
2N/Aint
2N/Amem_range_create(topo_mod_t *mod, tnode_t *pnode, int pflen,
2N/A mem_dimm_map_t *dp)
2N/A{
2N/A int inst, pfnext;
2N/A const char *nodename;
2N/A mem_dimm_map_t *d;
2N/A int nerr = 0;
2N/A
2N/A if (pnode == NULL)
2N/A return (1); /* definitely an error */
2N/A if (*(dp->dm_label + pflen) == '\0')
2N/A return (1); /* recursed too far */
2N/A
2N/A pfnext = slashorend(dp->dm_label, pflen);
2N/A nodename = nac2hc(dp->dm_label + pflen, &inst);
2N/A
2N/A if (nodename != NULL) {
2N/A if (topo_node_range_create(mod, pnode, nodename, 0,
2N/A MEM_DIMM_MAX) < 0) {
2N/A topo_mod_dprintf(mod, "failed to create "
2N/A "DIMM range %s error %s\n", nodename,
2N/A topo_mod_errmsg(mod));
2N/A return (-1);
2N/A }
2N/A } else {
2N/A /*
2N/A * Skip over NAC elements other than those listed
2N/A * above. These elements will appear
2N/A * in the DIMM's unum, but not in hc: scheme hierarchy.
2N/A */
2N/A
2N/A return (mem_range_create(mod, pnode, pfnext+1, dp));
2N/A }
2N/A
2N/A nerr += mem_inst_create(mod, pnode, pflen, dp);
2N/A
2N/A for (d = dp->dm_next; d != NULL; d = d->dm_next) {
2N/A if (strncmp(dp->dm_label, d->dm_label, pfnext) == 0)
2N/A continue; /* child of 1st instance -- already done */
2N/A else if (strncmp(dp->dm_label, d->dm_label,
2N/A pflen) == 0) { /* child of same parent */
2N/A if (nodename == nac2hc((d->dm_label)+pflen, &inst)) {
2N/A /*
2N/A * Same nodename as sibling. Don't create
2N/A * new range, or the enumeration will die.
2N/A */
2N/A nerr += mem_inst_create(mod, pnode, pflen, d);
2N/A dp = d;
2N/A } else {
2N/A nodename = nac2hc((d->dm_label)+pflen, &inst);
2N/A nerr += mem_range_create(mod, pnode, pflen, d);
2N/A dp = d;
2N/A }
2N/A }
2N/A else
2N/A return (nerr); /* finished all children of my parent */
2N/A }
2N/A return (nerr); /* reached end of mem_dimm_map_t list */
2N/A}
2N/Astatic int
2N/Amem_create(topo_mod_t *mod, tnode_t *rnode, md_mem_info_t *cm)
2N/A{
2N/A int l, nerrs;
2N/A char nodename[10]; /* allows up to 10^6 chips in system */
2N/A char *p;
2N/A mem_dimm_map_t *dp;
2N/A
2N/A if (strcmp(topo_node_name(rnode), "chip") == 0) {
2N/A
2N/A (void) snprintf(nodename, 10, "CMP%d",
2N/A topo_node_instance(rnode));
2N/A
2N/A for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) {
2N/A p = strstr(dp->dm_label, nodename);
2N/A if (p != NULL && (p = strchr(p, '/')) != NULL) {
2N/A l = p - (dp->dm_label) + 1;
2N/A break;
2N/A }
2N/A }
2N/A } else if (strcmp(topo_node_name(rnode), "motherboard") == 0) {
2N/A for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) {
2N/A p = strstr(dp->dm_label, "MB/MEM");
2N/A if (p != NULL) {
2N/A l = 3; /* start with MEM */
2N/A break;
2N/A }
2N/A }
2N/A } else {
2N/A return (1);
2N/A }
2N/A
2N/A if (dp != NULL)
2N/A nerrs = mem_range_create(mod, rnode, l, dp);
2N/A else
2N/A nerrs = 1;
2N/A return (nerrs);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * The hc-scheme memory enumerator is invoked from within a platform
2N/A * toplogy file. Make sure that the invocation is either
2N/A * 1) a child of the chip enumerator, which will cause the argument "rnode"
2N/A * below to be a chip node, and the dimm structures specific for that chip can
2N/A * then be built from its specific node, or
2N/A * 2) a child of the motherboard enumerator -- for Batoka and similar machines
2N/A * with cpu-boards.
2N/A */
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Adimm_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
2N/A topo_instance_t min, topo_instance_t max, void *arg, void *notused)
2N/A{
2N/A md_mem_info_t *mem = (md_mem_info_t *)arg;
2N/A
2N/A if (strcmp(name, DIMM_NODE_NAME) == 0)
2N/A return (mem_create(mod, rnode, mem));
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Adimm_release(topo_mod_t *mp, tnode_t *node)
2N/A{
2N/A}