root/usr/src/cmd/picl/plugins/sun4v/mdesc/cpu_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.
 */

#include "mdescplugin.h"
#include <limits.h>

/* These 3 variable are defined and set in mdescplugin.c */
extern picl_nodehdl_t   root_node;
extern md_t             *mdp;
extern mde_cookie_t     rootnode;

void
set_prop_info(ptree_propinfo_t *propinfo, int size, char *name, int type)
{
        propinfo->version = PICLD_PLUGIN_VERSION_1;
        propinfo->read = NULL;
        propinfo->write = NULL;
        propinfo->piclinfo.type = type;
        propinfo->piclinfo.accessmode = PICL_READ;
        propinfo->piclinfo.size = size;
        (void) strncpy(propinfo->piclinfo.name, name,
            sizeof (propinfo->piclinfo.name));
}

static boolean_t
prop_exists(picl_nodehdl_t node, char *name)
{
        int status;
        picl_prophdl_t proph;

        status = ptree_get_prop_by_name(node, name, &proph);
        if (status == PICL_SUCCESS)
                return (B_TRUE);
        else
                return (B_FALSE);
}

static void
add_md_prop(picl_nodehdl_t node, int size, char *name, void* value, int type)
{
        ptree_propinfo_t propinfo;
        picl_prophdl_t proph;

        if (!prop_exists(node, name)) {
                set_prop_info(&propinfo, size, name, type);

                (void) ptree_create_and_add_prop(node, &propinfo,
                    value, &proph);
        }
}
static void
add_tlb_props(picl_nodehdl_t node, mde_cookie_t *tlblistp, int ntlbs)
{
        int i;
        uint64_t int_value;
        uint8_t *type;
        char str[MAXSTRLEN];
        char property[MAXSTRLEN];
        char tlb_str[MAXSTRLEN];
        int type_size, str_size, total_size, type_flag;

        for (i = 0; i < ntlbs; i++) {
                if (md_get_prop_data(mdp, tlblistp[i], "type", &type,
                    &type_size)) {
                        return;
                }

                total_size = type_flag = 0;

                while (total_size < type_size) {
                        str_size = strlen((char *)type + total_size) + 1;
                        (void) strncpy(str, (char *)type + total_size,
                            sizeof (str));
                        if (strncmp(str, "instn", sizeof (str)) == 0)
                                type_flag |= ICACHE_FLAG;
                        if (strncmp(str, "data", sizeof (str)) == 0)
                                type_flag |= DCACHE_FLAG;
                        total_size += str_size;
                }

                switch (type_flag) {
                case 1:
                        (void) snprintf(tlb_str, sizeof (tlb_str),
                            "itlb");
                        break;
                case 2:
                        (void) snprintf(tlb_str, sizeof (tlb_str),
                            "dtlb");
                        break;
                default:
                        (void) snprintf(tlb_str, sizeof (tlb_str),
                            "Not a known cache type");
                }

                if (!(md_get_prop_val(mdp, tlblistp[i], "entries",
                    &int_value))) {
                        (void) snprintf(property, sizeof (property),
                            "%s-entries", tlb_str);
                        add_md_prop(node, sizeof (int_value), property,
                            &int_value, PICL_PTYPE_INT);
                }
        }
}

static void
add_cache_props(picl_nodehdl_t node, mde_cookie_t *cachelistp, int ncaches)
{
        int i;
        uint64_t int_value;
        uint8_t *type;
        char str[MAXSTRLEN];
        char property[MAXSTRLEN];
        char cache_str[MAXSTRLEN];
        int type_size, str_size, total_size, type_flag;

        for (i = 0; i < ncaches; i++) {
                if (md_get_prop_data(mdp, cachelistp[i], "type", &type,
                    &type_size)) {
                        return;
                }

                if (md_get_prop_val(mdp, cachelistp[i], "level", &int_value)) {
                        return;
                }

                total_size = type_flag = 0;

                while (total_size < type_size) {
                        str_size = strlen((char *)type + total_size) + 1;
                        (void) strncpy(str, (char *)type + total_size,
                            sizeof (str));
                        if (strncmp(str, "instn", sizeof (str)) == 0)
                                type_flag |= ICACHE_FLAG;
                        if (strncmp(str, "data", sizeof (str)) == 0)
                                type_flag |= DCACHE_FLAG;
                        total_size += str_size;
                }

                switch (type_flag) {
                case 1:
                        (void) snprintf(cache_str, sizeof (cache_str),
                            "l%d-icache", (int)int_value);
                        break;
                case 2:
                        (void) snprintf(cache_str, sizeof (cache_str),
                            "l%d-dcache", (int)int_value);
                        break;
                case 3:
                        (void) snprintf(cache_str, sizeof (cache_str),
                            "l%d-cache", (int)int_value);
                        break;
                default:
                        (void) snprintf(cache_str, sizeof (cache_str),
                            "Not a known cache type");
                }

                if (!(md_get_prop_val(mdp, cachelistp[i], "associativity",
                    &int_value))) {
                        (void) snprintf(property, sizeof (property),
                            "%s-associativity", cache_str);
                        add_md_prop(node, sizeof (int_value), property,
                            &int_value, PICL_PTYPE_INT);
                }

                if (!(md_get_prop_val(mdp, cachelistp[i], "size",
                    &int_value))) {
                        (void) snprintf(property, sizeof (property), "%s-size",
                            cache_str);
                        add_md_prop(node, sizeof (int_value), property,
                            &int_value, PICL_PTYPE_INT);
                }

                if (!(md_get_prop_val(mdp, cachelistp[i], "line-size",
                    &int_value))) {
                        (void) snprintf(property, sizeof (property),
                            "%s-line-size", cache_str);
                        add_md_prop(node, sizeof (int_value), property,
                            &int_value, PICL_PTYPE_INT);
                }
        }
}

static void
add_clock_frequency(picl_nodehdl_t pnode, mde_cookie_t mnode)
{
        uint64_t        uint64_value;
        uint32_t        uint32_value;

        if (md_get_prop_val(mdp, mnode, "clock-frequency",
            &uint64_value)) {
                return;
        }
        uint32_value = (uint32_t)(uint64_value & UINT32_MAX);
        add_md_prop(pnode, sizeof (uint32_value), "clock-frequency",
            &uint32_value, PICL_PTYPE_UNSIGNED_INT);
}

/*
 * Return the number of strings in the buffer
 */
static int
get_string_count(char *strdat, int length)
{
        int     count;
        char    *lastnull;
        char    *nullptr;

        count = 1;
        for (lastnull = &strdat[length - 1], nullptr = strchr(strdat, '\0');
            nullptr != lastnull; nullptr = strchr(nullptr + 1, '\0'))
                count++;

        return (count);
}

static void
add_compatible(picl_nodehdl_t pnode, mde_cookie_t mnode)
{
        char    *compat;
        int     len;
        int     count;
        void    add_string_list_prop(picl_nodehdl_t, char *, char *,
            unsigned int);

        if (prop_exists(pnode, "compatible"))
                return;
        if (md_get_prop_data(mdp, mnode, "compatible", (uint8_t **)&compat,
            &len)) {
                return;
        }
        if (compat[0] == '\0' || compat[len - 1] != '\0')
                return;
        count = get_string_count(compat, len);
        if (count == 1) {
                add_md_prop(pnode, len, "compatible", compat,
                    PICL_PTYPE_CHARSTRING);
                return;
        }
        (void) add_string_list_prop(pnode, "compatible", compat, count);
}

static void
add_device_type(picl_nodehdl_t pnode)
{
        char    *device_type = "cpu";

        add_md_prop(pnode, strlen(device_type) + 1, "device_type", device_type,
            PICL_PTYPE_CHARSTRING);
}

int
add_cpu_prop(picl_nodehdl_t node, void *args)
{
        mde_cookie_t *cpulistp;
        mde_cookie_t *cachelistp;
        mde_cookie_t *tlblistp;
        int x, num_nodes;
        int ncpus, ncaches, ntlbs;
        int status;
        int reg_prop[SUN4V_CPU_REGSIZE], cpuid;
        uint64_t int64_value;
        int int_value;

        status = ptree_get_propval_by_name(node, OBP_REG, reg_prop,
            sizeof (reg_prop));
        if (status != PICL_SUCCESS) {
                return (PICL_WALK_TERMINATE);
        }

        cpuid = CFGHDL_TO_CPUID(reg_prop[0]);

        /*
         * Allocate space for our searches.
         */

        num_nodes = md_node_count(mdp);

        cpulistp = (mde_cookie_t *) alloca(sizeof (mde_cookie_t) *num_nodes);
        if (cpulistp == NULL) {
                return (PICL_WALK_TERMINATE);
        }

        cachelistp = (mde_cookie_t *) alloca(sizeof (mde_cookie_t) *num_nodes);
        if (cachelistp == NULL) {
                return (PICL_WALK_TERMINATE);
        }

        tlblistp = (mde_cookie_t *) alloca(sizeof (mde_cookie_t) *num_nodes);
        if (tlblistp == NULL) {
                return (PICL_WALK_TERMINATE);
        }

        /*
         * Starting at the root node, scan the "fwd" dag for
         * all the cpus in this description.
         */

        ncpus = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
            md_find_name(mdp, "fwd"), cpulistp);

        if (ncpus < 0) {
                return (PICL_WALK_TERMINATE);
        }

        /*
         * Create PD cpus with a few select properties
         */

        for (x = 0; x < ncpus; x++) {
                if (md_get_prop_val(mdp, cpulistp[x], "id", &int64_value)) {
                        continue;
                }

                if (int64_value != cpuid)
                        continue;

                int_value = (int)(int64_value & INT32_MAX);

                add_md_prop(node, sizeof (int_value), OBP_PROP_CPUID,
                    &int_value, PICL_PTYPE_INT);

                add_md_prop(node, sizeof (int_value), OBP_PROP_PORTID,
                    &int_value, PICL_PTYPE_INT);

                add_clock_frequency(node, cpulistp[x]);

                add_compatible(node, cpulistp[x]);

                add_device_type(node);

                /* get caches for CPU */
                ncaches = md_scan_dag(mdp, cpulistp[x],
                    md_find_name(mdp, "cache"),
                    md_find_name(mdp, "fwd"),
                    cachelistp);

                add_cache_props(node, cachelistp, ncaches);

                /* get tlbs for CPU */
                ntlbs = md_scan_dag(mdp, cpulistp[x],
                    md_find_name(mdp, "tlb"),
                    md_find_name(mdp, "fwd"),
                    tlblistp);

                add_tlb_props(node, tlblistp, ntlbs);
        }

        return (PICL_WALK_CONTINUE);
}