1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
6 #include "libfdt_env.h"
11 #include "libfdt_internal.h"
13 static int fdt_nodename_eq_(const void *fdt
, int offset
,
14 const char *s
, int len
)
17 const char *p
= fdt_get_name(fdt
, offset
, &olen
);
23 if (memcmp(p
, s
, len
) != 0)
28 else if (!memchr(s
, '@', len
) && (p
[len
] == '@'))
34 const char *fdt_get_string(const void *fdt
, int stroffset
, int *lenp
)
36 int32_t totalsize
= fdt_ro_probe_(fdt
);
37 uint32_t absoffset
= stroffset
+ fdt_off_dt_strings(fdt
);
46 err
= -FDT_ERR_BADOFFSET
;
47 if (absoffset
>= totalsize
)
49 len
= totalsize
- absoffset
;
51 if (fdt_magic(fdt
) == FDT_MAGIC
) {
54 if (fdt_version(fdt
) >= 17) {
55 if (stroffset
>= fdt_size_dt_strings(fdt
))
57 if ((fdt_size_dt_strings(fdt
) - stroffset
) < len
)
58 len
= fdt_size_dt_strings(fdt
) - stroffset
;
60 } else if (fdt_magic(fdt
) == FDT_SW_MAGIC
) {
62 || (stroffset
< -fdt_size_dt_strings(fdt
)))
64 if ((-stroffset
) < len
)
67 err
= -FDT_ERR_INTERNAL
;
71 s
= (const char *)fdt
+ absoffset
;
72 n
= memchr(s
, '\0', len
);
74 /* missing terminating NULL */
75 err
= -FDT_ERR_TRUNCATED
;
89 const char *fdt_string(const void *fdt
, int stroffset
)
91 return fdt_get_string(fdt
, stroffset
, NULL
);
94 static int fdt_string_eq_(const void *fdt
, int stroffset
,
95 const char *s
, int len
)
98 const char *p
= fdt_get_string(fdt
, stroffset
, &slen
);
100 return p
&& (slen
== len
) && (memcmp(p
, s
, len
) == 0);
103 int fdt_find_max_phandle(const void *fdt
, uint32_t *phandle
)
111 offset
= fdt_next_node(fdt
, offset
, NULL
);
113 if (offset
== -FDT_ERR_NOTFOUND
)
119 value
= fdt_get_phandle(fdt
, offset
);
131 int fdt_generate_phandle(const void *fdt
, uint32_t *phandle
)
136 err
= fdt_find_max_phandle(fdt
, &max
);
140 if (max
== FDT_MAX_PHANDLE
)
141 return -FDT_ERR_NOPHANDLES
;
149 static const struct fdt_reserve_entry
*fdt_mem_rsv(const void *fdt
, int n
)
151 int offset
= n
* sizeof(struct fdt_reserve_entry
);
152 int absoffset
= fdt_off_mem_rsvmap(fdt
) + offset
;
154 if (absoffset
< fdt_off_mem_rsvmap(fdt
))
156 if (absoffset
> fdt_totalsize(fdt
) - sizeof(struct fdt_reserve_entry
))
158 return fdt_mem_rsv_(fdt
, n
);
161 int fdt_get_mem_rsv(const void *fdt
, int n
, uint64_t *address
, uint64_t *size
)
163 const struct fdt_reserve_entry
*re
;
166 re
= fdt_mem_rsv(fdt
, n
);
168 return -FDT_ERR_BADOFFSET
;
170 *address
= fdt64_ld(&re
->address
);
171 *size
= fdt64_ld(&re
->size
);
175 int fdt_num_mem_rsv(const void *fdt
)
178 const struct fdt_reserve_entry
*re
;
180 for (i
= 0; (re
= fdt_mem_rsv(fdt
, i
)) != NULL
; i
++) {
181 if (fdt64_ld(&re
->size
) == 0)
184 return -FDT_ERR_TRUNCATED
;
187 static int nextprop_(const void *fdt
, int offset
)
193 tag
= fdt_next_tag(fdt
, offset
, &nextoffset
);
198 return -FDT_ERR_BADSTRUCTURE
;
206 } while (tag
== FDT_NOP
);
208 return -FDT_ERR_NOTFOUND
;
211 int fdt_subnode_offset_namelen(const void *fdt
, int offset
,
212 const char *name
, int namelen
)
219 (offset
>= 0) && (depth
>= 0);
220 offset
= fdt_next_node(fdt
, offset
, &depth
))
222 && fdt_nodename_eq_(fdt
, offset
, name
, namelen
))
226 return -FDT_ERR_NOTFOUND
;
227 return offset
; /* error */
230 int fdt_subnode_offset(const void *fdt
, int parentoffset
,
233 return fdt_subnode_offset_namelen(fdt
, parentoffset
, name
, strlen(name
));
236 int fdt_path_offset_namelen(const void *fdt
, const char *path
, int namelen
)
238 const char *end
= path
+ namelen
;
239 const char *p
= path
;
244 /* see if we have an alias */
246 const char *q
= memchr(path
, '/', end
- p
);
251 p
= fdt_get_alias_namelen(fdt
, p
, q
- p
);
253 return -FDT_ERR_BADPATH
;
254 offset
= fdt_path_offset(fdt
, p
);
267 q
= memchr(p
, '/', end
- p
);
271 offset
= fdt_subnode_offset_namelen(fdt
, offset
, p
, q
-p
);
281 int fdt_path_offset(const void *fdt
, const char *path
)
283 return fdt_path_offset_namelen(fdt
, path
, strlen(path
));
286 const char *fdt_get_name(const void *fdt
, int nodeoffset
, int *len
)
288 const struct fdt_node_header
*nh
= fdt_offset_ptr_(fdt
, nodeoffset
);
292 if (((err
= fdt_ro_probe_(fdt
)) < 0)
293 || ((err
= fdt_check_node_offset_(fdt
, nodeoffset
)) < 0))
298 if (fdt_version(fdt
) < 0x10) {
300 * For old FDT versions, match the naming conventions of V16:
301 * give only the leaf name (after all /). The actual tree
302 * contents are loosely checked.
305 leaf
= strrchr(nameptr
, '/');
307 err
= -FDT_ERR_BADSTRUCTURE
;
314 *len
= strlen(nameptr
);
324 int fdt_first_property_offset(const void *fdt
, int nodeoffset
)
328 if ((offset
= fdt_check_node_offset_(fdt
, nodeoffset
)) < 0)
331 return nextprop_(fdt
, offset
);
334 int fdt_next_property_offset(const void *fdt
, int offset
)
336 if ((offset
= fdt_check_prop_offset_(fdt
, offset
)) < 0)
339 return nextprop_(fdt
, offset
);
342 static const struct fdt_property
*fdt_get_property_by_offset_(const void *fdt
,
347 const struct fdt_property
*prop
;
349 if ((err
= fdt_check_prop_offset_(fdt
, offset
)) < 0) {
355 prop
= fdt_offset_ptr_(fdt
, offset
);
358 *lenp
= fdt32_ld(&prop
->len
);
363 const struct fdt_property
*fdt_get_property_by_offset(const void *fdt
,
367 /* Prior to version 16, properties may need realignment
368 * and this API does not work. fdt_getprop_*() will, however. */
370 if (fdt_version(fdt
) < 0x10) {
372 *lenp
= -FDT_ERR_BADVERSION
;
376 return fdt_get_property_by_offset_(fdt
, offset
, lenp
);
379 static const struct fdt_property
*fdt_get_property_namelen_(const void *fdt
,
386 for (offset
= fdt_first_property_offset(fdt
, offset
);
388 (offset
= fdt_next_property_offset(fdt
, offset
))) {
389 const struct fdt_property
*prop
;
391 if (!(prop
= fdt_get_property_by_offset_(fdt
, offset
, lenp
))) {
392 offset
= -FDT_ERR_INTERNAL
;
395 if (fdt_string_eq_(fdt
, fdt32_ld(&prop
->nameoff
),
409 const struct fdt_property
*fdt_get_property_namelen(const void *fdt
,
412 int namelen
, int *lenp
)
414 /* Prior to version 16, properties may need realignment
415 * and this API does not work. fdt_getprop_*() will, however. */
416 if (fdt_version(fdt
) < 0x10) {
418 *lenp
= -FDT_ERR_BADVERSION
;
422 return fdt_get_property_namelen_(fdt
, offset
, name
, namelen
, lenp
,
427 const struct fdt_property
*fdt_get_property(const void *fdt
,
429 const char *name
, int *lenp
)
431 return fdt_get_property_namelen(fdt
, nodeoffset
, name
,
435 const void *fdt_getprop_namelen(const void *fdt
, int nodeoffset
,
436 const char *name
, int namelen
, int *lenp
)
439 const struct fdt_property
*prop
;
441 prop
= fdt_get_property_namelen_(fdt
, nodeoffset
, name
, namelen
, lenp
,
446 /* Handle realignment */
447 if (fdt_version(fdt
) < 0x10 && (poffset
+ sizeof(*prop
)) % 8 &&
448 fdt32_ld(&prop
->len
) >= 8)
449 return prop
->data
+ 4;
453 const void *fdt_getprop_by_offset(const void *fdt
, int offset
,
454 const char **namep
, int *lenp
)
456 const struct fdt_property
*prop
;
458 prop
= fdt_get_property_by_offset_(fdt
, offset
, lenp
);
464 name
= fdt_get_string(fdt
, fdt32_ld(&prop
->nameoff
),
474 /* Handle realignment */
475 if (fdt_version(fdt
) < 0x10 && (offset
+ sizeof(*prop
)) % 8 &&
476 fdt32_ld(&prop
->len
) >= 8)
477 return prop
->data
+ 4;
481 const void *fdt_getprop(const void *fdt
, int nodeoffset
,
482 const char *name
, int *lenp
)
484 return fdt_getprop_namelen(fdt
, nodeoffset
, name
, strlen(name
), lenp
);
487 uint32_t fdt_get_phandle(const void *fdt
, int nodeoffset
)
492 /* FIXME: This is a bit sub-optimal, since we potentially scan
493 * over all the properties twice. */
494 php
= fdt_getprop(fdt
, nodeoffset
, "phandle", &len
);
495 if (!php
|| (len
!= sizeof(*php
))) {
496 php
= fdt_getprop(fdt
, nodeoffset
, "linux,phandle", &len
);
497 if (!php
|| (len
!= sizeof(*php
)))
501 return fdt32_ld(php
);
504 const char *fdt_get_alias_namelen(const void *fdt
,
505 const char *name
, int namelen
)
509 aliasoffset
= fdt_path_offset(fdt
, "/aliases");
513 return fdt_getprop_namelen(fdt
, aliasoffset
, name
, namelen
, NULL
);
516 const char *fdt_get_alias(const void *fdt
, const char *name
)
518 return fdt_get_alias_namelen(fdt
, name
, strlen(name
));
521 int fdt_get_path(const void *fdt
, int nodeoffset
, char *buf
, int buflen
)
523 int pdepth
= 0, p
= 0;
524 int offset
, depth
, namelen
;
530 return -FDT_ERR_NOSPACE
;
532 for (offset
= 0, depth
= 0;
533 (offset
>= 0) && (offset
<= nodeoffset
);
534 offset
= fdt_next_node(fdt
, offset
, &depth
)) {
535 while (pdepth
> depth
) {
538 } while (buf
[p
-1] != '/');
542 if (pdepth
>= depth
) {
543 name
= fdt_get_name(fdt
, offset
, &namelen
);
546 if ((p
+ namelen
+ 1) <= buflen
) {
547 memcpy(buf
+ p
, name
, namelen
);
554 if (offset
== nodeoffset
) {
555 if (pdepth
< (depth
+ 1))
556 return -FDT_ERR_NOSPACE
;
558 if (p
> 1) /* special case so that root path is "/", not "" */
565 if ((offset
== -FDT_ERR_NOTFOUND
) || (offset
>= 0))
566 return -FDT_ERR_BADOFFSET
;
567 else if (offset
== -FDT_ERR_BADOFFSET
)
568 return -FDT_ERR_BADSTRUCTURE
;
570 return offset
; /* error from fdt_next_node() */
573 int fdt_supernode_atdepth_offset(const void *fdt
, int nodeoffset
,
574 int supernodedepth
, int *nodedepth
)
577 int supernodeoffset
= -FDT_ERR_INTERNAL
;
581 if (supernodedepth
< 0)
582 return -FDT_ERR_NOTFOUND
;
584 for (offset
= 0, depth
= 0;
585 (offset
>= 0) && (offset
<= nodeoffset
);
586 offset
= fdt_next_node(fdt
, offset
, &depth
)) {
587 if (depth
== supernodedepth
)
588 supernodeoffset
= offset
;
590 if (offset
== nodeoffset
) {
594 if (supernodedepth
> depth
)
595 return -FDT_ERR_NOTFOUND
;
597 return supernodeoffset
;
601 if ((offset
== -FDT_ERR_NOTFOUND
) || (offset
>= 0))
602 return -FDT_ERR_BADOFFSET
;
603 else if (offset
== -FDT_ERR_BADOFFSET
)
604 return -FDT_ERR_BADSTRUCTURE
;
606 return offset
; /* error from fdt_next_node() */
609 int fdt_node_depth(const void *fdt
, int nodeoffset
)
614 err
= fdt_supernode_atdepth_offset(fdt
, nodeoffset
, 0, &nodedepth
);
616 return (err
< 0) ? err
: -FDT_ERR_INTERNAL
;
620 int fdt_parent_offset(const void *fdt
, int nodeoffset
)
622 int nodedepth
= fdt_node_depth(fdt
, nodeoffset
);
626 return fdt_supernode_atdepth_offset(fdt
, nodeoffset
,
627 nodedepth
- 1, NULL
);
630 int fdt_node_offset_by_prop_value(const void *fdt
, int startoffset
,
631 const char *propname
,
632 const void *propval
, int proplen
)
640 /* FIXME: The algorithm here is pretty horrible: we scan each
641 * property of a node in fdt_getprop(), then if that didn't
642 * find what we want, we scan over them again making our way
643 * to the next node. Still it's the easiest to implement
644 * approach; performance can come later. */
645 for (offset
= fdt_next_node(fdt
, startoffset
, NULL
);
647 offset
= fdt_next_node(fdt
, offset
, NULL
)) {
648 val
= fdt_getprop(fdt
, offset
, propname
, &len
);
649 if (val
&& (len
== proplen
)
650 && (memcmp(val
, propval
, len
) == 0))
654 return offset
; /* error from fdt_next_node() */
657 int fdt_node_offset_by_phandle(const void *fdt
, uint32_t phandle
)
661 if ((phandle
== 0) || (phandle
== -1))
662 return -FDT_ERR_BADPHANDLE
;
666 /* FIXME: The algorithm here is pretty horrible: we
667 * potentially scan each property of a node in
668 * fdt_get_phandle(), then if that didn't find what
669 * we want, we scan over them again making our way to the next
670 * node. Still it's the easiest to implement approach;
671 * performance can come later. */
672 for (offset
= fdt_next_node(fdt
, -1, NULL
);
674 offset
= fdt_next_node(fdt
, offset
, NULL
)) {
675 if (fdt_get_phandle(fdt
, offset
) == phandle
)
679 return offset
; /* error from fdt_next_node() */
682 int fdt_stringlist_contains(const char *strlist
, int listlen
, const char *str
)
684 int len
= strlen(str
);
687 while (listlen
>= len
) {
688 if (memcmp(str
, strlist
, len
+1) == 0)
690 p
= memchr(strlist
, '\0', listlen
);
692 return 0; /* malformed strlist.. */
693 listlen
-= (p
-strlist
) + 1;
699 int fdt_stringlist_count(const void *fdt
, int nodeoffset
, const char *property
)
701 const char *list
, *end
;
702 int length
, count
= 0;
704 list
= fdt_getprop(fdt
, nodeoffset
, property
, &length
);
711 length
= strnlen(list
, end
- list
) + 1;
713 /* Abort if the last string isn't properly NUL-terminated. */
714 if (list
+ length
> end
)
715 return -FDT_ERR_BADVALUE
;
724 int fdt_stringlist_search(const void *fdt
, int nodeoffset
, const char *property
,
727 int length
, len
, idx
= 0;
728 const char *list
, *end
;
730 list
= fdt_getprop(fdt
, nodeoffset
, property
, &length
);
734 len
= strlen(string
) + 1;
738 length
= strnlen(list
, end
- list
) + 1;
740 /* Abort if the last string isn't properly NUL-terminated. */
741 if (list
+ length
> end
)
742 return -FDT_ERR_BADVALUE
;
744 if (length
== len
&& memcmp(list
, string
, length
) == 0)
751 return -FDT_ERR_NOTFOUND
;
754 const char *fdt_stringlist_get(const void *fdt
, int nodeoffset
,
755 const char *property
, int idx
,
758 const char *list
, *end
;
761 list
= fdt_getprop(fdt
, nodeoffset
, property
, &length
);
772 length
= strnlen(list
, end
- list
) + 1;
774 /* Abort if the last string isn't properly NUL-terminated. */
775 if (list
+ length
> end
) {
777 *lenp
= -FDT_ERR_BADVALUE
;
794 *lenp
= -FDT_ERR_NOTFOUND
;
799 int fdt_node_check_compatible(const void *fdt
, int nodeoffset
,
800 const char *compatible
)
805 prop
= fdt_getprop(fdt
, nodeoffset
, "compatible", &len
);
809 return !fdt_stringlist_contains(prop
, len
, compatible
);
812 int fdt_node_offset_by_compatible(const void *fdt
, int startoffset
,
813 const char *compatible
)
819 /* FIXME: The algorithm here is pretty horrible: we scan each
820 * property of a node in fdt_node_check_compatible(), then if
821 * that didn't find what we want, we scan over them again
822 * making our way to the next node. Still it's the easiest to
823 * implement approach; performance can come later. */
824 for (offset
= fdt_next_node(fdt
, startoffset
, NULL
);
826 offset
= fdt_next_node(fdt
, offset
, NULL
)) {
827 err
= fdt_node_check_compatible(fdt
, offset
, compatible
);
828 if ((err
< 0) && (err
!= -FDT_ERR_NOTFOUND
))
834 return offset
; /* error from fdt_next_node() */
837 int fdt_check_full(const void *fdt
, size_t bufsize
)
841 int offset
, nextoffset
= 0;
845 const char *propname
;
847 if (bufsize
< FDT_V1_SIZE
)
848 return -FDT_ERR_TRUNCATED
;
849 err
= fdt_check_header(fdt
);
852 if (bufsize
< fdt_totalsize(fdt
))
853 return -FDT_ERR_TRUNCATED
;
855 num_memrsv
= fdt_num_mem_rsv(fdt
);
861 tag
= fdt_next_tag(fdt
, offset
, &nextoffset
);
872 return -FDT_ERR_BADSTRUCTURE
;
878 return -FDT_ERR_BADSTRUCTURE
;
883 return -FDT_ERR_BADSTRUCTURE
;
888 prop
= fdt_getprop_by_offset(fdt
, offset
, &propname
,
895 return -FDT_ERR_INTERNAL
;