Add limited read-only support for older (V2 and V3) device tree to libfdt.
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.
FIXME: Need to add strrchr routien for 32 bit arm.
Signed-off-by: Nathan Whitehorn <nwhitehorn@freebsd.org>
[Rebased to current kernel libfdt version.]
Signed-off-by: Geoff Levand <geoff@infradead.org>
diff --git a/scripts/dtc/fdtget.c b/scripts/dtc/fdtget.c
index c2fbab2..f52567b 100644
--- a/scripts/dtc/fdtget.c
+++ b/scripts/dtc/fdtget.c
@@ -121,7 +121,6 @@
*/
static int list_properties(const void *blob, int node)
{
- const struct fdt_property *data;
const char *name;
int prop;
@@ -130,8 +129,7 @@
/* Stop silently when there are no more properties */
if (prop < 0)
return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
- data = fdt_get_property_by_offset(blob, prop, NULL);
- name = fdt_string(blob, fdt32_to_cpu(data->nameoff));
+ fdt_getprop_by_offset(blob, prop, &name, NULL);
if (name)
puts(name);
prop = fdt_next_property_offset(blob, prop);
diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c
index 22286a1..b443ddb 100644
--- a/scripts/dtc/libfdt/fdt.c
+++ b/scripts/dtc/libfdt/fdt.c
@@ -84,8 +84,9 @@
return NULL;
if (fdt_version(fdt) >= 0x11)
- if (((offset + len) < offset)
- || ((offset + len) > fdt_size_dt_struct(fdt)))
+ if (((offset + len) < offset) ||
+ (fdt_version(fdt) >= 0x10 &&
+ (offset + len) > fdt_size_dt_struct(fdt)))
return NULL;
return _fdt_offset_ptr(fdt, offset);
@@ -123,6 +124,9 @@
/* 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/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c
index 08de2cc..e5a8d72 100644
--- a/scripts/dtc/libfdt/fdt_ro.c
+++ b/scripts/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,31 @@
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;
- if (len)
- *len = strlen(nh->name);
+ nameptr = nh->name;
- return nh->name;
+ if (fdt_version(fdt) < 0x10 && nodeoffset != 0) {
+ /*
+ * For old FDT versions, match the naming conventions of V16:
+ * give only the leaf name (after all /) except for the
+ * root node, where we should still return / rather than ""
+ */
+ const char *leaf;
+ leaf = strrchr(nameptr, '/');
+ if (leaf != NULL)
+ nameptr = leaf+1;
+ }
+
+ if (len)
+ *len = strlen(nameptr);
+
+ return nameptr;
fail:
if (len)
@@ -268,9 +284,9 @@
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,11 +305,35 @@
return prop;
}
+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);
+}
+
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;
+ }
+
for (offset = fdt_first_property_offset(fdt, offset);
(offset >= 0);
(offset = fdt_next_property_offset(fdt, offset))) {
@@ -324,12 +364,33 @@
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
const char *name, int namelen, int *lenp)
{
- const struct fdt_property *prop;
+ const struct fdt_property *prop = NULL;
+ int offset = nodeoffset;
- prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
- if (!prop)
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ if (!(prop = _fdt_get_property_by_offset(fdt, offset, lenp))) {
+ if (lenp)
+ *lenp = -FDT_ERR_INTERNAL;
+ return NULL;
+ }
+
+ if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
+ name, namelen))
+ break;
+ }
+
+ if (lenp && offset < 0)
+ *lenp = offset;
+
+ if (!prop || offset < 0)
return NULL;
+ /* Handle realignment */
+ if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
+ fdt32_to_cpu(prop->len) > 8)
+ return prop->data + 4;
return prop->data;
}
@@ -338,11 +399,15 @@
{
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 (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
+ fdt32_to_cpu(prop->len) > 8)
+ return prop->data + 4;
return prop->data;
}
diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h
index 7f83023..9a63b04 100644
--- a/scripts/dtc/libfdt/libfdt.h
+++ b/scripts/dtc/libfdt/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 @@
* 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