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 2006 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 <sys/types.h>
30 #include <sys/esunddi.h>
31 #include <sys/promif_impl.h>
32 
33 #ifdef _KMDB
34 static pnode_t chosennode;
35 static pnode_t optionsnode;
36 #else
37 static char *gettoken(char *tp, char *token);
38 static pnode_t finddevice(char *path);
39 #endif
40 
41 /*
42  * Routines for walking the PROMs devinfo tree
43  */
44 
45 #ifdef _KMDB
46 
47 void
48 promif_set_nodes(pnode_t chosen, pnode_t options)
49 {
50 	chosennode = chosen;
51 	optionsnode = options;
52 }
53 
54 int
55 promif_finddevice(void *p)
56 {
57 	cell_t	*ci = (cell_t *)p;
58 	char *path;
59 
60 	ASSERT(ci[1] == 1);
61 
62 	path = p1275_cell2ptr(ci[3]);
63 
64 	if (strcmp("/chosen", path) == 0) {
65 		ci[4] = p1275_dnode2cell(chosennode);
66 	} else if (strcmp("/options", path) == 0) {
67 		ci[4] = p1275_dnode2cell(optionsnode);
68 	} else {
69 		/* only supports known nodes */
70 		ASSERT(0);
71 	}
72 
73 	return (0);
74 }
75 
76 #else
77 
78 int
79 promif_finddevice(void *p)
80 {
81 	cell_t	*ci = (cell_t *)p;
82 	pnode_t	node;
83 
84 	ASSERT(ci[1] == 1);
85 
86 	node = finddevice(p1275_cell2ptr(ci[3]));
87 
88 	ci[4] = p1275_dnode2cell(node);
89 
90 	return (0);
91 }
92 
93 #endif
94 
95 int
96 promif_nextnode(void *p)
97 {
98 	cell_t	*ci = (cell_t *)p;
99 	pnode_t	next;
100 
101 	ASSERT(ci[1] == 1);
102 
103 	next = promif_stree_nextnode(p1275_cell2dnode(ci[3]));
104 
105 	ci[4] = p1275_dnode2cell(next);
106 
107 	return (0);
108 }
109 
110 int
111 promif_childnode(void *p)
112 {
113 	cell_t	*ci = (cell_t *)p;
114 	pnode_t	child;
115 
116 	ASSERT(ci[1] == 1);
117 
118 	child = promif_stree_childnode(p1275_cell2dnode(ci[3]));
119 
120 	ci[4] = p1275_dnode2cell(child);
121 
122 	return (0);
123 }
124 
125 int
126 promif_parentnode(void *p)
127 {
128 	cell_t	*ci = (cell_t *)p;
129 	pnode_t	parent;
130 
131 	ASSERT(ci[1] == 1);
132 
133 	parent = promif_stree_parentnode(p1275_cell2dnode(ci[3]));
134 
135 	ci[4] = p1275_dnode2cell(parent);
136 
137 	return (0);
138 }
139 
140 #ifndef _KMDB
141 
142 /*
143  * Get a token from a prom pathname, collecting everything
144  * until a non-comma, non-colon separator is found. Any
145  * options, including the ':' option separator, on the end
146  * of the token are removed.
147  */
148 static char *
149 gettoken(char *tp, char *token)
150 {
151 	char *result = token;
152 
153 	for (;;) {
154 		tp = prom_path_gettoken(tp, token);
155 		token += prom_strlen(token);
156 		if ((*tp == ',') || (*tp == ':')) {
157 			*token++ = *tp++;
158 			*token = '\0';
159 			continue;
160 		}
161 		break;
162 	}
163 
164 	/* strip off any options from the token */
165 	prom_strip_options(result, result);
166 
167 	return (tp);
168 }
169 
170 /*
171  * Retrieve the unit address for a node by looking it up
172  * in the corresponding dip. -1 is returned if no unit
173  * address can be determined.
174  */
175 static int
176 get_unit_addr(pnode_t np, char *paddr)
177 {
178 	dev_info_t	*dip;
179 	char		*addr;
180 
181 	if ((dip = e_ddi_nodeid_to_dip(np)) == NULL) {
182 		return (-1);
183 	}
184 
185 	if ((addr = ddi_get_name_addr(dip)) == NULL) {
186 		ddi_release_devi(dip);
187 		return (-1);
188 	}
189 
190 	(void) prom_strcpy(paddr, addr);
191 
192 	ddi_release_devi(dip);
193 
194 	return (0);
195 }
196 
197 /*
198  * Get node id of node in prom tree that path identifies
199  */
200 static pnode_t
201 finddevice(char *path)
202 {
203 	char	name[OBP_MAXPROPNAME];
204 	char	addr[OBP_MAXPROPNAME];
205 	char	pname[OBP_MAXPROPNAME];
206 	char	paddr[OBP_MAXPROPNAME];
207 	char	*tp;
208 	pnode_t	np;
209 	pnode_t	device;
210 
211 	CIF_DBG_NODE("finddevice: %s\n", path);
212 
213 	tp = path;
214 	np = prom_rootnode();
215 	device = OBP_BADNODE;
216 
217 	/* must be a fully specified path */
218 	if (*tp++ != '/')
219 		goto done;
220 
221 	for (;;) {
222 		/* get the name from the path */
223 		tp = gettoken(tp, name);
224 		if (*name == '\0')
225 			break;
226 
227 		/* get the address from the path */
228 		if (*tp == '@') {
229 			tp++;
230 			tp = gettoken(tp, addr);
231 		} else {
232 			addr[0] = '\0';
233 		}
234 
235 		CIF_DBG_NODE("looking for: %s%s%s\n", name,
236 		    (*addr != '\0') ? "@" : "", addr);
237 
238 		if ((np = prom_childnode(np)) == OBP_NONODE)
239 			break;
240 
241 		while (np != OBP_NONODE) {
242 
243 			/* get the name from the current node */
244 			if (prom_getprop(np, OBP_NAME, pname) < 0)
245 				goto done;
246 
247 			/* get the address from the current node */
248 			if (get_unit_addr(np, paddr) < 0)
249 				paddr[0] = '\0';
250 
251 			/* compare the names and addresses */
252 			if ((prom_strcmp(name, pname) == 0) &&
253 			    (prom_strcmp(addr, paddr) == 0)) {
254 				CIF_DBG_NODE("found dev: %s%s%s (0x%x)\n",
255 				    pname, (*paddr != '\0') ? "@" : "",
256 				    paddr, np);
257 				break;
258 			} else {
259 				CIF_DBG_NODE("  no match: %s%s%s vs %s%s%s\n",
260 				    name, (*addr != '\0') ? "@" : "", addr,
261 				    pname, (*paddr != '\0') ? "@" : "", paddr);
262 			}
263 			np = prom_nextnode(np);
264 		}
265 
266 		/* path does not map to a node */
267 		if (np == OBP_NONODE)
268 			break;
269 
270 		if (*tp == '\0') {
271 			/* found a matching node */
272 			device = np;
273 			break;
274 		}
275 
276 		/*
277 		 * Continue the loop with the
278 		 * next component of the path.
279 		 */
280 		tp++;
281 	}
282 done:
283 
284 	if (device == OBP_BADNODE) {
285 		CIF_DBG_NODE("device not found\n\n");
286 	} else {
287 		CIF_DBG_NODE("returning 0x%x\n\n", device);
288 	}
289 
290 	return (device);
291 }
292 
293 #endif
294