From: Nathan Whitehorn <nwhiteh...@freebsd.org>

This can be useful in particular in the kernel when booting on systems
with FDT-emitting firmware that is out of date. Releases of kexec-tools
on ppc64 prior to the end of 2014 are notable examples of such.
Signed-off-by: Nathan Whitehorn <nwhiteh...@freebsd.org>
[dwg: Some whitespace cleanups]
Signed-off-by: David Gibson <da...@gibson.dropbear.id.au>
---
 cpukit/dtc/libfdt/fdt.c    |   3 ++
 cpukit/dtc/libfdt/fdt_ro.c | 100 ++++++++++++++++++++++++++++++++++++++-------
 cpukit/include/libfdt.h    |   5 ++-
 3 files changed, 92 insertions(+), 16 deletions(-)

diff --git a/cpukit/dtc/libfdt/fdt.c b/cpukit/dtc/libfdt/fdt.c
index fd13236736..7855a17877 100644
--- a/cpukit/dtc/libfdt/fdt.c
+++ b/cpukit/dtc/libfdt/fdt.c
@@ -123,6 +123,9 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int 
*nextoffset)
                /* skip-name offset, length and value */
                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
                        + fdt32_to_cpu(*lenp);
+               if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
+                   ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
+                       offset += 4;
                break;
 
        case FDT_END:
diff --git a/cpukit/dtc/libfdt/fdt_ro.c b/cpukit/dtc/libfdt/fdt_ro.c
index ce17814b2b..dfb3236da3 100644
--- a/cpukit/dtc/libfdt/fdt_ro.c
+++ b/cpukit/dtc/libfdt/fdt_ro.c
@@ -58,9 +58,10 @@
 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 || olen < len)
                /* short match */
                return 0;
 
@@ -233,16 +234,34 @@ 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))
                        goto fail;
 
+       nameptr = nh->name;
+
+       if (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)
@@ -268,9 +287,9 @@ int fdt_next_property_offset(const void *fdt, int 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;
@@ -289,23 +308,44 @@ const struct fdt_property 
*fdt_get_property_by_offset(const void *fdt,
        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_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))) {
+               if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
                        offset = -FDT_ERR_INTERNAL;
                        break;
                }
                if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
-                                  name, namelen))
+                                  name, namelen)) {
+                       if (poffset)
+                               *poffset = offset;
                        return prop;
+               }
        }
 
        if (lenp)
@@ -313,6 +353,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_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)
@@ -324,12 +383,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_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
+           fdt32_to_cpu(prop->len) >= 8)
+               return prop->data + 4;
        return prop->data;
 }
 
@@ -338,11 +403,16 @@ 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));
+
+       /* Handle realignment */
+       if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
+           fdt32_to_cpu(prop->len) >= 8)
+               return prop->data + 4;
        return prop->data;
 }
 
diff --git a/cpukit/include/libfdt.h b/cpukit/include/libfdt.h
index baa882cfa1..c8c00fa865 100644
--- a/cpukit/include/libfdt.h
+++ b/cpukit/include/libfdt.h
@@ -54,7 +54,7 @@
 #include <libfdt_env.h>
 #include <fdt.h>
 
-#define FDT_FIRST_SUPPORTED_VERSION    0x10
+#define FDT_FIRST_SUPPORTED_VERSION    0x02
 #define FDT_LAST_SUPPORTED_VERSION     0x11
 
 /* Error codes: informative error codes */
@@ -527,6 +527,9 @@ int fdt_next_property_offset(const void *fdt, int offset);
  * offset.  If lenp is non-NULL, the length of the property value is
  * also returned, in the integer pointed to by lenp.
  *
+ * Note that this code only works on device tree versions >= 16. fdt_getprop()
+ * works on all versions.
+ *
  * returns:
  *     pointer to the structure representing the property
  *             if lenp is non-NULL, *lenp contains the length of the property
-- 
2.13.7

_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to