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>master
parent
0929863aff
commit
c0e032e009
@ -0,0 +1,31 @@ |
||||
# scripts/dtc makefile
|
||||
|
||||
hostprogs-y := dtc
|
||||
always := $(hostprogs-y)
|
||||
|
||||
dtc-objs := dtc.o flattree.o fstree.o data.o livetree.o treesource.o \
|
||||
srcpos.o checks.o util.o
|
||||
dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o
|
||||
|
||||
# Source files need to get at the userspace version of libfdt_env.h to compile
|
||||
|
||||
HOSTCFLAGS_DTC := -I$(src) -I$(src)/libfdt
|
||||
|
||||
HOSTCFLAGS_checks.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_data.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_dtc.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_flattree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_fstree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_livetree.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_srcpos.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_treesource.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_util.o := $(HOSTCFLAGS_DTC)
|
||||
|
||||
HOSTCFLAGS_dtc-lexer.lex.o := $(HOSTCFLAGS_DTC)
|
||||
HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC)
|
||||
|
||||
# dependencies on generated files need to be listed explicitly
|
||||
$(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h |
||||
|
||||
# generated files need to be cleaned explicitly
|
||||
clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h
|
@ -0,0 +1,18 @@ |
||||
# Makefile.dtc
|
||||
#
|
||||
# This is not a complete Makefile of itself. Instead, it is designed to
|
||||
# be easily embeddable into other systems of Makefiles.
|
||||
#
|
||||
DTC_SRCS = \
|
||||
checks.c \
|
||||
data.c \
|
||||
dtc.c \
|
||||
flattree.c \
|
||||
fstree.c \
|
||||
livetree.c \
|
||||
srcpos.c \
|
||||
treesource.c \
|
||||
util.c
|
||||
|
||||
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
||||
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
@ -0,0 +1,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"); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
@ -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; |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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 */ |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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 */ |
@ -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); |
||||
} |
@ -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)); |
||||
} |
@ -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)
|
@ -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; |
||||
} |
@ -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 */ |
@ -0,0 +1,84 @@ |
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation |
||||
* Copyright (C) 2012 David Gibson, IBM Corporation. |
||||
* |
||||
* libfdt is dual licensed: you can use it either under the terms of |
||||
* the GPL, or the BSD license, at your option. |
||||
* |
||||
* a) This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public |
||||
* License along with this library; if not, write to the Free |
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
||||
* MA 02110-1301 USA |
||||
* |
||||
* Alternatively, |
||||
* |
||||
* b) Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials |
||||
* provided with the distribution. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
#include "libfdt_env.h" |
||||
|
||||
#include <fdt.h> |
||||
#include <libfdt.h> |
||||
|
||||
#include "libfdt_internal.h" |
||||
|
||||
int fdt_create_empty_tree(void *buf, int bufsize) |
||||
{ |
||||
int err; |
||||
|
||||
err = fdt_create(buf, bufsize); |
||||
if (err) |
||||
return err; |
||||
|
||||
err = fdt_finish_reservemap(buf); |
||||
if (err) |
||||
return err; |
||||
|
||||
err = fdt_begin_node(buf, ""); |
||||
if (err) |
||||
return err; |
||||
|
||||
err = fdt_end_node(buf); |
||||
if (err) |
||||
return err; |
||||
|
||||
err = fdt_finish(buf); |
||||
if (err) |
||||
return err; |
||||
|
||||
return fdt_open_into(buf, buf, bufsize); |
||||
} |
||||
|
@ -0,0 +1,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() */ |
||||
} |
@ -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; |
||||
} |
@ -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>"; |
||||
} |
@ -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; |
||||
} |
@ -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; |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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 */ |
@ -0,0 +1,95 @@ |
||||
#ifndef _LIBFDT_INTERNAL_H |
||||
#define _LIBFDT_INTERNAL_H |
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation |
||||
* Copyright (C) 2006 David Gibson, IBM Corporation. |
||||
* |
||||
* libfdt is dual licensed: you can use it either under the terms of |
||||
* the GPL, or the BSD license, at your option. |
||||
* |
||||
* a) This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public |
||||
* License along with this library; if not, write to the Free |
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
||||
* MA 02110-1301 USA |
||||
* |
||||
* Alternatively, |
||||
* |
||||
* b) Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* 1. Redistributions of source code must retain the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials |
||||
* provided with the distribution. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
#include <fdt.h> |
||||
|
||||
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) |
||||
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) |
||||
|
||||
#define FDT_CHECK_HEADER(fdt) \ |
||||
{ \
|
||||
int __err; \
|
||||
if ((__err = fdt_check_header(fdt)) != 0) \
|
||||
return __err; \
|
||||
} |
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset); |
||||
int _fdt_check_prop_offset(const void *fdt, int offset); |
||||
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); |
||||
int _fdt_node_end_offset(void *fdt, int nodeoffset); |
||||
|
||||
static inline const void *_fdt_offset_ptr(const void *fdt, int offset) |
||||
{ |
||||
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; |
||||
} |
||||
|
||||
static inline void *_fdt_offset_ptr_w(void *fdt, int offset) |
||||
{ |
||||
return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); |
||||
} |
||||
|
||||
static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) |
||||
{ |
||||
const struct fdt_reserve_entry *rsv_table = |
||||
(const struct fdt_reserve_entry *) |
||||
((const char *)fdt + fdt_off_mem_rsvmap(fdt)); |
||||
|
||||
return rsv_table + n; |
||||
} |
||||
static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) |
||||
{ |
||||
return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); |
||||
} |
||||
|
||||
#define FDT_SW_MAGIC (~FDT_MAGIC) |
||||
|
||||
#endif /* _LIBFDT_INTERNAL_H */ |
@ -0,0 +1,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); |
||||
} |
@ -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; |
||||
} |
@ -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_ */ |
@ -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); |
||||
} |
||||
|
@ -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}" |
@ -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); |
||||
} |
@ -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 */ |
@ -0,0 +1 @@ |
||||
#define DTC_VERSION "DTC 1.4.3" |
Loading…
Reference in new issue