Mercurial > illumos > illumos-gate
view usr/src/lib/fm/topo/modules/common/pcibus/pcibus.c @ 9921:0c3d84a756da
6768098 system panics with PCIe fabric.(0x0)(0x43) due to masked errors.
6814026 PLX disable RO algorithm is incorrect
6813298 Legacy PCI Express Endpoint is not used correctly in pcie module
6841301 PCI ECS accesses with pcitool don't work on AMD processors
6813766 faulty EPKT FMA rules need to expect ereports coming from hostbridge
6841816 PCIe Error Handling's scan_fabric doesn't handle failed IO Addresses well
6798264 PCIe error handling doesn't handle zero bdf well
6802636 fault address may not be decoded correctly during PCIe error handling
6843716 suspicious definition of PCIE_REQ_ID_DEV_MASK
6831766 coredump in pci_bridge_declare()
author | Krishna Elango <Krishna.Elango@Sun.COM> |
---|---|
date | Sat, 20 Jun 2009 15:42:11 -0700 |
parents | a52dad0f5e0f |
children | be69f645ce17 |
line wrap: on
line source
/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <sys/fm/protocol.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <alloca.h> #include <sys/param.h> #include <sys/pci.h> #include <sys/pcie.h> #include <libdevinfo.h> #include <libnvpair.h> #include <fm/topo_mod.h> #include <fm/topo_hc.h> #include <hostbridge.h> #include <pcibus.h> #include <did.h> #include <did_props.h> #include <util.h> extern txprop_t Bus_common_props[]; extern txprop_t Dev_common_props[]; extern txprop_t Fn_common_props[]; extern int Bus_propcnt; extern int Dev_propcnt; extern int Fn_propcnt; extern int platform_pci_label(topo_mod_t *mod, tnode_t *, nvlist_t *, nvlist_t **); extern int platform_pci_fru(topo_mod_t *mod, tnode_t *, nvlist_t *, nvlist_t **); static void pci_release(topo_mod_t *, tnode_t *); static int pci_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, topo_instance_t, void *, void *); static int pci_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static int pci_fru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static const topo_modops_t Pci_ops = { pci_enum, pci_release }; static const topo_modinfo_t Pci_info = { PCI_BUS, FM_FMRI_SCHEME_HC, PCI_ENUMR_VERS, &Pci_ops }; static const topo_method_t Pci_methods[] = { { TOPO_METH_LABEL, TOPO_METH_LABEL_DESC, TOPO_METH_LABEL_VERSION, TOPO_STABILITY_INTERNAL, pci_label }, { TOPO_METH_FRU_COMPUTE, TOPO_METH_FRU_COMPUTE_DESC, TOPO_METH_FRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, pci_fru }, { NULL } }; int _topo_init(topo_mod_t *modhdl, topo_version_t version) { /* * Turn on module debugging output */ if (getenv("TOPOPCIDBG") != NULL) topo_mod_setdebug(modhdl); topo_mod_dprintf(modhdl, "initializing pcibus builtin\n"); if (version != PCI_ENUMR_VERS) return (topo_mod_seterrno(modhdl, EMOD_VER_NEW)); topo_mod_register(modhdl, &Pci_info, TOPO_VERSION); topo_mod_dprintf(modhdl, "PCI Enumr initd\n"); return (0); } void _topo_fini(topo_mod_t *modhdl) { topo_mod_unregister(modhdl); } static int pci_label(topo_mod_t *mp, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { if (version > TOPO_METH_LABEL_VERSION) return (topo_mod_seterrno(mp, EMOD_VER_NEW)); return (platform_pci_label(mp, node, in, out)); } static int pci_fru(topo_mod_t *mp, tnode_t *node, topo_version_t version, nvlist_t *in, nvlist_t **out) { if (version > TOPO_METH_FRU_COMPUTE_VERSION) return (topo_mod_seterrno(mp, EMOD_VER_NEW)); return (platform_pci_fru(mp, node, in, out)); } static tnode_t * pci_tnode_create(topo_mod_t *mod, tnode_t *parent, const char *name, topo_instance_t i, void *priv) { tnode_t *ntn; if ((ntn = tnode_create(mod, parent, name, i, priv)) == NULL) return (NULL); if (topo_method_register(mod, ntn, Pci_methods) < 0) { topo_mod_dprintf(mod, "topo_method_register failed: %s\n", topo_strerror(topo_mod_errno(mod))); topo_node_unbind(ntn); return (NULL); } return (ntn); } /*ARGSUSED*/ static int hostbridge_asdevice(topo_mod_t *mod, tnode_t *bus) { di_node_t di; tnode_t *dev32; di = topo_node_getspecific(bus); assert(di != DI_NODE_NIL); if ((dev32 = pcidev_declare(mod, bus, di, 32)) == NULL) return (-1); if (pcifn_declare(mod, dev32, di, 0) == NULL) { topo_node_unbind(dev32); return (-1); } return (0); } tnode_t * pciexfn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn, *ptn; di_node_t pdn; uint_t class, subclass; char *devtyp, *pdevtyp; int pcie_devtyp, pexcap; boolean_t dev_is_pcie, pdev_is_pcie; /* We need the parent's dev info node for some of the info */ ptn = find_predecessor(parent, PCIEX_FUNCTION); /* If this is the first child under root, get root's ptn */ if (ptn == NULL) ptn = find_predecessor(parent, PCIEX_ROOT); if (ptn == NULL) return (NULL); pdn = topo_node_getspecific(ptn); /* Get the required info to populate the excap */ (void) pci_classcode_get(mod, dn, &class, &subclass); devtyp = pci_devtype_get(mod, dn); pdevtyp = pci_devtype_get(mod, pdn); pexcap = pciex_cap_get(mod, pdn); dev_is_pcie = devtyp && (strcmp(devtyp, "pciex") == 0); pdev_is_pcie = pdevtyp && (strcmp(pdevtyp, "pciex") == 0); /* * Populate the excap with correct PCIe device type. * * Device Parent Device Parent Device * excap device-type device-type excap Class Code * ------------------------------------------------------------------- * PCI(default) pci N/A N/A != bridge * PCIe pciex N/A N/A != bridge * Root Port Defined in hostbridge * Switch Up pciex pciex != up = bridge * Switch Down pciex pciex = up = bridge * PCIe-PCI pciex pci N/A = bridge * PCI-PCIe pci pciex N/A = bridge */ pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI_DEV; if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) { if (pdev_is_pcie) { if (dev_is_pcie) { if (pexcap != PCIE_PCIECAP_DEV_TYPE_UP) pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_UP; else pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_DOWN; } else { pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE2PCI; } } else { if (dev_is_pcie) pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCI2PCIE; } } else { if (pdev_is_pcie) pcie_devtyp = PCIE_PCIECAP_DEV_TYPE_PCIE_DEV; } if ((pd = did_find(mod, dn)) == NULL) return (NULL); did_excap_set(pd, pcie_devtyp); if ((ntn = pci_tnode_create(mod, parent, PCIEX_FUNCTION, i, dn)) == NULL) return (NULL); if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We may find pci-express buses or plain-pci buses beneath a function */ if (child_range_add(mod, ntn, PCIEX_BUS, 0, MAX_HB_BUSES) < 0) { topo_node_unbind(ntn); return (NULL); } if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) { topo_node_range_destroy(ntn, PCIEX_BUS); topo_node_unbind(ntn); return (NULL); } return (ntn); } tnode_t * pciexdev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn; if ((pd = did_find(mod, dn)) == NULL) return (NULL); did_settnode(pd, parent); if ((ntn = pci_tnode_create(mod, parent, PCIEX_DEVICE, i, dn)) == NULL) return (NULL); if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We can expect to find pci-express functions beneath the device */ if (child_range_add(mod, ntn, PCIEX_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) { topo_node_unbind(ntn); return (NULL); } return (ntn); } tnode_t * pciexbus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn; if ((pd = did_find(mod, dn)) == NULL) return (NULL); did_settnode(pd, parent); if ((ntn = pci_tnode_create(mod, parent, PCIEX_BUS, i, dn)) == NULL) return (NULL); if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We can expect to find pci-express devices beneath the bus */ if (child_range_add(mod, ntn, PCIEX_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) { topo_node_unbind(ntn); return (NULL); } return (ntn); } tnode_t * pcifn_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn; if ((pd = did_find(mod, dn)) == NULL) return (NULL); did_excap_set(pd, PCIE_PCIECAP_DEV_TYPE_PCI_DEV); if ((ntn = pci_tnode_create(mod, parent, PCI_FUNCTION, i, dn)) == NULL) return (NULL); if (did_props_set(ntn, pd, Fn_common_props, Fn_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We may find pci buses beneath a function */ if (child_range_add(mod, ntn, PCI_BUS, 0, MAX_HB_BUSES) < 0) { topo_node_unbind(ntn); return (NULL); } return (ntn); } tnode_t * pcidev_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn; if ((pd = did_find(mod, dn)) == NULL) return (NULL); /* remember parent tnode */ did_settnode(pd, parent); if ((ntn = pci_tnode_create(mod, parent, PCI_DEVICE, i, dn)) == NULL) return (NULL); if (did_props_set(ntn, pd, Dev_common_props, Dev_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We can expect to find pci functions beneath the device */ if (child_range_add(mod, ntn, PCI_FUNCTION, 0, MAX_PCIDEV_FNS) < 0) { topo_node_unbind(ntn); return (NULL); } return (ntn); } tnode_t * pcibus_declare(topo_mod_t *mod, tnode_t *parent, di_node_t dn, topo_instance_t i) { did_t *pd; tnode_t *ntn; int hbchild = 0; if ((pd = did_find(mod, dn)) == NULL) return (NULL); did_settnode(pd, parent); if ((ntn = pci_tnode_create(mod, parent, PCI_BUS, i, dn)) == NULL) return (NULL); /* * If our devinfo node is lacking certain information of its * own, and our parent topology node is a hostbridge, we may * need/want to inherit information available in the * hostbridge node's private data. */ if (strcmp(topo_node_name(parent), HOSTBRIDGE) == 0) hbchild = 1; if (did_props_set(ntn, pd, Bus_common_props, Bus_propcnt) < 0) { topo_node_unbind(ntn); return (NULL); } /* * We can expect to find pci devices beneath the bus */ if (child_range_add(mod, ntn, PCI_DEVICE, 0, MAX_PCIBUS_DEVS) < 0) { topo_node_unbind(ntn); return (NULL); } /* * On each bus child of the hostbridge, we represent the * hostbridge as a device outside the range of legal device * numbers. */ if (hbchild == 1) { if (hostbridge_asdevice(mod, ntn) < 0) { topo_node_range_destroy(ntn, PCI_DEVICE); topo_node_unbind(ntn); return (NULL); } } return (ntn); } static int pci_bridge_declare(topo_mod_t *mod, tnode_t *fn, di_node_t din, int board, int bridge, int rc, int depth) { int err; char *devtyp; devtyp = pci_devtype_get(mod, din); /* Check if the children are PCI or PCIe */ if (devtyp && (strcmp(devtyp, "pciex") == 0)) err = pci_children_instantiate(mod, fn, din, board, bridge, rc, TRUST_BDF, depth + 1); else err = pci_children_instantiate(mod, fn, din, board, bridge, rc - TO_PCI, TRUST_BDF, depth + 1); return (err); } static void declare_dev_and_fn(topo_mod_t *mod, tnode_t *bus, tnode_t **dev, di_node_t din, int board, int bridge, int rc, int devno, int fnno, int depth) { int dcnt = 0; tnode_t *fn; uint_t class, subclass; uint_t vid, did; did_t *dp = NULL; if (*dev == NULL) { if (rc >= 0) *dev = pciexdev_declare(mod, bus, din, devno); else *dev = pcidev_declare(mod, bus, din, devno); if (*dev == NULL) return; ++dcnt; } if (rc >= 0) fn = pciexfn_declare(mod, *dev, din, fnno); else fn = pcifn_declare(mod, *dev, din, fnno); if (fn == NULL) { if (dcnt) { topo_node_unbind(*dev); *dev = NULL; } return; } if (pci_classcode_get(mod, din, &class, &subclass) < 0) { topo_node_unbind(fn); if (dcnt) topo_node_unbind(*dev); return; } /* * This function may be a bridge. If not, check for a possible * topology map file and kick off its enumeration of lower-level * devices. */ if (class == PCI_CLASS_BRIDGE && subclass == PCI_BRIDGE_PCI) { (void) pci_bridge_declare(mod, fn, din, board, bridge, rc, depth); } /* * Check for a Neptune-based NIC. This could either be a Neptune * adapter card or an Neptune ASIC on a board (e.g. motherboard) * * For Netpune adapter cards, use xfp-hc-topology.xml to expand * topology to include the XFP optical module, which is a FRU on * the Neptune based 10giga fiber NICs. * * For Neptune ASICs, use the XAUI enumerator to expand topology. * The 10giga ports are externalized by a XAUI cards, which * are FRUs. The XAUI enumerator in turn instantiates the XFP * optical module FRUs. */ else if (class == PCI_CLASS_NET && di_uintprop_get(mod, din, DI_VENDIDPROP, &vid) >= 0 && di_uintprop_get(mod, din, DI_DEVIDPROP, &did) >= 0) { if (vid == SUN_VENDOR_ID && did == NEPTUNE_DEVICE_ID) { /* * Is this an adapter card? Check the bus's physlot */ dp = did_find(mod, topo_node_getspecific(bus)); if (did_physlot(dp) >= 0) { topo_mod_dprintf(mod, "Found Neptune slot\n"); (void) topo_mod_enummap(mod, fn, "xfp", FM_FMRI_SCHEME_HC); } else { topo_mod_dprintf(mod, "Found Neptune ASIC\n"); if (topo_mod_load(mod, XAUI, TOPO_VERSION) == NULL) { topo_mod_dprintf(mod, "pcibus enum " "could not load xaui enum\n"); topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); return; } else { if (topo_node_range_create(mod, fn, XAUI, 0, 1) < 0) { topo_mod_dprintf(mod, "child_range_add for " "XAUI failed: %s\n", topo_strerror( topo_mod_errno(mod))); return; } (void) topo_mod_enumerate(mod, fn, XAUI, XAUI, fnno, fnno, fn); } } } } } int pci_children_instantiate(topo_mod_t *mod, tnode_t *parent, di_node_t pn, int board, int bridge, int rc, int bover, int depth) { did_t *pps[MAX_PCIBUS_DEVS][MAX_PCIDEV_FNS]; did_t *bp = NULL; did_t *np; di_node_t sib; di_node_t din; tnode_t *bn = NULL; tnode_t *dn = NULL; int pb = -1; int b, d, f; for (d = 0; d < MAX_PCIBUS_DEVS; d++) for (f = 0; f < MAX_PCIDEV_FNS; f++) pps[d][f] = NULL; /* start at the parent's first sibling */ sib = di_child_node(pn); while (sib != DI_NODE_NIL) { np = did_create(mod, sib, board, bridge, rc, bover); if (np == NULL) return (-1); did_BDF(np, &b, &d, &f); pps[d][f] = np; if (bp == NULL) bp = np; if (pb < 0) pb = ((bover == TRUST_BDF) ? b : bover); sib = di_sibling_node(sib); } if (pb < 0 && bover < 0) return (0); if (rc >= 0) bn = pciexbus_declare(mod, parent, pn, ((pb < 0) ? bover : pb)); else bn = pcibus_declare(mod, parent, pn, ((pb < 0) ? bover : pb)); if (bn == NULL) return (-1); if (pb < 0) return (0); for (d = 0; d < MAX_PCIBUS_DEVS; d++) { for (f = 0; f < MAX_PCIDEV_FNS; f++) { if (pps[d][f] == NULL) continue; din = did_dinode(pps[d][f]); /* * Try to enumerate as many devices and functions as * possible. If we fail to declare a device, break * out of the function loop. */ declare_dev_and_fn(mod, bn, &dn, din, board, bridge, rc, d, f, depth); did_rele(pps[d][f]); if (dn == NULL) break; } dn = NULL; } return (0); } static int pciexbus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min, topo_instance_t max) { di_node_t pdn; int rc; int retval; /* * PCI-Express; root complex shares the hostbridge's instance * number. Parent node's private data is a simple di_node_t * and we have to construct our own did hash and did_t. */ rc = topo_node_instance(ptn); if ((pdn = topo_node_getspecific(ptn)) == DI_NODE_NIL) { topo_mod_dprintf(mp, "Parent %s node missing private data.\n" "Unable to proceed with %s enumeration.\n", pnm, PCIEX_BUS); return (0); } if (did_hash_init(mp) != 0) return (-1); if ((did_create(mp, pdn, 0, 0, rc, TRUST_BDF)) == NULL) return (-1); /* errno already set */ retval = pci_children_instantiate(mp, ptn, pdn, 0, 0, rc, (min == max) ? min : TRUST_BDF, 0); did_hash_fini(mp); return (retval); } static int pcibus_enum(topo_mod_t *mp, tnode_t *ptn, char *pnm, topo_instance_t min, topo_instance_t max, void *data) { did_t *didp, *hbdid = (did_t *)data; int retval; /* * XXTOPO: we should not be sharing private node data with another * module. PCI Bus; Parent node's private data is a did_t. We'll * use the did hash established by the parent. */ did_setspecific(mp, data); /* * If we're looking for a specific bus-instance, find the right * did_t in the chain, otherwise, there should be only one did_t. */ if (min == max) { int b; didp = hbdid; while (didp != NULL) { did_BDF(didp, &b, NULL, NULL); if (b == min) break; didp = did_link_get(didp); } if (didp == NULL) { topo_mod_dprintf(mp, "Parent %s node missing private data related\n" "to %s instance %d.\n", pnm, PCI_BUS, min); topo_mod_setspecific(mp, NULL); return (0); } } else { assert(did_link_get(hbdid) == NULL); didp = hbdid; } retval = pci_children_instantiate(mp, ptn, did_dinode(didp), did_board(didp), did_bridge(didp), did_rc(didp), (min == max) ? min : TRUST_BDF, 0); topo_mod_setspecific(mp, NULL); return (retval); } /*ARGSUSED*/ static int pci_enum(topo_mod_t *mod, tnode_t *ptn, const char *name, topo_instance_t min, topo_instance_t max, void *notused, void *data) { int retval; char *pname; topo_mod_dprintf(mod, "Enumerating pci!\n"); if (strcmp(name, PCI_BUS) != 0 && strcmp(name, PCIEX_BUS) != 0) { topo_mod_dprintf(mod, "Currently only know how to enumerate %s or %s.\n", PCI_BUS, PCIEX_BUS); return (0); } pname = topo_node_name(ptn); if (strcmp(pname, HOSTBRIDGE) != 0 && strcmp(pname, PCIEX_ROOT) != 0) { topo_mod_dprintf(mod, "Currently can only enumerate a %s or %s directly\n", PCI_BUS, PCIEX_BUS); topo_mod_dprintf(mod, "descended from a %s or %s node.\n", HOSTBRIDGE, PCIEX_ROOT); return (0); } if (strcmp(name, PCI_BUS) == 0) { retval = pcibus_enum(mod, ptn, pname, min, max, data); } else if (strcmp(name, PCIEX_BUS) == 0) { retval = pciexbus_enum(mod, ptn, pname, min, max); } else { topo_mod_dprintf(mod, "Currently only know how to enumerate %s or %s not %s.\n", PCI_BUS, PCIEX_BUS, name); return (0); } return (retval); } /*ARGSUSED*/ static void pci_release(topo_mod_t *mp, tnode_t *node) { topo_method_unregister_all(mp, node); /* * node private data (did_t) for this node is destroyed in * did_hash_destroy() */ topo_node_unbind(node); }