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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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/promif.h>
30 #include <sys/promimpl.h>
31 
32 /*
33  * Routines for walking the PROMs devinfo tree
34  */
35 pnode_t
prom_nextnode(pnode_t nodeid)36 prom_nextnode(pnode_t nodeid)
37 {
38 	cell_t ci[5];
39 
40 	ci[0] = p1275_ptr2cell("peer");		/* Service name */
41 	ci[1] = (cell_t)1;			/* #argument cells */
42 	ci[2] = (cell_t)1;			/* #result cells */
43 	ci[3] = p1275_dnode2cell(nodeid);	/* Arg1: input phandle */
44 	ci[4] = p1275_dnode2cell(OBP_NONODE);	/* Res1: Prime result */
45 
46 	promif_preprom();
47 	(void) p1275_cif_handler(&ci);
48 	promif_postprom();
49 
50 	return (p1275_cell2dnode(ci[4]));	/* Res1: peer phandle */
51 }
52 
53 pnode_t
prom_childnode(pnode_t nodeid)54 prom_childnode(pnode_t nodeid)
55 {
56 	cell_t ci[5];
57 
58 	ci[0] = p1275_ptr2cell("child");	/* Service name */
59 	ci[1] = (cell_t)1;			/* #argument cells */
60 	ci[2] = (cell_t)1;			/* #result cells */
61 	ci[3] = p1275_dnode2cell(nodeid);	/* Arg1: input phandle */
62 	ci[4] = p1275_dnode2cell(OBP_NONODE);	/* Res1: Prime result */
63 
64 	promif_preprom();
65 	(void) p1275_cif_handler(&ci);
66 	promif_postprom();
67 
68 	return (p1275_cell2dnode(ci[4]));	/* Res1: child phandle */
69 }
70 
71 /*
72  * prom_walk_devs() implements a generic walker for the OBP tree; this
73  * implementation uses an explicitly managed stack in order to save the
74  * overhead of a recursive implementation.
75  */
76 void
prom_walk_devs(pnode_t node,int (* cb)(pnode_t,void *,void *),void * arg,void * result)77 prom_walk_devs(pnode_t node, int (*cb)(pnode_t, void *, void *), void *arg,
78     void *result)
79 {
80 	pnode_t stack[OBP_STACKDEPTH];
81 	int stackidx = 0;
82 
83 	if (node == OBP_NONODE || node == OBP_BADNODE) {
84 		prom_panic("Invalid node specified as root of prom tree walk");
85 	}
86 
87 	stack[0] = node;
88 
89 	for (;;) {
90 		pnode_t curnode = stack[stackidx];
91 		pnode_t child;
92 
93 		/*
94 		 * We're out of stuff to do at this level, bump back up a level
95 		 * in the tree, and move to the next node;  if the new level
96 		 * will be level -1, we're done.
97 		 */
98 		if (curnode == OBP_NONODE || curnode == OBP_BADNODE) {
99 			stackidx--;
100 
101 			if (stackidx < 0)
102 				return;
103 
104 			stack[stackidx] = prom_nextnode(stack[stackidx]);
105 			continue;
106 		}
107 
108 		switch ((*cb)(curnode, arg, result)) {
109 
110 		case PROM_WALK_TERMINATE:
111 			return;
112 
113 		case PROM_WALK_CONTINUE:
114 			/*
115 			 * If curnode has a child, traverse to it,
116 			 * otherwise move to curnode's sibling.
117 			 */
118 			child = prom_childnode(curnode);
119 			if (child != OBP_NONODE && child != OBP_BADNODE) {
120 				stackidx++;
121 				stack[stackidx] = child;
122 			} else {
123 				stack[stackidx] =
124 				    prom_nextnode(stack[stackidx]);
125 			}
126 			break;
127 
128 		default:
129 			prom_panic("unrecognized walk directive");
130 		}
131 	}
132 }
133 
134 /*
135  * prom_findnode_bydevtype() searches the prom device subtree rooted at 'node'
136  * and returns the first node whose device type property matches the type
137  * supplied in 'devtype'.
138  */
139 static int
bytype_cb(pnode_t node,void * arg,void * result)140 bytype_cb(pnode_t node, void *arg, void *result)
141 {
142 	if (prom_devicetype(node, (char *)arg)) {
143 		*((pnode_t *)result) = node;
144 		return (PROM_WALK_TERMINATE);
145 	}
146 	return (PROM_WALK_CONTINUE);
147 }
148 
149 pnode_t
prom_findnode_bydevtype(pnode_t node,char * devtype)150 prom_findnode_bydevtype(pnode_t node, char *devtype)
151 {
152 	pnode_t result = OBP_NONODE;
153 	prom_walk_devs(node, bytype_cb, devtype, &result);
154 	return (result);
155 }
156 
157 
158 /*
159  * prom_findnode_byname() searches the prom device subtree rooted at 'node' and
160  * returns the first node whose name matches the name supplied in 'name'.
161  */
162 static int
byname_cb(pnode_t node,void * arg,void * result)163 byname_cb(pnode_t node, void *arg, void *result)
164 {
165 	if (prom_getnode_byname(node, (char *)arg)) {
166 		*((pnode_t *)result) = node;
167 		return (PROM_WALK_TERMINATE);
168 	}
169 	return (PROM_WALK_CONTINUE);
170 }
171 
172 pnode_t
prom_findnode_byname(pnode_t node,char * name)173 prom_findnode_byname(pnode_t node, char *name)
174 {
175 	pnode_t result = OBP_NONODE;
176 	prom_walk_devs(node, byname_cb, name, &result);
177 	return (result);
178 }
179 
180 /*
181  * Return the root nodeid.
182  * Calling prom_nextnode(0) returns the root nodeid.
183  */
184 pnode_t
prom_rootnode(void)185 prom_rootnode(void)
186 {
187 	static pnode_t rootnode;
188 
189 	return (rootnode ? rootnode : (rootnode = prom_nextnode(OBP_NONODE)));
190 }
191 
192 pnode_t
prom_parentnode(pnode_t nodeid)193 prom_parentnode(pnode_t nodeid)
194 {
195 	cell_t ci[5];
196 
197 	ci[0] = p1275_ptr2cell("parent");	/* Service name */
198 	ci[1] = (cell_t)1;			/* #argument cells */
199 	ci[2] = (cell_t)1;			/* #result cells */
200 	ci[3] = p1275_dnode2cell(nodeid);	/* Arg1: input phandle */
201 	ci[4] = p1275_dnode2cell(OBP_NONODE);	/* Res1: Prime result */
202 
203 	promif_preprom();
204 	(void) p1275_cif_handler(&ci);
205 	promif_postprom();
206 
207 	return (p1275_cell2dnode(ci[4]));	/* Res1: parent phandle */
208 }
209 
210 pnode_t
prom_finddevice(char * path)211 prom_finddevice(char *path)
212 {
213 	cell_t ci[5];
214 #ifdef PROM_32BIT_ADDRS
215 	char *opath = NULL;
216 	size_t len;
217 
218 	if ((uintptr_t)path > (uint32_t)-1) {
219 		opath = path;
220 		len = prom_strlen(opath) + 1; /* include terminating NUL */
221 		path = promplat_alloc(len);
222 		if (path == NULL) {
223 			return (OBP_BADNODE);
224 		}
225 		(void) prom_strcpy(path, opath);
226 	}
227 #endif
228 
229 	promif_preprom();
230 
231 	ci[0] = p1275_ptr2cell("finddevice");	/* Service name */
232 	ci[1] = (cell_t)1;			/* #argument cells */
233 	ci[2] = (cell_t)1;			/* #result cells */
234 	ci[3] = p1275_ptr2cell(path);		/* Arg1: pathname */
235 	ci[4] = p1275_dnode2cell(OBP_BADNODE);	/* Res1: Prime result */
236 
237 	(void) p1275_cif_handler(&ci);
238 
239 	promif_postprom();
240 
241 #ifdef PROM_32BIT_ADDRS
242 	if (opath != NULL)
243 		promplat_free(path, len);
244 #endif
245 
246 	return ((pnode_t)p1275_cell2dnode(ci[4])); /* Res1: phandle */
247 }
248 
249 pnode_t
prom_chosennode(void)250 prom_chosennode(void)
251 {
252 	static pnode_t chosen;
253 	pnode_t	node;
254 
255 	if (chosen)
256 		return (chosen);
257 
258 	node = prom_finddevice("/chosen");
259 
260 	if (node != OBP_BADNODE)
261 		return (chosen = node);
262 
263 	prom_fatal_error("prom_chosennode: Can't find </chosen>\n");
264 	/*NOTREACHED*/
265 
266 	/*
267 	 * gcc doesn't recognize "NOTREACHED" and puts the warning.
268 	 * To surpress it, returning an integer value is required.
269 	 */
270 	return ((pnode_t)0);
271 }
272 
273 /*
274  * Returns the nodeid of /aliases.
275  * /aliases exists in OBP >= 2.4 and in Open Firmware.
276  * Returns OBP_BADNODE if it doesn't exist.
277  */
278 pnode_t
prom_alias_node(void)279 prom_alias_node(void)
280 {
281 	static pnode_t node;
282 
283 	if (node == 0)
284 		node = prom_finddevice("/aliases");
285 	return (node);
286 }
287 
288 /*
289  * Returns the nodeid of /options.
290  * Returns OBP_BADNODE if it doesn't exist.
291  */
292 pnode_t
prom_optionsnode(void)293 prom_optionsnode(void)
294 {
295 	static pnode_t node;
296 
297 	if (node == 0)
298 		node = prom_finddevice("/options");
299 	return (node);
300 }
301