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/*
2N/A * x86 Generic FMA Topology Enumerator
2N/A */
2N/A
2N/A
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <sys/types.h>
2N/A#include <strings.h>
2N/A#include <sys/fcntl.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <fm/topo_hc.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/smbios.h>
2N/A#include <sys/smbios_impl.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <x86pi_impl.h>
2N/A
2N/A
2N/Astatic int x86pi_enum_start(topo_mod_t *, x86pi_enum_t *);
2N/Astatic int x86pi_enum_gentopo(topo_mod_t *, tnode_t *);
2N/A
2N/A/*
2N/A * Entry point called by libtopo when enumeration is required
2N/A */
2N/Astatic topo_enum_f x86pi_enum; /* libtopo enumeration entry point */
2N/A
2N/A/*
2N/A * Top level chassis node in a multiple chassis system; or the chassis
2N/A * node in a single chassis system.
2N/A */
2N/Astatic tnode_t *motherchassis_node = NULL;
2N/A
2N/A/* base board ID counts */
2N/Abb_cnt_t bb_cnt;
2N/A
2N/A/*
2N/A * Declare the operations vector and information structure used during
2N/A * module registration
2N/A */
2N/Astatic topo_modops_t x86pi_ops =
2N/A { x86pi_enum, NULL };
2N/A
2N/Astatic topo_modinfo_t x86pi_modinfo =
2N/A { X86PI_DESC, X86PI_SCHEME, X86PI_VERSION, &x86pi_ops };
2N/A
2N/A/*
2N/A * Used to pass SMBIOS' FM compatibility to the
2N/A * chip enumerator
2N/A */
2N/Aint x86pi_smbios = 0;
2N/A
2N/A/*
2N/A * Called by libtopo when the topo module is loaded.
2N/A */
2N/Aint
2N/A_topo_init(topo_mod_t *mod, topo_version_t version)
2N/A{
2N/A int result;
2N/A char isa[MAXNAMELEN];
2N/A
2N/A if (getenv("TOPOX86PIDBG") != NULL) {
2N/A /* Debugging is requested for this module */
2N/A topo_mod_setdebug(mod);
2N/A }
2N/A topo_mod_dprintf(mod, "module initializing.\n");
2N/A
2N/A /* see if we're being called to only generate authority */
2N/A topo_mod_dprintf(mod, "version (%d)\n", version);
2N/A if (version == TOPO_VERSION_AUTH_ONLY)
2N/A goto auth;
2N/A
2N/A if (version != TOPO_VERSION) {
2N/A (void) topo_mod_seterrno(mod, EMOD_VER_NEW);
2N/A topo_mod_dprintf(mod, "incompatible topo version %d\n",
2N/A version);
2N/A return (-1);
2N/A }
2N/A
2N/A /* Verify that this is a i86pc architecture machine */
2N/A (void) sysinfo(SI_MACHINE, isa, MAXNAMELEN);
2N/A if (strncmp(isa, "i86pc", MAXNAMELEN) != 0) {
2N/A topo_mod_dprintf(mod, "not i86pc architecture: %s\n", isa);
2N/A return (-1);
2N/A }
2N/A
2N/A result = topo_mod_register(mod, &x86pi_modinfo, TOPO_VERSION);
2N/A if (result < 0) {
2N/A topo_mod_dprintf(mod, "registration failed: %s\n",
2N/A topo_mod_errmsg(mod));
2N/A /* module errno already set */
2N/A return (-1);
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "module ready.\n");
2N/Aauth:
2N/A /* fill in topo identity info (system/component system/chassis) */
2N/A if (x86pi_set_topo_auth(mod) == 0)
2N/A topo_mod_dprintf(mod, "Authority set.\n");
2N/A else
2N/A topo_mod_dprintf(mod, "Failed to set authority.\n");
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Clean up any data used by the module before it is unloaded.
2N/A */
2N/Avoid
2N/A_topo_fini(topo_mod_t *mod)
2N/A{
2N/A topo_mod_dprintf(mod, "module finishing.\n");
2N/A
2N/A /* Unregister from libtopo */
2N/A topo_mod_unregister(mod);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Enumeration entry point for the x86 Generic topology enumerator
2N/A */
2N/A/* ARGSUSED */
2N/Astatic int
2N/Ax86pi_enum(topo_mod_t *mod, tnode_t *t_parent, const char *name,
2N/A topo_instance_t min, topo_instance_t max, void *pi_private, void *data)
2N/A{
2N/A int result;
2N/A hrtime_t starttime;
2N/A x86pi_enum_t x86pi;
2N/A
2N/A /* Begin enumeration */
2N/A starttime = gethrtime();
2N/A topo_mod_dprintf(mod, "enumeration starting.\n");
2N/A
2N/A /* initialize base board counts */
2N/A bb_cnt.cpuboard = 0;
2N/A bb_cnt.ioboard = 0;
2N/A bb_cnt.memboard = 0;
2N/A bb_cnt.motherboard = 0;
2N/A bb_cnt.systemboard = 0;
2N/A
2N/A /*
2N/A * Let's do some enumeration.
2N/A */
2N/A bzero(&x86pi, sizeof (x86pi_enum_t));
2N/A x86pi.t_parent = t_parent;
2N/A result = x86pi_enum_start(mod, &x86pi);
2N/A if (result != 0) {
2N/A topo_mod_dprintf(mod, "Enumeration failed.\n");
2N/A return (-1);
2N/A }
2N/A
2N/A /* Complete enumeration */
2N/A topo_mod_dprintf(mod, "enumeration complete in %lld ms.\n",
2N/A ((gethrtime() - starttime)/MICROSEC));
2N/A
2N/A /* All done */
2N/A return (result);
2N/A}
2N/A
2N/Astatic int
2N/Ax86pi_enum_start(topo_mod_t *mod, x86pi_enum_t *x86pi)
2N/A{
2N/A int rv;
2N/A int complvl = 0;
2N/A smbios_hdl_t *shp;
2N/A topo_mod_t *pcimp = NULL;
2N/A char *f = "x86pi_enum_start";
2N/A
2N/A /*
2N/A * Verify BIOS compliance.
2N/A */
2N/A shp = topo_mod_smbios(mod);
2N/A if (shp == NULL) {
2N/A topo_mod_dprintf(mod, "%s: failed to open SMBIOS\n", f);
2N/A complvl = X86PI_NONE;
2N/A } else {
2N/A complvl = x86pi_check_comp(mod);
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s: SMBIOS x86pi compliance: %s\n", f,
2N/A complvl == X86PI_FULL ? "FULL" : "NONE");
2N/A
2N/A if (complvl == X86PI_NONE) {
2N/A /* fall back to legacy enumeration */
2N/A topo_mod_dprintf(mod,
2N/A "%s: Calling legacy enumeration\n", f);
2N/A
2N/A return (topo_mod_enummap(mod, x86pi->t_parent,
2N/A "i86pc-legacy", FM_FMRI_SCHEME_HC));
2N/A }
2N/A
2N/A x86pi->priv = (void *)shp;
2N/A x86pi_smbios = complvl;
2N/A
2N/A if (x86pi_hbr_enum_init(mod, &pcimp) < 0) {
2N/A topo_mod_dprintf(mod, "%s: x86pi_hbr_enum_init() failed.\n", f);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Create the topology.
2N/A */
2N/A fac_done = 0;
2N/A rv = x86pi_enum_gentopo(mod, x86pi->t_parent);
2N/A
2N/A x86pi_hbr_enum_fini(mod, &pcimp);
2N/A
2N/A if (rv != 0) {
2N/A return (-1);
2N/A }
2N/A x86pi->mod = mod;
2N/A
2N/A if (fac_done == 0) {
2N/A (void) topo_mod_enummap(mod, motherchassis_node, "chassis",
2N/A FM_FMRI_SCHEME_HC);
2N/A (void) topo_mod_enummap(mod, motherchassis_node, "fan",
2N/A FM_FMRI_SCHEME_HC);
2N/A (void) topo_mod_enummap(mod, motherchassis_node, "psu",
2N/A FM_FMRI_SCHEME_HC);
2N/A }
2N/A
2N/A /* All done */
2N/A topo_mod_dprintf(mod, "%s: done.\n", f);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Create the i86pc topology
2N/A *
2N/A * If either Type 2 or Type 3 structures have contained elements/handles,
2N/A * walk them creating the topo.
2N/A *
2N/A * If there are no contained elements/handles, build this topo:
2N/A *
2N/A * Main Chassis
2N/A * Motherboard
2N/A * CMP Chip/Core/Strands
2N/A * Memory Controllers/Memory Devices (DIMMs)
2N/A * PCIE HostBrige
2N/A * PCIE Root Complex
2N/A *
2N/A */
2N/Astatic int
2N/Ax86pi_enum_gentopo(topo_mod_t *mod, tnode_t *t_parent)
2N/A{
2N/A int rv;
2N/A int nch, nbb, ncmp, i;
2N/A int ch_smbid, bb_smbid;
2N/A tnode_t *chassis_node = NULL;
2N/A tnode_t *basebd_node = NULL;
2N/A smbs_cnt_t *smbc;
2N/A tnode_t *pnode = NULL;
2N/A id_t psmbid;
2N/A int notvisited;
2N/A int bb_count, ch_count;
2N/A int min, max;
2N/A int ch_inst = 0;
2N/A topo_instance_t hbri = 0, rci = 0;
2N/A smbios_pciexrc_t hbr;
2N/A char *f = "x86pi_enum_gentopo";
2N/A smbios_hdl_t *shp;
2N/A
2N/A shp = topo_mod_smbios(mod);
2N/A if (shp == NULL) {
2N/A topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
2N/A return (-1);
2N/A }
2N/A
2N/A if (t_parent == NULL) {
2N/A topo_mod_dprintf(mod, "%s: NULL parent\n", f);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * "Chassis'"
2N/A */
2N/A /* Type 3 structs */
2N/A stypes[SMB_TYPE_CHASSIS].type = SMB_TYPE_CHASSIS;
2N/A x86pi_smb_strcnt(mod, &stypes[SMB_TYPE_CHASSIS]);
2N/A
2N/A ch_count = stypes[SMB_TYPE_CHASSIS].count;
2N/A
2N/A for (nch = 0; nch < ch_count; nch++) {
2N/A topo_mod_dprintf(mod, "%s: found %d chassis\n", f,
2N/A stypes[SMB_TYPE_CHASSIS].count);
2N/A
2N/A ch_smbid = stypes[SMB_TYPE_CHASSIS].ids[nch].id;
2N/A
2N/A /*
2N/A * Expect SMBIOS to set the first Chassis Structure to be the
2N/A * parent/mother of all chassis
2N/A */
2N/A if (nch == 0)
2N/A motherchassis_node = chassis_node =
2N/A x86pi_gen_chassis(mod, t_parent, ch_smbid,
2N/A ch_inst++);
2N/A else {
2N/A if (motherchassis_node != NULL)
2N/A chassis_node = x86pi_gen_chassis(mod,
2N/A motherchassis_node, ch_smbid, ch_inst++);
2N/A else
2N/A chassis_node = x86pi_gen_chassis(mod,
2N/A t_parent, ch_smbid, ch_inst++);
2N/A }
2N/A
2N/A if (chassis_node == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: Failed to create chassis %d\n", f, nch);
2N/A continue;
2N/A }
2N/A
2N/A /* enumerate disk bays */
2N/A x86pi_gen_bay(mod, chassis_node, ch_smbid, nch);
2N/A }
2N/A
2N/A /*
2N/A * "Base Board"
2N/A */
2N/A /* Type 2 structs */
2N/A stypes[SMB_TYPE_BASEBOARD].type = SMB_TYPE_BASEBOARD;
2N/A x86pi_smb_strcnt(mod, &stypes[SMB_TYPE_BASEBOARD]);
2N/A bb_count = notvisited = stypes[SMB_TYPE_BASEBOARD].count;
2N/A
2N/A for (nbb = 0; nbb < bb_count; nbb++) {
2N/A stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 0;
2N/A stypes[SMB_TYPE_BASEBOARD].ids[nbb].con_by_id = 0;
2N/A stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = NULL;
2N/A }
2N/A (void) x86pi_bb_contains(mod);
2N/A
2N/A min = 0;
2N/A nbb = 0;
2N/A do {
2N/A /*
2N/A * We have reached end of the array due to the
2N/A * parent-child relationship, without visiting all
2N/A * baseboards! so re-iterate..
2N/A * (or)
2N/A * All baseboards are visited and their contained
2N/A * processors are enumerated
2N/A * (and/or)
2N/A * More baseboards pending a visit
2N/A */
2N/A if (nbb > bb_count && notvisited)
2N/A nbb = 0;
2N/A else if (nbb > bb_count && !notvisited)
2N/A break;
2N/A if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited ==
2N/A X86PI_VISITED) {
2N/A nbb++;
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Get the Top-most Parent Baseboard, irrespective
2N/A * of its index in the array of Type-2s
2N/A * If this Baseboard has no Baseboard parents
2N/A * place it under the chassis that contains it
2N/A */
2N/A bb_smbid = x86pi_bb_topparent(mod, nbb, &pnode, &psmbid);
2N/A if (bb_smbid == -1 || pnode == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "Failed to get BaseBoard node (%d): parent\n",
2N/A nbb);
2N/A return (-1);
2N/A }
2N/A
2N/A if (stypes[SMB_TYPE_BASEBOARD].ids[nbb].id != bb_smbid) {
2N/A for (int i = 0; i < bb_count; i++) {
2N/A if (bb_smbid ==
2N/A stypes[SMB_TYPE_BASEBOARD].ids[i].id) {
2N/A stypes[SMB_TYPE_BASEBOARD].ids[i].\
2N/A visited = 1;
2N/A notvisited--;
2N/A break;
2N/A }
2N/A }
2N/A } else {
2N/A stypes[SMB_TYPE_BASEBOARD].ids[nbb].visited = 1;
2N/A notvisited--;
2N/A }
2N/A
2N/A basebd_node = x86pi_gen_bboard(mod, pnode, bb_smbid,
2N/A nbb, psmbid);
2N/A if (basebd_node == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "Failed to create BaseBoard node (%d)\n", nbb);
2N/A nbb++;
2N/A continue;
2N/A }
2N/A
2N/A stypes[SMB_TYPE_BASEBOARD].ids[nbb].node = basebd_node;
2N/A /*
2N/A * Look for contained handles here and if there are
2N/A * make sure the chip handle below is part of it.
2N/A */
2N/A ncmp = x86pi_bb_getchips(mod, nbb, bb_count);
2N/A if (ncmp > 0) {
2N/A max = min + ncmp - 1;
2N/A /* make sure the chip enum is loaded */
2N/A topo_mod_dprintf(mod, "%s: loading chip enum\n", f);
2N/A
2N/A if (topo_mod_load(mod, CHIP, TOPO_VERSION) == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: Failed to load %s module: %s\n", f,
2N/A CHIP, topo_strerror(topo_mod_errno(mod)));
2N/A } else {
2N/A /* create node range */
2N/A topo_mod_dprintf(mod,
2N/A "%s: chip range %d to %d\n",
2N/A f, min, max);
2N/A rv = topo_node_range_create(mod, basebd_node,
2N/A CHIP, min, max);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: Failed to create node range: "
2N/A "%s\n", f,
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A } else {
2N/A /* call the chip enumerator */
2N/A topo_mod_dprintf(mod, "%s: calling"
2N/A " chip enum\n", f);
2N/A rv =
2N/A topo_mod_enumerate(mod, basebd_node,
2N/A CHIP, CHIP, min, max,
2N/A &x86pi_smbios);
2N/A min = max + 1;
2N/A if (rv != 0)
2N/A topo_mod_dprintf(mod, "%s:%s"
2N/A "enumeration failed: \n",
2N/A f, CHIP);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* enumerate the hostbridge node */
2N/A rv = topo_node_range_create(mod, basebd_node, HOSTBRIDGE,
2N/A 0, 255);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: Failed to create %s range: %s\n",
2N/A f, HOSTBRIDGE, topo_mod_errmsg(mod));
2N/A continue;
2N/A }
2N/A
2N/A smbc = &stypes[SUN_OEM_PCIEXRC];
2N/A smbc->type = SUN_OEM_PCIEXRC;
2N/A x86pi_smb_strcnt(mod, smbc);
2N/A for (i = 0; i < smbc->count; i++) {
2N/A if (smbios_info_pciexrc(shp, smbc->ids[i].id,
2N/A &hbr) != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "smbios_info_pciexrc failed: "
2N/A "id = %d\n", (int)smbc->ids[i].id);
2N/A continue;
2N/A }
2N/A
2N/A if (hbr.smbpcie_bb != bb_smbid)
2N/A continue;
2N/A rv = x86pi_gen_hbr(mod, basebd_node,
2N/A smbc->ids[i].id, hbri, &rci);
2N/A if (rv != 0)
2N/A topo_mod_dprintf(mod,
2N/A "couldn't create hostbridge=%d\n", hbri);
2N/A hbri++;
2N/A }
2N/A nbb++;
2N/A
2N/A } while (notvisited);
2N/A
2N/A return (0);
2N/A}