2021-01-14 18:55:20 +00:00
|
|
|
/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
|
|
|
|
|
|
|
|
#include "adt.h"
|
|
|
|
#include "string.h"
|
|
|
|
|
|
|
|
/* This API is designed to match libfdt's read-only API */
|
|
|
|
|
|
|
|
#define ADT_CHECK_HEADER(adt) \
|
|
|
|
{ \
|
|
|
|
int err; \
|
|
|
|
if ((err = adt_check_header(adt)) != 0) \
|
|
|
|
return err; \
|
|
|
|
}
|
|
|
|
|
|
|
|
//#define DEBUG
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
#include "utils.h"
|
|
|
|
#define dprintf printf
|
|
|
|
#else
|
|
|
|
#define dprintf(...) \
|
|
|
|
do { \
|
|
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int _adt_check_node_offset(const void *adt, int offset)
|
|
|
|
{
|
|
|
|
if ((offset < 0) || (offset % ADT_ALIGN))
|
|
|
|
return -ADT_ERR_BADOFFSET;
|
|
|
|
|
|
|
|
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
|
|
|
|
|
|
|
|
// Sanity check
|
|
|
|
if (node->property_count > 2048 || !node->property_count ||
|
|
|
|
node->child_count > 2048)
|
|
|
|
return -ADT_ERR_BADOFFSET;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int _adt_check_prop_offset(const void *adt, int offset)
|
|
|
|
{
|
|
|
|
if ((offset < 0) || (offset % ADT_ALIGN))
|
|
|
|
return -ADT_ERR_BADOFFSET;
|
|
|
|
|
|
|
|
const struct adt_property *prop = ADT_PROP(adt, offset);
|
|
|
|
|
|
|
|
if (prop->size & 0x7ff00000) // up to 1MB properties
|
|
|
|
return -ADT_ERR_BADOFFSET;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_check_header(const void *adt)
|
|
|
|
{
|
|
|
|
return _adt_check_node_offset(adt, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _adt_string_eq(const char *a, const char *b, int len)
|
|
|
|
{
|
|
|
|
return (strlen(a) == len) && (memcmp(a, b, len) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _adt_nodename_eq(const char *a, const char *b, int len)
|
|
|
|
{
|
|
|
|
if (memcmp(a, b, len) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (a[len] == '\0')
|
|
|
|
return 1;
|
|
|
|
else if (!memchr(b, '@', len) && (a[len] == '@'))
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct adt_property *adt_get_property_namelen(const void *adt, int offset,
|
|
|
|
const char *name,
|
|
|
|
int namelen)
|
|
|
|
{
|
|
|
|
dprintf("adt_get_property_namelen(%p, %d, \"%s\", %d)\n", adt, offset, name,
|
|
|
|
namelen);
|
|
|
|
|
|
|
|
for (offset = adt_first_property_offset(adt, offset); (offset >= 0);
|
|
|
|
(offset = adt_next_property_offset(adt, offset))) {
|
|
|
|
const struct adt_property *prop;
|
|
|
|
|
|
|
|
prop = adt_get_property_by_offset(adt, offset);
|
|
|
|
|
|
|
|
dprintf(" off=0x%x name=\"%s\"\n", offset, prop->name);
|
|
|
|
if (_adt_string_eq(prop->name, name, namelen))
|
|
|
|
return prop;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct adt_property *adt_get_property(const void *adt, int nodeoffset,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
return adt_get_property_namelen(adt, nodeoffset, name, strlen(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *adt_getprop_namelen(const void *adt, int nodeoffset,
|
|
|
|
const char *name, int namelen, int *lenp)
|
|
|
|
{
|
|
|
|
const struct adt_property *prop;
|
|
|
|
|
|
|
|
prop = adt_get_property_namelen(adt, nodeoffset, name, namelen);
|
|
|
|
|
|
|
|
if (!prop)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (lenp)
|
|
|
|
*lenp = prop->size;
|
|
|
|
|
|
|
|
return prop->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *adt_getprop_by_offset(const void *adt, int offset,
|
|
|
|
const char **namep, int *lenp)
|
|
|
|
{
|
|
|
|
const struct adt_property *prop;
|
|
|
|
|
|
|
|
prop = adt_get_property_by_offset(adt, offset);
|
|
|
|
if (!prop)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (namep)
|
|
|
|
*namep = prop->name;
|
|
|
|
if (lenp)
|
|
|
|
*lenp = prop->size;
|
|
|
|
return prop->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *adt_getprop(const void *adt, int nodeoffset, const char *name,
|
|
|
|
int *lenp)
|
|
|
|
{
|
|
|
|
return adt_getprop_namelen(adt, nodeoffset, name, strlen(name), lenp);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int adt_getprop_copy(const void *adt, int nodeoffset, const char *name,
|
|
|
|
void *out, int len)
|
|
|
|
{
|
|
|
|
int plen;
|
|
|
|
|
|
|
|
const void *p = adt_getprop(adt, nodeoffset, name, &plen);
|
|
|
|
|
|
|
|
if (!p)
|
2021-01-15 00:27:37 +00:00
|
|
|
return -ADT_ERR_NOTFOUND;
|
2021-01-14 18:55:20 +00:00
|
|
|
|
|
|
|
if (plen != len)
|
2021-01-15 00:27:37 +00:00
|
|
|
return -ADT_ERR_BADLENGTH;
|
2021-01-14 18:55:20 +00:00
|
|
|
|
|
|
|
memcpy(out, p, len);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_first_child_offset(const void *adt, int offset)
|
|
|
|
{
|
|
|
|
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
|
|
|
|
|
|
|
|
int cnt = node->property_count;
|
|
|
|
offset = adt_first_property_offset(adt, offset);
|
|
|
|
|
|
|
|
while (cnt--) {
|
|
|
|
offset = adt_next_property_offset(adt, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_next_sibling_offset(const void *adt, int offset)
|
|
|
|
{
|
|
|
|
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
|
|
|
|
|
|
|
|
int cnt = node->child_count;
|
|
|
|
offset = adt_first_child_offset(adt, offset);
|
|
|
|
|
|
|
|
while (cnt--) {
|
|
|
|
offset = adt_next_sibling_offset(adt, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_subnode_offset_namelen(const void *adt, int offset, const char *name,
|
|
|
|
int namelen)
|
|
|
|
{
|
|
|
|
const struct adt_node_hdr *node = ADT_NODE(adt, offset);
|
|
|
|
|
|
|
|
ADT_CHECK_HEADER(adt);
|
|
|
|
|
|
|
|
offset = adt_first_child_offset(adt, offset);
|
|
|
|
|
|
|
|
for (int i = 0; i < node->child_count; i++) {
|
|
|
|
const char *cname = adt_get_name(adt, offset);
|
|
|
|
|
|
|
|
if (_adt_nodename_eq(cname, name, namelen))
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
offset = adt_next_sibling_offset(adt, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ADT_ERR_NOTFOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_subnode_offset(const void *adt, int parentoffset, const char *name)
|
|
|
|
{
|
|
|
|
return adt_subnode_offset_namelen(adt, parentoffset, name, strlen(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
int adt_path_offset(const void *adt, const char *path)
|
|
|
|
{
|
|
|
|
const char *end = path + strlen(path);
|
|
|
|
const char *p = path;
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
ADT_CHECK_HEADER(adt);
|
|
|
|
|
|
|
|
while (*p) {
|
|
|
|
const char *q;
|
|
|
|
|
|
|
|
while (*p == '/')
|
|
|
|
p++;
|
|
|
|
if (!*p)
|
|
|
|
return offset;
|
|
|
|
q = strchr(p, '/');
|
|
|
|
if (!q)
|
|
|
|
q = end;
|
|
|
|
|
|
|
|
offset = adt_subnode_offset_namelen(adt, offset, p, q - p);
|
|
|
|
if (offset < 0)
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
p = q;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *adt_get_name(const void *adt, int nodeoffset)
|
|
|
|
{
|
|
|
|
return adt_getprop(adt, nodeoffset, "name", NULL);
|
|
|
|
}
|