Browse Source

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 <nwhitehorn@freebsd.org>
[dwg: Some whitespace cleanups]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
main
Nathan Whitehorn 7 years ago committed by David Gibson
parent
commit
f1879e1a50
  1. 4
      fdtget.c
  2. 3
      libfdt/fdt.c
  3. 100
      libfdt/fdt_ro.c
  4. 5
      libfdt/libfdt.h

4
fdtget.c

@ -140,7 +140,6 @@ static int show_data(struct display_info *disp, const char *data, int len) @@ -140,7 +140,6 @@ static int show_data(struct display_info *disp, const char *data, int len)
*/
static int list_properties(const void *blob, int node)
{
const struct fdt_property *data;
const char *name;
int prop;

@ -149,8 +148,7 @@ static int list_properties(const void *blob, int node) @@ -149,8 +148,7 @@ static int list_properties(const void *blob, int node)
/* 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);

3
libfdt/fdt.c

@ -123,6 +123,9 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) @@ -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:

100
libfdt/fdt_ro.c

@ -58,9 +58,10 @@ @@ -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) @@ -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) @@ -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, @@ -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, @@ -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, @@ -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, @@ -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;
}


5
libfdt/libfdt.h

@ -54,7 +54,7 @@ @@ -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); @@ -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

Loading…
Cancel
Save