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) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <fm/topo_mod.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <string.h>
2N/A#include <alloca.h>
2N/A#include <libdevinfo.h>
2N/A#include <did_props.h>
2N/A
2N/A/*
2N/A * Including the following file gives us definitions of the three
2N/A * global arrays used to adjust labels, Slot_Rewrites, Physlot_Names,
2N/A * and Missing_Names. With those defined we can use the common labeling
2N/A * routines for pci.
2N/A */
2N/A#include "pci_sun4v.h"
2N/A#include "pci_sun4.h"
2N/A
2N/A#define PI_PROP_CHASSIS_LOCATION_NAME "chassis-location-name"
2N/A
2N/Atypedef struct _pci_fru {
2N/A tnode_t *node;
2N/A char *location;
2N/A int locsiz;
2N/A nvlist_t **out;
2N/A} _pci_fru_t;
2N/A
2N/A
2N/Astatic int platform_pci_fru_location(topo_mod_t *, tnode_t *, uchar_t *,
2N/A int, nvlist_t **);
2N/Astatic int platform_pci_fru_cb(topo_mod_t *, tnode_t *, void *);
2N/A
2N/A
2N/Aint
2N/Aplatform_pci_label(topo_mod_t *mod, tnode_t *node, nvlist_t *in,
2N/A nvlist_t **out)
2N/A{
2N/A int result;
2N/A int err;
2N/A int locsiz = 0;
2N/A uchar_t *loc = NULL;
2N/A char *nac = NULL;
2N/A
2N/A topo_mod_dprintf(mod, "entering platform_pci_label\n");
2N/A
2N/A *out = NULL;
2N/A result = di_bytes_get(mod, topo_node_getspecific(node),
2N/A PI_PROP_CHASSIS_LOCATION_NAME, &locsiz, &loc);
2N/A if (result == -1 || locsiz < 0) {
2N/A topo_mod_dprintf(mod, "platform_pci_label: %s not found (%s)\n",
2N/A PI_PROP_CHASSIS_LOCATION_NAME, strerror(errno));
2N/A
2N/A /* Invoke the generic label generator for this node */
2N/A return (pci_label_cmn(mod, node, in, out));
2N/A }
2N/A
2N/A /*
2N/A * We have crossed a FRU boundary. Use the value in the
2N/A * chassis-location-name property as the node label.
2N/A */
2N/A nac = alloca(locsiz+1);
2N/A (void) memset(nac, 0, locsiz+1);
2N/A (void) memcpy(nac, loc, locsiz);
2N/A result = topo_node_label_set(node, nac, &err);
2N/A if (result < 0) {
2N/A if (err != ETOPO_PROP_NOENT) {
2N/A return (topo_mod_seterrno(mod, err));
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/Aint
2N/Aplatform_pci_fru(topo_mod_t *mod, tnode_t *node, nvlist_t *in,
2N/A 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, *plat, *pp, **cp;
2N/A char *label;
2N/A int found_t1plat = 0;
2N/A uchar_t *loc;
2N/A int locsiz;
2N/A
2N/A topo_mod_dprintf(mod, "entering platform_pci_fru\n");
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 (void) topo_mod_seterrno(mod, err);
2N/A return (-1);
2N/A }
2N/A /* Delete the "SUNW," */
2N/A pp = strchr(plat, ',');
2N/A if (pp == NULL)
2N/A pp = plat;
2N/A else
2N/A ++pp;
2N/A
2N/A /* Is this an UltraSPARC-T1 platform? */
2N/A cp = usT1_plats;
2N/A while ((*cp != NULL) && (found_t1plat == 0)) {
2N/A if (strcmp(pp, *cp) == 0)
2N/A found_t1plat = 1;
2N/A cp++;
2N/A }
2N/A
2N/A topo_mod_strfree(mod, plat);
2N/A
2N/A /*
2N/A * On UltraSPARC-T1 systems, use the legacy hc scheme on
2N/A * the adapter slots to ensure ALOM on the SP can interpret
2N/A * the FRU correctly. For everything else, follow the normal
2N/A * code flow
2N/A */
2N/A if (found_t1plat) {
2N/A *out = NULL;
2N/A nm = topo_node_name(node);
2N/A if (strcmp(nm, PCI_DEVICE) != 0 &&
2N/A 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, "label method argument "
2N/A "not found.\n");
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 slotname associated with the device?
2N/A */
2N/A if ((label = pci_slot_label_lookup(mod, pnode, dp, pdp))
2N/A != NULL) {
2N/A nvlist_t *rnvl;
2N/A char buf[PATH_MAX];
2N/A
2N/A (void) snprintf(buf, PATH_MAX, "hc:///component=%s",
2N/A label);
2N/A if (topo_mod_str2nvl(mod, buf, &rnvl) < 0)
2N/A return (-1);
2N/A *out = rnvl;
2N/A }
2N/A return (0);
2N/A } else if (di_bytes_get(mod, topo_node_getspecific(node),
2N/A PI_PROP_CHASSIS_LOCATION_NAME, &locsiz, &loc) == 0 && locsiz > 0) {
2N/A /*
2N/A * We have crossed a FRU boundary and need to find the parent
2N/A * node with this location and set our FMRI to that value.
2N/A */
2N/A return (platform_pci_fru_location(mod, node, loc,
2N/A locsiz, out));
2N/A } else {
2N/A return (pci_fru_compute(mod, node, in, out));
2N/A }
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Aplatform_pci_fru_location(topo_mod_t *mod, tnode_t *node, uchar_t *loc,
2N/A int locsiz, nvlist_t **out)
2N/A{
2N/A int err;
2N/A tnode_t *parent;
2N/A tnode_t *top;
2N/A topo_walk_t *wp;
2N/A _pci_fru_t walkdata;
2N/A
2N/A topo_mod_dprintf(mod, "entering platform_pci_fru_location\n");
2N/A
2N/A /* Find the root node */
2N/A top = node;
2N/A while ((parent = topo_node_parent(top)) != NULL) {
2N/A top = parent;
2N/A }
2N/A *out = NULL;
2N/A walkdata.node = node;
2N/A walkdata.out = out;
2N/A walkdata.locsiz = locsiz;
2N/A walkdata.location = alloca(locsiz+1);
2N/A (void) memset(walkdata.location, 0, locsiz+1);
2N/A (void) memcpy(walkdata.location, loc, locsiz);
2N/A
2N/A /* Create a walker starting at the root node */
2N/A wp = topo_mod_walk_init(mod, top, platform_pci_fru_cb, &walkdata, &err);
2N/A if (wp == NULL) {
2N/A return (topo_mod_seterrno(mod, err));
2N/A }
2N/A
2N/A /*
2N/A * Walk the tree breadth first to hopefully avoid visiting too many
2N/A * nodes while searching for the node with the appropriate FMRI.
2N/A */
2N/A (void) topo_walk_step(wp, TOPO_WALK_SIBLING);
2N/A topo_walk_fini(wp);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Aplatform_pci_fru_cb(topo_mod_t *mod, tnode_t *node, void *private)
2N/A{
2N/A int err;
2N/A _pci_fru_t *walkdata = (_pci_fru_t *)private;
2N/A nvlist_t *fmri;
2N/A char *location;
2N/A int result, rc;
2N/A
2N/A if (node == walkdata->node) {
2N/A /* This is the starting node. Do not check the location */
2N/A return (TOPO_WALK_NEXT);
2N/A }
2N/A
2N/A if (topo_node_label(node, &location, &err) != 0) {
2N/A /* This node has no location property. Continue the walk */
2N/A return (TOPO_WALK_NEXT);
2N/A }
2N/A
2N/A result = TOPO_WALK_NEXT;
2N/A if (strncmp(location, walkdata->location, walkdata->locsiz) == 0) {
2N/A /*
2N/A * We have a match. Pass back the node's FRU FMRI.
2N/A */
2N/A rc = topo_node_fru(node, &fmri, NULL, &err);
2N/A if (rc == 0) {
2N/A *walkdata->out = fmri;
2N/A }
2N/A if (rc != 0) {
2N/A result = TOPO_WALK_TERMINATE;
2N/A (void) topo_mod_seterrno(mod, err);
2N/A }
2N/A }
2N/A topo_mod_strfree(mod, location);
2N/A return (result);
2N/A}