xref: /openbsd-src/sys/dev/ofw/fdt.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: fdt.c,v 1.17 2016/07/09 12:31:05 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
5  * Copyright (c) 2009 Mark Kettenis <kettenis@sfires.net>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 
24 #include <dev/ofw/fdt.h>
25 #include <dev/ofw/openfirm.h>
26 
27 /* XXX */
28 #define OPROMMAXPARAM	32
29 
30 unsigned int fdt_check_head(void *);
31 char	*fdt_get_str(u_int32_t);
32 void	*skip_property(u_int32_t *);
33 void	*skip_props(u_int32_t *);
34 void	*skip_node_name(u_int32_t *);
35 void	*skip_node(void *);
36 void	*skip_nops(u_int32_t *);
37 void	*fdt_parent_node_recurse(void *, void *);
38 void	*fdt_find_phandle_recurse(void *, uint32_t);
39 int	 fdt_node_property_int(void *, char *, int *);
40 int	 fdt_node_property_ints(void *, char *, int *, int);
41 int	 fdt_translate_memory_address(void *, struct fdt_memory *);
42 #ifdef DEBUG
43 void 	 fdt_print_node_recurse(void *, int);
44 #endif
45 
46 static int tree_inited = 0;
47 static struct fdt tree;
48 
49 unsigned int
50 fdt_check_head(void *fdt)
51 {
52 	struct fdt_head *fh;
53 	u_int32_t *ptr, *tok;
54 
55 	fh = fdt;
56 	ptr = (u_int32_t *)fdt;
57 
58 	if (betoh32(fh->fh_magic) != FDT_MAGIC)
59 		return 0;
60 
61 	if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
62 		return 0;
63 
64 	tok = skip_nops(ptr + (betoh32(fh->fh_struct_off) / 4));
65 	if (betoh32(*tok) != FDT_NODE_BEGIN)
66 		return 0;
67 
68 	/* check for end signature on version 17 blob */
69 	if ((betoh32(fh->fh_version) >= 17) &&
70 	    (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
71 	    (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
72 		return 0;
73 
74 	return betoh32(fh->fh_version);
75 }
76 
77 /*
78  * Initializes internal structures of module.
79  * Has to be called once, preferably in machdep.c.
80  */
81 int
82 fdt_init(void *fdt)
83 {
84 	int version;
85 
86 	bzero(&tree, sizeof(struct fdt));
87 	tree_inited = 0;
88 
89 	if (!fdt)
90 		return 0;
91 
92 	if (!(version = fdt_check_head(fdt)))
93 		return 0;
94 
95 	tree.header = (struct fdt_head *)fdt;
96 	tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
97 	tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
98 	tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
99 	tree.version = version;
100 	tree.strings_size = betoh32(tree.header->fh_strings_size);
101 	tree_inited = 1;
102 
103 	return version;
104 }
105 
106 /*
107  * Return the size of the FDT.
108  */
109 size_t
110 fdt_get_size(void *fdt)
111 {
112 	if (!fdt)
113 		return 0;
114 
115 	if (!fdt_check_head(fdt))
116 		return 0;
117 
118 	return betoh32(((struct fdt_head *)fdt)->fh_size);
119 }
120 
121 /*
122  * Retrieve string pointer from strings table.
123  */
124 char *
125 fdt_get_str(u_int32_t num)
126 {
127 	if (num > tree.strings_size)
128 		return NULL;
129 	return (tree.strings) ? (tree.strings + num) : NULL;
130 }
131 
132 /*
133  * Utility functions for skipping parts of tree.
134  */
135 
136 void *
137 skip_nops(u_int32_t *ptr)
138 {
139 	while (betoh32(*ptr) == FDT_NOP)
140 		ptr++;
141 
142 	return ptr;
143 }
144 
145 void *
146 skip_property(u_int32_t *ptr)
147 {
148 	u_int32_t size;
149 
150 	size = betoh32(*(ptr + 1));
151 	/* move forward by magic + size + nameid + rounded up property size */
152 	ptr += 3 + roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
153 
154 	return skip_nops(ptr);
155 }
156 
157 void *
158 skip_props(u_int32_t *ptr)
159 {
160 	while (betoh32(*ptr) == FDT_PROPERTY) {
161 		ptr = skip_property(ptr);
162 	}
163 	return ptr;
164 }
165 
166 void *
167 skip_node_name(u_int32_t *ptr)
168 {
169 	/* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
170 	ptr += roundup(strlen((char *)ptr) + 1,
171 	    sizeof(u_int32_t)) / sizeof(u_int32_t);
172 
173 	return skip_nops(ptr);
174 }
175 
176 /*
177  * Retrieves node property, the returned pointer is inside the fdt tree,
178  * so we should not modify content pointed by it directly.
179  */
180 int
181 fdt_node_property(void *node, char *name, char **out)
182 {
183 	u_int32_t *ptr;
184 	u_int32_t nameid;
185 	char *tmp;
186 
187 	if (!tree_inited)
188 		return -1;
189 
190 	ptr = (u_int32_t *)node;
191 
192 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
193 		return -1;
194 
195 	ptr = skip_node_name(ptr + 1);
196 
197 	while (betoh32(*ptr) == FDT_PROPERTY) {
198 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
199 		tmp = fdt_get_str(nameid);
200 		if (!strcmp(name, tmp)) {
201 			*out = (char *)(ptr + 3); /* beginning of the value */
202 			return betoh32(*(ptr + 1)); /* size of value */
203 		}
204 		ptr = skip_property(ptr);
205 	}
206 	return -1;
207 }
208 
209 /*
210  * Retrieves next node, skipping all the children nodes of the pointed node,
211  * returns pointer to next node, no matter if it exists or not.
212  */
213 void *
214 skip_node(void *node)
215 {
216 	u_int32_t *ptr = node;
217 
218 	ptr++;
219 
220 	ptr = skip_node_name(ptr);
221 	ptr = skip_props(ptr);
222 
223 	/* skip children */
224 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
225 		ptr = skip_node(ptr);
226 
227 	return skip_nops(ptr + 1);
228 }
229 
230 /*
231  * Retrieves next node, skipping all the children nodes of the pointed node,
232  * returns pointer to next node if exists, otherwise returns NULL.
233  * If passed 0 will return first node of the tree (root).
234  */
235 void *
236 fdt_next_node(void *node)
237 {
238 	u_int32_t *ptr;
239 
240 	if (!tree_inited)
241 		return NULL;
242 
243 	ptr = node;
244 
245 	if (node == NULL) {
246 		ptr = skip_nops(tree.tree);
247 		return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
248 	}
249 
250 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
251 		return NULL;
252 
253 	ptr++;
254 
255 	ptr = skip_node_name(ptr);
256 	ptr = skip_props(ptr);
257 
258 	/* skip children */
259 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
260 		ptr = skip_node(ptr);
261 
262 	if (betoh32(*ptr) != FDT_NODE_END)
263 		return NULL;
264 
265 	ptr = skip_nops(ptr + 1);
266 
267 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
268 		return NULL;
269 
270 	return ptr;
271 }
272 
273 int
274 fdt_next_property(void *node, char *name, char **nextname)
275 {
276 	u_int32_t *ptr;
277 	u_int32_t nameid;
278 
279 	if (!tree_inited)
280 		return 0;
281 
282 	ptr = (u_int32_t *)node;
283 
284 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
285 		return 0;
286 
287 	ptr = skip_node_name(ptr + 1);
288 
289 	while (betoh32(*ptr) == FDT_PROPERTY) {
290 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
291 		if (strcmp(name, "") == 0) {
292 			*nextname = fdt_get_str(nameid);
293 			return 1;
294 		}
295 		if (strcmp(name, fdt_get_str(nameid)) == 0) {
296 			ptr = skip_property(ptr);
297 			if (betoh32(*ptr) != FDT_PROPERTY)
298 				break;
299 			nameid = betoh32(*(ptr + 2));
300 			*nextname = fdt_get_str(nameid);
301 			return 1;
302 		}
303 		ptr = skip_property(ptr);
304 	}
305 	*nextname = "";
306 	return 1;
307 }
308 
309 /*
310  * Retrieves node property as integers and puts them in the given
311  * integer array.
312  */
313 int
314 fdt_node_property_ints(void *node, char *name, int *out, int outlen)
315 {
316 	int *data;
317 	int i, inlen;
318 
319 	inlen = fdt_node_property(node, name, (char **)&data) / sizeof(int);
320 	if (inlen <= 0)
321 		return -1;
322 
323 	for (i = 0; i < inlen && i < outlen; i++)
324 		out[i] = betoh32(data[i]);
325 
326 	return i;
327 }
328 
329 /*
330  * Retrieves node property as an integer.
331  */
332 int
333 fdt_node_property_int(void *node, char *name, int *out)
334 {
335 	return fdt_node_property_ints(node, name, out, 1);
336 }
337 
338 /*
339  * Retrieves next node, skipping all the children nodes of the pointed node
340  */
341 void *
342 fdt_child_node(void *node)
343 {
344 	u_int32_t *ptr;
345 
346 	if (!tree_inited)
347 		return NULL;
348 
349 	ptr = node;
350 
351 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
352 		return NULL;
353 
354 	ptr++;
355 
356 	ptr = skip_node_name(ptr);
357 	ptr = skip_props(ptr);
358 	/* check if there is a child node */
359 	return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
360 }
361 
362 /*
363  * Retrieves node name.
364  */
365 char *
366 fdt_node_name(void *node)
367 {
368 	u_int32_t *ptr;
369 
370 	if (!tree_inited)
371 		return NULL;
372 
373 	ptr = node;
374 
375 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
376 		return NULL;
377 
378 	return (char *)(ptr + 1);
379 }
380 
381 void *
382 fdt_find_node(char *name)
383 {
384 	void *node = fdt_next_node(0);
385 	const char *p = name;
386 
387 	if (!tree_inited)
388 		return NULL;
389 
390 	if (*p != '/')
391 		return NULL;
392 
393 	while (*p) {
394 		void *child;
395 		const char *q;
396 
397 		while (*p == '/')
398 			p++;
399 		if (*p == 0)
400 			return node;
401 		q = strchr(p, '/');
402 		if (q == NULL)
403 			q = p + strlen(p);
404 
405 		for (child = fdt_child_node(node); child;
406 		     child = fdt_next_node(child)) {
407 			if (strncmp(p, fdt_node_name(child), q - p) == 0) {
408 				node = child;
409 				break;
410 			}
411 		}
412 
413 		p = q;
414 	}
415 
416 	return node;
417 }
418 
419 void *
420 fdt_parent_node_recurse(void *pnode, void *child)
421 {
422 	void *node = fdt_child_node(pnode);
423 	void *tmp;
424 
425 	while (node && (node != child)) {
426 		if ((tmp = fdt_parent_node_recurse(node, child)))
427 			return tmp;
428 		node = fdt_next_node(node);
429 	}
430 	return (node) ? pnode : NULL;
431 }
432 
433 void *
434 fdt_parent_node(void *node)
435 {
436 	void *pnode = fdt_next_node(0);
437 
438 	if (!tree_inited)
439 		return NULL;
440 
441 	if (node == pnode)
442 		return NULL;
443 
444 	return fdt_parent_node_recurse(pnode, node);
445 }
446 
447 void *
448 fdt_find_phandle_recurse(void *node, uint32_t phandle)
449 {
450 	void *child;
451 	char *data;
452 	void *tmp;
453 	int len;
454 
455 	len = fdt_node_property(node, "phandle", &data);
456 	if (len < 0)
457 		len = fdt_node_property(node, "linux,phandle", &data);
458 
459 	if (len == sizeof(uint32_t) && bemtoh32(data) == phandle)
460 		return node;
461 
462 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
463 		if ((tmp = fdt_find_phandle_recurse(child, phandle)))
464 			return tmp;
465 
466 	return NULL;
467 }
468 
469 void *
470 fdt_find_phandle(uint32_t phandle)
471 {
472 	return fdt_find_phandle_recurse(fdt_next_node(0), phandle);
473 }
474 
475 /*
476  * Translate memory address depending on parent's range.
477  *
478  * Ranges are a way of mapping one address to another.  This ranges attribute
479  * is set on a node's parent.  This means if a node does not have a parent,
480  * there's nothing to translate.  If it does have a parent and the parent does
481  * not have a ranges attribute, there's nothing to translate either.
482  *
483  * If the parent has a ranges attribute and the attribute is not empty, the
484  * node's memory address has to be in one of the given ranges.  This range is
485  * then used to translate the memory address.
486  *
487  * If the parent has a ranges attribute, but the attribute is empty, there's
488  * nothing to translate.  But it's not a translation barrier.  It can be treated
489  * as a simple 1:1 mapping.
490  *
491  * Translation does not end here.  We need to check if the parent's parent also
492  * has a ranges attribute and ask the same questions again.
493  */
494 int
495 fdt_translate_memory_address(void *node, struct fdt_memory *mem)
496 {
497 	void *parent;
498 	int pac, psc, ac, sc, ret, rlen, rone, *range;
499 	uint64_t from, to, size;
500 
501 	/* No parent, no translation. */
502 	parent = fdt_parent_node(node);
503 	if (parent == NULL)
504 		return 0;
505 
506 	/* Extract ranges property from node. */
507 	rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
508 
509 	/* No ranges means translation barrier. Translation stops here. */
510 	if (range == NULL)
511 		return 0;
512 
513 	/* Empty ranges means 1:1 mapping. Continue translation on parent. */
514 	if (rlen <= 0)
515 		return fdt_translate_memory_address(parent, mem);
516 
517 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
518 	ret = fdt_node_property_int(parent, "#address-cells", &pac);
519 	if (ret != 1 || pac <= 0 || pac > 2)
520 		return EINVAL;
521 
522 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
523 	ret = fdt_node_property_int(parent, "#size-cells", &psc);
524 	if (ret != 1 || psc <= 0 || psc > 2)
525 		return EINVAL;
526 
527 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
528 	ret = fdt_node_property_int(node, "#address-cells", &ac);
529 	if (ret <= 0)
530 		ac = pac;
531 	else if (ret > 1 || ac <= 0 || ac > 2)
532 		return EINVAL;
533 
534 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
535 	ret = fdt_node_property_int(node, "#size-cells", &sc);
536 	if (ret <= 0)
537 		sc = psc;
538 	else if (ret > 1 || sc <= 0 || sc > 2)
539 		return EINVAL;
540 
541 	/* Must have at least one range. */
542 	rone = pac + ac + sc;
543 	if (rlen < rone)
544 		return ESRCH;
545 
546 	/* For each range. */
547 	for (; rlen >= rone; rlen -= rone, range += rone) {
548 		/* Extract from and size, so we can see if we fit. */
549 		from = betoh32(range[0]);
550 		if (ac == 2)
551 			from = (from << 32) + betoh32(range[1]);
552 		size = betoh32(range[ac + pac]);
553 		if (sc == 2)
554 			size = (size << 32) + betoh32(range[ac + pac + 1]);
555 
556 		/* Try next, if we're not in the range. */
557 		if (mem->addr < from || (mem->addr + mem->size) > (from + size))
558 			continue;
559 
560 		/* All good, extract to address and translate. */
561 		to = betoh32(range[ac]);
562 		if (pac == 2)
563 			to = (to << 32) + betoh32(range[ac + 1]);
564 
565 		mem->addr -= from;
566 		mem->addr += to;
567 		return fdt_translate_memory_address(parent, mem);
568 	}
569 
570 	/* To be successful, we must have returned in the for-loop. */
571 	return ESRCH;
572 }
573 
574 /*
575  * Parse the memory address and size of a node.
576  */
577 int
578 fdt_get_memory_address(void *node, int idx, struct fdt_memory *mem)
579 {
580 	void *parent;
581 	int ac, sc, off, ret, *in, inlen;
582 
583 	if (node == NULL || mem == NULL)
584 		return EINVAL;
585 
586 	parent = fdt_parent_node(node);
587 	if (parent == NULL)
588 		return EINVAL;
589 
590 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
591 	ret = fdt_node_property_int(parent, "#address-cells", &ac);
592 	if (ret != 1 || ac <= 0 || ac > 2)
593 		return EINVAL;
594 
595 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
596 	ret = fdt_node_property_int(parent, "#size-cells", &sc);
597 	if (ret != 1 || sc <= 0 || sc > 2)
598 		return EINVAL;
599 
600 	inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
601 	if (inlen < ((idx + 1) * (ac + sc)))
602 		return EINVAL;
603 
604 	off = idx * (ac + sc);
605 
606 	mem->addr = betoh32(in[off]);
607 	if (ac == 2)
608 		mem->addr = (mem->addr << 32) + betoh32(in[off + 1]);
609 
610 	mem->size = betoh32(in[off + ac]);
611 	if (sc == 2)
612 		mem->size = (mem->size << 32) + betoh32(in[off + ac + 1]);
613 
614 	return fdt_translate_memory_address(parent, mem);
615 }
616 
617 int
618 fdt_is_compatible(void *node, const char *name)
619 {
620 	char *data;
621 	int len;
622 
623 	len = fdt_node_property(node, "compatible", &data);
624 	while (len > 0) {
625 		if (strcmp(data, name) == 0)
626 			return 1;
627 		len -= strlen(data) + 1;
628 		data += strlen(data) + 1;
629 	}
630 
631 	return 0;
632 }
633 
634 #ifdef DEBUG
635 /*
636  * Debug methods for printing whole tree, particular odes and properies
637  */
638 void *
639 fdt_print_property(void *node, int level)
640 {
641 	u_int32_t *ptr;
642 	char *tmp, *value;
643 	int cnt;
644 	u_int32_t nameid, size;
645 
646 	ptr = (u_int32_t *)node;
647 
648 	if (!tree_inited)
649 		return NULL;
650 
651 	if (betoh32(*ptr) != FDT_PROPERTY)
652 		return ptr; /* should never happen */
653 
654 	/* extract property name_id and size */
655 	size = betoh32(*++ptr);
656 	nameid = betoh32(*++ptr);
657 
658 	for (cnt = 0; cnt < level; cnt++)
659 		printf("\t");
660 
661 	tmp = fdt_get_str(nameid);
662 	printf("\t%s : ", tmp ? tmp : "NO_NAME");
663 
664 	ptr++;
665 	value = (char *)ptr;
666 
667 	if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
668 	    !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
669 	    !strcmp(tmp, "linux,stdout-path")) {
670 		printf("%s", value);
671 	} else if (!strcmp(tmp, "clock-frequency") ||
672 	    !strcmp(tmp, "timebase-frequency")) {
673 		printf("%d", betoh32(*((unsigned int *)value)));
674 	} else {
675 		for (cnt = 0; cnt < size; cnt++) {
676 			if ((cnt % sizeof(u_int32_t)) == 0)
677 				printf(" ");
678 			printf("%02x", value[cnt]);
679 		}
680 	}
681 	ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
682 	printf("\n");
683 
684 	return ptr;
685 }
686 
687 void
688 fdt_print_node(void *node, int level)
689 {
690 	u_int32_t *ptr;
691 	int cnt;
692 
693 	ptr = (u_int32_t *)node;
694 
695 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
696 		return;
697 
698 	ptr++;
699 
700 	for (cnt = 0; cnt < level; cnt++)
701 		printf("\t");
702 	printf("%s :\n", fdt_node_name(node));
703 	ptr = skip_node_name(ptr);
704 
705 	while (betoh32(*ptr) == FDT_PROPERTY)
706 		ptr = fdt_print_property(ptr, level);
707 }
708 
709 void
710 fdt_print_node_recurse(void *node, int level)
711 {
712 	void *child;
713 
714 	fdt_print_node(node, level);
715 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
716 		fdt_print_node_recurse(child, level + 1);
717 }
718 
719 void
720 fdt_print_tree(void)
721 {
722 	fdt_print_node_recurse(fdt_next_node(0), 0);
723 }
724 #endif
725 
726 int
727 OF_peer(int handle)
728 {
729 	void *node = (char *)tree.header + handle;
730 
731 	if (handle == 0)
732 		node = fdt_find_node("/");
733 	else
734 		node = fdt_next_node(node);
735 	return node ? ((char *)node - (char *)tree.header) : 0;
736 }
737 
738 int
739 OF_child(int handle)
740 {
741 	void *node = (char *)tree.header + handle;
742 
743 	node = fdt_child_node(node);
744 	return node ? ((char *)node - (char *)tree.header) : 0;
745 }
746 
747 int
748 OF_parent(int handle)
749 {
750 	void *node = (char *)tree.header + handle;
751 
752 	node = fdt_parent_node(node);
753 	return node ? ((char *)node - (char *)tree.header) : 0;
754 }
755 
756 int
757 OF_finddevice(char *name)
758 {
759 	void *node;
760 
761 	node = fdt_find_node(name);
762 	return node ? ((char *)node - (char *)tree.header) : -1;
763 }
764 
765 int
766 OF_getnodebyname(int handle, const char *name)
767 {
768 	void *node = (char *)tree.header + handle;
769 
770 	if (handle == 0)
771 		node = fdt_find_node("/");
772 
773 	while (node) {
774 		if (strcmp(name, fdt_node_name(node)) == 0)
775 			break;
776 
777 		node = fdt_next_node(node);
778 	}
779 
780 	return node ? ((char *)node - (char *)tree.header) : 0;
781 }
782 
783 int
784 OF_getnodebyphandle(uint32_t phandle)
785 {
786 	void *node;
787 
788 	node = fdt_find_phandle(phandle);
789 	return node ? ((char *)node - (char *)tree.header) : 0;
790 }
791 
792 int
793 OF_getproplen(int handle, char *prop)
794 {
795 	void *node = (char *)tree.header + handle;
796 	char *data, *name;
797 	int len;
798 
799 	len = fdt_node_property(node, prop, &data);
800 
801 	/*
802 	 * The "name" property is optional since version 16 of the
803 	 * flattened device tree specification, so we synthesize one
804 	 * from the unit name of the node if it is missing.
805 	 */
806 	if (len < 0 && strcmp(prop, "name") == 0) {
807 		name = fdt_node_name(node);
808 		data = strchr(name, '@');
809 		if (data)
810 			len = data - name;
811 		else
812 			len = strlen(name);
813 		return len + 1;
814 	}
815 
816 	return len;
817 }
818 
819 int
820 OF_getprop(int handle, char *prop, void *buf, int buflen)
821 {
822 	void *node = (char *)tree.header + handle;
823 	char *data;
824 	int len;
825 
826 	len = fdt_node_property(node, prop, &data);
827 
828 	/*
829 	 * The "name" property is optional since version 16 of the
830 	 * flattened device tree specification, so we synthesize one
831 	 * from the unit name of the node if it is missing.
832 	 */
833 	if (len < 0 && strcmp(prop, "name") == 0) {
834 		data = fdt_node_name(node);
835 		if (data) {
836 			len = strlcpy(buf, data, buflen);
837 			data = strchr(buf, '@');
838 			if (data) {
839 				*data = 0;
840 				len = data - (char *)buf;
841 			}
842 			return len + 1;
843 		}
844 	}
845 
846 	if (len > 0)
847 		memcpy(buf, data, min(len, buflen));
848 	return len;
849 }
850 
851 uint32_t
852 OF_getpropint(int handle, char *prop, uint32_t defval)
853 {
854 	uint32_t val;
855 	int len;
856 
857 	len = OF_getprop(handle, prop, &val, sizeof(val));
858 	if (len != sizeof(val))
859 		return defval;
860 
861 	return betoh32(val);
862 }
863 
864 int
865 OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen)
866 {
867 	int len;
868 	int i;
869 
870 	len = OF_getprop(handle, prop, buf, buflen);
871 	if (len < 0 || (len % sizeof(uint32_t)))
872 		return -1;
873 
874 	for (i = 0; i < len / sizeof(uint32_t); i++)
875 		buf[i] = betoh32(buf[i]);
876 
877 	return len;
878 }
879 
880 int
881 OF_nextprop(int handle, char *prop, void *nextprop)
882 {
883 	void *node = (char *)tree.header + handle;
884 	char *data;
885 
886 	if (fdt_node_property(node, "name", &data) == -1) {
887 		if (strcmp(prop, "") == 0)
888 			return strlcpy(nextprop, "name", OPROMMAXPARAM);
889 		if (strcmp(prop, "name") == 0)
890 			prop = "";
891 	}
892 
893 	if (fdt_next_property(node, prop, &data))
894 		return strlcpy(nextprop, data, OPROMMAXPARAM);
895 	return -1;
896 }
897 
898 int
899 OF_is_compatible(int handle, const char *name)
900 {
901 	void *node = (char *)tree.header + handle;
902 	return (fdt_is_compatible(node, name));
903 }
904 
905