scripts: Add dtc
This adds the devicetree compiler to barebox. This is taken without changes from Linux v3.8 Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
14c290dabe
commit
e748e6c601
|
@ -1,6 +1,10 @@
|
|||
config OFTREE
|
||||
bool
|
||||
|
||||
config DTC
|
||||
bool
|
||||
|
||||
config OFDEVICE
|
||||
select OFTREE
|
||||
select DTC
|
||||
bool "Enable probing of devices from the devicetree"
|
||||
|
|
|
@ -33,3 +33,5 @@ obj-$(CONFIG_BAREBOXENV_TARGET) += bareboxenv-target
|
|||
|
||||
scripts/bareboxenv-target: scripts/bareboxenv.c FORCE
|
||||
$(call if_changed_dep,csingle)
|
||||
|
||||
subdir-$(CONFIG_DTC) += dtc
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# scripts/dtc makefile
|
||||
|
||||
hostprogs-y := dtc
|
||||
always := $(hostprogs-y)
|
||||
|
||||
dtc-objs := dtc.o flattree.o fstree.o data.o livetree.o treesource.o \
|
||||
srcpos.o checks.o util.o
|
||||
dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
|
||||
|
||||
# Source files need to get at the userspace version of libfdt_env.h to compile
|
||||
|
||||
HOSTCFLAGS_DTC := -I$(src) -I$(src)/libfdt
|
||||
|
||||
HOSTCFLAGS_checks.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_data.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_dtc.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_flattree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_fstree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_livetree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_srcpos.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_treesource.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_util.o := $(HOSTCFLAGS_DTC)
|
||||
|
||||
HOSTCFLAGS_dtc-lexer.lex.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC)
|
||||
|
||||
# dependencies on generated files need to be listed explicitly
|
||||
$(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h
|
||||
|
||||
# generated files need to be cleaned explicitly
|
||||
clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h
|
|
@ -0,0 +1,18 @@
|
|||
# Makefile.dtc
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
DTC_SRCS = \
|
||||
checks.c \
|
||||
data.c \
|
||||
dtc.c \
|
||||
flattree.c \
|
||||
fstree.c \
|
||||
livetree.c \
|
||||
srcpos.c \
|
||||
treesource.c \
|
||||
util.c
|
||||
|
||||
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
||||
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
|
@ -0,0 +1,759 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2007.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
||||
#ifdef TRACE_CHECKS
|
||||
#define TRACE(c, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "=== %s: ", (c)->name); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} while (0)
|
||||
#else
|
||||
#define TRACE(c, fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
enum checkstatus {
|
||||
UNCHECKED = 0,
|
||||
PREREQ,
|
||||
PASSED,
|
||||
FAILED,
|
||||
};
|
||||
|
||||
struct check;
|
||||
|
||||
typedef void (*tree_check_fn)(struct check *c, struct node *dt);
|
||||
typedef void (*node_check_fn)(struct check *c, struct node *dt, struct node *node);
|
||||
typedef void (*prop_check_fn)(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop);
|
||||
|
||||
struct check {
|
||||
const char *name;
|
||||
tree_check_fn tree_fn;
|
||||
node_check_fn node_fn;
|
||||
prop_check_fn prop_fn;
|
||||
void *data;
|
||||
bool warn, error;
|
||||
enum checkstatus status;
|
||||
int inprogress;
|
||||
int num_prereqs;
|
||||
struct check **prereq;
|
||||
};
|
||||
|
||||
#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \
|
||||
static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \
|
||||
static struct check nm = { \
|
||||
.name = #nm, \
|
||||
.tree_fn = (tfn), \
|
||||
.node_fn = (nfn), \
|
||||
.prop_fn = (pfn), \
|
||||
.data = (d), \
|
||||
.warn = (w), \
|
||||
.error = (e), \
|
||||
.status = UNCHECKED, \
|
||||
.num_prereqs = ARRAY_SIZE(nm##_prereqs), \
|
||||
.prereq = nm##_prereqs, \
|
||||
};
|
||||
#define WARNING(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__)
|
||||
#define ERROR(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__)
|
||||
#define CHECK(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__)
|
||||
|
||||
#define TREE_WARNING(nm, d, ...) \
|
||||
WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define TREE_ERROR(nm, d, ...) \
|
||||
ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define TREE_CHECK(nm, d, ...) \
|
||||
CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define NODE_WARNING(nm, d, ...) \
|
||||
WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define NODE_ERROR(nm, d, ...) \
|
||||
ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define NODE_CHECK(nm, d, ...) \
|
||||
CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define PROP_WARNING(nm, d, ...) \
|
||||
WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
#define PROP_ERROR(nm, d, ...) \
|
||||
ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
#define PROP_CHECK(nm, d, ...) \
|
||||
CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
|
||||
#ifdef __GNUC__
|
||||
static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
|
||||
#endif
|
||||
static inline void check_msg(struct check *c, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if ((c->warn && (quiet < 1))
|
||||
|| (c->error && (quiet < 2))) {
|
||||
fprintf(stderr, "%s (%s): ",
|
||||
(c->error) ? "ERROR" : "Warning", c->name);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define FAIL(c, ...) \
|
||||
do { \
|
||||
TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \
|
||||
(c)->status = FAILED; \
|
||||
check_msg((c), __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void check_nodes_props(struct check *c, struct node *dt, struct node *node)
|
||||
{
|
||||
struct node *child;
|
||||
struct property *prop;
|
||||
|
||||
TRACE(c, "%s", node->fullpath);
|
||||
if (c->node_fn)
|
||||
c->node_fn(c, dt, node);
|
||||
|
||||
if (c->prop_fn)
|
||||
for_each_property(node, prop) {
|
||||
TRACE(c, "%s\t'%s'", node->fullpath, prop->name);
|
||||
c->prop_fn(c, dt, node, prop);
|
||||
}
|
||||
|
||||
for_each_child(node, child)
|
||||
check_nodes_props(c, dt, child);
|
||||
}
|
||||
|
||||
static int run_check(struct check *c, struct node *dt)
|
||||
{
|
||||
int error = 0;
|
||||
int i;
|
||||
|
||||
assert(!c->inprogress);
|
||||
|
||||
if (c->status != UNCHECKED)
|
||||
goto out;
|
||||
|
||||
c->inprogress = 1;
|
||||
|
||||
for (i = 0; i < c->num_prereqs; i++) {
|
||||
struct check *prq = c->prereq[i];
|
||||
error |= run_check(prq, dt);
|
||||
if (prq->status != PASSED) {
|
||||
c->status = PREREQ;
|
||||
check_msg(c, "Failed prerequisite '%s'",
|
||||
c->prereq[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->status != UNCHECKED)
|
||||
goto out;
|
||||
|
||||
if (c->node_fn || c->prop_fn)
|
||||
check_nodes_props(c, dt, dt);
|
||||
|
||||
if (c->tree_fn)
|
||||
c->tree_fn(c, dt);
|
||||
if (c->status == UNCHECKED)
|
||||
c->status = PASSED;
|
||||
|
||||
TRACE(c, "\tCompleted, status %d", c->status);
|
||||
|
||||
out:
|
||||
c->inprogress = 0;
|
||||
if ((c->status != PASSED) && (c->error))
|
||||
error = 1;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility check functions
|
||||
*/
|
||||
|
||||
/* A check which always fails, for testing purposes only */
|
||||
static inline void check_always_fail(struct check *c, struct node *dt)
|
||||
{
|
||||
FAIL(c, "always_fail check");
|
||||
}
|
||||
TREE_CHECK(always_fail, NULL);
|
||||
|
||||
static void check_is_string(struct check *c, struct node *root,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
char *propname = c->data;
|
||||
|
||||
prop = get_property(node, propname);
|
||||
if (!prop)
|
||||
return; /* Not present, assumed ok */
|
||||
|
||||
if (!data_is_one_string(prop->val))
|
||||
FAIL(c, "\"%s\" property in %s is not a string",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_STRING(nm, propname) \
|
||||
WARNING(nm, NULL, check_is_string, NULL, (propname))
|
||||
#define ERROR_IF_NOT_STRING(nm, propname) \
|
||||
ERROR(nm, NULL, check_is_string, NULL, (propname))
|
||||
|
||||
static void check_is_cell(struct check *c, struct node *root,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
char *propname = c->data;
|
||||
|
||||
prop = get_property(node, propname);
|
||||
if (!prop)
|
||||
return; /* Not present, assumed ok */
|
||||
|
||||
if (prop->val.len != sizeof(cell_t))
|
||||
FAIL(c, "\"%s\" property in %s is not a single cell",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_CELL(nm, propname) \
|
||||
WARNING(nm, NULL, check_is_cell, NULL, (propname))
|
||||
#define ERROR_IF_NOT_CELL(nm, propname) \
|
||||
ERROR(nm, NULL, check_is_cell, NULL, (propname))
|
||||
|
||||
/*
|
||||
* Structural check functions
|
||||
*/
|
||||
|
||||
static void check_duplicate_node_names(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *child, *child2;
|
||||
|
||||
for_each_child(node, child)
|
||||
for (child2 = child->next_sibling;
|
||||
child2;
|
||||
child2 = child2->next_sibling)
|
||||
if (streq(child->name, child2->name))
|
||||
FAIL(c, "Duplicate node name %s",
|
||||
child->fullpath);
|
||||
}
|
||||
NODE_ERROR(duplicate_node_names, NULL);
|
||||
|
||||
static void check_duplicate_property_names(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop, *prop2;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
for (prop2 = prop->next; prop2; prop2 = prop2->next) {
|
||||
if (prop2->deleted)
|
||||
continue;
|
||||
if (streq(prop->name, prop2->name))
|
||||
FAIL(c, "Duplicate property name %s in %s",
|
||||
prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
NODE_ERROR(duplicate_property_names, NULL);
|
||||
|
||||
#define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
|
||||
#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define DIGITS "0123456789"
|
||||
#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
|
||||
|
||||
static void check_node_name_chars(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
int n = strspn(node->name, c->data);
|
||||
|
||||
if (n < strlen(node->name))
|
||||
FAIL(c, "Bad character '%c' in node %s",
|
||||
node->name[n], node->fullpath);
|
||||
}
|
||||
NODE_ERROR(node_name_chars, PROPNODECHARS "@");
|
||||
|
||||
static void check_node_name_format(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
if (strchr(get_unitname(node), '@'))
|
||||
FAIL(c, "Node %s has multiple '@' characters in name",
|
||||
node->fullpath);
|
||||
}
|
||||
NODE_ERROR(node_name_format, NULL, &node_name_chars);
|
||||
|
||||
static void check_property_name_chars(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
int n = strspn(prop->name, c->data);
|
||||
|
||||
if (n < strlen(prop->name))
|
||||
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
|
||||
prop->name[n], prop->name, node->fullpath);
|
||||
}
|
||||
PROP_ERROR(property_name_chars, PROPNODECHARS);
|
||||
|
||||
#define DESCLABEL_FMT "%s%s%s%s%s"
|
||||
#define DESCLABEL_ARGS(node,prop,mark) \
|
||||
((mark) ? "value of " : ""), \
|
||||
((prop) ? "'" : ""), \
|
||||
((prop) ? (prop)->name : ""), \
|
||||
((prop) ? "' in " : ""), (node)->fullpath
|
||||
|
||||
static void check_duplicate_label(struct check *c, struct node *dt,
|
||||
const char *label, struct node *node,
|
||||
struct property *prop, struct marker *mark)
|
||||
{
|
||||
struct node *othernode = NULL;
|
||||
struct property *otherprop = NULL;
|
||||
struct marker *othermark = NULL;
|
||||
|
||||
othernode = get_node_by_label(dt, label);
|
||||
|
||||
if (!othernode)
|
||||
otherprop = get_property_by_label(dt, label, &othernode);
|
||||
if (!othernode)
|
||||
othermark = get_marker_label(dt, label, &othernode,
|
||||
&otherprop);
|
||||
|
||||
if (!othernode)
|
||||
return;
|
||||
|
||||
if ((othernode != node) || (otherprop != prop) || (othermark != mark))
|
||||
FAIL(c, "Duplicate label '%s' on " DESCLABEL_FMT
|
||||
" and " DESCLABEL_FMT,
|
||||
label, DESCLABEL_ARGS(node, prop, mark),
|
||||
DESCLABEL_ARGS(othernode, otherprop, othermark));
|
||||
}
|
||||
|
||||
static void check_duplicate_label_node(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct label *l;
|
||||
|
||||
for_each_label(node->labels, l)
|
||||
check_duplicate_label(c, dt, l->label, node, NULL, NULL);
|
||||
}
|
||||
static void check_duplicate_label_prop(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct label *l;
|
||||
|
||||
for_each_label(prop->labels, l)
|
||||
check_duplicate_label(c, dt, l->label, node, prop, NULL);
|
||||
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
check_duplicate_label(c, dt, m->ref, node, prop, m);
|
||||
}
|
||||
ERROR(duplicate_label, NULL, check_duplicate_label_node,
|
||||
check_duplicate_label_prop, NULL);
|
||||
|
||||
static void check_explicit_phandles(struct check *c, struct node *root,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
struct marker *m;
|
||||
struct node *other;
|
||||
cell_t phandle;
|
||||
|
||||
if (!streq(prop->name, "phandle")
|
||||
&& !streq(prop->name, "linux,phandle"))
|
||||
return;
|
||||
|
||||
if (prop->val.len != sizeof(cell_t)) {
|
||||
FAIL(c, "%s has bad length (%d) %s property",
|
||||
node->fullpath, prop->val.len, prop->name);
|
||||
return;
|
||||
}
|
||||
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
assert(m->offset == 0);
|
||||
if (node != get_node_by_ref(root, m->ref))
|
||||
/* "Set this node's phandle equal to some
|
||||
* other node's phandle". That's nonsensical
|
||||
* by construction. */ {
|
||||
FAIL(c, "%s in %s is a reference to another node",
|
||||
prop->name, node->fullpath);
|
||||
return;
|
||||
}
|
||||
/* But setting this node's phandle equal to its own
|
||||
* phandle is allowed - that means allocate a unique
|
||||
* phandle for this node, even if it's not otherwise
|
||||
* referenced. The value will be filled in later, so
|
||||
* no further checking for now. */
|
||||
return;
|
||||
}
|
||||
|
||||
phandle = propval_cell(prop);
|
||||
|
||||
if ((phandle == 0) || (phandle == -1)) {
|
||||
FAIL(c, "%s has bad value (0x%x) in %s property",
|
||||
node->fullpath, phandle, prop->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->phandle && (node->phandle != phandle))
|
||||
FAIL(c, "%s has %s property which replaces existing phandle information",
|
||||
node->fullpath, prop->name);
|
||||
|
||||
other = get_node_by_phandle(root, phandle);
|
||||
if (other && (other != node)) {
|
||||
FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)",
|
||||
node->fullpath, phandle, other->fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
node->phandle = phandle;
|
||||
}
|
||||
PROP_ERROR(explicit_phandles, NULL);
|
||||
|
||||
static void check_name_properties(struct check *c, struct node *root,
|
||||
struct node *node)
|
||||
{
|
||||
struct property **pp, *prop = NULL;
|
||||
|
||||
for (pp = &node->proplist; *pp; pp = &((*pp)->next))
|
||||
if (streq((*pp)->name, "name")) {
|
||||
prop = *pp;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!prop)
|
||||
return; /* No name property, that's fine */
|
||||
|
||||
if ((prop->val.len != node->basenamelen+1)
|
||||
|| (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) {
|
||||
FAIL(c, "\"name\" property in %s is incorrect (\"%s\" instead"
|
||||
" of base node name)", node->fullpath, prop->val.val);
|
||||
} else {
|
||||
/* The name property is correct, and therefore redundant.
|
||||
* Delete it */
|
||||
*pp = prop->next;
|
||||
free(prop->name);
|
||||
data_free(prop->val);
|
||||
free(prop);
|
||||
}
|
||||
}
|
||||
ERROR_IF_NOT_STRING(name_is_string, "name");
|
||||
NODE_ERROR(name_properties, NULL, &name_is_string);
|
||||
|
||||
/*
|
||||
* Reference fixup functions
|
||||
*/
|
||||
|
||||
static void fixup_phandle_references(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
cell_t phandle;
|
||||
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
assert(m->offset + sizeof(cell_t) <= prop->val.len);
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (! refnode) {
|
||||
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
|
||||
m->ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
phandle = get_node_phandle(dt, refnode);
|
||||
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
|
||||
}
|
||||
}
|
||||
ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
|
||||
&duplicate_node_names, &explicit_phandles);
|
||||
|
||||
static void fixup_path_references(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
char *path;
|
||||
|
||||
for_each_marker_of_type(m, REF_PATH) {
|
||||
assert(m->offset <= prop->val.len);
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (!refnode) {
|
||||
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
|
||||
m->ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
path = refnode->fullpath;
|
||||
prop->val = data_insert_at_marker(prop->val, m, path,
|
||||
strlen(path) + 1);
|
||||
}
|
||||
}
|
||||
ERROR(path_references, NULL, NULL, fixup_path_references, NULL,
|
||||
&duplicate_node_names);
|
||||
|
||||
/*
|
||||
* Semantic checks
|
||||
*/
|
||||
WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells");
|
||||
WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells");
|
||||
WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells");
|
||||
|
||||
WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
|
||||
WARNING_IF_NOT_STRING(model_is_string, "model");
|
||||
WARNING_IF_NOT_STRING(status_is_string, "status");
|
||||
|
||||
static void fixup_addr_size_cells(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
node->addr_cells = -1;
|
||||
node->size_cells = -1;
|
||||
|
||||
prop = get_property(node, "#address-cells");
|
||||
if (prop)
|
||||
node->addr_cells = propval_cell(prop);
|
||||
|
||||
prop = get_property(node, "#size-cells");
|
||||
if (prop)
|
||||
node->size_cells = propval_cell(prop);
|
||||
}
|
||||
WARNING(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL,
|
||||
&address_cells_is_cell, &size_cells_is_cell);
|
||||
|
||||
#define node_addr_cells(n) \
|
||||
(((n)->addr_cells == -1) ? 2 : (n)->addr_cells)
|
||||
#define node_size_cells(n) \
|
||||
(((n)->size_cells == -1) ? 1 : (n)->size_cells)
|
||||
|
||||
static void check_reg_format(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
int addr_cells, size_cells, entrylen;
|
||||
|
||||
prop = get_property(node, "reg");
|
||||
if (!prop)
|
||||
return; /* No "reg", that's fine */
|
||||
|
||||
if (!node->parent) {
|
||||
FAIL(c, "Root node has a \"reg\" property");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop->val.len == 0)
|
||||
FAIL(c, "\"reg\" property in %s is empty", node->fullpath);
|
||||
|
||||
addr_cells = node_addr_cells(node->parent);
|
||||
size_cells = node_size_cells(node->parent);
|
||||
entrylen = (addr_cells + size_cells) * sizeof(cell_t);
|
||||
|
||||
if ((prop->val.len % entrylen) != 0)
|
||||
FAIL(c, "\"reg\" property in %s has invalid length (%d bytes) "
|
||||
"(#address-cells == %d, #size-cells == %d)",
|
||||
node->fullpath, prop->val.len, addr_cells, size_cells);
|
||||
}
|
||||
NODE_WARNING(reg_format, NULL, &addr_size_cells);
|
||||
|
||||
static void check_ranges_format(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
int c_addr_cells, p_addr_cells, c_size_cells, p_size_cells, entrylen;
|
||||
|
||||
prop = get_property(node, "ranges");
|
||||
if (!prop)
|
||||
return;
|
||||
|
||||
if (!node->parent) {
|
||||
FAIL(c, "Root node has a \"ranges\" property");
|
||||
return;
|
||||
}
|
||||
|
||||
p_addr_cells = node_addr_cells(node->parent);
|
||||
p_size_cells = node_size_cells(node->parent);
|
||||
c_addr_cells = node_addr_cells(node);
|
||||
c_size_cells = node_size_cells(node);
|
||||
entrylen = (p_addr_cells + c_addr_cells + c_size_cells) * sizeof(cell_t);
|
||||
|
||||
if (prop->val.len == 0) {
|
||||
if (p_addr_cells != c_addr_cells)
|
||||
FAIL(c, "%s has empty \"ranges\" property but its "
|
||||
"#address-cells (%d) differs from %s (%d)",
|
||||
node->fullpath, c_addr_cells, node->parent->fullpath,
|
||||
p_addr_cells);
|
||||
if (p_size_cells != c_size_cells)
|
||||
FAIL(c, "%s has empty \"ranges\" property but its "
|
||||
"#size-cells (%d) differs from %s (%d)",
|
||||
node->fullpath, c_size_cells, node->parent->fullpath,
|
||||
p_size_cells);
|
||||
} else if ((prop->val.len % entrylen) != 0) {
|
||||
FAIL(c, "\"ranges\" property in %s has invalid length (%d bytes) "
|
||||
"(parent #address-cells == %d, child #address-cells == %d, "
|
||||
"#size-cells == %d)", node->fullpath, prop->val.len,
|
||||
p_addr_cells, c_addr_cells, c_size_cells);
|
||||
}
|
||||
}
|
||||
NODE_WARNING(ranges_format, NULL, &addr_size_cells);
|
||||
|
||||
/*
|
||||
* Style checks
|
||||
*/
|
||||
static void check_avoid_default_addr_size(struct check *c, struct node *dt,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *reg, *ranges;
|
||||
|
||||
if (!node->parent)
|
||||
return; /* Ignore root node */
|
||||
|
||||
reg = get_property(node, "reg");
|
||||
ranges = get_property(node, "ranges");
|
||||
|
||||
if (!reg && !ranges)
|
||||
return;
|
||||
|
||||
if ((node->parent->addr_cells == -1))
|
||||
FAIL(c, "Relying on default #address-cells value for %s",
|
||||
node->fullpath);
|
||||
|
||||
if ((node->parent->size_cells == -1))
|
||||
FAIL(c, "Relying on default #size-cells value for %s",
|
||||
node->fullpath);
|
||||
}
|
||||
NODE_WARNING(avoid_default_addr_size, NULL, &addr_size_cells);
|
||||
|
||||
static void check_obsolete_chosen_interrupt_controller(struct check *c,
|
||||
struct node *dt)
|
||||
{
|
||||
struct node *chosen;
|
||||
struct property *prop;
|
||||
|
||||
chosen = get_node_by_path(dt, "/chosen");
|
||||
if (!chosen)
|
||||
return;
|
||||
|
||||
prop = get_property(chosen, "interrupt-controller");
|
||||
if (prop)
|
||||
FAIL(c, "/chosen has obsolete \"interrupt-controller\" "
|
||||
"property");
|
||||
}
|
||||
TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
|
||||
|
||||
static struct check *check_table[] = {
|
||||
&duplicate_node_names, &duplicate_property_names,
|
||||
&node_name_chars, &node_name_format, &property_name_chars,
|
||||
&name_is_string, &name_properties,
|
||||
|
||||
&duplicate_label,
|
||||
|
||||
&explicit_phandles,
|
||||
&phandle_references, &path_references,
|
||||
|
||||
&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
|
||||
&device_type_is_string, &model_is_string, &status_is_string,
|
||||
|
||||
&addr_size_cells, ®_format, &ranges_format,
|
||||
|
||||
&avoid_default_addr_size,
|
||||
&obsolete_chosen_interrupt_controller,
|
||||
|
||||
&always_fail,
|
||||
};
|
||||
|
||||
static void enable_warning_error(struct check *c, bool warn, bool error)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Raising level, also raise it for prereqs */
|
||||
if ((warn && !c->warn) || (error && !c->error))
|
||||
for (i = 0; i < c->num_prereqs; i++)
|
||||
enable_warning_error(c->prereq[i], warn, error);
|
||||
|
||||
c->warn = c->warn || warn;
|
||||
c->error = c->error || error;
|
||||
}
|
||||
|
||||
static void disable_warning_error(struct check *c, bool warn, bool error)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Lowering level, also lower it for things this is the prereq
|
||||
* for */
|
||||
if ((warn && c->warn) || (error && c->error)) {
|
||||
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
|
||||
struct check *cc = check_table[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < cc->num_prereqs; j++)
|
||||
if (cc->prereq[j] == c)
|
||||
disable_warning_error(cc, warn, error);
|
||||
}
|
||||
}
|
||||
|
||||
c->warn = c->warn && !warn;
|
||||
c->error = c->error && !error;
|
||||
}
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *optarg)
|
||||
{
|
||||
int i;
|
||||
const char *name = optarg;
|
||||
bool enable = true;
|
||||
|
||||
if ((strncmp(optarg, "no-", 3) == 0)
|
||||
|| (strncmp(optarg, "no_", 3) == 0)) {
|
||||
name = optarg + 3;
|
||||
enable = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
|
||||
struct check *c = check_table[i];
|
||||
|
||||
if (streq(c->name, name)) {
|
||||
if (enable)
|
||||
enable_warning_error(c, warn, error);
|
||||
else
|
||||
disable_warning_error(c, warn, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
die("Unrecognized check name \"%s\"\n", name);
|
||||
}
|
||||
|
||||
void process_checks(int force, struct boot_info *bi)
|
||||
{
|
||||
struct node *dt = bi->dt;
|
||||
int i;
|
||||
int error = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
|
||||
struct check *c = check_table[i];
|
||||
|
||||
if (c->warn || c->error)
|
||||
error = error || run_check(c, dt);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (!force) {
|
||||
fprintf(stderr, "ERROR: Input tree has errors, aborting "
|
||||
"(use -f to force output)\n");
|
||||
exit(2);
|
||||
} else if (quiet < 3) {
|
||||
fprintf(stderr, "Warning: Input tree has errors, "
|
||||
"output forced\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
||||
void data_free(struct data d)
|
||||
{
|
||||
struct marker *m, *nm;
|
||||
|
||||
m = d.markers;
|
||||
while (m) {
|
||||
nm = m->next;
|
||||
free(m->ref);
|
||||
free(m);
|
||||
m = nm;
|
||||
}
|
||||
|
||||
if (d.val)
|
||||
free(d.val);
|
||||
}
|
||||
|
||||
struct data data_grow_for(struct data d, int xlen)
|
||||
{
|
||||
struct data nd;
|
||||
int newsize;
|
||||
|
||||
if (xlen == 0)
|
||||
return d;
|
||||
|
||||
nd = d;
|
||||
|
||||
newsize = xlen;
|
||||
|
||||
while ((d.len + xlen) > newsize)
|
||||
newsize *= 2;
|
||||
|
||||
nd.val = xrealloc(d.val, newsize);
|
||||
|
||||
return nd;
|
||||
}
|
||||
|
||||
struct data data_copy_mem(const char *mem, int len)
|
||||
{
|
||||
struct data d;
|
||||
|
||||
d = data_grow_for(empty_data, len);
|
||||
|
||||
d.len = len;
|
||||
memcpy(d.val, mem, len);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_copy_escape_string(const char *s, int len)
|
||||
{
|
||||
int i = 0;
|
||||
struct data d;
|
||||
char *q;
|
||||
|
||||
d = data_grow_for(empty_data, strlen(s)+1);
|
||||
|
||||
q = d.val;
|
||||
while (i < len) {
|
||||
char c = s[i++];
|
||||
|
||||
if (c == '\\')
|
||||
c = get_escape_char(s, &i);
|
||||
|
||||
q[d.len++] = c;
|
||||
}
|
||||
|
||||
q[d.len++] = '\0';
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_copy_file(FILE *f, size_t maxlen)
|
||||
{
|
||||
struct data d = empty_data;
|
||||
|
||||
while (!feof(f) && (d.len < maxlen)) {
|
||||
size_t chunksize, ret;
|
||||
|
||||
if (maxlen == -1)
|
||||
chunksize = 4096;
|
||||
else
|
||||
chunksize = maxlen - d.len;
|
||||
|
||||
d = data_grow_for(d, chunksize);
|
||||
ret = fread(d.val + d.len, 1, chunksize, f);
|
||||
|
||||
if (ferror(f))
|
||||
die("Error reading file into data: %s", strerror(errno));
|
||||
|
||||
if (d.len + ret < d.len)
|
||||
die("Overflow reading file into data\n");
|
||||
|
||||
d.len += ret;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_append_data(struct data d, const void *p, int len)
|
||||
{
|
||||
d = data_grow_for(d, len);
|
||||
memcpy(d.val + d.len, p, len);
|
||||
d.len += len;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_insert_at_marker(struct data d, struct marker *m,
|
||||
const void *p, int len)
|
||||
{
|
||||
d = data_grow_for(d, len);
|
||||
memmove(d.val + m->offset + len, d.val + m->offset, d.len - m->offset);
|
||||
memcpy(d.val + m->offset, p, len);
|
||||
d.len += len;
|
||||
|
||||
/* Adjust all markers after the one we're inserting at */
|
||||
m = m->next;
|
||||
for_each_marker(m)
|
||||
m->offset += len;
|
||||
return d;
|
||||
}
|
||||
|
||||
static struct data data_append_markers(struct data d, struct marker *m)
|
||||
{
|
||||
struct marker **mp = &d.markers;
|
||||
|
||||
/* Find the end of the markerlist */
|
||||
while (*mp)
|
||||
mp = &((*mp)->next);
|
||||
*mp = m;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_merge(struct data d1, struct data d2)
|
||||
{
|
||||
struct data d;
|
||||
struct marker *m2 = d2.markers;
|
||||
|
||||
d = data_append_markers(data_append_data(d1, d2.val, d2.len), m2);
|
||||
|
||||
/* Adjust for the length of d1 */
|
||||
for_each_marker(m2)
|
||||
m2->offset += d1.len;
|
||||
|
||||
d2.markers = NULL; /* So data_free() doesn't clobber them */
|
||||
data_free(d2);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_append_integer(struct data d, uint64_t value, int bits)
|
||||
{
|
||||
uint8_t value_8;
|
||||
uint16_t value_16;
|
||||
uint32_t value_32;
|
||||
uint64_t value_64;
|
||||
|
||||
switch (bits) {
|
||||
case 8:
|
||||
value_8 = value;
|
||||
return data_append_data(d, &value_8, 1);
|
||||
|
||||
case 16:
|
||||
value_16 = cpu_to_fdt16(value);
|
||||
return data_append_data(d, &value_16, 2);
|
||||
|
||||
case 32:
|
||||
value_32 = cpu_to_fdt32(value);
|
||||
return data_append_data(d, &value_32, 4);
|
||||
|
||||
case 64:
|
||||
value_64 = cpu_to_fdt64(value);
|
||||
return data_append_data(d, &value_64, 8);
|
||||
|
||||
default:
|
||||
die("Invalid literal size (%d)\n", bits);
|
||||
}
|
||||
}
|
||||
|
||||
struct data data_append_re(struct data d, const struct fdt_reserve_entry *re)
|
||||
{
|
||||
struct fdt_reserve_entry bere;
|
||||
|
||||
bere.address = cpu_to_fdt64(re->address);
|
||||
bere.size = cpu_to_fdt64(re->size);
|
||||
|
||||
return data_append_data(d, &bere, sizeof(bere));
|
||||
}
|
||||
|
||||
struct data data_append_cell(struct data d, cell_t word)
|
||||
{
|
||||
return data_append_integer(d, word, sizeof(word) * 8);
|
||||
}
|
||||
|
||||
struct data data_append_addr(struct data d, uint64_t addr)
|
||||
{
|
||||
return data_append_integer(d, addr, sizeof(addr) * 8);
|
||||
}
|
||||
|
||||
struct data data_append_byte(struct data d, uint8_t byte)
|
||||
{
|
||||
return data_append_data(d, &byte, 1);
|
||||
}
|
||||
|
||||
struct data data_append_zeroes(struct data d, int len)
|
||||
{
|
||||
d = data_grow_for(d, len);
|
||||
|
||||
memset(d.val + d.len, 0, len);
|
||||
d.len += len;
|
||||
return d;
|
||||
}
|
||||
|
||||
struct data data_append_align(struct data d, int align)
|
||||
{
|
||||
int newlen = ALIGN(d.len, align);
|
||||
return data_append_zeroes(d, newlen - d.len);
|
||||
}
|
||||
|
||||
struct data data_add_marker(struct data d, enum markertype type, char *ref)
|
||||
{
|
||||
struct marker *m;
|
||||
|
||||
m = xmalloc(sizeof(*m));
|
||||
m->offset = d.len;
|
||||
m->type = type;
|
||||
m->ref = ref;
|
||||
m->next = NULL;
|
||||
|
||||
return data_append_markers(d, m);
|
||||
}
|
||||
|
||||
int data_is_one_string(struct data d)
|
||||
{
|
||||
int i;
|
||||
int len = d.len;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < len-1; i++)
|
||||
if (d.val[i] == '\0')
|
||||
return 0;
|
||||
|
||||
if (d.val[len-1] != '\0')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
%option noyywrap nounput noinput never-interactive
|
||||
|
||||
%x INCLUDE
|
||||
%x BYTESTRING
|
||||
%x PROPNODENAME
|
||||
%s V1
|
||||
|
||||
PROPNODECHAR [a-zA-Z0-9,._+*#?@-]
|
||||
PATHCHAR ({PROPNODECHAR}|[/])
|
||||
LABEL [a-zA-Z_][a-zA-Z0-9_]*
|
||||
STRING \"([^\\"]|\\.)*\"
|
||||
CHAR_LITERAL '([^']|\\')*'
|
||||
WS [[:space:]]
|
||||
COMMENT "/*"([^*]|\*+[^*/])*\*+"/"
|
||||
LINECOMMENT "//".*\n
|
||||
|
||||
%{
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
#include "dtc-parser.tab.h"
|
||||
|
||||
YYLTYPE yylloc;
|
||||
|
||||
/* CAUTION: this will stop working if we ever use yyless() or yyunput() */
|
||||
#define YY_USER_ACTION \
|
||||
{ \
|
||||
srcpos_update(&yylloc, yytext, yyleng); \
|
||||
}
|
||||
|
||||
/*#define LEXDEBUG 1*/
|
||||
|
||||
#ifdef LEXDEBUG
|
||||
#define DPRINT(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DPRINT(fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
static int dts_version = 1;
|
||||
|
||||
#define BEGIN_DEFAULT() DPRINT("<V1>\n"); \
|
||||
BEGIN(V1); \
|
||||
|
||||
static void push_input_file(const char *filename);
|
||||
static int pop_input_file(void);
|
||||
%}
|
||||
|
||||
%%
|
||||
<*>"/include/"{WS}*{STRING} {
|
||||
char *name = strchr(yytext, '\"') + 1;
|
||||
yytext[yyleng-1] = '\0';
|
||||
push_input_file(name);
|
||||
}
|
||||
|
||||
<*>^"#"(line)?{WS}+[0-9]+{WS}+{STRING}({WS}+[0-9]+)? {
|
||||
char *line, *tmp, *fn;
|
||||
/* skip text before line # */
|
||||
line = yytext;
|
||||
while (!isdigit(*line))
|
||||
line++;
|
||||
/* skip digits in line # */
|
||||
tmp = line;
|
||||
while (!isspace(*tmp))
|
||||
tmp++;
|
||||
/* "NULL"-terminate line # */
|
||||
*tmp = '\0';
|
||||
/* start of filename */
|
||||
fn = strchr(tmp + 1, '"') + 1;
|
||||
/* strip trailing " from filename */
|
||||
tmp = strchr(fn, '"');
|
||||
*tmp = 0;
|
||||
/* -1 since #line is the number of the next line */
|
||||
srcpos_set_line(xstrdup(fn), atoi(line) - 1);
|
||||
}
|
||||
|
||||
<*><<EOF>> {
|
||||
if (!pop_input_file()) {
|
||||
yyterminate();
|
||||
}
|
||||
}
|
||||
|
||||
<*>{STRING} {
|
||||
DPRINT("String: %s\n", yytext);
|
||||
yylval.data = data_copy_escape_string(yytext+1,
|
||||
yyleng-2);
|
||||
return DT_STRING;
|
||||
}
|
||||
|
||||
<*>"/dts-v1/" {
|
||||
DPRINT("Keyword: /dts-v1/\n");
|
||||
dts_version = 1;
|
||||
BEGIN_DEFAULT();
|
||||
return DT_V1;
|
||||
}
|
||||
|
||||
<*>"/memreserve/" {
|
||||
DPRINT("Keyword: /memreserve/\n");
|
||||
BEGIN_DEFAULT();
|
||||
return DT_MEMRESERVE;
|
||||
}
|
||||
|
||||
<*>"/bits/" {
|
||||
DPRINT("Keyword: /bits/\n");
|
||||
BEGIN_DEFAULT();
|
||||
return DT_BITS;
|
||||
}
|
||||
|
||||
<*>"/delete-property/" {
|
||||
DPRINT("Keyword: /delete-property/\n");
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
return DT_DEL_PROP;
|
||||
}
|
||||
|
||||
<*>"/delete-node/" {
|
||||
DPRINT("Keyword: /delete-node/\n");
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
return DT_DEL_NODE;
|
||||
}
|
||||
|
||||
<*>{LABEL}: {
|
||||
DPRINT("Label: %s\n", yytext);
|
||||
yylval.labelref = xstrdup(yytext);
|
||||
yylval.labelref[yyleng-1] = '\0';
|
||||
return DT_LABEL;
|
||||
}
|
||||
|
||||
<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
|
||||
yylval.literal = xstrdup(yytext);
|
||||
DPRINT("Literal: '%s'\n", yylval.literal);
|
||||
return DT_LITERAL;
|
||||
}
|
||||
|
||||
<*>{CHAR_LITERAL} {
|
||||
yytext[yyleng-1] = '\0';
|
||||
yylval.literal = xstrdup(yytext+1);
|
||||
DPRINT("Character literal: %s\n", yylval.literal);
|
||||
return DT_CHAR_LITERAL;
|
||||
}
|
||||
|
||||
<*>\&{LABEL} { /* label reference */
|
||||
DPRINT("Ref: %s\n", yytext+1);
|
||||
yylval.labelref = xstrdup(yytext+1);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<*>"&{/"{PATHCHAR}+\} { /* new-style path reference */
|
||||
yytext[yyleng-1] = '\0';
|
||||
DPRINT("Ref: %s\n", yytext+2);
|
||||
yylval.labelref = xstrdup(yytext+2);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<BYTESTRING>[0-9a-fA-F]{2} {
|
||||
yylval.byte = strtol(yytext, NULL, 16);
|
||||
DPRINT("Byte: %02x\n", (int)yylval.byte);
|
||||
return DT_BYTE;
|
||||
}
|
||||
|
||||
<BYTESTRING>"]" {
|
||||
DPRINT("/BYTESTRING\n");
|
||||
BEGIN_DEFAULT();
|
||||
return ']';
|
||||
}
|
||||
|
||||
<PROPNODENAME>\\?{PROPNODECHAR}+ {
|
||||
DPRINT("PropNodeName: %s\n", yytext);
|
||||
yylval.propnodename = xstrdup((yytext[0] == '\\') ?
|
||||
yytext + 1 : yytext);
|
||||
BEGIN_DEFAULT();
|
||||
return DT_PROPNODENAME;
|
||||
}
|
||||
|
||||
"/incbin/" {
|
||||
DPRINT("Binary Include\n");
|
||||
return DT_INCBIN;
|
||||
}
|
||||
|
||||
<*>{WS}+ /* eat whitespace */
|
||||
<*>{COMMENT}+ /* eat C-style comments */
|
||||
<*>{LINECOMMENT}+ /* eat C++-style comments */
|
||||
|
||||
<*>"<<" { return DT_LSHIFT; };
|
||||
<*>">>" { return DT_RSHIFT; };
|
||||
<*>"<=" { return DT_LE; };
|
||||
<*>">=" { return DT_GE; };
|
||||
<*>"==" { return DT_EQ; };
|
||||
<*>"!=" { return DT_NE; };
|
||||
<*>"&&" { return DT_AND; };
|
||||
<*>"||" { return DT_OR; };
|
||||
|
||||
<*>. {
|
||||
DPRINT("Char: %c (\\x%02x)\n", yytext[0],
|
||||
(unsigned)yytext[0]);
|
||||
if (yytext[0] == '[') {
|
||||
DPRINT("<BYTESTRING>\n");
|
||||
BEGIN(BYTESTRING);
|
||||
}
|
||||
if ((yytext[0] == '{')
|
||||
|| (yytext[0] == ';')) {
|
||||
DPRINT("<PROPNODENAME>\n");
|
||||
BEGIN(PROPNODENAME);
|
||||
}
|
||||
return yytext[0];
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
static void push_input_file(const char *filename)
|
||||
{
|
||||
assert(filename);
|
||||
|
||||
srcfile_push(filename);
|
||||
|
||||
yyin = current_srcfile->f;
|
||||
|
||||
yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
|
||||
static int pop_input_file(void)
|
||||
{
|
||||
if (srcfile_pop() == 0)
|
||||
return 0;
|
||||
|
||||
yypop_buffer_state();
|
||||
yyin = current_srcfile->f;
|
||||
|
||||
return 1;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
|||
|
||||
/* A Bison parser, made by GNU Bison 2.4.1. */
|
||||
|
||||
/* Skeleton interface for Bison's Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
|
||||
/* Tokens. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
/* Put the tokens into the symbol table, so that GDB and other debuggers
|
||||
know about them. */
|
||||
enum yytokentype {
|
||||
DT_V1 = 258,
|
||||
DT_MEMRESERVE = 259,
|
||||
DT_LSHIFT = 260,
|
||||
DT_RSHIFT = 261,
|
||||
DT_LE = 262,
|
||||
DT_GE = 263,
|
||||
DT_EQ = 264,
|
||||
DT_NE = 265,
|
||||
DT_AND = 266,
|
||||
DT_OR = 267,
|
||||
DT_BITS = 268,
|
||||
DT_DEL_PROP = 269,
|
||||
DT_DEL_NODE = 270,
|
||||
DT_PROPNODENAME = 271,
|
||||
DT_LITERAL = 272,
|
||||
DT_CHAR_LITERAL = 273,
|
||||
DT_BASE = 274,
|
||||
DT_BYTE = 275,
|
||||
DT_STRING = 276,
|
||||
DT_LABEL = 277,
|
||||
DT_REF = 278,
|
||||
DT_INCBIN = 279
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 1676 of yacc.c */
|
||||
#line 40 "dtc-parser.y"
|
||||
|
||||
char *propnodename;
|
||||
char *literal;
|
||||
char *labelref;
|
||||
unsigned int cbase;
|
||||
uint8_t byte;
|
||||
struct data data;
|
||||
|
||||
struct {
|
||||
struct data data;
|
||||
int bits;
|
||||
} array;
|
||||
|
||||
struct property *prop;
|
||||
struct property *proplist;
|
||||
struct node *node;
|
||||
struct node *nodelist;
|
||||
struct reserve_info *re;
|
||||
uint64_t integer;
|
||||
|
||||
|
||||
|
||||
/* Line 1676 of yacc.c */
|
||||
#line 99 "dtc-parser.tab.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
YYLTYPE yylloc;
|
||||
|
||||
extern int yylex(void);
|
||||
extern void print_error(char const *fmt, ...);
|
||||
extern void yyerror(char const *s);
|
||||
|
||||
extern struct boot_info *the_boot_info;
|
||||
extern int treesource_error;
|
||||
|
||||
static unsigned long long eval_literal(const char *s, int base, int bits);
|
||||
static unsigned char eval_char_literal(const char *s);
|
||||
%}
|
||||
|
||||
%union {
|
||||
char *propnodename;
|
||||
char *literal;
|
||||
char *labelref;
|
||||
unsigned int cbase;
|
||||
uint8_t byte;
|
||||
struct data data;
|
||||
|
||||
struct {
|
||||
struct data data;
|
||||
int bits;
|
||||
} array;
|
||||
|
||||
struct property *prop;
|
||||
struct property *proplist;
|
||||
struct node *node;
|
||||
struct node *nodelist;
|
||||
struct reserve_info *re;
|
||||
uint64_t integer;
|
||||
}
|
||||
|
||||
%token DT_V1
|
||||
%token DT_MEMRESERVE
|
||||
%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
|
||||
%token DT_BITS
|
||||
%token DT_DEL_PROP
|
||||
%token DT_DEL_NODE
|
||||
%token <propnodename> DT_PROPNODENAME
|
||||
%token <literal> DT_LITERAL
|
||||
%token <literal> DT_CHAR_LITERAL
|
||||
%token <cbase> DT_BASE
|
||||
%token <byte> DT_BYTE
|
||||
%token <data> DT_STRING
|
||||
%token <labelref> DT_LABEL
|
||||
%token <labelref> DT_REF
|
||||
%token DT_INCBIN
|
||||
|
||||
%type <data> propdata
|
||||
%type <data> propdataprefix
|
||||
%type <re> memreserve
|
||||
%type <re> memreserves
|
||||
%type <array> arrayprefix
|
||||
%type <data> bytestring
|
||||
%type <prop> propdef
|
||||
%type <proplist> proplist
|
||||
|
||||
%type <node> devicetree
|
||||
%type <node> nodedef
|
||||
%type <node> subnode
|
||||
%type <nodelist> subnodes
|
||||
|
||||
%type <integer> integer_prim
|
||||
%type <integer> integer_unary
|
||||
%type <integer> integer_mul
|
||||
%type <integer> integer_add
|
||||
%type <integer> integer_shift
|
||||
%type <integer> integer_rela
|
||||
%type <integer> integer_eq
|
||||
%type <integer> integer_bitand
|
||||
%type <integer> integer_bitxor
|
||||
%type <integer> integer_bitor
|
||||
%type <integer> integer_and
|
||||
%type <integer> integer_or
|
||||
%type <integer> integer_trinary
|
||||
%type <integer> integer_expr
|
||||
|
||||
%%
|
||||
|
||||
sourcefile:
|
||||
DT_V1 ';' memreserves devicetree
|
||||
{
|
||||
the_boot_info = build_boot_info($3, $4,
|
||||
guess_boot_cpuid($4));
|
||||
}
|
||||
;
|
||||
|
||||
memreserves:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| memreserve memreserves
|
||||
{
|
||||
$$ = chain_reserve_entry($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
memreserve:
|
||||
DT_MEMRESERVE integer_prim integer_prim ';'
|
||||
{
|
||||
$$ = build_reserve_entry($2, $3);
|
||||
}
|
||||
| DT_LABEL memreserve
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
devicetree:
|
||||
'/' nodedef
|
||||
{
|
||||
$$ = name_node($2, "");
|
||||
}
|
||||
| devicetree '/' nodedef
|
||||
{
|
||||
$$ = merge_nodes($1, $3);
|
||||
}
|
||||
| devicetree DT_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $2);
|
||||
|
||||
if (target)
|
||||
merge_nodes(target, $3);
|
||||
else
|
||||
print_error("label or path, '%s', not found", $2);
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_DEL_NODE DT_REF ';'
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (!target)
|
||||
print_error("label or path, '%s', not found", $3);
|
||||
else
|
||||
delete_node(target);
|
||||
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
nodedef:
|
||||
'{' proplist subnodes '}' ';'
|
||||
{
|
||||
$$ = build_node($2, $3);
|
||||
}
|
||||
;
|
||||
|
||||
proplist:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| proplist propdef
|
||||
{
|
||||
$$ = chain_property($2, $1);
|
||||
}
|
||||
;
|
||||
|
||||
propdef:
|
||||
DT_PROPNODENAME '=' propdata ';'
|
||||
{
|
||||
$$ = build_property($1, $3);
|
||||
}
|
||||
| DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = build_property($1, empty_data);
|
||||
}
|
||||
| DT_DEL_PROP DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = build_property_delete($2);
|
||||
}
|
||||
| DT_LABEL propdef
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
propdata:
|
||||
propdataprefix DT_STRING
|
||||
{
|
||||
$$ = data_merge($1, $2);
|
||||
}
|
||||
| propdataprefix arrayprefix '>'
|
||||
{
|
||||
$$ = data_merge($1, $2.data);
|
||||
}
|
||||
| propdataprefix '[' bytestring ']'
|
||||
{
|
||||
$$ = data_merge($1, $3);
|
||||
}
|
||||
| propdataprefix DT_REF
|
||||
{
|
||||
$$ = data_add_marker($1, REF_PATH, $2);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')'
|
||||
{
|
||||
FILE *f = srcfile_relative_open($4.val, NULL);
|
||||
struct data d;
|
||||
|
||||
if ($6 != 0)
|
||||
if (fseek(f, $6, SEEK_SET) != 0)
|
||||
print_error("Couldn't seek to offset %llu in \"%s\": %s",
|
||||
(unsigned long long)$6,
|
||||
$4.val,
|
||||
strerror(errno));
|
||||
|
||||
d = data_copy_file(f, $8);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
fclose(f);
|
||||
}
|
||||
| propdataprefix DT_INCBIN '(' DT_STRING ')'
|
||||
{
|
||||
FILE *f = srcfile_relative_open($4.val, NULL);
|
||||
struct data d = empty_data;
|
||||
|
||||
d = data_copy_file(f, -1);
|
||||
|
||||
$$ = data_merge($1, d);
|
||||
fclose(f);
|
||||
}
|
||||
| propdata DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
propdataprefix:
|
||||
/* empty */
|
||||
{
|
||||
$$ = empty_data;
|
||||
}
|
||||
| propdata ','
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| propdataprefix DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
arrayprefix:
|
||||
DT_BITS DT_LITERAL '<'
|
||||
{
|
||||
$$.data = empty_data;
|
||||
$$.bits = eval_literal($2, 0, 7);
|
||||
|
||||
if (($$.bits != 8) &&
|
||||
($$.bits != 16) &&
|
||||
($$.bits != 32) &&
|
||||
($$.bits != 64))
|
||||
{
|
||||
print_error("Only 8, 16, 32 and 64-bit elements"
|
||||
" are currently supported");
|
||||
$$.bits = 32;
|
||||
}
|
||||
}
|
||||
| '<'
|
||||
{
|
||||
$$.data = empty_data;
|
||||
$$.bits = 32;
|
||||
}
|
||||
| arrayprefix integer_prim
|
||||
{
|
||||
if ($1.bits < 64) {
|
||||
uint64_t mask = (1ULL << $1.bits) - 1;
|
||||
/*
|
||||
* Bits above mask must either be all zero
|
||||
* (positive within range of mask) or all one
|
||||
* (negative and sign-extended). The second
|
||||
* condition is true if when we set all bits
|
||||
* within the mask to one (i.e. | in the
|
||||
* mask), all bits are one.
|
||||
*/
|
||||
if (($2 > mask) && (($2 | mask) != -1ULL))
|
||||
print_error(
|
||||
"integer value out of range "
|
||||
"%016lx (%d bits)", $1.bits);
|
||||
}
|
||||
|
||||
$$.data = data_append_integer($1.data, $2, $1.bits);
|
||||
}
|
||||
| arrayprefix DT_REF
|
||||
{
|
||||
uint64_t val = ~0ULL >> (64 - $1.bits);
|
||||
|
||||
if ($1.bits == 32)
|
||||
$1.data = data_add_marker($1.data,
|
||||
REF_PHANDLE,
|
||||
$2);
|
||||
else
|
||||
print_error("References are only allowed in "
|
||||
"arrays with 32-bit elements.");
|
||||
|
||||
$$.data = data_append_integer($1.data, val, $1.bits);
|
||||
}
|
||||
| arrayprefix DT_LABEL
|
||||
{
|
||||
$$.data = data_add_marker($1.data, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
integer_prim:
|
||||
DT_LITERAL
|
||||
{
|
||||
$$ = eval_literal($1, 0, 64);
|
||||
}
|
||||
| DT_CHAR_LITERAL
|
||||
{
|
||||
$$ = eval_char_literal($1);
|
||||
}
|
||||
| '(' integer_expr ')'
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
integer_expr:
|
||||
integer_trinary
|
||||
;
|
||||
|
||||
integer_trinary:
|
||||
integer_or
|
||||
| integer_or '?' integer_expr ':' integer_trinary { $$ = $1 ? $3 : $5; }
|
||||
;
|
||||
|
||||
integer_or:
|
||||
integer_and
|
||||
| integer_or DT_OR integer_and { $$ = $1 || $3; }
|
||||
;
|
||||
|
||||
integer_and:
|
||||
integer_bitor
|
||||
| integer_and DT_AND integer_bitor { $$ = $1 && $3; }
|
||||
;
|
||||
|
||||
integer_bitor:
|
||||
integer_bitxor
|
||||
| integer_bitor '|' integer_bitxor { $$ = $1 | $3; }
|
||||
;
|
||||
|
||||
integer_bitxor:
|
||||
integer_bitand
|
||||
| integer_bitxor '^' integer_bitand { $$ = $1 ^ $3; }
|
||||
;
|
||||
|
||||
integer_bitand:
|
||||
integer_eq
|
||||
| integer_bitand '&' integer_eq { $$ = $1 & $3; }
|
||||
;
|
||||
|
||||
integer_eq:
|
||||
integer_rela
|
||||
| integer_eq DT_EQ integer_rela { $$ = $1 == $3; }
|
||||
| integer_eq DT_NE integer_rela { $$ = $1 != $3; }
|
||||
;
|
||||
|
||||
integer_rela:
|
||||
integer_shift
|
||||
| integer_rela '<' integer_shift { $$ = $1 < $3; }
|
||||
| integer_rela '>' integer_shift { $$ = $1 > $3; }
|
||||
| integer_rela DT_LE integer_shift { $$ = $1 <= $3; }
|
||||
| integer_rela DT_GE integer_shift { $$ = $1 >= $3; }
|
||||
;
|
||||
|
||||
integer_shift:
|
||||
integer_shift DT_LSHIFT integer_add { $$ = $1 << $3; }
|
||||
| integer_shift DT_RSHIFT integer_add { $$ = $1 >> $3; }
|
||||
| integer_add
|
||||
;
|
||||
|
||||
integer_add:
|
||||
integer_add '+' integer_mul { $$ = $1 + $3; }
|
||||
| integer_add '-' integer_mul { $$ = $1 - $3; }
|
||||
| integer_mul
|
||||
;
|
||||
|
||||
integer_mul:
|
||||
integer_mul '*' integer_unary { $$ = $1 * $3; }
|
||||
| integer_mul '/' integer_unary { $$ = $1 / $3; }
|
||||
| integer_mul '%' integer_unary { $$ = $1 % $3; }
|
||||
| integer_unary
|
||||
;
|
||||
|
||||
integer_unary:
|
||||
integer_prim
|
||||
| '-' integer_unary { $$ = -$2; }
|
||||
| '~' integer_unary { $$ = ~$2; }
|
||||
| '!' integer_unary { $$ = !$2; }
|
||||
;
|
||||
|
||||
bytestring:
|
||||
/* empty */
|
||||
{
|
||||
$$ = empty_data;
|
||||
}
|
||||
| bytestring DT_BYTE
|
||||
{
|
||||
$$ = data_append_byte($1, $2);
|
||||
}
|
||||
| bytestring DT_LABEL
|
||||
{
|
||||
$$ = data_add_marker($1, LABEL, $2);
|
||||
}
|
||||
;
|
||||
|
||||
subnodes:
|
||||
/* empty */
|
||||
{
|
||||
$$ = NULL;
|
||||
}
|
||||
| subnode subnodes
|
||||
{
|
||||
$$ = chain_node($1, $2);
|
||||
}
|
||||
| subnode propdef
|
||||
{
|
||||
print_error("syntax error: properties must precede subnodes");
|
||||
YYERROR;
|
||||
}
|
||||
;
|
||||
|
||||
subnode:
|
||||
DT_PROPNODENAME nodedef
|
||||
{
|
||||
$$ = name_node($2, $1);
|
||||
}
|
||||
| DT_DEL_NODE DT_PROPNODENAME ';'
|
||||
{
|
||||
$$ = name_node(build_node_delete(), $2);
|
||||
}
|
||||
| DT_LABEL subnode
|
||||
{
|
||||
add_label(&$2->labels, $1);
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void print_error(char const *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
srcpos_verror(&yylloc, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
treesource_error = 1;
|
||||
}
|
||||
|
||||
void yyerror(char const *s) {
|
||||
print_error("%s", s);
|
||||
}
|
||||
|
||||
static unsigned long long eval_literal(const char *s, int base, int bits)
|
||||
{
|
||||
unsigned long long val;
|
||||
char *e;
|
||||
|
||||
errno = 0;
|
||||
val = strtoull(s, &e, base);
|
||||
if (*e) {
|
||||
size_t uls = strspn(e, "UL");
|
||||
if (e[uls])
|
||||
print_error("bad characters in literal");
|
||||
}
|
||||
if ((errno == ERANGE)
|
||||
|| ((bits < 64) && (val >= (1ULL << bits))))
|
||||
print_error("literal out of range");
|
||||
else if (errno != 0)
|
||||
print_error("bad literal");
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned char eval_char_literal(const char *s)
|
||||
{
|
||||
int i = 1;
|
||||
char c = s[0];
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
print_error("empty character literal");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the first character in the character literal is a \ then process
|
||||
* the remaining characters as an escape encoding. If the first
|
||||
* character is neither an escape or a terminator it should be the only
|
||||
* character in the literal and will be returned.
|
||||
*/
|
||||
if (c == '\\')
|
||||
c = get_escape_char(s, &i);
|
||||
|
||||
if (s[i] != '\0')
|
||||
print_error("malformed character literal");
|
||||
|
||||
return c;
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
#include "version_gen.h"
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
int quiet; /* Level of quietness */
|
||||
int reservenum; /* Number of memory reservation slots */
|
||||
int minsize; /* Minimum blob size */
|
||||
int padsize; /* Additional padding to blob */
|
||||
int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */
|
||||
|
||||
static void fill_fullpaths(struct node *tree, const char *prefix)
|
||||
{
|
||||
struct node *child;
|
||||
const char *unit;
|
||||
|
||||
tree->fullpath = join_path(prefix, tree->name);
|
||||
|
||||
unit = strchr(tree->name, '@');
|
||||
if (unit)
|
||||
tree->basenamelen = unit - tree->name;
|
||||
else
|
||||
tree->basenamelen = strlen(tree->name);
|
||||
|
||||
for_each_child(tree, child)
|
||||
fill_fullpaths(child, tree->fullpath);
|
||||
}
|
||||
|
||||
static void __attribute__ ((noreturn)) usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tdtc [options] <input file>\n");
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
fprintf(stderr, "\t-h\n");
|
||||
fprintf(stderr, "\t\tThis help text\n");
|
||||
fprintf(stderr, "\t-q\n");
|
||||
fprintf(stderr, "\t\tQuiet: -q suppress warnings, -qq errors, -qqq all\n");
|
||||
fprintf(stderr, "\t-I <input format>\n");
|
||||
fprintf(stderr, "\t\tInput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tfs - /proc/device-tree style directory\n");
|
||||
fprintf(stderr, "\t-o <output file>\n");
|
||||
fprintf(stderr, "\t-O <output format>\n");
|
||||
fprintf(stderr, "\t\tOutput formats are:\n");
|
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n");
|
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n");
|
||||
fprintf(stderr, "\t\t\tasm - assembler source\n");
|
||||
fprintf(stderr, "\t-V <output version>\n");
|
||||
fprintf(stderr, "\t\tBlob version to produce, defaults to %d (relevant for dtb\n\t\tand asm output only)\n", DEFAULT_FDT_VERSION);
|
||||
fprintf(stderr, "\t-d <output dependency file>\n");
|
||||
fprintf(stderr, "\t-R <number>\n");
|
||||
fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n");
|
||||
fprintf(stderr, "\t-S <bytes>\n");
|
||||
fprintf(stderr, "\t\tMake the blob at least <bytes> long (extra space)\n");
|
||||
fprintf(stderr, "\t-p <bytes>\n");
|
||||
fprintf(stderr, "\t\tAdd padding to the blob of <bytes> long (extra space)\n");
|
||||
fprintf(stderr, "\t-b <number>\n");
|
||||
fprintf(stderr, "\t\tSet the physical boot cpu\n");
|
||||
fprintf(stderr, "\t-f\n");
|
||||
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n");
|
||||
fprintf(stderr, "\t-i\n");
|
||||
fprintf(stderr, "\t\tAdd a path to search for include files\n");
|
||||
fprintf(stderr, "\t-s\n");
|
||||
fprintf(stderr, "\t\tSort nodes and properties before outputting (only useful for\n\t\tcomparing trees)\n");
|
||||
fprintf(stderr, "\t-v\n");
|
||||
fprintf(stderr, "\t\tPrint DTC version and exit\n");
|
||||
fprintf(stderr, "\t-H <phandle format>\n");
|
||||
fprintf(stderr, "\t\tphandle formats are:\n");
|
||||
fprintf(stderr, "\t\t\tlegacy - \"linux,phandle\" properties only\n");
|
||||
fprintf(stderr, "\t\t\tepapr - \"phandle\" properties only\n");
|
||||
fprintf(stderr, "\t\t\tboth - Both \"linux,phandle\" and \"phandle\" properties\n");
|
||||
fprintf(stderr, "\t-W [no-]<checkname>\n");
|
||||
fprintf(stderr, "\t-E [no-]<checkname>\n");
|
||||
fprintf(stderr, "\t\t\tenable or disable warnings and errors\n");
|
||||
exit(3);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct boot_info *bi;
|
||||
const char *inform = "dts";
|
||||
const char *outform = "dts";
|
||||
const char *outname = "-";
|
||||
const char *depname = NULL;
|
||||
int force = 0, sort = 0;
|
||||
const char *arg;
|
||||
int opt;
|
||||
FILE *outf = NULL;
|
||||
int outversion = DEFAULT_FDT_VERSION;
|
||||
long long cmdline_boot_cpuid = -1;
|
||||
|
||||
quiet = 0;
|
||||
reservenum = 0;
|
||||
minsize = 0;
|
||||
padsize = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "hI:O:o:V:d:R:S:p:fqb:i:vH:sW:E:"))
|
||||
!= EOF) {
|
||||
switch (opt) {
|
||||
case 'I':
|
||||
inform = optarg;
|
||||
break;
|
||||
case 'O':
|
||||
outform = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
outname = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
outversion = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'd':
|
||||
depname = optarg;
|
||||
break;
|
||||
case 'R':
|
||||
reservenum = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'S':
|
||||
minsize = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'p':
|
||||
padsize = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet++;
|
||||
break;
|
||||
case 'b':
|
||||
cmdline_boot_cpuid = strtoll(optarg, NULL, 0);
|
||||
break;
|
||||
case 'i':
|
||||
srcfile_add_search_path(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
printf("Version: %s\n", DTC_VERSION);
|
||||
exit(0);
|
||||
case 'H':
|
||||
if (streq(optarg, "legacy"))
|
||||
phandle_format = PHANDLE_LEGACY;
|
||||
else if (streq(optarg, "epapr"))
|
||||
phandle_format = PHANDLE_EPAPR;
|
||||
else if (streq(optarg, "both"))
|
||||
phandle_format = PHANDLE_BOTH;
|
||||
else
|
||||
die("Invalid argument \"%s\" to -H option\n",
|
||||
optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
sort = 1;
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
parse_checks_option(true, false, optarg);
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
parse_checks_option(false, true, optarg);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > (optind+1))
|
||||
usage();
|
||||
else if (argc < (optind+1))
|
||||
arg = "-";
|
||||
else
|
||||
arg = argv[optind];
|
||||
|
||||
/* minsize and padsize are mutually exclusive */
|
||||
if (minsize && padsize)
|
||||
die("Can't set both -p and -S\n");
|
||||
|
||||
if (minsize)
|
||||
fprintf(stderr, "DTC: Use of \"-S\" is deprecated; it will be removed soon, use \"-p\" instead\n");
|
||||
|
||||
if (depname) {
|
||||
depfile = fopen(depname, "w");
|
||||
if (!depfile)
|
||||
die("Couldn't open dependency file %s: %s\n", depname,
|
||||
strerror(errno));
|
||||
fprintf(depfile, "%s:", outname);
|
||||
}
|
||||
|
||||
if (streq(inform, "dts"))
|
||||
bi = dt_from_source(arg);
|
||||
else if (streq(inform, "fs"))
|
||||
bi = dt_from_fs(arg);
|
||||
else if(streq(inform, "dtb"))
|
||||
bi = dt_from_blob(arg);
|
||||
else
|
||||
die("Unknown input format \"%s\"\n", inform);
|
||||
|
||||
if (depfile) {
|
||||
fputc('\n', depfile);
|
||||
fclose(depfile);
|
||||
}
|
||||
|
||||
if (cmdline_boot_cpuid != -1)
|
||||
bi->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
|
||||
fill_fullpaths(bi->dt, "");
|
||||
process_checks(force, bi);
|
||||
|
||||
if (sort)
|
||||
sort_tree(bi);
|
||||
|
||||
if (streq(outname, "-")) {
|
||||
outf = stdout;
|
||||
} else {
|
||||
outf = fopen(outname, "w");
|
||||
if (! outf)
|
||||
die("Couldn't open output file %s: %s\n",
|
||||
outname, strerror(errno));
|
||||
}
|
||||
|
||||
if (streq(outform, "dts")) {
|
||||
dt_to_source(outf, bi);
|
||||
} else if (streq(outform, "dtb")) {
|
||||
dt_to_blob(outf, bi, outversion);
|
||||
} else if (streq(outform, "asm")) {
|
||||
dt_to_asm(outf, bi, outversion);
|
||||
} else if (streq(outform, "null")) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
die("Unknown output format \"%s\"\n", outform);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
#ifndef _DTC_H
|
||||
#define _DTC_H
|
||||
|
||||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfdt_env.h>
|
||||
#include <fdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(fmt,args...) printf(fmt, ##args)
|
||||
#else
|
||||
#define debug(fmt,args...)
|
||||
#endif
|
||||
|
||||
|
||||
#define DEFAULT_FDT_VERSION 17
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*/
|
||||
extern int quiet; /* Level of quietness */
|
||||
extern int reservenum; /* Number of memory reservation slots */
|
||||
extern int minsize; /* Minimum blob size */
|
||||
extern int padsize; /* Additional padding to blob */
|
||||
extern int phandle_format; /* Use linux,phandle or phandle properties */
|
||||
|
||||
#define PHANDLE_LEGACY 0x1
|
||||
#define PHANDLE_EPAPR 0x2
|
||||
#define PHANDLE_BOTH 0x3
|
||||
|
||||
typedef uint32_t cell_t;
|
||||
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
|
||||
|
||||
#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/* Data blobs */
|
||||
enum markertype {
|
||||
REF_PHANDLE,
|
||||
REF_PATH,
|
||||
LABEL,
|
||||
};
|
||||
|
||||
struct marker {
|
||||
enum markertype type;
|
||||
int offset;
|
||||
char *ref;
|
||||
struct marker *next;
|
||||
};
|
||||
|
||||
struct data {
|
||||
int len;
|
||||
char *val;
|
||||
struct marker *markers;
|
||||
};
|
||||
|
||||
|
||||
#define empty_data ((struct data){ /* all .members = 0 or NULL */ })
|
||||
|
||||
#define for_each_marker(m) \
|
||||
for (; (m); (m) = (m)->next)
|
||||
#define for_each_marker_of_type(m, t) \
|
||||
for_each_marker(m) \
|
||||
if ((m)->type == (t))
|
||||
|
||||
void data_free(struct data d);
|
||||
|
||||
struct data data_grow_for(struct data d, int xlen);
|
||||
|
||||
struct data data_copy_mem(const char *mem, int len);
|
||||
struct data data_copy_escape_string(const char *s, int len);
|
||||
struct data data_copy_file(FILE *f, size_t len);
|
||||
|
||||
struct data data_append_data(struct data d, const void *p, int len);
|
||||
struct data data_insert_at_marker(struct data d, struct marker *m,
|
||||
const void *p, int len);
|
||||
struct data data_merge(struct data d1, struct data d2);
|
||||
struct data data_append_cell(struct data d, cell_t word);
|
||||
struct data data_append_integer(struct data d, uint64_t word, int bits);
|
||||
struct data data_append_re(struct data d, const struct fdt_reserve_entry *re);
|
||||
struct data data_append_addr(struct data d, uint64_t addr);
|
||||
struct data data_append_byte(struct data d, uint8_t byte);
|
||||
struct data data_append_zeroes(struct data d, int len);
|
||||
struct data data_append_align(struct data d, int align);
|
||||
|
||||
struct data data_add_marker(struct data d, enum markertype type, char *ref);
|
||||
|
||||
int data_is_one_string(struct data d);
|
||||
|
||||
/* DT constraints */
|
||||
|
||||
#define MAX_PROPNAME_LEN 31
|
||||
#define MAX_NODENAME_LEN 31
|
||||
|
||||
/* Live trees */
|
||||
struct label {
|
||||
int deleted;
|
||||
char *label;
|
||||
struct label *next;
|
||||
};
|
||||
|
||||
struct property {
|
||||
int deleted;
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
struct property *next;
|
||||
|
||||
struct label *labels;
|
||||
};
|
||||
|
||||
struct node {
|
||||
int deleted;
|
||||
char *name;
|
||||
struct property *proplist;
|
||||
struct node *children;
|
||||
|
||||
struct node *parent;
|
||||
struct node *next_sibling;
|
||||
|
||||
char *fullpath;
|
||||
int basenamelen;
|
||||
|
||||
cell_t phandle;
|
||||
int addr_cells, size_cells;
|
||||
|
||||
struct label *labels;
|
||||
};
|
||||
|
||||
#define for_each_label_withdel(l0, l) \
|
||||
for ((l) = (l0); (l); (l) = (l)->next)
|
||||
|
||||
#define for_each_label(l0, l) \
|
||||
for_each_label_withdel(l0, l) \
|
||||
if (!(l)->deleted)
|
||||
|
||||
#define for_each_property_withdel(n, p) \
|
||||
for ((p) = (n)->proplist; (p); (p) = (p)->next)
|
||||
|
||||
#define for_each_property(n, p) \
|
||||
for_each_property_withdel(n, p) \
|
||||
if (!(p)->deleted)
|
||||
|
||||
#define for_each_child_withdel(n, c) \
|
||||
for ((c) = (n)->children; (c); (c) = (c)->next_sibling)
|
||||
|
||||
#define for_each_child(n, c) \
|
||||
for_each_child_withdel(n, c) \
|
||||
if (!(c)->deleted)
|
||||
|
||||
void add_label(struct label **labels, char *label);
|
||||
void delete_labels(struct label **labels);
|
||||
|
||||
struct property *build_property(char *name, struct data val);
|
||||
struct property *build_property_delete(char *name);
|
||||
struct property *chain_property(struct property *first, struct property *list);
|
||||
struct property *reverse_properties(struct property *first);
|
||||
|
||||
struct node *build_node(struct property *proplist, struct node *children);
|
||||
struct node *build_node_delete(void);
|
||||
struct node *name_node(struct node *node, char *name);
|
||||
struct node *chain_node(struct node *first, struct node *list);
|
||||
struct node *merge_nodes(struct node *old_node, struct node *new_node);
|
||||
|
||||
void add_property(struct node *node, struct property *prop);
|
||||
void delete_property_by_name(struct node *node, char *name);
|
||||
void delete_property(struct property *prop);
|
||||
void add_child(struct node *parent, struct node *child);
|
||||
void delete_node_by_name(struct node *parent, char *name);
|
||||
void delete_node(struct node *node);
|
||||
|
||||
const char *get_unitname(struct node *node);
|
||||
struct property *get_property(struct node *node, const char *propname);
|
||||
cell_t propval_cell(struct property *prop);
|
||||
struct property *get_property_by_label(struct node *tree, const char *label,
|
||||
struct node **node);
|
||||
struct marker *get_marker_label(struct node *tree, const char *label,
|
||||
struct node **node, struct property **prop);
|
||||
struct node *get_subnode(struct node *node, const char *nodename);
|
||||
struct node *get_node_by_path(struct node *tree, const char *path);
|
||||
struct node *get_node_by_label(struct node *tree, const char *label);
|
||||
struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
|
||||
struct node *get_node_by_ref(struct node *tree, const char *ref);
|
||||
cell_t get_node_phandle(struct node *root, struct node *node);
|
||||
|
||||
uint32_t guess_boot_cpuid(struct node *tree);
|
||||
|
||||
/* Boot info (tree plus memreserve information */
|
||||
|
||||
struct reserve_info {
|
||||
struct fdt_reserve_entry re;
|
||||
|
||||
struct reserve_info *next;
|
||||
|
||||
struct label *labels;
|
||||
};
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t start, uint64_t len);
|
||||
struct reserve_info *chain_reserve_entry(struct reserve_info *first,
|
||||
struct reserve_info *list);
|
||||
struct reserve_info *add_reserve_entry(struct reserve_info *list,
|
||||
struct reserve_info *new);
|
||||
|
||||
|
||||
struct boot_info {
|
||||
struct reserve_info *reservelist;
|
||||
struct node *dt; /* the device tree */
|
||||
uint32_t boot_cpuid_phys;
|
||||
};
|
||||
|
||||
struct boot_info *build_boot_info(struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys);
|
||||
void sort_tree(struct boot_info *bi);
|
||||
|
||||
/* Checks */
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *optarg);
|
||||
void process_checks(int force, struct boot_info *bi);
|
||||
|
||||
/* Flattened trees */
|
||||
|
||||
void dt_to_blob(FILE *f, struct boot_info *bi, int version);
|
||||
void dt_to_asm(FILE *f, struct boot_info *bi, int version);
|
||||
|
||||
struct boot_info *dt_from_blob(const char *fname);
|
||||
|
||||
/* Tree source */
|
||||
|
||||
void dt_to_source(FILE *f, struct boot_info *bi);
|
||||
struct boot_info *dt_from_source(const char *f);
|
||||
|
||||
/* FS trees */
|
||||
|
||||
struct boot_info *dt_from_fs(const char *dirname);
|
||||
|
||||
#endif /* _DTC_H */
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* fdtdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt_env.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
|
||||
#define GET_CELL(p) (p += 4, *((const uint32_t *)(p-4)))
|
||||
|
||||
static void print_data(const char *data, int len)
|
||||
{
|
||||
int i;
|
||||
const char *p = data;
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (util_is_printable_string(data, len)) {
|
||||
printf(" = \"%s\"", (const char *)data);
|
||||
} else if ((len % 4) == 0) {
|
||||
printf(" = <");
|
||||
for (i = 0; i < len; i += 4)
|
||||
printf("0x%08x%s", fdt32_to_cpu(GET_CELL(p)),
|
||||
i < (len - 4) ? " " : "");
|
||||
printf(">");
|
||||
} else {
|
||||
printf(" = [");
|
||||
for (i = 0; i < len; i++)
|
||||
printf("%02x%s", *p++, i < len - 1 ? " " : "");
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_blob(void *blob)
|
||||
{
|
||||
struct fdt_header *bph = blob;
|
||||
uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap);
|
||||
uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct);
|
||||
uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings);
|
||||
struct fdt_reserve_entry *p_rsvmap =
|
||||
(struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap);
|
||||
const char *p_struct = (const char *)blob + off_dt;
|
||||
const char *p_strings = (const char *)blob + off_str;
|
||||
uint32_t version = fdt32_to_cpu(bph->version);
|
||||
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
|
||||
uint32_t tag;
|
||||
const char *p, *s, *t;
|
||||
int depth, sz, shift;
|
||||
int i;
|
||||
uint64_t addr, size;
|
||||
|
||||
depth = 0;
|
||||
shift = 4;
|
||||
|
||||
printf("/dts-v1/;\n");
|
||||
printf("// magic:\t\t0x%x\n", fdt32_to_cpu(bph->magic));
|
||||
printf("// totalsize:\t\t0x%x (%d)\n", totalsize, totalsize);
|
||||
printf("// off_dt_struct:\t0x%x\n", off_dt);
|
||||
printf("// off_dt_strings:\t0x%x\n", off_str);
|
||||
printf("// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap);
|
||||
printf("// version:\t\t%d\n", version);
|
||||
printf("// last_comp_version:\t%d\n",
|
||||
fdt32_to_cpu(bph->last_comp_version));
|
||||
if (version >= 2)
|
||||
printf("// boot_cpuid_phys:\t0x%x\n",
|
||||
fdt32_to_cpu(bph->boot_cpuid_phys));
|
||||
|
||||
if (version >= 3)
|
||||
printf("// size_dt_strings:\t0x%x\n",
|
||||
fdt32_to_cpu(bph->size_dt_strings));
|
||||
if (version >= 17)
|
||||
printf("// size_dt_struct:\t0x%x\n",
|
||||
fdt32_to_cpu(bph->size_dt_struct));
|
||||
printf("\n");
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
addr = fdt64_to_cpu(p_rsvmap[i].address);
|
||||
size = fdt64_to_cpu(p_rsvmap[i].size);
|
||||
if (addr == 0 && size == 0)
|
||||
break;
|
||||
|
||||
printf("/memreserve/ %llx %llx;\n",
|
||||
(unsigned long long)addr, (unsigned long long)size);
|
||||
}
|
||||
|
||||
p = p_struct;
|
||||
while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) {
|
||||
|
||||
/* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */
|
||||
|
||||
if (tag == FDT_BEGIN_NODE) {
|
||||
s = p;
|
||||
p = PALIGN(p + strlen(s) + 1, 4);
|
||||
|
||||
if (*s == '\0')
|
||||
s = "/";
|
||||
|
||||
printf("%*s%s {\n", depth * shift, "", s);
|
||||
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == FDT_END_NODE) {
|
||||
depth--;
|
||||
|
||||
printf("%*s};\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == FDT_NOP) {
|
||||
printf("%*s// [NOP]\n", depth * shift, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag != FDT_PROP) {
|
||||
fprintf(stderr, "%*s ** Unknown tag 0x%08x\n", depth * shift, "", tag);
|
||||
break;
|
||||
}
|
||||
sz = fdt32_to_cpu(GET_CELL(p));
|
||||
s = p_strings + fdt32_to_cpu(GET_CELL(p));
|
||||
if (version < 16 && sz >= 8)
|
||||
p = PALIGN(p, 8);
|
||||
t = p;
|
||||
|
||||
p = PALIGN(p + sz, 4);
|
||||
|
||||
printf("%*s%s", depth * shift, "", s);
|
||||
print_data(t, sz);
|
||||
printf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "supply input filename\n");
|
||||
return 5;
|
||||
}
|
||||
|
||||
buf = utilfdt_read(argv[1]);
|
||||
if (buf)
|
||||
dump_blob(buf);
|
||||
else
|
||||
return 10;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
||||
*
|
||||
* Portions from U-Boot cmd_fdt.c (C) Copyright 2007
|
||||
* Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
|
||||
* Based on code written by:
|
||||
* Pantelis Antoniou <pantelis.antoniou@gmail.com> and
|
||||
* Matthew McClintock <msm@freescale.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
enum display_mode {
|
||||
MODE_SHOW_VALUE, /* show values for node properties */
|
||||
MODE_LIST_PROPS, /* list the properties for a node */
|
||||
MODE_LIST_SUBNODES, /* list the subnodes of a node */
|
||||
};
|
||||
|
||||
/* Holds information which controls our output and options */
|
||||
struct display_info {
|
||||
int type; /* data type (s/i/u/x or 0 for default) */
|
||||
int size; /* data size (1/2/4) */
|
||||
enum display_mode mode; /* display mode that we are using */
|
||||
const char *default_val; /* default value if node/property not found */
|
||||
};
|
||||
|
||||
static void report_error(const char *where, int err)
|
||||
{
|
||||
fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays data of a given length according to selected options
|
||||
*
|
||||
* If a specific data type is provided in disp, then this is used. Otherwise
|
||||
* we try to guess the data type / size from the contents.
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param data Data to display
|
||||
* @param len Maximum length of buffer
|
||||
* @return 0 if ok, -1 if data does not match format
|
||||
*/
|
||||
static int show_data(struct display_info *disp, const char *data, int len)
|
||||
{
|
||||
int i, size;
|
||||
const uint8_t *p = (const uint8_t *)data;
|
||||
const char *s;
|
||||
int value;
|
||||
int is_string;
|
||||
char fmt[3];
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
is_string = (disp->type) == 's' ||
|
||||
(!disp->type && util_is_printable_string(data, len));
|
||||
if (is_string) {
|
||||
if (data[len - 1] != '\0') {
|
||||
fprintf(stderr, "Unterminated string\n");
|
||||
return -1;
|
||||
}
|
||||
for (s = data; s - data < len; s += strlen(s) + 1) {
|
||||
if (s != data)
|
||||
printf(" ");
|
||||
printf("%s", (const char *)s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size = disp->size;
|
||||
if (size == -1) {
|
||||
size = (len % 4) == 0 ? 4 : 1;
|
||||
} else if (len % size) {
|
||||
fprintf(stderr, "Property length must be a multiple of "
|
||||
"selected data size\n");
|
||||
return -1;
|
||||
}
|
||||
fmt[0] = '%';
|
||||
fmt[1] = disp->type ? disp->type : 'd';
|
||||
fmt[2] = '\0';
|
||||
for (i = 0; i < len; i += size, p += size) {
|
||||
if (i)
|
||||
printf(" ");
|
||||
value = size == 4 ? fdt32_to_cpu(*(const uint32_t *)p) :
|
||||
size == 2 ? (*p << 8) | p[1] : *p;
|
||||
printf(fmt, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all properties in a node, one per line.
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param node Node to display
|
||||
* @return 0 if ok, or FDT_ERR... if not.
|
||||
*/
|
||||
static int list_properties(const void *blob, int node)
|
||||
{
|
||||
const struct fdt_property *data;
|
||||
const char *name;
|
||||
int prop;
|
||||
|
||||
prop = fdt_first_property_offset(blob, node);
|
||||
do {
|
||||
/* 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));
|
||||
if (name)
|
||||
puts(name);
|
||||
prop = fdt_next_property_offset(blob, prop);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
#define MAX_LEVEL 32 /* how deeply nested we will go */
|
||||
|
||||
/**
|
||||
* List all subnodes in a node, one per line
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param node Node to display
|
||||
* @return 0 if ok, or FDT_ERR... if not.
|
||||
*/
|
||||
static int list_subnodes(const void *blob, int node)
|
||||
{
|
||||
int nextoffset; /* next node offset from libfdt */
|
||||
uint32_t tag; /* current tag */
|
||||
int level = 0; /* keep track of nesting level */
|
||||
const char *pathp;
|
||||
int depth = 1; /* the assumed depth of this node */
|
||||
|
||||
while (level >= 0) {
|
||||
tag = fdt_next_tag(blob, node, &nextoffset);
|
||||
switch (tag) {
|
||||
case FDT_BEGIN_NODE:
|
||||
pathp = fdt_get_name(blob, node, NULL);
|
||||
if (level <= depth) {
|
||||
if (pathp == NULL)
|
||||
pathp = "/* NULL pointer error */";
|
||||
if (*pathp == '\0')
|
||||
pathp = "/"; /* root is nameless */
|
||||
if (level == 1)
|
||||
puts(pathp);
|
||||
}
|
||||
level++;
|
||||
if (level >= MAX_LEVEL) {
|
||||
printf("Nested too deep, aborting.\n");
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case FDT_END_NODE:
|
||||
level--;
|
||||
if (level == 0)
|
||||
level = -1; /* exit the loop */
|
||||
break;
|
||||
case FDT_END:
|
||||
return 1;
|
||||
case FDT_PROP:
|
||||
break;
|
||||
default:
|
||||
if (level <= depth)
|
||||
printf("Unknown tag 0x%08X\n", tag);
|
||||
return 1;
|
||||
}
|
||||
node = nextoffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the data for a given node (and perhaps property) according to the
|
||||
* display option provided.
|
||||
*
|
||||
* @param blob FDT blob
|
||||
* @param disp Display information / options
|
||||
* @param node Node to display
|
||||
* @param property Name of property to display, or NULL if none
|
||||
* @return 0 if ok, -ve on error
|
||||
*/
|
||||
static int show_data_for_item(const void *blob, struct display_info *disp,
|
||||
int node, const char *property)
|
||||
{
|
||||
const void *value = NULL;
|
||||
int len, err = 0;
|
||||
|
||||
switch (disp->mode) {
|
||||
case MODE_LIST_PROPS:
|
||||
err = list_properties(blob, node);
|
||||
break;
|
||||
|
||||
case MODE_LIST_SUBNODES:
|
||||
err = list_subnodes(blob, node);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(property);
|
||||
value = fdt_getprop(blob, node, property, &len);
|
||||
if (value) {
|
||||
if (show_data(disp, value, len))
|
||||
err = -1;
|
||||
else
|
||||
printf("\n");
|
||||
} else if (disp->default_val) {
|
||||
puts(disp->default_val);
|
||||
} else {
|
||||
report_error(property, len);
|
||||
err = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the main fdtget operation, given a filename and valid arguments
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param filename Filename of blob file
|
||||
* @param arg List of arguments to process
|
||||
* @param arg_count Number of arguments
|
||||
* @param return 0 if ok, -ve on error
|
||||
*/
|
||||
static int do_fdtget(struct display_info *disp, const char *filename,
|
||||
char **arg, int arg_count, int args_per_step)
|
||||
{
|
||||
char *blob;
|
||||
const char *prop;
|
||||
int i, node;
|
||||
|
||||
blob = utilfdt_read(filename);
|
||||
if (!blob)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i + args_per_step <= arg_count; i += args_per_step) {
|
||||
node = fdt_path_offset(blob, arg[i]);
|
||||
if (node < 0) {
|
||||
if (disp->default_val) {
|
||||
puts(disp->default_val);
|
||||
continue;
|
||||
} else {
|
||||
report_error(arg[i], node);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
prop = args_per_step == 1 ? NULL : arg[i + 1];
|
||||
|
||||
if (show_data_for_item(blob, disp, node, prop))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *usage_msg =
|
||||
"fdtget - read values from device tree\n"
|
||||
"\n"
|
||||
"Each value is printed on a new line.\n\n"
|
||||
"Usage:\n"
|
||||
" fdtget <options> <dt file> [<node> <property>]...\n"
|
||||
" fdtget -p <options> <dt file> [<node> ]...\n"
|
||||
"Options:\n"
|
||||
"\t-t <type>\tType of data\n"
|
||||
"\t-p\t\tList properties for each node\n"
|
||||
"\t-l\t\tList subnodes for each node\n"
|
||||
"\t-d\t\tDefault value to display when the property is "
|
||||
"missing\n"
|
||||
"\t-h\t\tPrint this help\n\n"
|
||||
USAGE_TYPE_MSG;
|
||||
|
||||
static void usage(const char *msg)
|
||||
{
|
||||
if (msg)
|
||||
fprintf(stderr, "Error: %s\n\n", msg);
|
||||
|
||||
fprintf(stderr, "%s", usage_msg);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *filename = NULL;
|
||||
struct display_info disp;
|
||||
int args_per_step = 2;
|
||||
|
||||
/* set defaults */
|
||||
memset(&disp, '\0', sizeof(disp));
|
||||
disp.size = -1;
|
||||
disp.mode = MODE_SHOW_VALUE;
|
||||
for (;;) {
|
||||
int c = getopt(argc, argv, "d:hlpt:");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
usage(NULL);
|
||||
|
||||
case 't':
|
||||
if (utilfdt_decode_type(optarg, &disp.type,
|
||||
&disp.size))
|
||||
usage("Invalid type string");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
disp.mode = MODE_LIST_PROPS;
|
||||
args_per_step = 1;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
disp.mode = MODE_LIST_SUBNODES;
|
||||
args_per_step = 1;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
disp.default_val = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filename = argv[optind++];
|
||||
if (!filename)
|
||||
usage("Missing filename");
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
/* Allow no arguments, and silently succeed */
|
||||
if (!argc)
|
||||
return 0;
|
||||
|
||||
/* Check for node, property arguments */
|
||||
if (args_per_step == 2 && (argc % 2))
|
||||
usage("Must have an even number of arguments");
|
||||
|
||||
if (do_fdtget(&disp, filename, argv, argc, args_per_step))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* These are the operations we support */
|
||||
enum oper_type {
|
||||
OPER_WRITE_PROP, /* Write a property in a node */
|
||||
OPER_CREATE_NODE, /* Create a new node */
|
||||
};
|
||||
|
||||
struct display_info {
|
||||
enum oper_type oper; /* operation to perform */
|
||||
int type; /* data type (s/i/u/x or 0 for default) */
|
||||
int size; /* data size (1/2/4) */
|
||||
int verbose; /* verbose output */
|
||||
int auto_path; /* automatically create all path components */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Report an error with a particular node.
|
||||
*
|
||||
* @param name Node name to report error on
|
||||
* @param namelen Length of node name, or -1 to use entire string
|
||||
* @param err Error number to report (-FDT_ERR_...)
|
||||
*/
|
||||
static void report_error(const char *name, int namelen, int err)
|
||||
{
|
||||
if (namelen == -1)
|
||||
namelen = strlen(name);
|
||||
fprintf(stderr, "Error at '%1.*s': %s\n", namelen, name,
|
||||
fdt_strerror(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a series of arguments in a property value.
|
||||
*
|
||||
* @param disp Display information / options
|
||||
* @param arg List of arguments from command line
|
||||
* @param arg_count Number of arguments (may be 0)
|
||||
* @param valuep Returns buffer containing value
|
||||
* @param *value_len Returns length of value encoded
|
||||
*/
|
||||
static int encode_value(struct display_info *disp, char **arg, int arg_count,
|
||||
char **valuep, int *value_len)
|
||||
{
|
||||
char *value = NULL; /* holding area for value */
|
||||
int value_size = 0; /* size of holding area */
|
||||
char *ptr; /* pointer to current value position */
|
||||
int len; /* length of this cell/string/byte */
|
||||
int ival;
|
||||
int upto; /* the number of bytes we have written to buf */
|
||||
char fmt[3];
|
||||
|
||||
upto = 0;
|
||||
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "Decoding value:\n");
|
||||
|
||||
fmt[0] = '%';
|
||||
fmt[1] = disp->type ? disp->type : 'd';
|
||||
fmt[2] = '\0';
|
||||
for (; arg_count > 0; arg++, arg_count--, upto += len) {
|
||||
/* assume integer unless told otherwise */
|
||||
if (disp->type == 's')
|
||||
len = strlen(*arg) + 1;
|
||||
else
|
||||
len = disp->size == -1 ? 4 : disp->size;
|
||||
|
||||
/* enlarge our value buffer by a suitable margin if needed */
|
||||
if (upto + len > value_size) {
|
||||
value_size = (upto + len) + 500;
|
||||
value = realloc(value, value_size);
|
||||
if (!value) {
|
||||
fprintf(stderr, "Out of mmory: cannot alloc "
|
||||
"%d bytes\n", value_size);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ptr = value + upto;
|
||||
if (disp->type == 's') {
|
||||
memcpy(ptr, *arg, len);
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "\tstring: '%s'\n", ptr);
|
||||
} else {
|
||||
int *iptr = (int *)ptr;
|
||||
sscanf(*arg, fmt, &ival);
|
||||
if (len == 4)
|
||||
*iptr = cpu_to_fdt32(ival);
|
||||
else
|
||||
*ptr = (uint8_t)ival;
|
||||
if (disp->verbose) {
|
||||
fprintf(stderr, "\t%s: %d\n",
|
||||
disp->size == 1 ? "byte" :
|
||||
disp->size == 2 ? "short" : "int",
|
||||
ival);
|
||||
}
|
||||
}
|
||||
}
|
||||
*value_len = upto;
|
||||
*valuep = value;
|
||||
if (disp->verbose)
|
||||
fprintf(stderr, "Value size %d\n", upto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int store_key_value(void *blob, const char *node_name,
|
||||
const char *property, const char *buf, int len)
|
||||
{
|
||||
int node;
|
||||
int err;
|
||||
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = fdt_setprop(blob, node, property, buf, len);
|
||||
if (err) {
|
||||
report_error(property, -1, err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create paths as needed for all components of a path
|
||||
*
|
||||
* Any components of the path that do not exist are created. Errors are
|
||||
* reported.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param in_path Path to process
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int create_paths(void *blob, const char *in_path)
|
||||
{
|
||||
const char *path = in_path;
|
||||
const char *sep;
|
||||
int node, offset = 0;
|
||||
|
||||
/* skip leading '/' */
|
||||
while (*path == '/')
|
||||
path++;
|
||||
|
||||
for (sep = path; *sep; path = sep + 1, offset = node) {
|
||||
/* equivalent to strchrnul(), but it requires _GNU_SOURCE */
|
||||
sep = strchr(path, '/');
|
||||
if (!sep)
|
||||
sep = path + strlen(path);
|
||||
|
||||
node = fdt_subnode_offset_namelen(blob, offset, path,
|
||||
sep - path);
|
||||
if (node == -FDT_ERR_NOTFOUND) {
|
||||
node = fdt_add_subnode_namelen(blob, offset, path,
|
||||
sep - path);
|
||||
}
|
||||
if (node < 0) {
|
||||
report_error(path, sep - path, node);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new node in the fdt.
|
||||
*
|
||||
* This will overwrite the node_name string. Any error is reported.
|
||||
*
|
||||
* TODO: Perhaps create fdt_path_offset_namelen() so we don't need to do this.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Name of node to create
|
||||
* @return new node offset if found, or -1 on failure
|
||||
*/
|
||||
static int create_node(void *blob, const char *node_name)
|
||||
{
|
||||
int node = 0;
|
||||
char *p;
|
||||
|
||||
p = strrchr(node_name, '/');
|
||||
if (!p) {
|
||||
report_error(node_name, -1, -FDT_ERR_BADPATH);
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
if (p > node_name) {
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
node = fdt_add_subnode(blob, node, p + 1);
|
||||
if (node < 0) {
|
||||
report_error(p + 1, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_fdtput(struct display_info *disp, const char *filename,
|
||||
char **arg, int arg_count)
|
||||
{
|
||||
char *value;
|
||||
char *blob;
|
||||
int len, ret = 0;
|
||||
|
||||
blob = utilfdt_read(filename);
|
||||
if (!blob)
|
||||
return -1;
|
||||
|
||||
switch (disp->oper) {
|
||||
case OPER_WRITE_PROP:
|
||||
/*
|
||||
* Convert the arguments into a single binary value, then
|
||||
* store them into the property.
|
||||
*/
|
||||
assert(arg_count >= 2);
|
||||
if (disp->auto_path && create_paths(blob, *arg))
|
||||
return -1;
|
||||
if (encode_value(disp, arg + 2, arg_count - 2, &value, &len) ||
|
||||
store_key_value(blob, *arg, arg[1], value, len))
|
||||
ret = -1;
|
||||
break;
|
||||
case OPER_CREATE_NODE:
|
||||
for (; ret >= 0 && arg_count--; arg++) {
|
||||
if (disp->auto_path)
|
||||
ret = create_paths(blob, *arg);
|
||||
else
|
||||
ret = create_node(blob, *arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ret >= 0)
|
||||
ret = utilfdt_write(filename, blob);
|
||||
|
||||
free(blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *usage_msg =
|
||||
"fdtput - write a property value to a device tree\n"
|
||||
"\n"
|
||||
"The command line arguments are joined together into a single value.\n"
|
||||
"\n"
|
||||
"Usage:\n"
|
||||
" fdtput <options> <dt file> <node> <property> [<value>...]\n"
|
||||
" fdtput -c <options> <dt file> [<node>...]\n"
|
||||
"Options:\n"
|
||||
"\t-c\t\tCreate nodes if they don't already exist\n"
|
||||
"\t-p\t\tAutomatically create nodes as needed for the node path\n"
|
||||
"\t-t <type>\tType of data\n"
|
||||
"\t-v\t\tVerbose: display each value decoded from command line\n"
|
||||
"\t-h\t\tPrint this help\n\n"
|
||||
USAGE_TYPE_MSG;
|
||||
|
||||
static void usage(const char *msg)
|
||||
{
|
||||
if (msg)
|
||||
fprintf(stderr, "Error: %s\n\n", msg);
|
||||
|
||||
fprintf(stderr, "%s", usage_msg);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct display_info disp;
|
||||
char *filename = NULL;
|
||||
|
||||
memset(&disp, '\0', sizeof(disp));
|
||||
disp.size = -1;
|
||||
disp.oper = OPER_WRITE_PROP;
|
||||
for (;;) {
|
||||
int c = getopt(argc, argv, "chpt:v");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
/*
|
||||
* TODO: add options to:
|
||||
* - delete property
|
||||
* - delete node (optionally recursively)
|
||||
* - rename node
|
||||
* - pack fdt before writing
|
||||
* - set amount of free space when writing
|
||||
* - expand fdt if value doesn't fit
|
||||
*/
|
||||
switch (c) {
|
||||
case 'c':
|
||||
disp.oper = OPER_CREATE_NODE;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
usage(NULL);
|
||||
case 'p':
|
||||
disp.auto_path = 1;
|
||||
break;
|
||||
case 't':
|
||||
if (utilfdt_decode_type(optarg, &disp.type,
|
||||
&disp.size))
|
||||
usage("Invalid type string");
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
disp.verbose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
filename = argv[optind++];
|
||||
if (!filename)
|
||||
usage("Missing filename");
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (disp.oper == OPER_WRITE_PROP) {
|
||||
if (argc < 1)
|
||||
usage("Missing node");
|
||||
if (argc < 2)
|
||||
usage("Missing property");
|
||||
}
|
||||
|
||||
if (do_fdtput(&disp, filename, argv, argc))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,933 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
#define FTF_FULLPATH 0x1
|
||||
#define FTF_VARALIGN 0x2
|
||||
#define FTF_NAMEPROPS 0x4
|
||||
#define FTF_BOOTCPUID 0x8
|
||||
#define FTF_STRTABSIZE 0x10
|
||||
#define FTF_STRUCTSIZE 0x20
|
||||
#define FTF_NOPS 0x40
|
||||
|
||||
static struct version_info {
|
||||
int version;
|
||||
int last_comp_version;
|
||||
int hdr_size;
|
||||
int flags;
|
||||
} version_table[] = {
|
||||
{1, 1, FDT_V1_SIZE,
|
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS},
|
||||
{2, 1, FDT_V2_SIZE,
|
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID},
|
||||
{3, 1, FDT_V3_SIZE,
|
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE},
|
||||
{16, 16, FDT_V3_SIZE,
|
||||
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS},
|
||||
{17, 16, FDT_V17_SIZE,
|
||||
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS},
|
||||
};
|
||||
|
||||
struct emitter {
|
||||
void (*cell)(void *, cell_t);
|
||||
void (*string)(void *, char *, int);
|
||||
void (*align)(void *, int);
|
||||
void (*data)(void *, struct data);
|
||||
void (*beginnode)(void *, struct label *labels);
|
||||
void (*endnode)(void *, struct label *labels);
|
||||
void (*property)(void *, struct label *labels);
|
||||
};
|
||||
|
||||
static void bin_emit_cell(void *e, cell_t val)
|
||||
{
|
||||
struct data *dtbuf = e;
|
||||
|
||||
*dtbuf = data_append_cell(*dtbuf, val);
|
||||
}
|
||||
|
||||
static void bin_emit_string(void *e, char *str, int len)
|
||||
{
|
||||
struct data *dtbuf = e;
|
||||
|
||||
if (len == 0)
|
||||
len = strlen(str);
|
||||
|
||||
*dtbuf = data_append_data(*dtbuf, str, len);
|
||||
*dtbuf = data_append_byte(*dtbuf, '\0');
|
||||
}
|
||||
|
||||
static void bin_emit_align(void *e, int a)
|
||||
{
|
||||
struct data *dtbuf = e;
|
||||
|
||||
*dtbuf = data_append_align(*dtbuf, a);
|
||||
}
|
||||
|
||||
static void bin_emit_data(void *e, struct data d)
|
||||
{
|
||||
struct data *dtbuf = e;
|
||||
|
||||
*dtbuf = data_append_data(*dtbuf, d.val, d.len);
|
||||
}
|
||||
|
||||
static void bin_emit_beginnode(void *e, struct label *labels)
|
||||
{
|
||||
bin_emit_cell(e, FDT_BEGIN_NODE);
|
||||
}
|
||||
|
||||
static void bin_emit_endnode(void *e, struct label *labels)
|
||||
{
|
||||
bin_emit_cell(e, FDT_END_NODE);
|
||||
}
|
||||
|
||||
static void bin_emit_property(void *e, struct label *labels)
|
||||
{
|
||||
bin_emit_cell(e, FDT_PROP);
|
||||
}
|
||||
|
||||
static struct emitter bin_emitter = {
|
||||
.cell = bin_emit_cell,
|
||||
.string = bin_emit_string,
|
||||
.align = bin_emit_align,
|
||||
.data = bin_emit_data,
|
||||
.beginnode = bin_emit_beginnode,
|
||||
.endnode = bin_emit_endnode,
|
||||
.property = bin_emit_property,
|
||||
};
|
||||
|
||||
static void emit_label(FILE *f, const char *prefix, const char *label)
|
||||
{
|
||||
fprintf(f, "\t.globl\t%s_%s\n", prefix, label);
|
||||
fprintf(f, "%s_%s:\n", prefix, label);
|
||||
fprintf(f, "_%s_%s:\n", prefix, label);
|
||||
}
|
||||
|
||||
static void emit_offset_label(FILE *f, const char *label, int offset)
|
||||
{
|
||||
fprintf(f, "\t.globl\t%s\n", label);
|
||||
fprintf(f, "%s\t= . + %d\n", label, offset);
|
||||
}
|
||||
|
||||
#define ASM_EMIT_BELONG(f, fmt, ...) \
|
||||
{ \
|
||||
fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \
|
||||
fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \
|
||||
fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \
|
||||
fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \
|
||||
}
|
||||
|
||||
static void asm_emit_cell(void *e, cell_t val)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n",
|
||||
(val >> 24) & 0xff, (val >> 16) & 0xff,
|
||||
(val >> 8) & 0xff, val & 0xff);
|
||||
}
|
||||
|
||||
static void asm_emit_string(void *e, char *str, int len)
|
||||
{
|
||||
FILE *f = e;
|
||||
char c = 0;
|
||||
|
||||
if (len != 0) {
|
||||
/* XXX: ewww */
|
||||
c = str[len];
|
||||
str[len] = '\0';
|
||||
}
|
||||
|
||||
fprintf(f, "\t.string\t\"%s\"\n", str);
|
||||
|
||||
if (len != 0) {
|
||||
str[len] = c;
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_emit_align(void *e, int a)
|
||||
{
|
||||
FILE *f = e;
|
||||
|
||||
fprintf(f, "\t.balign\t%d, 0\n", a);
|
||||
}
|
||||
|
||||
static void asm_emit_data(void *e, struct data d)
|
||||
{
|
||||
FILE *f = e;
|
||||
int off = 0;
|
||||
struct marker *m = d.markers;
|
||||
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
emit_offset_label(f, m->ref, m->offset);
|
||||
|
||||
while ((d.len - off) >= sizeof(uint32_t)) {
|
||||
asm_emit_cell(e, fdt32_to_cpu(*((uint32_t *)(d.val+off))));
|
||||
off += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
while ((d.len - off) >= 1) {
|
||||
fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]);
|
||||
off += 1;
|
||||
}
|
||||
|
||||
assert(off == d.len);
|
||||
}
|
||||
|
||||
static void asm_emit_beginnode(void *e, struct label *labels)
|
||||
{
|
||||
FILE *f = e;
|
||||
struct label *l;
|
||||
|
||||
for_each_label(labels, l) {
|
||||
fprintf(f, "\t.globl\t%s\n", l->label);
|
||||
fprintf(f, "%s:\n", l->label);
|
||||
}
|
||||
fprintf(f, "\t/* FDT_BEGIN_NODE */\n");
|
||||
asm_emit_cell(e, FDT_BEGIN_NODE);
|
||||
}
|
||||
|
||||
static void asm_emit_endnode(void *e, struct label *labels)
|
||||
{
|
||||
FILE *f = e;
|
||||
struct label *l;
|
||||
|
||||
fprintf(f, "\t/* FDT_END_NODE */\n");
|
||||
asm_emit_cell(e, FDT_END_NODE);
|
||||
for_each_label(labels, l) {
|
||||
fprintf(f, "\t.globl\t%s_end\n", l->label);
|
||||
fprintf(f, "%s_end:\n", l->label);
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_emit_property(void *e, struct label *labels)
|
||||
{
|
||||
FILE *f = e;
|
||||
struct label *l;
|
||||
|
||||
for_each_label(labels, l) {
|
||||
fprintf(f, "\t.globl\t%s\n", l->label);
|
||||
fprintf(f, "%s:\n", l->label);
|
||||
}
|
||||
fprintf(f, "\t/* FDT_PROP */\n");
|
||||
asm_emit_cell(e, FDT_PROP);
|
||||
}
|
||||
|
||||
static struct emitter asm_emitter = {
|
||||
.cell = asm_emit_cell,
|
||||
.string = asm_emit_string,
|
||||
.align = asm_emit_align,
|
||||
.data = asm_emit_data,
|
||||
.beginnode = asm_emit_beginnode,
|
||||
.endnode = asm_emit_endnode,
|
||||
.property = asm_emit_property,
|
||||
};
|
||||
|
||||
static int stringtable_insert(struct data *d, const char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* FIXME: do this more efficiently? */
|
||||
|
||||
for (i = 0; i < d->len; i++) {
|
||||
if (streq(str, d->val + i))
|
||||
return i;
|
||||
}
|
||||
|
||||
*d = data_append_data(*d, str, strlen(str)+1);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void flatten_tree(struct node *tree, struct emitter *emit,
|
||||
void *etarget, struct data *strbuf,
|
||||
struct version_info *vi)
|
||||
{
|
||||
struct property *prop;
|
||||
struct node *child;
|
||||
int seen_name_prop = 0;
|
||||
|
||||
if (tree->deleted)
|
||||
return;
|
||||
|
||||
emit->beginnode(etarget, tree->labels);
|
||||
|
||||
if (vi->flags & FTF_FULLPATH)
|
||||
emit->string(etarget, tree->fullpath, 0);
|
||||
else
|
||||
emit->string(etarget, tree->name, 0);
|
||||
|
||||
emit->align(etarget, sizeof(cell_t));
|
||||
|
||||
for_each_property(tree, prop) {
|
||||
int nameoff;
|
||||
|
||||
if (streq(prop->name, "name"))
|
||||
seen_name_prop = 1;
|
||||
|
||||
nameoff = stringtable_insert(strbuf, prop->name);
|
||||
|
||||
emit->property(etarget, prop->labels);
|
||||
emit->cell(etarget, prop->val.len);
|
||||
emit->cell(etarget, nameoff);
|
||||
|
||||
if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8))
|
||||
emit->align(etarget, 8);
|
||||
|
||||
emit->data(etarget, prop->val);
|
||||
emit->align(etarget, sizeof(cell_t));
|
||||
}
|
||||
|
||||
if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) {
|
||||
emit->property(etarget, NULL);
|
||||
emit->cell(etarget, tree->basenamelen+1);
|
||||
emit->cell(etarget, stringtable_insert(strbuf, "name"));
|
||||
|
||||
if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8))
|
||||
emit->align(etarget, 8);
|
||||
|
||||
emit->string(etarget, tree->name, tree->basenamelen);
|
||||
emit->align(etarget, sizeof(cell_t));
|
||||
}
|
||||
|
||||
for_each_child(tree, child) {
|
||||
flatten_tree(child, emit, etarget, strbuf, vi);
|
||||
}
|
||||
|
||||
emit->endnode(etarget, tree->labels);
|
||||
}
|
||||
|
||||
static struct data flatten_reserve_list(struct reserve_info *reservelist,
|
||||
struct version_info *vi)
|
||||
{
|
||||
struct reserve_info *re;
|
||||
struct data d = empty_data;
|
||||
static struct fdt_reserve_entry null_re = {0,0};
|
||||
int j;
|
||||
|
||||
for (re = reservelist; re; re = re->next) {
|
||||
d = data_append_re(d, &re->re);
|
||||
}
|
||||
/*
|
||||
* Add additional reserved slots if the user asked for them.
|
||||
*/
|
||||
for (j = 0; j < reservenum; j++) {
|
||||
d = data_append_re(d, &null_re);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static void make_fdt_header(struct fdt_header *fdt,
|
||||
struct version_info *vi,
|
||||
int reservesize, int dtsize, int strsize,
|
||||
int boot_cpuid_phys)
|
||||
{
|
||||
int reserve_off;
|
||||
|
||||
reservesize += sizeof(struct fdt_reserve_entry);
|
||||
|
||||
memset(fdt, 0xff, sizeof(*fdt));
|
||||
|
||||
fdt->magic = cpu_to_fdt32(FDT_MAGIC);
|
||||
fdt->version = cpu_to_fdt32(vi->version);
|
||||
fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version);
|
||||
|
||||
/* Reserve map should be doubleword aligned */
|
||||
reserve_off = ALIGN(vi->hdr_size, 8);
|
||||
|
||||
fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off);
|
||||
fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize);
|
||||
fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize
|
||||
+ dtsize);
|
||||
fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize);
|
||||
|
||||
if (vi->flags & FTF_BOOTCPUID)
|
||||
fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys);
|
||||
if (vi->flags & FTF_STRTABSIZE)
|
||||
fdt->size_dt_strings = cpu_to_fdt32(strsize);
|
||||
if (vi->flags & FTF_STRUCTSIZE)
|
||||
fdt->size_dt_struct = cpu_to_fdt32(dtsize);
|
||||
}
|
||||
|
||||
void dt_to_blob(FILE *f, struct boot_info *bi, int version)
|
||||
{
|
||||
struct version_info *vi = NULL;
|
||||
int i;
|
||||
struct data blob = empty_data;
|
||||
struct data reservebuf = empty_data;
|
||||
struct data dtbuf = empty_data;
|
||||
struct data strbuf = empty_data;
|
||||
struct fdt_header fdt;
|
||||
int padlen = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(version_table); i++) {
|
||||
if (version_table[i].version == version)
|
||||
vi = &version_table[i];
|
||||
}
|
||||
if (!vi)
|
||||
die("Unknown device tree blob version %d\n", version);
|
||||
|
||||
flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi);
|
||||
bin_emit_cell(&dtbuf, FDT_END);
|
||||
|
||||
reservebuf = flatten_reserve_list(bi->reservelist, vi);
|
||||
|
||||
/* Make header */
|
||||
make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
|
||||
bi->boot_cpuid_phys);
|
||||
|
||||
/*
|
||||
* If the user asked for more space than is used, adjust the totalsize.
|
||||
*/
|
||||
if (minsize > 0) {
|
||||
padlen = minsize - fdt32_to_cpu(fdt.totalsize);
|
||||
if ((padlen < 0) && (quiet < 1))
|
||||
fprintf(stderr,
|
||||
"Warning: blob size %d >= minimum size %d\n",
|
||||
fdt32_to_cpu(fdt.totalsize), minsize);
|
||||
}
|
||||
|
||||
if (padsize > 0)
|
||||
padlen = padsize;
|
||||
|
||||
if (padlen > 0) {
|
||||
int tsize = fdt32_to_cpu(fdt.totalsize);
|
||||
tsize += padlen;
|
||||
fdt.totalsize = cpu_to_fdt32(tsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assemble the blob: start with the header, add with alignment
|
||||
* the reserve buffer, add the reserve map terminating zeroes,
|
||||
* the device tree itself, and finally the strings.
|
||||
*/
|
||||
blob = data_append_data(blob, &fdt, vi->hdr_size);
|
||||
blob = data_append_align(blob, 8);
|
||||
blob = data_merge(blob, reservebuf);
|
||||
blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry));
|
||||
blob = data_merge(blob, dtbuf);
|
||||
blob = data_merge(blob, strbuf);
|
||||
|
||||
/*
|
||||
* If the user asked for more space than is used, pad out the blob.
|
||||
*/
|
||||
if (padlen > 0)
|
||||
blob = data_append_zeroes(blob, padlen);
|
||||
|
||||
if (fwrite(blob.val, blob.len, 1, f) != 1) {
|
||||
if (ferror(f))
|
||||
die("Error writing device tree blob: %s\n",
|
||||
strerror(errno));
|
||||
else
|
||||
die("Short write on device tree blob\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* data_merge() frees the right-hand element so only the blob
|
||||
* remains to be freed.
|
||||
*/
|
||||
data_free(blob);
|
||||
}
|
||||
|
||||
static void dump_stringtable_asm(FILE *f, struct data strbuf)
|
||||
{
|
||||
const char *p;
|
||||
int len;
|
||||
|
||||
p = strbuf.val;
|
||||
|
||||
while (p < (strbuf.val + strbuf.len)) {
|
||||
len = strlen(p);
|
||||
fprintf(f, "\t.string \"%s\"\n", p);
|
||||
p += len+1;
|
||||
}
|
||||
}
|
||||
|
||||
void dt_to_asm(FILE *f, struct boot_info *bi, int version)
|
||||
{
|
||||
struct version_info *vi = NULL;
|
||||
int i;
|
||||
struct data strbuf = empty_data;
|
||||
struct reserve_info *re;
|
||||
const char *symprefix = "dt";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(version_table); i++) {
|
||||
if (version_table[i].version == version)
|
||||
vi = &version_table[i];
|
||||
}
|
||||
if (!vi)
|
||||
die("Unknown device tree blob version %d\n", version);
|
||||
|
||||
fprintf(f, "/* autogenerated by dtc, do not edit */\n\n");
|
||||
|
||||
emit_label(f, symprefix, "blob_start");
|
||||
emit_label(f, symprefix, "header");
|
||||
fprintf(f, "\t/* magic */\n");
|
||||
asm_emit_cell(f, FDT_MAGIC);
|
||||
fprintf(f, "\t/* totalsize */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t/* off_dt_struct */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t/* off_dt_strings */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t/* off_mem_rsvmap */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start",
|
||||
symprefix, symprefix);
|
||||
fprintf(f, "\t/* version */\n");
|
||||
asm_emit_cell(f, vi->version);
|
||||
fprintf(f, "\t/* last_comp_version */\n");
|
||||
asm_emit_cell(f, vi->last_comp_version);
|
||||
|
||||
if (vi->flags & FTF_BOOTCPUID) {
|
||||
fprintf(f, "\t/* boot_cpuid_phys */\n");
|
||||
asm_emit_cell(f, bi->boot_cpuid_phys);
|
||||
}
|
||||
|
||||
if (vi->flags & FTF_STRTABSIZE) {
|
||||
fprintf(f, "\t/* size_dt_strings */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start",
|
||||
symprefix, symprefix);
|
||||
}
|
||||
|
||||
if (vi->flags & FTF_STRUCTSIZE) {
|
||||
fprintf(f, "\t/* size_dt_struct */\n");
|
||||
ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start",
|
||||
symprefix, symprefix);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reserve map entries.
|
||||
* Align the reserve map to a doubleword boundary.
|
||||
* Each entry is an (address, size) pair of u64 values.
|
||||
* Always supply a zero-sized temination entry.
|
||||
*/
|
||||
asm_emit_align(f, 8);
|
||||
emit_label(f, symprefix, "reserve_map");
|
||||
|
||||
fprintf(f, "/* Memory reserve map from source file */\n");
|
||||
|
||||
/*
|
||||
* Use .long on high and low halfs of u64s to avoid .quad
|
||||
* as it appears .quad isn't available in some assemblers.
|
||||
*/
|
||||
for (re = bi->reservelist; re; re = re->next) {
|
||||
struct label *l;
|
||||
|
||||
for_each_label(re->labels, l) {
|
||||
fprintf(f, "\t.globl\t%s\n", l->label);
|
||||
fprintf(f, "%s:\n", l->label);
|
||||
}
|
||||
ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32));
|
||||
ASM_EMIT_BELONG(f, "0x%08x",
|
||||
(unsigned int)(re->re.address & 0xffffffff));
|
||||
ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32));
|
||||
ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff));
|
||||
}
|
||||
for (i = 0; i < reservenum; i++) {
|
||||
fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
|
||||
}
|
||||
|
||||
fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
|
||||
|
||||
emit_label(f, symprefix, "struct_start");
|
||||
flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi);
|
||||
|
||||
fprintf(f, "\t/* FDT_END */\n");
|
||||
asm_emit_cell(f, FDT_END);
|
||||
emit_label(f, symprefix, "struct_end");
|
||||
|
||||
emit_label(f, symprefix, "strings_start");
|
||||
dump_stringtable_asm(f, strbuf);
|
||||
emit_label(f, symprefix, "strings_end");
|
||||
|
||||
emit_label(f, symprefix, "blob_end");
|
||||
|
||||
/*
|
||||
* If the user asked for more space than is used, pad it out.
|
||||
*/
|
||||
if (minsize > 0) {
|
||||
fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n",
|
||||
minsize, symprefix, symprefix);
|
||||
}
|
||||
if (padsize > 0) {
|
||||
fprintf(f, "\t.space\t%d, 0\n", padsize);
|
||||
}
|
||||
emit_label(f, symprefix, "blob_abs_end");
|
||||
|
||||
data_free(strbuf);
|
||||
}
|
||||
|
||||
struct inbuf {
|
||||
char *base, *limit, *ptr;
|
||||
};
|
||||
|
||||
static void inbuf_init(struct inbuf *inb, void *base, void *limit)
|
||||
{
|
||||
inb->base = base;
|
||||
inb->limit = limit;
|
||||
inb->ptr = inb->base;
|
||||
}
|
||||
|
||||
static void flat_read_chunk(struct inbuf *inb, void *p, int len)
|
||||
{
|
||||
if ((inb->ptr + len) > inb->limit)
|
||||
die("Premature end of data parsing flat device tree\n");
|
||||
|
||||
memcpy(p, inb->ptr, len);
|
||||
|
||||
inb->ptr += len;
|
||||
}
|
||||
|
||||
static uint32_t flat_read_word(struct inbuf *inb)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
assert(((inb->ptr - inb->base) % sizeof(val)) == 0);
|
||||
|
||||
flat_read_chunk(inb, &val, sizeof(val));
|
||||
|
||||
return fdt32_to_cpu(val);
|
||||
}
|
||||
|
||||
static void flat_realign(struct inbuf *inb, int align)
|
||||
{
|
||||
int off = inb->ptr - inb->base;
|
||||
|
||||
inb->ptr = inb->base + ALIGN(off, align);
|
||||
if (inb->ptr > inb->limit)
|
||||
die("Premature end of data parsing flat device tree\n");
|
||||
}
|
||||
|
||||
static char *flat_read_string(struct inbuf *inb)
|
||||
{
|
||||
int len = 0;
|
||||
const char *p = inb->ptr;
|
||||
char *str;
|
||||
|
||||
do {
|
||||
if (p >= inb->limit)
|
||||
die("Premature end of data parsing flat device tree\n");
|
||||
len++;
|
||||
} while ((*p++) != '\0');
|
||||
|
||||
str = xstrdup(inb->ptr);
|
||||
|
||||
inb->ptr += len;
|
||||
|
||||
flat_realign(inb, sizeof(uint32_t));
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static struct data flat_read_data(struct inbuf *inb, int len)
|
||||
{
|
||||
struct data d = empty_data;
|
||||
|
||||
if (len == 0)
|
||||
return empty_data;
|
||||
|
||||
d = data_grow_for(d, len);
|
||||
d.len = len;
|
||||
|
||||
flat_read_chunk(inb, d.val, len);
|
||||
|
||||
flat_realign(inb, sizeof(uint32_t));
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static char *flat_read_stringtable(struct inbuf *inb, int offset)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = inb->base + offset;
|
||||
while (1) {
|
||||
if (p >= inb->limit || p < inb->base)
|
||||
die("String offset %d overruns string table\n",
|
||||
offset);
|
||||
|
||||
if (*p == '\0')
|
||||
break;
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
return xstrdup(inb->base + offset);
|
||||
}
|
||||
|
||||
static struct property *flat_read_property(struct inbuf *dtbuf,
|
||||
struct inbuf *strbuf, int flags)
|
||||
{
|
||||
uint32_t proplen, stroff;
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
proplen = flat_read_word(dtbuf);
|
||||
stroff = flat_read_word(dtbuf);
|
||||
|
||||
name = flat_read_stringtable(strbuf, stroff);
|
||||
|
||||
if ((flags & FTF_VARALIGN) && (proplen >= 8))
|
||||
flat_realign(dtbuf, 8);
|
||||
|
||||
val = flat_read_data(dtbuf, proplen);
|
||||
|
||||
return build_property(name, val);
|
||||
}
|
||||
|
||||
|
||||
static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
|
||||
{
|
||||
struct reserve_info *reservelist = NULL;
|
||||
struct reserve_info *new;
|
||||
struct fdt_reserve_entry re;
|
||||
|
||||
/*
|
||||
* Each entry is a pair of u64 (addr, size) values for 4 cell_t's.
|
||||
* List terminates at an entry with size equal to zero.
|
||||
*
|
||||
* First pass, count entries.
|
||||
*/
|
||||
while (1) {
|
||||
flat_read_chunk(inb, &re, sizeof(re));
|
||||
re.address = fdt64_to_cpu(re.address);
|
||||
re.size = fdt64_to_cpu(re.size);
|
||||
if (re.size == 0)
|
||||
break;
|
||||
|
||||
new = build_reserve_entry(re.address, re.size);
|
||||
reservelist = add_reserve_entry(reservelist, new);
|
||||
}
|
||||
|
||||
return reservelist;
|
||||
}
|
||||
|
||||
|
||||
static char *nodename_from_path(const char *ppath, const char *cpath)
|
||||
{
|
||||
int plen;
|
||||
|
||||
plen = strlen(ppath);
|
||||
|
||||
if (!strneq(ppath, cpath, plen))
|
||||
die("Path \"%s\" is not valid as a child of \"%s\"\n",
|
||||
cpath, ppath);
|
||||
|
||||
/* root node is a special case */
|
||||
if (!streq(ppath, "/"))
|
||||
plen++;
|
||||
|
||||
return xstrdup(cpath + plen);
|
||||
}
|
||||
|
||||
static struct node *unflatten_tree(struct inbuf *dtbuf,
|
||||
struct inbuf *strbuf,
|
||||
const char *parent_flatname, int flags)
|
||||
{
|
||||
struct node *node;
|
||||
char *flatname;
|
||||
uint32_t val;
|
||||
|
||||
node = build_node(NULL, NULL);
|
||||
|
||||
flatname = flat_read_string(dtbuf);
|
||||
|
||||
if (flags & FTF_FULLPATH)
|
||||
node->name = nodename_from_path(parent_flatname, flatname);
|
||||
else
|
||||
node->name = flatname;
|
||||
|
||||
do {
|
||||
struct property *prop;
|
||||
struct node *child;
|
||||
|
||||
val = flat_read_word(dtbuf);
|
||||
switch (val) {
|
||||
case FDT_PROP:
|
||||
if (node->children)
|
||||
fprintf(stderr, "Warning: Flat tree input has "
|
||||
"subnodes preceding a property.\n");
|
||||
prop = flat_read_property(dtbuf, strbuf, flags);
|
||||
add_property(node, prop);
|
||||
break;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
child = unflatten_tree(dtbuf,strbuf, flatname, flags);
|
||||
add_child(node, child);
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
die("Premature FDT_END in device tree blob\n");
|
||||
break;
|
||||
|
||||
case FDT_NOP:
|
||||
if (!(flags & FTF_NOPS))
|
||||
fprintf(stderr, "Warning: NOP tag found in flat tree"
|
||||
" version <16\n");
|
||||
|
||||
/* Ignore */
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Invalid opcode word %08x in device tree blob\n",
|
||||
val);
|
||||
}
|
||||
} while (val != FDT_END_NODE);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
struct boot_info *dt_from_blob(const char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
|
||||
uint32_t off_dt, off_str, off_mem_rsvmap;
|
||||
int rc;
|
||||
char *blob;
|
||||
struct fdt_header *fdt;
|
||||
char *p;
|
||||
struct inbuf dtbuf, strbuf;
|
||||
struct inbuf memresvbuf;
|
||||
int sizeleft;
|
||||
struct reserve_info *reservelist;
|
||||
struct node *tree;
|
||||
uint32_t val;
|
||||
int flags = 0;
|
||||
|
||||
f = srcfile_relative_open(fname, NULL);
|
||||
|
||||
rc = fread(&magic, sizeof(magic), 1, f);
|
||||
if (ferror(f))
|
||||
die("Error reading DT blob magic number: %s\n",
|
||||
strerror(errno));
|
||||
if (rc < 1) {
|
||||
if (feof(f))
|
||||
die("EOF reading DT blob magic number\n");
|
||||
else
|
||||
die("Mysterious short read reading magic number\n");
|
||||
}
|
||||
|
||||
magic = fdt32_to_cpu(magic);
|
||||
if (magic != FDT_MAGIC)
|
||||
die("Blob has incorrect magic number\n");
|
||||
|
||||
rc = fread(&totalsize, sizeof(totalsize), 1, f);
|
||||
if (ferror(f))
|
||||
die("Error reading DT blob size: %s\n", strerror(errno));
|
||||
if (rc < 1) {
|
||||
if (feof(f))
|
||||
die("EOF reading DT blob size\n");
|
||||
else
|
||||
die("Mysterious short read reading blob size\n");
|
||||
}
|
||||
|
||||
totalsize = fdt32_to_cpu(totalsize);
|
||||
if (totalsize < FDT_V1_SIZE)
|
||||
die("DT blob size (%d) is too small\n", totalsize);
|
||||
|
||||
blob = xmalloc(totalsize);
|
||||
|
||||
fdt = (struct fdt_header *)blob;
|
||||
fdt->magic = cpu_to_fdt32(magic);
|
||||
fdt->totalsize = cpu_to_fdt32(totalsize);
|
||||
|
||||
sizeleft = totalsize - sizeof(magic) - sizeof(totalsize);
|
||||
p = blob + sizeof(magic) + sizeof(totalsize);
|
||||
|
||||
while (sizeleft) {
|
||||
if (feof(f))
|
||||
die("EOF before reading %d bytes of DT blob\n",
|
||||
totalsize);
|
||||
|
||||
rc = fread(p, 1, sizeleft, f);
|
||||
if (ferror(f))
|
||||
die("Error reading DT blob: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
sizeleft -= rc;
|
||||
p += rc;
|
||||
}
|
||||
|
||||
off_dt = fdt32_to_cpu(fdt->off_dt_struct);
|
||||
off_str = fdt32_to_cpu(fdt->off_dt_strings);
|
||||
off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap);
|
||||
version = fdt32_to_cpu(fdt->version);
|
||||
boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys);
|
||||
|
||||
if (off_mem_rsvmap >= totalsize)
|
||||
die("Mem Reserve structure offset exceeds total size\n");
|
||||
|
||||
if (off_dt >= totalsize)
|
||||
die("DT structure offset exceeds total size\n");
|
||||
|
||||
if (off_str > totalsize)
|
||||
die("String table offset exceeds total size\n");
|
||||
|
||||
if (version >= 3) {
|
||||
uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings);
|
||||
if (off_str+size_str > totalsize)
|
||||
die("String table extends past total size\n");
|
||||
inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str);
|
||||
} else {
|
||||
inbuf_init(&strbuf, blob + off_str, blob + totalsize);
|
||||
}
|
||||
|
||||
if (version >= 17) {
|
||||
size_dt = fdt32_to_cpu(fdt->size_dt_struct);
|
||||
if (off_dt+size_dt > totalsize)
|
||||
die("Structure block extends past total size\n");
|
||||
}
|
||||
|
||||
if (version < 16) {
|
||||
flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN;
|
||||
} else {
|
||||
flags |= FTF_NOPS;
|
||||
}
|
||||
|
||||
inbuf_init(&memresvbuf,
|
||||
blob + off_mem_rsvmap, blob + totalsize);
|
||||
inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
|
||||
|
||||
reservelist = flat_read_mem_reserve(&memresvbuf);
|
||||
|
||||
val = flat_read_word(&dtbuf);
|
||||
|
||||
if (val != FDT_BEGIN_NODE)
|
||||
die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
|
||||
|
||||
tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
|
||||
|
||||
val = flat_read_word(&dtbuf);
|
||||
if (val != FDT_END)
|
||||
die("Device tree blob doesn't end with FDT_END\n");
|
||||
|
||||
free(blob);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return build_boot_info(reservelist, tree, boot_cpuid_phys);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static struct node *read_fstree(const char *dirname)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
struct stat st;
|
||||
struct node *tree;
|
||||
|
||||
d = opendir(dirname);
|
||||
if (!d)
|
||||
die("Couldn't opendir() \"%s\": %s\n", dirname, strerror(errno));
|
||||
|
||||
tree = build_node(NULL, NULL);
|
||||
|
||||
while ((de = readdir(d)) != NULL) {
|
||||
char *tmpnam;
|
||||
|
||||
if (streq(de->d_name, ".")
|
||||
|| streq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
tmpnam = join_path(dirname, de->d_name);
|
||||
|
||||
if (lstat(tmpnam, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpnam, strerror(errno));
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
struct property *prop;
|
||||
FILE *pfile;
|
||||
|
||||
pfile = fopen(tmpnam, "r");
|
||||
if (! pfile) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Cannot open %s: %s\n",
|
||||
tmpnam, strerror(errno));
|
||||
} else {
|
||||
prop = build_property(xstrdup(de->d_name),
|
||||
data_copy_file(pfile,
|
||||
st.st_size));
|
||||
add_property(tree, prop);
|
||||
fclose(pfile);
|
||||
}
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
struct node *newchild;
|
||||
|
||||
newchild = read_fstree(tmpnam);
|
||||
newchild = name_node(newchild, xstrdup(de->d_name));
|
||||
add_child(tree, newchild);
|
||||
}
|
||||
|
||||
free(tmpnam);
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return tree;
|
||||
}
|
||||
|
||||
struct boot_info *dt_from_fs(const char *dirname)
|
||||
{
|
||||
struct node *tree;
|
||||
|
||||
tree = read_fstree(dirname);
|
||||
tree = name_node(tree, "");
|
||||
|
||||
return build_boot_info(NULL, tree, guess_boot_cpuid(tree));
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Makefile.libfdt
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
|
||||
LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
|
||||
LIBFDT_VERSION = version.lds
|
||||
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c
|
||||
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_check_header(const void *fdt)
|
||||
{
|
||||
if (fdt_magic(fdt) == FDT_MAGIC) {
|
||||
/* Complete tree */
|
||||
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
|
||||
/* Unfinished sequential-write blob */
|
||||
if (fdt_size_dt_struct(fdt) == 0)
|
||||
return -FDT_ERR_BADSTATE;
|
||||
} else {
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (fdt_version(fdt) >= 0x11)
|
||||
if (((offset + len) < offset)
|
||||
|| ((offset + len) > fdt_size_dt_struct(fdt)))
|
||||
return NULL;
|
||||
|
||||
p = _fdt_offset_ptr(fdt, offset);
|
||||
|
||||
if (p + len < p)
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||
{
|
||||
const uint32_t *tagp, *lenp;
|
||||
uint32_t tag;
|
||||
int offset = startoffset;
|
||||
const char *p;
|
||||
|
||||
*nextoffset = -FDT_ERR_TRUNCATED;
|
||||
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
|
||||
if (!tagp)
|
||||
return FDT_END; /* premature end */
|
||||
tag = fdt32_to_cpu(*tagp);
|
||||
offset += FDT_TAGSIZE;
|
||||
|
||||
*nextoffset = -FDT_ERR_BADSTRUCTURE;
|
||||
switch (tag) {
|
||||
case FDT_BEGIN_NODE:
|
||||
/* skip name */
|
||||
do {
|
||||
p = fdt_offset_ptr(fdt, offset++, 1);
|
||||
} while (p && (*p != '\0'));
|
||||
if (!p)
|
||||
return FDT_END; /* premature end */
|
||||
break;
|
||||
|
||||
case FDT_PROP:
|
||||
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
|
||||
if (!lenp)
|
||||
return FDT_END; /* premature end */
|
||||
/* skip-name offset, length and value */
|
||||
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
|
||||
+ fdt32_to_cpu(*lenp);
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
case FDT_END_NODE:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
default:
|
||||
return FDT_END;
|
||||
}
|
||||
|
||||
if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
|
||||
return FDT_END; /* premature end */
|
||||
|
||||
*nextoffset = FDT_TAGALIGN(offset);
|
||||
return tag;
|
||||
}
|
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int _fdt_check_prop_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset < 0) || (offset % FDT_TAGSIZE)
|
||||
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_next_node(const void *fdt, int offset, int *depth)
|
||||
{
|
||||
int nextoffset = 0;
|
||||
uint32_t tag;
|
||||
|
||||
if (offset >= 0)
|
||||
if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
|
||||
return nextoffset;
|
||||
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
switch (tag) {
|
||||
case FDT_PROP:
|
||||
case FDT_NOP:
|
||||
break;
|
||||
|
||||
case FDT_BEGIN_NODE:
|
||||
if (depth)
|
||||
(*depth)++;
|
||||
break;
|
||||
|
||||
case FDT_END_NODE:
|
||||
if (depth && ((--(*depth)) < 0))
|
||||
return nextoffset;
|
||||
break;
|
||||
|
||||
case FDT_END:
|
||||
if ((nextoffset >= 0)
|
||||
|| ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
else
|
||||
return nextoffset;
|
||||
}
|
||||
} while (tag != FDT_BEGIN_NODE);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
const char *last = strtab + tabsize - len;
|
||||
const char *p;
|
||||
|
||||
for (p = strtab; p <= last; p++)
|
||||
if (memcmp(p, s, len) == 0)
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_move(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_totalsize(fdt) > bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memmove(buf, fdt, fdt_totalsize(fdt));
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef _FDT_H
|
||||
#define _FDT_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct fdt_header {
|
||||
uint32_t magic; /* magic word FDT_MAGIC */
|
||||
uint32_t totalsize; /* total size of DT block */
|
||||
uint32_t off_dt_struct; /* offset to structure */
|
||||
uint32_t off_dt_strings; /* offset to strings */
|
||||
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
uint32_t version; /* format version */
|
||||
uint32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
uint32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
uint32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
struct fdt_reserve_entry {
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct fdt_node_header {
|
||||
uint32_t tag;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct fdt_property {
|
||||
uint32_t tag;
|
||||
uint32_t len;
|
||||
uint32_t nameoff;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY */
|
||||
|
||||
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
|
||||
#define FDT_TAGSIZE sizeof(uint32_t)
|
||||
|
||||
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
|
||||
#define FDT_END_NODE 0x2 /* End node */
|
||||
#define FDT_PROP 0x3 /* Property: name off,
|
||||
size, content */
|
||||
#define FDT_NOP 0x4 /* nop */
|
||||
#define FDT_END 0x9
|
||||
|
||||
#define FDT_V1_SIZE (7*sizeof(uint32_t))
|
||||
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
|
||||
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
|
||||
#define FDT_V16_SIZE FDT_V3_SIZE
|
||||
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
|
||||
|
||||
#endif /* _FDT_H */
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2012 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_create_empty_tree(void *buf, int bufsize)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = fdt_create(buf, bufsize);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_finish_reservemap(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_begin_node(buf, "");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_end_node(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_finish(buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return fdt_open_into(buf, buf, bufsize);
|
||||
}
|
||||
|
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
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);
|
||||
|
||||
if (! p)
|
||||
/* short match */
|
||||
return 0;
|
||||
|
||||
if (memcmp(p, s, len) != 0)
|
||||
return 0;
|
||||
|
||||
if (p[len] == '\0')
|
||||
return 1;
|
||||
else if (!memchr(s, '@', len) && (p[len] == '@'))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *fdt_string(const void *fdt, int stroffset)
|
||||
{
|
||||
return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
|
||||
}
|
||||
|
||||
static int _fdt_string_eq(const void *fdt, int stroffset,
|
||||
const char *s, int len)
|
||||
{
|
||||
const char *p = fdt_string(fdt, stroffset);
|
||||
|
||||
return (strlen(p) == len) && (memcmp(p, s, len) == 0);
|
||||
}
|
||||
|
||||
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
|
||||
*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_num_mem_rsv(const void *fdt)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int _nextprop(const void *fdt, int offset)
|
||||
{
|
||||
uint32_t tag;
|
||||
int nextoffset;
|
||||
|
||||
do {
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
|
||||
switch (tag) {
|
||||
case FDT_END:
|
||||
if (nextoffset >= 0)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
else
|
||||
return nextoffset;
|
||||
|
||||
case FDT_PROP:
|
||||
return offset;
|
||||
}
|
||||
offset = nextoffset;
|
||||
} while (tag == FDT_NOP);
|
||||
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
int fdt_subnode_offset_namelen(const void *fdt, int offset,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int depth;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
for (depth = 0;
|
||||
(offset >= 0) && (depth >= 0);
|
||||
offset = fdt_next_node(fdt, offset, &depth))
|
||||
if ((depth == 1)
|
||||
&& _fdt_nodename_eq(fdt, offset, name, namelen))
|
||||
return offset;
|
||||
|
||||
if (depth < 0)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
return offset; /* error */
|
||||
}
|
||||
|
||||
int fdt_subnode_offset(const void *fdt, int parentoffset,
|
||||
const char *name)
|
||||
{
|
||||
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_path_offset(const void *fdt, const char *path)
|
||||
{
|
||||
const char *end = path + strlen(path);
|
||||
const char *p = path;
|
||||
int offset = 0;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* see if we have an alias */
|
||||
if (*path != '/') {
|
||||
const char *q = strchr(path, '/');
|
||||
|
||||
if (!q)
|
||||
q = end;
|
||||
|
||||
p = fdt_get_alias_namelen(fdt, p, q - p);
|
||||
if (!p)
|
||||
return -FDT_ERR_BADPATH;
|
||||
offset = fdt_path_offset(fdt, p);
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
const char *q;
|
||||
|
||||
while (*p == '/')
|
||||
p++;
|
||||
if (! *p)
|
||||
return offset;
|
||||
q = strchr(p, '/');
|
||||
if (! q)
|
||||
q = end;
|
||||
|
||||
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|
||||
{
|
||||
const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
|
||||
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);
|
||||
|
||||
return nh->name;
|
||||
|
||||
fail:
|
||||
if (len)
|
||||
*len = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_first_property_offset(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
|
||||
return offset;
|
||||
|
||||
return _nextprop(fdt, offset);
|
||||
}
|
||||
|
||||
int fdt_next_property_offset(const void *fdt, int offset)
|
||||
{
|
||||
if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
|
||||
return offset;
|
||||
|
||||
return _nextprop(fdt, offset);
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
||||
int offset,
|
||||
int *lenp)
|
||||
{
|
||||
int err;
|
||||
const struct fdt_property *prop;
|
||||
|
||||
if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
|
||||
if (lenp)
|
||||
*lenp = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prop = _fdt_offset_ptr(fdt, offset);
|
||||
|
||||
if (lenp)
|
||||
*lenp = fdt32_to_cpu(prop->len);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
||||
int offset,
|
||||
const char *name,
|
||||
int namelen, int *lenp)
|
||||
{
|
||||
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))) {
|
||||
offset = -FDT_ERR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
|
||||
name, namelen))
|
||||
return prop;
|
||||
}
|
||||
|
||||
if (lenp)
|
||||
*lenp = offset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct fdt_property *fdt_get_property(const void *fdt,
|
||||
int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
return fdt_get_property_namelen(fdt, nodeoffset, name,
|
||||
strlen(name), lenp);
|
||||
}
|
||||
|
||||
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
||||
const char *name, int namelen, int *lenp)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
|
||||
if (! prop)
|
||||
return NULL;
|
||||
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
const void *fdt_getprop_by_offset(const void *fdt, int offset,
|
||||
const char **namep, int *lenp)
|
||||
{
|
||||
const struct fdt_property *prop;
|
||||
|
||||
prop = fdt_get_property_by_offset(fdt, offset, lenp);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
if (namep)
|
||||
*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
|
||||
return prop->data;
|
||||
}
|
||||
|
||||
const void *fdt_getprop(const void *fdt, int nodeoffset,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
|
||||
}
|
||||
|
||||
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
|
||||
{
|
||||
const uint32_t *php;
|
||||
int len;
|
||||
|
||||
/* FIXME: This is a bit sub-optimal, since we potentially scan
|
||||
* over all the properties twice. */
|
||||
php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
|
||||
if (!php || (len != sizeof(*php))) {
|
||||
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
|
||||
if (!php || (len != sizeof(*php)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fdt32_to_cpu(*php);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias_namelen(const void *fdt,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
int aliasoffset;
|
||||
|
||||
aliasoffset = fdt_path_offset(fdt, "/aliases");
|
||||
if (aliasoffset < 0)
|
||||
return NULL;
|
||||
|
||||
return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
|
||||
}
|
||||
|
||||
const char *fdt_get_alias(const void *fdt, const char *name)
|
||||
{
|
||||
return fdt_get_alias_namelen(fdt, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
|
||||
{
|
||||
int pdepth = 0, p = 0;
|
||||
int offset, depth, namelen;
|
||||
const char *name;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (buflen < 2)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
for (offset = 0, depth = 0;
|
||||
(offset >= 0) && (offset <= nodeoffset);
|
||||
offset = fdt_next_node(fdt, offset, &depth)) {
|
||||
while (pdepth > depth) {
|
||||
do {
|
||||
p--;
|
||||
} while (buf[p-1] != '/');
|
||||
pdepth--;
|
||||
}
|
||||
|
||||
if (pdepth >= depth) {
|
||||
name = fdt_get_name(fdt, offset, &namelen);
|
||||
if (!name)
|
||||
return namelen;
|
||||
if ((p + namelen + 1) <= buflen) {
|
||||
memcpy(buf + p, name, namelen);
|
||||
p += namelen;
|
||||
buf[p++] = '/';
|
||||
pdepth++;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset == nodeoffset) {
|
||||
if (pdepth < (depth + 1))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
if (p > 1) /* special case so that root path is "/", not "" */
|
||||
p--;
|
||||
buf[p] = '\0';
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
else if (offset == -FDT_ERR_BADOFFSET)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
|
||||
int supernodedepth, int *nodedepth)
|
||||
{
|
||||
int offset, depth;
|
||||
int supernodeoffset = -FDT_ERR_INTERNAL;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (supernodedepth < 0)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
for (offset = 0, depth = 0;
|
||||
(offset >= 0) && (offset <= nodeoffset);
|
||||
offset = fdt_next_node(fdt, offset, &depth)) {
|
||||
if (depth == supernodedepth)
|
||||
supernodeoffset = offset;
|
||||
|
||||
if (offset == nodeoffset) {
|
||||
if (nodedepth)
|
||||
*nodedepth = depth;
|
||||
|
||||
if (supernodedepth > depth)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
else
|
||||
return supernodeoffset;
|
||||
}
|
||||
}
|
||||
|
||||
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
else if (offset == -FDT_ERR_BADOFFSET)
|
||||
return -FDT_ERR_BADSTRUCTURE;
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_node_depth(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int nodedepth;
|
||||
int err;
|
||||
|
||||
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
|
||||
if (err)
|
||||
return (err < 0) ? err : -FDT_ERR_INTERNAL;
|
||||
return nodedepth;
|
||||
}
|
||||
|
||||
int fdt_parent_offset(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int nodedepth = fdt_node_depth(fdt, nodeoffset);
|
||||
|
||||
if (nodedepth < 0)
|
||||
return nodedepth;
|
||||
return fdt_supernode_atdepth_offset(fdt, nodeoffset,
|
||||
nodedepth - 1, NULL);
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
|
||||
const char *propname,
|
||||
const void *propval, int proplen)
|
||||
{
|
||||
int offset;
|
||||
const void *val;
|
||||
int len;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
||||
* property of a node in fdt_getprop(), then if that didn't
|
||||
* find what we want, we scan over them again making our way
|
||||
* to the next node. Still it's the easiest to implement
|
||||
* approach; performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, startoffset, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
val = fdt_getprop(fdt, offset, propname, &len);
|
||||
if (val && (len == proplen)
|
||||
&& (memcmp(val, propval, len) == 0))
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if ((phandle == 0) || (phandle == -1))
|
||||
return -FDT_ERR_BADPHANDLE;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we
|
||||
* potentially scan each property of a node in
|
||||
* fdt_get_phandle(), then if that didn't find what
|
||||
* we want, we scan over them again making our way to the next
|
||||
* node. Still it's the easiest to implement approach;
|
||||
* performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, -1, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
if (fdt_get_phandle(fdt, offset) == phandle)
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
||||
|
||||
static int _fdt_stringlist_contains(const char *strlist, int listlen,
|
||||
const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
const char *p;
|
||||
|
||||
while (listlen >= len) {
|
||||
if (memcmp(str, strlist, len+1) == 0)
|
||||
return 1;
|
||||
p = memchr(strlist, '\0', listlen);
|
||||
if (!p)
|
||||
return 0; /* malformed strlist.. */
|
||||
listlen -= (p-strlist) + 1;
|
||||
strlist = p + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
||||
const char *compatible)
|
||||
{
|
||||
const void *prop;
|
||||
int len;
|
||||
|
||||
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
|
||||
if (!prop)
|
||||
return len;
|
||||
if (_fdt_stringlist_contains(prop, len, compatible))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
||||
const char *compatible)
|
||||
{
|
||||
int offset, err;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* FIXME: The algorithm here is pretty horrible: we scan each
|
||||
* property of a node in fdt_node_check_compatible(), then if
|
||||
* that didn't find what we want, we scan over them again
|
||||
* making our way to the next node. Still it's the easiest to
|
||||
* implement approach; performance can come later. */
|
||||
for (offset = fdt_next_node(fdt, startoffset, NULL);
|
||||
offset >= 0;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
err = fdt_node_check_compatible(fdt, offset, compatible);
|
||||
if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
|
||||
return err;
|
||||
else if (err == 0)
|
||||
return offset;
|
||||
}
|
||||
|
||||
return offset; /* error from fdt_next_node() */
|
||||
}
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int _fdt_blocks_misordered(const void *fdt,
|
||||
int mem_rsv_size, int struct_size)
|
||||
{
|
||||
return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
|
||||
|| (fdt_off_dt_struct(fdt) <
|
||||
(fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
|
||||
|| (fdt_off_dt_strings(fdt) <
|
||||
(fdt_off_dt_struct(fdt) + struct_size))
|
||||
|| (fdt_totalsize(fdt) <
|
||||
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
|
||||
}
|
||||
|
||||
static int _fdt_rw_check_header(void *fdt)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_version(fdt) < 17)
|
||||
return -FDT_ERR_BADVERSION;
|
||||
if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
|
||||
fdt_size_dt_struct(fdt)))
|
||||
return -FDT_ERR_BADLAYOUT;
|
||||
if (fdt_version(fdt) > 17)
|
||||
fdt_set_version(fdt, 17);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FDT_RW_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = _fdt_rw_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
}
|
||||
|
||||
static inline int _fdt_data_size(void *fdt)
|
||||
{
|
||||
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
||||
}
|
||||
|
||||
static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
|
||||
{
|
||||
char *p = splicepoint;
|
||||
char *end = (char *)fdt + _fdt_data_size(fdt);
|
||||
|
||||
if (((p + oldlen) < p) || ((p + oldlen) > end))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
memmove(p + newlen, p + oldlen, end - p - oldlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
|
||||
int oldn, int newn)
|
||||
{
|
||||
int delta = (newn - oldn) * sizeof(*p);
|
||||
int err;
|
||||
err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
|
||||
if (err)
|
||||
return err;
|
||||
fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
|
||||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_struct(void *fdt, void *p,
|
||||
int oldlen, int newlen)
|
||||
{
|
||||
int delta = newlen - oldlen;
|
||||
int err;
|
||||
|
||||
if ((err = _fdt_splice(fdt, p, oldlen, newlen)))
|
||||
return err;
|
||||
|
||||
fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
|
||||
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_splice_string(void *fdt, int newlen)
|
||||
{
|
||||
void *p = (char *)fdt
|
||||
+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
||||
int err;
|
||||
|
||||
if ((err = _fdt_splice(fdt, p, 0, newlen)))
|
||||
return err;
|
||||
|
||||
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_find_add_string(void *fdt, const char *s)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
|
||||
const char *p;
|
||||
char *new;
|
||||
int len = strlen(s) + 1;
|
||||
int err;
|
||||
|
||||
p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
|
||||
if (p)
|
||||
/* found it */
|
||||
return (p - strtab);
|
||||
|
||||
new = strtab + fdt_size_dt_strings(fdt);
|
||||
err = _fdt_splice_string(fdt, len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(new, s, len);
|
||||
return (new - strtab);
|
||||
}
|
||||
|
||||
int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
|
||||
{
|
||||
struct fdt_reserve_entry *re;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
|
||||
err = _fdt_splice_mem_rsv(fdt, re, 0, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
re->address = cpu_to_fdt64(address);
|
||||
re->size = cpu_to_fdt64(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_del_mem_rsv(void *fdt, int n)
|
||||
{
|
||||
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
if (n >= fdt_num_mem_rsv(fdt))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
err = _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
|
||||
int len, struct fdt_property **prop)
|
||||
{
|
||||
int oldlen;
|
||||
int err;
|
||||
|
||||
*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
|
||||
if (! (*prop))
|
||||
return oldlen;
|
||||
|
||||
if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
|
||||
FDT_TAGALIGN(len))))
|
||||
return err;
|
||||
|
||||
(*prop)->len = cpu_to_fdt32(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
|
||||
int len, struct fdt_property **prop)
|
||||
{
|
||||
int proplen;
|
||||
int nextoffset;
|
||||
int namestroff;
|
||||
int err;
|
||||
|
||||
if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
|
||||
return nextoffset;
|
||||
|
||||
namestroff = _fdt_find_add_string(fdt, name);
|
||||
if (namestroff < 0)
|
||||
return namestroff;
|
||||
|
||||
*prop = _fdt_offset_ptr_w(fdt, nextoffset);
|
||||
proplen = sizeof(**prop) + FDT_TAGALIGN(len);
|
||||
|
||||
err = _fdt_splice_struct(fdt, *prop, 0, proplen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
(*prop)->tag = cpu_to_fdt32(FDT_PROP);
|
||||
(*prop)->nameoff = cpu_to_fdt32(namestroff);
|
||||
(*prop)->len = cpu_to_fdt32(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_set_name(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
char *namep;
|
||||
int oldlen, newlen;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
|
||||
if (!namep)
|
||||
return oldlen;
|
||||
|
||||
newlen = strlen(name);
|
||||
|
||||
err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
|
||||
FDT_TAGALIGN(newlen+1));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(namep, name, newlen+1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_setprop(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err == -FDT_ERR_NOTFOUND)
|
||||
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(prop->data, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int err, oldlen, newlen;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
|
||||
if (prop) {
|
||||
newlen = len + oldlen;
|
||||
err = _fdt_splice_struct(fdt, prop->data,
|
||||
FDT_TAGALIGN(oldlen),
|
||||
FDT_TAGALIGN(newlen));
|
||||
if (err)
|
||||
return err;
|
||||
prop->len = cpu_to_fdt32(newlen);
|
||||
memcpy(prop->data + oldlen, val, len);
|
||||
} else {
|
||||
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
|
||||
if (err)
|
||||
return err;
|
||||
memcpy(prop->data, val, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_delprop(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int len, proplen;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
|
||||
if (! prop)
|
||||
return len;
|
||||
|
||||
proplen = sizeof(*prop) + FDT_TAGALIGN(len);
|
||||
return _fdt_splice_struct(fdt, prop, proplen, 0);
|
||||
}
|
||||
|
||||
int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
||||
const char *name, int namelen)
|
||||
{
|
||||
struct fdt_node_header *nh;
|
||||
int offset, nextoffset;
|
||||
int nodelen;
|
||||
int err;
|
||||
uint32_t tag;
|
||||
uint32_t *endtag;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
|
||||
if (offset >= 0)
|
||||
return -FDT_ERR_EXISTS;
|
||||
else if (offset != -FDT_ERR_NOTFOUND)
|
||||
return offset;
|
||||
|
||||
/* Try to place the new node after the parent's properties */
|
||||
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
|
||||
do {
|
||||
offset = nextoffset;
|
||||
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
||||
} while ((tag == FDT_PROP) || (tag == FDT_NOP));
|
||||
|
||||
nh = _fdt_offset_ptr_w(fdt, offset);
|
||||
nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
|
||||
|
||||
err = _fdt_splice_struct(fdt, nh, 0, nodelen);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
|
||||
memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
|
||||
memcpy(nh->name, name, namelen);
|
||||
endtag = (uint32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
|
||||
*endtag = cpu_to_fdt32(FDT_END_NODE);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
|
||||
{
|
||||
return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_del_node(void *fdt, int nodeoffset)
|
||||
{
|
||||
int endoffset;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
|
||||
if (endoffset < 0)
|
||||
return endoffset;
|
||||
|
||||
return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
|
||||
endoffset - nodeoffset, 0);
|
||||
}
|
||||
|
||||
static void _fdt_packblocks(const char *old, char *new,
|
||||
int mem_rsv_size, int struct_size)
|
||||
{
|
||||
int mem_rsv_off, struct_off, strings_off;
|
||||
|
||||
mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
|
||||
struct_off = mem_rsv_off + mem_rsv_size;
|
||||
strings_off = struct_off + struct_size;
|
||||
|
||||
memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
|
||||
fdt_set_off_mem_rsvmap(new, mem_rsv_off);
|
||||
|
||||
memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
|
||||
fdt_set_off_dt_struct(new, struct_off);
|
||||
fdt_set_size_dt_struct(new, struct_size);
|
||||
|
||||
memmove(new + strings_off, old + fdt_off_dt_strings(old),
|
||||
fdt_size_dt_strings(old));
|
||||
fdt_set_off_dt_strings(new, strings_off);
|
||||
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
|
||||
}
|
||||
|
||||
int fdt_open_into(const void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
int err;
|
||||
int mem_rsv_size, struct_size;
|
||||
int newsize;
|
||||
const char *fdtstart = fdt;
|
||||
const char *fdtend = fdtstart + fdt_totalsize(fdt);
|
||||
char *tmp;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
||||
* sizeof(struct fdt_reserve_entry);
|
||||
|
||||
if (fdt_version(fdt) >= 17) {
|
||||
struct_size = fdt_size_dt_struct(fdt);
|
||||
} else {
|
||||
struct_size = 0;
|
||||
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
|
||||
;
|
||||
if (struct_size < 0)
|
||||
return struct_size;
|
||||
}
|
||||
|
||||
if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
|
||||
/* no further work necessary */
|
||||
err = fdt_move(fdt, buf, bufsize);
|
||||
if (err)
|
||||
return err;
|
||||
fdt_set_version(buf, 17);
|
||||
fdt_set_size_dt_struct(buf, struct_size);
|
||||
fdt_set_totalsize(buf, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Need to reorder */
|
||||
newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
|
||||
+ struct_size + fdt_size_dt_strings(fdt);
|
||||
|
||||
if (bufsize < newsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
/* First attempt to build converted tree at beginning of buffer */
|
||||
tmp = buf;
|
||||
/* But if that overlaps with the old tree... */
|
||||
if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
|
||||
/* Try right after the old tree instead */
|
||||
tmp = (char *)(uintptr_t)fdtend;
|
||||
if ((tmp + newsize) > ((char *)buf + bufsize))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
}
|
||||
|
||||
_fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);
|
||||
memmove(buf, tmp, newsize);
|
||||
|
||||
fdt_set_magic(buf, FDT_MAGIC);
|
||||
fdt_set_totalsize(buf, bufsize);
|
||||
fdt_set_version(buf, 17);
|
||||
fdt_set_last_comp_version(buf, 16);
|
||||
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_pack(void *fdt)
|
||||
{
|
||||
int mem_rsv_size;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
||||
* sizeof(struct fdt_reserve_entry);
|
||||
_fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
|
||||
fdt_set_totalsize(fdt, _fdt_data_size(fdt));
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
struct fdt_errtabent {
|
||||
const char *str;
|
||||
};
|
||||
|
||||
#define FDT_ERRTABENT(val) \
|
||||
[(val)] = { .str = #val, }
|
||||
|
||||
static struct fdt_errtabent fdt_errtable[] = {
|
||||
FDT_ERRTABENT(FDT_ERR_NOTFOUND),
|
||||
FDT_ERRTABENT(FDT_ERR_EXISTS),
|
||||
FDT_ERRTABENT(FDT_ERR_NOSPACE),
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_BADOFFSET),
|
||||
FDT_ERRTABENT(FDT_ERR_BADPATH),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTATE),
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_TRUNCATED),
|
||||
FDT_ERRTABENT(FDT_ERR_BADMAGIC),
|
||||
FDT_ERRTABENT(FDT_ERR_BADVERSION),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
|
||||
};
|
||||
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
|
||||
|
||||
const char *fdt_strerror(int errval)
|
||||
{
|
||||
if (errval > 0)
|
||||
return "<valid offset/length>";
|
||||
else if (errval == 0)
|
||||
return "<no error>";
|
||||
else if (errval > -FDT_ERRTABSIZE) {
|
||||
const char *s = fdt_errtable[-errval].str;
|
||||
|
||||
if (s)
|
||||
return s;
|
||||
}
|
||||
|
||||
return "<unknown error>";
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
static int _fdt_sw_check_header(void *fdt)
|
||||
{
|
||||
if (fdt_magic(fdt) != FDT_SW_MAGIC)
|
||||
return -FDT_ERR_BADMAGIC;
|
||||
/* FIXME: should check more details about the header state */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FDT_SW_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = _fdt_sw_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
}
|
||||
|
||||
static void *_fdt_grab_space(void *fdt, size_t len)
|
||||
{
|
||||
int offset = fdt_size_dt_struct(fdt);
|
||||
int spaceleft;
|
||||
|
||||
spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
|
||||
- fdt_size_dt_strings(fdt);
|
||||
|
||||
if ((offset + len < offset) || (offset + len > spaceleft))
|
||||
return NULL;
|
||||
|
||||
fdt_set_size_dt_struct(fdt, offset + len);
|
||||
return _fdt_offset_ptr_w(fdt, offset);
|
||||
}
|
||||
|
||||
int fdt_create(void *buf, int bufsize)
|
||||
{
|
||||
void *fdt = buf;
|
||||
|
||||
if (bufsize < sizeof(struct fdt_header))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memset(buf, 0, bufsize);
|
||||
|
||||
fdt_set_magic(fdt, FDT_SW_MAGIC);
|
||||
fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
|
||||
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
|
||||
fdt_set_totalsize(fdt, bufsize);
|
||||
|
||||
fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
|
||||
sizeof(struct fdt_reserve_entry)));
|
||||
fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
|
||||
fdt_set_off_dt_strings(fdt, bufsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
|
||||
{
|
||||
struct fdt_reserve_entry *re;
|
||||
int offset;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
if (fdt_size_dt_struct(fdt))
|
||||
return -FDT_ERR_BADSTATE;
|
||||
|
||||
offset = fdt_off_dt_struct(fdt);
|
||||
if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
re = (struct fdt_reserve_entry *)((char *)fdt + offset);
|
||||
re->address = cpu_to_fdt64(addr);
|
||||
re->size = cpu_to_fdt64(size);
|
||||
|
||||
fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_finish_reservemap(void *fdt)
|
||||
{
|
||||
return fdt_add_reservemap_entry(fdt, 0, 0);
|
||||
}
|
||||
|
||||
int fdt_begin_node(void *fdt, const char *name)
|
||||
{
|
||||
struct fdt_node_header *nh;
|
||||
int namelen = strlen(name) + 1;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
|
||||
if (! nh)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
|
||||
memcpy(nh->name, name, namelen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_end_node(void *fdt)
|
||||
{
|
||||
uint32_t *en;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
en = _fdt_grab_space(fdt, FDT_TAGSIZE);
|
||||
if (! en)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
*en = cpu_to_fdt32(FDT_END_NODE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _fdt_find_add_string(void *fdt, const char *s)
|
||||
{
|
||||
char *strtab = (char *)fdt + fdt_totalsize(fdt);
|
||||
const char *p;
|
||||
int strtabsize = fdt_size_dt_strings(fdt);
|
||||
int len = strlen(s) + 1;
|
||||
int struct_top, offset;
|
||||
|
||||
p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
|
||||
if (p)
|
||||
return p - strtab;
|
||||
|
||||
/* Add it */
|
||||
offset = -strtabsize - len;
|
||||
struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
if (fdt_totalsize(fdt) + offset < struct_top)
|
||||
return 0; /* no more room :( */
|
||||
|
||||
memcpy(strtab + offset, s, len);
|
||||
fdt_set_size_dt_strings(fdt, strtabsize + len);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_property(void *fdt, const char *name, const void *val, int len)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int nameoff;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
nameoff = _fdt_find_add_string(fdt, name);
|
||||
if (nameoff == 0)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
|
||||
if (! prop)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
prop->tag = cpu_to_fdt32(FDT_PROP);
|
||||
prop->nameoff = cpu_to_fdt32(nameoff);
|
||||
prop->len = cpu_to_fdt32(len);
|
||||
memcpy(prop->data, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_finish(void *fdt)
|
||||
{
|
||||
char *p = (char *)fdt;
|
||||
uint32_t *end;
|
||||
int oldstroffset, newstroffset;
|
||||
uint32_t tag;
|
||||
int offset, nextoffset;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
/* Add terminator */
|
||||
end = _fdt_grab_space(fdt, sizeof(*end));
|
||||
if (! end)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
*end = cpu_to_fdt32(FDT_END);
|
||||
|
||||
/* Relocate the string table */
|
||||
oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
|
||||
newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
|
||||
memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
|
||||
fdt_set_off_dt_strings(fdt, newstroffset);
|
||||
|
||||
/* Walk the structure, correcting string offsets */
|
||||
offset = 0;
|
||||
while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
|
||||
if (tag == FDT_PROP) {
|
||||
struct fdt_property *prop =
|
||||
_fdt_offset_ptr_w(fdt, offset);
|
||||
int nameoff;
|
||||
|
||||
nameoff = fdt32_to_cpu(prop->nameoff);
|
||||
nameoff += fdt_size_dt_strings(fdt);
|
||||
prop->nameoff = cpu_to_fdt32(nameoff);
|
||||
}
|
||||
offset = nextoffset;
|
||||
}
|
||||
if (nextoffset < 0)
|
||||
return nextoffset;
|
||||
|
||||
/* Finally, adjust the header */
|
||||
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
|
||||
fdt_set_magic(fdt, FDT_MAGIC);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen);
|
||||
if (! propval)
|
||||
return proplen;
|
||||
|
||||
if (proplen != len)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memcpy(propval, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _fdt_nop_region(void *start, int len)
|
||||
{
|
||||
uint32_t *p;
|
||||
|
||||
for (p = start; (char *)p < ((char *)start + len); p++)
|
||||
*p = cpu_to_fdt32(FDT_NOP);
|
||||
}
|
||||
|
||||
int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
|
||||
{
|
||||
struct fdt_property *prop;
|
||||
int len;
|
||||
|
||||
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
|
||||
if (! prop)
|
||||
return len;
|
||||
|
||||
_fdt_nop_region(prop, len + sizeof(*prop));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _fdt_node_end_offset(void *fdt, int offset)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
while ((offset >= 0) && (depth >= 0))
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_nop_node(void *fdt, int nodeoffset)
|
||||
{
|
||||
int endoffset;
|
||||
|
||||
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
|
||||
if (endoffset < 0)
|
||||
return endoffset;
|
||||
|
||||
_fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0),
|
||||
endoffset - nodeoffset);
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
#ifndef _LIBFDT_ENV_H
|
||||
#define _LIBFDT_ENV_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EXTRACT_BYTE(n) ((unsigned long long)((uint8_t *)&x)[n])
|
||||
static inline uint16_t fdt16_to_cpu(uint16_t x)
|
||||
{
|
||||
return (EXTRACT_BYTE(0) << 8) | EXTRACT_BYTE(1);
|
||||
}
|
||||
#define cpu_to_fdt16(x) fdt16_to_cpu(x)
|
||||
|
||||
static inline uint32_t fdt32_to_cpu(uint32_t x)
|
||||
{
|
||||
return (EXTRACT_BYTE(0) << 24) | (EXTRACT_BYTE(1) << 16) | (EXTRACT_BYTE(2) << 8) | EXTRACT_BYTE(3);
|
||||
}
|
||||
#define cpu_to_fdt32(x) fdt32_to_cpu(x)
|
||||
|
||||
static inline uint64_t fdt64_to_cpu(uint64_t x)
|
||||
{
|
||||
return (EXTRACT_BYTE(0) << 56) | (EXTRACT_BYTE(1) << 48) | (EXTRACT_BYTE(2) << 40) | (EXTRACT_BYTE(3) << 32)
|
||||
| (EXTRACT_BYTE(4) << 24) | (EXTRACT_BYTE(5) << 16) | (EXTRACT_BYTE(6) << 8) | EXTRACT_BYTE(7);
|
||||
}
|
||||
#define cpu_to_fdt64(x) fdt64_to_cpu(x)
|
||||
#undef EXTRACT_BYTE
|
||||
|
||||
#endif /* _LIBFDT_ENV_H */
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef _LIBFDT_INTERNAL_H
|
||||
#define _LIBFDT_INTERNAL_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <fdt.h>
|
||||
|
||||
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
|
||||
|
||||
#define FDT_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = fdt_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
}
|
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset);
|
||||
int _fdt_check_prop_offset(const void *fdt, int offset);
|
||||
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
|
||||
int _fdt_node_end_offset(void *fdt, int nodeoffset);
|
||||
|
||||
static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
|
||||
{
|
||||
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
|
||||
}
|
||||
|
||||
static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
|
||||
{
|
||||
return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset);
|
||||
}
|
||||
|
||||
static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
|
||||
{
|
||||
const struct fdt_reserve_entry *rsv_table =
|
||||
(const struct fdt_reserve_entry *)
|
||||
((const char *)fdt + fdt_off_mem_rsvmap(fdt));
|
||||
|
||||
return rsv_table + n;
|
||||
}
|
||||
static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
|
||||
{
|
||||
return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n);
|
||||
}
|
||||
|
||||
#define FDT_SW_MAGIC (~FDT_MAGIC)
|
||||
|
||||
#endif /* _LIBFDT_INTERNAL_H */
|
|
@ -0,0 +1,709 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
|
||||
/*
|
||||
* Tree building functions
|
||||
*/
|
||||
|
||||
void add_label(struct label **labels, char *label)
|
||||
{
|
||||
struct label *new;
|
||||
|
||||
/* Make sure the label isn't already there */
|
||||
for_each_label_withdel(*labels, new)
|
||||
if (streq(new->label, label)) {
|
||||
new->deleted = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
new = xmalloc(sizeof(*new));
|
||||
memset(new, 0, sizeof(*new));
|
||||
new->label = label;
|
||||
new->next = *labels;
|
||||
*labels = new;
|
||||
}
|
||||
|
||||
void delete_labels(struct label **labels)
|
||||
{
|
||||
struct label *label;
|
||||
|
||||
for_each_label(*labels, label)
|
||||
label->deleted = 1;
|
||||
}
|
||||
|
||||
struct property *build_property(char *name, struct data val)
|
||||
{
|
||||
struct property *new = xmalloc(sizeof(*new));
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->name = name;
|
||||
new->val = val;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct property *build_property_delete(char *name)
|
||||
{
|
||||
struct property *new = xmalloc(sizeof(*new));
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->name = name;
|
||||
new->deleted = 1;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct property *chain_property(struct property *first, struct property *list)
|
||||
{
|
||||
assert(first->next == NULL);
|
||||
|
||||
first->next = list;
|
||||
return first;
|
||||
}
|
||||
|
||||
struct property *reverse_properties(struct property *first)
|
||||
{
|
||||
struct property *p = first;
|
||||
struct property *head = NULL;
|
||||
struct property *next;
|
||||
|
||||
while (p) {
|
||||
next = p->next;
|
||||
p->next = head;
|
||||
head = p;
|
||||
p = next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
struct node *build_node(struct property *proplist, struct node *children)
|
||||
{
|
||||
struct node *new = xmalloc(sizeof(*new));
|
||||
struct node *child;
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->proplist = reverse_properties(proplist);
|
||||
new->children = children;
|
||||
|
||||
for_each_child(new, child) {
|
||||
child->parent = new;
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct node *build_node_delete(void)
|
||||
{
|
||||
struct node *new = xmalloc(sizeof(*new));
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->deleted = 1;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct node *name_node(struct node *node, char *name)
|
||||
{
|
||||
assert(node->name == NULL);
|
||||
|
||||
node->name = name;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
struct node *merge_nodes(struct node *old_node, struct node *new_node)
|
||||
{
|
||||
struct property *new_prop, *old_prop;
|
||||
struct node *new_child, *old_child;
|
||||
struct label *l;
|
||||
|
||||
old_node->deleted = 0;
|
||||
|
||||
/* Add new node labels to old node */
|
||||
for_each_label_withdel(new_node->labels, l)
|
||||
add_label(&old_node->labels, l->label);
|
||||
|
||||
/* Move properties from the new node to the old node. If there
|
||||
* is a collision, replace the old value with the new */
|
||||
while (new_node->proplist) {
|
||||
/* Pop the property off the list */
|
||||
new_prop = new_node->proplist;
|
||||
new_node->proplist = new_prop->next;
|
||||
new_prop->next = NULL;
|
||||
|
||||
if (new_prop->deleted) {
|
||||
delete_property_by_name(old_node, new_prop->name);
|
||||
free(new_prop);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Look for a collision, set new value if there is */
|
||||
for_each_property_withdel(old_node, old_prop) {
|
||||
if (streq(old_prop->name, new_prop->name)) {
|
||||
/* Add new labels to old property */
|
||||
for_each_label_withdel(new_prop->labels, l)
|
||||
add_label(&old_prop->labels, l->label);
|
||||
|
||||
old_prop->val = new_prop->val;
|
||||
old_prop->deleted = 0;
|
||||
free(new_prop);
|
||||
new_prop = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no collision occurred, add property to the old node. */
|
||||
if (new_prop)
|
||||
add_property(old_node, new_prop);
|
||||
}
|
||||
|
||||
/* Move the override child nodes into the primary node. If
|
||||
* there is a collision, then merge the nodes. */
|
||||
while (new_node->children) {
|
||||
/* Pop the child node off the list */
|
||||
new_child = new_node->children;
|
||||
new_node->children = new_child->next_sibling;
|
||||
new_child->parent = NULL;
|
||||
new_child->next_sibling = NULL;
|
||||
|
||||
if (new_child->deleted) {
|
||||
delete_node_by_name(old_node, new_child->name);
|
||||
free(new_child);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Search for a collision. Merge if there is */
|
||||
for_each_child_withdel(old_node, old_child) {
|
||||
if (streq(old_child->name, new_child->name)) {
|
||||
merge_nodes(old_child, new_child);
|
||||
new_child = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no collision occured, add child to the old node. */
|
||||
if (new_child)
|
||||
add_child(old_node, new_child);
|
||||
}
|
||||
|
||||
/* The new node contents are now merged into the old node. Free
|
||||
* the new node. */
|
||||
free(new_node);
|
||||
|
||||
return old_node;
|
||||
}
|
||||
|
||||
struct node *chain_node(struct node *first, struct node *list)
|
||||
{
|
||||
assert(first->next_sibling == NULL);
|
||||
|
||||
first->next_sibling = list;
|
||||
return first;
|
||||
}
|
||||
|
||||
void add_property(struct node *node, struct property *prop)
|
||||
{
|
||||
struct property **p;
|
||||
|
||||
prop->next = NULL;
|
||||
|
||||
p = &node->proplist;
|
||||
while (*p)
|
||||
p = &((*p)->next);
|
||||
|
||||
*p = prop;
|
||||
}
|
||||
|
||||
void delete_property_by_name(struct node *node, char *name)
|
||||
{
|
||||
struct property *prop = node->proplist;
|
||||
|
||||
while (prop) {
|
||||
if (!strcmp(prop->name, name)) {
|
||||
delete_property(prop);
|
||||
return;
|
||||
}
|
||||
prop = prop->next;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_property(struct property *prop)
|
||||
{
|
||||
prop->deleted = 1;
|
||||
delete_labels(&prop->labels);
|
||||
}
|
||||
|
||||
void add_child(struct node *parent, struct node *child)
|
||||
{
|
||||
struct node **p;
|
||||
|
||||
child->next_sibling = NULL;
|
||||
child->parent = parent;
|
||||
|
||||
p = &parent->children;
|
||||
while (*p)
|
||||
p = &((*p)->next_sibling);
|
||||
|
||||
*p = child;
|
||||
}
|
||||
|
||||
void delete_node_by_name(struct node *parent, char *name)
|
||||
{
|
||||
struct node *node = parent->children;
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(node->name, name)) {
|
||||
delete_node(node);
|
||||
return;
|
||||
}
|
||||
node = node->next_sibling;
|
||||
}
|
||||
}
|
||||
|
||||
void delete_node(struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
struct node *child;
|
||||
|
||||
node->deleted = 1;
|
||||
for_each_child(node, child)
|
||||
delete_node(child);
|
||||
for_each_property(node, prop)
|
||||
delete_property(prop);
|
||||
delete_labels(&node->labels);
|
||||
}
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size)
|
||||
{
|
||||
struct reserve_info *new = xmalloc(sizeof(*new));
|
||||
|
||||
memset(new, 0, sizeof(*new));
|
||||
|
||||
new->re.address = address;
|
||||
new->re.size = size;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
struct reserve_info *chain_reserve_entry(struct reserve_info *first,
|
||||
struct reserve_info *list)
|
||||
{
|
||||
assert(first->next == NULL);
|
||||
|
||||
first->next = list;
|
||||
return first;
|
||||
}
|
||||
|
||||
struct reserve_info *add_reserve_entry(struct reserve_info *list,
|
||||
struct reserve_info *new)
|
||||
{
|
||||
struct reserve_info *last;
|
||||
|
||||
new->next = NULL;
|
||||
|
||||
if (! list)
|
||||
return new;
|
||||
|
||||
for (last = list; last->next; last = last->next)
|
||||
;
|
||||
|
||||
last->next = new;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct boot_info *build_boot_info(struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys)
|
||||
{
|
||||
struct boot_info *bi;
|
||||
|
||||
bi = xmalloc(sizeof(*bi));
|
||||
bi->reservelist = reservelist;
|
||||
bi->dt = tree;
|
||||
bi->boot_cpuid_phys = boot_cpuid_phys;
|
||||
|
||||
return bi;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree accessor functions
|
||||
*/
|
||||
|
||||
const char *get_unitname(struct node *node)
|
||||
{
|
||||
if (node->name[node->basenamelen] == '\0')
|
||||
return "";
|
||||
else
|
||||
return node->name + node->basenamelen + 1;
|
||||
}
|
||||
|
||||
struct property *get_property(struct node *node, const char *propname)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, prop)
|
||||
if (streq(prop->name, propname))
|
||||
return prop;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cell_t propval_cell(struct property *prop)
|
||||
{
|
||||
assert(prop->val.len == sizeof(cell_t));
|
||||
return fdt32_to_cpu(*((cell_t *)prop->val.val));
|
||||
}
|
||||
|
||||
struct property *get_property_by_label(struct node *tree, const char *label,
|
||||
struct node **node)
|
||||
{
|
||||
struct property *prop;
|
||||
struct node *c;
|
||||
|
||||
*node = tree;
|
||||
|
||||
for_each_property(tree, prop) {
|
||||
struct label *l;
|
||||
|
||||
for_each_label(prop->labels, l)
|
||||
if (streq(l->label, label))
|
||||
return prop;
|
||||
}
|
||||
|
||||
for_each_child(tree, c) {
|
||||
prop = get_property_by_label(c, label, node);
|
||||
if (prop)
|
||||
return prop;
|
||||
}
|
||||
|
||||
*node = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct marker *get_marker_label(struct node *tree, const char *label,
|
||||
struct node **node, struct property **prop)
|
||||
{
|
||||
struct marker *m;
|
||||
struct property *p;
|
||||
struct node *c;
|
||||
|
||||
*node = tree;
|
||||
|
||||
for_each_property(tree, p) {
|
||||
*prop = p;
|
||||
m = p->val.markers;
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
if (streq(m->ref, label))
|
||||
return m;
|
||||
}
|
||||
|
||||
for_each_child(tree, c) {
|
||||
m = get_marker_label(c, label, node, prop);
|
||||
if (m)
|
||||
return m;
|
||||
}
|
||||
|
||||
*prop = NULL;
|
||||
*node = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *get_subnode(struct node *node, const char *nodename)
|
||||
{
|
||||
struct node *child;
|
||||
|
||||
for_each_child(node, child)
|
||||
if (streq(child->name, nodename))
|
||||
return child;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *get_node_by_path(struct node *tree, const char *path)
|
||||
{
|
||||
const char *p;
|
||||
struct node *child;
|
||||
|
||||
if (!path || ! (*path)) {
|
||||
if (tree->deleted)
|
||||
return NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
while (path[0] == '/')
|
||||
path++;
|
||||
|
||||
p = strchr(path, '/');
|
||||
|
||||
for_each_child(tree, child) {
|
||||
if (p && strneq(path, child->name, p-path))
|
||||
return get_node_by_path(child, p+1);
|
||||
else if (!p && streq(path, child->name))
|
||||
return child;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *get_node_by_label(struct node *tree, const char *label)
|
||||
{
|
||||
struct node *child, *node;
|
||||
struct label *l;
|
||||
|
||||
assert(label && (strlen(label) > 0));
|
||||
|
||||
for_each_label(tree->labels, l)
|
||||
if (streq(l->label, label))
|
||||
return tree;
|
||||
|
||||
for_each_child(tree, child) {
|
||||
node = get_node_by_label(child, label);
|
||||
if (node)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
|
||||
{
|
||||
struct node *child, *node;
|
||||
|
||||
assert((phandle != 0) && (phandle != -1));
|
||||
|
||||
if (tree->phandle == phandle) {
|
||||
if (tree->deleted)
|
||||
return NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
for_each_child(tree, child) {
|
||||
node = get_node_by_phandle(child, phandle);
|
||||
if (node)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct node *get_node_by_ref(struct node *tree, const char *ref)
|
||||
{
|
||||
if (ref[0] == '/')
|
||||
return get_node_by_path(tree, ref);
|
||||
else
|
||||
return get_node_by_label(tree, ref);
|
||||
}
|
||||
|
||||
cell_t get_node_phandle(struct node *root, struct node *node)
|
||||
{
|
||||
static cell_t phandle = 1; /* FIXME: ick, static local */
|
||||
|
||||
if ((node->phandle != 0) && (node->phandle != -1))
|
||||
return node->phandle;
|
||||
|
||||
while (get_node_by_phandle(root, phandle))
|
||||
phandle++;
|
||||
|
||||
node->phandle = phandle;
|
||||
|
||||
if (!get_property(node, "linux,phandle")
|
||||
&& (phandle_format & PHANDLE_LEGACY))
|
||||
add_property(node,
|
||||
build_property("linux,phandle",
|
||||
data_append_cell(empty_data, phandle)));
|
||||
|
||||
if (!get_property(node, "phandle")
|
||||
&& (phandle_format & PHANDLE_EPAPR))
|
||||
add_property(node,
|
||||
build_property("phandle",
|
||||
data_append_cell(empty_data, phandle)));
|
||||
|
||||
/* If the node *does* have a phandle property, we must
|
||||
* be dealing with a self-referencing phandle, which will be
|
||||
* fixed up momentarily in the caller */
|
||||
|
||||
return node->phandle;
|
||||
}
|
||||
|
||||
uint32_t guess_boot_cpuid(struct node *tree)
|
||||
{
|
||||
struct node *cpus, *bootcpu;
|
||||
struct property *reg;
|
||||
|
||||
cpus = get_node_by_path(tree, "/cpus");
|
||||
if (!cpus)
|
||||
return 0;
|
||||
|
||||
|
||||
bootcpu = cpus->children;
|
||||
if (!bootcpu)
|
||||
return 0;
|
||||
|
||||
reg = get_property(bootcpu, "reg");
|
||||
if (!reg || (reg->val.len != sizeof(uint32_t)))
|
||||
return 0;
|
||||
|
||||
/* FIXME: Sanity check node? */
|
||||
|
||||
return propval_cell(reg);
|
||||
}
|
||||
|
||||
static int cmp_reserve_info(const void *ax, const void *bx)
|
||||
{
|
||||
const struct reserve_info *a, *b;
|
||||
|
||||
a = *((const struct reserve_info * const *)ax);
|
||||
b = *((const struct reserve_info * const *)bx);
|
||||
|
||||
if (a->re.address < b->re.address)
|
||||
return -1;
|
||||
else if (a->re.address > b->re.address)
|
||||
return 1;
|
||||
else if (a->re.size < b->re.size)
|
||||
return -1;
|
||||
else if (a->re.size > b->re.size)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sort_reserve_entries(struct boot_info *bi)
|
||||
{
|
||||
struct reserve_info *ri, **tbl;
|
||||
int n = 0, i = 0;
|
||||
|
||||
for (ri = bi->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
n++;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
tbl = xmalloc(n * sizeof(*tbl));
|
||||
|
||||
for (ri = bi->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
tbl[i++] = ri;
|
||||
|
||||
qsort(tbl, n, sizeof(*tbl), cmp_reserve_info);
|
||||
|
||||
bi->reservelist = tbl[0];
|
||||
for (i = 0; i < (n-1); i++)
|
||||
tbl[i]->next = tbl[i+1];
|
||||
tbl[n-1]->next = NULL;
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
static int cmp_prop(const void *ax, const void *bx)
|
||||
{
|
||||
const struct property *a, *b;
|
||||
|
||||
a = *((const struct property * const *)ax);
|
||||
b = *((const struct property * const *)bx);
|
||||
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
static void sort_properties(struct node *node)
|
||||
{
|
||||
int n = 0, i = 0;
|
||||
struct property *prop, **tbl;
|
||||
|
||||
for_each_property_withdel(node, prop)
|
||||
n++;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
tbl = xmalloc(n * sizeof(*tbl));
|
||||
|
||||
for_each_property_withdel(node, prop)
|
||||
tbl[i++] = prop;
|
||||
|
||||
qsort(tbl, n, sizeof(*tbl), cmp_prop);
|
||||
|
||||
node->proplist = tbl[0];
|
||||
for (i = 0; i < (n-1); i++)
|
||||
tbl[i]->next = tbl[i+1];
|
||||
tbl[n-1]->next = NULL;
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
static int cmp_subnode(const void *ax, const void *bx)
|
||||
{
|
||||
const struct node *a, *b;
|
||||
|
||||
a = *((const struct node * const *)ax);
|
||||
b = *((const struct node * const *)bx);
|
||||
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
static void sort_subnodes(struct node *node)
|
||||
{
|
||||
int n = 0, i = 0;
|
||||
struct node *subnode, **tbl;
|
||||
|
||||
for_each_child_withdel(node, subnode)
|
||||
n++;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
tbl = xmalloc(n * sizeof(*tbl));
|
||||
|
||||
for_each_child_withdel(node, subnode)
|
||||
tbl[i++] = subnode;
|
||||
|
||||
qsort(tbl, n, sizeof(*tbl), cmp_subnode);
|
||||
|
||||
node->children = tbl[0];
|
||||
for (i = 0; i < (n-1); i++)
|
||||
tbl[i]->next_sibling = tbl[i+1];
|
||||
tbl[n-1]->next_sibling = NULL;
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
static void sort_node(struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
|
||||
sort_properties(node);
|
||||
sort_subnodes(node);
|
||||
for_each_child_withdel(node, c)
|
||||
sort_node(c);
|
||||
}
|
||||
|
||||
void sort_tree(struct boot_info *bi)
|
||||
{
|
||||
sort_reserve_entries(bi);
|
||||
sort_node(bi->dt);
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
/* A node in our list of directories to search for source/include files */
|
||||
struct search_path {
|
||||
struct search_path *next; /* next node in list, NULL for end */
|
||||
const char *dirname; /* name of directory to search */
|
||||
};
|
||||
|
||||
/* This is the list of directories that we search for source files */
|
||||
static struct search_path *search_path_head, **search_path_tail;
|
||||
|
||||
|
||||
static char *dirname(const char *path)
|
||||
{
|
||||
const char *slash = strrchr(path, '/');
|
||||
|
||||
if (slash) {
|
||||
int len = slash - path;
|
||||
char *dir = xmalloc(len + 1);
|
||||
|
||||
memcpy(dir, path, len);
|
||||
dir[len] = '\0';
|
||||
return dir;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *depfile; /* = NULL */
|
||||
struct srcfile_state *current_srcfile; /* = NULL */
|
||||
|
||||
/* Detect infinite include recursion. */
|
||||
#define MAX_SRCFILE_DEPTH (100)
|
||||
static int srcfile_depth; /* = 0 */
|
||||
|
||||
|
||||
/**
|
||||
* Try to open a file in a given directory.
|
||||
*
|
||||
* If the filename is an absolute path, then dirname is ignored. If it is a
|
||||
* relative path, then we look in that directory for the file.
|
||||
*
|
||||
* @param dirname Directory to look in, or NULL for none
|
||||
* @param fname Filename to look for
|
||||
* @param fp Set to NULL if file did not open
|
||||
* @return allocated filename on success (caller must free), NULL on failure
|
||||
*/
|
||||
static char *try_open(const char *dirname, const char *fname, FILE **fp)
|
||||
{
|
||||
char *fullname;
|
||||
|
||||
if (!dirname || fname[0] == '/')
|
||||
fullname = xstrdup(fname);
|
||||
else
|
||||
fullname = join_path(dirname, fname);
|
||||
|
||||
*fp = fopen(fullname, "r");
|
||||
if (!*fp) {
|
||||
free(fullname);
|
||||
fullname = NULL;
|
||||
}
|
||||
|
||||
return fullname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a file for read access
|
||||
*
|
||||
* If it is a relative filename, we search the full search path for it.
|
||||
*
|
||||
* @param fname Filename to open
|
||||
* @param fp Returns pointer to opened FILE, or NULL on failure
|
||||
* @return pointer to allocated filename, which caller must free
|
||||
*/
|
||||
static char *fopen_any_on_path(const char *fname, FILE **fp)
|
||||
{
|
||||
const char *cur_dir = NULL;
|
||||
struct search_path *node;
|
||||
char *fullname;
|
||||
|
||||
/* Try current directory first */
|
||||
assert(fp);
|
||||
if (current_srcfile)
|
||||
cur_dir = current_srcfile->dir;
|
||||
fullname = try_open(cur_dir, fname, fp);
|
||||
|
||||
/* Failing that, try each search path in turn */
|
||||
for (node = search_path_head; !*fp && node; node = node->next)
|
||||
fullname = try_open(node->dirname, fname, fp);
|
||||
|
||||
return fullname;
|
||||
}
|
||||
|
||||
FILE *srcfile_relative_open(const char *fname, char **fullnamep)
|
||||
{
|
||||
FILE *f;
|
||||
char *fullname;
|
||||
|
||||
if (streq(fname, "-")) {
|
||||
f = stdin;
|
||||
fullname = xstrdup("<stdin>");
|
||||
} else {
|
||||
fullname = fopen_any_on_path(fname, &f);
|
||||
if (!f)
|
||||
die("Couldn't open \"%s\": %s\n", fname,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
if (depfile)
|
||||
fprintf(depfile, " %s", fullname);
|
||||
|
||||
if (fullnamep)
|
||||
*fullnamep = fullname;
|
||||
else
|
||||
free(fullname);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void srcfile_push(const char *fname)
|
||||
{
|
||||
struct srcfile_state *srcfile;
|
||||
|
||||
if (srcfile_depth++ >= MAX_SRCFILE_DEPTH)
|
||||
die("Includes nested too deeply");
|
||||
|
||||
srcfile = xmalloc(sizeof(*srcfile));
|
||||
|
||||
srcfile->f = srcfile_relative_open(fname, &srcfile->name);
|
||||
srcfile->dir = dirname(srcfile->name);
|
||||
srcfile->prev = current_srcfile;
|
||||
|
||||
srcfile->lineno = 1;
|
||||
srcfile->colno = 1;
|
||||
|
||||
current_srcfile = srcfile;
|
||||
}
|
||||
|
||||
int srcfile_pop(void)
|
||||
{
|
||||
struct srcfile_state *srcfile = current_srcfile;
|
||||
|
||||
assert(srcfile);
|
||||
|
||||
current_srcfile = srcfile->prev;
|
||||
|
||||
if (fclose(srcfile->f))
|
||||
die("Error closing \"%s\": %s\n", srcfile->name,
|
||||
strerror(errno));
|
||||
|
||||
/* FIXME: We allow the srcfile_state structure to leak,
|
||||
* because it could still be referenced from a location
|
||||
* variable being carried through the parser somewhere. To
|
||||
* fix this we could either allocate all the files from a
|
||||
* table, or use a pool allocator. */
|
||||
|
||||
return current_srcfile ? 1 : 0;
|
||||
}
|
||||
|
||||
void srcfile_add_search_path(const char *dirname)
|
||||
{
|
||||
struct search_path *node;
|
||||
|
||||
/* Create the node */
|
||||
node = xmalloc(sizeof(*node));
|
||||
node->next = NULL;
|
||||
node->dirname = xstrdup(dirname);
|
||||
|
||||
/* Add to the end of our list */
|
||||
if (search_path_tail)
|
||||
*search_path_tail = node;
|
||||
else
|
||||
search_path_head = node;
|
||||
search_path_tail = &node->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* The empty source position.
|
||||
*/
|
||||
|
||||
struct srcpos srcpos_empty = {
|
||||
.first_line = 0,
|
||||
.first_column = 0,
|
||||
.last_line = 0,
|
||||
.last_column = 0,
|
||||
.file = NULL,
|
||||
};
|
||||
|
||||
#define TAB_SIZE 8
|
||||
|
||||
void srcpos_update(struct srcpos *pos, const char *text, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pos->file = current_srcfile;
|
||||
|
||||
pos->first_line = current_srcfile->lineno;
|
||||
pos->first_column = current_srcfile->colno;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (text[i] == '\n') {
|
||||
current_srcfile->lineno++;
|
||||
current_srcfile->colno = 1;
|
||||
} else if (text[i] == '\t') {
|
||||
current_srcfile->colno =
|
||||
ALIGN(current_srcfile->colno, TAB_SIZE);
|
||||
} else {
|
||||
current_srcfile->colno++;
|
||||
}
|
||||
|
||||
pos->last_line = current_srcfile->lineno;
|
||||
pos->last_column = current_srcfile->colno;
|
||||
}
|
||||
|
||||
struct srcpos *
|
||||
srcpos_copy(struct srcpos *pos)
|
||||
{
|
||||
struct srcpos *pos_new;
|
||||
|
||||
pos_new = xmalloc(sizeof(struct srcpos));
|
||||
memcpy(pos_new, pos, sizeof(struct srcpos));
|
||||
|
||||
return pos_new;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
srcpos_dump(struct srcpos *pos)
|
||||
{
|
||||
printf("file : \"%s\"\n",
|
||||
pos->file ? (char *) pos->file : "<no file>");
|
||||
printf("first_line : %d\n", pos->first_line);
|
||||
printf("first_column: %d\n", pos->first_column);
|
||||
printf("last_line : %d\n", pos->last_line);
|
||||
printf("last_column : %d\n", pos->last_column);
|
||||
printf("file : %s\n", pos->file->name);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
srcpos_string(struct srcpos *pos)
|
||||
{
|
||||
const char *fname = "<no-file>";
|
||||
char *pos_str;
|
||||
int rc;
|
||||
|
||||
if (pos)
|
||||
fname = pos->file->name;
|
||||
|
||||
|
||||
if (pos->first_line != pos->last_line)
|
||||
rc = asprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_line, pos->last_column);
|
||||
else if (pos->first_column != pos->last_column)
|
||||
rc = asprintf(&pos_str, "%s:%d.%d-%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_column);
|
||||
else
|
||||
rc = asprintf(&pos_str, "%s:%d.%d", fname,
|
||||
pos->first_line, pos->first_column);
|
||||
|
||||
if (rc == -1)
|
||||
die("Couldn't allocate in srcpos string");
|
||||
|
||||
return pos_str;
|
||||
}
|
||||
|
||||
void
|
||||
srcpos_verror(struct srcpos *pos, char const *fmt, va_list va)
|
||||
{
|
||||
const char *srcstr;
|
||||
|
||||
srcstr = srcpos_string(pos);
|
||||
|
||||
fprintf(stdout, "Error: %s ", srcstr);
|
||||
vfprintf(stdout, fmt, va);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
srcpos_error(struct srcpos *pos, char const *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
srcpos_verror(pos, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
srcpos_warn(struct srcpos *pos, char const *fmt, ...)
|
||||
{
|
||||
const char *srcstr;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
|
||||
srcstr = srcpos_string(pos);
|
||||
|
||||
fprintf(stderr, "Warning: %s ", srcstr);
|
||||
vfprintf(stderr, fmt, va);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void srcpos_set_line(char *f, int l)
|
||||
{
|
||||
current_srcfile->name = f;
|
||||
current_srcfile->lineno = l;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef _SRCPOS_H_
|
||||
#define _SRCPOS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct srcfile_state {
|
||||
FILE *f;
|
||||
char *name;
|
||||
char *dir;
|
||||
int lineno, colno;
|
||||
struct srcfile_state *prev;
|
||||
};
|
||||
|
||||
extern FILE *depfile; /* = NULL */
|
||||
extern struct srcfile_state *current_srcfile; /* = NULL */
|
||||
|
||||
/**
|
||||
* Open a source file.
|
||||
*
|
||||
* If the source file is a relative pathname, then it is searched for in the
|
||||
* current directory (the directory of the last source file read) and after
|
||||
* that in the search path.
|
||||
*
|
||||
* We work through the search path in order from the first path specified to
|
||||
* the last.
|
||||
*
|
||||
* If the file is not found, then this function does not return, but calls
|
||||
* die().
|
||||
*
|
||||
* @param fname Filename to search
|
||||
* @param fullnamep If non-NULL, it is set to the allocated filename of the
|
||||
* file that was opened. The caller is then responsible
|
||||
* for freeing the pointer.
|
||||
* @return pointer to opened FILE
|
||||
*/
|
||||
FILE *srcfile_relative_open(const char *fname, char **fullnamep);
|
||||
|
||||
void srcfile_push(const char *fname);
|
||||
int srcfile_pop(void);
|
||||
|
||||
/**
|
||||
* Add a new directory to the search path for input files
|
||||
*
|
||||
* The new path is added at the end of the list.
|
||||
*
|
||||
* @param dirname Directory to add
|
||||
*/
|
||||
void srcfile_add_search_path(const char *dirname);
|
||||
|
||||
struct srcpos {
|
||||
int first_line;
|
||||
int first_column;
|
||||
int last_line;
|
||||
int last_column;
|
||||
struct srcfile_state *file;
|
||||
};
|
||||
|
||||
#define YYLTYPE struct srcpos
|
||||
|
||||
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
|
||||
(Current).first_column = YYRHSLOC(Rhs, 1).first_column; \
|
||||
(Current).last_line = YYRHSLOC(Rhs, N).last_line; \
|
||||
(Current).last_column = YYRHSLOC (Rhs, N).last_column; \
|
||||
(Current).file = YYRHSLOC(Rhs, N).file; \
|
||||
} else { \
|
||||
(Current).first_line = (Current).last_line = \
|
||||
YYRHSLOC(Rhs, 0).last_line; \
|
||||
(Current).first_column = (Current).last_column = \
|
||||
YYRHSLOC(Rhs, 0).last_column; \
|
||||
(Current).file = YYRHSLOC (Rhs, 0).file; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Fictional source position used for IR nodes that are
|
||||
* created without otherwise knowing a true source position.
|
||||
* For example,constant definitions from the command line.
|
||||
*/
|
||||
extern struct srcpos srcpos_empty;
|
||||
|
||||
extern void srcpos_update(struct srcpos *pos, const char *text, int len);
|
||||
extern struct srcpos *srcpos_copy(struct srcpos *pos);
|
||||
extern char *srcpos_string(struct srcpos *pos);
|
||||
extern void srcpos_dump(struct srcpos *pos);
|
||||
|
||||
extern void srcpos_verror(struct srcpos *pos, char const *, va_list va)
|
||||
__attribute__((format(printf, 2, 0)));
|
||||
extern void srcpos_error(struct srcpos *pos, char const *, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
extern void srcpos_warn(struct srcpos *pos, char const *, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
extern void srcpos_set_line(char *f, int l);
|
||||
|
||||
#endif /* _SRCPOS_H_ */
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
extern FILE *yyin;
|
||||
extern int yyparse(void);
|
||||
extern YYLTYPE yylloc;
|
||||
|
||||
struct boot_info *the_boot_info;
|
||||
int treesource_error;
|
||||
|
||||
struct boot_info *dt_from_source(const char *fname)
|
||||
{
|
||||
the_boot_info = NULL;
|
||||
treesource_error = 0;
|
||||
|
||||
srcfile_push(fname);
|
||||
yyin = current_srcfile->f;
|
||||
yylloc.file = current_srcfile;
|
||||
|
||||
if (yyparse() != 0)
|
||||
die("Unable to parse input tree\n");
|
||||
|
||||
if (treesource_error)
|
||||
die("Syntax error parsing input tree\n");
|
||||
|
||||
return the_boot_info;
|
||||
}
|
||||
|
||||
static void write_prefix(FILE *f, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < level; i++)
|
||||
fputc('\t', f);
|
||||
}
|
||||
|
||||
static int isstring(char c)
|
||||
{
|
||||
return (isprint(c)
|
||||
|| (c == '\0')
|
||||
|| strchr("\a\b\t\n\v\f\r", c));
|
||||
}
|
||||
|
||||
static void write_propval_string(FILE *f, struct data val)
|
||||
{
|
||||
const char *str = val.val;
|
||||
int i;
|
||||
struct marker *m = val.markers;
|
||||
|
||||
assert(str[val.len-1] == '\0');
|
||||
|
||||
while (m && (m->offset == 0)) {
|
||||
if (m->type == LABEL)
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
m = m->next;
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
|
||||
for (i = 0; i < (val.len-1); i++) {
|
||||
char c = str[i];
|
||||
|
||||
switch (c) {
|
||||
case '\a':
|
||||
fprintf(f, "\\a");
|
||||
break;
|
||||
case '\b':
|
||||
fprintf(f, "\\b");
|
||||
break;
|
||||
case '\t':
|
||||
fprintf(f, "\\t");
|
||||
break;
|
||||
case '\n':
|
||||
fprintf(f, "\\n");
|
||||
break;
|
||||
case '\v':
|
||||
fprintf(f, "\\v");
|
||||
break;
|
||||
case '\f':
|
||||
fprintf(f, "\\f");
|
||||
break;
|
||||
case '\r':
|
||||
fprintf(f, "\\r");
|
||||
break;
|
||||
case '\\':
|
||||
fprintf(f, "\\\\");
|
||||
break;
|
||||
case '\"':
|
||||
fprintf(f, "\\\"");
|
||||
break;
|
||||
case '\0':
|
||||
fprintf(f, "\", ");
|
||||
while (m && (m->offset < i)) {
|
||||
if (m->type == LABEL) {
|
||||
assert(m->offset == (i+1));
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
}
|
||||
m = m->next;
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
break;
|
||||
default:
|
||||
if (isprint(c))
|
||||
fprintf(f, "%c", c);
|
||||
else
|
||||
fprintf(f, "\\x%02hhx", c);
|
||||
}
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
|
||||
/* Wrap up any labels at the end of the value */
|
||||
for_each_marker_of_type(m, LABEL) {
|
||||
assert (m->offset == val.len);
|
||||
fprintf(f, " %s:", m->ref);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_propval_cells(FILE *f, struct data val)
|
||||
{
|
||||
void *propend = val.val + val.len;
|
||||
cell_t *cp = (cell_t *)val.val;
|
||||
struct marker *m = val.markers;
|
||||
|
||||
fprintf(f, "<");
|
||||
for (;;) {
|
||||
while (m && (m->offset <= ((char *)cp - val.val))) {
|
||||
if (m->type == LABEL) {
|
||||
assert(m->offset == ((char *)cp - val.val));
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
}
|
||||
m = m->next;
|
||||
}
|
||||
|
||||
fprintf(f, "0x%x", fdt32_to_cpu(*cp++));
|
||||
if ((void *)cp >= propend)
|
||||
break;
|
||||
fprintf(f, " ");
|
||||
}
|
||||
|
||||
/* Wrap up any labels at the end of the value */
|
||||
for_each_marker_of_type(m, LABEL) {
|
||||
assert (m->offset == val.len);
|
||||
fprintf(f, " %s:", m->ref);
|
||||
}
|
||||
fprintf(f, ">");
|
||||
}
|
||||
|
||||
static void write_propval_bytes(FILE *f, struct data val)
|
||||
{
|
||||
void *propend = val.val + val.len;
|
||||
const char *bp = val.val;
|
||||
struct marker *m = val.markers;
|
||||
|
||||
fprintf(f, "[");
|
||||
for (;;) {
|
||||
while (m && (m->offset == (bp-val.val))) {
|
||||
if (m->type == LABEL)
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
m = m->next;
|
||||
}
|
||||
|
||||
fprintf(f, "%02hhx", *bp++);
|
||||
if ((const void *)bp >= propend)
|
||||
break;
|
||||
fprintf(f, " ");
|
||||
}
|
||||
|
||||
/* Wrap up any labels at the end of the value */
|
||||
for_each_marker_of_type(m, LABEL) {
|
||||
assert (m->offset == val.len);
|
||||
fprintf(f, " %s:", m->ref);
|
||||
}
|
||||
fprintf(f, "]");
|
||||
}
|
||||
|
||||
static void write_propval(FILE *f, struct property *prop)
|
||||
{
|
||||
int len = prop->val.len;
|
||||
const char *p = prop->val.val;
|
||||
struct marker *m = prop->val.markers;
|
||||
int nnotstring = 0, nnul = 0;
|
||||
int nnotstringlbl = 0, nnotcelllbl = 0;
|
||||
int i;
|
||||
|
||||
if (len == 0) {
|
||||
fprintf(f, ";\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (! isstring(p[i]))
|
||||
nnotstring++;
|
||||
if (p[i] == '\0')
|
||||
nnul++;
|
||||
}
|
||||
|
||||
for_each_marker_of_type(m, LABEL) {
|
||||
if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0'))
|
||||
nnotstringlbl++;
|
||||
if ((m->offset % sizeof(cell_t)) != 0)
|
||||
nnotcelllbl++;
|
||||
}
|
||||
|
||||
fprintf(f, " = ");
|
||||
if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul < (len-nnul))
|
||||
&& (nnotstringlbl == 0)) {
|
||||
write_propval_string(f, prop->val);
|
||||
} else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) {
|
||||
write_propval_cells(f, prop->val);
|
||||
} else {
|
||||
write_propval_bytes(f, prop->val);
|
||||
}
|
||||
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
static void write_tree_source_node(FILE *f, struct node *tree, int level)
|
||||
{
|
||||
struct property *prop;
|
||||
struct node *child;
|
||||
struct label *l;
|
||||
|
||||
write_prefix(f, level);
|
||||
for_each_label(tree->labels, l)
|
||||
fprintf(f, "%s: ", l->label);
|
||||
if (tree->name && (*tree->name))
|
||||
fprintf(f, "%s {\n", tree->name);
|
||||
else
|
||||
fprintf(f, "/ {\n");
|
||||
|
||||
for_each_property(tree, prop) {
|
||||
write_prefix(f, level+1);
|
||||
for_each_label(prop->labels, l)
|
||||
fprintf(f, "%s: ", l->label);
|
||||
fprintf(f, "%s", prop->name);
|
||||
write_propval(f, prop);
|
||||
}
|
||||
for_each_child(tree, child) {
|
||||
fprintf(f, "\n");
|
||||
write_tree_source_node(f, child, level+1);
|
||||
}
|
||||
write_prefix(f, level);
|
||||
fprintf(f, "};\n");
|
||||
}
|
||||
|
||||
|
||||
void dt_to_source(FILE *f, struct boot_info *bi)
|
||||
{
|
||||
struct reserve_info *re;
|
||||
|
||||
fprintf(f, "/dts-v1/;\n\n");
|
||||
|
||||
for (re = bi->reservelist; re; re = re->next) {
|
||||
struct label *l;
|
||||
|
||||
for_each_label(re->labels, l)
|
||||
fprintf(f, "%s: ", l->label);
|
||||
fprintf(f, "/memreserve/\t0x%016llx 0x%016llx;\n",
|
||||
(unsigned long long)re->re.address,
|
||||
(unsigned long long)re->re.size);
|
||||
}
|
||||
|
||||
write_tree_source_node(f, bi->dt, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright 2011 The Chromium Authors, All Rights Reserved.
|
||||
* Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*
|
||||
* util_is_printable_string contributed by
|
||||
* Pantelis Antoniou <pantelis.antoniou AT gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libfdt.h"
|
||||
#include "util.h"
|
||||
|
||||
char *xstrdup(const char *s)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
char *dup = xmalloc(len);
|
||||
|
||||
memcpy(dup, s, len);
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
char *join_path(const char *path, const char *name)
|
||||
{
|
||||
int lenp = strlen(path);
|
||||
int lenn = strlen(name);
|
||||
int len;
|
||||
int needslash = 1;
|
||||
char *str;
|
||||
|
||||
len = lenp + lenn + 2;
|
||||
if ((lenp > 0) && (path[lenp-1] == '/')) {
|
||||
needslash = 0;
|
||||
len--;
|
||||
}
|
||||
|
||||
str = xmalloc(len);
|
||||
memcpy(str, path, lenp);
|
||||
if (needslash) {
|
||||
str[lenp] = '/';
|
||||
lenp++;
|
||||
}
|
||||
memcpy(str+lenp, name, lenn+1);
|
||||
return str;
|
||||
}
|
||||
|
||||
int util_is_printable_string(const void *data, int len)
|
||||
{
|
||||
const char *s = data;
|
||||
const char *ss;
|
||||
|
||||
/* zero length is not */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
ss = s;
|
||||
while (*s && isprint(*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s != '\0' || (s + 1 - ss) < len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a octal encoded character starting at index i in string s. The
|
||||
* resulting character will be returned and the index i will be updated to
|
||||
* point at the character directly after the end of the encoding, this may be
|
||||
* the '\0' terminator of the string.
|
||||
*/
|
||||
static char get_oct_char(const char *s, int *i)
|
||||
{
|
||||
char x[4];
|
||||
char *endx;
|
||||
long val;
|
||||
|
||||
x[3] = '\0';
|
||||
strncpy(x, s + *i, 3);
|
||||
|
||||
val = strtol(x, &endx, 8);
|
||||
|
||||
assert(endx > x);
|
||||
|
||||
(*i) += endx - x;
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a hexadecimal encoded character starting at index i in string s. The
|
||||
* resulting character will be returned and the index i will be updated to
|
||||
* point at the character directly after the end of the encoding, this may be
|
||||
* the '\0' terminator of the string.
|
||||
*/
|
||||
static char get_hex_char(const char *s, int *i)
|
||||
{
|
||||
char x[3];
|
||||
char *endx;
|
||||
long val;
|
||||
|
||||
x[2] = '\0';
|
||||
strncpy(x, s + *i, 2);
|
||||
|
||||
val = strtol(x, &endx, 16);
|
||||
if (!(endx > x))
|
||||
die("\\x used with no following hex digits\n");
|
||||
|
||||
(*i) += endx - x;
|
||||
return val;
|
||||
}
|
||||
|
||||
char get_escape_char(const char *s, int *i)
|
||||
{
|
||||
char c = s[*i];
|
||||
int j = *i + 1;
|
||||
char val;
|
||||
|
||||
assert(c);
|
||||
switch (c) {
|
||||
case 'a':
|
||||
val = '\a';
|
||||
break;
|
||||
case 'b':
|
||||
val = '\b';
|
||||
break;
|
||||
case 't':
|
||||
val = '\t';
|
||||
break;
|
||||
case 'n':
|
||||
val = '\n';
|
||||
break;
|
||||
case 'v':
|
||||
val = '\v';
|
||||
break;
|
||||
case 'f':
|
||||
val = '\f';
|
||||
break;
|
||||
case 'r':
|
||||
val = '\r';
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
j--; /* need to re-read the first digit as
|
||||
* part of the octal value */
|
||||
val = get_oct_char(s, &j);
|
||||
break;
|
||||
case 'x':
|
||||
val = get_hex_char(s, &j);
|
||||
break;
|
||||
default:
|
||||
val = c;
|
||||
}
|
||||
|
||||
(*i) = j;
|
||||
return val;
|
||||
}
|
||||
|
||||
int utilfdt_read_err(const char *filename, char **buffp)
|
||||
{
|
||||
int fd = 0; /* assume stdin */
|
||||
char *buf = NULL;
|
||||
off_t bufsize = 1024, offset = 0;
|
||||
int ret = 0;
|
||||
|
||||
*buffp = NULL;
|
||||
if (strcmp(filename, "-") != 0) {
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* Loop until we have read everything */
|
||||
buf = malloc(bufsize);
|
||||
do {
|
||||
/* Expand the buffer to hold the next chunk */
|
||||
if (offset == bufsize) {
|
||||
bufsize *= 2;
|
||||
buf = realloc(buf, bufsize);
|
||||
if (!buf) {
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = read(fd, &buf[offset], bufsize - offset);
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
} while (ret != 0);
|
||||
|
||||
/* Clean up, including closing stdin; return errno on error */
|
||||
close(fd);
|
||||
if (ret)
|
||||
free(buf);
|
||||
else
|
||||
*buffp = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *utilfdt_read(const char *filename)
|
||||
{
|
||||
char *buff;
|
||||
int ret = utilfdt_read_err(filename, &buff);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
|
||||
strerror(ret));
|
||||
return NULL;
|
||||
}
|
||||
/* Successful read */
|
||||
return buff;
|
||||
}
|
||||
|
||||
int utilfdt_write_err(const char *filename, const void *blob)
|
||||
{
|
||||
int fd = 1; /* assume stdout */
|
||||
int totalsize;
|
||||
int offset;
|
||||
int ret = 0;
|
||||
const char *ptr = blob;
|
||||
|
||||
if (strcmp(filename, "-") != 0) {
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (fd < 0)
|
||||
return errno;
|
||||
}
|
||||
|
||||
totalsize = fdt_totalsize(blob);
|
||||
offset = 0;
|
||||
|
||||
while (offset < totalsize) {
|
||||
ret = write(fd, ptr + offset, totalsize - offset);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
offset += ret;
|
||||
}
|
||||
/* Close the file/stdin; return errno on error */
|
||||
if (fd != 1)
|
||||
close(fd);
|
||||
return ret < 0 ? -ret : 0;
|
||||
}
|
||||
|
||||
|
||||
int utilfdt_write(const char *filename, const void *blob)
|
||||
{
|
||||
int ret = utilfdt_write_err(filename, blob);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename,
|
||||
strerror(ret));
|
||||
}
|
||||
return ret ? -1 : 0;
|
||||
}
|
||||
|
||||
int utilfdt_decode_type(const char *fmt, int *type, int *size)
|
||||
{
|
||||
int qualifier = 0;
|
||||
|
||||
if (!*fmt)
|
||||
return -1;
|
||||
|
||||
/* get the conversion qualifier */
|
||||
*size = -1;
|
||||
if (strchr("hlLb", *fmt)) {
|
||||
qualifier = *fmt++;
|
||||
if (qualifier == *fmt) {
|
||||
switch (*fmt++) {
|
||||
/* TODO: case 'l': qualifier = 'L'; break;*/
|
||||
case 'h':
|
||||
qualifier = 'b';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* we should now have a type */
|
||||
if ((*fmt == '\0') || !strchr("iuxs", *fmt))
|
||||
return -1;
|
||||
|
||||
/* convert qualifier (bhL) to byte size */
|
||||
if (*fmt != 's')
|
||||
*size = qualifier == 'b' ? 1 :
|
||||
qualifier == 'h' ? 2 :
|
||||
qualifier == 'l' ? 4 : -1;
|
||||
*type = *fmt++;
|
||||
|
||||
/* that should be it! */
|
||||
if (*fmt)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
* Copyright 2011 The Chromium Authors, All Rights Reserved.
|
||||
* Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
static inline void __attribute__((noreturn)) die(char * str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
vfprintf(stderr, str, ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void *xmalloc(size_t len)
|
||||
{
|
||||
void *new = malloc(len);
|
||||
|
||||
if (!new)
|
||||
die("malloc() failed\n");
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static inline void *xrealloc(void *p, size_t len)
|
||||
{
|
||||
void *new = realloc(p, len);
|
||||
|
||||
if (!new)
|
||||
die("realloc() failed (len=%d)\n", len);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
extern char *xstrdup(const char *s);
|
||||
extern char *join_path(const char *path, const char *name);
|
||||
|
||||
/**
|
||||
* Check a string of a given length to see if it is all printable and
|
||||
* has a valid terminator.
|
||||
*
|
||||
* @param data The string to check
|
||||
* @param len The string length including terminator
|
||||
* @return 1 if a valid printable string, 0 if not */
|
||||
int util_is_printable_string(const void *data, int len);
|
||||
|
||||
/*
|
||||
* Parse an escaped character starting at index i in string s. The resulting
|
||||
* character will be returned and the index i will be updated to point at the
|
||||
* character directly after the end of the encoding, this may be the '\0'
|
||||
* terminator of the string.
|
||||
*/
|
||||
char get_escape_char(const char *s, int *i);
|
||||
|
||||
/**
|
||||
* Read a device tree file into a buffer. This will report any errors on
|
||||
* stderr.
|
||||
*
|
||||
* @param filename The filename to read, or - for stdin
|
||||
* @return Pointer to allocated buffer containing fdt, or NULL on error
|
||||
*/
|
||||
char *utilfdt_read(const char *filename);
|
||||
|
||||
/**
|
||||
* Read a device tree file into a buffer. Does not report errors, but only
|
||||
* returns them. The value returned can be passed to strerror() to obtain
|
||||
* an error message for the user.
|
||||
*
|
||||
* @param filename The filename to read, or - for stdin
|
||||
* @param buffp Returns pointer to buffer containing fdt
|
||||
* @return 0 if ok, else an errno value representing the error
|
||||
*/
|
||||
int utilfdt_read_err(const char *filename, char **buffp);
|
||||
|
||||
|
||||
/**
|
||||
* Write a device tree buffer to a file. This will report any errors on
|
||||
* stderr.
|
||||
*
|
||||
* @param filename The filename to write, or - for stdout
|
||||
* @param blob Poiner to buffer containing fdt
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
int utilfdt_write(const char *filename, const void *blob);
|
||||
|
||||
/**
|
||||
* Write a device tree buffer to a file. Does not report errors, but only
|
||||
* returns them. The value returned can be passed to strerror() to obtain
|
||||
* an error message for the user.
|
||||
*
|
||||
* @param filename The filename to write, or - for stdout
|
||||
* @param blob Poiner to buffer containing fdt
|
||||
* @return 0 if ok, else an errno value representing the error
|
||||
*/
|
||||
int utilfdt_write_err(const char *filename, const void *blob);
|
||||
|
||||
/**
|
||||
* Decode a data type string. The purpose of this string
|
||||
*
|
||||
* The string consists of an optional character followed by the type:
|
||||
* Modifier characters:
|
||||
* hh or b 1 byte
|
||||
* h 2 byte
|
||||
* l 4 byte, default
|
||||
*
|
||||
* Type character:
|
||||
* s string
|
||||
* i signed integer
|
||||
* u unsigned integer
|
||||
* x hex
|
||||
*
|
||||
* TODO: Implement ll modifier (8 bytes)
|
||||
* TODO: Implement o type (octal)
|
||||
*
|
||||
* @param fmt Format string to process
|
||||
* @param type Returns type found(s/d/u/x), or 0 if none
|
||||
* @param size Returns size found(1,2,4,8) or 4 if none
|
||||
* @return 0 if ok, -1 on error (no type given, or other invalid format)
|
||||
*/
|
||||
int utilfdt_decode_type(const char *fmt, int *type, int *size);
|
||||
|
||||
/*
|
||||
* This is a usage message fragment for the -t option. It is the format
|
||||
* supported by utilfdt_decode_type.
|
||||
*/
|
||||
|
||||
#define USAGE_TYPE_MSG \
|
||||
"<type>\ts=string, i=int, u=unsigned, x=hex\n" \
|
||||
"\tOptional modifier prefix:\n" \
|
||||
"\t\thh or b=byte, h=2 byte, l=4 byte (default)\n";
|
||||
|
||||
#endif /* _UTIL_H */
|
|
@ -0,0 +1 @@
|
|||
#define DTC_VERSION "DTC 1.2.0-g37c0b6a0"
|
Loading…
Reference in New Issue