1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include "priplugin.h"
30
31static int
32find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
33    const char *pval, picl_nodehdl_t *nodeh);
34static int
35compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
36    const char *pval);
37
38/*
39 * Gather IO device nodes from the PRI and use the info to
40 * find the corresponding nodes in PICL's device tree, insert
41 * a Label into the devtree containing the "nac" from the PRI,
42 * and add a reference property to the corresponding fru tree node.
43 */
44void
45io_dev_addlabel(md_t *mdp)
46{
47	int status, substatus, i, node_count, component_count, busaddr_match;
48	int type_size, nac_size;
49	picl_nodehdl_t platnode, tpn;
50	char busaddr[PICL_PROPNAMELEN_MAX], *p, *q;
51	char path[PICL_PROPNAMELEN_MAX];
52	mde_cookie_t *components, md_rootnode;
53	char *type, *nac, *pri_path, *saved_path;
54
55	if (mdp == NULL)
56		return;
57
58	md_rootnode = md_root_node(mdp);
59
60	/*
61	 * Find and remember the roots of the /frutree and /platform trees.
62	 */
63	if ((status = ptree_get_node_by_path(PLATFORM_PATH, &platnode)) !=
64	    PICL_SUCCESS) {
65		pri_debug(LOG_NOTICE,
66		    "io_dev_label: can't find platform node: %s\n",
67		    picl_strerror(status));
68		return;
69	}
70
71	node_count = md_node_count(mdp);
72	if (node_count == 0) {
73		pri_debug(LOG_NOTICE, "io_dev_addlabel: no nodes to "
74		    "process\n");
75		return;
76	}
77	components = (mde_cookie_t *)malloc(node_count *
78	    sizeof (mde_cookie_t));
79	if (components == NULL) {
80		pri_debug(LOG_NOTICE,
81		    "io_dev_addlabel: can't get memory for IO nodes\n");
82		return;
83	}
84
85	component_count = md_scan_dag(mdp, md_rootnode,
86	    md_find_name(mdp, "component"),
87	    md_find_name(mdp, "fwd"), components);
88
89	for (i = 0; i < component_count; ++i) {
90		tpn = platnode;
91
92		/*
93		 * Try to fetch the "type" as a string or as "data" until we
94		 * can agree on what its tag type should be.
95		 */
96		if (md_get_prop_str(mdp, components[i], "type", &type) ==
97		    -1) {
98			if (md_get_prop_data(mdp, components[i], "type",
99			    (uint8_t **)&type, &type_size)) {
100				pri_debug(LOG_NOTICE, "io_add_devlabel: "
101				    "can't get type for component %d\n", i);
102			continue;
103			}
104		}
105
106		/*
107		 * Isolate components of type "io".
108		 */
109		if (strcmp((const char *)type, "io")) {
110			pri_debug(LOG_NOTICE,
111			    "io_add_devlabel: skipping component %d with "
112			    "type %s\n", i, type);
113			continue;
114		}
115
116		/*
117		 * Now get the nac and raw path from the PRI.
118		 */
119		if (md_get_prop_str(mdp, components[i], "nac", &nac) == -1) {
120			pri_debug(LOG_NOTICE,
121			    "io_add_devlabel: can't get nac value for device "
122			    "<%s>\n", type);
123			continue;
124		} else
125			nac_size = strlen(nac) + 1;
126
127		if (md_get_prop_str(mdp, components[i], "path", &pri_path) ==
128		    -1) {
129			pri_debug(LOG_NOTICE,
130			    "io_add_devlabel: can't get path value for "
131			    "device <%s>\n", type);
132			continue;
133		}
134
135		(void) strlcpy(path, pri_path, sizeof (path));
136
137		pri_debug(LOG_NOTICE, "io_add_devlabel: processing component "
138		    "%d, type <%s>, nac <%s>, path <%s>\n", i, type, nac,
139		    path);
140
141		/*
142		 * This loop visits each path component where those
143		 * components are delimited with '/' and '@' characters.
144		 * Each path component is a search key into the /platform
145		 * tree; we're looking to match the bus-addr field of
146		 * a node if that field is defined.  If each path component
147		 * matches up then we now have the corresponding device
148		 * path for that IO device.  Add a Label property to the
149		 * leaf node.
150		 */
151		for (busaddr_match = 1, p = q = (char *)path; q; p = q + 1) {
152
153			/*
154			 * Isolate the bus address for this node by skipping
155			 * over the first delimiter if present and writing
156			 * a NUL character over the next '/'.
157			 */
158			if (*p == '/')
159				++p;
160			if (*p == '@')
161				++p;
162			if ((q = strchr((const char *)p, '/')) != NULL)
163				*q = '\0';
164
165			/*
166			 * See if there's a match, at this level only, in the
167			 * device tree.  We cannot skip generations in the
168			 * device tree, which is why we're not doing a
169			 * recursive search for bus-addr.  bus-addr must
170			 * be found at each node along the way.  By doing
171			 * this we'll stay in sync with the path components
172			 * in the PRI.
173			 */
174			if ((status = find_node_by_string_prop(tpn,
175			    PICL_PROP_BUS_ADDR, (const char *)p, &tpn)) !=
176			    PICL_SUCCESS) {
177				pri_debug(LOG_NOTICE,
178				    "can't find %s property of <%s> "
179				    "for nac %s: %s\n",
180				    PICL_PROP_BUS_ADDR, p, nac,
181				    picl_strerror(status));
182				busaddr_match = 0;
183				break;
184			}
185
186			/*
187			 * Note path component for the leaf so we can use
188			 * it below.
189			 */
190			saved_path = p;
191		}
192
193		/*
194		 * We could not drill down through the bus-addrs, so skip this
195		 * device and move on to the next.
196		 */
197		if (busaddr_match == 0) {
198			pri_debug(LOG_NOTICE, "io_add_devlabel: no matching "
199			    "bus-addr path for this nac - skipping\n");
200			continue;
201		}
202
203		nac_size = strlen((const char *)nac) + 1;
204
205		/*
206		 * This loop adds a Label property to all the functions
207		 * on the device we matched from the PRI path.
208		 */
209		for (status = PICL_SUCCESS; status == PICL_SUCCESS;
210		    status = ptree_get_propval_by_name(tpn,
211		    PICL_PROP_PEER, &tpn, sizeof (picl_nodehdl_t))) {
212			/*
213			 * Add Labels to peers that have the same bus-addr
214			 * value (ignoring the function numbers.)
215			 */
216			if ((substatus = ptree_get_propval_by_name(tpn,
217			    PICL_PROP_BUS_ADDR,
218			    busaddr, sizeof (busaddr))) != PICL_SUCCESS) {
219				pri_debug(LOG_NOTICE,
220				    "io_add_device: can't get %s "
221				    "property from picl devtree: %s\n",
222				    PICL_PROP_BUS_ADDR,
223				    picl_strerror(substatus));
224			} else {
225				if (strncmp(busaddr, saved_path,
226				    PICL_PROPNAMELEN_MAX) == 0) {
227					add_md_prop(tpn, nac_size,
228					    PICL_PROP_LABEL, nac,
229					    PICL_PTYPE_CHARSTRING);
230				}
231			}
232		}
233	}
234	free(components);
235}
236
237/*
238 * These two functions shamelessly stolen from picldevtree.c
239 */
240
241/*
242 * Return 1 if this node has this property with the given value.
243 */
244static int
245compare_string_propval(picl_nodehdl_t nodeh, const char *pname,
246    const char *pval)
247{
248	char *pvalbuf;
249	int err;
250	int len;
251	ptree_propinfo_t pinfo;
252	picl_prophdl_t proph;
253
254	err = ptree_get_prop_by_name(nodeh, pname, &proph);
255	if (err != PICL_SUCCESS)	/* prop doesn't exist */
256		return (0);
257
258	err = ptree_get_propinfo(proph, &pinfo);
259	if (pinfo.piclinfo.type != PICL_PTYPE_CHARSTRING)
260		return (0);	/* not string prop */
261
262	len = strlen(pval) + 1;
263
264	pvalbuf = alloca(len);
265	if (pvalbuf == NULL)
266		return (0);
267
268	err = ptree_get_propval(proph, pvalbuf, len);
269	if ((err == PICL_SUCCESS) && (strcmp(pvalbuf, pval) == 0))
270		return (1);	/* prop match */
271
272	return (0);
273}
274
275/*
276 * Search this node's children for the given property.
277 */
278static int
279find_node_by_string_prop(picl_nodehdl_t rooth, const char *pname,
280    const char *pval, picl_nodehdl_t *nodeh)
281{
282	picl_nodehdl_t childh;
283	int err;
284
285	for (err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
286	    sizeof (picl_nodehdl_t)); err != PICL_PROPNOTFOUND;
287	    err = ptree_get_propval_by_name(childh, PICL_PROP_PEER,
288	    &childh, sizeof (picl_nodehdl_t))) {
289		if (err != PICL_SUCCESS)
290			return (err);
291
292		if (compare_string_propval(childh, pname, pval)) {
293			*nodeh = childh;
294			return (PICL_SUCCESS);
295		}
296	}
297	return (PICL_ENDOFLIST);
298}
299