fdt: Sync up to the latest libfdt
[oweals/u-boot.git] / lib / libfdt / fdt_ro.c
index 693de9aa5ad8032375b22749a25bc236dc15d950..560041b603e1a7a1928a5816bf76dc91bed38122 100644 (file)
 
 #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;
 
@@ -34,46 +35,85 @@ static int _fdt_nodename_eq(const void *fdt, int offset,
                return 0;
 }
 
-const char *fdt_string(const void *fdt, int stroffset)
-{
-       return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
-}
-
-static int _fdt_string_eq(const void *fdt, int stroffset,
-                         const char *s, int len)
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
 {
-       const char *p = fdt_string(fdt, stroffset);
+       int32_t totalsize;
+       uint32_t absoffset;
+       size_t len;
+       int err;
+       const char *s, *n;
 
-       return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
-}
+       if (!fdt_chk_extra()) {
+               s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
 
-uint32_t fdt_get_max_phandle(const void *fdt)
-{
-       uint32_t max_phandle = 0;
-       int offset;
+               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;
+       }
 
-       for (offset = fdt_next_node(fdt, -1, NULL);;
-            offset = fdt_next_node(fdt, offset, NULL)) {
-               uint32_t phandle;
+       s = (const char *)fdt + absoffset;
+       n = memchr(s, '\0', len);
+       if (!n) {
+               /* missing terminating NULL */
+               err = -FDT_ERR_TRUNCATED;
+               goto fail;
+       }
 
-               if (offset == -FDT_ERR_NOTFOUND)
-                       return max_phandle;
+       if (lenp)
+               *lenp = n - s;
+       return s;
 
-               if (offset < 0)
-                       return (uint32_t)-1;
+fail:
+       if (lenp)
+               *lenp = err;
+       return NULL;
+}
 
-               phandle = fdt_get_phandle(fdt, offset);
-               if (phandle == (uint32_t)-1)
-                       continue;
+const char *fdt_string(const void *fdt, int stroffset)
+{
+       return fdt_get_string(fdt, stroffset, NULL);
+}
 
-               if (phandle > max_phandle)
-                       max_phandle = phandle;
-       }
+static int fdt_string_eq_(const void *fdt, int stroffset,
+                         const char *s, int len)
+{
+       int slen;
+       const char *p = fdt_get_string(fdt, stroffset, &slen);
 
-       return 0;
+       return p && (slen == len) && (memcmp(p, s, len) == 0);
 }
 
-int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
 {
        uint32_t max = 0;
        int offset = -1;
@@ -95,6 +135,21 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
                        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;
 
@@ -104,24 +159,48 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
        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;
@@ -150,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)
@@ -170,36 +249,17 @@ int fdt_subnode_offset(const void *fdt, int parentoffset,
        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
 }
 
-/*
- * Find the next of path separator, note we need to search for both '/' and ':'
- * and then take the first one so that we do the right thing for e.g.
- * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
- * first searching for either ':' or '/' does not work.
- */
-static const char *fdt_path_next_separator(const char *path, int len)
-{
-       const void *sep1 = memchr(path, '/', len);
-       const void *sep2 = memchr(path, ':', len);
-
-       if (sep1 && sep2)
-               return (sep1 < sep2) ? sep1 : sep2;
-       else if (sep1)
-               return sep1;
-       else
-               return sep2;
-}
-
 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 {
        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 = fdt_path_next_separator(path, namelen);
+               const char *q = memchr(path, '/', end - p);
 
                if (!q)
                        q = end;
@@ -212,17 +272,16 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
                p = q;
        }
 
-       while (*p && (p < end)) {
+       while (p < end) {
                const char *q;
 
-               while (*p == '/')
+               while (*p == '/') {
                        p++;
-
-               if (*p == '\0' || *p == ':')
-                       return offset;
-
-               q = fdt_path_next_separator(p, end - p);
-               if (!q)
+                       if (p == end)
+                               return offset;
+               }
+               q = memchr(p, '/', end - p);
+               if (! q)
                        q = end;
 
                offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
@@ -243,16 +302,35 @@ int fdt_path_offset(const void *fdt, const char *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 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)
@@ -267,7 +345,7 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset)
        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)
@@ -275,17 +353,17 @@ int fdt_next_property_offset(const void *fdt, int offset)
        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;
@@ -294,28 +372,50 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
        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)
@@ -323,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)
@@ -334,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);
+       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;
 }
 
@@ -348,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;
 }
 
@@ -376,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,
@@ -402,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;
@@ -454,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;
@@ -476,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() */
 }
@@ -491,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;
 }
 
@@ -513,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
@@ -539,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
@@ -692,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
@@ -711,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