1 // SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
6 #include <linux/libfdt_env.h>
10 #include <linux/libfdt.h>
15 #include "libfdt_internal.h"
17 static int fdt_nodename_eq_(const void *fdt, int offset,
18 const char *s, int len)
21 const char *p = fdt_get_name(fdt, offset, &olen);
23 if (!p || (fdt_chk_extra() && olen < len))
27 if (memcmp(p, s, len) != 0)
32 else if (!memchr(s, '@', len) && (p[len] == '@'))
38 const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
46 if (!fdt_chk_extra()) {
47 s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
53 totalsize = fdt_ro_probe_(fdt);
58 err = -FDT_ERR_BADOFFSET;
59 absoffset = stroffset + fdt_off_dt_strings(fdt);
60 if (absoffset >= totalsize)
62 len = totalsize - absoffset;
64 if (fdt_magic(fdt) == FDT_MAGIC) {
67 if (!fdt_chk_version() || fdt_version(fdt) >= 17) {
68 if (stroffset >= fdt_size_dt_strings(fdt))
70 if ((fdt_size_dt_strings(fdt) - stroffset) < len)
71 len = fdt_size_dt_strings(fdt) - stroffset;
73 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
75 || (stroffset < -fdt_size_dt_strings(fdt)))
77 if ((-stroffset) < len)
80 err = -FDT_ERR_INTERNAL;
84 s = (const char *)fdt + absoffset;
85 n = memchr(s, '\0', len);
87 /* missing terminating NULL */
88 err = -FDT_ERR_TRUNCATED;
102 const char *fdt_string(const void *fdt, int stroffset)
104 return fdt_get_string(fdt, stroffset, NULL);
107 static int fdt_string_eq_(const void *fdt, int stroffset,
108 const char *s, int len)
111 const char *p = fdt_get_string(fdt, stroffset, &slen);
113 return p && (slen == len) && (memcmp(p, s, len) == 0);
116 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
124 offset = fdt_next_node(fdt, offset, NULL);
126 if (offset == -FDT_ERR_NOTFOUND)
132 value = fdt_get_phandle(fdt, offset);
144 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
149 err = fdt_find_max_phandle(fdt, &max);
153 if (max == FDT_MAX_PHANDLE)
154 return -FDT_ERR_NOPHANDLES;
162 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
164 int offset = n * sizeof(struct fdt_reserve_entry);
165 int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
167 if (fdt_chk_extra()) {
168 if (absoffset < fdt_off_mem_rsvmap(fdt))
170 if (absoffset > fdt_totalsize(fdt) -
171 sizeof(struct fdt_reserve_entry))
174 return fdt_mem_rsv_(fdt, n);
177 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
179 const struct fdt_reserve_entry *re;
182 re = fdt_mem_rsv(fdt, n);
183 if (fdt_chk_extra() && !re)
184 return -FDT_ERR_BADOFFSET;
186 *address = fdt64_ld(&re->address);
187 *size = fdt64_ld(&re->size);
191 int fdt_num_mem_rsv(const void *fdt)
194 const struct fdt_reserve_entry *re;
196 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
197 if (fdt64_ld(&re->size) == 0)
200 return -FDT_ERR_TRUNCATED;
203 static int nextprop_(const void *fdt, int offset)
209 tag = fdt_next_tag(fdt, offset, &nextoffset);
214 return -FDT_ERR_BADSTRUCTURE;
222 } while (tag == FDT_NOP);
224 return -FDT_ERR_NOTFOUND;
227 int fdt_subnode_offset_namelen(const void *fdt, int offset,
228 const char *name, int namelen)
235 (offset >= 0) && (depth >= 0);
236 offset = fdt_next_node(fdt, offset, &depth))
238 && fdt_nodename_eq_(fdt, offset, name, namelen))
242 return -FDT_ERR_NOTFOUND;
243 return offset; /* error */
246 int fdt_subnode_offset(const void *fdt, int parentoffset,
249 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
252 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
254 const char *end = path + namelen;
255 const char *p = path;
260 /* see if we have an alias */
262 const char *q = memchr(path, '/', end - p);
267 p = fdt_get_alias_namelen(fdt, p, q - p);
269 return -FDT_ERR_BADPATH;
270 offset = fdt_path_offset(fdt, p);
283 q = memchr(p, '/', end - p);
287 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
297 int fdt_path_offset(const void *fdt, const char *path)
299 return fdt_path_offset_namelen(fdt, path, strlen(path));
302 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
304 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
308 if (fdt_chk_extra() &&
309 (((err = fdt_ro_probe_(fdt)) < 0)
310 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
315 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
317 * For old FDT versions, match the naming conventions of V16:
318 * give only the leaf name (after all /). The actual tree
319 * contents are loosely checked.
322 leaf = strrchr(nameptr, '/');
324 err = -FDT_ERR_BADSTRUCTURE;
331 *len = strlen(nameptr);
341 int fdt_first_property_offset(const void *fdt, int nodeoffset)
345 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
348 return nextprop_(fdt, offset);
351 int fdt_next_property_offset(const void *fdt, int offset)
353 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
356 return nextprop_(fdt, offset);
359 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
364 const struct fdt_property *prop;
366 if (fdt_chk_basic() && (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
372 prop = fdt_offset_ptr_(fdt, offset);
375 *lenp = fdt32_ld(&prop->len);
380 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
384 /* Prior to version 16, properties may need realignment
385 * and this API does not work. fdt_getprop_*() will, however. */
387 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
389 *lenp = -FDT_ERR_BADVERSION;
393 return fdt_get_property_by_offset_(fdt, offset, lenp);
396 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
403 for (offset = fdt_first_property_offset(fdt, offset);
405 (offset = fdt_next_property_offset(fdt, offset))) {
406 const struct fdt_property *prop;
408 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
409 if (fdt_chk_extra() && !prop) {
410 offset = -FDT_ERR_INTERNAL;
413 if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
427 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
430 int namelen, int *lenp)
432 /* Prior to version 16, properties may need realignment
433 * and this API does not work. fdt_getprop_*() will, however. */
434 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
436 *lenp = -FDT_ERR_BADVERSION;
440 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
445 const struct fdt_property *fdt_get_property(const void *fdt,
447 const char *name, int *lenp)
449 return fdt_get_property_namelen(fdt, nodeoffset, name,
453 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
454 const char *name, int namelen, int *lenp)
457 const struct fdt_property *prop;
459 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
464 /* Handle realignment */
465 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
466 (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
467 return prop->data + 4;
471 const void *fdt_getprop_by_offset(const void *fdt, int offset,
472 const char **namep, int *lenp)
474 const struct fdt_property *prop;
476 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
483 if (fdt_chk_extra()) {
484 name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
493 *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff));
497 /* Handle realignment */
498 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
499 (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
500 return prop->data + 4;
504 const void *fdt_getprop(const void *fdt, int nodeoffset,
505 const char *name, int *lenp)
507 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
510 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
515 /* FIXME: This is a bit sub-optimal, since we potentially scan
516 * over all the properties twice. */
517 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
518 if (!php || (len != sizeof(*php))) {
519 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
520 if (!php || (len != sizeof(*php)))
524 return fdt32_ld(php);
527 const char *fdt_get_alias_namelen(const void *fdt,
528 const char *name, int namelen)
532 aliasoffset = fdt_path_offset(fdt, "/aliases");
536 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
539 const char *fdt_get_alias(const void *fdt, const char *name)
541 return fdt_get_alias_namelen(fdt, name, strlen(name));
544 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
546 int pdepth = 0, p = 0;
547 int offset, depth, namelen;
553 return -FDT_ERR_NOSPACE;
555 for (offset = 0, depth = 0;
556 (offset >= 0) && (offset <= nodeoffset);
557 offset = fdt_next_node(fdt, offset, &depth)) {
558 while (pdepth > depth) {
561 } while (buf[p-1] != '/');
565 if (pdepth >= depth) {
566 name = fdt_get_name(fdt, offset, &namelen);
569 if ((p + namelen + 1) <= buflen) {
570 memcpy(buf + p, name, namelen);
577 if (offset == nodeoffset) {
578 if (pdepth < (depth + 1))
579 return -FDT_ERR_NOSPACE;
581 if (p > 1) /* special case so that root path is "/", not "" */
588 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
589 return -FDT_ERR_BADOFFSET;
590 else if (offset == -FDT_ERR_BADOFFSET)
591 return -FDT_ERR_BADSTRUCTURE;
593 return offset; /* error from fdt_next_node() */
596 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
597 int supernodedepth, int *nodedepth)
600 int supernodeoffset = -FDT_ERR_INTERNAL;
604 if (supernodedepth < 0)
605 return -FDT_ERR_NOTFOUND;
607 for (offset = 0, depth = 0;
608 (offset >= 0) && (offset <= nodeoffset);
609 offset = fdt_next_node(fdt, offset, &depth)) {
610 if (depth == supernodedepth)
611 supernodeoffset = offset;
613 if (offset == nodeoffset) {
617 if (supernodedepth > depth)
618 return -FDT_ERR_NOTFOUND;
620 return supernodeoffset;
624 if (fdt_chk_extra()) {
625 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
626 return -FDT_ERR_BADOFFSET;
627 else if (offset == -FDT_ERR_BADOFFSET)
628 return -FDT_ERR_BADSTRUCTURE;
631 return offset; /* error from fdt_next_node() */
634 int fdt_node_depth(const void *fdt, int nodeoffset)
639 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
641 return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL;
645 int fdt_parent_offset(const void *fdt, int nodeoffset)
647 int nodedepth = fdt_node_depth(fdt, nodeoffset);
651 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
652 nodedepth - 1, NULL);
655 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
656 const char *propname,
657 const void *propval, int proplen)
665 /* FIXME: The algorithm here is pretty horrible: we scan each
666 * property of a node in fdt_getprop(), then if that didn't
667 * find what we want, we scan over them again making our way
668 * to the next node. Still it's the easiest to implement
669 * approach; performance can come later. */
670 for (offset = fdt_next_node(fdt, startoffset, NULL);
672 offset = fdt_next_node(fdt, offset, NULL)) {
673 val = fdt_getprop(fdt, offset, propname, &len);
674 if (val && (len == proplen)
675 && (memcmp(val, propval, len) == 0))
679 return offset; /* error from fdt_next_node() */
682 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
686 if ((phandle == 0) || (phandle == -1))
687 return -FDT_ERR_BADPHANDLE;
691 /* FIXME: The algorithm here is pretty horrible: we
692 * potentially scan each property of a node in
693 * fdt_get_phandle(), then if that didn't find what
694 * we want, we scan over them again making our way to the next
695 * node. Still it's the easiest to implement approach;
696 * performance can come later. */
697 for (offset = fdt_next_node(fdt, -1, NULL);
699 offset = fdt_next_node(fdt, offset, NULL)) {
700 if (fdt_get_phandle(fdt, offset) == phandle)
704 return offset; /* error from fdt_next_node() */
707 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
709 int len = strlen(str);
712 while (listlen >= len) {
713 if (memcmp(str, strlist, len+1) == 0)
715 p = memchr(strlist, '\0', listlen);
717 return 0; /* malformed strlist.. */
718 listlen -= (p-strlist) + 1;
724 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
726 const char *list, *end;
727 int length, count = 0;
729 list = fdt_getprop(fdt, nodeoffset, property, &length);
736 length = strnlen(list, end - list) + 1;
738 /* Abort if the last string isn't properly NUL-terminated. */
739 if (list + length > end)
740 return -FDT_ERR_BADVALUE;
749 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
752 int length, len, idx = 0;
753 const char *list, *end;
755 list = fdt_getprop(fdt, nodeoffset, property, &length);
759 len = strlen(string) + 1;
763 length = strnlen(list, end - list) + 1;
765 /* Abort if the last string isn't properly NUL-terminated. */
766 if (list + length > end)
767 return -FDT_ERR_BADVALUE;
769 if (length == len && memcmp(list, string, length) == 0)
776 return -FDT_ERR_NOTFOUND;
779 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
780 const char *property, int idx,
783 const char *list, *end;
786 list = fdt_getprop(fdt, nodeoffset, property, &length);
797 length = strnlen(list, end - list) + 1;
799 /* Abort if the last string isn't properly NUL-terminated. */
800 if (list + length > end) {
802 *lenp = -FDT_ERR_BADVALUE;
819 *lenp = -FDT_ERR_NOTFOUND;
824 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
825 const char *compatible)
830 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
834 return !fdt_stringlist_contains(prop, len, compatible);
837 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
838 const char *compatible)
844 /* FIXME: The algorithm here is pretty horrible: we scan each
845 * property of a node in fdt_node_check_compatible(), then if
846 * that didn't find what we want, we scan over them again
847 * making our way to the next node. Still it's the easiest to
848 * implement approach; performance can come later. */
849 for (offset = fdt_next_node(fdt, startoffset, NULL);
851 offset = fdt_next_node(fdt, offset, NULL)) {
852 err = fdt_node_check_compatible(fdt, offset, compatible);
853 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
859 return offset; /* error from fdt_next_node() */
862 #if !defined(CHECK_LEVEL) || CHECK_LEVEL > 0
863 int fdt_check_full(const void *fdt, size_t bufsize)
867 int offset, nextoffset = 0;
871 const char *propname;
873 if (bufsize < FDT_V1_SIZE)
874 return -FDT_ERR_TRUNCATED;
875 err = fdt_check_header(fdt);
878 if (bufsize < fdt_totalsize(fdt))
879 return -FDT_ERR_TRUNCATED;
881 num_memrsv = fdt_num_mem_rsv(fdt);
887 tag = fdt_next_tag(fdt, offset, &nextoffset);
898 return -FDT_ERR_BADSTRUCTURE;
904 return -FDT_ERR_BADSTRUCTURE;
909 return -FDT_ERR_BADSTRUCTURE;
914 prop = fdt_getprop_by_offset(fdt, offset, &propname,
921 return -FDT_ERR_INTERNAL;