/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "priplugin.h"
static int
find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
const char *pval, picl_nodehdl_t *nodeh);
static int
compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
const char *pval);
/*
* Gather IO device nodes from the PRI and use the info to
* find the corresponding nodes in PICL's device tree, insert
* a Label into the devtree containing the "nac" from the PRI,
* and add a reference property to the corresponding fru tree node.
*/
void
io_dev_addlabel(md_t *mdp)
{
int status, substatus, i, node_count, component_count, busaddr_match;
int type_size, nac_size;
picl_nodehdl_t platnode, tpn;
char busaddr[PICL_PROPNAMELEN_MAX], *p, *q;
char path[PICL_PROPNAMELEN_MAX];
mde_cookie_t *components, md_rootnode;
char *type, *nac, *pri_path, *saved_path;
if (mdp == NULL)
return;
md_rootnode = md_root_node(mdp);
/*
* Find and remember the roots of the /frutree and /platform trees.
*/
if ((status = ptree_get_node_by_path(PLATFORM_PATH, &platnode)) !=
PICL_SUCCESS) {
pri_debug(LOG_NOTICE,
"io_dev_label: can't find platform node: %s\n",
picl_strerror(status));
return;
}
node_count = md_node_count(mdp);
if (node_count == 0) {
pri_debug(LOG_NOTICE, "io_dev_addlabel: no nodes to "
"process\n");
return;
}
components = (mde_cookie_t *)malloc(node_count *
sizeof (mde_cookie_t));
if (components == NULL) {
pri_debug(LOG_NOTICE,
"io_dev_addlabel: can't get memory for IO nodes\n");
return;
}
component_count = md_scan_dag(mdp, md_rootnode,
md_find_name(mdp, "component"),
md_find_name(mdp, "fwd"), components);
for (i = 0; i < component_count; ++i) {
tpn = platnode;
/*
* Try to fetch the "type" as a string or as "data" until we
* can agree on what its tag type should be.
*/
if (md_get_prop_str(mdp, components[i], "type", &type) ==
-1) {
if (md_get_prop_data(mdp, components[i], "type",
(uint8_t **)&type, &type_size)) {
pri_debug(LOG_NOTICE, "io_add_devlabel: "
"can't get type for component %d\n", i);
continue;
}
}
/*
* Isolate components of type "io".
*/
if (strcmp((const char *)type, "io")) {
pri_debug(LOG_NOTICE,
"io_add_devlabel: skipping component %d with "
"type %s\n", i, type);
continue;
}
/*
* Now get the nac and raw path from the PRI.
*/
if (md_get_prop_str(mdp, components[i], "nac", &nac) == -1) {
pri_debug(LOG_NOTICE,
"io_add_devlabel: can't get nac value for device "
"<%s>\n", type);
continue;
} else
nac_size = strlen(nac) + 1;
if (md_get_prop_str(mdp, components[i], "path", &pri_path) ==
-1) {
pri_debug(LOG_NOTICE,
"io_add_devlabel: can't get path value for "
"device <%s>\n", type);
continue;
}
(void) strlcpy(path, pri_path, sizeof (path));
pri_debug(LOG_NOTICE, "io_add_devlabel: processing component "
"%d, type <%s>, nac <%s>, path <%s>\n", i, type, nac,
path);
/*
* This loop visits each path component where those
* components are delimited with '/' and '@' characters.
* Each path component is a search key into the /platform
* tree; we're looking to match the bus-addr field of
* a node if that field is defined. If each path component
* matches up then we now have the corresponding device
* path for that IO device. Add a Label property to the
* leaf node.
*/
for (busaddr_match = 1, p = q = (char *)path; q; p = q + 1) {
/*
* Isolate the bus address for this node by skipping
* over the first delimiter if present and writing
* a NUL character over the next '/'.
*/
if (*p == '/')
++p;
if (*p == '@')
++p;
if ((q = strchr((const char *)p, '/')) != NULL)
*q = '\0';
/*
* See if there's a match, at this level only, in the
* device tree. We cannot skip generations in the
* device tree, which is why we're not doing a
* recursive search for bus-addr. bus-addr must
* be found at each node along the way. By doing
* this we'll stay in sync with the path components
* in the PRI.
*/
if ((status = find_node_by_string_prop(tpn,
PICL_PROP_BUS_ADDR, (const char *)p, &tpn)) !=
PICL_SUCCESS) {
pri_debug(LOG_NOTICE,
"can't find %s property of <%s> "
"for nac %s: %s\n",
PICL_PROP_BUS_ADDR, p, nac,
picl_strerror(status));
busaddr_match = 0;
break;
}
/*
* Note path component for the leaf so we can use
* it below.
*/
saved_path = p;
}
/*
* We could not drill down through the bus-addrs, so skip this
* device and move on to the next.
*/
if (busaddr_match == 0) {
pri_debug(LOG_NOTICE, "io_add_devlabel: no matching "
"bus-addr path for this nac - skipping\n");
continue;
}
nac_size = strlen((const char *)nac) + 1;
/*
* This loop adds a Label property to all the functions
* on the device we matched from the PRI path.
*/
for (status = PICL_SUCCESS; status == PICL_SUCCESS;
status = ptree_get_propval_by_name(tpn,
PICL_PROP_PEER, &tpn, sizeof (picl_nodehdl_t))) {
/*
* Add Labels to peers that have the same bus-addr
* value (ignoring the function numbers.)
*/
if ((substatus = ptree_get_propval_by_name(tpn,
PICL_PROP_BUS_ADDR,
busaddr, sizeof (busaddr))) != PICL_SUCCESS) {
pri_debug(LOG_NOTICE,
"io_add_device: can't get %s "
"property from picl devtree: %s\n",
PICL_PROP_BUS_ADDR,
picl_strerror(substatus));
} else {
if (strncmp(busaddr, saved_path,
PICL_PROPNAMELEN_MAX) == 0) {
add_md_prop(tpn, nac_size,
PICL_PROP_LABEL, nac,
PICL_PTYPE_CHARSTRING);
}
}
}
}
free(components);
}
/*
* These two functions shamelessly stolen from picldevtree.c
*/
/*
* Return 1 if this node has this property with the given value.
*/
static int
compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
const char *pval)
{
char *pvalbuf;
int err;
int len;
ptree_propinfo_t pinfo;
picl_prophdl_t proph;
err = ptree_get_prop_by_name(nodeh, pname, &proph);
if (err != PICL_SUCCESS) /* prop doesn't exist */
return (0);
err = ptree_get_propinfo(proph, &pinfo);
if (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING)
return (0); /* not string prop */
len = strlen(pval) + 1;
pvalbuf = alloca(len);
if (pvalbuf == NULL)
return (0);
err = ptree_get_propval(proph, pvalbuf, len);
if ((err == PICL_SUCCESS) && (strcmp(pvalbuf, pval) == 0))
return (1); /* prop match */
return (0);
}
/*
* Search this node's children for the given property.
*/
static int
find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
const char *pval, picl_nodehdl_t *nodeh)
{
picl_nodehdl_t childh;
int err;
for (err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND;
err = ptree_get_propval_by_name(childh, PICL_PROP_PEER,
&childh, sizeof (picl_nodehdl_t))) {
if (err != PICL_SUCCESS)
return (err);
if (compare_string_propval(childh, pname, pval)) {
*nodeh = childh;
return (PICL_SUCCESS);
}
}
return (PICL_ENDOFLIST);
}