xref: /openbsd-src/sys/dev/ofw/fdt.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: fdt.c,v 1.19 2016/08/23 18:12:09 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_reg(void *, struct fdt_reg *);
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 		if (child == NULL)
414 			return NULL; /* No match found. */
415 
416 		p = q;
417 	}
418 
419 	return node;
420 }
421 
422 void *
423 fdt_parent_node_recurse(void *pnode, void *child)
424 {
425 	void *node = fdt_child_node(pnode);
426 	void *tmp;
427 
428 	while (node && (node != child)) {
429 		if ((tmp = fdt_parent_node_recurse(node, child)))
430 			return tmp;
431 		node = fdt_next_node(node);
432 	}
433 	return (node) ? pnode : NULL;
434 }
435 
436 void *
437 fdt_parent_node(void *node)
438 {
439 	void *pnode = fdt_next_node(0);
440 
441 	if (!tree_inited)
442 		return NULL;
443 
444 	if (node == pnode)
445 		return NULL;
446 
447 	return fdt_parent_node_recurse(pnode, node);
448 }
449 
450 void *
451 fdt_find_phandle_recurse(void *node, uint32_t phandle)
452 {
453 	void *child;
454 	char *data;
455 	void *tmp;
456 	int len;
457 
458 	len = fdt_node_property(node, "phandle", &data);
459 	if (len < 0)
460 		len = fdt_node_property(node, "linux,phandle", &data);
461 
462 	if (len == sizeof(uint32_t) && bemtoh32(data) == phandle)
463 		return node;
464 
465 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
466 		if ((tmp = fdt_find_phandle_recurse(child, phandle)))
467 			return tmp;
468 
469 	return NULL;
470 }
471 
472 void *
473 fdt_find_phandle(uint32_t phandle)
474 {
475 	return fdt_find_phandle_recurse(fdt_next_node(0), phandle);
476 }
477 
478 /*
479  * Translate memory address depending on parent's range.
480  *
481  * Ranges are a way of mapping one address to another.  This ranges attribute
482  * is set on a node's parent.  This means if a node does not have a parent,
483  * there's nothing to translate.  If it does have a parent and the parent does
484  * not have a ranges attribute, there's nothing to translate either.
485  *
486  * If the parent has a ranges attribute and the attribute is not empty, the
487  * node's memory address has to be in one of the given ranges.  This range is
488  * then used to translate the memory address.
489  *
490  * If the parent has a ranges attribute, but the attribute is empty, there's
491  * nothing to translate.  But it's not a translation barrier.  It can be treated
492  * as a simple 1:1 mapping.
493  *
494  * Translation does not end here.  We need to check if the parent's parent also
495  * has a ranges attribute and ask the same questions again.
496  */
497 int
498 fdt_translate_reg(void *node, struct fdt_reg *reg)
499 {
500 	void *parent;
501 	int pac, psc, ac, sc, ret, rlen, rone, *range;
502 	uint64_t from, to, size;
503 
504 	/* No parent, no translation. */
505 	parent = fdt_parent_node(node);
506 	if (parent == NULL)
507 		return 0;
508 
509 	/* Extract ranges property from node. */
510 	rlen = fdt_node_property(node, "ranges", (char **)&range) / sizeof(int);
511 
512 	/* No ranges means translation barrier. Translation stops here. */
513 	if (range == NULL)
514 		return 0;
515 
516 	/* Empty ranges means 1:1 mapping. Continue translation on parent. */
517 	if (rlen <= 0)
518 		return fdt_translate_reg(parent, reg);
519 
520 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
521 	ret = fdt_node_property_int(parent, "#address-cells", &pac);
522 	if (ret != 1 || pac <= 0 || pac > 2)
523 		return EINVAL;
524 
525 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
526 	ret = fdt_node_property_int(parent, "#size-cells", &psc);
527 	if (ret != 1 || psc <= 0 || psc > 2)
528 		return EINVAL;
529 
530 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
531 	ret = fdt_node_property_int(node, "#address-cells", &ac);
532 	if (ret <= 0)
533 		ac = pac;
534 	else if (ret > 1 || ac <= 0 || ac > 2)
535 		return EINVAL;
536 
537 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
538 	ret = fdt_node_property_int(node, "#size-cells", &sc);
539 	if (ret <= 0)
540 		sc = psc;
541 	else if (ret > 1 || sc <= 0 || sc > 2)
542 		return EINVAL;
543 
544 	/* Must have at least one range. */
545 	rone = pac + ac + sc;
546 	if (rlen < rone)
547 		return ESRCH;
548 
549 	/* For each range. */
550 	for (; rlen >= rone; rlen -= rone, range += rone) {
551 		/* Extract from and size, so we can see if we fit. */
552 		from = betoh32(range[0]);
553 		if (ac == 2)
554 			from = (from << 32) + betoh32(range[1]);
555 		size = betoh32(range[ac + pac]);
556 		if (sc == 2)
557 			size = (size << 32) + betoh32(range[ac + pac + 1]);
558 
559 		/* Try next, if we're not in the range. */
560 		if (reg->addr < from || (reg->addr + reg->size) > (from + size))
561 			continue;
562 
563 		/* All good, extract to address and translate. */
564 		to = betoh32(range[ac]);
565 		if (pac == 2)
566 			to = (to << 32) + betoh32(range[ac + 1]);
567 
568 		reg->addr -= from;
569 		reg->addr += to;
570 		return fdt_translate_reg(parent, reg);
571 	}
572 
573 	/* To be successful, we must have returned in the for-loop. */
574 	return ESRCH;
575 }
576 
577 /*
578  * Parse the memory address and size of a node.
579  */
580 int
581 fdt_get_reg(void *node, int idx, struct fdt_reg *reg)
582 {
583 	void *parent;
584 	int ac, sc, off, ret, *in, inlen;
585 
586 	if (node == NULL || reg == NULL)
587 		return EINVAL;
588 
589 	parent = fdt_parent_node(node);
590 	if (parent == NULL)
591 		return EINVAL;
592 
593 	/* We only support 32-bit (1), and 64-bit (2) wide addresses here. */
594 	ret = fdt_node_property_int(parent, "#address-cells", &ac);
595 	if (ret != 1 || ac <= 0 || ac > 2)
596 		return EINVAL;
597 
598 	/* We only support 32-bit (1), and 64-bit (2) wide sizes here. */
599 	ret = fdt_node_property_int(parent, "#size-cells", &sc);
600 	if (ret != 1 || sc <= 0 || sc > 2)
601 		return EINVAL;
602 
603 	inlen = fdt_node_property(node, "reg", (char **)&in) / sizeof(int);
604 	if (inlen < ((idx + 1) * (ac + sc)))
605 		return EINVAL;
606 
607 	off = idx * (ac + sc);
608 
609 	reg->addr = betoh32(in[off]);
610 	if (ac == 2)
611 		reg->addr = (reg->addr << 32) + betoh32(in[off + 1]);
612 
613 	reg->size = betoh32(in[off + ac]);
614 	if (sc == 2)
615 		reg->size = (reg->size << 32) + betoh32(in[off + ac + 1]);
616 
617 	return fdt_translate_reg(parent, reg);
618 }
619 
620 int
621 fdt_is_compatible(void *node, const char *name)
622 {
623 	char *data;
624 	int len;
625 
626 	len = fdt_node_property(node, "compatible", &data);
627 	while (len > 0) {
628 		if (strcmp(data, name) == 0)
629 			return 1;
630 		len -= strlen(data) + 1;
631 		data += strlen(data) + 1;
632 	}
633 
634 	return 0;
635 }
636 
637 #ifdef DEBUG
638 /*
639  * Debug methods for printing whole tree, particular odes and properies
640  */
641 void *
642 fdt_print_property(void *node, int level)
643 {
644 	u_int32_t *ptr;
645 	char *tmp, *value;
646 	int cnt;
647 	u_int32_t nameid, size;
648 
649 	ptr = (u_int32_t *)node;
650 
651 	if (!tree_inited)
652 		return NULL;
653 
654 	if (betoh32(*ptr) != FDT_PROPERTY)
655 		return ptr; /* should never happen */
656 
657 	/* extract property name_id and size */
658 	size = betoh32(*++ptr);
659 	nameid = betoh32(*++ptr);
660 
661 	for (cnt = 0; cnt < level; cnt++)
662 		printf("\t");
663 
664 	tmp = fdt_get_str(nameid);
665 	printf("\t%s : ", tmp ? tmp : "NO_NAME");
666 
667 	ptr++;
668 	value = (char *)ptr;
669 
670 	if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
671 	    !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
672 	    !strcmp(tmp, "linux,stdout-path")) {
673 		printf("%s", value);
674 	} else if (!strcmp(tmp, "clock-frequency") ||
675 	    !strcmp(tmp, "timebase-frequency")) {
676 		printf("%d", betoh32(*((unsigned int *)value)));
677 	} else {
678 		for (cnt = 0; cnt < size; cnt++) {
679 			if ((cnt % sizeof(u_int32_t)) == 0)
680 				printf(" ");
681 			printf("%02x", value[cnt]);
682 		}
683 	}
684 	ptr += roundup(size, sizeof(u_int32_t)) / sizeof(u_int32_t);
685 	printf("\n");
686 
687 	return ptr;
688 }
689 
690 void
691 fdt_print_node(void *node, int level)
692 {
693 	u_int32_t *ptr;
694 	int cnt;
695 
696 	ptr = (u_int32_t *)node;
697 
698 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
699 		return;
700 
701 	ptr++;
702 
703 	for (cnt = 0; cnt < level; cnt++)
704 		printf("\t");
705 	printf("%s :\n", fdt_node_name(node));
706 	ptr = skip_node_name(ptr);
707 
708 	while (betoh32(*ptr) == FDT_PROPERTY)
709 		ptr = fdt_print_property(ptr, level);
710 }
711 
712 void
713 fdt_print_node_recurse(void *node, int level)
714 {
715 	void *child;
716 
717 	fdt_print_node(node, level);
718 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
719 		fdt_print_node_recurse(child, level + 1);
720 }
721 
722 void
723 fdt_print_tree(void)
724 {
725 	fdt_print_node_recurse(fdt_next_node(0), 0);
726 }
727 #endif
728 
729 int
730 OF_peer(int handle)
731 {
732 	void *node = (char *)tree.header + handle;
733 
734 	if (handle == 0)
735 		node = fdt_find_node("/");
736 	else
737 		node = fdt_next_node(node);
738 	return node ? ((char *)node - (char *)tree.header) : 0;
739 }
740 
741 int
742 OF_child(int handle)
743 {
744 	void *node = (char *)tree.header + handle;
745 
746 	node = fdt_child_node(node);
747 	return node ? ((char *)node - (char *)tree.header) : 0;
748 }
749 
750 int
751 OF_parent(int handle)
752 {
753 	void *node = (char *)tree.header + handle;
754 
755 	node = fdt_parent_node(node);
756 	return node ? ((char *)node - (char *)tree.header) : 0;
757 }
758 
759 int
760 OF_finddevice(char *name)
761 {
762 	void *node;
763 
764 	node = fdt_find_node(name);
765 	return node ? ((char *)node - (char *)tree.header) : -1;
766 }
767 
768 int
769 OF_getnodebyname(int handle, const char *name)
770 {
771 	void *node = (char *)tree.header + handle;
772 
773 	if (handle == 0)
774 		node = fdt_find_node("/");
775 
776 	while (node) {
777 		if (strcmp(name, fdt_node_name(node)) == 0)
778 			break;
779 
780 		node = fdt_next_node(node);
781 	}
782 
783 	return node ? ((char *)node - (char *)tree.header) : 0;
784 }
785 
786 int
787 OF_getnodebyphandle(uint32_t phandle)
788 {
789 	void *node;
790 
791 	node = fdt_find_phandle(phandle);
792 	return node ? ((char *)node - (char *)tree.header) : 0;
793 }
794 
795 int
796 OF_getproplen(int handle, char *prop)
797 {
798 	void *node = (char *)tree.header + handle;
799 	char *data, *name;
800 	int len;
801 
802 	len = fdt_node_property(node, prop, &data);
803 
804 	/*
805 	 * The "name" property is optional since version 16 of the
806 	 * flattened device tree specification, so we synthesize one
807 	 * from the unit name of the node if it is missing.
808 	 */
809 	if (len < 0 && strcmp(prop, "name") == 0) {
810 		name = fdt_node_name(node);
811 		data = strchr(name, '@');
812 		if (data)
813 			len = data - name;
814 		else
815 			len = strlen(name);
816 		return len + 1;
817 	}
818 
819 	return len;
820 }
821 
822 int
823 OF_getprop(int handle, char *prop, void *buf, int buflen)
824 {
825 	void *node = (char *)tree.header + handle;
826 	char *data;
827 	int len;
828 
829 	len = fdt_node_property(node, prop, &data);
830 
831 	/*
832 	 * The "name" property is optional since version 16 of the
833 	 * flattened device tree specification, so we synthesize one
834 	 * from the unit name of the node if it is missing.
835 	 */
836 	if (len < 0 && strcmp(prop, "name") == 0) {
837 		data = fdt_node_name(node);
838 		if (data) {
839 			len = strlcpy(buf, data, buflen);
840 			data = strchr(buf, '@');
841 			if (data) {
842 				*data = 0;
843 				len = data - (char *)buf;
844 			}
845 			return len + 1;
846 		}
847 	}
848 
849 	if (len > 0)
850 		memcpy(buf, data, min(len, buflen));
851 	return len;
852 }
853 
854 uint32_t
855 OF_getpropint(int handle, char *prop, uint32_t defval)
856 {
857 	uint32_t val;
858 	int len;
859 
860 	len = OF_getprop(handle, prop, &val, sizeof(val));
861 	if (len != sizeof(val))
862 		return defval;
863 
864 	return betoh32(val);
865 }
866 
867 int
868 OF_getpropintarray(int handle, char *prop, uint32_t *buf, int buflen)
869 {
870 	int len;
871 	int i;
872 
873 	len = OF_getprop(handle, prop, buf, buflen);
874 	if (len < 0 || (len % sizeof(uint32_t)))
875 		return -1;
876 
877 	for (i = 0; i < len / sizeof(uint32_t); i++)
878 		buf[i] = betoh32(buf[i]);
879 
880 	return len;
881 }
882 
883 int
884 OF_nextprop(int handle, char *prop, void *nextprop)
885 {
886 	void *node = (char *)tree.header + handle;
887 	char *data;
888 
889 	if (fdt_node_property(node, "name", &data) == -1) {
890 		if (strcmp(prop, "") == 0)
891 			return strlcpy(nextprop, "name", OPROMMAXPARAM);
892 		if (strcmp(prop, "name") == 0)
893 			prop = "";
894 	}
895 
896 	if (fdt_next_property(node, prop, &data))
897 		return strlcpy(nextprop, data, OPROMMAXPARAM);
898 	return -1;
899 }
900 
901 int
902 OF_is_compatible(int handle, const char *name)
903 {
904 	void *node = (char *)tree.header + handle;
905 	return (fdt_is_compatible(node, name));
906 }
907 
908