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
parent
37dea76e97
commit
f1879e1a50
4
fdtget.c
4
fdtget.c
|
@ -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)
|
static int list_properties(const void *blob, int node)
|
||||||
{
|
{
|
||||||
const struct fdt_property *data;
|
|
||||||
const char *name;
|
const char *name;
|
||||||
int prop;
|
int prop;
|
||||||
|
|
||||||
|
@ -149,8 +148,7 @@ static int list_properties(const void *blob, int node)
|
||||||
/* Stop silently when there are no more properties */
|
/* Stop silently when there are no more properties */
|
||||||
if (prop < 0)
|
if (prop < 0)
|
||||||
return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
|
return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
|
||||||
data = fdt_get_property_by_offset(blob, prop, NULL);
|
fdt_getprop_by_offset(blob, prop, &name, NULL);
|
||||||
name = fdt_string(blob, fdt32_to_cpu(data->nameoff));
|
|
||||||
if (name)
|
if (name)
|
||||||
puts(name);
|
puts(name);
|
||||||
prop = fdt_next_property_offset(blob, prop);
|
prop = fdt_next_property_offset(blob, prop);
|
||||||
|
|
|
@ -123,6 +123,9 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||||
/* skip-name offset, length and value */
|
/* skip-name offset, length and value */
|
||||||
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
|
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
|
||||||
+ fdt32_to_cpu(*lenp);
|
+ fdt32_to_cpu(*lenp);
|
||||||
|
if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
|
||||||
|
((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
|
||||||
|
offset += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FDT_END:
|
case FDT_END:
|
||||||
|
|
|
@ -58,9 +58,10 @@
|
||||||
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 *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 */
|
/* short match */
|
||||||
return 0;
|
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 char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|
||||||
{
|
{
|
||||||
const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
|
const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
|
||||||
|
const char *nameptr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (((err = fdt_check_header(fdt)) != 0)
|
if (((err = fdt_check_header(fdt)) != 0)
|
||||||
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
|
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (len)
|
nameptr = nh->name;
|
||||||
*len = strlen(nh->name);
|
|
||||||
|
|
||||||
return 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(nameptr);
|
||||||
|
|
||||||
|
return nameptr;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (len)
|
if (len)
|
||||||
|
@ -268,7 +287,7 @@ int fdt_next_property_offset(const void *fdt, int offset)
|
||||||
return nextprop_(fdt, offset);
|
return nextprop_(fdt, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
|
||||||
int offset,
|
int offset,
|
||||||
int *lenp)
|
int *lenp)
|
||||||
{
|
{
|
||||||
|
@ -289,30 +308,70 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
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,
|
int offset,
|
||||||
const char *name,
|
const char *name,
|
||||||
int namelen, int *lenp)
|
int namelen,
|
||||||
|
int *lenp,
|
||||||
|
int *poffset)
|
||||||
{
|
{
|
||||||
for (offset = fdt_first_property_offset(fdt, offset);
|
for (offset = fdt_first_property_offset(fdt, offset);
|
||||||
(offset >= 0);
|
(offset >= 0);
|
||||||
(offset = fdt_next_property_offset(fdt, offset))) {
|
(offset = fdt_next_property_offset(fdt, offset))) {
|
||||||
const struct fdt_property *prop;
|
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;
|
offset = -FDT_ERR_INTERNAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
|
if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
|
||||||
name, namelen))
|
name, namelen)) {
|
||||||
|
if (poffset)
|
||||||
|
*poffset = offset;
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (lenp)
|
if (lenp)
|
||||||
*lenp = offset;
|
*lenp = offset;
|
||||||
return NULL;
|
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,
|
const struct fdt_property *fdt_get_property(const void *fdt,
|
||||||
int nodeoffset,
|
int nodeoffset,
|
||||||
const char *name, int *lenp)
|
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 void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
||||||
const char *name, int namelen, int *lenp)
|
const char *name, int namelen, int *lenp)
|
||||||
{
|
{
|
||||||
|
int poffset;
|
||||||
const struct fdt_property *prop;
|
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)
|
if (!prop)
|
||||||
return NULL;
|
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;
|
return prop->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,11 +403,16 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
||||||
{
|
{
|
||||||
const struct fdt_property *prop;
|
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)
|
if (!prop)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (namep)
|
if (namep)
|
||||||
*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
|
*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;
|
return prop->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
#include <libfdt_env.h>
|
#include <libfdt_env.h>
|
||||||
#include <fdt.h>
|
#include <fdt.h>
|
||||||
|
|
||||||
#define FDT_FIRST_SUPPORTED_VERSION 0x10
|
#define FDT_FIRST_SUPPORTED_VERSION 0x02
|
||||||
#define FDT_LAST_SUPPORTED_VERSION 0x11
|
#define FDT_LAST_SUPPORTED_VERSION 0x11
|
||||||
|
|
||||||
/* Error codes: informative error codes */
|
/* 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
|
* offset. If lenp is non-NULL, the length of the property value is
|
||||||
* also returned, in the integer pointed to by lenp.
|
* 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:
|
* returns:
|
||||||
* pointer to the structure representing the property
|
* pointer to the structure representing the property
|
||||||
* if lenp is non-NULL, *lenp contains the length of the property
|
* if lenp is non-NULL, *lenp contains the length of the property
|
||||||
|
|
Loading…
Reference in New Issue