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