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