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) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <alloca.h>
2N/A#include <assert.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <libnvpair.h>
2N/A#include <string.h>
2N/A#include <sys/fm/protocol.h>
2N/A
2N/A#include <did.h>
2N/A#include <pcibus.h>
2N/A#include <pcibus_labels.h>
2N/A
2N/Aextern slotnm_rewrite_t *Slot_Rewrites;
2N/Aextern physlot_names_t *Physlot_Names;
2N/Aextern missing_names_t *Missing_Names;
2N/A
2N/A/*
2N/A * Do a platform specific label lookup based on physical slot number.
2N/A */
2N/Astatic const char *
2N/Apci_label_physlot_lookup(topo_mod_t *mod, char *platform, did_t *dp)
2N/A{
2N/A const char *rlabel = NULL;
2N/A int n, p, i;
2N/A
2N/A topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n",
2N/A __func__, platform);
2N/A
2N/A if ((n = did_physlot(dp)) < 0 || Physlot_Names == NULL ||
2N/A platform == NULL)
2N/A return (NULL);
2N/A
2N/A topo_mod_dprintf(mod, "%s: doing a lookup for physlot=%d\n",
2N/A __func__, n);
2N/A
2N/A for (p = 0; p < Physlot_Names->psn_nplats; p++) {
2N/A topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
2N/A __func__, Physlot_Names->psn_names[p].pnm_platform);
2N/A if (strcasecmp(Physlot_Names->psn_names[p].pnm_platform,
2N/A platform) != 0)
2N/A continue;
2N/A topo_mod_dprintf(mod, "%s: found lookup table for this "
2N/A "platform\n", __func__);
2N/A for (i = 0; i < Physlot_Names->psn_names[p].pnm_nnames; i++) {
2N/A physnm_t ps;
2N/A ps = Physlot_Names->psn_names[p].pnm_names[i];
2N/A if (ps.ps_num == n) {
2N/A topo_mod_dprintf(mod, "%s: matched entry=%d, "
2N/A "label=%s\n", __func__, i, ps.ps_label);
2N/A rlabel = ps.ps_label;
2N/A break;
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A if (rlabel != NULL) {
2N/A topo_mod_dprintf(mod, "%s: returning label=%s\n",
2N/A __func__, rlabel);
2N/A }
2N/A return (rlabel);
2N/A}
2N/A
2N/A/*
2N/A * Do a platform specific label lookup based on slot name.
2N/A */
2N/Astatic const char *
2N/Apci_label_slotname_lookup(topo_mod_t *mod, char *platform,
2N/A const char *label, did_t *dp)
2N/A{
2N/A const char *rlabel = label;
2N/A int s, i, ret;
2N/A
2N/A if (Slot_Rewrites == NULL || platform == NULL)
2N/A return (rlabel);
2N/A
2N/A topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s\n",
2N/A __func__, platform);
2N/A
2N/A for (s = 0; s < Slot_Rewrites->srw_nplats; s++) {
2N/A topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
2N/A __func__, Slot_Rewrites->srw_platrewrites[s].prw_platform);
2N/A if (strcasecmp(Slot_Rewrites->srw_platrewrites[s].prw_platform,
2N/A platform) != 0)
2N/A continue;
2N/A topo_mod_dprintf(mod, "%s: found lookup table for this "
2N/A "platform\n", __func__);
2N/A for (i = 0;
2N/A i < Slot_Rewrites->srw_platrewrites[s].prw_nrewrites;
2N/A i++) {
2N/A slot_rwd_t rw;
2N/A rw = Slot_Rewrites->srw_platrewrites[s].prw_rewrites[i];
2N/A if (strcmp(rw.srw_obp, label) == 0) {
2N/A topo_mod_dprintf(mod, "%s: matched entry=%d, "
2N/A "old_label=%s, new_label=%s\n",
2N/A __func__, i, rw.srw_obp,
2N/A rw.srw_new ? rw.srw_new : NULL);
2N/A /*
2N/A * If a test function is specified then call
2N/A * it to do an additional check.
2N/A */
2N/A if (rw.srw_test != NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: calling test function=%p\n",
2N/A __func__, rw.srw_test);
2N/A if (ret = rw.srw_test(mod, dp))
2N/A rlabel = rw.srw_new;
2N/A topo_mod_dprintf(mod,
2N/A "%s: test function return=%d\n",
2N/A __func__, ret);
2N/A } else {
2N/A rlabel = rw.srw_new;
2N/A }
2N/A break;
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A topo_mod_dprintf(mod, "%s: returning label=%s\n", __func__,
2N/A rlabel ? rlabel : "NULL");
2N/A return (rlabel);
2N/A}
2N/A
2N/A/*
2N/A * Do a platform specific label lookup based on bus, dev, etc.
2N/A */
2N/Astatic const char *
2N/Apci_label_missing_lookup(topo_mod_t *mod, char *platform, did_t *dp)
2N/A{
2N/A const char *rlabel = NULL;
2N/A int board, bridge, rc, bus, dev;
2N/A int p, i, ret;
2N/A
2N/A if (Missing_Names == NULL || platform == NULL)
2N/A return (NULL);
2N/A
2N/A bridge = did_bridge(dp);
2N/A board = did_board(dp);
2N/A rc = did_rc(dp);
2N/A did_BDF(dp, &bus, &dev, NULL);
2N/A
2N/A topo_mod_dprintf(mod, "%s: doing a lookup for platform=%s, "
2N/A "board=%d, bridge=%d, rc=%d, bus=%d, dev=%d\n",
2N/A __func__, platform, board, bridge, rc, bus, dev);
2N/A
2N/A for (p = 0; p < Missing_Names->mn_nplats; p++) {
2N/A topo_mod_dprintf(mod, "%s: comparing against platform=%s\n",
2N/A __func__, Missing_Names->mn_names[p].pdl_platform);
2N/A if (strcasecmp(Missing_Names->mn_names[p].pdl_platform,
2N/A platform) != 0)
2N/A continue;
2N/A topo_mod_dprintf(mod, "%s: found lookup table for this "
2N/A "platform\n", __func__);
2N/A for (i = 0; i < Missing_Names->mn_names[p].pdl_nnames; i++) {
2N/A devlab_t m;
2N/A m = Missing_Names->mn_names[p].pdl_names[i];
2N/A if ((m.dl_board == -1 || m.dl_board == board) &&
2N/A (m.dl_bridge == -1 || m.dl_bridge == bridge) &&
2N/A (m.dl_rc == -1 || m.dl_rc == rc) &&
2N/A (m.dl_bus == -1 || m.dl_bus == bus) &&
2N/A (m.dl_dev == -1 || m.dl_dev == dev)) {
2N/A topo_mod_dprintf(mod, "%s: matched entry=%d, "
2N/A "label=%s\n", __func__, i, m.dl_label);
2N/A /*
2N/A * If a test function is specified then call
2N/A * it to do an additional test.
2N/A */
2N/A if (m.dl_test != NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: calling test function=%p\n",
2N/A __func__, m.dl_test);
2N/A if (ret = m.dl_test(mod, dp))
2N/A rlabel = m.dl_label;
2N/A topo_mod_dprintf(mod,
2N/A "%s: test function return=%d\n",
2N/A __func__, ret);
2N/A if (ret)
2N/A break;
2N/A } else {
2N/A rlabel = m.dl_label;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A if (rlabel != NULL) {
2N/A topo_mod_dprintf(mod, "%s: match found, label=%s\n",
2N/A __func__, rlabel);
2N/A }
2N/A return (rlabel);
2N/A}
2N/A
2N/A/*
2N/A * Do an overall slot label lookup for the device node.
2N/A */
2N/Achar *
2N/Apci_slot_label_lookup(topo_mod_t *mod, tnode_t *node, did_t *dp, did_t *pdp)
2N/A{
2N/A tnode_t *anode, *apnode;
2N/A did_t *adp, *apdp;
2N/A char *plat, *pp, *l, *ancestor_l = NULL, *new_l = NULL;
2N/A int err, b, d, f, done = 0, label_override;
2N/A size_t len;
2N/A
2N/A did_BDF(dp, &b, &d, &f);
2N/A
2N/A topo_mod_dprintf(mod, "%s: entry: node=%p, node_name=%s, "
2N/A "node_inst=%d, dp=%p, dp_bdf=%d/%d/%d, pdp=%p\n",
2N/A __func__, node, topo_node_name(node), topo_node_instance(node),
2N/A dp, b, d, f, pdp);
2N/A
2N/A /*
2N/A * The slot label is determined in the following order:
2N/A * - Platform specific lookup based on physical slot #.
2N/A * - Platform specific lookup based on default label string.
2N/A * - Platform specific lookup based on device number (Note that this
2N/A * can override the default label by pci_label_slotname_lookup
2N/A * returning null, and the pci_label_missing_lookup setting a new
2N/A * value).
2N/A * - Default label.
2N/A * If no platform known, then just use default label.
2N/A */
2N/A if (topo_prop_get_string(node, FM_FMRI_AUTHORITY,
2N/A FM_FMRI_AUTH_V1_CHASSIS_NM, &plat, &err) == 0) {
2N/A /*
2N/A * Trim SUNW, from the platform name
2N/A */
2N/A pp = strchr(plat, ',');
2N/A if (pp == NULL)
2N/A pp = plat;
2N/A else
2N/A ++pp;
2N/A
2N/A if ((l = (char *)pci_label_physlot_lookup(mod, pp, pdp))
2N/A != NULL) {
2N/A label_override = 1;
2N/A } else {
2N/A if ((l = (char *)did_physlot_name(pdp, d)) != NULL) {
2N/A if ((new_l = (char *)pci_label_slotname_lookup(
2N/A mod, pp, l, dp)) != NULL &&
2N/A strcmp(l, new_l) != 0) {
2N/A label_override = 1;
2N/A l = new_l;
2N/A }
2N/A }
2N/A if (l == NULL) {
2N/A if ((l = (char *)pci_label_missing_lookup(mod,
2N/A pp, dp)) != NULL)
2N/A label_override = 1;
2N/A }
2N/A }
2N/A topo_mod_strfree(mod, plat);
2N/A } else {
2N/A l = (char *)did_physlot_name(pdp, d);
2N/A }
2N/A if (l == NULL) {
2N/A topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n",
2N/A __func__, node, "NULL");
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * If we haven't overridden the label, then we may need to
2N/A * concatenate with that of an ancestor node. Check ancestors for a
2N/A * slot label until we either find one or hit a non-pci device.
2N/A * If an ancestor has a slot label, then this node's label
2N/A * is generated by concatenating the default label onto the
2N/A * ancestor's label.
2N/A *
2N/A * We grab pairs of ancestors (parent and child) as we go up
2N/A * the tree because the parent is checked for the presence
2N/A * of a slot while the child contains the label.
2N/A */
2N/A if (!label_override) {
2N/A
2N/A topo_mod_dprintf(mod, "%s: node=%p: node has a slot label "
2N/A "%s, checking ancestors for slot labels\n", __func__, node,
2N/A l);
2N/A
2N/A anode = topo_node_parent(node);
2N/A
2N/A /*
2N/A * Check ancestors for a slot label until we
2N/A * either find one or hit a non-pci device.
2N/A */
2N/A while (!done) {
2N/A
2N/A /*
2N/A * Get next ancestor node and data pointers.
2N/A */
2N/A anode = topo_node_parent(anode);
2N/A if (anode != NULL) {
2N/A adp = did_find(mod,
2N/A topo_node_getspecific(anode));
2N/A apnode = topo_node_parent(anode);
2N/A if (apnode != NULL)
2N/A apdp = did_find(mod,
2N/A topo_node_getspecific(apnode));
2N/A else
2N/A apdp = NULL;
2N/A } else {
2N/A apnode = NULL;
2N/A apdp = adp = NULL;
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s: node=%p: checking next "
2N/A "two ancestors: anode=%p, adp=%p "
2N/A "apnode=%p, apdp=%p\n",
2N/A __func__, node, anode, adp, apnode, apdp);
2N/A if ((anode != NULL) && (adp != NULL)) {
2N/A did_BDF(adp, &b, &d, &f);
2N/A topo_mod_dprintf(mod, "%s: node=%p: "
2N/A "anode_name=%s[%d], anode_bdf=%d/%d/%d\n",
2N/A __func__, node, topo_node_name(anode),
2N/A topo_node_instance(anode), b, d, f);
2N/A }
2N/A if ((apnode != NULL) && (apdp != NULL)) {
2N/A did_BDF(apdp, &b, &d, &f);
2N/A topo_mod_dprintf(mod, "%s: node=%p: "
2N/A "apnode_name=%s[%d], "
2N/A "apnode_bdf=%d/%d/%d\n",
2N/A __func__, node, topo_node_name(apnode),
2N/A topo_node_instance(apnode), b, d, f);
2N/A }
2N/A
2N/A /*
2N/A * If the ancestors do not exist or are not pci
2N/A * devices then we're done searching.
2N/A *
2N/A * Otherwise, if the ancestor has a physical slot,
2N/A * and it is a different slot than the one we
2N/A * started with then lookup the ancestor label,
2N/A * and we're done.
2N/A */
2N/A if ((anode == NULL) || (adp == NULL) ||
2N/A (apnode == NULL) || (apdp == NULL)) {
2N/A done++;
2N/A } else if (did_physlot_exists(apdp) &&
2N/A (apdp != pdp)) {
2N/A if (topo_node_label(anode, &ancestor_l,
2N/A &err) != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: node=%p: topo_node_label() "
2N/A "FAILED!", __func__, node);
2N/A (void) topo_mod_seterrno(mod, err);
2N/A return (NULL);
2N/A }
2N/A done++;
2N/A topo_mod_dprintf(mod, "%s: node=%p: found "
2N/A "ancestor with a slot, label=%s ",
2N/A __func__, node, ancestor_l);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If we found an ancestor with a slot label, and this node has
2N/A * a physical slot number label then concatenate the two to form
2N/A * this node's label. Otherwise, do a full slot label lookup.
2N/A */
2N/A if (ancestor_l) {
2N/A topo_mod_dprintf(mod, "%s: node=%p: concatenating "
2N/A "ancestor_l=%s and l=%s\n",
2N/A __func__, node, ancestor_l, l);
2N/A len = strlen(ancestor_l) + strlen(l) + 2;
2N/A new_l = alloca(len);
2N/A (void) snprintf(new_l, len, "%s/%s", ancestor_l, l);
2N/A l = new_l;
2N/A } else {
2N/A topo_mod_dprintf(mod, "%s: node=%p: no ancestor "
2N/A "slot found\n", __func__, node);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If we calculated a slot label, then save it in the
2N/A * node's data structure so we can free it later.
2N/A */
2N/A if (did_slot_label_get(dp) != NULL)
2N/A topo_mod_strfree(mod, did_slot_label_get(dp));
2N/A l = topo_mod_strdup(mod, l);
2N/A did_slot_label_set(dp, l);
2N/A topo_mod_dprintf(mod, "%s: exit: node=%p: label=%s\n",
2N/A __func__, node, l);
2N/A
2N/A return (l);
2N/A}
2N/A
2N/Aint
2N/Apci_label_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
2N/A{
2N/A uint64_t ptr;
2N/A char *l;
2N/A did_t *dp, *pdp;
2N/A tnode_t *pnode;
2N/A char *nm;
2N/A int err;
2N/A
2N/A /*
2N/A * If it's not a device or a PCI-express bus (which could potentially
2N/A * represent a slot, and therefore we might need to capture its slot
2N/A * name information), just inherit any label from our parent
2N/A */
2N/A *out = NULL;
2N/A nm = topo_node_name(node);
2N/A if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
2N/A strcmp(nm, PCIEX_BUS) != 0) {
2N/A if (topo_node_label_set(node, NULL, &err) < 0)
2N/A if (err != ETOPO_PROP_NOENT)
2N/A return (topo_mod_seterrno(mod, err));
2N/A return (0);
2N/A }
2N/A
2N/A if (nvlist_lookup_uint64(in, TOPO_METH_LABEL_ARG_NVL, &ptr) != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: label method argument not found.\n", __func__);
2N/A return (-1);
2N/A }
2N/A dp = (did_t *)(uintptr_t)ptr;
2N/A pnode = did_gettnode(dp);
2N/A pdp = did_find(mod, topo_node_getspecific(pnode));
2N/A
2N/A /*
2N/A * Is there a slot label associated with the device?
2N/A */
2N/A if ((l = pci_slot_label_lookup(mod, node, dp, pdp)) != NULL) {
2N/A nvlist_t *rnvl;
2N/A
2N/A if (topo_mod_nvalloc(mod, &rnvl, NV_UNIQUE_NAME) != 0 ||
2N/A nvlist_add_string(rnvl, TOPO_METH_LABEL_RET_STR, l) != 0)
2N/A return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2N/A *out = rnvl;
2N/A return (0);
2N/A } else {
2N/A if (topo_node_label_set(node, NULL, &err) < 0)
2N/A if (err != ETOPO_PROP_NOENT)
2N/A return (topo_mod_seterrno(mod, err));
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Apci_fru_cmn(topo_mod_t *mod, tnode_t *node, nvlist_t *in, nvlist_t **out)
2N/A{
2N/A int err = 0;
2N/A uint64_t ptr;
2N/A did_t *dp, *pdp;
2N/A tnode_t *pnode;
2N/A char *nm;
2N/A
2N/A *out = NULL;
2N/A nm = topo_node_name(node);
2N/A if (strcmp(nm, PCI_DEVICE) != 0 && strcmp(nm, PCIEX_DEVICE) != 0 &&
2N/A strcmp(nm, PCIEX_BUS) != 0)
2N/A return (0);
2N/A
2N/A if (nvlist_lookup_uint64(in, "nv1", &ptr) != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: label method argument not found.\n", __func__);
2N/A return (-1);
2N/A }
2N/A dp = (did_t *)(uintptr_t)ptr;
2N/A pnode = did_gettnode(dp);
2N/A pdp = did_find(mod, topo_node_getspecific(pnode));
2N/A
2N/A /*
2N/A * Is there a slot label associated with the device?
2N/A */
2N/A if (pci_slot_label_lookup(mod, pnode, dp, pdp) != NULL) {
2N/A nvlist_t *rnvl;
2N/A
2N/A if (topo_node_resource(node, &rnvl, &err) < 0 || rnvl == NULL) {
2N/A topo_mod_dprintf(mod, "%s: error: %s\n",
2N/A __func__, topo_strerror(topo_mod_errno(mod)));
2N/A return (topo_mod_seterrno(mod, err));
2N/A }
2N/A *out = rnvl;
2N/A }
2N/A return (0);
2N/A}