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 2008 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_impl.h>
30 #include <sys/kmem.h>
31 #include <sys/machsystm.h>
32
33 /*
34 * A property attached to a node in the kernel's
35 * shadow copy of the PROM device tree.
36 */
37 typedef struct prom_prop {
38 struct prom_prop *pp_next;
39 char *pp_name;
40 int pp_len;
41 void *pp_val;
42 } prom_prop_t;
43
44 /*
45 * A node in the kernel's shadow copy of the PROM
46 * device tree.
47 */
48 typedef struct prom_node {
49 pnode_t pn_nodeid;
50 struct prom_prop *pn_propp;
51 struct prom_node *pn_parent;
52 struct prom_node *pn_child;
53 struct prom_node *pn_sibling;
54 } prom_node_t;
55
56 static prom_node_t *promif_root;
57
58 static prom_node_t *find_node(pnode_t nodeid);
59 static prom_node_t *find_node_work(prom_node_t *np, pnode_t node);
60 static int getproplen(prom_node_t *pnp, char *name);
61 static void *getprop(prom_node_t *pnp, char *name);
62 static char *nextprop(prom_node_t *pnp, char *name);
63
64 #ifndef _KMDB
65 static void create_prop(prom_node_t *pnp, char *name, void *val, int len);
66 static prom_node_t *create_node(prom_node_t *parent, pnode_t node);
67 static void create_peers(prom_node_t *pnp, pnode_t node);
68 static void create_children(prom_node_t *pnp, pnode_t parent);
69 #endif
70
71 /*
72 * Hooks for kmdb for accessing the PROM shadow tree. The driver portion
73 * of kmdb will retrieve the root of the tree and pass it down to the
74 * debugger portion of kmdb. As the kmdb debugger is standalone, it has
75 * its own promif_root pointer that it will be set to the value passed by
76 * the driver so that kmdb points to the shadow tree maintained by the kernel.
77 * So the "get" function is in the kernel while the "set" function is in kmdb.
78 */
79 #ifdef _KMDB
80 void
promif_stree_setroot(void * root)81 promif_stree_setroot(void *root)
82 {
83 promif_root = (prom_node_t *)root;
84 }
85 #else
86 void *
promif_stree_getroot(void)87 promif_stree_getroot(void)
88 {
89 return (promif_root);
90 }
91 #endif
92
93 /*
94 * Interfaces used internally by promif functions.
95 * These hide all accesses to the shadow tree.
96 */
97
98 pnode_t
promif_stree_parentnode(pnode_t nodeid)99 promif_stree_parentnode(pnode_t nodeid)
100 {
101 prom_node_t *pnp;
102
103 pnp = find_node(nodeid);
104 if (pnp && pnp->pn_parent) {
105 return (pnp->pn_parent->pn_nodeid);
106 }
107
108 return (OBP_NONODE);
109 }
110
111 pnode_t
promif_stree_childnode(pnode_t nodeid)112 promif_stree_childnode(pnode_t nodeid)
113 {
114 prom_node_t *pnp;
115
116 pnp = find_node(nodeid);
117 if (pnp && pnp->pn_child)
118 return (pnp->pn_child->pn_nodeid);
119
120 return (OBP_NONODE);
121 }
122
123 pnode_t
promif_stree_nextnode(pnode_t nodeid)124 promif_stree_nextnode(pnode_t nodeid)
125 {
126 prom_node_t *pnp;
127
128 /*
129 * Note: next(0) returns the root node
130 */
131 pnp = find_node(nodeid);
132 if (pnp && (nodeid == OBP_NONODE))
133 return (pnp->pn_nodeid);
134 if (pnp && pnp->pn_sibling)
135 return (pnp->pn_sibling->pn_nodeid);
136
137 return (OBP_NONODE);
138 }
139
140 int
promif_stree_getproplen(pnode_t nodeid,char * name)141 promif_stree_getproplen(pnode_t nodeid, char *name)
142 {
143 prom_node_t *pnp;
144
145 pnp = find_node(nodeid);
146 if (pnp == NULL)
147 return (-1);
148
149 return (getproplen(pnp, name));
150 }
151
152 int
promif_stree_getprop(pnode_t nodeid,char * name,void * value)153 promif_stree_getprop(pnode_t nodeid, char *name, void *value)
154 {
155 prom_node_t *pnp;
156 void *prop;
157 int len;
158
159 pnp = find_node(nodeid);
160 if (pnp == NULL) {
161 prom_printf("find_node: no node?\n");
162 return (-1);
163 }
164
165 len = getproplen(pnp, name);
166 if (len > 0) {
167 prop = getprop(pnp, name);
168 bcopy(prop, value, len);
169 } else {
170 prom_printf("find_node: getproplen: %d\n", len);
171 }
172
173 return (len);
174 }
175
176 char *
promif_stree_nextprop(pnode_t nodeid,char * name,char * next)177 promif_stree_nextprop(pnode_t nodeid, char *name, char *next)
178 {
179 prom_node_t *pnp;
180 char *propname;
181
182 next[0] = '\0';
183
184 pnp = find_node(nodeid);
185 if (pnp == NULL)
186 return (NULL);
187
188 propname = nextprop(pnp, name);
189 if (propname == NULL)
190 return (next);
191
192 (void) prom_strcpy(next, propname);
193
194 return (next);
195 }
196
197 static prom_node_t *
find_node_work(prom_node_t * np,pnode_t node)198 find_node_work(prom_node_t *np, pnode_t node)
199 {
200 prom_node_t *nnp;
201 prom_node_t *snp;
202
203 for (snp = np; snp != NULL; snp = snp->pn_sibling) {
204 if (snp->pn_nodeid == node)
205 return (snp);
206
207 if (snp->pn_child)
208 if ((nnp = find_node_work(snp->pn_child, node)) != NULL)
209 return (nnp);
210 }
211
212 return (NULL);
213 }
214
215 static prom_node_t *
find_node(pnode_t nodeid)216 find_node(pnode_t nodeid)
217 {
218
219 if (nodeid == OBP_NONODE)
220 return (promif_root);
221
222 if (promif_root == NULL)
223 return (NULL);
224
225 return (find_node_work(promif_root, nodeid));
226 }
227
228 static int
getproplen(prom_node_t * pnp,char * name)229 getproplen(prom_node_t *pnp, char *name)
230 {
231 struct prom_prop *propp;
232
233 for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
234 if (prom_strcmp(propp->pp_name, name) == 0)
235 return (propp->pp_len);
236
237 return (-1);
238 }
239
240 static void *
getprop(prom_node_t * np,char * name)241 getprop(prom_node_t *np, char *name)
242 {
243 struct prom_prop *propp;
244
245 for (propp = np->pn_propp; propp != NULL; propp = propp->pp_next)
246 if (prom_strcmp(propp->pp_name, name) == 0)
247 return (propp->pp_val);
248
249 return (NULL);
250 }
251
252 static char *
nextprop(prom_node_t * pnp,char * name)253 nextprop(prom_node_t *pnp, char *name)
254 {
255 struct prom_prop *propp;
256
257 /*
258 * getting next of NULL or a null string returns the first prop name
259 */
260 if (name == NULL || *name == '\0')
261 if (pnp->pn_propp)
262 return (pnp->pn_propp->pp_name);
263
264 for (propp = pnp->pn_propp; propp != NULL; propp = propp->pp_next)
265 if (prom_strcmp(propp->pp_name, name) == 0)
266 if (propp->pp_next)
267 return (propp->pp_next->pp_name);
268
269 return (NULL);
270 }
271
272 #ifndef _KMDB
273
274 int
promif_stree_setprop(pnode_t nodeid,char * name,void * value,int len)275 promif_stree_setprop(pnode_t nodeid, char *name, void *value, int len)
276 {
277 prom_node_t *pnp;
278 struct prom_prop *prop;
279
280 pnp = find_node(nodeid);
281 if (pnp == NULL) {
282 prom_printf("find_node: no node?\n");
283 return (-1);
284 }
285
286 /*
287 * If a property with this name exists, replace the existing
288 * value.
289 */
290 for (prop = pnp->pn_propp; prop; prop = prop->pp_next)
291 if (prom_strcmp(prop->pp_name, name) == 0) {
292 /*
293 * Make sure we don't get dispatched onto a
294 * different cpu if we happen to sleep. See
295 * kern_postprom().
296 */
297 thread_affinity_set(curthread, CPU->cpu_id);
298 kmem_free(prop->pp_val, prop->pp_len);
299
300 prop->pp_val = NULL;
301 if (len > 0) {
302 prop->pp_val = kmem_zalloc(len, KM_SLEEP);
303 bcopy(value, prop->pp_val, len);
304 }
305 thread_affinity_clear(curthread);
306 prop->pp_len = len;
307 return (len);
308 }
309
310 return (-1);
311 }
312
313 /*
314 * Create a promif private copy of boot's device tree.
315 */
316 void
promif_stree_init(void)317 promif_stree_init(void)
318 {
319 pnode_t node;
320 prom_node_t *pnp;
321
322 node = prom_rootnode();
323 promif_root = pnp = create_node(OBP_NONODE, node);
324
325 create_peers(pnp, node);
326 create_children(pnp, node);
327 }
328
329 static void
create_children(prom_node_t * pnp,pnode_t parent)330 create_children(prom_node_t *pnp, pnode_t parent)
331 {
332 prom_node_t *cnp;
333 pnode_t child;
334
335 _NOTE(CONSTCOND)
336 while (1) {
337 child = prom_childnode(parent);
338 if (child == 0)
339 break;
340 if (prom_getproplen(child, "name") <= 0) {
341 parent = child;
342 continue;
343 }
344 cnp = create_node(pnp, child);
345 pnp->pn_child = cnp;
346 create_peers(cnp, child);
347 pnp = cnp;
348 parent = child;
349 }
350 }
351
352 static void
create_peers(prom_node_t * np,pnode_t node)353 create_peers(prom_node_t *np, pnode_t node)
354 {
355 prom_node_t *pnp;
356 pnode_t peer;
357
358 _NOTE(CONSTCOND)
359 while (1) {
360 peer = prom_nextnode(node);
361 if (peer == 0)
362 break;
363 if (prom_getproplen(peer, "name") <= 0) {
364 node = peer;
365 continue;
366 }
367 pnp = create_node(np->pn_parent, peer);
368 np->pn_sibling = pnp;
369 create_children(pnp, peer);
370 np = pnp;
371 node = peer;
372 }
373 }
374
375 static prom_node_t *
create_node(prom_node_t * parent,pnode_t node)376 create_node(prom_node_t *parent, pnode_t node)
377 {
378 prom_node_t *pnp;
379 char prvname[OBP_MAXPROPNAME];
380 char propname[OBP_MAXPROPNAME];
381 int proplen;
382 void *propval;
383
384 /*
385 * Make sure we don't get dispatched onto a different
386 * cpu if we happen to sleep. See kern_postprom().
387 */
388 thread_affinity_set(curthread, CPU->cpu_id);
389
390 pnp = kmem_zalloc(sizeof (prom_node_t), KM_SLEEP);
391 pnp->pn_nodeid = node;
392 pnp->pn_parent = parent;
393
394 prvname[0] = '\0';
395
396 _NOTE(CONSTCOND)
397 while (1) {
398 (void) prom_nextprop(node, prvname, propname);
399 if (prom_strlen(propname) == 0)
400 break;
401 if ((proplen = prom_getproplen(node, propname)) == -1)
402 continue;
403 propval = NULL;
404 if (proplen != 0) {
405 propval = kmem_zalloc(proplen, KM_SLEEP);
406 (void) prom_getprop(node, propname, propval);
407 }
408 create_prop(pnp, propname, propval, proplen);
409
410 (void) prom_strcpy(prvname, propname);
411 }
412
413 thread_affinity_clear(curthread);
414
415 return (pnp);
416 }
417
418 static void
create_prop(prom_node_t * pnp,char * name,void * val,int len)419 create_prop(prom_node_t *pnp, char *name, void *val, int len)
420 {
421 struct prom_prop *prop;
422 struct prom_prop *newprop;
423
424 /*
425 * Make sure we don't get dispatched onto a different
426 * cpu if we happen to sleep. See kern_postprom().
427 */
428 thread_affinity_set(curthread, CPU->cpu_id);
429 newprop = kmem_zalloc(sizeof (*newprop), KM_SLEEP);
430 newprop->pp_name = kmem_zalloc(prom_strlen(name) + 1, KM_SLEEP);
431 thread_affinity_clear(curthread);
432
433 (void) prom_strcpy(newprop->pp_name, name);
434 newprop->pp_val = val;
435 newprop->pp_len = len;
436
437 if (pnp->pn_propp == NULL) {
438 pnp->pn_propp = newprop;
439 return;
440 }
441
442 /* move to the end of the prop list */
443 for (prop = pnp->pn_propp; prop->pp_next != NULL; prop = prop->pp_next)
444 /* empty */;
445
446 /* append the new prop */
447 prop->pp_next = newprop;
448 }
449
450 static void
promif_dump_tree(prom_node_t * pnp)451 promif_dump_tree(prom_node_t *pnp)
452 {
453 int i;
454 static int level = 0;
455
456 if (pnp == NULL)
457 return;
458
459 for (i = 0; i < level; i++) {
460 prom_printf(" ");
461 }
462
463 prom_printf("Node 0x%x (parent=0x%x, sibling=0x%x)\n", pnp->pn_nodeid,
464 (pnp->pn_parent) ? pnp->pn_parent->pn_nodeid : 0,
465 (pnp->pn_sibling) ? pnp->pn_sibling->pn_nodeid : 0);
466
467 if (pnp->pn_child != NULL) {
468 level++;
469 promif_dump_tree(pnp->pn_child);
470 level--;
471 }
472
473 if (pnp->pn_sibling != NULL)
474 promif_dump_tree(pnp->pn_sibling);
475 }
476
477 #endif
478