From c13e400a31fceb64bd985ac48ef9c52b7502318b Mon Sep 17 00:00:00 2001 From: Nathan Whitehorn Date: Thu, 25 Jan 2018 05:13:40 +0000 Subject: 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. Signed-off-by: Nathan Whitehorn [dwg: Some whitespace cleanups] Signed-off-by: David Gibson --- cpukit/dtc/libfdt/fdt.c | 3 ++ cpukit/dtc/libfdt/fdt_ro.c | 100 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 15 deletions(-) (limited to 'cpukit/dtc') 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; } -- cgit v1.2.3