mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 07:04:28 +00:00
scripts/dtc: Update to upstream version v1.4.3
Using the update-dtc-source.sh script from Linux v4.14-rc1 import the portions of dtc that we require. We bring in update-dtc-source.sh and scripts/dtc/Makefile from Linux v4.14-rc1. Rework DTC_FLAGS handling to not require a test. Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
parent
0929863aff
commit
c0e032e009
35 changed files with 15100 additions and 13 deletions
|
@ -172,11 +172,6 @@ ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh)
|
|||
# Usage: $(call ld-ifversion, -ge, 22252, y)
|
||||
ld-ifversion = $(shell [ $(ld-version) $(1) $(2) ] && echo $(3) || echo $(4))
|
||||
|
||||
# dtc-option
|
||||
# Usage: DTC_FLAGS += $(call dtc-option,-Wno-unit_address_vs_reg)
|
||||
dtc-option = $(call try-run,\
|
||||
echo '/dts-v1/; / {};' | $(DTC) $(1),$(1),$(2))
|
||||
|
||||
######
|
||||
|
||||
###
|
||||
|
|
|
@ -58,8 +58,8 @@ endif
|
|||
|
||||
KBUILD_CFLAGS += $(warning)
|
||||
|
||||
dtc-warning-2 += $(call dtc-option,-Wnode_name_chars_strict)
|
||||
dtc-warning-2 += $(call dtc-option,-Wproperty_name_chars_strict)
|
||||
dtc-warning-2 += -Wnode_name_chars_strict
|
||||
dtc-warning-2 += -Wproperty_name_chars_strict
|
||||
|
||||
dtc-warning := $(dtc-warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
|
||||
dtc-warning += $(dtc-warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
|
||||
|
@ -70,11 +70,11 @@ DTC_FLAGS += $(dtc-warning)
|
|||
else
|
||||
|
||||
# Disable noisy checks by default
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-unit_address_vs_reg)
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-simple_bus_reg)
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-unit_address_format)
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-pci_bridge)
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-pci_device_bus_num)
|
||||
DTC_FLAGS += $(call dtc-option,-Wno-pci_device_reg)
|
||||
DTC_FLAGS += -Wno-unit_address_vs_reg
|
||||
DTC_FLAGS += -Wno-simple_bus_reg
|
||||
DTC_FLAGS += -Wno-unit_address_format
|
||||
DTC_FLAGS += -Wno-pci_bridge
|
||||
DTC_FLAGS += -Wno-pci_device_bus_num
|
||||
DTC_FLAGS += -Wno-pci_device_reg
|
||||
|
||||
endif
|
||||
|
|
31
scripts/dtc/Makefile
Normal file
31
scripts/dtc/Makefile
Normal file
|
@ -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
|
18
scripts/dtc/Makefile.dtc
Normal file
18
scripts/dtc/Makefile.dtc
Normal file
|
@ -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)
|
849
scripts/dtc/checks.c
Normal file
849
scripts/dtc/checks.c
Normal file
|
@ -0,0 +1,849 @@
|
|||
/*
|
||||
* (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 (*check_fn)(struct check *c, struct dt_info *dti, struct node *node);
|
||||
|
||||
struct check {
|
||||
const char *name;
|
||||
check_fn fn;
|
||||
void *data;
|
||||
bool warn, error;
|
||||
enum checkstatus status;
|
||||
bool inprogress;
|
||||
int num_prereqs;
|
||||
struct check **prereq;
|
||||
};
|
||||
|
||||
#define CHECK_ENTRY(_nm, _fn, _d, _w, _e, ...) \
|
||||
static struct check *_nm##_prereqs[] = { __VA_ARGS__ }; \
|
||||
static struct check _nm = { \
|
||||
.name = #_nm, \
|
||||
.fn = (_fn), \
|
||||
.data = (_d), \
|
||||
.warn = (_w), \
|
||||
.error = (_e), \
|
||||
.status = UNCHECKED, \
|
||||
.num_prereqs = ARRAY_SIZE(_nm##_prereqs), \
|
||||
.prereq = _nm##_prereqs, \
|
||||
};
|
||||
#define WARNING(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, true, false, __VA_ARGS__)
|
||||
#define ERROR(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, false, true, __VA_ARGS__)
|
||||
#define CHECK(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, false, false, __VA_ARGS__)
|
||||
|
||||
#ifdef __GNUC__
|
||||
static inline void check_msg(struct check *c, struct dt_info *dti,
|
||||
const char *fmt, ...) __attribute__((format (printf, 3, 4)));
|
||||
#endif
|
||||
static inline void check_msg(struct check *c, struct dt_info *dti,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if ((c->warn && (quiet < 1))
|
||||
|| (c->error && (quiet < 2))) {
|
||||
fprintf(stderr, "%s: %s (%s): ",
|
||||
strcmp(dti->outname, "-") ? dti->outname : "<stdout>",
|
||||
(c->error) ? "ERROR" : "Warning", c->name);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#define FAIL(c, dti, ...) \
|
||||
do { \
|
||||
TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \
|
||||
(c)->status = FAILED; \
|
||||
check_msg((c), dti, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *child;
|
||||
|
||||
TRACE(c, "%s", node->fullpath);
|
||||
if (c->fn)
|
||||
c->fn(c, dti, node);
|
||||
|
||||
for_each_child(node, child)
|
||||
check_nodes_props(c, dti, child);
|
||||
}
|
||||
|
||||
static bool run_check(struct check *c, struct dt_info *dti)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
bool error = false;
|
||||
int i;
|
||||
|
||||
assert(!c->inprogress);
|
||||
|
||||
if (c->status != UNCHECKED)
|
||||
goto out;
|
||||
|
||||
c->inprogress = true;
|
||||
|
||||
for (i = 0; i < c->num_prereqs; i++) {
|
||||
struct check *prq = c->prereq[i];
|
||||
error = error || run_check(prq, dti);
|
||||
if (prq->status != PASSED) {
|
||||
c->status = PREREQ;
|
||||
check_msg(c, dti, "Failed prerequisite '%s'",
|
||||
c->prereq[i]->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->status != UNCHECKED)
|
||||
goto out;
|
||||
|
||||
check_nodes_props(c, dti, dt);
|
||||
|
||||
if (c->status == UNCHECKED)
|
||||
c->status = PASSED;
|
||||
|
||||
TRACE(c, "\tCompleted, status %d", c->status);
|
||||
|
||||
out:
|
||||
c->inprogress = false;
|
||||
if ((c->status != PASSED) && (c->error))
|
||||
error = true;
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility check functions
|
||||
*/
|
||||
|
||||
/* A check which always fails, for testing purposes only */
|
||||
static inline void check_always_fail(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
FAIL(c, dti, "always_fail check");
|
||||
}
|
||||
CHECK(always_fail, check_always_fail, NULL);
|
||||
|
||||
static void check_is_string(struct check *c, struct dt_info *dti,
|
||||
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, dti, "\"%s\" property in %s is not a string",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_STRING(nm, propname) \
|
||||
WARNING(nm, check_is_string, (propname))
|
||||
#define ERROR_IF_NOT_STRING(nm, propname) \
|
||||
ERROR(nm, check_is_string, (propname))
|
||||
|
||||
static void check_is_cell(struct check *c, struct dt_info *dti,
|
||||
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, dti, "\"%s\" property in %s is not a single cell",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_CELL(nm, propname) \
|
||||
WARNING(nm, check_is_cell, (propname))
|
||||
#define ERROR_IF_NOT_CELL(nm, propname) \
|
||||
ERROR(nm, check_is_cell, (propname))
|
||||
|
||||
/*
|
||||
* Structural check functions
|
||||
*/
|
||||
|
||||
static void check_duplicate_node_names(struct check *c, struct dt_info *dti,
|
||||
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, dti, "Duplicate node name %s",
|
||||
child->fullpath);
|
||||
}
|
||||
ERROR(duplicate_node_names, check_duplicate_node_names, NULL);
|
||||
|
||||
static void check_duplicate_property_names(struct check *c, struct dt_info *dti,
|
||||
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, dti, "Duplicate property name %s in %s",
|
||||
prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
ERROR(duplicate_property_names, check_duplicate_property_names, NULL);
|
||||
|
||||
#define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
|
||||
#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define DIGITS "0123456789"
|
||||
#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
|
||||
#define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-"
|
||||
|
||||
static void check_node_name_chars(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
int n = strspn(node->name, c->data);
|
||||
|
||||
if (n < strlen(node->name))
|
||||
FAIL(c, dti, "Bad character '%c' in node %s",
|
||||
node->name[n], node->fullpath);
|
||||
}
|
||||
ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@");
|
||||
|
||||
static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
int n = strspn(node->name, c->data);
|
||||
|
||||
if (n < node->basenamelen)
|
||||
FAIL(c, dti, "Character '%c' not recommended in node %s",
|
||||
node->name[n], node->fullpath);
|
||||
}
|
||||
CHECK(node_name_chars_strict, check_node_name_chars_strict, PROPNODECHARSSTRICT);
|
||||
|
||||
static void check_node_name_format(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
if (strchr(get_unitname(node), '@'))
|
||||
FAIL(c, dti, "Node %s has multiple '@' characters in name",
|
||||
node->fullpath);
|
||||
}
|
||||
ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
|
||||
|
||||
static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
const char *unitname = get_unitname(node);
|
||||
struct property *prop = get_property(node, "reg");
|
||||
|
||||
if (!prop) {
|
||||
prop = get_property(node, "ranges");
|
||||
if (prop && !prop->val.len)
|
||||
prop = NULL;
|
||||
}
|
||||
|
||||
if (prop) {
|
||||
if (!unitname[0])
|
||||
FAIL(c, dti, "Node %s has a reg or ranges property, but no unit name",
|
||||
node->fullpath);
|
||||
} else {
|
||||
if (unitname[0])
|
||||
FAIL(c, dti, "Node %s has a unit name, but no reg property",
|
||||
node->fullpath);
|
||||
}
|
||||
}
|
||||
WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL);
|
||||
|
||||
static void check_property_name_chars(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
int n = strspn(prop->name, c->data);
|
||||
|
||||
if (n < strlen(prop->name))
|
||||
FAIL(c, dti, "Bad character '%c' in property name \"%s\", node %s",
|
||||
prop->name[n], prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS);
|
||||
|
||||
static void check_property_name_chars_strict(struct check *c,
|
||||
struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
const char *name = prop->name;
|
||||
int n = strspn(name, c->data);
|
||||
|
||||
if (n == strlen(prop->name))
|
||||
continue;
|
||||
|
||||
/* Certain names are whitelisted */
|
||||
if (streq(name, "device_type"))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* # is only allowed at the beginning of property names not counting
|
||||
* the vendor prefix.
|
||||
*/
|
||||
if (name[n] == '#' && ((n == 0) || (name[n-1] == ','))) {
|
||||
name += n + 1;
|
||||
n = strspn(name, c->data);
|
||||
}
|
||||
if (n < strlen(name))
|
||||
FAIL(c, dti, "Character '%c' not recommended in property name \"%s\", node %s",
|
||||
name[n], prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
CHECK(property_name_chars_strict, check_property_name_chars_strict, PROPNODECHARSSTRICT);
|
||||
|
||||
#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 dt_info *dti,
|
||||
const char *label, struct node *node,
|
||||
struct property *prop, struct marker *mark)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
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, dti, "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 dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct label *l;
|
||||
struct property *prop;
|
||||
|
||||
for_each_label(node->labels, l)
|
||||
check_duplicate_label(c, dti, l->label, node, NULL, NULL);
|
||||
|
||||
for_each_property(node, prop) {
|
||||
struct marker *m = prop->val.markers;
|
||||
|
||||
for_each_label(prop->labels, l)
|
||||
check_duplicate_label(c, dti, l->label, node, prop, NULL);
|
||||
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
check_duplicate_label(c, dti, m->ref, node, prop, m);
|
||||
}
|
||||
}
|
||||
ERROR(duplicate_label, check_duplicate_label_node, NULL);
|
||||
|
||||
static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
|
||||
struct node *node, const char *propname)
|
||||
{
|
||||
struct node *root = dti->dt;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
cell_t phandle;
|
||||
|
||||
prop = get_property(node, propname);
|
||||
if (!prop)
|
||||
return 0;
|
||||
|
||||
if (prop->val.len != sizeof(cell_t)) {
|
||||
FAIL(c, dti, "%s has bad length (%d) %s property",
|
||||
node->fullpath, prop->val.len, prop->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, dti, "%s in %s is a reference to another node",
|
||||
prop->name, node->fullpath);
|
||||
}
|
||||
/* 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
|
||||
* we treat it as having no phandle data for now. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
phandle = propval_cell(prop);
|
||||
|
||||
if ((phandle == 0) || (phandle == -1)) {
|
||||
FAIL(c, dti, "%s has bad value (0x%x) in %s property",
|
||||
node->fullpath, phandle, prop->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return phandle;
|
||||
}
|
||||
|
||||
static void check_explicit_phandles(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *root = dti->dt;
|
||||
struct node *other;
|
||||
cell_t phandle, linux_phandle;
|
||||
|
||||
/* Nothing should have assigned phandles yet */
|
||||
assert(!node->phandle);
|
||||
|
||||
phandle = check_phandle_prop(c, dti, node, "phandle");
|
||||
|
||||
linux_phandle = check_phandle_prop(c, dti, node, "linux,phandle");
|
||||
|
||||
if (!phandle && !linux_phandle)
|
||||
/* No valid phandles; nothing further to check */
|
||||
return;
|
||||
|
||||
if (linux_phandle && phandle && (phandle != linux_phandle))
|
||||
FAIL(c, dti, "%s has mismatching 'phandle' and 'linux,phandle'"
|
||||
" properties", node->fullpath);
|
||||
|
||||
if (linux_phandle && !phandle)
|
||||
phandle = linux_phandle;
|
||||
|
||||
other = get_node_by_phandle(root, phandle);
|
||||
if (other && (other != node)) {
|
||||
FAIL(c, dti, "%s has duplicated phandle 0x%x (seen before at %s)",
|
||||
node->fullpath, phandle, other->fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
node->phandle = phandle;
|
||||
}
|
||||
ERROR(explicit_phandles, check_explicit_phandles, NULL);
|
||||
|
||||
static void check_name_properties(struct check *c, struct dt_info *dti,
|
||||
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, dti, "\"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");
|
||||
ERROR(name_properties, check_name_properties, NULL, &name_is_string);
|
||||
|
||||
/*
|
||||
* Reference fixup functions
|
||||
*/
|
||||
|
||||
static void fixup_phandle_references(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, 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) {
|
||||
if (!(dti->dtsflags & DTSF_PLUGIN))
|
||||
FAIL(c, dti, "Reference to non-existent node or "
|
||||
"label \"%s\"\n", m->ref);
|
||||
else /* mark the entry as unresolved */
|
||||
*((cell_t *)(prop->val.val + m->offset)) =
|
||||
cpu_to_fdt32(0xffffffff);
|
||||
continue;
|
||||
}
|
||||
|
||||
phandle = get_node_phandle(dt, refnode);
|
||||
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
ERROR(phandle_references, fixup_phandle_references, NULL,
|
||||
&duplicate_node_names, &explicit_phandles);
|
||||
|
||||
static void fixup_path_references(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, 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, dti, "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, 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 dt_info *dti,
|
||||
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, fixup_addr_size_cells, 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 dt_info *dti,
|
||||
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, dti, "Root node has a \"reg\" property");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop->val.len == 0)
|
||||
FAIL(c, dti, "\"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 (!entrylen || (prop->val.len % entrylen) != 0)
|
||||
FAIL(c, dti, "\"reg\" property in %s has invalid length (%d bytes) "
|
||||
"(#address-cells == %d, #size-cells == %d)",
|
||||
node->fullpath, prop->val.len, addr_cells, size_cells);
|
||||
}
|
||||
WARNING(reg_format, check_reg_format, NULL, &addr_size_cells);
|
||||
|
||||
static void check_ranges_format(struct check *c, struct dt_info *dti,
|
||||
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, dti, "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, dti, "%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, dti, "%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, dti, "\"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);
|
||||
}
|
||||
}
|
||||
WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells);
|
||||
|
||||
/*
|
||||
* Style checks
|
||||
*/
|
||||
static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti,
|
||||
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, dti, "Relying on default #address-cells value for %s",
|
||||
node->fullpath);
|
||||
|
||||
if (node->parent->size_cells == -1)
|
||||
FAIL(c, dti, "Relying on default #size-cells value for %s",
|
||||
node->fullpath);
|
||||
}
|
||||
WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL,
|
||||
&addr_size_cells);
|
||||
|
||||
static void check_obsolete_chosen_interrupt_controller(struct check *c,
|
||||
struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *chosen;
|
||||
struct property *prop;
|
||||
|
||||
if (node != dt)
|
||||
return;
|
||||
|
||||
|
||||
chosen = get_node_by_path(dt, "/chosen");
|
||||
if (!chosen)
|
||||
return;
|
||||
|
||||
prop = get_property(chosen, "interrupt-controller");
|
||||
if (prop)
|
||||
FAIL(c, dti, "/chosen has obsolete \"interrupt-controller\" "
|
||||
"property");
|
||||
}
|
||||
WARNING(obsolete_chosen_interrupt_controller,
|
||||
check_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,
|
||||
|
||||
&property_name_chars_strict,
|
||||
&node_name_chars_strict,
|
||||
|
||||
&addr_size_cells, ®_format, &ranges_format,
|
||||
|
||||
&unit_address_vs_reg,
|
||||
|
||||
&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 *arg)
|
||||
{
|
||||
int i;
|
||||
const char *name = arg;
|
||||
bool enable = true;
|
||||
|
||||
if ((strncmp(arg, "no-", 3) == 0)
|
||||
|| (strncmp(arg, "no_", 3) == 0)) {
|
||||
name = arg + 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(bool force, struct dt_info *dti)
|
||||
{
|
||||
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, dti);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
269
scripts/dtc/data.c
Normal file
269
scripts/dtc/data.c
Normal file
|
@ -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, len + 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);
|
||||
}
|
||||
|
||||
bool data_is_one_string(struct data d)
|
||||
{
|
||||
int i;
|
||||
int len = d.len;
|
||||
|
||||
if (len == 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < len-1; i++)
|
||||
if (d.val[i] == '\0')
|
||||
return false;
|
||||
|
||||
if (d.val[len-1] != '\0')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
311
scripts/dtc/dtc-lexer.l
Normal file
311
scripts/dtc/dtc-lexer.l
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* (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 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;
|
||||
extern bool treesource_error;
|
||||
|
||||
/* 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 bool pop_input_file(void);
|
||||
#ifdef __GNUC__
|
||||
static void lexical_error(const char *fmt, ...)
|
||||
__attribute__((format (printf, 1, 2)));
|
||||
#else
|
||||
static void lexical_error(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
<*>"/include/"{WS}*{STRING} {
|
||||
char *name = strchr(yytext, '\"') + 1;
|
||||
yytext[yyleng-1] = '\0';
|
||||
push_input_file(name);
|
||||
}
|
||||
|
||||
<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? {
|
||||
char *line, *fnstart, *fnend;
|
||||
struct data fn;
|
||||
/* skip text before line # */
|
||||
line = yytext;
|
||||
while (!isdigit((unsigned char)*line))
|
||||
line++;
|
||||
|
||||
/* regexp ensures that first and list "
|
||||
* in the whole yytext are those at
|
||||
* beginning and end of the filename string */
|
||||
fnstart = memchr(yytext, '"', yyleng);
|
||||
for (fnend = yytext + yyleng - 1;
|
||||
*fnend != '"'; fnend--)
|
||||
;
|
||||
assert(fnstart && fnend && (fnend > fnstart));
|
||||
|
||||
fn = data_copy_escape_string(fnstart + 1,
|
||||
fnend - fnstart - 1);
|
||||
|
||||
/* Don't allow nuls in filenames */
|
||||
if (memchr(fn.val, '\0', fn.len - 1))
|
||||
lexical_error("nul in line number directive");
|
||||
|
||||
/* -1 since #line is the number of the next line */
|
||||
srcpos_set_line(xstrdup(fn.val), atoi(line) - 1);
|
||||
data_free(fn);
|
||||
}
|
||||
|
||||
<*><<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;
|
||||
}
|
||||
|
||||
<*>"/plugin/" {
|
||||
DPRINT("Keyword: /plugin/\n");
|
||||
return DT_PLUGIN;
|
||||
}
|
||||
|
||||
<*>"/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)? {
|
||||
char *e;
|
||||
DPRINT("Integer Literal: '%s'\n", yytext);
|
||||
|
||||
errno = 0;
|
||||
yylval.integer = strtoull(yytext, &e, 0);
|
||||
|
||||
if (*e && e[strspn(e, "UL")]) {
|
||||
lexical_error("Bad integer literal '%s'",
|
||||
yytext);
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
lexical_error("Integer literal '%s' out of range",
|
||||
yytext);
|
||||
else
|
||||
/* ERANGE is the only strtoull error triggerable
|
||||
* by strings matching the pattern */
|
||||
assert(errno == 0);
|
||||
return DT_LITERAL;
|
||||
}
|
||||
|
||||
<*>{CHAR_LITERAL} {
|
||||
struct data d;
|
||||
DPRINT("Character literal: %s\n", yytext);
|
||||
|
||||
d = data_copy_escape_string(yytext+1, yyleng-2);
|
||||
if (d.len == 1) {
|
||||
lexical_error("Empty character literal");
|
||||
yylval.integer = 0;
|
||||
} else {
|
||||
yylval.integer = (unsigned char)d.val[0];
|
||||
|
||||
if (d.len > 2)
|
||||
lexical_error("Character literal has %d"
|
||||
" characters instead of 1",
|
||||
d.len - 1);
|
||||
}
|
||||
|
||||
data_free(d);
|
||||
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 bool pop_input_file(void)
|
||||
{
|
||||
if (srcfile_pop() == 0)
|
||||
return false;
|
||||
|
||||
yypop_buffer_state();
|
||||
yyin = current_srcfile->f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lexical_error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
srcpos_verror(&yylloc, "Lexical error", fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
treesource_error = true;
|
||||
}
|
2260
scripts/dtc/dtc-lexer.lex.c_shipped
Normal file
2260
scripts/dtc/dtc-lexer.lex.c_shipped
Normal file
File diff suppressed because it is too large
Load diff
2301
scripts/dtc/dtc-parser.tab.c_shipped
Normal file
2301
scripts/dtc/dtc-parser.tab.c_shipped
Normal file
File diff suppressed because it is too large
Load diff
123
scripts/dtc/dtc-parser.tab.h_shipped
Normal file
123
scripts/dtc/dtc-parser.tab.h_shipped
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* A Bison parser, made by GNU Bison 3.0.2. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2013 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. */
|
||||
|
||||
#ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED
|
||||
# define YY_YY_DTC_PARSER_TAB_H_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
# define YYDEBUG 0
|
||||
#endif
|
||||
#if YYDEBUG
|
||||
extern int yydebug;
|
||||
#endif
|
||||
|
||||
/* Token type. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
DT_V1 = 258,
|
||||
DT_PLUGIN = 259,
|
||||
DT_MEMRESERVE = 260,
|
||||
DT_LSHIFT = 261,
|
||||
DT_RSHIFT = 262,
|
||||
DT_LE = 263,
|
||||
DT_GE = 264,
|
||||
DT_EQ = 265,
|
||||
DT_NE = 266,
|
||||
DT_AND = 267,
|
||||
DT_OR = 268,
|
||||
DT_BITS = 269,
|
||||
DT_DEL_PROP = 270,
|
||||
DT_DEL_NODE = 271,
|
||||
DT_PROPNODENAME = 272,
|
||||
DT_LITERAL = 273,
|
||||
DT_CHAR_LITERAL = 274,
|
||||
DT_BYTE = 275,
|
||||
DT_STRING = 276,
|
||||
DT_LABEL = 277,
|
||||
DT_REF = 278,
|
||||
DT_INCBIN = 279
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 39 "dtc-parser.y" /* yacc.c:1909 */
|
||||
|
||||
char *propnodename;
|
||||
char *labelref;
|
||||
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;
|
||||
unsigned int flags;
|
||||
|
||||
#line 99 "dtc-parser.tab.h" /* yacc.c:1909 */
|
||||
};
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
/* Location type. */
|
||||
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
|
||||
typedef struct YYLTYPE YYLTYPE;
|
||||
struct YYLTYPE
|
||||
{
|
||||
int first_line;
|
||||
int first_column;
|
||||
int last_line;
|
||||
int last_column;
|
||||
};
|
||||
# define YYLTYPE_IS_DECLARED 1
|
||||
# define YYLTYPE_IS_TRIVIAL 1
|
||||
#endif
|
||||
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
extern YYLTYPE yylloc;
|
||||
int yyparse (void);
|
||||
|
||||
#endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED */
|
519
scripts/dtc/dtc-parser.y
Normal file
519
scripts/dtc/dtc-parser.y
Normal file
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* (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 <inttypes.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
extern int yylex(void);
|
||||
extern void yyerror(char const *s);
|
||||
#define ERROR(loc, ...) \
|
||||
do { \
|
||||
srcpos_error((loc), "Error", __VA_ARGS__); \
|
||||
treesource_error = true; \
|
||||
} while (0)
|
||||
|
||||
extern struct dt_info *parser_output;
|
||||
extern bool treesource_error;
|
||||
%}
|
||||
|
||||
%union {
|
||||
char *propnodename;
|
||||
char *labelref;
|
||||
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;
|
||||
unsigned int flags;
|
||||
}
|
||||
|
||||
%token DT_V1
|
||||
%token DT_PLUGIN
|
||||
%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 <integer> DT_LITERAL
|
||||
%token <integer> DT_CHAR_LITERAL
|
||||
%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 <flags> header
|
||||
%type <flags> headers
|
||||
%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:
|
||||
headers memreserves devicetree
|
||||
{
|
||||
parser_output = build_dt_info($1, $2, $3,
|
||||
guess_boot_cpuid($3));
|
||||
}
|
||||
;
|
||||
|
||||
header:
|
||||
DT_V1 ';'
|
||||
{
|
||||
$$ = DTSF_V1;
|
||||
}
|
||||
| DT_V1 ';' DT_PLUGIN ';'
|
||||
{
|
||||
$$ = DTSF_V1 | DTSF_PLUGIN;
|
||||
}
|
||||
;
|
||||
|
||||
headers:
|
||||
header
|
||||
| header headers
|
||||
{
|
||||
if ($2 != $1)
|
||||
ERROR(&@2, "Header flags don't match earlier ones");
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
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_LABEL DT_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (target) {
|
||||
add_label(&target->labels, $2);
|
||||
merge_nodes(target, $4);
|
||||
} else
|
||||
ERROR(&@3, "Label or path %s not found", $3);
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $2);
|
||||
|
||||
if (target)
|
||||
merge_nodes(target, $3);
|
||||
else
|
||||
ERROR(&@2, "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)
|
||||
delete_node(target);
|
||||
else
|
||||
ERROR(&@3, "Label or path %s not found", $3);
|
||||
|
||||
|
||||
$$ = $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)
|
||||
die("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 '<'
|
||||
{
|
||||
unsigned long long bits;
|
||||
|
||||
bits = $2;
|
||||
|
||||
if ((bits != 8) && (bits != 16) &&
|
||||
(bits != 32) && (bits != 64)) {
|
||||
ERROR(&@2, "Array elements must be"
|
||||
" 8, 16, 32 or 64-bits");
|
||||
bits = 32;
|
||||
}
|
||||
|
||||
$$.data = empty_data;
|
||||
$$.bits = bits;
|
||||
}
|
||||
| '<'
|
||||
{
|
||||
$$.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))
|
||||
ERROR(&@2, "Value out of range for"
|
||||
" %d-bit array element", $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
|
||||
ERROR(&@2, "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
|
||||
| DT_CHAR_LITERAL
|
||||
| '(' 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
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 / $3;
|
||||
} else {
|
||||
ERROR(&@$, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| integer_mul '%' integer_unary
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 % $3;
|
||||
} else {
|
||||
ERROR(&@$, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| 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
|
||||
{
|
||||
ERROR(&@2, "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 yyerror(char const *s)
|
||||
{
|
||||
ERROR(&yylloc, "%s", s);
|
||||
}
|
366
scripts/dtc/dtc.c
Normal file
366
scripts/dtc/dtc.c
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* (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 <sys/stat.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.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 alignsize; /* Additional padding to blob accroding to the alignsize */
|
||||
int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */
|
||||
int generate_symbols; /* enable symbols & fixup support */
|
||||
int generate_fixups; /* suppress generation of fixups on symbol support */
|
||||
int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
|
||||
static int is_power_of_2(int x)
|
||||
{
|
||||
return (x > 0) && ((x & (x - 1)) == 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
#define FDT_VERSION(version) _FDT_VERSION(version)
|
||||
#define _FDT_VERSION(version) #version
|
||||
static const char usage_synopsis[] = "dtc [options] <input file>";
|
||||
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@Ahv";
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"quiet", no_argument, NULL, 'q'},
|
||||
{"in-format", a_argument, NULL, 'I'},
|
||||
{"out", a_argument, NULL, 'o'},
|
||||
{"out-format", a_argument, NULL, 'O'},
|
||||
{"out-version", a_argument, NULL, 'V'},
|
||||
{"out-dependency", a_argument, NULL, 'd'},
|
||||
{"reserve", a_argument, NULL, 'R'},
|
||||
{"space", a_argument, NULL, 'S'},
|
||||
{"pad", a_argument, NULL, 'p'},
|
||||
{"align", a_argument, NULL, 'a'},
|
||||
{"boot-cpu", a_argument, NULL, 'b'},
|
||||
{"force", no_argument, NULL, 'f'},
|
||||
{"include", a_argument, NULL, 'i'},
|
||||
{"sort", no_argument, NULL, 's'},
|
||||
{"phandle", a_argument, NULL, 'H'},
|
||||
{"warning", a_argument, NULL, 'W'},
|
||||
{"error", a_argument, NULL, 'E'},
|
||||
{"symbols", no_argument, NULL, '@'},
|
||||
{"auto-alias", no_argument, NULL, 'A'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, no_argument, NULL, 0x0},
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"\n\tQuiet: -q suppress warnings, -qq errors, -qqq all",
|
||||
"\n\tInput formats are:\n"
|
||||
"\t\tdts - device tree source text\n"
|
||||
"\t\tdtb - device tree blob\n"
|
||||
"\t\tfs - /proc/device-tree style directory",
|
||||
"\n\tOutput file",
|
||||
"\n\tOutput formats are:\n"
|
||||
"\t\tdts - device tree source text\n"
|
||||
"\t\tdtb - device tree blob\n"
|
||||
"\t\tasm - assembler source",
|
||||
"\n\tBlob version to produce, defaults to "FDT_VERSION(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
|
||||
"\n\tOutput dependency file",
|
||||
"\n\tMake space for <number> reserve map entries (for dtb and asm output)",
|
||||
"\n\tMake the blob at least <bytes> long (extra space)",
|
||||
"\n\tAdd padding to the blob of <bytes> long (extra space)",
|
||||
"\n\tMake the blob align to the <bytes> (extra space)",
|
||||
"\n\tSet the physical boot cpu",
|
||||
"\n\tTry to produce output even if the input tree has errors",
|
||||
"\n\tAdd a path to search for include files",
|
||||
"\n\tSort nodes and properties before outputting (useful for comparing trees)",
|
||||
"\n\tValid phandle formats are:\n"
|
||||
"\t\tlegacy - \"linux,phandle\" properties only\n"
|
||||
"\t\tepapr - \"phandle\" properties only\n"
|
||||
"\t\tboth - Both \"linux,phandle\" and \"phandle\" properties",
|
||||
"\n\tEnable/disable warnings (prefix with \"no-\")",
|
||||
"\n\tEnable/disable errors (prefix with \"no-\")",
|
||||
"\n\tEnable generation of symbols",
|
||||
"\n\tEnable auto-alias of labels",
|
||||
"\n\tPrint this help and exit",
|
||||
"\n\tPrint version and exit",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *guess_type_by_name(const char *fname, const char *fallback)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
s = strrchr(fname, '.');
|
||||
if (s == NULL)
|
||||
return fallback;
|
||||
if (!strcasecmp(s, ".dts"))
|
||||
return "dts";
|
||||
if (!strcasecmp(s, ".dtb"))
|
||||
return "dtb";
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static const char *guess_input_format(const char *fname, const char *fallback)
|
||||
{
|
||||
struct stat statbuf;
|
||||
uint32_t magic;
|
||||
FILE *f;
|
||||
|
||||
if (stat(fname, &statbuf) != 0)
|
||||
return fallback;
|
||||
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
return "fs";
|
||||
|
||||
if (!S_ISREG(statbuf.st_mode))
|
||||
return fallback;
|
||||
|
||||
f = fopen(fname, "r");
|
||||
if (f == NULL)
|
||||
return fallback;
|
||||
if (fread(&magic, 4, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return fallback;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
magic = fdt32_to_cpu(magic);
|
||||
if (magic == FDT_MAGIC)
|
||||
return "dtb";
|
||||
|
||||
return guess_type_by_name(fname, fallback);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct dt_info *dti;
|
||||
const char *inform = NULL;
|
||||
const char *outform = NULL;
|
||||
const char *outname = "-";
|
||||
const char *depname = NULL;
|
||||
bool force = false, sort = false;
|
||||
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;
|
||||
alignsize = 0;
|
||||
|
||||
while ((opt = util_getopt_long()) != 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 'a':
|
||||
alignsize = strtol(optarg, NULL, 0);
|
||||
if (!is_power_of_2(alignsize))
|
||||
die("Invalid argument \"%d\" to -a option\n",
|
||||
alignsize);
|
||||
break;
|
||||
case 'f':
|
||||
force = true;
|
||||
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':
|
||||
util_version();
|
||||
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 = true;
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
parse_checks_option(true, false, optarg);
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
parse_checks_option(false, true, optarg);
|
||||
break;
|
||||
|
||||
case '@':
|
||||
generate_symbols = 1;
|
||||
break;
|
||||
case 'A':
|
||||
auto_label_aliases = 1;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage(NULL);
|
||||
default:
|
||||
usage("unknown option");
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > (optind+1))
|
||||
usage("missing files");
|
||||
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 (depname) {
|
||||
depfile = fopen(depname, "w");
|
||||
if (!depfile)
|
||||
die("Couldn't open dependency file %s: %s\n", depname,
|
||||
strerror(errno));
|
||||
fprintf(depfile, "%s:", outname);
|
||||
}
|
||||
|
||||
if (inform == NULL)
|
||||
inform = guess_input_format(arg, "dts");
|
||||
if (outform == NULL) {
|
||||
outform = guess_type_by_name(outname, NULL);
|
||||
if (outform == NULL) {
|
||||
if (streq(inform, "dts"))
|
||||
outform = "dtb";
|
||||
else
|
||||
outform = "dts";
|
||||
}
|
||||
}
|
||||
if (streq(inform, "dts"))
|
||||
dti = dt_from_source(arg);
|
||||
else if (streq(inform, "fs"))
|
||||
dti = dt_from_fs(arg);
|
||||
else if(streq(inform, "dtb"))
|
||||
dti = dt_from_blob(arg);
|
||||
else
|
||||
die("Unknown input format \"%s\"\n", inform);
|
||||
|
||||
dti->outname = outname;
|
||||
|
||||
if (depfile) {
|
||||
fputc('\n', depfile);
|
||||
fclose(depfile);
|
||||
}
|
||||
|
||||
if (cmdline_boot_cpuid != -1)
|
||||
dti->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
|
||||
fill_fullpaths(dti->dt, "");
|
||||
process_checks(force, dti);
|
||||
|
||||
/* on a plugin, generate by default */
|
||||
if (dti->dtsflags & DTSF_PLUGIN) {
|
||||
generate_fixups = 1;
|
||||
}
|
||||
|
||||
if (auto_label_aliases)
|
||||
generate_label_tree(dti, "aliases", false);
|
||||
|
||||
if (generate_symbols)
|
||||
generate_label_tree(dti, "__symbols__", true);
|
||||
|
||||
if (generate_fixups) {
|
||||
generate_fixups_tree(dti, "__fixups__");
|
||||
generate_local_fixups_tree(dti, "__local_fixups__");
|
||||
}
|
||||
|
||||
if (sort)
|
||||
sort_tree(dti);
|
||||
|
||||
if (streq(outname, "-")) {
|
||||
outf = stdout;
|
||||
} else {
|
||||
outf = fopen(outname, "wb");
|
||||
if (! outf)
|
||||
die("Couldn't open output file %s: %s\n",
|
||||
outname, strerror(errno));
|
||||
}
|
||||
|
||||
if (streq(outform, "dts")) {
|
||||
dt_to_source(outf, dti);
|
||||
} else if (streq(outform, "dtb")) {
|
||||
dt_to_blob(outf, dti, outversion);
|
||||
} else if (streq(outform, "asm")) {
|
||||
dt_to_asm(outf, dti, outversion);
|
||||
} else if (streq(outform, "null")) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
die("Unknown output format \"%s\"\n", outform);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
285
scripts/dtc/dtc.h
Normal file
285
scripts/dtc/dtc.h
Normal file
|
@ -0,0 +1,285 @@
|
|||
#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(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#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 alignsize; /* Additional padding to blob accroding to the alignsize */
|
||||
extern int phandle_format; /* Use linux,phandle or phandle properties */
|
||||
extern int generate_symbols; /* generate symbols for nodes with labels */
|
||||
extern int generate_fixups; /* generate fixups */
|
||||
extern int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
|
||||
#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))
|
||||
|
||||
/* 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){ 0 /* 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);
|
||||
|
||||
bool data_is_one_string(struct data d);
|
||||
|
||||
/* DT constraints */
|
||||
|
||||
#define MAX_PROPNAME_LEN 31
|
||||
#define MAX_NODENAME_LEN 31
|
||||
|
||||
/* Live trees */
|
||||
struct label {
|
||||
bool deleted;
|
||||
char *label;
|
||||
struct label *next;
|
||||
};
|
||||
|
||||
struct property {
|
||||
bool deleted;
|
||||
char *name;
|
||||
struct data val;
|
||||
|
||||
struct property *next;
|
||||
|
||||
struct label *labels;
|
||||
};
|
||||
|
||||
struct node {
|
||||
bool 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);
|
||||
void append_to_property(struct node *node,
|
||||
char *name, const void *data, int len);
|
||||
|
||||
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 dt_info {
|
||||
unsigned int dtsflags;
|
||||
struct reserve_info *reservelist;
|
||||
uint32_t boot_cpuid_phys;
|
||||
struct node *dt; /* the device tree */
|
||||
const char *outname; /* filename being written to, "-" for stdout */
|
||||
};
|
||||
|
||||
/* DTS version flags definitions */
|
||||
#define DTSF_V1 0x0001 /* /dts-v1/ */
|
||||
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
|
||||
|
||||
struct dt_info *build_dt_info(unsigned int dtsflags,
|
||||
struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys);
|
||||
void sort_tree(struct dt_info *dti);
|
||||
void generate_label_tree(struct dt_info *dti, char *name, bool allocph);
|
||||
void generate_fixups_tree(struct dt_info *dti, char *name);
|
||||
void generate_local_fixups_tree(struct dt_info *dti, char *name);
|
||||
|
||||
/* Checks */
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *arg);
|
||||
void process_checks(bool force, struct dt_info *dti);
|
||||
|
||||
/* Flattened trees */
|
||||
|
||||
void dt_to_blob(FILE *f, struct dt_info *dti, int version);
|
||||
void dt_to_asm(FILE *f, struct dt_info *dti, int version);
|
||||
|
||||
struct dt_info *dt_from_blob(const char *fname);
|
||||
|
||||
/* Tree source */
|
||||
|
||||
void dt_to_source(FILE *f, struct dt_info *dti);
|
||||
struct dt_info *dt_from_source(const char *f);
|
||||
|
||||
/* FS trees */
|
||||
|
||||
struct dt_info *dt_from_fs(const char *dirname);
|
||||
|
||||
#endif /* _DTC_H */
|
946
scripts/dtc/flattree.c
Normal file
946
scripts/dtc/flattree.c
Normal file
|
@ -0,0 +1,946 @@
|
|||
/*
|
||||
* (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;
|
||||
bool seen_name_prop = false;
|
||||
|
||||
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 = true;
|
||||
|
||||
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 dt_info *dti, 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(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
|
||||
bin_emit_cell(&dtbuf, FDT_END);
|
||||
|
||||
reservebuf = flatten_reserve_list(dti->reservelist, vi);
|
||||
|
||||
/* Make header */
|
||||
make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
|
||||
dti->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) {
|
||||
padlen = 0;
|
||||
if (quiet < 1)
|
||||
fprintf(stderr,
|
||||
"Warning: blob size %d >= minimum size %d\n",
|
||||
fdt32_to_cpu(fdt.totalsize), minsize);
|
||||
}
|
||||
}
|
||||
|
||||
if (padsize > 0)
|
||||
padlen = padsize;
|
||||
|
||||
if (alignsize > 0)
|
||||
padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize)
|
||||
- fdt32_to_cpu(fdt.totalsize);
|
||||
|
||||
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 dt_info *dti, 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, dti->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 = dti->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(dti->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);
|
||||
}
|
||||
if (alignsize > 0)
|
||||
asm_emit_align(f, alignsize);
|
||||
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);
|
||||
|
||||
if (node->name != flatname) {
|
||||
free(flatname);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
struct dt_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 < off_str) || (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 < off_dt) || (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_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
|
||||
}
|
90
scripts/dtc/fstree.c
Normal file
90
scripts/dtc/fstree.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* (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 *tmpname;
|
||||
|
||||
if (streq(de->d_name, ".")
|
||||
|| streq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
tmpname = join_path(dirname, de->d_name);
|
||||
|
||||
if (lstat(tmpname, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpname, strerror(errno));
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
struct property *prop;
|
||||
FILE *pfile;
|
||||
|
||||
pfile = fopen(tmpname, "rb");
|
||||
if (! pfile) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Cannot open %s: %s\n",
|
||||
tmpname, 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(tmpname);
|
||||
newchild = name_node(newchild, xstrdup(de->d_name));
|
||||
add_child(tree, newchild);
|
||||
}
|
||||
|
||||
free(tmpname);
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return tree;
|
||||
}
|
||||
|
||||
struct dt_info *dt_from_fs(const char *dirname)
|
||||
{
|
||||
struct node *tree;
|
||||
|
||||
tree = read_fstree(dirname);
|
||||
tree = name_node(tree, "");
|
||||
|
||||
return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
|
||||
}
|
11
scripts/dtc/libfdt/Makefile.libfdt
Normal file
11
scripts/dtc/libfdt/Makefile.libfdt
Normal file
|
@ -0,0 +1,11 @@
|
|||
# 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 \
|
||||
fdt_addresses.c fdt_overlay.c
|
||||
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
|
251
scripts/dtc/libfdt/fdt.c
Normal file
251
scripts/dtc/libfdt/fdt.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
|
||||
if ((absoffset < offset)
|
||||
|| ((absoffset + len) < absoffset)
|
||||
|| (absoffset + len) > fdt_totalsize(fdt))
|
||||
return NULL;
|
||||
|
||||
if (fdt_version(fdt) >= 0x11)
|
||||
if (((offset + len) < offset)
|
||||
|| ((offset + len) > fdt_size_dt_struct(fdt)))
|
||||
return NULL;
|
||||
|
||||
return _fdt_offset_ptr(fdt, offset);
|
||||
}
|
||||
|
||||
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||
{
|
||||
const fdt32_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;
|
||||
}
|
||||
|
||||
int fdt_first_subnode(const void *fdt, int offset)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
if (offset < 0 || depth != 1)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_next_subnode(const void *fdt, int offset)
|
||||
{
|
||||
int depth = 1;
|
||||
|
||||
/*
|
||||
* With respect to the parent, the depth of the next subnode will be
|
||||
* the same as the last.
|
||||
*/
|
||||
do {
|
||||
offset = fdt_next_node(fdt, offset, &depth);
|
||||
if (offset < 0 || depth < 1)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
} while (depth > 1);
|
||||
|
||||
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;
|
||||
}
|
111
scripts/dtc/libfdt/fdt.h
Normal file
111
scripts/dtc/libfdt/fdt.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef _FDT_H
|
||||
#define _FDT_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct fdt_header {
|
||||
fdt32_t magic; /* magic word FDT_MAGIC */
|
||||
fdt32_t totalsize; /* total size of DT block */
|
||||
fdt32_t off_dt_struct; /* offset to structure */
|
||||
fdt32_t off_dt_strings; /* offset to strings */
|
||||
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
fdt32_t version; /* format version */
|
||||
fdt32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
fdt32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
fdt32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
struct fdt_reserve_entry {
|
||||
fdt64_t address;
|
||||
fdt64_t size;
|
||||
};
|
||||
|
||||
struct fdt_node_header {
|
||||
fdt32_t tag;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct fdt_property {
|
||||
fdt32_t tag;
|
||||
fdt32_t len;
|
||||
fdt32_t nameoff;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY */
|
||||
|
||||
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
|
||||
#define FDT_TAGSIZE sizeof(fdt32_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(fdt32_t))
|
||||
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
|
||||
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
|
||||
#define FDT_V16_SIZE FDT_V3_SIZE
|
||||
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
|
||||
|
||||
#endif /* _FDT_H */
|
84
scripts/dtc/libfdt/fdt_empty_tree.c
Normal file
84
scripts/dtc/libfdt/fdt_empty_tree.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
703
scripts/dtc/libfdt/fdt_ro.c
Normal file
703
scripts/dtc/libfdt/fdt_ro.c
Normal file
|
@ -0,0 +1,703 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
uint32_t fdt_get_max_phandle(const void *fdt)
|
||||
{
|
||||
uint32_t max_phandle = 0;
|
||||
int offset;
|
||||
|
||||
for (offset = fdt_next_node(fdt, -1, NULL);;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
uint32_t phandle;
|
||||
|
||||
if (offset == -FDT_ERR_NOTFOUND)
|
||||
return max_phandle;
|
||||
|
||||
if (offset < 0)
|
||||
return (uint32_t)-1;
|
||||
|
||||
phandle = fdt_get_phandle(fdt, offset);
|
||||
if (phandle == (uint32_t)-1)
|
||||
continue;
|
||||
|
||||
if (phandle > max_phandle)
|
||||
max_phandle = phandle;
|
||||
}
|
||||
|
||||
return 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_namelen(const void *fdt, const char *path, int namelen)
|
||||
{
|
||||
const char *end = path + namelen;
|
||||
const char *p = path;
|
||||
int offset = 0;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
/* see if we have an alias */
|
||||
if (*path != '/') {
|
||||
const char *q = memchr(path, '/', end - p);
|
||||
|
||||
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 < end) {
|
||||
const char *q;
|
||||
|
||||
while (*p == '/') {
|
||||
p++;
|
||||
if (p == end)
|
||||
return offset;
|
||||
}
|
||||
q = memchr(p, '/', end - p);
|
||||
if (! q)
|
||||
q = end;
|
||||
|
||||
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_path_offset(const void *fdt, const char *path)
|
||||
{
|
||||
return fdt_path_offset_namelen(fdt, path, strlen(path));
|
||||
}
|
||||
|
||||
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 fdt32_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() */
|
||||
}
|
||||
|
||||
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_stringlist_count(const void *fdt, int nodeoffset, const char *property)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length, count = 0;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
list += length;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
||||
const char *string)
|
||||
{
|
||||
int length, len, idx = 0;
|
||||
const char *list, *end;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
len = strlen(string) + 1;
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
if (length == len && memcmp(list, string, length) == 0)
|
||||
return idx;
|
||||
|
||||
list += length;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
||||
const char *property, int idx,
|
||||
int *lenp)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list) {
|
||||
if (lenp)
|
||||
*lenp = length;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end) {
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_BADVALUE;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
if (lenp)
|
||||
*lenp = length - 1;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
list += length;
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_NOTFOUND;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return !fdt_stringlist_contains(prop, len, compatible);
|
||||
}
|
||||
|
||||
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() */
|
||||
}
|
491
scripts/dtc/libfdt/fdt_rw.c
Normal file
491
scripts/dtc/libfdt/fdt_rw.c
Normal file
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* 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 ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
|
||||
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);
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
if (n >= fdt_num_mem_rsv(fdt))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
return _fdt_splice_mem_rsv(fdt, re, 1, 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;
|
||||
|
||||
if (len)
|
||||
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;
|
||||
fdt32_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 = (fdt32_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;
|
||||
}
|
102
scripts/dtc/libfdt/fdt_strerror.c
Normal file
102
scripts/dtc/libfdt/fdt_strerror.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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_BADPHANDLE),
|
||||
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),
|
||||
FDT_ERRTABENT(FDT_ERR_INTERNAL),
|
||||
FDT_ERRTABENT(FDT_ERR_BADNCELLS),
|
||||
FDT_ERRTABENT(FDT_ERR_BADVALUE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
|
||||
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
|
||||
};
|
||||
#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>";
|
||||
}
|
288
scripts/dtc/libfdt/fdt_sw.c
Normal file
288
scripts/dtc/libfdt/fdt_sw.c
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* 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_resize(void *fdt, void *buf, int bufsize)
|
||||
{
|
||||
size_t headsize, tailsize;
|
||||
char *oldtail, *newtail;
|
||||
|
||||
FDT_SW_CHECK_HEADER(fdt);
|
||||
|
||||
headsize = fdt_off_dt_struct(fdt);
|
||||
tailsize = fdt_size_dt_strings(fdt);
|
||||
|
||||
if ((headsize + tailsize) > bufsize)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
|
||||
newtail = (char *)buf + bufsize - tailsize;
|
||||
|
||||
/* Two cases to avoid clobbering data if the old and new
|
||||
* buffers partially overlap */
|
||||
if (buf <= fdt) {
|
||||
memmove(buf, fdt, headsize);
|
||||
memmove(newtail, oldtail, tailsize);
|
||||
} else {
|
||||
memmove(newtail, oldtail, tailsize);
|
||||
memmove(buf, fdt, headsize);
|
||||
}
|
||||
|
||||
fdt_set_off_dt_strings(buf, bufsize);
|
||||
fdt_set_totalsize(buf, 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)
|
||||
{
|
||||
fdt32_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;
|
||||
fdt32_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;
|
||||
}
|
139
scripts/dtc/libfdt/fdt_wip.c
Normal file
139
scripts/dtc/libfdt/fdt_wip.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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_namelen_partial(void *fdt, int nodeoffset,
|
||||
const char *name, int namelen,
|
||||
uint32_t idx, const void *val,
|
||||
int len)
|
||||
{
|
||||
void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
|
||||
&proplen);
|
||||
if (!propval)
|
||||
return proplen;
|
||||
|
||||
if (proplen < (len + idx))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memcpy((char *)propval + idx, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
{
|
||||
const void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
|
||||
if (! propval)
|
||||
return proplen;
|
||||
|
||||
if (proplen != len)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
|
||||
strlen(name), 0,
|
||||
val, len);
|
||||
}
|
||||
|
||||
static void _fdt_nop_region(void *start, int len)
|
||||
{
|
||||
fdt32_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;
|
||||
}
|
1833
scripts/dtc/libfdt/libfdt.h
Normal file
1833
scripts/dtc/libfdt/libfdt.h
Normal file
File diff suppressed because it is too large
Load diff
112
scripts/dtc/libfdt/libfdt_env.h
Normal file
112
scripts/dtc/libfdt/libfdt_env.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef _LIBFDT_ENV_H
|
||||
#define _LIBFDT_ENV_H
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
||||
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __CHECKER__
|
||||
#define __force __attribute__((force))
|
||||
#define __bitwise __attribute__((bitwise))
|
||||
#else
|
||||
#define __force
|
||||
#define __bitwise
|
||||
#endif
|
||||
|
||||
typedef uint16_t __bitwise fdt16_t;
|
||||
typedef uint32_t __bitwise fdt32_t;
|
||||
typedef uint64_t __bitwise fdt64_t;
|
||||
|
||||
#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
|
||||
#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
|
||||
#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
|
||||
(EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
|
||||
#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
|
||||
(EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
|
||||
(EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
|
||||
(EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
|
||||
|
||||
static inline uint16_t fdt16_to_cpu(fdt16_t x)
|
||||
{
|
||||
return (__force uint16_t)CPU_TO_FDT16(x);
|
||||
}
|
||||
static inline fdt16_t cpu_to_fdt16(uint16_t x)
|
||||
{
|
||||
return (__force fdt16_t)CPU_TO_FDT16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t fdt32_to_cpu(fdt32_t x)
|
||||
{
|
||||
return (__force uint32_t)CPU_TO_FDT32(x);
|
||||
}
|
||||
static inline fdt32_t cpu_to_fdt32(uint32_t x)
|
||||
{
|
||||
return (__force fdt32_t)CPU_TO_FDT32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t fdt64_to_cpu(fdt64_t x)
|
||||
{
|
||||
return (__force uint64_t)CPU_TO_FDT64(x);
|
||||
}
|
||||
static inline fdt64_t cpu_to_fdt64(uint64_t x)
|
||||
{
|
||||
return (__force fdt64_t)CPU_TO_FDT64(x);
|
||||
}
|
||||
#undef CPU_TO_FDT64
|
||||
#undef CPU_TO_FDT32
|
||||
#undef CPU_TO_FDT16
|
||||
#undef EXTRACT_BYTE
|
||||
|
||||
#endif /* _LIBFDT_ENV_H */
|
95
scripts/dtc/libfdt/libfdt_internal.h
Normal file
95
scripts/dtc/libfdt/libfdt_internal.h
Normal file
|
@ -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 */
|
980
scripts/dtc/livetree.c
Normal file
980
scripts/dtc/livetree.c
Normal file
|
@ -0,0 +1,980 @@
|
|||
/*
|
||||
* (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 occurred, 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 (streq(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 (streq(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);
|
||||
}
|
||||
|
||||
void append_to_property(struct node *node,
|
||||
char *name, const void *data, int len)
|
||||
{
|
||||
struct data d;
|
||||
struct property *p;
|
||||
|
||||
p = get_property(node, name);
|
||||
if (p) {
|
||||
d = data_append_data(p->val, data, len);
|
||||
p->val = d;
|
||||
} else {
|
||||
d = data_append_data(empty_data, data, len);
|
||||
p = build_property(name, d);
|
||||
add_property(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
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 dt_info *build_dt_info(unsigned int dtsflags,
|
||||
struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys)
|
||||
{
|
||||
struct dt_info *dti;
|
||||
|
||||
dti = xmalloc(sizeof(*dti));
|
||||
dti->dtsflags = dtsflags;
|
||||
dti->reservelist = reservelist;
|
||||
dti->dt = tree;
|
||||
dti->boot_cpuid_phys = boot_cpuid_phys;
|
||||
|
||||
return dti;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (streq(ref, "/"))
|
||||
return tree;
|
||||
else 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 dt_info *dti)
|
||||
{
|
||||
struct reserve_info *ri, **tbl;
|
||||
int n = 0, i = 0;
|
||||
|
||||
for (ri = dti->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
n++;
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
tbl = xmalloc(n * sizeof(*tbl));
|
||||
|
||||
for (ri = dti->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
tbl[i++] = ri;
|
||||
|
||||
qsort(tbl, n, sizeof(*tbl), cmp_reserve_info);
|
||||
|
||||
dti->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 dt_info *dti)
|
||||
{
|
||||
sort_reserve_entries(dti);
|
||||
sort_node(dti->dt);
|
||||
}
|
||||
|
||||
/* utility helper to avoid code duplication */
|
||||
static struct node *build_and_name_child_node(struct node *parent, char *name)
|
||||
{
|
||||
struct node *node;
|
||||
|
||||
node = build_node(NULL, NULL);
|
||||
name_node(node, xstrdup(name));
|
||||
add_child(parent, node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static struct node *build_root_node(struct node *dt, char *name)
|
||||
{
|
||||
struct node *an;
|
||||
|
||||
an = get_subnode(dt, name);
|
||||
if (!an)
|
||||
an = build_and_name_child_node(dt, name);
|
||||
|
||||
if (!an)
|
||||
die("Could not build root node /%s\n", name);
|
||||
|
||||
return an;
|
||||
}
|
||||
|
||||
static bool any_label_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
|
||||
if (node->labels)
|
||||
return true;
|
||||
|
||||
for_each_child(node, c)
|
||||
if (any_label_tree(dti, c))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void generate_label_tree_internal(struct dt_info *dti,
|
||||
struct node *an, struct node *node,
|
||||
bool allocph)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *p;
|
||||
struct label *l;
|
||||
|
||||
/* if there are labels */
|
||||
if (node->labels) {
|
||||
|
||||
/* now add the label in the node */
|
||||
for_each_label(node->labels, l) {
|
||||
|
||||
/* check whether the label already exists */
|
||||
p = get_property(an, l->label);
|
||||
if (p) {
|
||||
fprintf(stderr, "WARNING: label %s already"
|
||||
" exists in /%s", l->label,
|
||||
an->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* insert it */
|
||||
p = build_property(l->label,
|
||||
data_copy_mem(node->fullpath,
|
||||
strlen(node->fullpath) + 1));
|
||||
add_property(an, p);
|
||||
}
|
||||
|
||||
/* force allocation of a phandle for this node */
|
||||
if (allocph)
|
||||
(void)get_node_phandle(dt, node);
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_label_tree_internal(dti, an, c, allocph);
|
||||
}
|
||||
|
||||
static bool any_fixup_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
if (!get_node_by_ref(dti->dt, m->ref))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c) {
|
||||
if (any_fixup_tree(dti, c))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_fixup_entry(struct dt_info *dti, struct node *fn,
|
||||
struct node *node, struct property *prop,
|
||||
struct marker *m)
|
||||
{
|
||||
char *entry;
|
||||
|
||||
/* m->ref can only be a REF_PHANDLE, but check anyway */
|
||||
assert(m->type == REF_PHANDLE);
|
||||
|
||||
/* there shouldn't be any ':' in the arguments */
|
||||
if (strchr(node->fullpath, ':') || strchr(prop->name, ':'))
|
||||
die("arguments should not contain ':'\n");
|
||||
|
||||
xasprintf(&entry, "%s:%s:%u",
|
||||
node->fullpath, prop->name, m->offset);
|
||||
append_to_property(fn, m->ref, entry, strlen(entry) + 1);
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static void generate_fixups_tree_internal(struct dt_info *dti,
|
||||
struct node *fn,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
struct node *refnode;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (!refnode)
|
||||
add_fixup_entry(dti, fn, node, prop, m);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_fixups_tree_internal(dti, fn, c);
|
||||
}
|
||||
|
||||
static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
if (get_node_by_ref(dti->dt, m->ref))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c) {
|
||||
if (any_local_fixup_tree(dti, c))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_local_fixup_entry(struct dt_info *dti,
|
||||
struct node *lfn, struct node *node,
|
||||
struct property *prop, struct marker *m,
|
||||
struct node *refnode)
|
||||
{
|
||||
struct node *wn, *nwn; /* local fixup node, walk node, new */
|
||||
uint32_t value_32;
|
||||
char **compp;
|
||||
int i, depth;
|
||||
|
||||
/* walk back retreiving depth */
|
||||
depth = 0;
|
||||
for (wn = node; wn; wn = wn->parent)
|
||||
depth++;
|
||||
|
||||
/* allocate name array */
|
||||
compp = xmalloc(sizeof(*compp) * depth);
|
||||
|
||||
/* store names in the array */
|
||||
for (wn = node, i = depth - 1; wn; wn = wn->parent, i--)
|
||||
compp[i] = wn->name;
|
||||
|
||||
/* walk the path components creating nodes if they don't exist */
|
||||
for (wn = lfn, i = 1; i < depth; i++, wn = nwn) {
|
||||
/* if no node exists, create it */
|
||||
nwn = get_subnode(wn, compp[i]);
|
||||
if (!nwn)
|
||||
nwn = build_and_name_child_node(wn, compp[i]);
|
||||
}
|
||||
|
||||
free(compp);
|
||||
|
||||
value_32 = cpu_to_fdt32(m->offset);
|
||||
append_to_property(wn, prop->name, &value_32, sizeof(value_32));
|
||||
}
|
||||
|
||||
static void generate_local_fixups_tree_internal(struct dt_info *dti,
|
||||
struct node *lfn,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
struct node *refnode;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (refnode)
|
||||
add_local_fixup_entry(dti, lfn, node, prop, m, refnode);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_local_fixups_tree_internal(dti, lfn, c);
|
||||
}
|
||||
|
||||
void generate_label_tree(struct dt_info *dti, char *name, bool allocph)
|
||||
{
|
||||
if (!any_label_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_label_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt, allocph);
|
||||
}
|
||||
|
||||
void generate_fixups_tree(struct dt_info *dti, char *name)
|
||||
{
|
||||
if (!any_fixup_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_fixups_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt);
|
||||
}
|
||||
|
||||
void generate_local_fixups_tree(struct dt_info *dti, char *name)
|
||||
{
|
||||
if (!any_local_fixup_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt);
|
||||
}
|
302
scripts/dtc/srcpos.c
Normal file
302
scripts/dtc/srcpos.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* 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 *get_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, "rb");
|
||||
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 = get_dirname(srcfile->name);
|
||||
srcfile->prev = current_srcfile;
|
||||
|
||||
srcfile->lineno = 1;
|
||||
srcfile->colno = 1;
|
||||
|
||||
current_srcfile = srcfile;
|
||||
}
|
||||
|
||||
bool 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 ? true : false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char *
|
||||
srcpos_string(struct srcpos *pos)
|
||||
{
|
||||
const char *fname = "<no-file>";
|
||||
char *pos_str;
|
||||
|
||||
if (pos->file && pos->file->name)
|
||||
fname = pos->file->name;
|
||||
|
||||
|
||||
if (pos->first_line != pos->last_line)
|
||||
xasprintf(&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)
|
||||
xasprintf(&pos_str, "%s:%d.%d-%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_column);
|
||||
else
|
||||
xasprintf(&pos_str, "%s:%d.%d", fname,
|
||||
pos->first_line, pos->first_column);
|
||||
|
||||
return pos_str;
|
||||
}
|
||||
|
||||
void srcpos_verror(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, va_list va)
|
||||
{
|
||||
char *srcstr;
|
||||
|
||||
srcstr = srcpos_string(pos);
|
||||
|
||||
fprintf(stderr, "%s: %s ", prefix, srcstr);
|
||||
vfprintf(stderr, fmt, va);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
free(srcstr);
|
||||
}
|
||||
|
||||
void srcpos_error(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
srcpos_verror(pos, prefix, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void srcpos_set_line(char *f, int l)
|
||||
{
|
||||
current_srcfile->name = f;
|
||||
current_srcfile->lineno = l;
|
||||
}
|
118
scripts/dtc/srcpos.h
Normal file
118
scripts/dtc/srcpos.h
Normal file
|
@ -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>
|
||||
#include <stdbool.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);
|
||||
bool 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_verror(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, va_list va)
|
||||
__attribute__((format(printf, 3, 0)));
|
||||
extern void srcpos_error(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
extern void srcpos_set_line(char *f, int l);
|
||||
|
||||
#endif /* _SRCPOS_H_ */
|
284
scripts/dtc/treesource.c
Normal file
284
scripts/dtc/treesource.c
Normal file
|
@ -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 dt_info *parser_output;
|
||||
bool treesource_error;
|
||||
|
||||
struct dt_info *dt_from_source(const char *fname)
|
||||
{
|
||||
parser_output = NULL;
|
||||
treesource_error = false;
|
||||
|
||||
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 parser_output;
|
||||
}
|
||||
|
||||
static void write_prefix(FILE *f, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < level; i++)
|
||||
fputc('\t', f);
|
||||
}
|
||||
|
||||
static bool isstring(char c)
|
||||
{
|
||||
return (isprint((unsigned char)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 + 1))) {
|
||||
if (m->type == LABEL) {
|
||||
assert(m->offset == (i+1));
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
}
|
||||
m = m->next;
|
||||
}
|
||||
fprintf(f, "\"");
|
||||
break;
|
||||
default:
|
||||
if (isprint((unsigned char)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", (unsigned char)(*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 dt_info *dti)
|
||||
{
|
||||
struct reserve_info *re;
|
||||
|
||||
fprintf(f, "/dts-v1/;\n\n");
|
||||
|
||||
for (re = dti->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, dti->dt, 0);
|
||||
}
|
||||
|
81
scripts/dtc/update-dtc-source.sh
Executable file
81
scripts/dtc/update-dtc-source.sh
Executable file
|
@ -0,0 +1,81 @@
|
|||
#!/bin/sh
|
||||
# Simple script to update the version of DTC carried by the Linux kernel
|
||||
#
|
||||
# This script assumes that the dtc and the linux git trees are in the
|
||||
# same directory. After building dtc in the dtc directory, it copies the
|
||||
# source files and generated source files into the scripts/dtc directory
|
||||
# in the kernel and creates a git commit updating them to the new
|
||||
# version.
|
||||
#
|
||||
# Usage: from the top level Linux source tree, run:
|
||||
# $ ./scripts/dtc/update-dtc-source.sh
|
||||
#
|
||||
# The script will change into the dtc tree, build and test dtc, copy the
|
||||
# relevant files into the kernel tree and create a git commit. The commit
|
||||
# message will need to be modified to reflect the version of DTC being
|
||||
# imported
|
||||
#
|
||||
# TODO:
|
||||
# This script is pretty basic, but it is seldom used so a few manual tasks
|
||||
# aren't a big deal. If anyone is interested in making it more robust, the
|
||||
# the following would be nice:
|
||||
# * Actually fail to complete if any testcase fails.
|
||||
# - The dtc "make check" target needs to return a failure
|
||||
# * Extract the version number from the dtc repo for the commit message
|
||||
# * Build dtc in the kernel tree
|
||||
# * run 'make check" on dtc built from the kernel tree
|
||||
|
||||
set -ev
|
||||
|
||||
DTC_UPSTREAM_PATH=`pwd`/../dtc
|
||||
DTC_LINUX_PATH=`pwd`/scripts/dtc
|
||||
|
||||
DTC_SOURCE="checks.c data.c dtc.c dtc.h flattree.c fstree.c livetree.c srcpos.c \
|
||||
srcpos.h treesource.c util.c util.h version_gen.h Makefile.dtc \
|
||||
dtc-lexer.l dtc-parser.y"
|
||||
DTC_GENERATED="dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h"
|
||||
LIBFDT_SOURCE="Makefile.libfdt fdt.c fdt.h fdt_empty_tree.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c fdt_wip.c libfdt.h libfdt_env.h libfdt_internal.h"
|
||||
|
||||
get_last_dtc_version() {
|
||||
git log --oneline scripts/dtc/ | grep 'upstream' | head -1 | sed -e 's/^.* \(.*\)/\1/'
|
||||
}
|
||||
|
||||
last_dtc_ver=$(get_last_dtc_version)
|
||||
|
||||
# Build DTC
|
||||
cd $DTC_UPSTREAM_PATH
|
||||
make clean
|
||||
make check
|
||||
dtc_version=$(git describe HEAD)
|
||||
dtc_log=$(git log --oneline ${last_dtc_ver}..)
|
||||
|
||||
|
||||
# Copy the files into the Linux tree
|
||||
cd $DTC_LINUX_PATH
|
||||
for f in $DTC_SOURCE; do
|
||||
cp ${DTC_UPSTREAM_PATH}/${f} ${f}
|
||||
git add ${f}
|
||||
done
|
||||
for f in $DTC_GENERATED; do
|
||||
cp ${DTC_UPSTREAM_PATH}/$f ${f}_shipped
|
||||
git add ${f}_shipped
|
||||
done
|
||||
for f in $LIBFDT_SOURCE; do
|
||||
cp ${DTC_UPSTREAM_PATH}/libfdt/${f} libfdt/${f}
|
||||
git add libfdt/${f}
|
||||
done
|
||||
|
||||
sed -i -- 's/#include <libfdt_env.h>/#include "libfdt_env.h"/g' ./libfdt/libfdt.h
|
||||
sed -i -- 's/#include <fdt.h>/#include "fdt.h"/g' ./libfdt/libfdt.h
|
||||
git add ./libfdt/libfdt.h
|
||||
|
||||
commit_msg=$(cat << EOF
|
||||
scripts/dtc: Update to upstream version ${dtc_version}
|
||||
|
||||
This adds the following commits from upstream:
|
||||
|
||||
${dtc_log}
|
||||
EOF
|
||||
)
|
||||
|
||||
git commit -e -v -s -m "${commit_msg}"
|
473
scripts/dtc/util.c
Normal file
473
scripts/dtc/util.c
Normal file
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* 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"
|
||||
#include "version_gen.h"
|
||||
|
||||
char *xstrdup(const char *s)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
char *d = xmalloc(len);
|
||||
|
||||
memcpy(d, s, len);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/* based in part from (3) vsnprintf */
|
||||
int xasprintf(char **strp, const char *fmt, ...)
|
||||
{
|
||||
int n, size = 128; /* start with 128 bytes */
|
||||
char *p;
|
||||
va_list ap;
|
||||
|
||||
/* initial pointer is NULL making the fist realloc to be malloc */
|
||||
p = NULL;
|
||||
while (1) {
|
||||
p = xrealloc(p, size);
|
||||
|
||||
/* Try to print in the allocated space. */
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(p, size, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* If that worked, return the string. */
|
||||
if (n > -1 && n < size)
|
||||
break;
|
||||
/* Else try again with more space. */
|
||||
if (n > -1) /* glibc 2.1 */
|
||||
size = n + 1; /* precisely what is needed */
|
||||
else /* glibc 2.0 */
|
||||
size *= 2; /* twice the old size */
|
||||
}
|
||||
*strp = p;
|
||||
return strlen(p);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool util_is_printable_string(const void *data, int len)
|
||||
{
|
||||
const char *s = data;
|
||||
const char *ss, *se;
|
||||
|
||||
/* zero length is not */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
se = s + len;
|
||||
|
||||
while (s < se) {
|
||||
ss = s;
|
||||
while (s < se && *s && isprint((unsigned char)*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s != '\0' || s == ss)
|
||||
return 0;
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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_len(const char *filename, char **buffp, off_t *len)
|
||||
{
|
||||
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 = xmalloc(bufsize);
|
||||
do {
|
||||
/* Expand the buffer to hold the next chunk */
|
||||
if (offset == bufsize) {
|
||||
bufsize *= 2;
|
||||
buf = xrealloc(buf, bufsize);
|
||||
}
|
||||
|
||||
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;
|
||||
*len = bufsize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int utilfdt_read_err(const char *filename, char **buffp)
|
||||
{
|
||||
off_t len;
|
||||
return utilfdt_read_err_len(filename, buffp, &len);
|
||||
}
|
||||
|
||||
char *utilfdt_read_len(const char *filename, off_t *len)
|
||||
{
|
||||
char *buff;
|
||||
int ret = utilfdt_read_err_len(filename, &buff, len);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename,
|
||||
strerror(ret));
|
||||
return NULL;
|
||||
}
|
||||
/* Successful read */
|
||||
return buff;
|
||||
}
|
||||
|
||||
char *utilfdt_read(const char *filename)
|
||||
{
|
||||
off_t len;
|
||||
return utilfdt_read_len(filename, &len);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void utilfdt_print_data(const char *data, int len)
|
||||
{
|
||||
int i;
|
||||
const char *s;
|
||||
|
||||
/* no data, don't print */
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if (util_is_printable_string(data, len)) {
|
||||
printf(" = ");
|
||||
|
||||
s = data;
|
||||
do {
|
||||
printf("\"%s\"", s);
|
||||
s += strlen(s) + 1;
|
||||
if (s < data + len)
|
||||
printf(", ");
|
||||
} while (s < data + len);
|
||||
|
||||
} else if ((len % 4) == 0) {
|
||||
const uint32_t *cell = (const uint32_t *)data;
|
||||
|
||||
printf(" = <");
|
||||
for (i = 0, len /= 4; i < len; i++)
|
||||
printf("0x%08x%s", fdt32_to_cpu(cell[i]),
|
||||
i < (len - 1) ? " " : "");
|
||||
printf(">");
|
||||
} else {
|
||||
const unsigned char *p = (const unsigned char *)data;
|
||||
printf(" = [");
|
||||
for (i = 0; i < len; i++)
|
||||
printf("%02x%s", *p++, i < len - 1 ? " " : "");
|
||||
printf("]");
|
||||
}
|
||||
}
|
||||
|
||||
void util_version(void)
|
||||
{
|
||||
printf("Version: %s\n", DTC_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void util_usage(const char *errmsg, const char *synopsis,
|
||||
const char *short_opts, struct option const long_opts[],
|
||||
const char * const opts_help[])
|
||||
{
|
||||
FILE *fp = errmsg ? stderr : stdout;
|
||||
const char a_arg[] = "<arg>";
|
||||
size_t a_arg_len = strlen(a_arg) + 1;
|
||||
size_t i;
|
||||
int optlen;
|
||||
|
||||
fprintf(fp,
|
||||
"Usage: %s\n"
|
||||
"\n"
|
||||
"Options: -[%s]\n", synopsis, short_opts);
|
||||
|
||||
/* prescan the --long opt length to auto-align */
|
||||
optlen = 0;
|
||||
for (i = 0; long_opts[i].name; ++i) {
|
||||
/* +1 is for space between --opt and help text */
|
||||
int l = strlen(long_opts[i].name) + 1;
|
||||
if (long_opts[i].has_arg == a_argument)
|
||||
l += a_arg_len;
|
||||
if (optlen < l)
|
||||
optlen = l;
|
||||
}
|
||||
|
||||
for (i = 0; long_opts[i].name; ++i) {
|
||||
/* helps when adding new applets or options */
|
||||
assert(opts_help[i] != NULL);
|
||||
|
||||
/* first output the short flag if it has one */
|
||||
if (long_opts[i].val > '~')
|
||||
fprintf(fp, " ");
|
||||
else
|
||||
fprintf(fp, " -%c, ", long_opts[i].val);
|
||||
|
||||
/* then the long flag */
|
||||
if (long_opts[i].has_arg == no_argument)
|
||||
fprintf(fp, "--%-*s", optlen, long_opts[i].name);
|
||||
else
|
||||
fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg,
|
||||
(int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
|
||||
|
||||
/* finally the help text */
|
||||
fprintf(fp, "%s\n", opts_help[i]);
|
||||
}
|
||||
|
||||
if (errmsg) {
|
||||
fprintf(fp, "\nError: %s\n", errmsg);
|
||||
exit(EXIT_FAILURE);
|
||||
} else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
265
scripts/dtc/util.h
Normal file
265
scripts/dtc/util.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.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
|
||||
*/
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#ifdef __GNUC__
|
||||
static inline void
|
||||
__attribute__((noreturn)) __attribute__((format (printf, 1, 2)))
|
||||
die(const char *str, ...)
|
||||
#else
|
||||
static inline void die(const char *str, ...)
|
||||
#endif
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
vfprintf(stderr, str, ap);
|
||||
va_end(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=%zd)\n", len);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
extern char *xstrdup(const char *s);
|
||||
|
||||
#ifdef __GNUC__
|
||||
extern int __attribute__((format (printf, 2, 3)))
|
||||
xasprintf(char **strp, const char *fmt, ...);
|
||||
#else
|
||||
extern int xasprintf(char **strp, const char *fmt, ...);
|
||||
#endif
|
||||
extern char *join_path(const char *path, const char *name);
|
||||
|
||||
/**
|
||||
* Check a property of a given length to see if it is all printable and
|
||||
* has a valid terminator. The property can contain either a single string,
|
||||
* or multiple strings each of non-zero length.
|
||||
*
|
||||
* @param data The string to check
|
||||
* @param len The string length including terminator
|
||||
* @return 1 if a valid printable string, 0 if not
|
||||
*/
|
||||
bool 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);
|
||||
|
||||
/**
|
||||
* Like utilfdt_read(), but also passes back the size of the file read.
|
||||
*
|
||||
* @param len If non-NULL, the amount of data we managed to read
|
||||
*/
|
||||
char *utilfdt_read_len(const char *filename, off_t *len);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Like utilfdt_read_err(), but also passes back the size of the file read.
|
||||
*
|
||||
* @param len If non-NULL, the amount of data we managed to read
|
||||
*/
|
||||
int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len);
|
||||
|
||||
/**
|
||||
* 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)";
|
||||
|
||||
/**
|
||||
* Print property data in a readable format to stdout
|
||||
*
|
||||
* Properties that look like strings will be printed as strings. Otherwise
|
||||
* the data will be displayed either as cells (if len is a multiple of 4
|
||||
* bytes) or bytes.
|
||||
*
|
||||
* If len is 0 then this function does nothing.
|
||||
*
|
||||
* @param data Pointers to property data
|
||||
* @param len Length of property data
|
||||
*/
|
||||
void utilfdt_print_data(const char *data, int len);
|
||||
|
||||
/**
|
||||
* Show source version and exit
|
||||
*/
|
||||
void util_version(void) __attribute__((noreturn));
|
||||
|
||||
/**
|
||||
* Show usage and exit
|
||||
*
|
||||
* This helps standardize the output of various utils. You most likely want
|
||||
* to use the usage() helper below rather than call this.
|
||||
*
|
||||
* @param errmsg If non-NULL, an error message to display
|
||||
* @param synopsis The initial example usage text (and possible examples)
|
||||
* @param short_opts The string of short options
|
||||
* @param long_opts The structure of long options
|
||||
* @param opts_help An array of help strings (should align with long_opts)
|
||||
*/
|
||||
void util_usage(const char *errmsg, const char *synopsis,
|
||||
const char *short_opts, struct option const long_opts[],
|
||||
const char * const opts_help[]) __attribute__((noreturn));
|
||||
|
||||
/**
|
||||
* Show usage and exit
|
||||
*
|
||||
* If you name all your usage variables with usage_xxx, then you can call this
|
||||
* help macro rather than expanding all arguments yourself.
|
||||
*
|
||||
* @param errmsg If non-NULL, an error message to display
|
||||
*/
|
||||
#define usage(errmsg) \
|
||||
util_usage(errmsg, usage_synopsis, usage_short_opts, \
|
||||
usage_long_opts, usage_opts_help)
|
||||
|
||||
/**
|
||||
* Call getopt_long() with standard options
|
||||
*
|
||||
* Since all util code runs getopt in the same way, provide a helper.
|
||||
*/
|
||||
#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \
|
||||
usage_long_opts, NULL)
|
||||
|
||||
/* Helper for aligning long_opts array */
|
||||
#define a_argument required_argument
|
||||
|
||||
/* Helper for usage_short_opts string constant */
|
||||
#define USAGE_COMMON_SHORT_OPTS "hV"
|
||||
|
||||
/* Helper for usage_long_opts option array */
|
||||
#define USAGE_COMMON_LONG_OPTS \
|
||||
{"help", no_argument, NULL, 'h'}, \
|
||||
{"version", no_argument, NULL, 'V'}, \
|
||||
{NULL, no_argument, NULL, 0x0}
|
||||
|
||||
/* Helper for usage_opts_help array */
|
||||
#define USAGE_COMMON_OPTS_HELP \
|
||||
"Print this help and exit", \
|
||||
"Print version and exit", \
|
||||
NULL
|
||||
|
||||
/* Helper for getopt case statements */
|
||||
#define case_USAGE_COMMON_FLAGS \
|
||||
case 'h': usage(NULL); \
|
||||
case 'V': util_version(); \
|
||||
case '?': usage("unknown option");
|
||||
|
||||
#endif /* _UTIL_H */
|
1
scripts/dtc/version_gen.h
Normal file
1
scripts/dtc/version_gen.h
Normal file
|
@ -0,0 +1 @@
|
|||
#define DTC_VERSION "DTC 1.4.3"
|
Loading…
Reference in a new issue