root/usr/src/cmd/picl/plugins/sun4v/pri/mem_prop_update.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * The PRI plug-in picks up memory configuration data from the PRI
 * and injects this into PICL's /platform tree.  It only populates
 * the logical view of memory: memory, memory-segment, memory-bank.
 * It does not populate the /device tree since there are no memory
 * controller devices on sun4v.
 */

#include "priplugin.h"
#include "../../common/memcfg/piclmemcfg.h"

static void
add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
        uint64_t size);

static void
add_bank_props(picl_nodehdl_t node, mde_cookie_t banklistp,
        md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id);
static uint64_t countbits(uint64_t v);

static void
add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
        md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base);

/*
 * Callback function for picl_walk_tree_by_class().
 * NOTE: picl_walk_tree_by_class() maps the return codes PICL_WALK_CONTINUE
 * and PICL_WALK_TERMINATE to PICL_SUCCESS.
 */
int
add_mem_prop(picl_nodehdl_t node, void *args)
{
        mde_cookie_t *memorylistp, *segmentlistp, *banklistp;
        picl_prophdl_t memh, segmenth, bankh;
        mde_cookie_t *buf, md_rootnode;
        int j, k, num_nodes, interleave, err;
        int nsegments, nbanks, nmemory;
        uint64_t memsize, segsize, segbase;
        uint64_t size, mask;
        md_t *mdp = (md_t *)args;

        if (mdp == NULL)
                return (PICL_WALK_CONTINUE);

        md_rootnode = md_root_node(mdp);

        /*
         * An absence of nodes or failure to obtain memory for searches
         * or absence of the /memory node will cause this to fail.
         * Return PICL_WALK_SUCCESS to allow the plug-in to continue.
         */
        num_nodes = md_node_count(mdp);
        if (num_nodes == 0) {
                pri_debug(LOG_NOTICE, "add_mem_prop: no nodes to walk\n");
                return (PICL_SUCCESS);
        }
        buf = (mde_cookie_t *)malloc(sizeof (mde_cookie_t) * num_nodes * 3);
        if (buf == NULL) {
                pri_debug(LOG_NOTICE, "add_mem_prop: can't allocate memory\n");
                return (PICL_SUCCESS);
        }

        memorylistp = &buf[0];
        segmentlistp = &buf[num_nodes];
        banklistp = &buf[num_nodes * 2];

        if ((ptree_get_node_by_path(MEMORY_PATH, &memh)) != PICL_SUCCESS) {
                pri_debug(LOG_NOTICE,
                    "add_mem_prop: can't find /memory node in platform tree\n");
                free(buf);
                return (PICL_SUCCESS);
        }

        /*
         * There should be only one memory node.
         * If we can't find what we're looking for in the DAG then
         * return PICL_PROPNOTFOUND to get the caller to re-try with
         * a different property name.
         */
        nmemory = md_scan_dag(mdp, md_rootnode, md_find_name(mdp,
            "memory-segments"), md_find_name(mdp, "fwd"), memorylistp);
        if (nmemory != 1) {
                pri_debug(LOG_NOTICE,
                    "add_mem_prop: wrong number of memory dags: expected "
                    "1, got %d\n", nmemory);
                free(buf);
                return (PICL_PROPNOTFOUND);
        }

        nsegments = md_scan_dag(mdp, memorylistp[0],
            md_find_name(mdp, "memory-segment"),
            md_find_name(mdp, "fwd"),
            segmentlistp);

        if (nsegments == 0) {
                pri_debug(LOG_NOTICE, "add_mem_prop: wrong number of memory "
                    "segments: expected >0, got %d\n", nsegments);
                free(buf);
                return (PICL_PROPNOTFOUND);
        }

        /*
         * Add memory segments, keep running total of system memory.
         */
        for (memsize = 0, segsize = 0, j = 0; j < nsegments;
            ++j, memsize += segsize) {
                nbanks = 0;
                err = ptree_create_and_add_node(memh,
                    PICL_NAME_MEMORY_SEGMENT,
                    PICL_CLASS_MEMORY_SEGMENT, &segmenth);
                if (err == PICL_SUCCESS) {
                        size = 0;
                        mask = 0;

                        /*
                         * Need to pull this out here since it's used for
                         * the ID.
                         */
                        if (md_get_prop_val(mdp, segmentlistp[j], "base",
                            &segbase))
                                segbase = 0ULL;

                        /*
                         * Add banks under each segment.
                         */
                        nbanks = md_scan_dag(mdp, segmentlistp[j],
                            md_find_name(mdp, "memory-bank"),
                            md_find_name(mdp, "fwd"),
                            banklistp);

                        if (nbanks <= 0) {
                                pri_debug(LOG_NOTICE, "add_mem_prop: no banks "
                                    "found for segment %d\n", j);
                        } else {
                                for (k = 0; k < nbanks; ++k) {
                                        err =
                                            ptree_create_and_add_node(segmenth,
                                            PICL_NAME_MEMORY_BANK,
                                            PICL_CLASS_MEMORY_BANK, &bankh);
                                        if (err == PICL_SUCCESS) {
                                                /*
                                                 * Add AddressMatch,
                                                 * AddressMask, Size, and
                                                 * ID to each bank.
                                                 */
                                                add_bank_props(bankh,
                                                    banklistp[k],
                                                    mdp,
                                                    &size, &mask,
                                                    (segbase >> 32) * j + k);
                                        }
                                }
                        }
                }

                /*
                 * Add Interleave, BaseAddress, and Size to each segment.
                 */
                interleave = 2 << (countbits(mask & (size - 1)) - 1);
                add_segment_props(segmenth, segmentlistp[j],
                    mdp, interleave, &segsize, segbase);
        }

        /*
         * Add TransferSize and Size (total memory) to this node.
         */
        add_memory_props(memh, memorylistp[0], mdp, memsize);

        free(buf);
        return (PICL_WALK_CONTINUE);
}

static void
add_bank_props(picl_nodehdl_t bankh, mde_cookie_t banklistp,
        md_t *mdp, uint64_t *size, uint64_t *mask, unsigned int id)
{
        uint64_t int_value;
        mde_cookie_t *dimmlistp;
        int node_count, i, type_size, nac_size, status;
        uint8_t *type;
        char *pc, *nac;
        picl_prophdl_t dimmh;

        *size = 0ULL;
        *mask = 0ULL;

        node_count = md_node_count(mdp);
        dimmlistp = (mde_cookie_t *)malloc(node_count * sizeof (mde_cookie_t));
        if (dimmlistp == NULL) {
                pri_debug(LOG_NOTICE,
                    "add_bank_props: can't allocate memory\n");
                return;
        }

        if (!md_get_prop_val(mdp, banklistp, "size", &int_value)) {
                add_md_prop(bankh, sizeof (int_value), PICL_PROP_SIZE,
                    &int_value, PICL_PTYPE_UNSIGNED_INT);
                *size = int_value;
        }
        if (!md_get_prop_val(mdp, banklistp, "mask",
            &int_value)) {
                add_md_prop(bankh, sizeof (int_value),
                    PICL_PROP_ADDRESSMASK,
                    &int_value, PICL_PTYPE_UNSIGNED_INT);
                *mask = int_value;
        }
        if (!md_get_prop_val(mdp, banklistp, "match",
            &int_value)) {
                add_md_prop(bankh, sizeof (int_value),
                    PICL_PROP_ADDRESSMATCH,
                    &int_value, PICL_PTYPE_UNSIGNED_INT);
        }

        add_md_prop(bankh, sizeof (id), PICL_PROP_ID, &id,
            PICL_PTYPE_INT);

        node_count = md_scan_dag(mdp, banklistp, md_find_name(mdp, "component"),
            md_find_name(mdp, "fwd"), dimmlistp);

        for (i = 0; i < node_count; ++i) {
                status = md_get_prop_str(mdp, dimmlistp[i], "type",
                    (char **)&type);
                if (status == -1) {
                        status = md_get_prop_data(mdp, dimmlistp[i],
                            "type", &type, &type_size);
                }
                if (status == -1) /* can't get node type - just skip */
                        continue;
                if (strcmp((const char *)type, "dimm") == 0) {
                        if (md_get_prop_str(mdp, dimmlistp[i], "nac",
                            (char **)&nac) == 0) {
                                nac_size = strlen(nac) + 1;
                                if (ptree_create_and_add_node(bankh,
                                    PICL_NAME_MEMORY_MODULE,
                                    PICL_CLASS_MEMORY_MODULE, &dimmh) ==
                                    PICL_SUCCESS) {
                                        add_md_prop(dimmh, nac_size,
                                            "nac", nac,
                                            PICL_PTYPE_CHARSTRING);
                                        if ((pc = strrchr(nac, '/')) != NULL)
                                                nac = ++pc;
                                        nac_size = strlen(nac) + 1;
                                        add_md_prop(dimmh, nac_size,
                                            PICL_PROP_LABEL, nac,
                                            PICL_PTYPE_CHARSTRING);
                                }
                        }
                }
        }
        free(dimmlistp);
}

static uint64_t
countbits(uint64_t v)
{
        uint64_t c;     /* c accumulates the total bits set in v */

        for (c = 0; v; c++)
                v &= v - 1;     /* clear the least significant bit set */
        return (c);
}

static void
add_segment_props(picl_nodehdl_t node, mde_cookie_t segmentlistp,
    md_t *mdp, uint64_t interleave, uint64_t *size, uint64_t base)
{
        uint64_t int_value;

        *size = 0;
        if (!md_get_prop_val(mdp, segmentlistp, "size", &int_value)) {
                add_md_prop(node, sizeof (int_value),
                    PICL_PROP_SIZE, &int_value,
                    PICL_PTYPE_UNSIGNED_INT);
                *size = int_value;
        }
        add_md_prop(node, sizeof (base), PICL_PROP_BASEADDRESS,
            &base, PICL_PTYPE_UNSIGNED_INT);

        add_md_prop(node, sizeof (interleave), PICL_PROP_INTERLEAVE_FACTOR,
            &interleave, PICL_PTYPE_UNSIGNED_INT);
}

static void
add_memory_props(picl_nodehdl_t node, mde_cookie_t memorylistp, md_t *mdp,
        uint64_t size)
{
        uint64_t int_value;

        /*
         * If the top-level node has a size property then use that,
         * otherwise use the size that was calculated by the caller
         * and passed in.
         */
        if (md_get_prop_val(mdp, memorylistp, "size", &int_value))
                int_value = size;
        add_md_prop(node, sizeof (int_value), PICL_PROP_SIZE, &int_value,
            PICL_PTYPE_UNSIGNED_INT);
        if (!md_get_prop_val(mdp, memorylistp, "transfer_size",
            &int_value)) {
                add_md_prop(node, sizeof (int_value),
                    PICL_PROP_TRANSFER_SIZE,
                    &int_value, PICL_PTYPE_UNSIGNED_INT);
        }
}