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