root/usr/src/lib/fm/topo/modules/i86pc/x86pi/x86pi_bboard.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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Create Base Board (MB) topology node from SMBIOS Type 2 structure
 */

#include <sys/types.h>
#include <strings.h>
#include <fm/topo_mod.h>
#include <fm/topo_hc.h>
#include <sys/systeminfo.h>
#include <sys/smbios_impl.h>
#include <sys/smbios.h>
#include <x86pi_impl.h>

/* base baoed type values to hc-canonical-name */
static const struct x86pi_bb_name {
        int type;
        const char *name;
} x86pi_bb_names[] = {
        { SMB_BBT_SBLADE,       "systemboard" },
        { SMB_BBT_PROC,         "cpuboard" },
        { SMB_BBT_IO,           "ioboard" },
        { SMB_BBT_MEM,          "memboard" },
        { SMB_BBT_DAUGHTER,     "systemboard" },
        { SMB_BBT_MOTHER,       "motherboard" },
        { SMB_BBT_PROCMEM,      "systemboard" },
        { SMB_BBT_PROCIO,       "systemboard" },
        { SMB_BBT_INTER,        "systemboard" },
        { 0x00 }
};

tnode_t *
x86pi_gen_bboard(topo_mod_t *mod, tnode_t *t_parent, int smb_id,
    int instance, int psmb_id)
{
        int             rv;
        smbios_info_t   ip;
        smbios_bboard_t bb;
        smbios_struct_t sp;
        x86pi_hcfmri_t  bb_hcfmri;
        tnode_t         *bb_node;
        const struct x86pi_bb_name *bbnp;
        static int      cpuboard = 0;
        static int      memboard = 0;
        static int      ioboard = 0;
        static int      systemboard = 0;
        static int      motherboard = 0;
        char            *f = "x86pi_gen_bboard";
        smbios_hdl_t    *shp;

        topo_mod_dprintf(mod, "%s\n", f);

        shp = topo_mod_smbios(mod);
        if (shp == NULL) {
                topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
                return (NULL);
        }

        /* SMBIOS Base Board struct */
        rv = smbios_info_bboard(shp, smb_id, &bb);
        if (rv != 0) {
                topo_mod_dprintf(mod, "%s: smbios_info_bboard() failed\n", f);
                return (NULL);
        }
        (void) smbios_lookup_id(shp, psmb_id, &sp);
        if (sp.smbstr_type == SMB_TYPE_CHASSIS &&
            bb.smbb_chassis != psmb_id) {
                topo_mod_dprintf(mod, "%s: base board (%d) does not belong to "
                    "chassis (%d)\n", f, smb_id, psmb_id);
                return (NULL);
        }

        /* SMBIOS Base Board strings */
        rv = smbios_info_common(shp, smb_id, &ip);
        if (rv != 0) {
                return (NULL);
        }

        /*
         * populate string entries
         *
         * We don't set "product" because it may contain characters
         * unacceptable by fmri.   topo_mod_auth() will set the product-id
         * for us and call topo_cleanup_auth_str() when necessary.
         */
        bb_hcfmri.serial_number = x86pi_cleanup_smbios_str(mod,
            ip.smbi_serial, 0);
        bb_hcfmri.version = x86pi_cleanup_smbios_str(mod, ip.smbi_version, 0);
        /* asset tag string contains the part number */
        bb_hcfmri.part_number = x86pi_cleanup_smbios_str(mod,
            ip.smbi_asset, 0);
        bb_hcfmri.location = x86pi_cleanup_smbios_str(mod, ip.smbi_location, 0);

        /* determine the hc-name */
        for (bbnp = x86pi_bb_names; bbnp->type != 0x00; bbnp++) {
                if (bbnp->type == bb.smbb_type) {
                        switch (bbnp->type) {
                                case SMB_BBT_PROC :
                                        instance = cpuboard++;
                                        break;
                                case SMB_BBT_IO :
                                        instance = ioboard++;
                                        break;
                                case SMB_BBT_MEM :
                                        instance = memboard++;
                                        break;
                                case SMB_BBT_MOTHER :
                                        instance = motherboard++;
                                        break;
                                default :
                                        /*
                                         * Enumerate any other baseboard type
                                         * as systemboard.
                                         *
                                         * SMB_BBT_UNKNOWN
                                         * SMB_BBT_OTHER
                                         * SMB_BBT_SBLADE
                                         * SMB_BBT_CSWITCH
                                         * SMB_BBT_SMM
                                         * SMB_BBT_DAUGHTER
                                         * SMB_BBT_PROCMEM
                                         * SMB_BBT_PROCIO
                                         * SMB_BBT_INTER
                                         */
                                        instance = systemboard++;
                                        break;
                        }
                        break;
                }
        }

        bb_hcfmri.instance = instance;
        if (bbnp->type != 0x00)
                bb_hcfmri.hc_name = topo_mod_strdup(mod, bbnp->name);
        else
                bb_hcfmri.hc_name = topo_mod_strdup(mod, "NULL");

        topo_mod_dprintf(mod, "%s: S/N (%s)\n", f, bb_hcfmri.serial_number);
        topo_mod_dprintf(mod, "%s: version/N (%s)\n", f, bb_hcfmri.version);
        topo_mod_dprintf(mod, "%s: Part/N (%s)\n", f, bb_hcfmri.part_number);
        topo_mod_dprintf(mod, "%s: location (%s)\n", f, bb_hcfmri.location);
        topo_mod_dprintf(mod, "%s: instance (%d)\n", f, bb_hcfmri.instance);
        topo_mod_dprintf(mod, "%s: hc_name (%s)\n", f, bb_hcfmri.hc_name);

        rv = x86pi_enum_generic(mod, &bb_hcfmri, t_parent, t_parent, &bb_node,
            X86PI_ENUM_FRU);
        if (rv != 0) {
                topo_mod_dprintf(mod, "%s: failed to create tnode %d\n", f,
                    instance);
                bb_node = NULL;
        }

        /* free up strings */
        if (bb_hcfmri.hc_name != NULL) {
                topo_mod_strfree(mod, (char *)bb_hcfmri.hc_name);
        }
        if (bb_hcfmri.part_number != NULL) {
                topo_mod_strfree(mod, (char *)bb_hcfmri.part_number);
        }
        if (bb_hcfmri.serial_number != NULL) {
                topo_mod_strfree(mod, (char *)bb_hcfmri.serial_number);
        }
        if (bb_hcfmri.version != NULL) {
                topo_mod_strfree(mod, (char *)bb_hcfmri.version);
        }
        if (bb_hcfmri.location != NULL) {
                topo_mod_strfree(mod, (char *)bb_hcfmri.location);
        }

        return (bb_node);
}


int
x86pi_bb_getchips(topo_mod_t *mod, int index, int nboards)
{
        id_t            *cid;
        int             count;
        int             ncmp = 0;
        smbios_struct_t sp;
        smbs_cnt_t      *smbc = NULL;
        smbios_hdl_t *shp;

        shp = topo_mod_smbios(mod);
        if (shp == NULL)
                return (ncmp);

        cid = stypes[SMB_TYPE_BASEBOARD].ids[index].con_ids;
        count = stypes[SMB_TYPE_BASEBOARD].ids[index].con_cnt;

        for (int i = 0; i < count; i++) {
                (void) smbios_lookup_id(shp, cid[i], &sp);
                if (sp.smbstr_type == SMB_TYPE_PROCESSOR) {
                        ncmp++;
                }
        }

        /*
         * If there are missing SMB_TYPE_PROCESSOR structures
         * contained within SMB_TYPE_BASEBOARD, and if the
         * system has only one baseboard we enumerate
         * all processors under it.
         */
        smbc = &stypes[SMB_TYPE_PROCESSOR];
        smbc->type = SMB_TYPE_PROCESSOR;
        x86pi_smb_strcnt(mod, smbc);

        if (nboards == 1) {
                if (ncmp != stypes[SMB_TYPE_PROCESSOR].count)
                        ncmp = stypes[SMB_TYPE_PROCESSOR].count;
        } else {
                if (ncmp == 0) {
                        topo_mod_dprintf(mod, "failed to get processors"
                            " (or) no processors are contained"
                            " within baseboard instance %d, unable to"
                            " enumerate chips\n", index);
                }
        }

        return (ncmp);
}


id_t
x86pi_bb_topparent(topo_mod_t *mod, int index, tnode_t **pnode, id_t *psmbid)
{

        id_t    top_bb_smbid = -1;
        id_t    smb_id;
        int     bb_count, ch_count;
        smbios_struct_t sp;
        smbios_hdl_t *shp;

        shp = topo_mod_smbios(mod);
        if (shp == NULL)
                return (top_bb_smbid);

        smb_id = stypes[SMB_TYPE_BASEBOARD].ids[index].con_by_id;
        (void) smbios_lookup_id(shp, smb_id, &sp);

        if (sp.smbstr_type == SMB_TYPE_CHASSIS) {
                top_bb_smbid = stypes[SMB_TYPE_BASEBOARD].ids[index].id;
                *psmbid = smb_id;
                ch_count = stypes[SMB_TYPE_CHASSIS].count;
                for (int i = 0; i < ch_count; i++)
                        if (stypes[SMB_TYPE_CHASSIS].ids[i].id == *psmbid)
                                *pnode = stypes[SMB_TYPE_CHASSIS].ids[i].node;

                return (top_bb_smbid);

        } else if (sp.smbstr_type == SMB_TYPE_BASEBOARD) {
                bb_count = stypes[SMB_TYPE_BASEBOARD].count;
                for (int i = 0; i < bb_count; i++) {
                        if (stypes[SMB_TYPE_BASEBOARD].ids[i].id == smb_id) {
                                if (stypes[SMB_TYPE_BASEBOARD].ids[i].visited
                                    == X86PI_VISITED) {
                                        top_bb_smbid =
                                            stypes[SMB_TYPE_BASEBOARD].\
                                            ids[index].id;
                                        *pnode =
                                            stypes[SMB_TYPE_BASEBOARD].ids[i].\
                                            node;
                                        *psmbid =
                                            stypes[SMB_TYPE_BASEBOARD].ids[i].\
                                            id;
                                        break;
                                }
                                top_bb_smbid = x86pi_bb_topparent(mod,
                                    i, pnode, psmbid);
                                break;
                        }
                }
        }

        return (top_bb_smbid);
}


id_t
x86pi_bb_chassis(topo_mod_t *mod, id_t bb_smbid)
{
        smbios_bboard_t bb;
        int             rv;
        smbios_hdl_t *shp;

        shp = topo_mod_smbios(mod);
        if (shp == NULL)
                return (-1);

        rv = smbios_info_bboard(shp, bb_smbid, &bb);
        if (rv != 0)
                return (-1);

        return (bb.smbb_chassis);
}


int
x86pi_bb_contains(topo_mod_t *mod)
{
        int             rv;
        id_t            smb_id;
        smbios_bboard_t bb;
        int             bb_count = 0;
        uint_t          cont_cnt = 0;
        smbios_struct_t sp;
        smbios_hdl_t *shp;

        shp = topo_mod_smbios(mod);
        if (shp == NULL)
                return (-1);

        bb_count = stypes[SMB_TYPE_BASEBOARD].count;
        for (int i = 0; i < bb_count; i++) {
                smb_id = stypes[SMB_TYPE_BASEBOARD].ids[i].id;
                /* SMBIOS Base Board struct */
                rv = smbios_info_bboard(shp, smb_id, &bb);
                if (rv != 0)
                        return (-1);
                /* Set Baseboard - Chassis Relationship */
                if (stypes[SMB_TYPE_BASEBOARD].ids[i].con_by_id == 0) {
                        stypes[SMB_TYPE_BASEBOARD].ids[i].con_by_id =
                            rv = x86pi_bb_chassis(mod, smb_id);
                        if (rv == -1) {
                                topo_mod_dprintf(mod, " failed to get"
                                    " the chassis handle\n");
                                return (rv);
                        }
                }

                /* SMBIOS contained object handles */
                cont_cnt = bb.smbb_contn;
                if (cont_cnt > 0) {
                        id_t *cont_hdl;
                        uint16_t hdl;

                        /* allocate space for and get contained handles */
                        cont_hdl = topo_mod_alloc(mod, cont_cnt *
                            sizeof (id_t));
                        rv = smbios_info_contains(shp, smb_id, cont_cnt,
                            cont_hdl);
                        if (rv > SMB_CONT_MAX) {
                                topo_mod_free(mod, cont_hdl, cont_cnt *
                                    sizeof (id_t));
                                return (-1);
                        }
                        cont_cnt = MIN(rv, cont_cnt);

                        /* attach contained handles */
                        stypes[SMB_TYPE_BASEBOARD].ids[i].con_cnt = cont_cnt;
                        for (int j = 0; j < cont_cnt; j++) {
                                hdl = (uint16_t)cont_hdl[j];
                                topo_mod_dprintf(mod, "id %d contained handle"
                                    " %d: %d\n", i, j, hdl);
                                stypes[SMB_TYPE_BASEBOARD].ids[i].\
                                    con_ids[j] = hdl;
                                (void) smbios_lookup_id(shp, hdl, &sp);
                                if (sp.smbstr_type == SMB_TYPE_BASEBOARD) {
                                        for (int k = 0; k < bb_count; k++)
                                                if (stypes[SMB_TYPE_BASEBOARD].\
                                                    ids[k].id == hdl)
                                                        stypes[\
                                                            SMB_TYPE_BASEBOARD\
                                                            ].ids[k].con_by_id =
                                                            smb_id;
                                }
                        }
                        topo_mod_free(mod, cont_hdl, cont_cnt * sizeof (id_t));
                }
        }
        return (0);
}