fdt: Sync up to the latest libfdt
[oweals/u-boot.git] / lib / libfdt / fdt_ro.c
index b65f4e23ad7b019e37993282090b1a99aab36770..560041b603e1a7a1928a5816bf76dc91bed38122 100644 (file)
@@ -1,70 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
 /*
  * libfdt - Flat Device Tree manipulation
  * Copyright (C) 2006 David Gibson, IBM Corporation.
- *
- * libfdt is dual licensed: you can use it either under the terms of
- * the GPL, or the BSD license, at your option.
- *
- *  a) This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of the
- *     License, or (at your option) any later version.
- *
- *     This library is distributed in the hope that it will be useful,
- *     but WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *     GNU General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public
- *     License along with this library; if not, write to the Free
- *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
- *     MA 02110-1301 USA
- *
- * Alternatively,
- *
- *  b) Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *     1. Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *     2. Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-#include "libfdt_env.h"
+#include <linux/libfdt_env.h>
 
 #ifndef USE_HOSTCC
 #include <fdt.h>
-#include <libfdt.h>
+#include <linux/libfdt.h>
 #else
 #include "fdt_host.h"
 #endif
 
 #include "libfdt_internal.h"
 
-static int _fdt_nodename_eq(const void *fdt, int offset,
+static int fdt_nodename_eq_(const void *fdt, int offset,
                            const char *s, int len)
 {
-       const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
+       int olen;
+       const char *p = fdt_get_name(fdt, offset, &olen);
 
-       if (! p)
+       if (!p || (fdt_chk_extra() && olen < len))
                /* short match */
                return 0;
 
@@ -79,37 +35,172 @@ static int _fdt_nodename_eq(const void *fdt, int offset,
                return 0;
 }
 
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
+{
+       int32_t totalsize;
+       uint32_t absoffset;
+       size_t len;
+       int err;
+       const char *s, *n;
+
+       if (!fdt_chk_extra()) {
+               s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+
+               if (lenp)
+                       *lenp = strlen(s);
+               return s;
+       }
+       totalsize = fdt_ro_probe_(fdt);
+       err = totalsize;
+       if (totalsize < 0)
+               goto fail;
+
+       err = -FDT_ERR_BADOFFSET;
+       absoffset = stroffset + fdt_off_dt_strings(fdt);
+       if (absoffset >= totalsize)
+               goto fail;
+       len = totalsize - absoffset;
+
+       if (fdt_magic(fdt) == FDT_MAGIC) {
+               if (stroffset < 0)
+                       goto fail;
+               if (!fdt_chk_version() || fdt_version(fdt) >= 17) {
+                       if (stroffset >= fdt_size_dt_strings(fdt))
+                               goto fail;
+                       if ((fdt_size_dt_strings(fdt) - stroffset) < len)
+                               len = fdt_size_dt_strings(fdt) - stroffset;
+               }
+       } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+               if ((stroffset >= 0)
+                   || (stroffset < -fdt_size_dt_strings(fdt)))
+                       goto fail;
+               if ((-stroffset) < len)
+                       len = -stroffset;
+       } else {
+               err = -FDT_ERR_INTERNAL;
+               goto fail;
+       }
+
+       s = (const char *)fdt + absoffset;
+       n = memchr(s, '\0', len);
+       if (!n) {
+               /* missing terminating NULL */
+               err = -FDT_ERR_TRUNCATED;
+               goto fail;
+       }
+
+       if (lenp)
+               *lenp = n - s;
+       return s;
+
+fail:
+       if (lenp)
+               *lenp = err;
+       return NULL;
+}
+
 const char *fdt_string(const void *fdt, int stroffset)
 {
-       return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+       return fdt_get_string(fdt, stroffset, NULL);
 }
 
-static int _fdt_string_eq(const void *fdt, int stroffset,
+static int fdt_string_eq_(const void *fdt, int stroffset,
                          const char *s, int len)
 {
-       const char *p = fdt_string(fdt, stroffset);
+       int slen;
+       const char *p = fdt_get_string(fdt, stroffset, &slen);
+
+       return p && (slen == len) && (memcmp(p, s, len) == 0);
+}
+
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
+{
+       uint32_t max = 0;
+       int offset = -1;
 
-       return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+       while (true) {
+               uint32_t value;
+
+               offset = fdt_next_node(fdt, offset, NULL);
+               if (offset < 0) {
+                       if (offset == -FDT_ERR_NOTFOUND)
+                               break;
+
+                       return offset;
+               }
+
+               value = fdt_get_phandle(fdt, offset);
+
+               if (value > max)
+                       max = value;
+       }
+
+       if (phandle)
+               *phandle = max;
+
+       return 0;
+}
+
+int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
+{
+       uint32_t max;
+       int err;
+
+       err = fdt_find_max_phandle(fdt, &max);
+       if (err < 0)
+               return err;
+
+       if (max == FDT_MAX_PHANDLE)
+               return -FDT_ERR_NOPHANDLES;
+
+       if (phandle)
+               *phandle = max + 1;
+
+       return 0;
+}
+
+static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
+{
+       int offset = n * sizeof(struct fdt_reserve_entry);
+       int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
+
+       if (fdt_chk_extra()) {
+               if (absoffset < fdt_off_mem_rsvmap(fdt))
+                       return NULL;
+               if (absoffset > fdt_totalsize(fdt) -
+                   sizeof(struct fdt_reserve_entry))
+                       return NULL;
+       }
+       return fdt_mem_rsv_(fdt, n);
 }
 
 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
 {
-       FDT_CHECK_HEADER(fdt);
-       *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
-       *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+       const struct fdt_reserve_entry *re;
+
+       FDT_RO_PROBE(fdt);
+       re = fdt_mem_rsv(fdt, n);
+       if (fdt_chk_extra() && !re)
+               return -FDT_ERR_BADOFFSET;
+
+       *address = fdt64_ld(&re->address);
+       *size = fdt64_ld(&re->size);
        return 0;
 }
 
 int fdt_num_mem_rsv(const void *fdt)
 {
-       int i = 0;
+       int i;
+       const struct fdt_reserve_entry *re;
 
-       while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
-               i++;
-       return i;
+       for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
+               if (fdt64_ld(&re->size) == 0)
+                       return i;
+       }
+       return -FDT_ERR_TRUNCATED;
 }
 
-static int _nextprop(const void *fdt, int offset)
+static int nextprop_(const void *fdt, int offset)
 {
        uint32_t tag;
        int nextoffset;
@@ -138,13 +229,13 @@ int fdt_subnode_offset_namelen(const void *fdt, int offset,
 {
        int depth;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        for (depth = 0;
             (offset >= 0) && (depth >= 0);
             offset = fdt_next_node(fdt, offset, &depth))
                if ((depth == 1)
-                   && _fdt_nodename_eq(fdt, offset, name, namelen))
+                   && fdt_nodename_eq_(fdt, offset, name, namelen))
                        return offset;
 
        if (depth < 0)
@@ -158,17 +249,17 @@ int fdt_subnode_offset(const void *fdt, int parentoffset,
        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
 }
 
-int fdt_path_offset(const void *fdt, const char *path)
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 {
-       const char *end = path + strlen(path);
+       const char *end = path + namelen;
        const char *p = path;
        int offset = 0;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        /* see if we have an alias */
        if (*path != '/') {
-               const char *q = strchr(path, '/');
+               const char *q = memchr(path, '/', end - p);
 
                if (!q)
                        q = end;
@@ -181,14 +272,15 @@ int fdt_path_offset(const void *fdt, const char *path)
                p = q;
        }
 
-       while (*p) {
+       while (p < end) {
                const char *q;
 
-               while (*p == '/')
+               while (*p == '/') {
                        p++;
-               if (! *p)
-                       return offset;
-               q = strchr(p, '/');
+                       if (p == end)
+                               return offset;
+               }
+               q = memchr(p, '/', end - p);
                if (! q)
                        q = end;
 
@@ -202,19 +294,43 @@ int fdt_path_offset(const void *fdt, const char *path)
        return offset;
 }
 
+int fdt_path_offset(const void *fdt, const char *path)
+{
+       return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
 {
-       const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
+       const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
+       const char *nameptr;
        int err;
 
-       if (((err = fdt_check_header(fdt)) != 0)
-           || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
+       if (fdt_chk_extra() &&
+           (((err = fdt_ro_probe_(fdt)) < 0)
+            || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
+               goto fail;
+
+       nameptr = nh->name;
+
+       if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
+               /*
+                * For old FDT versions, match the naming conventions of V16:
+                * give only the leaf name (after all /). The actual tree
+                * contents are loosely checked.
+                */
+               const char *leaf;
+               leaf = strrchr(nameptr, '/');
+               if (leaf == NULL) {
+                       err = -FDT_ERR_BADSTRUCTURE;
                        goto fail;
+               }
+               nameptr = leaf+1;
+       }
 
        if (len)
-               *len = strlen(nh->name);
+               *len = strlen(nameptr);
 
-       return nh->name;
+       return nameptr;
 
  fail:
        if (len)
@@ -226,58 +342,80 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset)
 {
        int offset;
 
-       if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+       if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
                return offset;
 
-       return _nextprop(fdt, offset);
+       return nextprop_(fdt, offset);
 }
 
 int fdt_next_property_offset(const void *fdt, int offset)
 {
-       if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
+       if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
                return offset;
 
-       return _nextprop(fdt, offset);
+       return nextprop_(fdt, offset);
 }
 
-const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
-                                                     int offset,
-                                                     int *lenp)
+static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
+                                                             int offset,
+                                                             int *lenp)
 {
        int err;
        const struct fdt_property *prop;
 
-       if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
+       if (fdt_chk_basic() && (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
                if (lenp)
                        *lenp = err;
                return NULL;
        }
 
-       prop = _fdt_offset_ptr(fdt, offset);
+       prop = fdt_offset_ptr_(fdt, offset);
 
        if (lenp)
-               *lenp = fdt32_to_cpu(prop->len);
+               *lenp = fdt32_ld(&prop->len);
 
        return prop;
 }
 
-const struct fdt_property *fdt_get_property_namelen(const void *fdt,
-                                                   int offset,
-                                                   const char *name,
-                                                   int namelen, int *lenp)
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+                                                     int offset,
+                                                     int *lenp)
+{
+       /* Prior to version 16, properties may need realignment
+        * and this API does not work. fdt_getprop_*() will, however. */
+
+       if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
+               if (lenp)
+                       *lenp = -FDT_ERR_BADVERSION;
+               return NULL;
+       }
+
+       return fdt_get_property_by_offset_(fdt, offset, lenp);
+}
+
+static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
+                                                           int offset,
+                                                           const char *name,
+                                                           int namelen,
+                                                           int *lenp,
+                                                           int *poffset)
 {
        for (offset = fdt_first_property_offset(fdt, offset);
             (offset >= 0);
             (offset = fdt_next_property_offset(fdt, offset))) {
                const struct fdt_property *prop;
 
-               if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
+               prop = fdt_get_property_by_offset_(fdt, offset, lenp);
+               if (fdt_chk_extra() && !prop) {
                        offset = -FDT_ERR_INTERNAL;
                        break;
                }
-               if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
-                                  name, namelen))
+               if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
+                                  name, namelen)) {
+                       if (poffset)
+                               *poffset = offset;
                        return prop;
+               }
        }
 
        if (lenp)
@@ -285,6 +423,25 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt,
        return NULL;
 }
 
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+                                                   int offset,
+                                                   const char *name,
+                                                   int namelen, int *lenp)
+{
+       /* Prior to version 16, properties may need realignment
+        * and this API does not work. fdt_getprop_*() will, however. */
+       if (fdt_chk_version() && fdt_version(fdt) < 0x10) {
+               if (lenp)
+                       *lenp = -FDT_ERR_BADVERSION;
+               return NULL;
+       }
+
+       return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
+                                        NULL);
+}
+
+
 const struct fdt_property *fdt_get_property(const void *fdt,
                                            int nodeoffset,
                                            const char *name, int *lenp)
@@ -296,12 +453,18 @@ const struct fdt_property *fdt_get_property(const void *fdt,
 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
                                const char *name, int namelen, int *lenp)
 {
+       int poffset;
        const struct fdt_property *prop;
 
-       prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
-       if (! prop)
+       prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
+                                        &poffset);
+       if (!prop)
                return NULL;
 
+       /* Handle realignment */
+       if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
+           (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
+               return prop->data + 4;
        return prop->data;
 }
 
@@ -310,11 +473,31 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
 {
        const struct fdt_property *prop;
 
-       prop = fdt_get_property_by_offset(fdt, offset, lenp);
+       prop = fdt_get_property_by_offset_(fdt, offset, lenp);
        if (!prop)
                return NULL;
-       if (namep)
-               *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+       if (namep) {
+               const char *name;
+               int namelen;
+
+               if (fdt_chk_extra()) {
+                       name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
+                                             &namelen);
+                       if (!name) {
+                               if (lenp)
+                                       *lenp = namelen;
+                               return NULL;
+                       }
+                       *namep = name;
+               } else {
+                       *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff));
+               }
+       }
+
+       /* Handle realignment */
+       if (fdt_chk_version() && fdt_version(fdt) < 0x10 &&
+           (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
+               return prop->data + 4;
        return prop->data;
 }
 
@@ -338,7 +521,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
                        return 0;
        }
 
-       return fdt32_to_cpu(*php);
+       return fdt32_ld(php);
 }
 
 const char *fdt_get_alias_namelen(const void *fdt,
@@ -364,7 +547,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
        int offset, depth, namelen;
        const char *name;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        if (buflen < 2)
                return -FDT_ERR_NOSPACE;
@@ -416,7 +599,7 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
        int offset, depth;
        int supernodeoffset = -FDT_ERR_INTERNAL;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        if (supernodedepth < 0)
                return -FDT_ERR_NOTFOUND;
@@ -438,10 +621,12 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
                }
        }
 
-       if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
-               return -FDT_ERR_BADOFFSET;
-       else if (offset == -FDT_ERR_BADOFFSET)
-               return -FDT_ERR_BADSTRUCTURE;
+       if (fdt_chk_extra()) {
+               if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+                       return -FDT_ERR_BADOFFSET;
+               else if (offset == -FDT_ERR_BADOFFSET)
+                       return -FDT_ERR_BADSTRUCTURE;
+       }
 
        return offset; /* error from fdt_next_node() */
 }
@@ -453,7 +638,7 @@ int fdt_node_depth(const void *fdt, int nodeoffset)
 
        err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
        if (err)
-               return (err < 0) ? err : -FDT_ERR_INTERNAL;
+               return (!fdt_chk_extra() || err < 0) ? err : -FDT_ERR_INTERNAL;
        return nodedepth;
 }
 
@@ -475,7 +660,7 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
        const void *val;
        int len;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        /* FIXME: The algorithm here is pretty horrible: we scan each
         * property of a node in fdt_getprop(), then if that didn't
@@ -501,7 +686,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
        if ((phandle == 0) || (phandle == -1))
                return -FDT_ERR_BADPHANDLE;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        /* FIXME: The algorithm here is pretty horrible: we
         * potentially scan each property of a node in
@@ -536,6 +721,106 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
        return 0;
 }
 
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
+{
+       const char *list, *end;
+       int length, count = 0;
+
+       list = fdt_getprop(fdt, nodeoffset, property, &length);
+       if (!list)
+               return length;
+
+       end = list + length;
+
+       while (list < end) {
+               length = strnlen(list, end - list) + 1;
+
+               /* Abort if the last string isn't properly NUL-terminated. */
+               if (list + length > end)
+                       return -FDT_ERR_BADVALUE;
+
+               list += length;
+               count++;
+       }
+
+       return count;
+}
+
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+                         const char *string)
+{
+       int length, len, idx = 0;
+       const char *list, *end;
+
+       list = fdt_getprop(fdt, nodeoffset, property, &length);
+       if (!list)
+               return length;
+
+       len = strlen(string) + 1;
+       end = list + length;
+
+       while (list < end) {
+               length = strnlen(list, end - list) + 1;
+
+               /* Abort if the last string isn't properly NUL-terminated. */
+               if (list + length > end)
+                       return -FDT_ERR_BADVALUE;
+
+               if (length == len && memcmp(list, string, length) == 0)
+                       return idx;
+
+               list += length;
+               idx++;
+       }
+
+       return -FDT_ERR_NOTFOUND;
+}
+
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+                              const char *property, int idx,
+                              int *lenp)
+{
+       const char *list, *end;
+       int length;
+
+       list = fdt_getprop(fdt, nodeoffset, property, &length);
+       if (!list) {
+               if (lenp)
+                       *lenp = length;
+
+               return NULL;
+       }
+
+       end = list + length;
+
+       while (list < end) {
+               length = strnlen(list, end - list) + 1;
+
+               /* Abort if the last string isn't properly NUL-terminated. */
+               if (list + length > end) {
+                       if (lenp)
+                               *lenp = -FDT_ERR_BADVALUE;
+
+                       return NULL;
+               }
+
+               if (idx == 0) {
+                       if (lenp)
+                               *lenp = length - 1;
+
+                       return list;
+               }
+
+               list += length;
+               idx--;
+       }
+
+       if (lenp)
+               *lenp = -FDT_ERR_NOTFOUND;
+
+       return NULL;
+}
+
 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
                              const char *compatible)
 {
@@ -545,10 +830,8 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
        prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
        if (!prop)
                return len;
-       if (fdt_stringlist_contains(prop, len, compatible))
-               return 0;
-       else
-               return 1;
+
+       return !fdt_stringlist_contains(prop, len, compatible);
 }
 
 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
@@ -556,7 +839,7 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
 {
        int offset, err;
 
-       FDT_CHECK_HEADER(fdt);
+       FDT_RO_PROBE(fdt);
 
        /* FIXME: The algorithm here is pretty horrible: we scan each
         * property of a node in fdt_node_check_compatible(), then if
@@ -575,3 +858,68 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
 
        return offset; /* error from fdt_next_node() */
 }
+
+#if !defined(CHECK_LEVEL) || CHECK_LEVEL > 0
+int fdt_check_full(const void *fdt, size_t bufsize)
+{
+       int err;
+       int num_memrsv;
+       int offset, nextoffset = 0;
+       uint32_t tag;
+       unsigned depth = 0;
+       const void *prop;
+       const char *propname;
+
+       if (bufsize < FDT_V1_SIZE)
+               return -FDT_ERR_TRUNCATED;
+       err = fdt_check_header(fdt);
+       if (err != 0)
+               return err;
+       if (bufsize < fdt_totalsize(fdt))
+               return -FDT_ERR_TRUNCATED;
+
+       num_memrsv = fdt_num_mem_rsv(fdt);
+       if (num_memrsv < 0)
+               return num_memrsv;
+
+       while (1) {
+               offset = nextoffset;
+               tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+               if (nextoffset < 0)
+                       return nextoffset;
+
+               switch (tag) {
+               case FDT_NOP:
+                       break;
+
+               case FDT_END:
+                       if (depth != 0)
+                               return -FDT_ERR_BADSTRUCTURE;
+                       return 0;
+
+               case FDT_BEGIN_NODE:
+                       depth++;
+                       if (depth > INT_MAX)
+                               return -FDT_ERR_BADSTRUCTURE;
+                       break;
+
+               case FDT_END_NODE:
+                       if (depth == 0)
+                               return -FDT_ERR_BADSTRUCTURE;
+                       depth--;
+                       break;
+
+               case FDT_PROP:
+                       prop = fdt_getprop_by_offset(fdt, offset, &propname,
+                                                    &err);
+                       if (!prop)
+                               return err;
+                       break;
+
+               default:
+                       return -FDT_ERR_INTERNAL;
+               }
+       }
+}
+#endif