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);
19 if (!p || (fdt_chk_extra() && olen < len))
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)
42 if (!fdt_chk_extra()) {
43 s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
49 totalsize = fdt_ro_probe_(fdt);
54 err = -FDT_ERR_BADOFFSET;
55 absoffset = stroffset + fdt_off_dt_strings(fdt);
56 if (absoffset >= totalsize)
58 len = totalsize - absoffset;
60 if (fdt_magic(fdt) == FDT_MAGIC) {
63 if (!fdt_chk_version() || fdt_version(fdt) >= 17) {
64 if (stroffset >= fdt_size_dt_strings(fdt))
66 if ((fdt_size_dt_strings(fdt) - stroffset) < len)
67 len = fdt_size_dt_strings(fdt) - stroffset;
69 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
71 || (stroffset < -fdt_size_dt_strings(fdt)))
73 if ((-stroffset) < len)
76 err = -FDT_ERR_INTERNAL;
80 s = (const char *)fdt + absoffset;
81 n = memchr(s, '\0', len);
83 /* missing terminating NULL */
84 err = -FDT_ERR_TRUNCATED;
98 const char *fdt_string(const void *fdt, int stroffset)
100 return fdt_get_string(fdt, stroffset, NULL);
103 static int fdt_string_eq_(const void *fdt, int stroffset,
104 const char *s, int len)
107 const char *p = fdt_get_string(fdt, stroffset, &slen);
109 return p && (slen == len) && (memcmp(p, s, len) == 0);
112 int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
120 offset = fdt_next_node(fdt, offset, NULL);
122 if (offset == -FDT_ERR_NOTFOUND)
128 value = fdt_get_phandle(fdt, offset);
140 int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
145 err = fdt_find_max_phandle(fdt, &max);
149 if (max == FDT_MAX_PHANDLE)
150 return -FDT_ERR_NOPHANDLES;
158 static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
160 int offset = n * sizeof(struct fdt_reserve_entry);
161 int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
163 if (fdt_chk_extra()) {
164 if (absoffset < fdt_off_mem_rsvmap(fdt))
166 if (absoffset > fdt_totalsize(fdt) -
167 sizeof(struct fdt_reserve_entry))
170 return fdt_mem_rsv_(fdt, n);
173 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
175 const struct fdt_reserve_entry *re;
178 re = fdt_mem_rsv(fdt, n);
179 if (fdt_chk_extra() && !re)
180 return -FDT_ERR_BADOFFSET;
182 *address = fdt64_to_cpu(re->address);
183 *size = fdt64_to_cpu(re->size);
187 int fdt_num_mem_rsv(const void *fdt)
190 const struct fdt_reserve_entry *re;
192 for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
193 if (fdt64_to_cpu(re->size) == 0)
196 return -FDT_ERR_TRUNCATED;
199 static int nextprop_(const void *fdt, int offset)
205 tag = fdt_next_tag(fdt, offset, &nextoffset);
210 return -FDT_ERR_BADSTRUCTURE;
218 } while (tag == FDT_NOP);
220 return -FDT_ERR_NOTFOUND;
223 int fdt_subnode_offset_namelen(const void *fdt, int offset,
224 const char *name, int namelen)
231 (offset >= 0) && (depth >= 0);
232 offset = fdt_next_node(fdt, offset, &depth))
234 && fdt_nodename_eq_(fdt, offset, name, namelen))
238 return -FDT_ERR_NOTFOUND;
239 return offset; /* error */
242 int fdt_subnode_offset(const void *fdt, int parentoffset,
245 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
248 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
250 const char *end = path + namelen;
251 const char *p = path;
256 /* see if we have an alias */
258 const char *q = memchr(path, '/', end - p);
263 p = fdt_get_alias_namelen(fdt, p, q - p);
265 return -FDT_ERR_BADPATH;
266 offset = fdt_path_offset(fdt, p);
279 q = memchr(p, '/', end - p);
283 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
293 int fdt_path_offset(const void *fdt, const char *path)
295 return fdt_path_offset_namelen(fdt, path, strlen(path));
298 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
300 const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
304 if (fdt_chk_extra() &&
305 (((err = fdt_ro_probe_(fdt)) < 0)
306 || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
311 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
313 * For old FDT versions, match the naming conventions of V16:
314 * give only the leaf name (after all /). The actual tree
315 * contents are loosely checked.
318 leaf = strrchr(nameptr, '/');
320 err = -FDT_ERR_BADSTRUCTURE;
327 *len = strlen(nameptr);
337 int fdt_first_property_offset(const void *fdt, int nodeoffset)
341 if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
344 return nextprop_(fdt, offset);
347 int fdt_next_property_offset(const void *fdt, int offset)
349 if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
352 return nextprop_(fdt, offset);
355 static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
360 const struct fdt_property *prop;
362 if (fdt_chk_basic() && (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
368 prop = fdt_offset_ptr_(fdt, offset);
371 *lenp = fdt32_to_cpu(prop->len);
376 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
380 /* Prior to version 16, properties may need realignment
381 * and this API does not work. fdt_getprop_*() will, however. */
383 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
385 *lenp = -FDT_ERR_BADVERSION;
389 return fdt_get_property_by_offset_(fdt, offset, lenp);
392 static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
399 for (offset = fdt_first_property_offset(fdt, offset);
401 (offset = fdt_next_property_offset(fdt, offset))) {
402 const struct fdt_property *prop;
404 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
405 if (fdt_chk_extra() && !prop) {
406 offset = -FDT_ERR_INTERNAL;
409 if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
423 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
426 int namelen, int *lenp)
428 /* Prior to version 16, properties may need realignment
429 * and this API does not work. fdt_getprop_*() will, however. */
430 if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
432 *lenp = -FDT_ERR_BADVERSION;
436 return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
441 const struct fdt_property *fdt_get_property(const void *fdt,
443 const char *name, int *lenp)
445 return fdt_get_property_namelen(fdt, nodeoffset, name,
449 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
450 const char *name, int namelen, int *lenp)
453 const struct fdt_property *prop;
455 prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
460 /* Handle realignment */
461 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
462 (poffset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
463 return prop->data + 4;
467 const void *fdt_getprop_by_offset(const void *fdt, int offset,
468 const char **namep, int *lenp)
470 const struct fdt_property *prop;
472 prop = fdt_get_property_by_offset_(fdt, offset, lenp);
479 if (fdt_chk_extra()) {
480 name = fdt_get_string(fdt, fdt32_to_cpu(prop->nameoff),
489 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
493 /* Handle realignment */
494 if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
495 (offset + sizeof(*prop)) % 8 && fdt32_to_cpu(prop->len) >= 8)
496 return prop->data + 4;
500 const void *fdt_getprop(const void *fdt, int nodeoffset,
501 const char *name, int *lenp)
503 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
506 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
511 /* FIXME: This is a bit sub-optimal, since we potentially scan
512 * over all the properties twice. */
513 php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
514 if (!php || (len != sizeof(*php))) {
515 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
516 if (!php || (len != sizeof(*php)))
520 return fdt32_to_cpu(*php);
523 const char *fdt_get_alias_namelen(const void *fdt,
524 const char *name, int namelen)
528 aliasoffset = fdt_path_offset(fdt, "/aliases");
532 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
535 const char *fdt_get_alias(const void *fdt, const char *name)
537 return fdt_get_alias_namelen(fdt, name, strlen(name));
540 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
542 int pdepth = 0, p = 0;
543 int offset, depth, namelen;
549 return -FDT_ERR_NOSPACE;
551 for (offset = 0, depth = 0;
552 (offset >= 0) && (offset <= nodeoffset);
553 offset = fdt_next_node(fdt, offset, &depth)) {
554 while (pdepth > depth) {
557 } while (buf[p-1] != '/');
561 if (pdepth >= depth) {
562 name = fdt_get_name(fdt, offset, &namelen);
565 if ((p + namelen + 1) <= buflen) {
566 memcpy(buf + p, name, namelen);
573 if (offset == nodeoffset) {
574 if (pdepth < (depth + 1))
575 return -FDT_ERR_NOSPACE;
577 if (p > 1) /* special case so that root path is "/", not "" */
584 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
585 return -FDT_ERR_BADOFFSET;
586 else if (offset == -FDT_ERR_BADOFFSET)
587 return -FDT_ERR_BADSTRUCTURE;
589 return offset; /* error from fdt_next_node() */
592 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
593 int supernodedepth, int *nodedepth)
596 int supernodeoffset = -FDT_ERR_INTERNAL;
600 if (supernodedepth < 0)
601 return -FDT_ERR_NOTFOUND;
603 for (offset = 0, depth = 0;
604 (offset >= 0) && (offset <= nodeoffset);
605 offset = fdt_next_node(fdt, offset, &depth)) {
606 if (depth == supernodedepth)
607 supernodeoffset = offset;
609 if (offset == nodeoffset) {
613 if (supernodedepth > depth)
614 return -FDT_ERR_NOTFOUND;
616 return supernodeoffset;
620 if (fdt_chk_extra()) {
621 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
622 return -FDT_ERR_BADOFFSET;
623 else if (offset == -FDT_ERR_BADOFFSET)
624 return -FDT_ERR_BADSTRUCTURE;
627 return offset; /* error from fdt_next_node() */
630 int fdt_node_depth(const void *fdt, int nodeoffset)
635 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
637 return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL;
641 int fdt_parent_offset(const void *fdt, int nodeoffset)
643 int nodedepth = fdt_node_depth(fdt, nodeoffset);
647 return fdt_supernode_atdepth_offset(fdt, nodeoffset,
648 nodedepth - 1, NULL);
651 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
652 const char *propname,
653 const void *propval, int proplen)
661 /* FIXME: The algorithm here is pretty horrible: we scan each
662 * property of a node in fdt_getprop(), then if that didn't
663 * find what we want, we scan over them again making our way
664 * to the next node. Still it's the easiest to implement
665 * approach; performance can come later. */
666 for (offset = fdt_next_node(fdt, startoffset, NULL);
668 offset = fdt_next_node(fdt, offset, NULL)) {
669 val = fdt_getprop(fdt, offset, propname, &len);
670 if (val && (len == proplen)
671 && (memcmp(val, propval, len) == 0))
675 return offset; /* error from fdt_next_node() */
678 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
682 if ((phandle == 0) || (phandle == -1))
683 return -FDT_ERR_BADPHANDLE;
687 /* FIXME: The algorithm here is pretty horrible: we
688 * potentially scan each property of a node in
689 * fdt_get_phandle(), then if that didn't find what
690 * we want, we scan over them again making our way to the next
691 * node. Still it's the easiest to implement approach;
692 * performance can come later. */
693 for (offset = fdt_next_node(fdt, -1, NULL);
695 offset = fdt_next_node(fdt, offset, NULL)) {
696 if (fdt_get_phandle(fdt, offset) == phandle)
700 return offset; /* error from fdt_next_node() */
703 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
705 int len = strlen(str);
708 while (listlen >= len) {
709 if (memcmp(str, strlist, len+1) == 0)
711 p = memchr(strlist, '\0', listlen);
713 return 0; /* malformed strlist.. */
714 listlen -= (p-strlist) + 1;
720 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
722 const char *list, *end;
723 int length, count = 0;
725 list = fdt_getprop(fdt, nodeoffset, property, &length);
732 length = strnlen(list, end - list) + 1;
734 /* Abort if the last string isn't properly NUL-terminated. */
735 if (list + length > end)
736 return -FDT_ERR_BADVALUE;
745 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
748 int length, len, idx = 0;
749 const char *list, *end;
751 list = fdt_getprop(fdt, nodeoffset, property, &length);
755 len = strlen(string) + 1;
759 length = strnlen(list, end - list) + 1;
761 /* Abort if the last string isn't properly NUL-terminated. */
762 if (list + length > end)
763 return -FDT_ERR_BADVALUE;
765 if (length == len && memcmp(list, string, length) == 0)
772 return -FDT_ERR_NOTFOUND;
775 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
776 const char *property, int idx,
779 const char *list, *end;
782 list = fdt_getprop(fdt, nodeoffset, property, &length);
793 length = strnlen(list, end - list) + 1;
795 /* Abort if the last string isn't properly NUL-terminated. */
796 if (list + length > end) {
798 *lenp = -FDT_ERR_BADVALUE;
815 *lenp = -FDT_ERR_NOTFOUND;
820 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
821 const char *compatible)
826 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
830 return !fdt_stringlist_contains(prop, len, compatible);
833 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
834 const char *compatible)
840 /* FIXME: The algorithm here is pretty horrible: we scan each
841 * property of a node in fdt_node_check_compatible(), then if
842 * that didn't find what we want, we scan over them again
843 * making our way to the next node. Still it's the easiest to
844 * implement approach; performance can come later. */
845 for (offset = fdt_next_node(fdt, startoffset, NULL);
847 offset = fdt_next_node(fdt, offset, NULL)) {
848 err = fdt_node_check_compatible(fdt, offset, compatible);
849 if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
855 return offset; /* error from fdt_next_node() */
858 #if !defined(FDT_ASSUME_MASK) || FDT_ASSUME_MASK != 0xff
859 int fdt_check_full(const void *fdt, size_t bufsize)
863 int offset, nextoffset = 0;
867 const char *propname;
869 if (bufsize < FDT_V1_SIZE)
870 return -FDT_ERR_TRUNCATED;
871 err = fdt_check_header(fdt);
874 if (bufsize < fdt_totalsize(fdt))
875 return -FDT_ERR_TRUNCATED;
877 num_memrsv = fdt_num_mem_rsv(fdt);
883 tag = fdt_next_tag(fdt, offset, &nextoffset);
894 return -FDT_ERR_BADSTRUCTURE;
900 return -FDT_ERR_BADSTRUCTURE;
905 return -FDT_ERR_BADSTRUCTURE;
910 prop = fdt_getprop_by_offset(fdt, offset, &propname,
917 return -FDT_ERR_INTERNAL;