root/usr/src/uts/intel/os/fmsmb.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/nvpair.h>
#include <sys/cmn_err.h>
#include <sys/fm/util.h>
#include <sys/fm/protocol.h>
#include <sys/smbios.h>
#include <sys/smbios_impl.h>

/*
 * Variable used to determine if the x86 generic topology enumerator will
 * revert to legacy enumeration. I.E. Big Kill Switch... tunable via
 * /etc/system
 */
int x86gentopo_legacy = 0;

#define MC              0
#define PROC            1
#define MAX_PAIRS       20
#define MAX_CONT        40

typedef struct bbindex  {
        int count;
        uint16_t index[MAX_PAIRS];
} bbindex_t;

/*
 * the enum values come from DMTF
 */
typedef enum baseb {
        BB_BAD = 0,             /* There is no bb value 0 */
        BB_UNKNOWN,             /* Unknown */
        BB_OTHER,               /* Other */
        BB_BLADE,               /* Server Blade */
        BB_CONNSW,              /* Connectivity Switch */
        BB_SMM,                 /* System Management Module */
        BB_PROCMOD,             /* Processor Module */
        BB_IOMOD,               /* I/O Module */
        BB_MEMMOD,              /* Memory Module */
        BB_DBOARD,              /* Daughter Board */
        BB_MBOARD,              /* Motherboard */
        BB_PROCMMOD,            /* Processor/Memory Module */
        BB_PROCIOMOD,           /* Processor/IO Module */
        BB_ICONNBD              /* Interconnect Board */
} bbd_t;

static struct bboard_type {
        bbd_t           baseb;
        const char      *name;
} bbd_type[] = {
        {BB_BAD,                NULL},
        {BB_UNKNOWN,            "unknown"},
        {BB_OTHER,              "other"},
        {BB_BLADE,              "systemboard"},
        {BB_CONNSW,             "connswitch"},
        {BB_SMM,                "smmodule"},
        {BB_PROCMOD,            "cpuboard"},
        {BB_IOMOD,              "ioboard"},
        {BB_MEMMOD,             "memboard"},
        {BB_DBOARD,             "systemboard"},
        {BB_MBOARD,             "motherboard"},
        {BB_PROCMMOD,           "systemboard"},
        {BB_PROCIOMOD,          "systemboard"},
        {BB_ICONNBD,            "systemboard"}
};

typedef struct smbs_con_ids {
        int id;
        int inst;
        int cont_count;
        uint16_t **cont_ids;
        int cont_by_id;
        int visited;
} smbs_con_ids_t;

typedef struct smbs_cnt {
        int type;                       /* SMBIOS stucture type */
        int count;                      /* number of table entries */
        smbs_con_ids_t **ids;           /* SMBIOS table entry id(s) */
} smbs_cnt_t;

/*
 * dynamically allocate the storage for the smbs_cnt_t
 */
static smbs_cnt_t *
smb_create_strcnt(int count)
{
        smbs_cnt_t *types = NULL;
        int i, j;

        types = kmem_zalloc(sizeof (smbs_cnt_t), KM_SLEEP);

        types->ids = (smbs_con_ids_t **)kmem_zalloc(
            count * sizeof (smbs_con_ids_t *), KM_SLEEP);

        for (i = 0; i < count; i++) {
                types->ids[i] = (smbs_con_ids_t *)kmem_zalloc(
                    sizeof (smbs_con_ids_t), KM_SLEEP);
        }

        for (i = 0; i < count; i++) {
                types->ids[i]->cont_ids = (uint16_t **)kmem_zalloc(
                    MAX_CONT * sizeof (uint16_t *), KM_SLEEP);
        }

        for (i = 0; i < count; i++) {
                for (j = 0; j < MAX_CONT; j++) {
                        types->ids[i]->cont_ids[j] = (uint16_t *)kmem_zalloc(
                            sizeof (uint16_t), KM_SLEEP);
                }
        }
        return (types);
}

/*
 * free the smbs_cnt_t memory
 */
static void
smb_free_strcnt(smbs_cnt_t *types, int count)
{
        int i, j;

        if (types == NULL)
                return;

        for (i = 0; i < count; i++) {
                for (j = 0; j < MAX_CONT; j++) {
                        if (types->ids[i]->cont_ids[j] != NULL)
                                kmem_free(types->ids[i]->cont_ids[j],
                                    sizeof (uint16_t));
                }
        }

        for (i = 0; i < count; i++) {
                if (types->ids[i]->cont_ids != NULL)
                        kmem_free(types->ids[i]->cont_ids,
                            MAX_CONT * sizeof (uint16_t *));
        }

        for (i = 0; i < count; i++) {
                if (types->ids[i] != NULL)
                        kmem_free(types->ids[i], sizeof (smbs_con_ids_t));
        }

        if (types->ids != NULL)
                kmem_free(types->ids, count * sizeof (smbs_con_ids_t *));

        if (types != NULL)
                kmem_free(types, sizeof (smbs_cnt_t));

}

/*
 * count number of the structure type in the ksmbios
 */
static int
smb_cnttypes(smbios_hdl_t *shp, int type)
{
        const smb_struct_t *sp = shp->sh_structs;
        int nstructs = shp->sh_nstructs;
        int i;
        int cnt = 0;

        for (i = 0, cnt = 0; i < nstructs; i++, sp++) {
                if (sp->smbst_hdr->smbh_type == type)
                        cnt++;
        }
        return (cnt);
}

static void
smb_strcnt(smbios_hdl_t *shp, smbs_cnt_t *stype)
{
        const smb_struct_t *sp = shp->sh_structs;
        int nstructs = shp->sh_nstructs;
        smbios_bboard_t bb;
        int i, cnt;
        int mb_cnt = 0;
        int cpub_cnt = 0;
        int sysb_cnt = 0;
        int memb_cnt = 0;
        int iob_cnt = 0;
        int inst = 0;
        int rc = 0;

        for (i = 0, cnt = 0; i < nstructs; i++, sp++) {
                if (sp->smbst_hdr->smbh_type == stype->type) {
                        stype->ids[cnt]->id = sp->smbst_hdr->smbh_hdl;
                        stype->ids[cnt]->inst = cnt;
                        stype->ids[cnt]->visited = 0;
                        stype->ids[cnt]->cont_by_id = -1;
                        if (stype->type == SMB_TYPE_BASEBOARD) {
                                rc = smbios_info_bboard(shp,
                                    stype->ids[cnt]->id, &bb);
                                if (rc == 0) {
                                        switch (bb.smbb_type) {
                                                case SMB_BBT_PROC :
                                                        inst = cpub_cnt++;
                                                        break;
                                                case SMB_BBT_IO :
                                                        inst = iob_cnt++;
                                                        break;
                                                case SMB_BBT_MEM :
                                                        inst = memb_cnt++;
                                                        break;
                                                case SMB_BBT_MOTHER :
                                                        inst = mb_cnt++;
                                                        break;
                                                default:
                                                        /*
                                                         * 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
                                                         */
                                                        inst = sysb_cnt++;
                                                        break;
                                        }
                                        stype->ids[cnt]->inst = inst;
                                }
                        }
                        cnt++;
                }
        }
        stype->count = cnt;
}

/*
 * Go through the smbios structures looking for type 2. Fill in
 * the cont_id and cont_by_id for each type 2
 *
 */
static void
smb_bb_contains(smbios_hdl_t *shp, smbs_cnt_t *stype)
{
        int i, j, cnt, c;
        uint_t cont_count;
        const smb_struct_t *spt;
        smbios_bboard_t smb_bb;
        uint16_t bb_id, cont_id;
        uint_t cont_len;
        id_t *cont_hdl = NULL;
        int rc;

        for (cnt = 0; cnt < stype->count; cnt++) {
                bb_id = stype->ids[cnt]->id;
                (void) smbios_info_bboard(shp, stype->ids[cnt]->id, &smb_bb);
                cont_count = (uint_t)smb_bb.smbb_contn;
                if (cont_count == 0) {
                        continue;
                }

                cont_len = sizeof (id_t);
                cont_hdl = kmem_zalloc(cont_count * cont_len, KM_SLEEP);
                if (cont_hdl == NULL)
                        continue;

                rc = smbios_info_contains(shp, stype->ids[cnt]->id,
                    cont_count, cont_hdl);
                if (rc > SMB_CONT_MAX) {
                        kmem_free(cont_hdl, cont_count * cont_len);
                        continue;
                }
                cont_count = MIN(rc, cont_count);

                /*
                 * fill in the type 2 and type 4 ids which are
                 * contained in this type 2
                 */
                c = 0;
                for (j = 0; j < cont_count; j++) {
                        cont_id = (uint16_t)cont_hdl[j];
                        spt = smb_lookup_id(shp, cont_id);
                        if (spt->smbst_hdr->smbh_type == SMB_TYPE_BASEBOARD ||
                            spt->smbst_hdr->smbh_type == SMB_TYPE_PROCESSOR) {
                                *stype->ids[cnt]->cont_ids[c] = cont_id;
                                c++;
                        }

                        if (spt->smbst_hdr->smbh_type == SMB_TYPE_BASEBOARD) {
                                for (i = 0; i < stype->count; i++) {
                                        if (stype->ids[i]->id == cont_id) {
                                                stype->ids[i]->cont_by_id =
                                                    bb_id;
                                        }
                                }
                        }

                }
                stype->ids[cnt]->cont_count = c;
                if (cont_hdl != NULL)
                        kmem_free(cont_hdl, cont_count * cont_len);
        }
}

/*
 * Verify SMBIOS structures for x86 generic topology.
 *
 * Return (0) on success.
 */
static int
fm_smb_check(smbios_hdl_t *shp)
{
        int i, j;
        int bb_cnt = 0;
        int pr_cnt = 0;
        int expr_cnt = 0;
        int ma_cnt = 0;
        int exma_cnt = 0;
        int mdev_cnt = 0;
        int exmdev_cnt = 0;
        uint16_t bb_id;
        uint16_t pr_id, expr_id;
        uint16_t ma_id, exma_id;
        uint16_t mdev_id, exmdev_id;
        uint16_t *sys_ma;
        smbios_bboard_t bb;
        smbios_processor_ext_t exproc;
        smbios_memarray_t ma;
        smbios_memarray_ext_t exma;
        smbios_memdevice_t mdev;
        smbios_memdevice_ext_t exmdev;
        smbs_cnt_t *bb_stype;
        smbs_cnt_t *pr_stype, *expr_stype;
        smbs_cnt_t *ma_stype, *exma_stype;
        smbs_cnt_t *mdev_stype, *exmdev_stype;

        /*
         * Verify the existance of the requuired extended OEM-Specific
         * structures and they coincide with the structures they extend
         * (e.g. the number of extended processor structures equal the
         * number of processor structures).
         */
        pr_cnt = smb_cnttypes(shp, SMB_TYPE_PROCESSOR);
        expr_cnt = smb_cnttypes(shp, SUN_OEM_EXT_PROCESSOR);
        ma_cnt = smb_cnttypes(shp, SMB_TYPE_MEMARRAY);
        exma_cnt = smb_cnttypes(shp, SUN_OEM_EXT_MEMARRAY);
        mdev_cnt = smb_cnttypes(shp, SMB_TYPE_MEMDEVICE);
        exmdev_cnt = smb_cnttypes(shp, SUN_OEM_EXT_MEMDEVICE);
        if (expr_cnt == 0 || exma_cnt == 0 || exmdev_cnt == 0 ||
            expr_cnt != pr_cnt || exma_cnt > ma_cnt ||
            exmdev_cnt > mdev_cnt) {
#ifdef  DEBUG
                cmn_err(CE_NOTE, "!Structure mismatch: ext_proc (%d) "
                    "proc (%d) ext_ma (%d) ma (%d) ext_mdev (%d) mdev (%d)\n",
                    expr_cnt, pr_cnt, exma_cnt, ma_cnt, exmdev_cnt,
                    mdev_cnt);
#endif  /* DEBUG */
                return (-1);
        }

        /*
         * Verify the OEM-Specific structrures are correctly
         * linked to the SMBIOS structure types they extend.
         */

        /* allocate processor stypes */
        pr_stype = smb_create_strcnt(pr_cnt);
        expr_stype = smb_create_strcnt(expr_cnt);

        /* fill in stypes */
        pr_stype->type = SMB_TYPE_PROCESSOR;
        smb_strcnt(shp, pr_stype);
        expr_stype->type = SUN_OEM_EXT_PROCESSOR;
        smb_strcnt(shp, expr_stype);

        /* verify the ext proc struct belong to the proc struct */
        for (i = 0; i < pr_cnt; i++) {
                pr_id = pr_stype->ids[i]->id;
                expr_id = expr_stype->ids[i]->id;
                (void) smbios_info_extprocessor(shp, expr_id, &exproc);
                if (exproc.smbpe_processor != pr_id) {
#ifdef  DEBUG
                        cmn_err(CE_NOTE, "!Processor struct linkage (%d)", i);
#endif  /* DEBUG */
                        smb_free_strcnt(pr_stype, pr_cnt);
                        smb_free_strcnt(expr_stype, expr_cnt);
                        return (-1);
                }
        }

        /* free stypes */
        smb_free_strcnt(pr_stype, pr_cnt);
        smb_free_strcnt(expr_stype, expr_cnt);

        /* allocate memory array stypes */
        ma_stype = smb_create_strcnt(ma_cnt);
        exma_stype = smb_create_strcnt(exma_cnt);
        sys_ma = kmem_zalloc(sizeof (uint16_t) * ma_cnt, KM_SLEEP);

        /* fill in stypes */
        ma_stype->type = SMB_TYPE_MEMARRAY;
        smb_strcnt(shp, ma_stype);
        exma_stype->type = SUN_OEM_EXT_MEMARRAY;
        smb_strcnt(shp, exma_stype);

        /* verify linkage from ext memarray struct to memarray struct */
        for (i = 0; i < ma_cnt; i++) {
                sys_ma[i] = (uint16_t)-1;
                ma_id = ma_stype->ids[i]->id;
                (void) smbios_info_memarray(shp, ma_id, &ma);
                if (ma.smbma_use != SMB_MAU_SYSTEM)
                        continue;
                /* this memarray is system memory */
                sys_ma[i] = ma_id;
                exma_id = exma_stype->ids[i]->id;
                (void) smbios_info_extmemarray(shp, exma_id, &exma);
                if (exma.smbmae_ma != ma_id) {
#ifdef  DEBUG
                        cmn_err(CE_NOTE,
                            "!Memory Array struct linkage (%d)", i);
#endif  /* DEBUG */
                        smb_free_strcnt(ma_stype, ma_cnt);
                        smb_free_strcnt(exma_stype, exma_cnt);
                        kmem_free(sys_ma, sizeof (uint16_t) * ma_cnt);
                        return (-1);
                }
        }

        /* free stypes */
        smb_free_strcnt(ma_stype, ma_cnt);
        smb_free_strcnt(exma_stype, exma_cnt);

        /* allocate memory device stypes */
        mdev_stype = smb_create_strcnt(mdev_cnt);
        exmdev_stype = smb_create_strcnt(exmdev_cnt);

        /* fill in stypes */
        mdev_stype->type = SMB_TYPE_MEMDEVICE;
        smb_strcnt(shp, mdev_stype);
        exmdev_stype->type = SUN_OEM_EXT_MEMDEVICE;
        smb_strcnt(shp, exmdev_stype);

        /* verify linkage */
        for (i = 0; i < mdev_cnt; i++) {
                mdev_id = mdev_stype->ids[i]->id;
                (void) smbios_info_memdevice(shp, mdev_id, &mdev);
                /* only check system memory devices */
                for (j = 0; j < ma_cnt; j++) {
                        if (sys_ma[j] == mdev.smbmd_array)
                                break;
                }
                if (j == ma_cnt)
                        continue;
                exmdev_id = exmdev_stype->ids[i]->id;
                (void) smbios_info_extmemdevice(shp, exmdev_id, &exmdev);
                if (exmdev.smbmdeve_md != mdev_id) {
#ifdef  DEBUG
                        cmn_err(CE_NOTE, "!Memory Device struct linkage (%d)",
                            i);
#endif  /* DEBUG */
                        smb_free_strcnt(mdev_stype, mdev_cnt);
                        smb_free_strcnt(exmdev_stype, exmdev_cnt);
                        kmem_free(sys_ma, sizeof (uint16_t) * ma_cnt);
                        return (-1);
                }
        }

        /* free stypes */
        smb_free_strcnt(mdev_stype, mdev_cnt);
        smb_free_strcnt(exmdev_stype, exmdev_cnt);
        kmem_free(sys_ma, sizeof (uint16_t) * ma_cnt);

        /*
         * Verify the presece of contained handles if there are more
         * than one Type-2 (Base Board) structures.
         */
        bb_cnt = smb_cnttypes(shp, SMB_TYPE_BASEBOARD);
        if (bb_cnt > 1) {
                /* allocate base board stypes */
                bb_stype = smb_create_strcnt(bb_cnt);

                /* fill in stypes */
                bb_stype->type = SMB_TYPE_BASEBOARD;
                smb_strcnt(shp, bb_stype);

                /* verify contained handles */
                for (i = 0; i < bb_cnt; i++) {
                        bb_id = bb_stype->ids[i]->id;
                        (void) smbios_info_bboard(shp, bb_id, &bb);
                        if (bb.smbb_contn == 0) {
#ifdef  DEBUG
                                cmn_err(CE_NOTE, "!No contained hanldes (%d)",
                                    i);
#endif  /* DEBUG */
                                smb_free_strcnt(bb_stype, bb_cnt);
                                return (-1);
                        }
                }

                /* free stypes */
                smb_free_strcnt(bb_stype, bb_cnt);
        }

        return (0);
}

void
fm_smb_fmacompat()
{
        int i, j;
        int id;
        int cnt;
        const char **oem_strings = NULL;
        smbs_cnt_t *oemstypes;
        smbios_hdl_t *shp;
        int strcnt;
        int compat = 0;

        /* check for BKS */
        if (x86gentopo_legacy == 1) {
                return;
        }

        shp = ksmbios;
        if (shp == NULL) {
                goto bad;
        }

        /* OEM strings (Type 11) */
        strcnt = smb_cnttypes(shp, SMB_TYPE_OEMSTR);
        if (strcnt == 0)
                goto bad;

        oemstypes = smb_create_strcnt(strcnt);
        if (oemstypes == NULL)
                goto bad;

        oemstypes->type = SMB_TYPE_OEMSTR;
        smb_strcnt(shp, oemstypes);

        for (i = 0; i < oemstypes->count && compat == 0; i++) {
                id = oemstypes->ids[i]->id;
                cnt = smbios_info_strtab(shp, id, 0, NULL);
                if (cnt > 0) {
                        oem_strings = kmem_zalloc(sizeof (char *) * cnt,
                            KM_SLEEP);
                        (void) smbios_info_strtab(shp, id, cnt, oem_strings);

                        for (j = 0; j < cnt; j++) {
                                if (strncmp(oem_strings[j], SMB_PRMS1,
                                    strlen(SMB_PRMS1) + 1) == 0) {
                                        compat = 1;
                                        break;
                                }
                        }
                        kmem_free(oem_strings, sizeof (char *) * cnt);
                }
        }
        smb_free_strcnt(oemstypes, strcnt);

        /* sanity check SMBIOS structures */
        if ((compat != 0) && (fm_smb_check(shp) == 0))
                return;

bad:
        /* not compatible with x86gentopo; revert to legacy enumeration */
#ifdef  DEBUG
        cmn_err(CE_NOTE,
            "!SMBIOS is not compatible with x86 generic topology.");
        cmn_err(CE_NOTE, "!Invoking legacy x86 topology enumeration.");
#endif  /* DEBUG */
        x86gentopo_legacy = 1;
}

static int
find_matching_apic(smbios_hdl_t *shp, uint16_t proc_id, uint_t strand_apicid)
{
        uint16_t ext_id;
        int i, j;
        smbios_processor_ext_t ep;
        smbs_cnt_t *pstypes;
        int strcnt;

        strcnt = smb_cnttypes(shp, SUN_OEM_EXT_PROCESSOR);
        if (strcnt == 0)
                return (0);

        pstypes = smb_create_strcnt(strcnt);
        if (pstypes == NULL)
                return (0);

        pstypes->type = SUN_OEM_EXT_PROCESSOR;
        smb_strcnt(shp, pstypes);
        for (i = 0; i < pstypes->count; i++) {
                ext_id = pstypes->ids[i]->id;
                (void) smbios_info_extprocessor(shp, ext_id, &ep);
                if (ep.smbpe_processor == proc_id) {
                        for (j = 0; j < ep.smbpe_n; j++) {
                                if (ep.smbpe_apicid[j] == strand_apicid) {
                                        smb_free_strcnt(pstypes, strcnt);
                                        return (1);
                                }
                        }
                }
        }
        smb_free_strcnt(pstypes, strcnt);
        return (0);
}

/*
 * go throught the type 2 structure contained_ids looking for
 * the type 4 which  has strand_apicid == this strand_apicid
 */
static int
find_matching_proc(smbios_hdl_t *shp, uint_t strand_apicid,
    uint16_t bb_id, uint16_t proc_hdl, int is_proc)
{
        int n;
        const smb_struct_t *sp;
        smbios_bboard_t bb;
        uint_t cont_count, cont_len;
        uint16_t cont_id;
        id_t *cont_hdl = NULL;
        int rc;


        (void) smbios_info_bboard(shp, bb_id, &bb);
        cont_count = (uint_t)bb.smbb_contn;
        if (cont_count == 0)
                return (0);

        cont_len = sizeof (id_t);
        cont_hdl = kmem_zalloc(cont_count * cont_len, KM_SLEEP);
        if (cont_hdl == NULL)
                return (0);

        rc = smbios_info_contains(shp, bb_id, cont_count, cont_hdl);
        if (rc > SMB_CONT_MAX) {
                kmem_free(cont_hdl, cont_count * cont_len);
                return (0);
        }
        cont_count = MIN(rc, cont_count);

        for (n = 0; n < cont_count; n++) {
                cont_id = (uint16_t)cont_hdl[n];
                sp = smb_lookup_id(shp, cont_id);
                if (sp->smbst_hdr->smbh_type == SMB_TYPE_PROCESSOR) {
                        if (is_proc) {
                                if (find_matching_apic(shp, cont_id,
                                    strand_apicid)) {
                                        kmem_free(cont_hdl,
                                            cont_count * cont_len);
                                        return (1);
                                }
                        } else {
                                if (cont_id == proc_hdl) {
                                        kmem_free(cont_hdl,
                                            cont_count * cont_len);
                                        return (1);
                                }
                        }
                }
        }
        if (cont_hdl != NULL)
                kmem_free(cont_hdl, cont_count * cont_len);

        return (0);
}

void
get_bboard_index(smbs_cnt_t *bbstypes, uint_t bb_id, bbindex_t *bb_idx)
{
        int curr_id, tmp_id;
        int i, j, nb;
        bbindex_t tmp_idx;

        for (i = 0; i < MAX_PAIRS; i++)
                tmp_idx.index[i] = 0;

        tmp_idx.count = 0;

        curr_id = bb_id;
        for (nb = bbstypes->count-1, i = 0; nb >= 0; nb--) {
                tmp_id = bbstypes->ids[nb]->id;
                if (tmp_id == curr_id) {
                        tmp_idx.index[i] = nb;
                        tmp_idx.count++;
                        curr_id = bbstypes->ids[nb]->cont_by_id;
                        if (curr_id == -1)
                                break;
                        i++;
                }
        }

        for (i = tmp_idx.count - 1, j = 0; i >= 0; i--) {
                bb_idx->index[j] = tmp_idx.index[i];
                j++;
        }

        bb_idx->count = tmp_idx.count;
}

int
get_chassis_inst(smbios_hdl_t *shp, uint16_t *chassis_inst,
    uint16_t bb_id, int *chcnt)
{
        int ch_strcnt;
        smbs_cnt_t *chstypes;
        uint16_t chassis_id, tmp_id;
        smbios_bboard_t bb;
        int rc = 0;
        int i;

        rc = smbios_info_bboard(shp, bb_id, &bb);
        if (rc != 0) {
                return (-1);
        }

        chassis_id = bb.smbb_chassis;

        ch_strcnt = smb_cnttypes(shp, SMB_TYPE_CHASSIS);

        if (ch_strcnt == 0)
                return (-1);

        chstypes = smb_create_strcnt(ch_strcnt);
        if (chstypes == NULL)
                return (-1);

        chstypes->type = SMB_TYPE_CHASSIS;
        smb_strcnt(shp, chstypes);

        for (i = 0; i < chstypes->count; i++) {
                tmp_id = chstypes->ids[i]->id;
                if (tmp_id == chassis_id) {
                        *chassis_inst = chstypes->ids[i]->inst;
                        if (chstypes->ids[i]->inst != 0)
                                *chcnt = 2;
                        else
                                *chcnt = 1;
                        smb_free_strcnt(chstypes, ch_strcnt);
                        return (0);
                }
        }

        smb_free_strcnt(chstypes, ch_strcnt);
        return (-1);
}

int
smb_get_bb_fmri(smbios_hdl_t *shp, nvlist_t *fmri,  uint_t parent,
    smbs_cnt_t *bbstypes)
{
        int rc = 0;
        int i, j, n, cnt;
        int id, index;
        nvlist_t *pairs[MAX_PAIRS];
        smbios_bboard_t bb;
        uint16_t chassis_inst, mch_inst;
        char name[40];
        char idstr[11];
        bbindex_t bb_idx;
        uint16_t bbid;
        int chcnt = 0;

        for (n = 0; n < MAX_PAIRS; n++) {
                bb_idx.index[n] = 0;
                pairs[n] = NULL;
        }
        bb_idx.count = 0;

        get_bboard_index(bbstypes, parent, &bb_idx);

        index = bb_idx.index[0];
        bbid = bbstypes->ids[index]->id;

        rc = get_chassis_inst(shp, &chassis_inst, bbid, &chcnt);

        if (rc != 0) {
                return (rc);
        }

        if ((bb_idx.count + chcnt) > MAX_PAIRS) {
                return (-1);
        }

        i = 0;
        if (chcnt > 1) {
                /*
                 * create main chassis pair
                 */
                pairs[i] = fm_nvlist_create(NULL);
                if (pairs[i] == NULL) {
                        return (-1);
                }
                mch_inst = 0;
                (void) snprintf(idstr, sizeof (idstr), "%u", mch_inst);
                if ((nvlist_add_string(pairs[i], FM_FMRI_HC_NAME,
                    "chassis") != 0) ||
                    (nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr)) != 0) {
                        fm_nvlist_destroy(pairs[i], FM_NVA_FREE);
                        return (-1);
                }
                i++;
        }

        /*
         * create chassis pair
         */
        pairs[i] = fm_nvlist_create(NULL);
        if (pairs[i] == NULL) {
                for (n = 0; n < MAX_PAIRS; n++) {
                        if (pairs[n] != NULL)
                                fm_nvlist_destroy(pairs[n], FM_NVA_FREE);
                }
                return (-1);
        }
        (void) snprintf(idstr, sizeof (idstr), "%u", chassis_inst);
        if ((nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, "chassis") != 0) ||
            (nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr) != 0)) {
                for (n = 0; n < MAX_PAIRS; n++) {
                        if (pairs[n] != NULL)
                                fm_nvlist_destroy(pairs[n], FM_NVA_FREE);
                }
                return (-1);
        }

        for (j = 0, i = chcnt, cnt = chcnt; j < bb_idx.count; j++) {
                index = bb_idx.index[j];
                bbid = bbstypes->ids[index]->id;
                rc =  smbios_info_bboard(shp, bbid, &bb);
                if (rc != 0) {
                        rc = -1;
                        break;
                }

                pairs[i] = fm_nvlist_create(NULL);
                if (pairs[i] == NULL) {
                        rc = -1;
                        break;
                }

                id = bbstypes->ids[index]->inst;
                (void) snprintf(idstr, sizeof (idstr), "%u", id);
                (void) strncpy(name, bbd_type[bb.smbb_type].name,
                    sizeof (name));
                cnt++;

                if (nvlist_add_string(pairs[i], FM_FMRI_HC_NAME, name) != 0 ||
                    nvlist_add_string(pairs[i], FM_FMRI_HC_ID, idstr)
                    != 0) {
                        rc = -1;
                        break;
                }
                i++;
        }

        if (rc != -1) {
                if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST,
                    pairs, cnt) != 0) {
                        rc = -1;
                }
        }

        for (n = 0; n < cnt; n++) {
                if (pairs[n] != NULL)
                        fm_nvlist_destroy(pairs[n], FM_NVA_FREE);
        }

        return (rc);
}

/*
 * pass in strand_apic id
 * return chip's bboards list which has strand_apicid == passed
 * in strand_apic id
 */
static nvlist_t *
smb_bboard(uint_t strand_apicid, uint16_t proc_hdl, int is_proc)
{
        smbios_hdl_t *shp;
        smbs_cnt_t *bbstypes;
        int nb;
        int bb_smbid;
        nvlist_t *fmri = NULL;
        int rc = 0;
        int bb_strcnt;

        if (x86gentopo_legacy)
                return (NULL);

        shp = ksmbios;
        if (shp == NULL) {
                goto bad;
        }

        /*
         * Type 2 structs : "base board"
         */
        bb_strcnt = smb_cnttypes(shp, SMB_TYPE_BASEBOARD);
        if (bb_strcnt == 0) {
                goto bad;
        }

        bbstypes = smb_create_strcnt(bb_strcnt);
        if (bbstypes == NULL)  {
                goto bad;
        }

        bbstypes->type = SMB_TYPE_BASEBOARD;
        smb_strcnt(shp, bbstypes);
        smb_bb_contains(shp, bbstypes);

        for (nb = 0; nb < bbstypes->count; nb++) {
                if (bbstypes->ids[nb]->visited) {
                        continue;
                }

                bbstypes->ids[nb]->visited = 1;
                bb_smbid = bbstypes->ids[nb]->id;

                /*
                 * check if there is a matching  processor under
                 * this board. If found, find base board(s) of this proc
                 * If proc is not in contained handle of a base board and
                 * there is only one base board in the system, treat that base
                 * board as the parent of the proc
                 */
                if (find_matching_proc(shp, strand_apicid,
                    bb_smbid, proc_hdl, is_proc) || (bbstypes->count == 1)) {
                        fmri = fm_nvlist_create(NULL);
                        if (fmri == NULL) {
                                smb_free_strcnt(bbstypes, bb_strcnt);
                                goto bad;
                        }
                        /*
                         * find parent by walking the cont_by_id
                         */
                        rc = smb_get_bb_fmri(shp, fmri, bb_smbid, bbstypes);
                        smb_free_strcnt(bbstypes, bb_strcnt);
                        if (rc == 0) {
                                return (fmri);
                        } else
                                goto bad;
                }

        }

        smb_free_strcnt(bbstypes, bb_strcnt);
bad:
        /* revert to legacy enumeration */
        x86gentopo_legacy = 1;

        return (NULL);
}

nvlist_t *
fm_smb_bboard(uint_t strand_apicid)
{
        return (smb_bboard(strand_apicid, 0, PROC));
}

int
fm_smb_chipinst(uint_t strand_apicid, uint_t *chip_inst, uint16_t *smbiosid)
{
        int n;
        smbios_hdl_t *shp;
        uint16_t proc_id;
        smbs_cnt_t *pstypes;
        int strcnt;

        if (x86gentopo_legacy)
                return (-1);

        shp = ksmbios;
        if (shp == NULL) {
                goto bad;
        }

        strcnt = smb_cnttypes(shp, SMB_TYPE_PROCESSOR);
        if (strcnt == 0)
                goto bad;

        pstypes = smb_create_strcnt(strcnt);
        if (pstypes == NULL)
                goto bad;

        pstypes->type = SMB_TYPE_PROCESSOR;
        smb_strcnt(shp, pstypes);
        for (n = 0; n < pstypes->count; n++) {
                proc_id = pstypes->ids[n]->id;
                if (find_matching_apic(shp, proc_id, strand_apicid)) {
                        *chip_inst = pstypes->ids[n]->inst;
                        *smbiosid = pstypes->ids[n]->id;
                        smb_free_strcnt(pstypes, strcnt);
                        return (0);
                }
        }
        smb_free_strcnt(pstypes, strcnt);
bad:
        /* revert to legacy enumerarion */
        x86gentopo_legacy = 1;

        return (-1);
}

nvlist_t *
fm_smb_mc_bboards(uint_t bdf)
{

        int i;
        smbios_hdl_t *shp;
        uint16_t ext_id;
        smbios_memarray_ext_t em;
        nvlist_t *fmri = NULL;
        smbs_cnt_t *mastypes;
        int strcnt;

        if (x86gentopo_legacy)
                return (NULL);

        shp = ksmbios;
        if (shp == NULL) {
                goto bad;
        }

        strcnt = smb_cnttypes(shp, SUN_OEM_EXT_MEMARRAY);
        if (strcnt == 0)
                goto bad;

        mastypes = smb_create_strcnt(strcnt);
        if (mastypes == NULL)
                goto bad;

        mastypes->type = SUN_OEM_EXT_MEMARRAY;
        smb_strcnt(shp, mastypes);
        for (i = 0; i < mastypes->count; i++) {
                ext_id = mastypes->ids[i]->id;
                (void) smbios_info_extmemarray(shp, ext_id, &em);
                if (em.smbmae_bdf == bdf) {
                        fmri = smb_bboard(0, em.smbmae_comp, MC);
                        smb_free_strcnt(mastypes, strcnt);
                        return (fmri);
                }
        }
        smb_free_strcnt(mastypes, strcnt);
bad:
        /* revert to legacy enumerarion */
        x86gentopo_legacy = 1;

        return (NULL);
}

int
fm_smb_mc_chipinst(uint_t bdf, uint_t *chip_inst)
{

        int i, j;
        smbios_hdl_t *shp;
        smbios_memarray_ext_t em;
        uint16_t ext_id, proc_id;
        smbs_cnt_t *mastypes;
        smbs_cnt_t *pstypes;
        int ma_strcnt, p_strcnt;

        if (x86gentopo_legacy)
                return (-1);

        shp = ksmbios;
        if (shp == NULL) {
                goto bad;
        }

        ma_strcnt = smb_cnttypes(shp, SUN_OEM_EXT_MEMARRAY);
        if (ma_strcnt == 0)
                goto bad;

        mastypes = smb_create_strcnt(ma_strcnt);
        if (mastypes == NULL)
                goto bad;

        mastypes->type = SUN_OEM_EXT_MEMARRAY;
        smb_strcnt(shp, mastypes);
        pstypes = NULL;
        p_strcnt = 0;
        for (i = 0; i < mastypes->count; i++) {
                ext_id = mastypes->ids[i]->id;
                (void) smbios_info_extmemarray(shp, ext_id, &em);
                if (em.smbmae_bdf == bdf) {
                        p_strcnt = smb_cnttypes(shp, SMB_TYPE_PROCESSOR);
                        if (p_strcnt == 0) {
                                smb_free_strcnt(mastypes, ma_strcnt);
                                goto bad;
                        }

                        pstypes = smb_create_strcnt(p_strcnt);
                        if (pstypes == NULL) {
                                smb_free_strcnt(mastypes, ma_strcnt);
                                goto bad;
                        }

                        pstypes->type = SMB_TYPE_PROCESSOR;
                        smb_strcnt(shp, pstypes);
                        for (j = 0; j < pstypes->count; j++) {
                                proc_id = pstypes->ids[j]->id;
                                if (proc_id == em.smbmae_comp) {
                                        *chip_inst = pstypes->ids[j]->inst;
                                        smb_free_strcnt(mastypes, ma_strcnt);
                                        smb_free_strcnt(pstypes, p_strcnt);
                                        return (0);
                                }
                        }
                }
        }
        smb_free_strcnt(mastypes, ma_strcnt);
        smb_free_strcnt(pstypes, p_strcnt);
bad:
        /* revert to legacy enumeration */
        x86gentopo_legacy = 1;

        return (-1);
}