xref: /openbsd-src/sys/dev/ofw/fdt.c (revision 3fe5dae2ec441a3017833425acacc03e363b19df)
1*3fe5dae2Skettenis /*	$OpenBSD: fdt.c,v 1.35 2024/03/27 23:05:27 kettenis Exp $	*/
2c03f87daSbmercer 
3c03f87daSbmercer /*
4c03f87daSbmercer  * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
5b244cb8bSkettenis  * Copyright (c) 2009 Mark Kettenis <kettenis@openbsd.org>
6c03f87daSbmercer  *
7c03f87daSbmercer  * Permission to use, copy, modify, and distribute this software for any
8c03f87daSbmercer  * purpose with or without fee is hereby granted, provided that the above
9c03f87daSbmercer  * copyright notice and this permission notice appear in all copies.
10c03f87daSbmercer  *
11c03f87daSbmercer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12c03f87daSbmercer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13c03f87daSbmercer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14c03f87daSbmercer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15c03f87daSbmercer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16c03f87daSbmercer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17c03f87daSbmercer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18c03f87daSbmercer  */
19c03f87daSbmercer 
20c03f87daSbmercer #include <sys/types.h>
21c03f87daSbmercer #include <sys/systm.h>
2250946bcbSkettenis #include <sys/malloc.h>
23c03f87daSbmercer 
24c03f87daSbmercer #include <dev/ofw/fdt.h>
25c03f87daSbmercer #include <dev/ofw/openfirm.h>
26c03f87daSbmercer 
27c03f87daSbmercer unsigned int fdt_check_head(void *);
28c03f87daSbmercer char	*fdt_get_str(u_int32_t);
29c03f87daSbmercer void	*skip_property(u_int32_t *);
30c03f87daSbmercer void	*skip_props(u_int32_t *);
31c03f87daSbmercer void	*skip_node_name(u_int32_t *);
32dd86c6a0Spatrick void	*skip_node(void *);
33808ecccaSvisa void	*skip_nops(u_int32_t *);
34c03f87daSbmercer void	*fdt_parent_node_recurse(void *, void *);
35d0cd2b3aSkettenis void	*fdt_find_phandle_recurse(void *, uint32_t);
36401e79efSpatrick int	 fdt_node_property_int(void *, char *, int *);
37401e79efSpatrick int	 fdt_node_property_ints(void *, char *, int *, int);
38b7952965Spatrick int	 fdt_translate_reg(void *, struct fdt_reg *);
39c03f87daSbmercer #ifdef DEBUG
40c03f87daSbmercer void 	 fdt_print_node_recurse(void *, int);
41c03f87daSbmercer #endif
42c03f87daSbmercer 
43c03f87daSbmercer static int tree_inited = 0;
44c03f87daSbmercer static struct fdt tree;
45c03f87daSbmercer 
46c03f87daSbmercer unsigned int
fdt_check_head(void * fdt)47c03f87daSbmercer fdt_check_head(void *fdt)
48c03f87daSbmercer {
49c03f87daSbmercer 	struct fdt_head *fh;
50808ecccaSvisa 	u_int32_t *ptr, *tok;
51c03f87daSbmercer 
52c03f87daSbmercer 	fh = fdt;
53c03f87daSbmercer 	ptr = (u_int32_t *)fdt;
54c03f87daSbmercer 
55c03f87daSbmercer 	if (betoh32(fh->fh_magic) != FDT_MAGIC)
56c03f87daSbmercer 		return 0;
57c03f87daSbmercer 
58c03f87daSbmercer 	if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
59c03f87daSbmercer 		return 0;
60c03f87daSbmercer 
61808ecccaSvisa 	tok = skip_nops(ptr + (betoh32(fh->fh_struct_off) / 4));
62808ecccaSvisa 	if (betoh32(*tok) != FDT_NODE_BEGIN)
63c03f87daSbmercer 		return 0;
64c03f87daSbmercer 
65c03f87daSbmercer 	/* check for end signature on version 17 blob */
66c03f87daSbmercer 	if ((betoh32(fh->fh_version) >= 17) &&
673622bda5Smpi 	    (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
683622bda5Smpi 	    (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
69c03f87daSbmercer 		return 0;
70c03f87daSbmercer 
71c03f87daSbmercer 	return betoh32(fh->fh_version);
72c03f87daSbmercer }
73c03f87daSbmercer 
74c03f87daSbmercer /*
75c03f87daSbmercer  * Initializes internal structures of module.
76c03f87daSbmercer  * Has to be called once, preferably in machdep.c.
77c03f87daSbmercer  */
78c03f87daSbmercer int
fdt_init(void * fdt)79c03f87daSbmercer fdt_init(void *fdt)
80c03f87daSbmercer {
81c03f87daSbmercer 	int version;
82c03f87daSbmercer 
83c03f87daSbmercer 	bzero(&tree, sizeof(struct fdt));
84c03f87daSbmercer 	tree_inited = 0;
85c03f87daSbmercer 
86c03f87daSbmercer 	if (!fdt)
87c03f87daSbmercer 		return 0;
88c03f87daSbmercer 
89c03f87daSbmercer 	if (!(version = fdt_check_head(fdt)))
90c03f87daSbmercer 		return 0;
91c03f87daSbmercer 
92c03f87daSbmercer 	tree.header = (struct fdt_head *)fdt;
93c03f87daSbmercer 	tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
94c03f87daSbmercer 	tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
95c03f87daSbmercer 	tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
961a409cc7Skettenis 	tree.end = (char *)fdt + betoh32(tree.header->fh_size);
97c03f87daSbmercer 	tree.version = version;
98c03f87daSbmercer 	tree.strings_size = betoh32(tree.header->fh_strings_size);
991a409cc7Skettenis 	if (tree.version >= 17)
1001a409cc7Skettenis 		tree.struct_size = betoh32(tree.header->fh_struct_size);
101c03f87daSbmercer 	tree_inited = 1;
102c03f87daSbmercer 
103c03f87daSbmercer 	return version;
104c03f87daSbmercer }
105c03f87daSbmercer 
1061a409cc7Skettenis void
fdt_finalize(void)1071a409cc7Skettenis fdt_finalize(void)
1081a409cc7Skettenis {
1091a409cc7Skettenis 	char *start = (char *)tree.header;
1101a409cc7Skettenis 
1111a409cc7Skettenis 	tree.header->fh_size = htobe32(tree.end - start);
1121a409cc7Skettenis 	tree.header->fh_struct_off = htobe32(tree.tree - start);
1131a409cc7Skettenis 	tree.header->fh_strings_off = htobe32(tree.strings - start);
1141a409cc7Skettenis 	tree.header->fh_reserve_off = htobe32(tree.memory - start);
1151a409cc7Skettenis 	tree.header->fh_strings_size = htobe32(tree.strings_size);
1161a409cc7Skettenis 	if (tree.version >= 17)
1171a409cc7Skettenis 		tree.header->fh_struct_size = htobe32(tree.struct_size);
1181a409cc7Skettenis }
1191a409cc7Skettenis 
120c03f87daSbmercer /*
121401e79efSpatrick  * Return the size of the FDT.
122401e79efSpatrick  */
123401e79efSpatrick size_t
fdt_get_size(void * fdt)124401e79efSpatrick fdt_get_size(void *fdt)
125401e79efSpatrick {
126401e79efSpatrick 	if (!fdt)
127401e79efSpatrick 		return 0;
128401e79efSpatrick 
129401e79efSpatrick 	if (!fdt_check_head(fdt))
130401e79efSpatrick 		return 0;
131401e79efSpatrick 
132401e79efSpatrick 	return betoh32(((struct fdt_head *)fdt)->fh_size);
133401e79efSpatrick }
134401e79efSpatrick 
135401e79efSpatrick /*
136c03f87daSbmercer  * Retrieve string pointer from strings table.
137c03f87daSbmercer  */
138c03f87daSbmercer char *
fdt_get_str(u_int32_t num)139c03f87daSbmercer fdt_get_str(u_int32_t num)
140c03f87daSbmercer {
141c03f87daSbmercer 	if (num > tree.strings_size)
142c03f87daSbmercer 		return NULL;
143c03f87daSbmercer 	return (tree.strings) ? (tree.strings + num) : NULL;
144c03f87daSbmercer }
145c03f87daSbmercer 
1461a409cc7Skettenis int
fdt_add_str(char * name)1471a409cc7Skettenis fdt_add_str(char *name)
1481a409cc7Skettenis {
1491a409cc7Skettenis 	size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
1501a409cc7Skettenis 	char *end = tree.strings + tree.strings_size;
1511a409cc7Skettenis 
1521a409cc7Skettenis 	memmove(end + len, end, tree.end - end);
1531a409cc7Skettenis 	tree.strings_size += len;
1541a409cc7Skettenis 	if (tree.tree > tree.strings)
1551a409cc7Skettenis 		tree.tree += len;
1561a409cc7Skettenis 	if (tree.memory > tree.strings)
1571a409cc7Skettenis 		tree.memory += len;
1581a409cc7Skettenis 	tree.end += len;
1591a409cc7Skettenis 	memset(end, 0, len);
1601a409cc7Skettenis 	memcpy(end, name, strlen(name));
1611a409cc7Skettenis 
1621a409cc7Skettenis 	return (end - tree.strings);
1631a409cc7Skettenis }
1641a409cc7Skettenis 
165c03f87daSbmercer /*
166c03f87daSbmercer  * Utility functions for skipping parts of tree.
167c03f87daSbmercer  */
168808ecccaSvisa 
169808ecccaSvisa void *
skip_nops(u_int32_t * ptr)170808ecccaSvisa skip_nops(u_int32_t *ptr)
171808ecccaSvisa {
172808ecccaSvisa 	while (betoh32(*ptr) == FDT_NOP)
173808ecccaSvisa 		ptr++;
174808ecccaSvisa 
175808ecccaSvisa 	return ptr;
176808ecccaSvisa }
177808ecccaSvisa 
178c03f87daSbmercer void *
skip_property(u_int32_t * ptr)179c03f87daSbmercer skip_property(u_int32_t *ptr)
180c03f87daSbmercer {
181c03f87daSbmercer 	u_int32_t size;
182c03f87daSbmercer 
183c03f87daSbmercer 	size = betoh32(*(ptr + 1));
184c03f87daSbmercer 	/* move forward by magic + size + nameid + rounded up property size */
185c03f87daSbmercer 	ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
186c03f87daSbmercer 
187808ecccaSvisa 	return skip_nops(ptr);
188c03f87daSbmercer }
189c03f87daSbmercer 
190c03f87daSbmercer void *
skip_props(u_int32_t * ptr)191c03f87daSbmercer skip_props(u_int32_t *ptr)
192c03f87daSbmercer {
193c03f87daSbmercer 	while (betoh32(*ptr) == FDT_PROPERTY) {
194c03f87daSbmercer 		ptr = skip_property(ptr);
195c03f87daSbmercer 	}
196c03f87daSbmercer 	return ptr;
197c03f87daSbmercer }
198c03f87daSbmercer 
199c03f87daSbmercer void *
skip_node_name(u_int32_t * ptr)200c03f87daSbmercer skip_node_name(u_int32_t *ptr)
201c03f87daSbmercer {
202c03f87daSbmercer 	/* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
203808ecccaSvisa 	ptr += roundup(strlen((char *)ptr) + 1,
204c03f87daSbmercer 	    sizeof(u_int32_t)) / sizeof(u_int32_t);
205808ecccaSvisa 
206808ecccaSvisa 	return skip_nops(ptr);
207c03f87daSbmercer }
208c03f87daSbmercer 
209c03f87daSbmercer /*
210c03f87daSbmercer  * Retrieves node property, the returned pointer is inside the fdt tree,
211c03f87daSbmercer  * so we should not modify content pointed by it directly.
212c03f87daSbmercer  */
213c03f87daSbmercer int
fdt_node_property(void * node,char * name,char ** out)214c03f87daSbmercer fdt_node_property(void *node, char *name, char **out)
215c03f87daSbmercer {
216c03f87daSbmercer 	u_int32_t *ptr;
217c03f87daSbmercer 	u_int32_t nameid;
218c03f87daSbmercer 	char *tmp;
219c03f87daSbmercer 
220c03f87daSbmercer 	if (!tree_inited)
22195ccb12bSkettenis 		return -1;
222c03f87daSbmercer 
223c03f87daSbmercer 	ptr = (u_int32_t *)node;
224c03f87daSbmercer 
225c03f87daSbmercer 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
22695ccb12bSkettenis 		return -1;
227c03f87daSbmercer 
228c03f87daSbmercer 	ptr = skip_node_name(ptr + 1);
229c03f87daSbmercer 
230c03f87daSbmercer 	while (betoh32(*ptr) == FDT_PROPERTY) {
231c03f87daSbmercer 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
232c03f87daSbmercer 		tmp = fdt_get_str(nameid);
233c03f87daSbmercer 		if (!strcmp(name, tmp)) {
234c03f87daSbmercer 			*out = (char *)(ptr + 3); /* beginning of the value */
235c03f87daSbmercer 			return betoh32(*(ptr + 1)); /* size of value */
236c03f87daSbmercer 		}
237c03f87daSbmercer 		ptr = skip_property(ptr);
238c03f87daSbmercer 	}
23995ccb12bSkettenis 	return -1;
240c03f87daSbmercer }
241c03f87daSbmercer 
2421a409cc7Skettenis int
fdt_node_set_property(void * node,char * name,void * data,int len)2431a409cc7Skettenis fdt_node_set_property(void *node, char *name, void *data, int len)
2441a409cc7Skettenis {
2451a409cc7Skettenis 	uint32_t *ptr, *next;
2461a409cc7Skettenis 	uint32_t nameid;
2471a409cc7Skettenis 	uint32_t curlen;
2481a409cc7Skettenis 	size_t delta;
2491a409cc7Skettenis 	char *tmp;
2501a409cc7Skettenis 
2511a409cc7Skettenis 	if (!tree_inited)
2521a409cc7Skettenis 		return 0;
2531a409cc7Skettenis 
2541a409cc7Skettenis 	ptr = (uint32_t *)node;
2551a409cc7Skettenis 
2561a409cc7Skettenis 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
2571a409cc7Skettenis 		return 0;
2581a409cc7Skettenis 
2591a409cc7Skettenis 	ptr = skip_node_name(ptr + 1);
2601a409cc7Skettenis 
2611a409cc7Skettenis 	while (betoh32(*ptr) == FDT_PROPERTY) {
2621a409cc7Skettenis 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
2631a409cc7Skettenis 		tmp = fdt_get_str(nameid);
2641a409cc7Skettenis 		next = skip_property(ptr);
2651a409cc7Skettenis 		if (!strcmp(name, tmp)) {
2661a409cc7Skettenis 			curlen = betoh32(*(ptr + 1));
2671a409cc7Skettenis 			delta = roundup(len, sizeof(uint32_t)) -
2681a409cc7Skettenis 			    roundup(curlen, sizeof(uint32_t));
2691a409cc7Skettenis 			memmove((char *)next + delta, next,
2701a409cc7Skettenis 			    tree.end - (char *)next);
2711a409cc7Skettenis 			tree.struct_size += delta;
2721a409cc7Skettenis 			if (tree.strings > tree.tree)
2731a409cc7Skettenis 				tree.strings += delta;
2741a409cc7Skettenis 			if (tree.memory > tree.tree)
2751a409cc7Skettenis 				tree.memory += delta;
2761a409cc7Skettenis 			tree.end += delta;
2771a409cc7Skettenis 			*(ptr + 1) = htobe32(len);
2781a409cc7Skettenis 			memcpy(ptr + 3, data, len);
2791a409cc7Skettenis 			return 1;
2801a409cc7Skettenis 		}
2811a409cc7Skettenis 		ptr = next;
2821a409cc7Skettenis 	}
2831a409cc7Skettenis 	return 0;
2841a409cc7Skettenis }
2851a409cc7Skettenis 
2861a409cc7Skettenis int
fdt_node_add_property(void * node,char * name,void * data,int len)2871a409cc7Skettenis fdt_node_add_property(void *node, char *name, void *data, int len)
2881a409cc7Skettenis {
2891a409cc7Skettenis 	char *dummy;
2901a409cc7Skettenis 
2911a409cc7Skettenis 	if (!tree_inited)
2921a409cc7Skettenis 		return 0;
2931a409cc7Skettenis 
2941a409cc7Skettenis 	if (fdt_node_property(node, name, &dummy) == -1) {
2951a409cc7Skettenis 		uint32_t *ptr = (uint32_t *)node;
2961a409cc7Skettenis 
2971a409cc7Skettenis 		if (betoh32(*ptr) != FDT_NODE_BEGIN)
2981a409cc7Skettenis 			return 0;
2991a409cc7Skettenis 
3001a409cc7Skettenis 		ptr = skip_node_name(ptr + 1);
3011a409cc7Skettenis 
3021a409cc7Skettenis 		memmove(ptr + 3, ptr, tree.end - (char *)ptr);
3031a409cc7Skettenis 		tree.struct_size += 3 * sizeof(uint32_t);
3041a409cc7Skettenis 		if (tree.strings > tree.tree)
3051a409cc7Skettenis 			tree.strings += 3 * sizeof(uint32_t);
3061a409cc7Skettenis 		if (tree.memory > tree.tree)
3071a409cc7Skettenis 			tree.memory += 3 * sizeof(uint32_t);
3081a409cc7Skettenis 		tree.end += 3 * sizeof(uint32_t);
3091a409cc7Skettenis 		*ptr++ = htobe32(FDT_PROPERTY);
3101a409cc7Skettenis 		*ptr++ = htobe32(0);
3111a409cc7Skettenis 		*ptr++ = htobe32(fdt_add_str(name));
3121a409cc7Skettenis 	}
3131a409cc7Skettenis 
3141a409cc7Skettenis 	return fdt_node_set_property(node, name, data, len);
3151a409cc7Skettenis }
3161a409cc7Skettenis 
317c03f87daSbmercer /*
318dd86c6a0Spatrick  * Retrieves next node, skipping all the children nodes of the pointed node,
319dd86c6a0Spatrick  * returns pointer to next node, no matter if it exists or not.
320dd86c6a0Spatrick  */
321dd86c6a0Spatrick void *
skip_node(void * node)322dd86c6a0Spatrick skip_node(void *node)
323dd86c6a0Spatrick {
324dd86c6a0Spatrick 	u_int32_t *ptr = node;
325dd86c6a0Spatrick 
326dd86c6a0Spatrick 	ptr++;
327dd86c6a0Spatrick 
328dd86c6a0Spatrick 	ptr = skip_node_name(ptr);
329dd86c6a0Spatrick 	ptr = skip_props(ptr);
330dd86c6a0Spatrick 
331dd86c6a0Spatrick 	/* skip children */
332dd86c6a0Spatrick 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
333dd86c6a0Spatrick 		ptr = skip_node(ptr);
334dd86c6a0Spatrick 
335808ecccaSvisa 	return skip_nops(ptr + 1);
336dd86c6a0Spatrick }
337dd86c6a0Spatrick 
338dd86c6a0Spatrick /*
339dd86c6a0Spatrick  * Retrieves next node, skipping all the children nodes of the pointed node,
340dd86c6a0Spatrick  * returns pointer to next node if exists, otherwise returns NULL.
341dd86c6a0Spatrick  * If passed 0 will return first node of the tree (root).
342c03f87daSbmercer  */
343c03f87daSbmercer void *
fdt_next_node(void * node)344c03f87daSbmercer fdt_next_node(void *node)
345c03f87daSbmercer {
346c03f87daSbmercer 	u_int32_t *ptr;
347c03f87daSbmercer 
348c03f87daSbmercer 	if (!tree_inited)
349c03f87daSbmercer 		return NULL;
350c03f87daSbmercer 
351c03f87daSbmercer 	ptr = node;
352c03f87daSbmercer 
353dd86c6a0Spatrick 	if (node == NULL) {
3541a409cc7Skettenis 		ptr = skip_nops((uint32_t *)tree.tree);
355c03f87daSbmercer 		return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
356c03f87daSbmercer 	}
357c03f87daSbmercer 
358c03f87daSbmercer 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
359c03f87daSbmercer 		return NULL;
360c03f87daSbmercer 
361c03f87daSbmercer 	ptr++;
362c03f87daSbmercer 
363c03f87daSbmercer 	ptr = skip_node_name(ptr);
364c03f87daSbmercer 	ptr = skip_props(ptr);
365c03f87daSbmercer 
366c03f87daSbmercer 	/* skip children */
367c03f87daSbmercer 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
368dd86c6a0Spatrick 		ptr = skip_node(ptr);
369c03f87daSbmercer 
370dd86c6a0Spatrick 	if (betoh32(*ptr) != FDT_NODE_END)
371dd86c6a0Spatrick 		return NULL;
372dd86c6a0Spatrick 
373808ecccaSvisa 	ptr = skip_nops(ptr + 1);
374808ecccaSvisa 
375808ecccaSvisa 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
376dd86c6a0Spatrick 		return NULL;
377dd86c6a0Spatrick 
378808ecccaSvisa 	return ptr;
379c03f87daSbmercer }
380c03f87daSbmercer 
381d571cddaSkettenis int
fdt_next_property(void * node,char * name,char ** nextname)382d571cddaSkettenis fdt_next_property(void *node, char *name, char **nextname)
383d571cddaSkettenis {
384d571cddaSkettenis 	u_int32_t *ptr;
385d571cddaSkettenis 	u_int32_t nameid;
386d571cddaSkettenis 
387d571cddaSkettenis 	if (!tree_inited)
388d571cddaSkettenis 		return 0;
389d571cddaSkettenis 
390d571cddaSkettenis 	ptr = (u_int32_t *)node;
391d571cddaSkettenis 
392d571cddaSkettenis 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
393d571cddaSkettenis 		return 0;
394d571cddaSkettenis 
395d571cddaSkettenis 	ptr = skip_node_name(ptr + 1);
396d571cddaSkettenis 
397d571cddaSkettenis 	while (betoh32(*ptr) == FDT_PROPERTY) {
398d571cddaSkettenis 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
399d571cddaSkettenis 		if (strcmp(name, "") == 0) {
400d571cddaSkettenis 			*nextname = fdt_get_str(nameid);
401d571cddaSkettenis 			return 1;
402d571cddaSkettenis 		}
403d571cddaSkettenis 		if (strcmp(name, fdt_get_str(nameid)) == 0) {
404d571cddaSkettenis 			ptr = skip_property(ptr);
405d571cddaSkettenis 			if (betoh32(*ptr) != FDT_PROPERTY)
406d571cddaSkettenis 				break;
407d571cddaSkettenis 			nameid = betoh32(*(ptr + 2));
408d571cddaSkettenis 			*nextname = fdt_get_str(nameid);
409d571cddaSkettenis 			return 1;
410d571cddaSkettenis 		}
411d571cddaSkettenis 		ptr = skip_property(ptr);
412d571cddaSkettenis 	}
413d571cddaSkettenis 	*nextname = "";
414d571cddaSkettenis 	return 1;
415d571cddaSkettenis }
416d571cddaSkettenis 
417c03f87daSbmercer /*
418401e79efSpatrick  * Retrieves node property as integers and puts them in the given
419401e79efSpatrick  * integer array.
420401e79efSpatrick  */
421401e79efSpatrick int
fdt_node_property_ints(void * node,char * name,int * out,int outlen)422401e79efSpatrick fdt_node_property_ints(void *node, char *name, int *out, int outlen)
423401e79efSpatrick {
424401e79efSpatrick 	int *data;
425401e79efSpatrick 	int i, inlen;
426401e79efSpatrick 
427401e79efSpatrick 	inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
428401e79efSpatrick 	if (inlen <= 0)
429401e79efSpatrick 		return -1;
430401e79efSpatrick 
431401e79efSpatrick 	for (i = 0; i < inlen && i < outlen; i++)
432401e79efSpatrick 		out[i] = betoh32(data[i]);
433401e79efSpatrick 
434401e79efSpatrick 	return i;
435401e79efSpatrick }
436401e79efSpatrick 
437401e79efSpatrick /*
438401e79efSpatrick  * Retrieves node property as an integer.
439401e79efSpatrick  */
440401e79efSpatrick int
fdt_node_property_int(void * node,char * name,int * out)441401e79efSpatrick fdt_node_property_int(void *node, char *name, int *out)
442401e79efSpatrick {
443401e79efSpatrick 	return fdt_node_property_ints(node, name, out, 1);
444401e79efSpatrick }
445401e79efSpatrick 
446401e79efSpatrick /*
447c03f87daSbmercer  * Retrieves next node, skipping all the children nodes of the pointed node
448c03f87daSbmercer  */
449c03f87daSbmercer void *
fdt_child_node(void * node)450c03f87daSbmercer fdt_child_node(void *node)
451c03f87daSbmercer {
452c03f87daSbmercer 	u_int32_t *ptr;
453c03f87daSbmercer 
454c03f87daSbmercer 	if (!tree_inited)
455c03f87daSbmercer 		return NULL;
456c03f87daSbmercer 
457c03f87daSbmercer 	ptr = node;
458c03f87daSbmercer 
459c03f87daSbmercer 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
460c03f87daSbmercer 		return NULL;
461c03f87daSbmercer 
462c03f87daSbmercer 	ptr++;
463c03f87daSbmercer 
464c03f87daSbmercer 	ptr = skip_node_name(ptr);
465c03f87daSbmercer 	ptr = skip_props(ptr);
466c03f87daSbmercer 	/* check if there is a child node */
467c03f87daSbmercer 	return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
468c03f87daSbmercer }
469c03f87daSbmercer 
470c03f87daSbmercer /*
471c03f87daSbmercer  * Retrieves node name.
472c03f87daSbmercer  */
473c03f87daSbmercer char *
fdt_node_name(void * node)474c03f87daSbmercer fdt_node_name(void *node)
475c03f87daSbmercer {
476c03f87daSbmercer 	u_int32_t *ptr;
477c03f87daSbmercer 
478c03f87daSbmercer 	if (!tree_inited)
479c03f87daSbmercer 		return NULL;
480c03f87daSbmercer 
481c03f87daSbmercer 	ptr = node;
482c03f87daSbmercer 
483c03f87daSbmercer 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
484c03f87daSbmercer 		return NULL;
485c03f87daSbmercer 
486c03f87daSbmercer 	return (char *)(ptr + 1);
487c03f87daSbmercer }
488c03f87daSbmercer 
489c03f87daSbmercer void *
fdt_find_node(char * name)490c03f87daSbmercer fdt_find_node(char *name)
491c03f87daSbmercer {
492c03f87daSbmercer 	void *node = fdt_next_node(0);
493c03f87daSbmercer 	const char *p = name;
494c03f87daSbmercer 
495c03f87daSbmercer 	if (!tree_inited)
496c03f87daSbmercer 		return NULL;
497c03f87daSbmercer 
498c03f87daSbmercer 	if (*p != '/')
499c03f87daSbmercer 		return NULL;
500c03f87daSbmercer 
501c03f87daSbmercer 	while (*p) {
502c03f87daSbmercer 		void *child;
503c03f87daSbmercer 		const char *q;
504*3fe5dae2Skettenis 		const char *s;
505c03f87daSbmercer 
506c03f87daSbmercer 		while (*p == '/')
507c03f87daSbmercer 			p++;
508c03f87daSbmercer 		if (*p == 0)
509c03f87daSbmercer 			return node;
510c03f87daSbmercer 		q = strchr(p, '/');
511c03f87daSbmercer 		if (q == NULL)
512c03f87daSbmercer 			q = p + strlen(p);
513c03f87daSbmercer 
514*3fe5dae2Skettenis 		/* Check for a complete match. */
515c03f87daSbmercer 		for (child = fdt_child_node(node); child;
516c03f87daSbmercer 		     child = fdt_next_node(child)) {
517*3fe5dae2Skettenis 			s = fdt_node_name(child);
518*3fe5dae2Skettenis 			if (strncmp(p, s, q - p) == 0 && s[q - p] == '\0')
519c03f87daSbmercer 				break;
520c03f87daSbmercer 		}
521*3fe5dae2Skettenis 		if (child) {
522*3fe5dae2Skettenis 			node = child;
523*3fe5dae2Skettenis 			p = q;
524*3fe5dae2Skettenis 			continue;
525c03f87daSbmercer 		}
526c03f87daSbmercer 
527*3fe5dae2Skettenis 		/* Check for a match without the unit name. */
528*3fe5dae2Skettenis 		for (child = fdt_child_node(node); child;
529*3fe5dae2Skettenis 		     child = fdt_next_node(child)) {
530*3fe5dae2Skettenis 			s = fdt_node_name(child);
531*3fe5dae2Skettenis 			if (strncmp(p, s, q - p) == 0 && s[q - p] == '@')
532*3fe5dae2Skettenis 				break;
533*3fe5dae2Skettenis 		}
534*3fe5dae2Skettenis 		if (child) {
535*3fe5dae2Skettenis 			node = child;
536c03f87daSbmercer 			p = q;
537*3fe5dae2Skettenis 			continue;
538*3fe5dae2Skettenis 		}
539*3fe5dae2Skettenis 
540*3fe5dae2Skettenis 		return NULL;	/* No match found. */
541c03f87daSbmercer 	}
542c03f87daSbmercer 
543c03f87daSbmercer 	return node;
544c03f87daSbmercer }
545c03f87daSbmercer 
546c03f87daSbmercer void *
fdt_parent_node_recurse(void * pnode,void * child)547c03f87daSbmercer fdt_parent_node_recurse(void *pnode, void *child)
548c03f87daSbmercer {
549c03f87daSbmercer 	void *node = fdt_child_node(pnode);
550c03f87daSbmercer 	void *tmp;
551c03f87daSbmercer 
552c03f87daSbmercer 	while (node && (node != child)) {
553c03f87daSbmercer 		if ((tmp = fdt_parent_node_recurse(node, child)))
554c03f87daSbmercer 			return tmp;
555c03f87daSbmercer 		node = fdt_next_node(node);
556c03f87daSbmercer 	}
557c03f87daSbmercer 	return (node) ? pnode : NULL;
558c03f87daSbmercer }
559c03f87daSbmercer 
560c03f87daSbmercer void *
fdt_parent_node(void * node)561c03f87daSbmercer fdt_parent_node(void *node)
562c03f87daSbmercer {
563c03f87daSbmercer 	void *pnode = fdt_next_node(0);
564c03f87daSbmercer 
565c03f87daSbmercer 	if (!tree_inited)
566c03f87daSbmercer 		return NULL;
567c03f87daSbmercer 
568b9dd9f55Smpi 	if (node == pnode)
569b9dd9f55Smpi 		return NULL;
570b9dd9f55Smpi 
571c03f87daSbmercer 	return fdt_parent_node_recurse(pnode, node);
572c03f87daSbmercer }
573c03f87daSbmercer 
574d0cd2b3aSkettenis void *
fdt_find_phandle_recurse(void * node,uint32_t phandle)575d0cd2b3aSkettenis fdt_find_phandle_recurse(void *node, uint32_t phandle)
576d0cd2b3aSkettenis {
577d0cd2b3aSkettenis 	void *child;
578d0cd2b3aSkettenis 	char *data;
579d0cd2b3aSkettenis 	void *tmp;
580d0cd2b3aSkettenis 	int len;
581d0cd2b3aSkettenis 
582d0cd2b3aSkettenis 	len = fdt_node_property(node, "phandle", &data);
583d0cd2b3aSkettenis 	if (len < 0)
584d0cd2b3aSkettenis 		len = fdt_node_property(node, "linux,phandle", &data);
585d0cd2b3aSkettenis 
586d0cd2b3aSkettenis 	if (len == sizeof(uint32_t) && bemtoh32(data) == phandle)
587d0cd2b3aSkettenis 		return node;
588d0cd2b3aSkettenis 
589d0cd2b3aSkettenis 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
590d0cd2b3aSkettenis 		if ((tmp = fdt_find_phandle_recurse(child, phandle)))
591d0cd2b3aSkettenis 			return tmp;
592d0cd2b3aSkettenis 
593d0cd2b3aSkettenis 	return NULL;
594d0cd2b3aSkettenis }
595d0cd2b3aSkettenis 
596d0cd2b3aSkettenis void *
fdt_find_phandle(uint32_t phandle)597d0cd2b3aSkettenis fdt_find_phandle(uint32_t phandle)
598d0cd2b3aSkettenis {
599d0cd2b3aSkettenis 	return fdt_find_phandle_recurse(fdt_next_node(0), phandle);
600d0cd2b3aSkettenis }
601d0cd2b3aSkettenis 
602598f9116Skettenis void
fdt_get_cells(void * node,int * ac,int * sc)603598f9116Skettenis fdt_get_cells(void *node, int *ac, int *sc)
604598f9116Skettenis {
605598f9116Skettenis 	void *parent;
606598f9116Skettenis 
607598f9116Skettenis 	parent = fdt_parent_node(node);
608598f9116Skettenis 	if (parent == NULL)
609598f9116Skettenis 		*ac = *sc = 1;
610598f9116Skettenis 	else
611598f9116Skettenis 		fdt_get_cells(parent, ac, sc);
612598f9116Skettenis 
613598f9116Skettenis 	fdt_node_property_int(node, "#address-cells", ac);
614598f9116Skettenis 	fdt_node_property_int(node, "#size-cells", sc);
615598f9116Skettenis }
616598f9116Skettenis 
617401e79efSpatrick /*
6187858eedaSpatrick  * Translate memory address depending on parent's range.
6197858eedaSpatrick  *
6207858eedaSpatrick  * Ranges are a way of mapping one address to another.  This ranges attribute
6217858eedaSpatrick  * is set on a node's parent.  This means if a node does not have a parent,
6227858eedaSpatrick  * there's nothing to translate.  If it does have a parent and the parent does
6237858eedaSpatrick  * not have a ranges attribute, there's nothing to translate either.
6247858eedaSpatrick  *
6257858eedaSpatrick  * If the parent has a ranges attribute and the attribute is not empty, the
6267858eedaSpatrick  * node's memory address has to be in one of the given ranges.  This range is
6277858eedaSpatrick  * then used to translate the memory address.
6287858eedaSpatrick  *
6297858eedaSpatrick  * If the parent has a ranges attribute, but the attribute is empty, there's
6307858eedaSpatrick  * nothing to translate.  But it's not a translation barrier.  It can be treated
6317858eedaSpatrick  * as a simple 1:1 mapping.
6327858eedaSpatrick  *
6337858eedaSpatrick  * Translation does not end here.  We need to check if the parent's parent also
6347858eedaSpatrick  * has a ranges attribute and ask the same questions again.
6357858eedaSpatrick  */
6367858eedaSpatrick int
fdt_translate_reg(void * node,struct fdt_reg * reg)637b7952965Spatrick fdt_translate_reg(void *node, struct fdt_reg *reg)
6387858eedaSpatrick {
6397858eedaSpatrick 	void *parent;
640598f9116Skettenis 	int pac, psc, ac, sc, rlen, rone, *range;
6417858eedaSpatrick 	uint64_t from, to, size;
6427858eedaSpatrick 
6437858eedaSpatrick 	/* No parent, no translation. */
6447858eedaSpatrick 	parent = fdt_parent_node(node);
6457858eedaSpatrick 	if (parent == NULL)
6467858eedaSpatrick 		return 0;
6477858eedaSpatrick 
6487858eedaSpatrick 	/* Extract ranges property from node. */
6497858eedaSpatrick 	rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
6507858eedaSpatrick 
6517858eedaSpatrick 	/* No ranges means translation barrier. Translation stops here. */
6527858eedaSpatrick 	if (range == NULL)
6537858eedaSpatrick 		return 0;
6547858eedaSpatrick 
6557858eedaSpatrick 	/* Empty ranges means 1:1 mapping. Continue translation on parent. */
6567858eedaSpatrick 	if (rlen <= 0)
657b7952965Spatrick 		return fdt_translate_reg(parent, reg);
6587858eedaSpatrick 
659598f9116Skettenis 	/*
660598f9116Skettenis 	 * Get parent address/size width.  We only support 32-bit (1)
661598f9116Skettenis 	 * and 64-bit (2) wide addresses and sizes here.
662598f9116Skettenis 	 */
663598f9116Skettenis 	fdt_get_cells(parent, &pac, &psc);
664598f9116Skettenis 	if (pac <= 0 || pac > 2 || psc <= 0 || psc > 2)
6657858eedaSpatrick 		return EINVAL;
6667858eedaSpatrick 
667598f9116Skettenis 	/*
668598f9116Skettenis 	 * Get our own address/size width.  Again, we only support
669598f9116Skettenis 	 * 32-bit (1) and 64-bit (2) wide addresses and sizes here.
670598f9116Skettenis 	 */
671598f9116Skettenis 	fdt_get_cells(node, &ac, &sc);
672598f9116Skettenis 	if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
6737858eedaSpatrick 		return EINVAL;
6747858eedaSpatrick 
6757858eedaSpatrick 	/* Must have at least one range. */
6767858eedaSpatrick 	rone = pac + ac + sc;
6777858eedaSpatrick 	if (rlen < rone)
6787858eedaSpatrick 		return ESRCH;
6797858eedaSpatrick 
6807858eedaSpatrick 	/* For each range. */
6817858eedaSpatrick 	for (; rlen >= rone; rlen -= rone, range += rone) {
6827858eedaSpatrick 		/* Extract from and size, so we can see if we fit. */
6837858eedaSpatrick 		from = betoh32(range[0]);
6847858eedaSpatrick 		if (ac == 2)
6857858eedaSpatrick 			from = (from << 32) + betoh32(range[1]);
6867858eedaSpatrick 		size = betoh32(range[ac + pac]);
6877858eedaSpatrick 		if (sc == 2)
6887858eedaSpatrick 			size = (size << 32) + betoh32(range[ac + pac + 1]);
6897858eedaSpatrick 
6907858eedaSpatrick 		/* Try next, if we're not in the range. */
691b7952965Spatrick 		if (reg->addr < from || (reg->addr + reg->size) > (from + size))
6927858eedaSpatrick 			continue;
6937858eedaSpatrick 
6947858eedaSpatrick 		/* All good, extract to address and translate. */
6957858eedaSpatrick 		to = betoh32(range[ac]);
6967858eedaSpatrick 		if (pac == 2)
6977858eedaSpatrick 			to = (to << 32) + betoh32(range[ac + 1]);
6987858eedaSpatrick 
699b7952965Spatrick 		reg->addr -= from;
700b7952965Spatrick 		reg->addr += to;
701b7952965Spatrick 		return fdt_translate_reg(parent, reg);
7027858eedaSpatrick 	}
7037858eedaSpatrick 
7047858eedaSpatrick 	/* To be successful, we must have returned in the for-loop. */
7057858eedaSpatrick 	return ESRCH;
7067858eedaSpatrick }
7077858eedaSpatrick 
7087858eedaSpatrick /*
709401e79efSpatrick  * Parse the memory address and size of a node.
710401e79efSpatrick  */
711401e79efSpatrick int
fdt_get_reg(void * node,int idx,struct fdt_reg * reg)712b7952965Spatrick fdt_get_reg(void *node, int idx, struct fdt_reg *reg)
713401e79efSpatrick {
714401e79efSpatrick 	void *parent;
715598f9116Skettenis 	int ac, sc, off, *in, inlen;
716401e79efSpatrick 
717b7952965Spatrick 	if (node == NULL || reg == NULL)
718ed972280Spatrick 		return EINVAL;
719401e79efSpatrick 
720401e79efSpatrick 	parent = fdt_parent_node(node);
721401e79efSpatrick 	if (parent == NULL)
722ed972280Spatrick 		return EINVAL;
723401e79efSpatrick 
724598f9116Skettenis 	/*
725598f9116Skettenis 	 * Get parent address/size width.  We only support 32-bit (1)
726598f9116Skettenis 	 * and 64-bit (2) wide addresses and sizes here.
727598f9116Skettenis 	 */
728598f9116Skettenis 	fdt_get_cells(parent, &ac, &sc);
729598f9116Skettenis 	if (ac <= 0 || ac > 2 || sc <= 0 || sc > 2)
730ed972280Spatrick 		return EINVAL;
731401e79efSpatrick 
732401e79efSpatrick 	inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
733401e79efSpatrick 	if (inlen < ((idx + 1) * (ac + sc)))
734ed972280Spatrick 		return EINVAL;
735401e79efSpatrick 
736401e79efSpatrick 	off = idx * (ac + sc);
737401e79efSpatrick 
738b7952965Spatrick 	reg->addr = betoh32(in[off]);
739401e79efSpatrick 	if (ac == 2)
740b7952965Spatrick 		reg->addr = (reg->addr << 32) + betoh32(in[off + 1]);
741401e79efSpatrick 
742b7952965Spatrick 	reg->size = betoh32(in[off + ac]);
743401e79efSpatrick 	if (sc == 2)
744b7952965Spatrick 		reg->size = (reg->size << 32) + betoh32(in[off + ac + 1]);
745401e79efSpatrick 
746b7952965Spatrick 	return fdt_translate_reg(parent, reg);
747401e79efSpatrick }
748401e79efSpatrick 
749557e6971Sjsg int
fdt_is_compatible(void * node,const char * name)750557e6971Sjsg fdt_is_compatible(void *node, const char *name)
751557e6971Sjsg {
752557e6971Sjsg 	char *data;
753557e6971Sjsg 	int len;
754557e6971Sjsg 
755557e6971Sjsg 	len = fdt_node_property(node, "compatible", &data);
756557e6971Sjsg 	while (len > 0) {
757557e6971Sjsg 		if (strcmp(data, name) == 0)
758557e6971Sjsg 			return 1;
759557e6971Sjsg 		len -= strlen(data) + 1;
760557e6971Sjsg 		data += strlen(data) + 1;
761557e6971Sjsg 	}
762557e6971Sjsg 
763557e6971Sjsg 	return 0;
764557e6971Sjsg }
765557e6971Sjsg 
766c03f87daSbmercer #ifdef DEBUG
767c03f87daSbmercer /*
7684b1a56afSjsg  * Debug methods for printing whole tree, particular nodes and properties
769c03f87daSbmercer  */
770c03f87daSbmercer void *
fdt_print_property(void * node,int level)771c03f87daSbmercer fdt_print_property(void *node, int level)
772c03f87daSbmercer {
773c03f87daSbmercer 	u_int32_t *ptr;
774c03f87daSbmercer 	char *tmp, *value;
775c03f87daSbmercer 	int cnt;
776c03f87daSbmercer 	u_int32_t nameid, size;
777c03f87daSbmercer 
778c03f87daSbmercer 	ptr = (u_int32_t *)node;
779c03f87daSbmercer 
780c03f87daSbmercer 	if (!tree_inited)
781c03f87daSbmercer 		return NULL;
782c03f87daSbmercer 
783c03f87daSbmercer 	if (betoh32(*ptr) != FDT_PROPERTY)
784c03f87daSbmercer 		return ptr; /* should never happen */
785c03f87daSbmercer 
786c03f87daSbmercer 	/* extract property name_id and size */
787c03f87daSbmercer 	size = betoh32(*++ptr);
788c03f87daSbmercer 	nameid = betoh32(*++ptr);
789c03f87daSbmercer 
790c03f87daSbmercer 	for (cnt = 0; cnt < level; cnt++)
791c03f87daSbmercer 		printf("\t");
792c03f87daSbmercer 
793c03f87daSbmercer 	tmp = fdt_get_str(nameid);
794c03f87daSbmercer 	printf("\t%s : ", tmp ? tmp : "NO_NAME");
795c03f87daSbmercer 
796c03f87daSbmercer 	ptr++;
797c03f87daSbmercer 	value = (char *)ptr;
798c03f87daSbmercer 
799c03f87daSbmercer 	if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
800c03f87daSbmercer 	    !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
801c03f87daSbmercer 	    !strcmp(tmp, "linux,stdout-path")) {
802c03f87daSbmercer 		printf("%s", value);
803c03f87daSbmercer 	} else if (!strcmp(tmp, "clock-frequency") ||
804c03f87daSbmercer 	    !strcmp(tmp, "timebase-frequency")) {
805c03f87daSbmercer 		printf("%d", betoh32(*((unsigned int *)value)));
806c03f87daSbmercer 	} else {
807c03f87daSbmercer 		for (cnt = 0; cnt < size; cnt++) {
808c03f87daSbmercer 			if ((cnt % sizeof(u_int32_t)) == 0)
809c03f87daSbmercer 				printf(" ");
810c03f87daSbmercer 			printf("%02x", value[cnt]);
811c03f87daSbmercer 		}
812c03f87daSbmercer 	}
813c03f87daSbmercer 	ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
814c03f87daSbmercer 	printf("\n");
815c03f87daSbmercer 
816c03f87daSbmercer 	return ptr;
817c03f87daSbmercer }
818c03f87daSbmercer 
819c03f87daSbmercer void
fdt_print_node(void * node,int level)820c03f87daSbmercer fdt_print_node(void *node, int level)
821c03f87daSbmercer {
822c03f87daSbmercer 	u_int32_t *ptr;
823c03f87daSbmercer 	int cnt;
824c03f87daSbmercer 
825c03f87daSbmercer 	ptr = (u_int32_t *)node;
826c03f87daSbmercer 
827c03f87daSbmercer 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
828c03f87daSbmercer 		return;
829c03f87daSbmercer 
830c03f87daSbmercer 	ptr++;
831c03f87daSbmercer 
832c03f87daSbmercer 	for (cnt = 0; cnt < level; cnt++)
833c03f87daSbmercer 		printf("\t");
834c03f87daSbmercer 	printf("%s :\n", fdt_node_name(node));
835c03f87daSbmercer 	ptr = skip_node_name(ptr);
836c03f87daSbmercer 
837c03f87daSbmercer 	while (betoh32(*ptr) == FDT_PROPERTY)
838c03f87daSbmercer 		ptr = fdt_print_property(ptr, level);
839c03f87daSbmercer }
840c03f87daSbmercer 
841c03f87daSbmercer void
fdt_print_node_recurse(void * node,int level)842c03f87daSbmercer fdt_print_node_recurse(void *node, int level)
843c03f87daSbmercer {
844c03f87daSbmercer 	void *child;
845c03f87daSbmercer 
846c03f87daSbmercer 	fdt_print_node(node, level);
847c03f87daSbmercer 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
848c03f87daSbmercer 		fdt_print_node_recurse(child, level + 1);
849c03f87daSbmercer }
850c03f87daSbmercer 
851c03f87daSbmercer void
fdt_print_tree(void)852c03f87daSbmercer fdt_print_tree(void)
853c03f87daSbmercer {
854c03f87daSbmercer 	fdt_print_node_recurse(fdt_next_node(0), 0);
855c03f87daSbmercer }
856c03f87daSbmercer #endif
857c03f87daSbmercer 
858c03f87daSbmercer int
OF_peer(int handle)859c03f87daSbmercer OF_peer(int handle)
860c03f87daSbmercer {
861c03f87daSbmercer 	void *node = (char *)tree.header + handle;
862c03f87daSbmercer 
863c03f87daSbmercer 	if (handle == 0)
864c03f87daSbmercer 		node = fdt_find_node("/");
865c03f87daSbmercer 	else
866c03f87daSbmercer 		node = fdt_next_node(node);
867c03f87daSbmercer 	return node ? ((char *)node - (char *)tree.header) : 0;
868c03f87daSbmercer }
869c03f87daSbmercer 
870c03f87daSbmercer int
OF_child(int handle)871c03f87daSbmercer OF_child(int handle)
872c03f87daSbmercer {
873c03f87daSbmercer 	void *node = (char *)tree.header + handle;
874c03f87daSbmercer 
875c03f87daSbmercer 	node = fdt_child_node(node);
876c03f87daSbmercer 	return node ? ((char *)node - (char *)tree.header) : 0;
877c03f87daSbmercer }
878c03f87daSbmercer 
879c03f87daSbmercer int
OF_parent(int handle)880c03f87daSbmercer OF_parent(int handle)
881c03f87daSbmercer {
882c03f87daSbmercer 	void *node = (char *)tree.header + handle;
883c03f87daSbmercer 
884c03f87daSbmercer 	node = fdt_parent_node(node);
885c03f87daSbmercer 	return node ? ((char *)node - (char *)tree.header) : 0;
886c03f87daSbmercer }
887c03f87daSbmercer 
888c03f87daSbmercer int
OF_finddevice(char * name)889c03f87daSbmercer OF_finddevice(char *name)
890c03f87daSbmercer {
891c03f87daSbmercer 	void *node;
892c03f87daSbmercer 
893c03f87daSbmercer 	node = fdt_find_node(name);
894c03f87daSbmercer 	return node ? ((char *)node - (char *)tree.header) : -1;
895c03f87daSbmercer }
896c03f87daSbmercer 
897c03f87daSbmercer int
OF_getnodebyname(int handle,const char * name)898d571cddaSkettenis OF_getnodebyname(int handle, const char *name)
899d571cddaSkettenis {
900d571cddaSkettenis 	void *node = (char *)tree.header + handle;
901e807a99bSpatrick 	void *child;
902e807a99bSpatrick 	char *data;
903e807a99bSpatrick 	int len;
904d571cddaSkettenis 
905d571cddaSkettenis 	if (handle == 0)
906d571cddaSkettenis 		node = fdt_find_node("/");
907d571cddaSkettenis 
908e807a99bSpatrick 	for (child = fdt_child_node(node); child;
909e807a99bSpatrick 	     child = fdt_next_node(child)) {
910e807a99bSpatrick 		if (strcmp(name, fdt_node_name(child)) == 0)
911d571cddaSkettenis 			break;
912d571cddaSkettenis 	}
913e807a99bSpatrick 	if (child)
914e807a99bSpatrick 		return (char *)child - (char *)tree.header;
915d571cddaSkettenis 
916e807a99bSpatrick 	len = strlen(name);
917e807a99bSpatrick 	for (child = fdt_child_node(node); child;
918e807a99bSpatrick 	     child = fdt_next_node(child)) {
919e807a99bSpatrick 		data = fdt_node_name(child);
920e807a99bSpatrick 		if (strncmp(name, data, len) == 0 &&
921e807a99bSpatrick 		    strlen(data) > len && data[len] == '@')
922e807a99bSpatrick 			break;
923e807a99bSpatrick 	}
924e807a99bSpatrick 	if (child)
925e807a99bSpatrick 		return (char *)child - (char *)tree.header;
926e807a99bSpatrick 
927e807a99bSpatrick 	return 0;
928d571cddaSkettenis }
929d571cddaSkettenis 
930d571cddaSkettenis int
OF_getnodebyphandle(uint32_t phandle)931d0cd2b3aSkettenis OF_getnodebyphandle(uint32_t phandle)
932d0cd2b3aSkettenis {
933d0cd2b3aSkettenis 	void *node;
934d0cd2b3aSkettenis 
935d0cd2b3aSkettenis 	node = fdt_find_phandle(phandle);
936d0cd2b3aSkettenis 	return node ? ((char *)node - (char *)tree.header) : 0;
937d0cd2b3aSkettenis }
938d0cd2b3aSkettenis 
939d0cd2b3aSkettenis int
OF_getproplen(int handle,char * prop)940c03f87daSbmercer OF_getproplen(int handle, char *prop)
941c03f87daSbmercer {
942c03f87daSbmercer 	void *node = (char *)tree.header + handle;
943d571cddaSkettenis 	char *data, *name;
944d571cddaSkettenis 	int len;
945c03f87daSbmercer 
946d571cddaSkettenis 	len = fdt_node_property(node, prop, &data);
947d571cddaSkettenis 
948d571cddaSkettenis 	/*
949d571cddaSkettenis 	 * The "name" property is optional since version 16 of the
950d571cddaSkettenis 	 * flattened device tree specification, so we synthesize one
951d571cddaSkettenis 	 * from the unit name of the node if it is missing.
952d571cddaSkettenis 	 */
95395ccb12bSkettenis 	if (len < 0 && strcmp(prop, "name") == 0) {
954d571cddaSkettenis 		name = fdt_node_name(node);
955d571cddaSkettenis 		data = strchr(name, '@');
956d571cddaSkettenis 		if (data)
957d571cddaSkettenis 			len = data - name;
958d571cddaSkettenis 		else
959d571cddaSkettenis 			len = strlen(name);
960d571cddaSkettenis 		return len + 1;
961d571cddaSkettenis 	}
962d571cddaSkettenis 
963d571cddaSkettenis 	return len;
964c03f87daSbmercer }
965c03f87daSbmercer 
966c03f87daSbmercer int
OF_getprop(int handle,char * prop,void * buf,int buflen)967c03f87daSbmercer OF_getprop(int handle, char *prop, void *buf, int buflen)
968c03f87daSbmercer {
969c03f87daSbmercer 	void *node = (char *)tree.header + handle;
970c03f87daSbmercer 	char *data;
971c03f87daSbmercer 	int len;
972c03f87daSbmercer 
973c03f87daSbmercer 	len = fdt_node_property(node, prop, &data);
974c03f87daSbmercer 
975c03f87daSbmercer 	/*
976c03f87daSbmercer 	 * The "name" property is optional since version 16 of the
977c03f87daSbmercer 	 * flattened device tree specification, so we synthesize one
978c03f87daSbmercer 	 * from the unit name of the node if it is missing.
979c03f87daSbmercer 	 */
98095ccb12bSkettenis 	if (len < 0 && strcmp(prop, "name") == 0) {
981c03f87daSbmercer 		data = fdt_node_name(node);
982c03f87daSbmercer 		if (data) {
983c03f87daSbmercer 			len = strlcpy(buf, data, buflen);
984c03f87daSbmercer 			data = strchr(buf, '@');
98572a32a39Skettenis 			if (data) {
986c03f87daSbmercer 				*data = 0;
98772a32a39Skettenis 				len = data - (char *)buf;
98872a32a39Skettenis 			}
989c03f87daSbmercer 			return len + 1;
990c03f87daSbmercer 		}
991c03f87daSbmercer 	}
992c03f87daSbmercer 
993c03f87daSbmercer 	if (len > 0)
994c03f87daSbmercer 		memcpy(buf, data, min(len, buflen));
995c03f87daSbmercer 	return len;
996c03f87daSbmercer }
997c3d9d24eSkettenis 
9983ff54624Skettenis int
OF_getpropbool(int handle,char * prop)9993ff54624Skettenis OF_getpropbool(int handle, char *prop)
10003ff54624Skettenis {
10013ff54624Skettenis 	void *node = (char *)tree.header + handle;
10023ff54624Skettenis 	char *data;
10033ff54624Skettenis 
10043ff54624Skettenis 	return (fdt_node_property(node, prop, &data) >= 0);
10053ff54624Skettenis }
10063ff54624Skettenis 
1007bf5fea1eSkettenis uint32_t
OF_getpropint(int handle,char * prop,uint32_t defval)1008bf5fea1eSkettenis OF_getpropint(int handle, char *prop, uint32_t defval)
1009bf5fea1eSkettenis {
1010bf5fea1eSkettenis 	uint32_t val;
1011bf5fea1eSkettenis 	int len;
1012bf5fea1eSkettenis 
1013bf5fea1eSkettenis 	len = OF_getprop(handle, prop, &val, sizeof(val));
1014bf5fea1eSkettenis 	if (len != sizeof(val))
1015bf5fea1eSkettenis 		return defval;
1016bf5fea1eSkettenis 
1017bf5fea1eSkettenis 	return betoh32(val);
1018bf5fea1eSkettenis }
1019bf5fea1eSkettenis 
1020bf5fea1eSkettenis int
OF_getpropintarray(int handle,char * prop,uint32_t * buf,int buflen)1021bf5fea1eSkettenis OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen)
1022bf5fea1eSkettenis {
1023bf5fea1eSkettenis 	int len;
1024bf5fea1eSkettenis 	int i;
1025bf5fea1eSkettenis 
1026bf5fea1eSkettenis 	len = OF_getprop(handle, prop, buf, buflen);
1027bf5fea1eSkettenis 	if (len < 0 || (len % sizeof(uint32_t)))
1028bf5fea1eSkettenis 		return -1;
1029bf5fea1eSkettenis 
10303685e9f5Skettenis 	for (i = 0; i < min(len, buflen) / sizeof(uint32_t); i++)
1031bf5fea1eSkettenis 		buf[i] = betoh32(buf[i]);
1032bf5fea1eSkettenis 
1033bf5fea1eSkettenis 	return len;
1034bf5fea1eSkettenis }
1035bf5fea1eSkettenis 
10362e72ff40Skettenis uint64_t
OF_getpropint64(int handle,char * prop,uint64_t defval)10372e72ff40Skettenis OF_getpropint64(int handle, char *prop, uint64_t defval)
10382e72ff40Skettenis {
10392e72ff40Skettenis 	uint64_t val;
10402e72ff40Skettenis 	int len;
10412e72ff40Skettenis 
10422e72ff40Skettenis 	len = OF_getprop(handle, prop, &val, sizeof(val));
10432e72ff40Skettenis 	if (len != sizeof(val))
10442e72ff40Skettenis 		return defval;
10452e72ff40Skettenis 
10462e72ff40Skettenis 	return betoh64(val);
10472e72ff40Skettenis }
10482e72ff40Skettenis 
1049c3d9d24eSkettenis int
OF_getpropint64array(int handle,char * prop,uint64_t * buf,int buflen)1050196daab3Sgkoehler OF_getpropint64array(int handle, char *prop, uint64_t *buf, int buflen)
1051196daab3Sgkoehler {
1052196daab3Sgkoehler 	int len;
1053196daab3Sgkoehler 	int i;
1054196daab3Sgkoehler 
1055196daab3Sgkoehler 	len = OF_getprop(handle, prop, buf, buflen);
1056196daab3Sgkoehler 	if (len < 0 || (len % sizeof(uint64_t)))
1057196daab3Sgkoehler 		return -1;
1058196daab3Sgkoehler 
10595fece840Sjasper 	for (i = 0; i < min(len, buflen) / sizeof(uint64_t); i++)
1060196daab3Sgkoehler 		buf[i] = betoh64(buf[i]);
1061196daab3Sgkoehler 
1062196daab3Sgkoehler 	return len;
1063196daab3Sgkoehler }
1064196daab3Sgkoehler 
1065196daab3Sgkoehler int
OF_nextprop(int handle,char * prop,void * nextprop)1066d571cddaSkettenis OF_nextprop(int handle, char *prop, void *nextprop)
1067d571cddaSkettenis {
1068d571cddaSkettenis 	void *node = (char *)tree.header + handle;
1069d571cddaSkettenis 	char *data;
1070d571cddaSkettenis 
1071ed9c5852Skettenis 	if (fdt_node_property(node, "name", &data) == -1) {
1072d571cddaSkettenis 		if (strcmp(prop, "") == 0)
107344d8669dSkettenis 			return strlcpy(nextprop, "name", OFMAXPARAM);
1074d571cddaSkettenis 		if (strcmp(prop, "name") == 0)
1075d571cddaSkettenis 			prop = "";
1076d571cddaSkettenis 	}
1077d571cddaSkettenis 
1078d571cddaSkettenis 	if (fdt_next_property(node, prop, &data))
107944d8669dSkettenis 		return strlcpy(nextprop, data, OFMAXPARAM);
1080d571cddaSkettenis 	return -1;
1081d571cddaSkettenis }
1082d571cddaSkettenis 
1083d571cddaSkettenis int
OF_is_compatible(int handle,const char * name)1084c3d9d24eSkettenis OF_is_compatible(int handle, const char *name)
1085c3d9d24eSkettenis {
1086c3d9d24eSkettenis 	void *node = (char *)tree.header + handle;
1087557e6971Sjsg 	return (fdt_is_compatible(node, name));
1088c3d9d24eSkettenis }
1089c3d9d24eSkettenis 
109050946bcbSkettenis int
OF_getindex(int handle,const char * entry,const char * prop)109150946bcbSkettenis OF_getindex(int handle, const char *entry, const char *prop)
109250946bcbSkettenis {
109350946bcbSkettenis 	char *names;
109450946bcbSkettenis 	char *name;
109550946bcbSkettenis 	char *end;
109650946bcbSkettenis 	int idx = 0;
109750946bcbSkettenis 	int len;
109850946bcbSkettenis 
109950946bcbSkettenis 	if (entry == NULL)
110050946bcbSkettenis 		return 0;
110150946bcbSkettenis 
110250946bcbSkettenis 	len = OF_getproplen(handle, (char *)prop);
110350946bcbSkettenis 	if (len <= 0)
110450946bcbSkettenis 		return -1;
110550946bcbSkettenis 
110650946bcbSkettenis 	names = malloc(len, M_TEMP, M_WAITOK);
110750946bcbSkettenis 	OF_getprop(handle, (char *)prop, names, len);
110850946bcbSkettenis 	end = names + len;
110950946bcbSkettenis 	name = names;
111050946bcbSkettenis 	while (name < end) {
111150946bcbSkettenis 		if (strcmp(name, entry) == 0) {
111250946bcbSkettenis 			free(names, M_TEMP, len);
111350946bcbSkettenis 			return idx;
111450946bcbSkettenis 		}
111550946bcbSkettenis 		name += strlen(name) + 1;
111650946bcbSkettenis 		idx++;
111750946bcbSkettenis 	}
111850946bcbSkettenis 	free(names, M_TEMP, len);
111950946bcbSkettenis 	return -1;
112050946bcbSkettenis }
1121