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 */
37typedef 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 */
48typedef 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
56static prom_node_t *promif_root;
57
58static prom_node_t *find_node(pnode_t nodeid);
59static prom_node_t *find_node_work(prom_node_t *np, pnode_t node);
60static int getproplen(prom_node_t *pnp, char *name);
61static void *getprop(prom_node_t *pnp, char *name);
62static char *nextprop(prom_node_t *pnp, char *name);
63
64#ifndef _KMDB
65static void create_prop(prom_node_t *pnp, char *name, void *val, int len);
66static prom_node_t *create_node(prom_node_t *parent, pnode_t node);
67static void create_peers(prom_node_t *pnp, pnode_t node);
68static 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
80void
81promif_stree_setroot(void *root)
82{
83	promif_root = (prom_node_t *)root;
84}
85#else
86void *
87promif_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
98pnode_t
99promif_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
111pnode_t
112promif_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
123pnode_t
124promif_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
140int
141promif_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
152int
153promif_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
176char *
177promif_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
197static prom_node_t *
198find_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
215static prom_node_t *
216find_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
228static int
229getproplen(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
240static void *
241getprop(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
252static char *
253nextprop(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
274int
275promif_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 */
316void
317promif_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
329static void
330create_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
352static void
353create_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
375static prom_node_t *
376create_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
418static void
419create_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
450static void
451promif_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