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