13062Scindi /*
23062Scindi * CDDL HEADER START
33062Scindi *
43062Scindi * The contents of this file are subject to the terms of the
53062Scindi * Common Development and Distribution License (the "License").
63062Scindi * You may not use this file except in compliance with the License.
73062Scindi *
83062Scindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93062Scindi * or http://www.opensolaris.org/os/licensing.
103062Scindi * See the License for the specific language governing permissions
113062Scindi * and limitations under the License.
123062Scindi *
133062Scindi * When distributing Covered Code, include this CDDL HEADER in each
143062Scindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153062Scindi * If applicable, add the following below this CDDL HEADER, with the
163062Scindi * fields enclosed by brackets "[]" replaced with your own identifying
173062Scindi * information: Portions Copyright [yyyy] [name of copyright owner]
183062Scindi *
193062Scindi * CDDL HEADER END
203062Scindi */
213062Scindi
223062Scindi /*
23*10462SSean.Ye@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
243062Scindi * Use is subject to license terms.
253062Scindi */
263062Scindi
273062Scindi #include <string.h>
283062Scindi #include <fm/topo_mod.h>
293062Scindi #include <fm/topo_hc.h>
303062Scindi #include <libdevinfo.h>
313062Scindi #include <limits.h>
323062Scindi #include <sys/fm/protocol.h>
333062Scindi #include <sys/param.h>
343062Scindi #include <sys/systeminfo.h>
353062Scindi #include <assert.h>
363062Scindi
373062Scindi #include <hostbridge.h>
383062Scindi #include <pcibus.h>
393062Scindi #include <did.h>
403062Scindi #include <did_props.h>
413062Scindi #include <util.h>
423062Scindi
433062Scindi /*
443062Scindi * hostbridge.c
453062Scindi * Generic code shared by all the hostbridge enumerators
463062Scindi */
473062Scindi static void hb_release(topo_mod_t *, tnode_t *);
483062Scindi static int hb_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
493062Scindi nvlist_t **);
503062Scindi static int hb_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
513062Scindi topo_instance_t, void *, void *);
523062Scindi
533062Scindi extern int platform_hb_label(topo_mod_t *, tnode_t *, nvlist_t *, nvlist_t **);
543062Scindi extern int platform_hb_enum(topo_mod_t *, tnode_t *,
553062Scindi const char *, topo_instance_t, topo_instance_t);
563062Scindi
573062Scindi extern txprop_t ExHB_common_props[];
583062Scindi extern txprop_t HB_common_props[];
593062Scindi extern txprop_t RC_common_props[];
603062Scindi extern int ExHB_propcnt;
613062Scindi extern int HB_propcnt;
623062Scindi extern int RC_propcnt;
633062Scindi
643062Scindi static int specific_hb_enum(topo_mod_t *, tnode_t *, const char *,
653062Scindi topo_instance_t, topo_instance_t, void *);
663062Scindi
673062Scindi static const topo_modops_t Hb_ops =
683062Scindi { hb_enum, hb_release };
693062Scindi static const topo_modinfo_t Hb_info =
703062Scindi { HOSTBRIDGE, FM_FMRI_SCHEME_HC, HB_ENUMR_VERS, &Hb_ops };
713062Scindi
723062Scindi static const topo_method_t Hb_methods[] = {
733062Scindi { TOPO_METH_LABEL, TOPO_METH_LABEL_DESC,
743062Scindi TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, hb_label },
753062Scindi { NULL }
763062Scindi };
773062Scindi
783062Scindi static const topo_pgroup_info_t hb_auth_pgroup = {
793062Scindi FM_FMRI_AUTHORITY,
803062Scindi TOPO_STABILITY_PRIVATE,
813062Scindi TOPO_STABILITY_PRIVATE,
823062Scindi 1
833062Scindi };
843062Scindi
853062Scindi int
_topo_init(topo_mod_t * modhdl,topo_version_t version)863062Scindi _topo_init(topo_mod_t *modhdl, topo_version_t version)
873062Scindi {
883062Scindi /*
893062Scindi * Turn on module debugging output
903062Scindi */
913062Scindi if (getenv("TOPOHBDBG") != NULL)
923062Scindi topo_mod_setdebug(modhdl);
933062Scindi topo_mod_dprintf(modhdl, "initializing hostbridge enumerator\n");
943062Scindi
953062Scindi if (version != HB_ENUMR_VERS)
963062Scindi return (topo_mod_seterrno(modhdl, EMOD_VER_NEW));
973062Scindi
983062Scindi if (topo_mod_register(modhdl, &Hb_info, TOPO_VERSION) < 0) {
993062Scindi topo_mod_dprintf(modhdl, "hostbridge registration failed: %s\n",
1003062Scindi topo_mod_errmsg(modhdl));
1013062Scindi return (-1); /* mod errno already set */
1023062Scindi }
1033062Scindi
1043062Scindi topo_mod_dprintf(modhdl, "Hostbridge enumr initd\n");
1053062Scindi
1063062Scindi return (0);
1073062Scindi }
1083062Scindi
1093062Scindi void
_topo_fini(topo_mod_t * modhdl)1103062Scindi _topo_fini(topo_mod_t *modhdl)
1113062Scindi {
1123062Scindi topo_mod_unregister(modhdl);
1133062Scindi }
1143062Scindi
1153062Scindi static int
hb_label(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)1163062Scindi hb_label(topo_mod_t *mp, tnode_t *node, topo_version_t version,
1173062Scindi nvlist_t *in, nvlist_t **out)
1183062Scindi {
1193062Scindi if (version > TOPO_METH_LABEL_VERSION)
1203062Scindi return (topo_mod_seterrno(mp, EMOD_VER_NEW));
1213062Scindi return (platform_hb_label(mp, node, in, out));
1223062Scindi }
1233062Scindi
1243062Scindi static topo_mod_t *
pci_enumr_load(topo_mod_t * mp)1253062Scindi pci_enumr_load(topo_mod_t *mp)
1263062Scindi {
1273062Scindi topo_mod_t *rp = NULL;
1283062Scindi
1293062Scindi if ((rp = topo_mod_load(mp, PCI_ENUM, PCI_ENUMR_VERS)) == NULL) {
1303062Scindi topo_mod_dprintf(mp,
1313062Scindi "%s enumerator could not load %s.\n", HOSTBRIDGE, PCI_ENUM);
1323062Scindi }
1333062Scindi return (rp);
1343062Scindi }
1353062Scindi
1363062Scindi /*ARGSUSED*/
1373062Scindi static int
hb_enum(topo_mod_t * mp,tnode_t * pn,const char * name,topo_instance_t imin,topo_instance_t imax,void * notused,void * data)1383062Scindi hb_enum(topo_mod_t *mp, tnode_t *pn, const char *name, topo_instance_t imin,
1393062Scindi topo_instance_t imax, void *notused, void *data)
1403062Scindi {
1414328Scindi int rv;
1423062Scindi topo_mod_t *pcimod;
1433062Scindi
1443062Scindi if (strcmp(name, HOSTBRIDGE) != 0) {
1453062Scindi topo_mod_dprintf(mp,
1463062Scindi "Currently only know how to enumerate %s components.\n",
1473062Scindi HOSTBRIDGE);
1483062Scindi return (0);
1493062Scindi }
1503062Scindi /*
1513062Scindi * Load the pcibus enumerator
1523062Scindi */
1533062Scindi if ((pcimod = pci_enumr_load(mp)) == NULL)
1543062Scindi return (-1);
1553062Scindi
1563062Scindi /*
1573062Scindi * If we're asked to enumerate a whole range of hostbridges, then
1583062Scindi * we need to find them all. If we're just asked to enumerate a
1593062Scindi * single hostbridge, we expect our caller to have passed us linked
1603062Scindi * did_t structures we can use to enumerate the singled out hostbridge.
1613062Scindi */
1623062Scindi if (imin != imax) {
1633062Scindi
1643062Scindi if (did_hash_init(mp) < 0) {
1653062Scindi topo_mod_dprintf(mp,
1663062Scindi "Hash initialization for hostbridge "
1673062Scindi "enumerator failed.\n");
1683062Scindi topo_mod_unload(pcimod);
1693062Scindi return (-1);
1703062Scindi }
1714328Scindi rv = platform_hb_enum(mp, pn, name, imin, imax);
1723062Scindi did_hash_fini(mp);
1733062Scindi } else {
1744328Scindi rv = specific_hb_enum(mp, pn, name, imin, imax, data);
1753062Scindi }
1764328Scindi
1774328Scindi return (rv);
1783062Scindi }
1793062Scindi
1803062Scindi /*ARGSUSED*/
1813062Scindi static void
hb_release(topo_mod_t * mp,tnode_t * node)1823062Scindi hb_release(topo_mod_t *mp, tnode_t *node)
1833062Scindi {
1843062Scindi topo_method_unregister_all(mp, node);
1853062Scindi }
1863062Scindi
1873062Scindi static tnode_t *
hb_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t i,void * priv)1883062Scindi hb_tnode_create(topo_mod_t *mod, tnode_t *parent,
1893062Scindi const char *name, topo_instance_t i, void *priv)
1903062Scindi {
1913062Scindi int err;
1923062Scindi nvlist_t *fmri;
1933062Scindi tnode_t *ntn;
1943062Scindi nvlist_t *auth = topo_mod_auth(mod, parent);
1953062Scindi
1963062Scindi fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
1973062Scindi NULL, auth, NULL, NULL, NULL);
1983062Scindi nvlist_free(auth);
1993062Scindi if (fmri == NULL) {
2003062Scindi topo_mod_dprintf(mod,
2013062Scindi "Unable to make nvlist for %s bind: %s.\n",
2023062Scindi name, topo_mod_errmsg(mod));
2033062Scindi return (NULL);
2043062Scindi }
2053062Scindi
2063062Scindi ntn = topo_node_bind(mod, parent, name, i, fmri);
2073062Scindi if (ntn == NULL) {
2083062Scindi topo_mod_dprintf(mod,
2093062Scindi "topo_node_bind (%s%d/%s%d) failed: %s\n",
2103062Scindi topo_node_name(parent), topo_node_instance(parent),
2113062Scindi name, i,
2123062Scindi topo_strerror(topo_mod_errno(mod)));
2133062Scindi nvlist_free(fmri);
2143062Scindi return (NULL);
2153062Scindi }
2163062Scindi nvlist_free(fmri);
2173062Scindi topo_node_setspecific(ntn, priv);
2183062Scindi
2193062Scindi if (topo_pgroup_create(ntn, &hb_auth_pgroup, &err) == 0) {
2203062Scindi (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2213062Scindi FM_FMRI_AUTH_PRODUCT, &err);
2223062Scindi (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
223*10462SSean.Ye@Sun.COM FM_FMRI_AUTH_PRODUCT_SN, &err);
224*10462SSean.Ye@Sun.COM (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2253062Scindi FM_FMRI_AUTH_CHASSIS, &err);
2263062Scindi (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
2273062Scindi FM_FMRI_AUTH_SERVER, &err);
2283062Scindi }
2293062Scindi
2303062Scindi if (topo_method_register(mod, ntn, Hb_methods) < 0) {
2313062Scindi topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
2323062Scindi topo_strerror(topo_mod_errno(mod)));
2333062Scindi topo_node_unbind(ntn);
2343062Scindi return (NULL);
2353062Scindi }
2363062Scindi return (ntn);
2373062Scindi }
2383062Scindi
2393062Scindi tnode_t *
pcihostbridge_declare(topo_mod_t * mod,tnode_t * parent,di_node_t din,topo_instance_t i)2403062Scindi pcihostbridge_declare(topo_mod_t *mod, tnode_t *parent, di_node_t din,
2413062Scindi topo_instance_t i)
2423062Scindi {
2433062Scindi did_t *pd;
2443062Scindi tnode_t *ntn;
2453062Scindi
2463062Scindi if ((pd = did_find(mod, din)) == NULL)
2473062Scindi return (NULL);
2483062Scindi if ((ntn = hb_tnode_create(mod, parent, HOSTBRIDGE, i, din)) == NULL)
2493062Scindi return (NULL);
2503062Scindi if (did_props_set(ntn, pd, HB_common_props, HB_propcnt) < 0) {
2513062Scindi topo_node_unbind(ntn);
2523062Scindi return (NULL);
2533062Scindi }
2543062Scindi /*
2553062Scindi * We expect to find pci buses beneath the hostbridge.
2563062Scindi */
2573062Scindi if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) {
2583062Scindi topo_node_unbind(ntn);
2593062Scindi return (NULL);
2603062Scindi }
2613062Scindi return (ntn);
2623062Scindi }
2633062Scindi
2643062Scindi tnode_t *
pciexhostbridge_declare(topo_mod_t * mod,tnode_t * parent,di_node_t din,topo_instance_t hi)2653062Scindi pciexhostbridge_declare(topo_mod_t *mod, tnode_t *parent, di_node_t din,
2663062Scindi topo_instance_t hi)
2673062Scindi {
2683062Scindi did_t *pd;
2693062Scindi tnode_t *ntn;
2703062Scindi
2713062Scindi if ((pd = did_find(mod, din)) == NULL)
2723062Scindi return (NULL);
2733062Scindi if ((ntn = hb_tnode_create(mod, parent, HOSTBRIDGE, hi, din)) == NULL)
2743062Scindi return (NULL);
2753062Scindi if (did_props_set(ntn, pd, ExHB_common_props, ExHB_propcnt) < 0) {
2763062Scindi topo_node_unbind(ntn);
2773062Scindi return (NULL);
2783062Scindi }
2793062Scindi /*
2803062Scindi * We expect to find root complexes beneath the hostbridge.
2813062Scindi */
2823062Scindi if (child_range_add(mod, ntn, PCIEX_ROOT, 0, MAX_HB_BUSES) < 0) {
2833062Scindi topo_node_unbind(ntn);
2843062Scindi return (NULL);
2853062Scindi }
2863062Scindi return (ntn);
2873062Scindi }
2883062Scindi
2893062Scindi tnode_t *
pciexrc_declare(topo_mod_t * mod,tnode_t * parent,di_node_t din,topo_instance_t ri)2903062Scindi pciexrc_declare(topo_mod_t *mod, tnode_t *parent, di_node_t din,
2913062Scindi topo_instance_t ri)
2923062Scindi {
2933062Scindi did_t *pd;
2943062Scindi tnode_t *ntn;
2953062Scindi
2963062Scindi if ((pd = did_find(mod, din)) == NULL)
2973062Scindi return (NULL);
2983062Scindi did_markrc(pd);
2993062Scindi if ((ntn = hb_tnode_create(mod, parent, PCIEX_ROOT, ri, din)) == NULL)
3003062Scindi return (NULL);
3013062Scindi if (did_props_set(ntn, pd, RC_common_props, RC_propcnt) < 0) {
3023062Scindi topo_node_unbind(ntn);
3033062Scindi return (NULL);
3043062Scindi }
3053062Scindi /*
3063062Scindi * We expect to find pci-express buses beneath a root complex
3073062Scindi */
3083062Scindi if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) {
3093062Scindi topo_node_range_destroy(ntn, PCIEX_BUS);
3103062Scindi return (NULL);
3113062Scindi }
3123062Scindi return (ntn);
3133062Scindi }
3143062Scindi
3153062Scindi /*ARGSUSED*/
3163062Scindi static int
specific_hb_enum(topo_mod_t * mod,tnode_t * pn,const char * name,topo_instance_t imin,topo_instance_t imax,void * priv)3173062Scindi specific_hb_enum(topo_mod_t *mod, tnode_t *pn, const char *name,
3183062Scindi topo_instance_t imin, topo_instance_t imax, void *priv)
3193062Scindi {
3203062Scindi tnode_t *hb;
3213062Scindi did_t *iodid = (did_t *)priv;
3223062Scindi did_t *didp;
3233062Scindi int brc = 0;
3243062Scindi int bus;
3253062Scindi
3263062Scindi did_setspecific(mod, priv);
3273062Scindi
3283062Scindi /*
3293062Scindi * Find the hostbridge of interest
3303062Scindi */
3313062Scindi didp = iodid;
3323062Scindi for (brc = 0; brc < imin; brc++)
3333062Scindi didp = did_chain_get(didp);
3343062Scindi assert(didp != NULL);
3353062Scindi
3363062Scindi if ((hb = pcihostbridge_declare(mod, pn, did_dinode(didp), imin))
3373062Scindi == NULL) {
3383062Scindi return (-1);
3393062Scindi }
3403062Scindi while (didp != NULL) {
3413062Scindi did_BDF(didp, &bus, NULL, NULL);
3423062Scindi if (topo_mod_enumerate(mod,
3433062Scindi hb, PCI_BUS, PCI_BUS, bus, bus, didp) != 0) {
3443062Scindi return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
3453062Scindi }
3463062Scindi didp = did_link_get(didp);
3473062Scindi }
3483062Scindi
3493062Scindi return (0);
3503062Scindi }
351