Reset master to upstream

Reset master to upstream 120775eb1c
and merge history.

Bug: 35151782
Test: Build dtc, use output dtc to build dts, can output correct dtb.
Change-Id: I4d3ef31a8597f7b214fea2d70ceb99a08d57d925
This commit is contained in:
SzuWei Lin 2017-02-17 10:58:26 +08:00
commit 507cd9883a
64 changed files with 2861 additions and 667 deletions

View file

@ -2,17 +2,17 @@ Device Tree Dynamic Object format internals
------------------------------------------- -------------------------------------------
The Device Tree for most platforms is a static representation of The Device Tree for most platforms is a static representation of
the hardware capabilities. This is insufficient for many platforms the hardware capabilities. This is insufficient for platforms
that need to dynamically insert device tree fragments to the that need to dynamically insert Device Tree fragments into the
running kernel's live tree. live tree.
This document explains the the device tree object format and the This document explains the the Device Tree object format and
modifications made to the device tree compiler, which make it possible. modifications made to the Device Tree compiler, which make it possible.
1. Simplified Problem Definition 1. Simplified Problem Definition
-------------------------------- --------------------------------
Assume we have a platform which boots using following simplified device tree. Assume we have a platform which boots using following simplified Device Tree.
---- foo.dts ----------------------------------------------------------------- ---- foo.dts -----------------------------------------------------------------
/* FOO platform */ /* FOO platform */
@ -27,12 +27,12 @@ Assume we have a platform which boots using following simplified device tree.
ocp: ocp { ocp: ocp {
/* peripherals that are always instantiated */ /* peripherals that are always instantiated */
peripheral1 { ... }; peripheral1 { ... };
} };
}; };
---- foo.dts ----------------------------------------------------------------- ---- foo.dts -----------------------------------------------------------------
We have a number of peripherals that after probing (using some undefined method) We have a number of peripherals that after probing (using some undefined method)
should result in different device tree configuration. should result in different Device Tree configuration.
We cannot boot with this static tree because due to the configuration of the We cannot boot with this static tree because due to the configuration of the
foo platform there exist multiple conficting peripherals DT fragments. foo platform there exist multiple conficting peripherals DT fragments.
@ -57,8 +57,8 @@ So for the bar peripheral we would have this:
bar { bar {
compatible = "corp,bar"; compatible = "corp,bar";
... /* various properties and child nodes */ ... /* various properties and child nodes */
} };
} };
}; };
---- foo+bar.dts ------------------------------------------------------------- ---- foo+bar.dts -------------------------------------------------------------
@ -86,8 +86,8 @@ While for the baz peripheral we would have this:
/* reference to another point in the tree */ /* reference to another point in the tree */
ref-to-res = <&baz_res>; ref-to-res = <&baz_res>;
... /* various properties and child nodes */ ... /* various properties and child nodes */
} };
} };
}; };
---- foo+baz.dts ------------------------------------------------------------- ---- foo+baz.dts -------------------------------------------------------------
@ -97,13 +97,15 @@ reference another node in the DT tree.
2. Device Tree Object Format Requirements 2. Device Tree Object Format Requirements
----------------------------------------- -----------------------------------------
Since the device tree is used for booting a number of very different hardware Since the Device Tree is used for booting a number of very different hardware
platforms it is imperative that we tread very carefully. platforms it is imperative that we tread very carefully.
2.a) No changes to the Device Tree binary format. We cannot modify the tree 2.a) No changes to the Device Tree binary format for the base tree. We cannot
format at all and all the information we require should be encoded using device modify the tree format at all and all the information we require should be
tree itself. We can add nodes that can be safely ignored by both bootloaders and encoded using Device Tree itself. We can add nodes that can be safely ignored
the kernel. by both bootloaders and the kernel. The plugin dtbs are optionally tagged
with a different magic number in the header but otherwise they're simple
blobs.
2.b) Changes to the DTS source format should be absolutely minimal, and should 2.b) Changes to the DTS source format should be absolutely minimal, and should
only be needed for the DT fragment definitions, and not the base boot DT. only be needed for the DT fragment definitions, and not the base boot DT.
@ -121,7 +123,9 @@ possible to express everything using the existing DT syntax.
The basic unit of addressing in Device Tree is the phandle. Turns out it's The basic unit of addressing in Device Tree is the phandle. Turns out it's
relatively simple to extend the way phandles are generated and referenced relatively simple to extend the way phandles are generated and referenced
so that it's possible to dynamically convert symbolic references (labels) so that it's possible to dynamically convert symbolic references (labels)
to phandle values. to phandle values. This is a valid assumption as long as the author uses
reference syntax and does not assign phandle values manually (which might
be a problem with decompiled source files).
We can roughly divide the operation into two steps. We can roughly divide the operation into two steps.
@ -138,13 +142,11 @@ $ fdtdump foo.dtb
... ...
res { res {
... ...
linux,phandle = <0x00000001>;
phandle = <0x00000001>; phandle = <0x00000001>;
... ...
}; };
ocp { ocp {
... ...
linux,phandle = <0x00000002>;
phandle = <0x00000002>; phandle = <0x00000002>;
... ...
}; };
@ -161,14 +163,19 @@ This blob can be used to boot the board normally, the __symbols__ node will
be safely ignored both by the bootloader and the kernel (the only loss will be safely ignored both by the bootloader and the kernel (the only loss will
be a few bytes of memory and disk space). be a few bytes of memory and disk space).
We generate a __symbols__ node to record nodes that had labels in the base
tree (or subsequent loaded overlays) so that they can be matched up with
references made to them in Device Tree objects.
3.b) The Device Tree fragments must be compiled with the same option but they 3.b) The Device Tree fragments must be compiled with the same option but they
must also have a tag (/plugin/) that allows undefined references to labels must also have a tag (/plugin/) that allows undefined references to nodes
that are not present at compilation time to be recorded so that the runtime that are not present at compilation time to be recorded so that the runtime
loader can fix them. loader can fix them.
So the bar peripheral's DTS format would be of the form: So the bar peripheral's DTS format would be of the form:
/plugin/; /* allow undefined label references and record them */ /dts-v1/;
/plugin/; /* allow undefined references and record them */
/ { / {
.... /* various properties for loader use; i.e. part id etc. */ .... /* various properties for loader use; i.e. part id etc. */
fragment@0 { fragment@0 {
@ -184,7 +191,7 @@ So the bar peripheral's DTS format would be of the form:
}; };
Note that there's a target property that specifies the location where the Note that there's a target property that specifies the location where the
contents of the overlay node will be placed, and it references the label contents of the overlay node will be placed, and it references the node
in the foo.dts file. in the foo.dts file.
$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts $ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
@ -193,7 +200,7 @@ $ fdtdump bar.dtbo
/ { / {
... /* properties */ ... /* properties */
fragment@0 { fragment@0 {
target = <0xdeadbeef>; target = <0xffffffff>;
__overlay__ { __overlay__ {
bar { bar {
compatible = "corp,bar"; compatible = "corp,bar";
@ -206,27 +213,29 @@ $ fdtdump bar.dtbo
}; };
}; };
No __symbols__ has been generated (no label in bar.dts). No __symbols__ node has been generated (no label in bar.dts).
Note that the target's ocp label is undefined, so the phandle handle Note that the target's ocp label is undefined, so the phandle
value is filled with the illegal value '0xdeadbeef', while a __fixups__ value is filled with the illegal value '0xffffffff', while a __fixups__
node has been generated, which marks the location in the tree where node has been generated, which marks the location in the tree where
the label lookup should store the runtime phandle value of the ocp node. the label lookup should store the runtime phandle value of the ocp node.
The format of the __fixups__ node entry is The format of the __fixups__ node entry is
<label> = "<local-full-path>:<property-name>:<offset>"; <label> = "<local-full-path>:<property-name>:<offset>"
[, "<local-full-path>:<property-name>:<offset>"...];
<label> Is the label we're referring <label> Is the label we're referring
<local-full-path> Is the full path of the node the reference is <local-full-path> Is the full path of the node the reference is
<property-name> Is the name of the property containing the <property-name> Is the name of the property containing the
reference reference
<offset> The offset (in bytes) of where the property's <offset> The offset (in bytes) of where the property's
phandle value is located. phandle value is located.
Doing the same with the baz peripheral's DTS format is a little bit more Doing the same with the baz peripheral's DTS format is a little bit more
involved, since baz contains references to local labels which require involved, since baz contains references to local labels which require
local fixups. local fixups.
/dts-v1/;
/plugin/; /* allow undefined label references and record them */ /plugin/; /* allow undefined label references and record them */
/ { / {
.... /* various properties for loader use; i.e. part id etc. */ .... /* various properties for loader use; i.e. part id etc. */
@ -259,17 +268,16 @@ $ fdtdump baz.dtbo
/ { / {
... /* properties */ ... /* properties */
fragment@0 { fragment@0 {
target = <0xdeadbeef>; target = <0xffffffff>;
__overlay__ { __overlay__ {
res_baz { res_baz {
.... ....
linux,phandle = <0x00000001>;
phandle = <0x00000001>; phandle = <0x00000001>;
}; };
}; };
}; };
fragment@1 { fragment@1 {
target = <0xdeadbeef>; target = <0xffffffff>;
__overlay__ { __overlay__ {
baz { baz {
compatible = "corp,baz"; compatible = "corp,baz";
@ -295,7 +303,8 @@ $ fdtdump baz.dtbo
This is similar to the bar case, but the reference of a local label by the This is similar to the bar case, but the reference of a local label by the
baz node generates a __local_fixups__ entry that records the place that the baz node generates a __local_fixups__ entry that records the place that the
local reference is being made. Since phandles are allocated starting at 1 local reference is being made. No matter how phandles are allocated from dtc
the run time loader must apply an offset to each phandle in every dynamic the run time loader must apply an offset to each phandle in every dynamic
DT object loaded. The __local_fixups__ node records the place of every DT object loaded. The __local_fixups__ node records the offset relative to the
local reference so that the loader can apply the offset. start of every local reference within that property so that the loader can apply
the offset.

View file

@ -120,7 +120,7 @@ Options:
Relevant for dtb and asm output only. Relevant for dtb and asm output only.
-@ -@
Generates a __symbols__ node at the root node of the resulting blob Generates a __symbols__ node at the root node of the resulting blob
for any node labels used, and for any local references using phandles for any node labels used, and for any local references using phandles
it also generates a __local_fixups__ node that tracks them. it also generates a __local_fixups__ node that tracks them.
@ -160,14 +160,17 @@ Additionally, dtc performs various sanity checks on the tree.
Here is a very rough overview of the layout of a DTS source file: Here is a very rough overview of the layout of a DTS source file:
sourcefile: list_of_memreserve devicetree sourcefile: versioninfo plugindecl list_of_memreserve devicetree
memreserve: label 'memreserve' ADDR ADDR ';' memreserve: label 'memreserve' ADDR ADDR ';'
| label 'memreserve' ADDR '-' ADDR ';' | label 'memreserve' ADDR '-' ADDR ';'
devicetree: '/' nodedef devicetree: '/' nodedef
versioninfo: '/' 'dts-v1' '/' ';'
plugindecl: '/' 'plugin' '/' ';' plugindecl: '/' 'plugin' '/' ';'
| /* empty */
nodedef: '{' list_of_property list_of_subnode '}' ';' nodedef: '{' list_of_property list_of_subnode '}' ';'
@ -225,7 +228,7 @@ Node may contain sub-nodes to obtain a hierarchical structure.
For example: For example:
- A child node named "childnode" whose unit name is - A child node named "childnode" whose unit name is
"childnode at address". It it turn has a string property "childnode at address". It in turn has a string property
called "childprop". called "childprop".
childnode@addresss { childnode@addresss {

View file

@ -10,7 +10,7 @@
# #
VERSION = 1 VERSION = 1
PATCHLEVEL = 4 PATCHLEVEL = 4
SUBLEVEL = 1 SUBLEVEL = 2
EXTRAVERSION = EXTRAVERSION =
LOCAL_VERSION = LOCAL_VERSION =
CONFIG_LOCALVERSION = CONFIG_LOCALVERSION =
@ -197,10 +197,32 @@ fdtget: $(FDTGET_OBJS) $(LIBFDT_archive)
fdtput: $(FDTPUT_OBJS) $(LIBFDT_archive) fdtput: $(FDTPUT_OBJS) $(LIBFDT_archive)
dist: dist:
git archive --format=tar --prefix=dtc-v$(dtc_version)/ HEAD \ git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \
> ../dtc-v$(dtc_version).tar > ../dtc-$(dtc_version).tar
cat ../dtc-v$(dtc_version).tar | \ cat ../dtc-$(dtc_version).tar | \
gzip -9 > ../dtc-v$(dtc_version).tgz gzip -9 > ../dtc-$(dtc_version).tar.gz
#
# Release signing and uploading
# This is for maintainer convenience, don't try this at home.
#
ifeq ($(MAINTAINER),y)
GPG = gpg2
KUP = kup
KUPDIR = /pub/software/utils/dtc
kup: dist
$(GPG) --detach-sign --armor -o ../dtc-$(dtc_version).tar.sign \
../dtc-$(dtc_version).tar
$(KUP) put ../dtc-$(dtc_version).tar.gz ../dtc-$(dtc_version).tar.sign \
$(KUPDIR)/dtc-$(dtc_version).tar.gz
endif
tags: FORCE
rm -f tags
find . \( -name tests -type d -prune \) -o \
\( ! -name '*.tab.[ch]' ! -name '*.lex.c' \
-name '*.[chly]' -type f -print \) | xargs ctags -a
# #
# Testsuite rules # Testsuite rules

4
README
View file

@ -14,7 +14,3 @@ mailto:devicetree-compiler@vger.kernel.org
Core device tree bindings are discussed on the devicetree-spec list: Core device tree bindings are discussed on the devicetree-spec list:
mailto:devicetree-spec@vger.kernel.org mailto:devicetree-spec@vger.kernel.org
Home Page
---------
http://devicetree.org/Device_Tree_Compiler

View file

@ -1,3 +1,3 @@
URL: https://git.kernel.org/cgit/utils/dtc/dtc.git/commit/?id=b06e55c88b9b922ff7e25cd62a4709b65524f0fc URL: https://git.kernel.org/cgit/utils/dtc/dtc.git/commit/?id=120775eb1cf39f8dcecd695c3ff1cfef8aeb669d
Version: 1.4.1 plus bugfixes Version: 1.4.2 plus bugfixes
Owners: cphoenix Owners: cphoenix

412
checks.c
View file

@ -40,16 +40,11 @@ enum checkstatus {
struct check; struct check;
typedef void (*tree_check_fn)(struct check *c, struct node *dt); typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node);
typedef void (*node_check_fn)(struct check *c, struct node *dt, struct node *node);
typedef void (*prop_check_fn)(struct check *c, struct node *dt,
struct node *node, struct property *prop);
struct check { struct check {
const char *name; const char *name;
tree_check_fn tree_fn; check_fn fn;
node_check_fn node_fn;
prop_check_fn prop_fn;
void *data; void *data;
bool warn, error; bool warn, error;
enum checkstatus status; enum checkstatus status;
@ -58,45 +53,24 @@ struct check {
struct check **prereq; struct check **prereq;
}; };
#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \ #define CHECK_ENTRY(_nm, _fn, _d, _w, _e, ...) \
static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \ static struct check *_nm##_prereqs[] = { __VA_ARGS__ }; \
static struct check nm = { \ static struct check _nm = { \
.name = #nm, \ .name = #_nm, \
.tree_fn = (tfn), \ .fn = (_fn), \
.node_fn = (nfn), \ .data = (_d), \
.prop_fn = (pfn), \ .warn = (_w), \
.data = (d), \ .error = (_e), \
.warn = (w), \
.error = (e), \
.status = UNCHECKED, \ .status = UNCHECKED, \
.num_prereqs = ARRAY_SIZE(nm##_prereqs), \ .num_prereqs = ARRAY_SIZE(_nm##_prereqs), \
.prereq = nm##_prereqs, \ .prereq = _nm##_prereqs, \
}; };
#define WARNING(nm, tfn, nfn, pfn, d, ...) \ #define WARNING(_nm, _fn, _d, ...) \
CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__) CHECK_ENTRY(_nm, _fn, _d, true, false, __VA_ARGS__)
#define ERROR(nm, tfn, nfn, pfn, d, ...) \ #define ERROR(_nm, _fn, _d, ...) \
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__) CHECK_ENTRY(_nm, _fn, _d, false, true, __VA_ARGS__)
#define CHECK(nm, tfn, nfn, pfn, d, ...) \ #define CHECK(_nm, _fn, _d, ...) \
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__) CHECK_ENTRY(_nm, _fn, _d, false, false, __VA_ARGS__)
#define TREE_WARNING(nm, d, ...) \
WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
#define TREE_ERROR(nm, d, ...) \
ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
#define TREE_CHECK(nm, d, ...) \
CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
#define NODE_WARNING(nm, d, ...) \
WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
#define NODE_ERROR(nm, d, ...) \
ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
#define NODE_CHECK(nm, d, ...) \
CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
#define PROP_WARNING(nm, d, ...) \
WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
#define PROP_ERROR(nm, d, ...) \
ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
#define PROP_CHECK(nm, d, ...) \
CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
#ifdef __GNUC__ #ifdef __GNUC__
static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3))); static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
@ -123,27 +97,21 @@ static inline void check_msg(struct check *c, const char *fmt, ...)
check_msg((c), __VA_ARGS__); \ check_msg((c), __VA_ARGS__); \
} while (0) } while (0)
static void check_nodes_props(struct check *c, struct node *dt, struct node *node) static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node)
{ {
struct node *child; struct node *child;
struct property *prop;
TRACE(c, "%s", node->fullpath); TRACE(c, "%s", node->fullpath);
if (c->node_fn) if (c->fn)
c->node_fn(c, dt, node); c->fn(c, dti, node);
if (c->prop_fn)
for_each_property(node, prop) {
TRACE(c, "%s\t'%s'", node->fullpath, prop->name);
c->prop_fn(c, dt, node, prop);
}
for_each_child(node, child) for_each_child(node, child)
check_nodes_props(c, dt, child); check_nodes_props(c, dti, child);
} }
static bool run_check(struct check *c, struct node *dt) static bool run_check(struct check *c, struct dt_info *dti)
{ {
struct node *dt = dti->dt;
bool error = false; bool error = false;
int i; int i;
@ -156,7 +124,7 @@ static bool run_check(struct check *c, struct node *dt)
for (i = 0; i < c->num_prereqs; i++) { for (i = 0; i < c->num_prereqs; i++) {
struct check *prq = c->prereq[i]; struct check *prq = c->prereq[i];
error = error || run_check(prq, dt); error = error || run_check(prq, dti);
if (prq->status != PASSED) { if (prq->status != PASSED) {
c->status = PREREQ; c->status = PREREQ;
check_msg(c, "Failed prerequisite '%s'", check_msg(c, "Failed prerequisite '%s'",
@ -167,11 +135,8 @@ static bool run_check(struct check *c, struct node *dt)
if (c->status != UNCHECKED) if (c->status != UNCHECKED)
goto out; goto out;
if (c->node_fn || c->prop_fn) check_nodes_props(c, dti, dt);
check_nodes_props(c, dt, dt);
if (c->tree_fn)
c->tree_fn(c, dt);
if (c->status == UNCHECKED) if (c->status == UNCHECKED)
c->status = PASSED; c->status = PASSED;
@ -189,13 +154,14 @@ out:
*/ */
/* A check which always fails, for testing purposes only */ /* A check which always fails, for testing purposes only */
static inline void check_always_fail(struct check *c, struct node *dt) static inline void check_always_fail(struct check *c, struct dt_info *dti,
struct node *node)
{ {
FAIL(c, "always_fail check"); FAIL(c, "always_fail check");
} }
TREE_CHECK(always_fail, NULL); CHECK(always_fail, check_always_fail, NULL);
static void check_is_string(struct check *c, struct node *root, static void check_is_string(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop; struct property *prop;
@ -210,11 +176,11 @@ static void check_is_string(struct check *c, struct node *root,
propname, node->fullpath); propname, node->fullpath);
} }
#define WARNING_IF_NOT_STRING(nm, propname) \ #define WARNING_IF_NOT_STRING(nm, propname) \
WARNING(nm, NULL, check_is_string, NULL, (propname)) WARNING(nm, check_is_string, (propname))
#define ERROR_IF_NOT_STRING(nm, propname) \ #define ERROR_IF_NOT_STRING(nm, propname) \
ERROR(nm, NULL, check_is_string, NULL, (propname)) ERROR(nm, check_is_string, (propname))
static void check_is_cell(struct check *c, struct node *root, static void check_is_cell(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop; struct property *prop;
@ -229,15 +195,15 @@ static void check_is_cell(struct check *c, struct node *root,
propname, node->fullpath); propname, node->fullpath);
} }
#define WARNING_IF_NOT_CELL(nm, propname) \ #define WARNING_IF_NOT_CELL(nm, propname) \
WARNING(nm, NULL, check_is_cell, NULL, (propname)) WARNING(nm, check_is_cell, (propname))
#define ERROR_IF_NOT_CELL(nm, propname) \ #define ERROR_IF_NOT_CELL(nm, propname) \
ERROR(nm, NULL, check_is_cell, NULL, (propname)) ERROR(nm, check_is_cell, (propname))
/* /*
* Structural check functions * Structural check functions
*/ */
static void check_duplicate_node_names(struct check *c, struct node *dt, static void check_duplicate_node_names(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct node *child, *child2; struct node *child, *child2;
@ -250,9 +216,9 @@ static void check_duplicate_node_names(struct check *c, struct node *dt,
FAIL(c, "Duplicate node name %s", FAIL(c, "Duplicate node name %s",
child->fullpath); child->fullpath);
} }
NODE_ERROR(duplicate_node_names, NULL); ERROR(duplicate_node_names, check_duplicate_node_names, NULL);
static void check_duplicate_property_names(struct check *c, struct node *dt, static void check_duplicate_property_names(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop, *prop2; struct property *prop, *prop2;
@ -267,14 +233,15 @@ static void check_duplicate_property_names(struct check *c, struct node *dt,
} }
} }
} }
NODE_ERROR(duplicate_property_names, NULL); ERROR(duplicate_property_names, check_duplicate_property_names, NULL);
#define LOWERCASE "abcdefghijklmnopqrstuvwxyz" #define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define DIGITS "0123456789" #define DIGITS "0123456789"
#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" #define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
#define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-"
static void check_node_name_chars(struct check *c, struct node *dt, static void check_node_name_chars(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
int n = strspn(node->name, c->data); int n = strspn(node->name, c->data);
@ -283,19 +250,30 @@ static void check_node_name_chars(struct check *c, struct node *dt,
FAIL(c, "Bad character '%c' in node %s", FAIL(c, "Bad character '%c' in node %s",
node->name[n], node->fullpath); node->name[n], node->fullpath);
} }
NODE_ERROR(node_name_chars, PROPNODECHARS "@"); ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@");
static void check_node_name_format(struct check *c, struct node *dt, 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, "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) struct node *node)
{ {
if (strchr(get_unitname(node), '@')) if (strchr(get_unitname(node), '@'))
FAIL(c, "Node %s has multiple '@' characters in name", FAIL(c, "Node %s has multiple '@' characters in name",
node->fullpath); node->fullpath);
} }
NODE_ERROR(node_name_format, NULL, &node_name_chars); ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
static void check_unit_address_vs_reg(struct check *c, struct node *dt, static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
const char *unitname = get_unitname(node); const char *unitname = get_unitname(node);
struct property *prop = get_property(node, "reg"); struct property *prop = get_property(node, "reg");
@ -316,18 +294,54 @@ static void check_unit_address_vs_reg(struct check *c, struct node *dt,
node->fullpath); node->fullpath);
} }
} }
NODE_WARNING(unit_address_vs_reg, NULL); WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL);
static void check_property_name_chars(struct check *c, struct node *dt, static void check_property_name_chars(struct check *c, struct dt_info *dti,
struct node *node, struct property *prop) struct node *node)
{ {
int n = strspn(prop->name, c->data); struct property *prop;
if (n < strlen(prop->name)) for_each_property(node, prop) {
FAIL(c, "Bad character '%c' in property name \"%s\", node %s", int n = strspn(prop->name, c->data);
prop->name[n], prop->name, node->fullpath);
if (n < strlen(prop->name))
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
prop->name[n], prop->name, node->fullpath);
}
} }
PROP_ERROR(property_name_chars, PROPNODECHARS); 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, "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_FMT "%s%s%s%s%s"
#define DESCLABEL_ARGS(node,prop,mark) \ #define DESCLABEL_ARGS(node,prop,mark) \
@ -336,10 +350,11 @@ PROP_ERROR(property_name_chars, PROPNODECHARS);
((prop) ? (prop)->name : ""), \ ((prop) ? (prop)->name : ""), \
((prop) ? "' in " : ""), (node)->fullpath ((prop) ? "' in " : ""), (node)->fullpath
static void check_duplicate_label(struct check *c, struct node *dt, static void check_duplicate_label(struct check *c, struct dt_info *dti,
const char *label, struct node *node, const char *label, struct node *node,
struct property *prop, struct marker *mark) struct property *prop, struct marker *mark)
{ {
struct node *dt = dti->dt;
struct node *othernode = NULL; struct node *othernode = NULL;
struct property *otherprop = NULL; struct property *otherprop = NULL;
struct marker *othermark = NULL; struct marker *othermark = NULL;
@ -362,44 +377,43 @@ static void check_duplicate_label(struct check *c, struct node *dt,
DESCLABEL_ARGS(othernode, otherprop, othermark)); DESCLABEL_ARGS(othernode, otherprop, othermark));
} }
static void check_duplicate_label_node(struct check *c, struct node *dt, static void check_duplicate_label_node(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct label *l; struct label *l;
struct property *prop;
for_each_label(node->labels, l) for_each_label(node->labels, l)
check_duplicate_label(c, dt, l->label, node, NULL, NULL); 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);
}
} }
static void check_duplicate_label_prop(struct check *c, struct node *dt, ERROR(duplicate_label, check_duplicate_label_node, NULL);
struct node *node, struct property *prop)
{ static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
struct marker *m = prop->val.markers; struct node *node, const char *propname)
struct label *l;
for_each_label(prop->labels, l)
check_duplicate_label(c, dt, l->label, node, prop, NULL);
for_each_marker_of_type(m, LABEL)
check_duplicate_label(c, dt, m->ref, node, prop, m);
}
ERROR(duplicate_label, NULL, check_duplicate_label_node,
check_duplicate_label_prop, NULL);
static void check_explicit_phandles(struct check *c, struct node *root,
struct node *node, struct property *prop)
{ {
struct node *root = dti->dt;
struct property *prop;
struct marker *m; struct marker *m;
struct node *other;
cell_t phandle; cell_t phandle;
if (!streq(prop->name, "phandle") prop = get_property(node, propname);
&& !streq(prop->name, "linux,phandle")) if (!prop)
return; return 0;
if (prop->val.len != sizeof(cell_t)) { if (prop->val.len != sizeof(cell_t)) {
FAIL(c, "%s has bad length (%d) %s property", FAIL(c, "%s has bad length (%d) %s property",
node->fullpath, prop->val.len, prop->name); node->fullpath, prop->val.len, prop->name);
return; return 0;
} }
m = prop->val.markers; m = prop->val.markers;
@ -411,14 +425,13 @@ static void check_explicit_phandles(struct check *c, struct node *root,
* by construction. */ { * by construction. */ {
FAIL(c, "%s in %s is a reference to another node", FAIL(c, "%s in %s is a reference to another node",
prop->name, node->fullpath); prop->name, node->fullpath);
return;
} }
/* But setting this node's phandle equal to its own /* But setting this node's phandle equal to its own
* phandle is allowed - that means allocate a unique * phandle is allowed - that means allocate a unique
* phandle for this node, even if it's not otherwise * phandle for this node, even if it's not otherwise
* referenced. The value will be filled in later, so * referenced. The value will be filled in later, so
* no further checking for now. */ * we treat it as having no phandle data for now. */
return; return 0;
} }
phandle = propval_cell(prop); phandle = propval_cell(prop);
@ -426,12 +439,36 @@ static void check_explicit_phandles(struct check *c, struct node *root,
if ((phandle == 0) || (phandle == -1)) { if ((phandle == 0) || (phandle == -1)) {
FAIL(c, "%s has bad value (0x%x) in %s property", FAIL(c, "%s has bad value (0x%x) in %s property",
node->fullpath, phandle, prop->name); node->fullpath, phandle, prop->name);
return; return 0;
} }
if (node->phandle && (node->phandle != phandle)) return phandle;
FAIL(c, "%s has %s property which replaces existing phandle information", }
node->fullpath, prop->name);
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, "%s has mismatching 'phandle' and 'linux,phandle'"
" properties", node->fullpath);
if (linux_phandle && !phandle)
phandle = linux_phandle;
other = get_node_by_phandle(root, phandle); other = get_node_by_phandle(root, phandle);
if (other && (other != node)) { if (other && (other != node)) {
@ -442,9 +479,9 @@ static void check_explicit_phandles(struct check *c, struct node *root,
node->phandle = phandle; node->phandle = phandle;
} }
PROP_ERROR(explicit_phandles, NULL); ERROR(explicit_phandles, check_explicit_phandles, NULL);
static void check_name_properties(struct check *c, struct node *root, static void check_name_properties(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property **pp, *prop = NULL; struct property **pp, *prop = NULL;
@ -472,64 +509,73 @@ static void check_name_properties(struct check *c, struct node *root,
} }
} }
ERROR_IF_NOT_STRING(name_is_string, "name"); ERROR_IF_NOT_STRING(name_is_string, "name");
NODE_ERROR(name_properties, NULL, &name_is_string); ERROR(name_properties, check_name_properties, NULL, &name_is_string);
/* /*
* Reference fixup functions * Reference fixup functions
*/ */
static void fixup_phandle_references(struct check *c, struct node *dt, static void fixup_phandle_references(struct check *c, struct dt_info *dti,
struct node *node, struct property *prop) struct node *node)
{ {
struct marker *m = prop->val.markers; struct node *dt = dti->dt;
struct node *refnode; struct property *prop;
cell_t phandle;
for_each_marker_of_type(m, REF_PHANDLE) { for_each_property(node, prop) {
assert(m->offset + sizeof(cell_t) <= prop->val.len); struct marker *m = prop->val.markers;
struct node *refnode;
cell_t phandle;
refnode = get_node_by_ref(dt, m->ref); for_each_marker_of_type(m, REF_PHANDLE) {
if (! refnode) { assert(m->offset + sizeof(cell_t) <= prop->val.len);
if (!source_is_plugin)
FAIL(c, "Reference to non-existent node or " refnode = get_node_by_ref(dt, m->ref);
"label \"%s\"\n", m->ref); if (! refnode) {
else /* mark the entry as unresolved */ if (!(dti->dtsflags & DTSF_PLUGIN))
*((cell_t *)(prop->val.val + m->offset)) = FAIL(c, "Reference to non-existent node or "
cpu_to_fdt32(0xffffffff); "label \"%s\"\n", m->ref);
continue; 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);
} }
phandle = get_node_phandle(dt, refnode);
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
} }
} }
ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, ERROR(phandle_references, fixup_phandle_references, NULL,
&duplicate_node_names, &explicit_phandles); &duplicate_node_names, &explicit_phandles);
static void fixup_path_references(struct check *c, struct node *dt, static void fixup_path_references(struct check *c, struct dt_info *dti,
struct node *node, struct property *prop) struct node *node)
{ {
struct marker *m = prop->val.markers; struct node *dt = dti->dt;
struct node *refnode; struct property *prop;
char *path;
for_each_marker_of_type(m, REF_PATH) { for_each_property(node, prop) {
assert(m->offset <= prop->val.len); struct marker *m = prop->val.markers;
struct node *refnode;
char *path;
refnode = get_node_by_ref(dt, m->ref); for_each_marker_of_type(m, REF_PATH) {
if (!refnode) { assert(m->offset <= prop->val.len);
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
m->ref); refnode = get_node_by_ref(dt, m->ref);
continue; if (!refnode) {
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
m->ref);
continue;
}
path = refnode->fullpath;
prop->val = data_insert_at_marker(prop->val, m, path,
strlen(path) + 1);
} }
path = refnode->fullpath;
prop->val = data_insert_at_marker(prop->val, m, path,
strlen(path) + 1);
} }
} }
ERROR(path_references, NULL, NULL, fixup_path_references, NULL, ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names);
&duplicate_node_names);
/* /*
* Semantic checks * Semantic checks
@ -542,7 +588,7 @@ WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
WARNING_IF_NOT_STRING(model_is_string, "model"); WARNING_IF_NOT_STRING(model_is_string, "model");
WARNING_IF_NOT_STRING(status_is_string, "status"); WARNING_IF_NOT_STRING(status_is_string, "status");
static void fixup_addr_size_cells(struct check *c, struct node *dt, static void fixup_addr_size_cells(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop; struct property *prop;
@ -558,7 +604,7 @@ static void fixup_addr_size_cells(struct check *c, struct node *dt,
if (prop) if (prop)
node->size_cells = propval_cell(prop); node->size_cells = propval_cell(prop);
} }
WARNING(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL, WARNING(addr_size_cells, fixup_addr_size_cells, NULL,
&address_cells_is_cell, &size_cells_is_cell); &address_cells_is_cell, &size_cells_is_cell);
#define node_addr_cells(n) \ #define node_addr_cells(n) \
@ -566,7 +612,7 @@ WARNING(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL,
#define node_size_cells(n) \ #define node_size_cells(n) \
(((n)->size_cells == -1) ? 1 : (n)->size_cells) (((n)->size_cells == -1) ? 1 : (n)->size_cells)
static void check_reg_format(struct check *c, struct node *dt, static void check_reg_format(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop; struct property *prop;
@ -593,9 +639,9 @@ static void check_reg_format(struct check *c, struct node *dt,
"(#address-cells == %d, #size-cells == %d)", "(#address-cells == %d, #size-cells == %d)",
node->fullpath, prop->val.len, addr_cells, size_cells); node->fullpath, prop->val.len, addr_cells, size_cells);
} }
NODE_WARNING(reg_format, NULL, &addr_size_cells); WARNING(reg_format, check_reg_format, NULL, &addr_size_cells);
static void check_ranges_format(struct check *c, struct node *dt, static void check_ranges_format(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *prop; struct property *prop;
@ -634,12 +680,12 @@ static void check_ranges_format(struct check *c, struct node *dt,
p_addr_cells, c_addr_cells, c_size_cells); p_addr_cells, c_addr_cells, c_size_cells);
} }
} }
NODE_WARNING(ranges_format, NULL, &addr_size_cells); WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells);
/* /*
* Style checks * Style checks
*/ */
static void check_avoid_default_addr_size(struct check *c, struct node *dt, static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti,
struct node *node) struct node *node)
{ {
struct property *reg, *ranges; struct property *reg, *ranges;
@ -661,14 +707,21 @@ static void check_avoid_default_addr_size(struct check *c, struct node *dt,
FAIL(c, "Relying on default #size-cells value for %s", FAIL(c, "Relying on default #size-cells value for %s",
node->fullpath); node->fullpath);
} }
NODE_WARNING(avoid_default_addr_size, NULL, &addr_size_cells); WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL,
&addr_size_cells);
static void check_obsolete_chosen_interrupt_controller(struct check *c, static void check_obsolete_chosen_interrupt_controller(struct check *c,
struct node *dt) struct dt_info *dti,
struct node *node)
{ {
struct node *dt = dti->dt;
struct node *chosen; struct node *chosen;
struct property *prop; struct property *prop;
if (node != dt)
return;
chosen = get_node_by_path(dt, "/chosen"); chosen = get_node_by_path(dt, "/chosen");
if (!chosen) if (!chosen)
return; return;
@ -678,16 +731,8 @@ static void check_obsolete_chosen_interrupt_controller(struct check *c,
FAIL(c, "/chosen has obsolete \"interrupt-controller\" " FAIL(c, "/chosen has obsolete \"interrupt-controller\" "
"property"); "property");
} }
TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); WARNING(obsolete_chosen_interrupt_controller,
check_obsolete_chosen_interrupt_controller, NULL);
static void check_deprecated_plugin_syntax(struct check *c,
struct node *dt)
{
if (deprecated_plugin_syntax_warning)
FAIL(c, "Use '/dts-v1/ /plugin/'; syntax. /dts-v1/; /plugin/; "
"is going to be removed in next versions");
}
TREE_WARNING(deprecated_plugin_syntax, NULL);
static struct check *check_table[] = { static struct check *check_table[] = {
&duplicate_node_names, &duplicate_property_names, &duplicate_node_names, &duplicate_property_names,
@ -702,13 +747,15 @@ static struct check *check_table[] = {
&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
&device_type_is_string, &model_is_string, &status_is_string, &device_type_is_string, &model_is_string, &status_is_string,
&property_name_chars_strict,
&node_name_chars_strict,
&addr_size_cells, &reg_format, &ranges_format, &addr_size_cells, &reg_format, &ranges_format,
&unit_address_vs_reg, &unit_address_vs_reg,
&avoid_default_addr_size, &avoid_default_addr_size,
&obsolete_chosen_interrupt_controller, &obsolete_chosen_interrupt_controller,
&deprecated_plugin_syntax,
&always_fail, &always_fail,
}; };
@ -774,9 +821,8 @@ void parse_checks_option(bool warn, bool error, const char *arg)
die("Unrecognized check name \"%s\"\n", name); die("Unrecognized check name \"%s\"\n", name);
} }
void process_checks(bool force, struct boot_info *bi) void process_checks(bool force, struct dt_info *dti)
{ {
struct node *dt = bi->dt;
int i; int i;
int error = 0; int error = 0;
@ -784,7 +830,7 @@ void process_checks(bool force, struct boot_info *bi)
struct check *c = check_table[i]; struct check *c = check_table[i];
if (c->warn || c->error) if (c->warn || c->error)
error = error || run_check(c, dt); error = error || run_check(c, dti);
} }
if (error) { if (error) {

View file

@ -223,6 +223,8 @@ static void convert_file(const char *fname)
while(yylex()) while(yylex())
; ;
free(newname);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])

View file

@ -189,16 +189,16 @@ static void lexical_error(const char *fmt, ...);
if (d.len == 1) { if (d.len == 1) {
lexical_error("Empty character literal"); lexical_error("Empty character literal");
yylval.integer = 0; yylval.integer = 0;
return DT_CHAR_LITERAL; } 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);
} }
yylval.integer = (unsigned char)d.val[0]; data_free(d);
if (d.len > 2)
lexical_error("Character literal has %d"
" characters instead of 1",
d.len - 1);
return DT_CHAR_LITERAL; return DT_CHAR_LITERAL;
} }

View file

@ -32,7 +32,7 @@ extern void yyerror(char const *s);
treesource_error = true; \ treesource_error = true; \
} while (0) } while (0)
extern struct boot_info *the_boot_info; extern struct dt_info *parser_output;
extern bool treesource_error; extern bool treesource_error;
%} %}
@ -53,7 +53,7 @@ extern bool treesource_error;
struct node *nodelist; struct node *nodelist;
struct reserve_info *re; struct reserve_info *re;
uint64_t integer; uint64_t integer;
bool is_plugin; unsigned int flags;
} }
%token DT_V1 %token DT_V1
@ -74,7 +74,8 @@ extern bool treesource_error;
%type <data> propdata %type <data> propdata
%type <data> propdataprefix %type <data> propdataprefix
%type <is_plugin> plugindecl %type <flags> header
%type <flags> headers
%type <re> memreserve %type <re> memreserve
%type <re> memreserves %type <re> memreserves
%type <array> arrayprefix %type <array> arrayprefix
@ -105,39 +106,31 @@ extern bool treesource_error;
%% %%
sourcefile: sourcefile:
basesource headers memreserves devicetree
| pluginsource
;
basesource:
DT_V1 ';' plugindecl memreserves devicetree
{ {
source_is_plugin = $3; parser_output = build_dt_info($1, $2, $3,
if (source_is_plugin) guess_boot_cpuid($3));
deprecated_plugin_syntax_warning = true;
the_boot_info = build_boot_info($4, $5,
guess_boot_cpuid($5));
} }
; ;
plugindecl: header:
/* empty */ DT_V1 ';'
{ {
$$ = false; $$ = DTSF_V1;
} }
| DT_PLUGIN ';' | DT_V1 ';' DT_PLUGIN ';'
{ {
$$ = true; $$ = DTSF_V1 | DTSF_PLUGIN;
} }
; ;
pluginsource: headers:
DT_V1 DT_PLUGIN ';' memreserves devicetree header
| header headers
{ {
source_is_plugin = true; if ($2 != $1)
deprecated_plugin_syntax_warning = false; ERROR(&@2, "Header flags don't match earlier ones");
the_boot_info = build_boot_info($4, $5, $$ = $1;
guess_boot_cpuid($5));
} }
; ;
@ -178,10 +171,10 @@ devicetree:
{ {
struct node *target = get_node_by_ref($1, $3); struct node *target = get_node_by_ref($1, $3);
add_label(&target->labels, $2); if (target) {
if (target) add_label(&target->labels, $2);
merge_nodes(target, $4); merge_nodes(target, $4);
else } else
ERROR(&@3, "Label or path %s not found", $3); ERROR(&@3, "Label or path %s not found", $3);
$$ = $1; $$ = $1;
} }
@ -189,14 +182,10 @@ devicetree:
{ {
struct node *target = get_node_by_ref($1, $2); struct node *target = get_node_by_ref($1, $2);
if (target) { if (target)
merge_nodes(target, $3); merge_nodes(target, $3);
} else { else
if (symbol_fixup_support) ERROR(&@2, "Label or path %s not found", $2);
add_orphan_node($1, $3, $2);
else
ERROR(&@2, "Label or path %s not found", $2);
}
$$ = $1; $$ = $1;
} }
| devicetree DT_DEL_NODE DT_REF ';' | devicetree DT_DEL_NODE DT_REF ';'
@ -211,11 +200,6 @@ devicetree:
$$ = $1; $$ = $1;
} }
| /* empty */
{
/* build empty node */
$$ = name_node(build_node(NULL, NULL), "");
}
; ;
nodedef: nodedef:

72
dtc.c
View file

@ -30,9 +30,16 @@ int quiet; /* Level of quietness */
int reservenum; /* Number of memory reservation slots */ int reservenum; /* Number of memory reservation slots */
int minsize; /* Minimum blob size */ int minsize; /* Minimum blob size */
int padsize; /* Additional padding to blob */ 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 phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */
int symbol_fixup_support; int generate_symbols; /* enable symbols & fixup support */
int auto_label_aliases; 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) static void fill_fullpaths(struct node *tree, const char *prefix)
{ {
@ -55,7 +62,7 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
#define FDT_VERSION(version) _FDT_VERSION(version) #define FDT_VERSION(version) _FDT_VERSION(version)
#define _FDT_VERSION(version) #version #define _FDT_VERSION(version) #version
static const char usage_synopsis[] = "dtc [options] <input file>"; static const char usage_synopsis[] = "dtc [options] <input file>";
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:@Ahv"; 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[] = { static struct option const usage_long_opts[] = {
{"quiet", no_argument, NULL, 'q'}, {"quiet", no_argument, NULL, 'q'},
{"in-format", a_argument, NULL, 'I'}, {"in-format", a_argument, NULL, 'I'},
@ -66,6 +73,7 @@ static struct option const usage_long_opts[] = {
{"reserve", a_argument, NULL, 'R'}, {"reserve", a_argument, NULL, 'R'},
{"space", a_argument, NULL, 'S'}, {"space", a_argument, NULL, 'S'},
{"pad", a_argument, NULL, 'p'}, {"pad", a_argument, NULL, 'p'},
{"align", a_argument, NULL, 'a'},
{"boot-cpu", a_argument, NULL, 'b'}, {"boot-cpu", a_argument, NULL, 'b'},
{"force", no_argument, NULL, 'f'}, {"force", no_argument, NULL, 'f'},
{"include", a_argument, NULL, 'i'}, {"include", a_argument, NULL, 'i'},
@ -95,6 +103,7 @@ static const char * const usage_opts_help[] = {
"\n\tMake space for <number> reserve map entries (for dtb and asm output)", "\n\tMake space for <number> reserve map entries (for dtb and asm output)",
"\n\tMake the blob at least <bytes> long (extra space)", "\n\tMake the blob at least <bytes> long (extra space)",
"\n\tAdd padding to the blob of <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\tSet the physical boot cpu",
"\n\tTry to produce output even if the input tree has errors", "\n\tTry to produce output even if the input tree has errors",
"\n\tAdd a path to search for include files", "\n\tAdd a path to search for include files",
@ -105,7 +114,7 @@ static const char * const usage_opts_help[] = {
"\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties",
"\n\tEnable/disable warnings (prefix with \"no-\")", "\n\tEnable/disable warnings (prefix with \"no-\")",
"\n\tEnable/disable errors (prefix with \"no-\")", "\n\tEnable/disable errors (prefix with \"no-\")",
"\n\tEnable symbols/fixup support", "\n\tEnable generation of symbols",
"\n\tEnable auto-alias of labels", "\n\tEnable auto-alias of labels",
"\n\tPrint this help and exit", "\n\tPrint this help and exit",
"\n\tPrint version and exit", "\n\tPrint version and exit",
@ -159,7 +168,7 @@ static const char *guess_input_format(const char *fname, const char *fallback)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct boot_info *bi; struct dt_info *dti;
const char *inform = NULL; const char *inform = NULL;
const char *outform = NULL; const char *outform = NULL;
const char *outname = "-"; const char *outname = "-";
@ -175,6 +184,7 @@ int main(int argc, char *argv[])
reservenum = 0; reservenum = 0;
minsize = 0; minsize = 0;
padsize = 0; padsize = 0;
alignsize = 0;
while ((opt = util_getopt_long()) != EOF) { while ((opt = util_getopt_long()) != EOF) {
switch (opt) { switch (opt) {
@ -202,6 +212,12 @@ int main(int argc, char *argv[])
case 'p': case 'p':
padsize = strtol(optarg, NULL, 0); padsize = strtol(optarg, NULL, 0);
break; break;
case 'a':
alignsize = strtol(optarg, NULL, 0);
if (!is_power_of_2(alignsize))
die("Invalid argument \"%d\" to -a option\n",
optarg);
break;
case 'f': case 'f':
force = true; force = true;
break; break;
@ -239,12 +255,14 @@ int main(int argc, char *argv[])
case 'E': case 'E':
parse_checks_option(false, true, optarg); parse_checks_option(false, true, optarg);
break; break;
case '@': case '@':
symbol_fixup_support = 1; generate_symbols = 1;
break; break;
case 'A': case 'A':
auto_label_aliases = 1; auto_label_aliases = 1;
break; break;
case 'h': case 'h':
usage(NULL); usage(NULL);
default: default:
@ -283,11 +301,11 @@ int main(int argc, char *argv[])
} }
} }
if (streq(inform, "dts")) if (streq(inform, "dts"))
bi = dt_from_source(arg); dti = dt_from_source(arg);
else if (streq(inform, "fs")) else if (streq(inform, "fs"))
bi = dt_from_fs(arg); dti = dt_from_fs(arg);
else if(streq(inform, "dtb")) else if(streq(inform, "dtb"))
bi = dt_from_blob(arg); dti = dt_from_blob(arg);
else else
die("Unknown input format \"%s\"\n", inform); die("Unknown input format \"%s\"\n", inform);
@ -297,19 +315,29 @@ int main(int argc, char *argv[])
} }
if (cmdline_boot_cpuid != -1) if (cmdline_boot_cpuid != -1)
bi->boot_cpuid_phys = cmdline_boot_cpuid; dti->boot_cpuid_phys = cmdline_boot_cpuid;
fill_fullpaths(bi->dt, ""); fill_fullpaths(dti->dt, "");
process_checks(force, bi); 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) if (sort)
sort_tree(bi); sort_tree(dti);
if (symbol_fixup_support || auto_label_aliases)
generate_label_node(bi->dt, bi->dt);
if (symbol_fixup_support)
generate_fixups_node(bi->dt, bi->dt);
if (streq(outname, "-")) { if (streq(outname, "-")) {
outf = stdout; outf = stdout;
@ -321,11 +349,11 @@ int main(int argc, char *argv[])
} }
if (streq(outform, "dts")) { if (streq(outform, "dts")) {
dt_to_source(outf, bi); dt_to_source(outf, dti);
} else if (streq(outform, "dtb")) { } else if (streq(outform, "dtb")) {
dt_to_blob(outf, bi, outversion); dt_to_blob(outf, dti, outversion);
} else if (streq(outform, "asm")) { } else if (streq(outform, "asm")) {
dt_to_asm(outf, bi, outversion); dt_to_asm(outf, dti, outversion);
} else if (streq(outform, "null")) { } else if (streq(outform, "null")) {
/* do nothing */ /* do nothing */
} else { } else {

50
dtc.h
View file

@ -20,7 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA * USA
*/ */
#define _GNU_SOURCE
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -53,16 +53,12 @@ extern int quiet; /* Level of quietness */
extern int reservenum; /* Number of memory reservation slots */ extern int reservenum; /* Number of memory reservation slots */
extern int minsize; /* Minimum blob size */ extern int minsize; /* Minimum blob size */
extern int padsize; /* Additional padding to blob */ 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 phandle_format; /* Use linux,phandle or phandle properties */
extern int symbol_fixup_support;/* enable symbols & fixup support */ 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 */ extern int auto_label_aliases; /* auto generate labels -> aliases */
/*
* Tree source globals
*/
extern bool source_is_plugin;
extern bool deprecated_plugin_syntax_warning;
#define PHANDLE_LEGACY 0x1 #define PHANDLE_LEGACY 0x1
#define PHANDLE_EPAPR 0x2 #define PHANDLE_EPAPR 0x2
#define PHANDLE_BOTH 0x3 #define PHANDLE_BOTH 0x3
@ -202,7 +198,6 @@ struct node *build_node_delete(void);
struct node *name_node(struct node *node, char *name); struct node *name_node(struct node *node, char *name);
struct node *chain_node(struct node *first, struct node *list); struct node *chain_node(struct node *first, struct node *list);
struct node *merge_nodes(struct node *old_node, struct node *new_node); struct node *merge_nodes(struct node *old_node, struct node *new_node);
void add_orphan_node(struct node *old_node, struct node *new_node, char *ref);
void add_property(struct node *node, struct property *prop); void add_property(struct node *node, struct property *prop);
void delete_property_by_name(struct node *node, char *name); void delete_property_by_name(struct node *node, char *name);
@ -210,6 +205,8 @@ void delete_property(struct property *prop);
void add_child(struct node *parent, struct node *child); void add_child(struct node *parent, struct node *child);
void delete_node_by_name(struct node *parent, char *name); void delete_node_by_name(struct node *parent, char *name);
void delete_node(struct node *node); 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); const char *get_unitname(struct node *node);
struct property *get_property(struct node *node, const char *propname); struct property *get_property(struct node *node, const char *propname);
@ -244,37 +241,44 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
struct reserve_info *new); struct reserve_info *new);
struct boot_info { struct dt_info {
unsigned int dtsflags;
struct reserve_info *reservelist; struct reserve_info *reservelist;
struct node *dt; /* the device tree */
uint32_t boot_cpuid_phys; uint32_t boot_cpuid_phys;
struct node *dt; /* the device tree */
}; };
struct boot_info *build_boot_info(struct reserve_info *reservelist, /* DTS version flags definitions */
struct node *tree, uint32_t boot_cpuid_phys); #define DTSF_V1 0x0001 /* /dts-v1/ */
void sort_tree(struct boot_info *bi); #define DTSF_PLUGIN 0x0002 /* /plugin/ */
void generate_label_node(struct node *node, struct node *dt);
void generate_fixups_node(struct node *node, struct node *dt); 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 */ /* Checks */
void parse_checks_option(bool warn, bool error, const char *arg); void parse_checks_option(bool warn, bool error, const char *arg);
void process_checks(bool force, struct boot_info *bi); void process_checks(bool force, struct dt_info *dti);
/* Flattened trees */ /* Flattened trees */
void dt_to_blob(FILE *f, struct boot_info *bi, int version); void dt_to_blob(FILE *f, struct dt_info *dti, int version);
void dt_to_asm(FILE *f, struct boot_info *bi, int version); void dt_to_asm(FILE *f, struct dt_info *dti, int version);
struct boot_info *dt_from_blob(const char *fname); struct dt_info *dt_from_blob(const char *fname);
/* Tree source */ /* Tree source */
void dt_to_source(FILE *f, struct boot_info *bi); void dt_to_source(FILE *f, struct dt_info *dti);
struct boot_info *dt_from_source(const char *f); struct dt_info *dt_from_source(const char *f);
/* FS trees */ /* FS trees */
struct boot_info *dt_from_fs(const char *dirname); struct dt_info *dt_from_fs(const char *dirname);
#endif /* _DTC_H */ #endif /* _DTC_H */

View file

@ -15,6 +15,9 @@
#include "util.h" #include "util.h"
#define FDT_MAGIC_SIZE 4
#define MAX_VERSION 17
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a)))) #define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
#define GET_CELL(p) (p += 4, *((const uint32_t *)(p-4))) #define GET_CELL(p) (p += 4, *((const uint32_t *)(p-4)))
@ -157,6 +160,20 @@ static const char * const usage_opts_help[] = {
USAGE_COMMON_OPTS_HELP USAGE_COMMON_OPTS_HELP
}; };
static bool valid_header(char *p, off_t len)
{
if (len < sizeof(struct fdt_header) ||
fdt_magic(p) != FDT_MAGIC ||
fdt_version(p) > MAX_VERSION ||
fdt_last_comp_version(p) >= MAX_VERSION ||
fdt_totalsize(p) >= len ||
fdt_off_dt_struct(p) >= len ||
fdt_off_dt_strings(p) >= len)
return 0;
else
return 1;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int opt; int opt;
@ -188,26 +205,21 @@ int main(int argc, char *argv[])
/* try and locate an embedded fdt in a bigger blob */ /* try and locate an embedded fdt in a bigger blob */
if (scan) { if (scan) {
unsigned char smagic[4]; unsigned char smagic[FDT_MAGIC_SIZE];
char *p = buf; char *p = buf;
char *endp = buf + len; char *endp = buf + len;
fdt_set_magic(smagic, FDT_MAGIC); fdt_set_magic(smagic, FDT_MAGIC);
/* poor man's memmem */ /* poor man's memmem */
while (true) { while ((endp - p) >= FDT_MAGIC_SIZE) {
p = memchr(p, smagic[0], endp - p - 4); p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
if (!p) if (!p)
break; break;
if (fdt_magic(p) == FDT_MAGIC) { if (fdt_magic(p) == FDT_MAGIC) {
/* try and validate the main struct */ /* try and validate the main struct */
off_t this_len = endp - p; off_t this_len = endp - p;
fdt32_t max_version = 17; if (valid_header(p, this_len))
if (fdt_version(p) <= max_version &&
fdt_last_comp_version(p) < max_version &&
fdt_totalsize(p) < this_len &&
fdt_off_dt_struct(p) < this_len &&
fdt_off_dt_strings(p) < this_len)
break; break;
if (debug) if (debug)
printf("%s: skipping fdt magic at offset %#zx\n", printf("%s: skipping fdt magic at offset %#zx\n",
@ -215,11 +227,12 @@ int main(int argc, char *argv[])
} }
++p; ++p;
} }
if (!p) if (!p || endp - p < sizeof(struct fdt_header))
die("%s: could not locate fdt magic\n", file); die("%s: could not locate fdt magic\n", file);
printf("%s: found fdt at offset %#zx\n", file, p - buf); printf("%s: found fdt at offset %#zx\n", file, p - buf);
buf = p; buf = p;
} } else if (!valid_header(buf, len))
die("%s: header is not valid\n", file);
dump_blob(buf, debug); dump_blob(buf, debug);

View file

@ -266,14 +266,20 @@ static int do_fdtget(struct display_info *disp, const char *filename,
continue; continue;
} else { } else {
report_error(arg[i], node); report_error(arg[i], node);
free(blob);
return -1; return -1;
} }
} }
prop = args_per_step == 1 ? NULL : arg[i + 1]; prop = args_per_step == 1 ? NULL : arg[i + 1];
if (show_data_for_item(blob, disp, node, prop)) if (show_data_for_item(blob, disp, node, prop)) {
free(blob);
return -1; return -1;
}
} }
free(blob);
return 0; return 0;
} }

View file

@ -328,7 +328,7 @@ static int delete_node(char *blob, const char *node_name)
static int do_fdtput(struct display_info *disp, const char *filename, static int do_fdtput(struct display_info *disp, const char *filename,
char **arg, int arg_count) char **arg, int arg_count)
{ {
char *value; char *value = NULL;
char *blob; char *blob;
char *node; char *node;
int len, ret = 0; int len, ret = 0;
@ -374,6 +374,11 @@ static int do_fdtput(struct display_info *disp, const char *filename,
} }
free(blob); free(blob);
if (value) {
free(value);
}
return ret; return ret;
} }

View file

@ -366,7 +366,7 @@ static void make_fdt_header(struct fdt_header *fdt,
fdt->size_dt_struct = cpu_to_fdt32(dtsize); fdt->size_dt_struct = cpu_to_fdt32(dtsize);
} }
void dt_to_blob(FILE *f, struct boot_info *bi, int version) void dt_to_blob(FILE *f, struct dt_info *dti, int version)
{ {
struct version_info *vi = NULL; struct version_info *vi = NULL;
int i; int i;
@ -384,29 +384,36 @@ void dt_to_blob(FILE *f, struct boot_info *bi, int version)
if (!vi) if (!vi)
die("Unknown device tree blob version %d\n", version); die("Unknown device tree blob version %d\n", version);
flatten_tree(bi->dt, &bin_emitter, &dtbuf, &strbuf, vi); flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
bin_emit_cell(&dtbuf, FDT_END); bin_emit_cell(&dtbuf, FDT_END);
reservebuf = flatten_reserve_list(bi->reservelist, vi); reservebuf = flatten_reserve_list(dti->reservelist, vi);
/* Make header */ /* Make header */
make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
bi->boot_cpuid_phys); dti->boot_cpuid_phys);
/* /*
* If the user asked for more space than is used, adjust the totalsize. * If the user asked for more space than is used, adjust the totalsize.
*/ */
if (minsize > 0) { if (minsize > 0) {
padlen = minsize - fdt32_to_cpu(fdt.totalsize); padlen = minsize - fdt32_to_cpu(fdt.totalsize);
if ((padlen < 0) && (quiet < 1)) if (padlen < 0) {
fprintf(stderr, padlen = 0;
"Warning: blob size %d >= minimum size %d\n", if (quiet < 1)
fdt32_to_cpu(fdt.totalsize), minsize); fprintf(stderr,
"Warning: blob size %d >= minimum size %d\n",
fdt32_to_cpu(fdt.totalsize), minsize);
}
} }
if (padsize > 0) if (padsize > 0)
padlen = padsize; padlen = padsize;
if (alignsize > 0)
padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize)
- fdt32_to_cpu(fdt.totalsize);
if (padlen > 0) { if (padlen > 0) {
int tsize = fdt32_to_cpu(fdt.totalsize); int tsize = fdt32_to_cpu(fdt.totalsize);
tsize += padlen; tsize += padlen;
@ -460,7 +467,7 @@ static void dump_stringtable_asm(FILE *f, struct data strbuf)
} }
} }
void dt_to_asm(FILE *f, struct boot_info *bi, int version) void dt_to_asm(FILE *f, struct dt_info *dti, int version)
{ {
struct version_info *vi = NULL; struct version_info *vi = NULL;
int i; int i;
@ -500,7 +507,7 @@ void dt_to_asm(FILE *f, struct boot_info *bi, int version)
if (vi->flags & FTF_BOOTCPUID) { if (vi->flags & FTF_BOOTCPUID) {
fprintf(f, "\t/* boot_cpuid_phys */\n"); fprintf(f, "\t/* boot_cpuid_phys */\n");
asm_emit_cell(f, bi->boot_cpuid_phys); asm_emit_cell(f, dti->boot_cpuid_phys);
} }
if (vi->flags & FTF_STRTABSIZE) { if (vi->flags & FTF_STRTABSIZE) {
@ -530,7 +537,7 @@ void dt_to_asm(FILE *f, struct boot_info *bi, int version)
* Use .long on high and low halfs of u64s to avoid .quad * Use .long on high and low halfs of u64s to avoid .quad
* as it appears .quad isn't available in some assemblers. * as it appears .quad isn't available in some assemblers.
*/ */
for (re = bi->reservelist; re; re = re->next) { for (re = dti->reservelist; re; re = re->next) {
struct label *l; struct label *l;
for_each_label(re->labels, l) { for_each_label(re->labels, l) {
@ -550,7 +557,7 @@ void dt_to_asm(FILE *f, struct boot_info *bi, int version)
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"); emit_label(f, symprefix, "struct_start");
flatten_tree(bi->dt, &asm_emitter, f, &strbuf, vi); flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
fprintf(f, "\t/* FDT_END */\n"); fprintf(f, "\t/* FDT_END */\n");
asm_emit_cell(f, FDT_END); asm_emit_cell(f, FDT_END);
@ -572,6 +579,8 @@ void dt_to_asm(FILE *f, struct boot_info *bi, int version)
if (padsize > 0) { if (padsize > 0) {
fprintf(f, "\t.space\t%d, 0\n", padsize); fprintf(f, "\t.space\t%d, 0\n", padsize);
} }
if (alignsize > 0)
asm_emit_align(f, alignsize);
emit_label(f, symprefix, "blob_abs_end"); emit_label(f, symprefix, "blob_abs_end");
data_free(strbuf); data_free(strbuf);
@ -797,11 +806,15 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
} }
} while (val != FDT_END_NODE); } while (val != FDT_END_NODE);
if (node->name != flatname) {
free(flatname);
}
return node; return node;
} }
struct boot_info *dt_from_blob(const char *fname) struct dt_info *dt_from_blob(const char *fname)
{ {
FILE *f; FILE *f;
uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
@ -929,5 +942,5 @@ struct boot_info *dt_from_blob(const char *fname)
fclose(f); fclose(f);
return build_boot_info(reservelist, tree, boot_cpuid_phys); return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
} }

View file

@ -79,13 +79,12 @@ static struct node *read_fstree(const char *dirname)
return tree; return tree;
} }
struct boot_info *dt_from_fs(const char *dirname) struct dt_info *dt_from_fs(const char *dirname)
{ {
struct node *tree; struct node *tree;
tree = read_fstree(dirname); tree = read_fstree(dirname);
tree = name_node(tree, ""); tree = name_node(tree, "");
return build_boot_info(NULL, tree, guess_boot_cpuid(tree)); return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
} }

View file

@ -8,7 +8,8 @@ common_src_files := \
fdt_rw.c \ fdt_rw.c \
fdt_strerror.c \ fdt_strerror.c \
fdt_empty_tree.c \ fdt_empty_tree.c \
fdt_addresses.c fdt_addresses.c \
fdt_overlay.c
################################################# #################################################

View file

@ -7,5 +7,5 @@ LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
LIBFDT_VERSION = version.lds LIBFDT_VERSION = version.lds
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \ LIBFDT_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_addresses.c fdt_overlay.c
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)

676
libfdt/fdt_overlay.c Normal file
View file

@ -0,0 +1,676 @@
#include "libfdt_env.h"
#include <fdt.h>
#include <libfdt.h>
#include "libfdt_internal.h"
/**
* overlay_get_target_phandle - retrieves the target phandle of a fragment
* @fdto: pointer to the device tree overlay blob
* @fragment: node offset of the fragment in the overlay
*
* overlay_get_target_phandle() retrieves the target phandle of an
* overlay fragment when that fragment uses a phandle (target
* property) instead of a path (target-path property).
*
* returns:
* the phandle pointed by the target property
* 0, if the phandle was not found
* -1, if the phandle was malformed
*/
static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
{
const uint32_t *val;
int len;
val = fdt_getprop(fdto, fragment, "target", &len);
if (!val)
return 0;
if ((len != sizeof(*val)) || (*val == (uint32_t)-1))
return (uint32_t)-1;
return fdt32_to_cpu(*val);
}
/**
* overlay_get_target - retrieves the offset of a fragment's target
* @fdt: Base device tree blob
* @fdto: Device tree overlay blob
* @fragment: node offset of the fragment in the overlay
*
* overlay_get_target() retrieves the target offset in the base
* device tree of a fragment, no matter how the actual targetting is
* done (through a phandle or a path)
*
* returns:
* the targetted node offset in the base device tree
* Negative error code on error
*/
static int overlay_get_target(const void *fdt, const void *fdto,
int fragment)
{
uint32_t phandle;
const char *path;
int path_len;
/* Try first to do a phandle based lookup */
phandle = overlay_get_target_phandle(fdto, fragment);
if (phandle == (uint32_t)-1)
return -FDT_ERR_BADPHANDLE;
if (phandle)
return fdt_node_offset_by_phandle(fdt, phandle);
/* And then a path based lookup */
path = fdt_getprop(fdto, fragment, "target-path", &path_len);
if (!path) {
/*
* If we haven't found either a target or a
* target-path property in a node that contains a
* __overlay__ subnode (we wouldn't be called
* otherwise), consider it a improperly written
* overlay
*/
if (path_len == -FDT_ERR_NOTFOUND)
return -FDT_ERR_BADOVERLAY;
return path_len;
}
return fdt_path_offset(fdt, path);
}
/**
* overlay_phandle_add_offset - Increases a phandle by an offset
* @fdt: Base device tree blob
* @node: Device tree overlay blob
* @name: Name of the property to modify (phandle or linux,phandle)
* @delta: offset to apply
*
* overlay_phandle_add_offset() increments a node phandle by a given
* offset.
*
* returns:
* 0 on success.
* Negative error code on error
*/
static int overlay_phandle_add_offset(void *fdt, int node,
const char *name, uint32_t delta)
{
const uint32_t *val;
uint32_t adj_val;
int len;
val = fdt_getprop(fdt, node, name, &len);
if (!val)
return len;
if (len != sizeof(*val))
return -FDT_ERR_BADPHANDLE;
adj_val = fdt32_to_cpu(*val);
if ((adj_val + delta) < adj_val)
return -FDT_ERR_NOPHANDLES;
adj_val += delta;
if (adj_val == (uint32_t)-1)
return -FDT_ERR_NOPHANDLES;
return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
}
/**
* overlay_adjust_node_phandles - Offsets the phandles of a node
* @fdto: Device tree overlay blob
* @node: Offset of the node we want to adjust
* @delta: Offset to shift the phandles of
*
* overlay_adjust_node_phandles() adds a constant to all the phandles
* of a given node. This is mainly use as part of the overlay
* application process, when we want to update all the overlay
* phandles to not conflict with the overlays of the base device tree.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_adjust_node_phandles(void *fdto, int node,
uint32_t delta)
{
int child;
int ret;
ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
if (ret && ret != -FDT_ERR_NOTFOUND)
return ret;
ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
if (ret && ret != -FDT_ERR_NOTFOUND)
return ret;
fdt_for_each_subnode(child, fdto, node) {
ret = overlay_adjust_node_phandles(fdto, child, delta);
if (ret)
return ret;
}
return 0;
}
/**
* overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
* @fdto: Device tree overlay blob
* @delta: Offset to shift the phandles of
*
* overlay_adjust_local_phandles() adds a constant to all the
* phandles of an overlay. This is mainly use as part of the overlay
* application process, when we want to update all the overlay
* phandles to not conflict with the overlays of the base device tree.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
{
/*
* Start adjusting the phandles from the overlay root
*/
return overlay_adjust_node_phandles(fdto, 0, delta);
}
/**
* overlay_update_local_node_references - Adjust the overlay references
* @fdto: Device tree overlay blob
* @tree_node: Node offset of the node to operate on
* @fixup_node: Node offset of the matching local fixups node
* @delta: Offset to shift the phandles of
*
* overlay_update_local_nodes_references() update the phandles
* pointing to a node within the device tree overlay by adding a
* constant delta.
*
* This is mainly used as part of a device tree application process,
* where you want the device tree overlays phandles to not conflict
* with the ones from the base device tree before merging them.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_update_local_node_references(void *fdto,
int tree_node,
int fixup_node,
uint32_t delta)
{
int fixup_prop;
int fixup_child;
int ret;
fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
const uint32_t *fixup_val;
const char *tree_val;
const char *name;
int fixup_len;
int tree_len;
int i;
fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
&name, &fixup_len);
if (!fixup_val)
return fixup_len;
if (fixup_len % sizeof(uint32_t))
return -FDT_ERR_BADOVERLAY;
tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
if (!tree_val) {
if (tree_len == -FDT_ERR_NOTFOUND)
return -FDT_ERR_BADOVERLAY;
return tree_len;
}
for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
uint32_t adj_val, poffset;
poffset = fdt32_to_cpu(fixup_val[i]);
/*
* phandles to fixup can be unaligned.
*
* Use a memcpy for the architectures that do
* not support unaligned accesses.
*/
memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
adj_val = fdt32_to_cpu(adj_val);
adj_val += delta;
adj_val = cpu_to_fdt32(adj_val);
ret = fdt_setprop_inplace_namelen_partial(fdto,
tree_node,
name,
strlen(name),
poffset,
&adj_val,
sizeof(adj_val));
if (ret == -FDT_ERR_NOSPACE)
return -FDT_ERR_BADOVERLAY;
if (ret)
return ret;
}
}
fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
NULL);
int tree_child;
tree_child = fdt_subnode_offset(fdto, tree_node,
fixup_child_name);
if (ret == -FDT_ERR_NOTFOUND)
return -FDT_ERR_BADOVERLAY;
if (tree_child < 0)
return tree_child;
ret = overlay_update_local_node_references(fdto,
tree_child,
fixup_child,
delta);
if (ret)
return ret;
}
return 0;
}
/**
* overlay_update_local_references - Adjust the overlay references
* @fdto: Device tree overlay blob
* @delta: Offset to shift the phandles of
*
* overlay_update_local_references() update all the phandles pointing
* to a node within the device tree overlay by adding a constant
* delta to not conflict with the base overlay.
*
* This is mainly used as part of a device tree application process,
* where you want the device tree overlays phandles to not conflict
* with the ones from the base device tree before merging them.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_update_local_references(void *fdto, uint32_t delta)
{
int fixups;
fixups = fdt_path_offset(fdto, "/__local_fixups__");
if (fixups < 0) {
/* There's no local phandles to adjust, bail out */
if (fixups == -FDT_ERR_NOTFOUND)
return 0;
return fixups;
}
/*
* Update our local references from the root of the tree
*/
return overlay_update_local_node_references(fdto, 0, fixups,
delta);
}
/**
* overlay_fixup_one_phandle - Set an overlay phandle to the base one
* @fdt: Base Device Tree blob
* @fdto: Device tree overlay blob
* @symbols_off: Node offset of the symbols node in the base device tree
* @path: Path to a node holding a phandle in the overlay
* @path_len: number of path characters to consider
* @name: Name of the property holding the phandle reference in the overlay
* @name_len: number of name characters to consider
* @poffset: Offset within the overlay property where the phandle is stored
* @label: Label of the node referenced by the phandle
*
* overlay_fixup_one_phandle() resolves an overlay phandle pointing to
* a node in the base device tree.
*
* This is part of the device tree overlay application process, when
* you want all the phandles in the overlay to point to the actual
* base dt nodes.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_fixup_one_phandle(void *fdt, void *fdto,
int symbols_off,
const char *path, uint32_t path_len,
const char *name, uint32_t name_len,
int poffset, const char *label)
{
const char *symbol_path;
uint32_t phandle;
int symbol_off, fixup_off;
int prop_len;
if (symbols_off < 0)
return symbols_off;
symbol_path = fdt_getprop(fdt, symbols_off, label,
&prop_len);
if (!symbol_path)
return prop_len;
symbol_off = fdt_path_offset(fdt, symbol_path);
if (symbol_off < 0)
return symbol_off;
phandle = fdt_get_phandle(fdt, symbol_off);
if (!phandle)
return -FDT_ERR_NOTFOUND;
fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
if (fixup_off == -FDT_ERR_NOTFOUND)
return -FDT_ERR_BADOVERLAY;
if (fixup_off < 0)
return fixup_off;
phandle = cpu_to_fdt32(phandle);
return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
name, name_len, poffset,
&phandle, sizeof(phandle));
};
/**
* overlay_fixup_phandle - Set an overlay phandle to the base one
* @fdt: Base Device Tree blob
* @fdto: Device tree overlay blob
* @symbols_off: Node offset of the symbols node in the base device tree
* @property: Property offset in the overlay holding the list of fixups
*
* overlay_fixup_phandle() resolves all the overlay phandles pointed
* to in a __fixups__ property, and updates them to match the phandles
* in use in the base device tree.
*
* This is part of the device tree overlay application process, when
* you want all the phandles in the overlay to point to the actual
* base dt nodes.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
int property)
{
const char *value;
const char *label;
int len;
value = fdt_getprop_by_offset(fdto, property,
&label, &len);
if (!value) {
if (len == -FDT_ERR_NOTFOUND)
return -FDT_ERR_INTERNAL;
return len;
}
do {
const char *path, *name, *fixup_end;
const char *fixup_str = value;
uint32_t path_len, name_len;
uint32_t fixup_len;
char *sep, *endptr;
int poffset, ret;
fixup_end = memchr(value, '\0', len);
if (!fixup_end)
return -FDT_ERR_BADOVERLAY;
fixup_len = fixup_end - fixup_str;
len -= fixup_len + 1;
value += fixup_len + 1;
path = fixup_str;
sep = memchr(fixup_str, ':', fixup_len);
if (!sep || *sep != ':')
return -FDT_ERR_BADOVERLAY;
path_len = sep - path;
if (path_len == (fixup_len - 1))
return -FDT_ERR_BADOVERLAY;
fixup_len -= path_len + 1;
name = sep + 1;
sep = memchr(name, ':', fixup_len);
if (!sep || *sep != ':')
return -FDT_ERR_BADOVERLAY;
name_len = sep - name;
if (!name_len)
return -FDT_ERR_BADOVERLAY;
poffset = strtoul(sep + 1, &endptr, 10);
if ((*endptr != '\0') || (endptr <= (sep + 1)))
return -FDT_ERR_BADOVERLAY;
ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
path, path_len, name, name_len,
poffset, label);
if (ret)
return ret;
} while (len > 0);
return 0;
}
/**
* overlay_fixup_phandles - Resolve the overlay phandles to the base
* device tree
* @fdt: Base Device Tree blob
* @fdto: Device tree overlay blob
*
* overlay_fixup_phandles() resolves all the overlay phandles pointing
* to nodes in the base device tree.
*
* This is one of the steps of the device tree overlay application
* process, when you want all the phandles in the overlay to point to
* the actual base dt nodes.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_fixup_phandles(void *fdt, void *fdto)
{
int fixups_off, symbols_off;
int property;
/* We can have overlays without any fixups */
fixups_off = fdt_path_offset(fdto, "/__fixups__");
if (fixups_off == -FDT_ERR_NOTFOUND)
return 0; /* nothing to do */
if (fixups_off < 0)
return fixups_off;
/* And base DTs without symbols */
symbols_off = fdt_path_offset(fdt, "/__symbols__");
if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
return symbols_off;
fdt_for_each_property_offset(property, fdto, fixups_off) {
int ret;
ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
if (ret)
return ret;
}
return 0;
}
/**
* overlay_apply_node - Merges a node into the base device tree
* @fdt: Base Device Tree blob
* @target: Node offset in the base device tree to apply the fragment to
* @fdto: Device tree overlay blob
* @node: Node offset in the overlay holding the changes to merge
*
* overlay_apply_node() merges a node into a target base device tree
* node pointed.
*
* This is part of the final step in the device tree overlay
* application process, when all the phandles have been adjusted and
* resolved and you just have to merge overlay into the base device
* tree.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_apply_node(void *fdt, int target,
void *fdto, int node)
{
int property;
int subnode;
fdt_for_each_property_offset(property, fdto, node) {
const char *name;
const void *prop;
int prop_len;
int ret;
prop = fdt_getprop_by_offset(fdto, property, &name,
&prop_len);
if (prop_len == -FDT_ERR_NOTFOUND)
return -FDT_ERR_INTERNAL;
if (prop_len < 0)
return prop_len;
ret = fdt_setprop(fdt, target, name, prop, prop_len);
if (ret)
return ret;
}
fdt_for_each_subnode(subnode, fdto, node) {
const char *name = fdt_get_name(fdto, subnode, NULL);
int nnode;
int ret;
nnode = fdt_add_subnode(fdt, target, name);
if (nnode == -FDT_ERR_EXISTS) {
nnode = fdt_subnode_offset(fdt, target, name);
if (nnode == -FDT_ERR_NOTFOUND)
return -FDT_ERR_INTERNAL;
}
if (nnode < 0)
return nnode;
ret = overlay_apply_node(fdt, nnode, fdto, subnode);
if (ret)
return ret;
}
return 0;
}
/**
* overlay_merge - Merge an overlay into its base device tree
* @fdt: Base Device Tree blob
* @fdto: Device tree overlay blob
*
* overlay_merge() merges an overlay into its base device tree.
*
* This is the final step in the device tree overlay application
* process, when all the phandles have been adjusted and resolved and
* you just have to merge overlay into the base device tree.
*
* returns:
* 0 on success
* Negative error code on failure
*/
static int overlay_merge(void *fdt, void *fdto)
{
int fragment;
fdt_for_each_subnode(fragment, fdto, 0) {
int overlay;
int target;
int ret;
/*
* Each fragments will have an __overlay__ node. If
* they don't, it's not supposed to be merged
*/
overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
if (overlay == -FDT_ERR_NOTFOUND)
continue;
if (overlay < 0)
return overlay;
target = overlay_get_target(fdt, fdto, fragment);
if (target < 0)
return target;
ret = overlay_apply_node(fdt, target, fdto, overlay);
if (ret)
return ret;
}
return 0;
}
int fdt_overlay_apply(void *fdt, void *fdto)
{
uint32_t delta = fdt_get_max_phandle(fdt);
int ret;
FDT_CHECK_HEADER(fdt);
FDT_CHECK_HEADER(fdto);
ret = overlay_adjust_local_phandles(fdto, delta);
if (ret)
goto err;
ret = overlay_update_local_references(fdto, delta);
if (ret)
goto err;
ret = overlay_fixup_phandles(fdt, fdto);
if (ret)
goto err;
ret = overlay_merge(fdt, fdto);
if (ret)
goto err;
/*
* The overlay has been damaged, erase its magic.
*/
fdt_set_magic(fdto, ~0);
return 0;
err:
/*
* The overlay might have been damaged, erase its magic.
*/
fdt_set_magic(fdto, ~0);
/*
* The base device tree might have been damaged, erase its
* magic.
*/
fdt_set_magic(fdt, ~0);
return ret;
}

View file

@ -88,6 +88,32 @@ static int _fdt_string_eq(const void *fdt, int stroffset,
return (strlen(p) == len) && (memcmp(p, s, len) == 0); 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) int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{ {
FDT_CHECK_HEADER(fdt); FDT_CHECK_HEADER(fdt);
@ -545,7 +571,7 @@ int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
list = fdt_getprop(fdt, nodeoffset, property, &length); list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list) if (!list)
return -length; return length;
end = list + length; end = list + length;
@ -571,7 +597,7 @@ int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
list = fdt_getprop(fdt, nodeoffset, property, &length); list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list) if (!list)
return -length; return length;
len = strlen(string) + 1; len = strlen(string) + 1;
end = list + length; end = list + length;
@ -647,10 +673,8 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
if (!prop) if (!prop)
return len; return len;
if (fdt_stringlist_contains(prop, len, compatible))
return 0; return !fdt_stringlist_contains(prop, len, compatible);
else
return 1;
} }
int fdt_node_offset_by_compatible(const void *fdt, int startoffset, int fdt_node_offset_by_compatible(const void *fdt, int startoffset,

View file

@ -191,17 +191,13 @@ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
int fdt_del_mem_rsv(void *fdt, int n) int fdt_del_mem_rsv(void *fdt, int n)
{ {
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
int err;
FDT_RW_CHECK_HEADER(fdt); FDT_RW_CHECK_HEADER(fdt);
if (n >= fdt_num_mem_rsv(fdt)) if (n >= fdt_num_mem_rsv(fdt))
return -FDT_ERR_NOTFOUND; return -FDT_ERR_NOTFOUND;
err = _fdt_splice_mem_rsv(fdt, re, 1, 0); return _fdt_splice_mem_rsv(fdt, re, 1, 0);
if (err)
return err;
return 0;
} }
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,

View file

@ -69,6 +69,7 @@ static struct fdt_errtabent fdt_errtable[] = {
FDT_ERRTABENT(FDT_ERR_BADOFFSET), FDT_ERRTABENT(FDT_ERR_BADOFFSET),
FDT_ERRTABENT(FDT_ERR_BADPATH), FDT_ERRTABENT(FDT_ERR_BADPATH),
FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
FDT_ERRTABENT(FDT_ERR_BADSTATE), FDT_ERRTABENT(FDT_ERR_BADSTATE),
FDT_ERRTABENT(FDT_ERR_TRUNCATED), FDT_ERRTABENT(FDT_ERR_TRUNCATED),
@ -76,6 +77,11 @@ static struct fdt_errtabent fdt_errtable[] = {
FDT_ERRTABENT(FDT_ERR_BADVERSION), FDT_ERRTABENT(FDT_ERR_BADVERSION),
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
FDT_ERRTABENT(FDT_ERR_BADLAYOUT), 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])) #define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))

View file

@ -55,21 +55,42 @@
#include "libfdt_internal.h" #include "libfdt_internal.h"
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
const void *val, int len) const char *name, int namelen,
uint32_t idx, const void *val,
int len)
{ {
void *propval; void *propval;
int proplen; int proplen;
propval = fdt_getprop_w(fdt, nodeoffset, name, &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) if (! propval)
return proplen; return proplen;
if (proplen != len) if (proplen != len)
return -FDT_ERR_NOSPACE; return -FDT_ERR_NOSPACE;
memcpy(propval, val, len); return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
return 0; strlen(name), 0,
val, len);
} }
static void _fdt_nop_region(void *start, int len) static void _fdt_nop_region(void *start, int len)

View file

@ -61,7 +61,7 @@
#define FDT_ERR_NOTFOUND 1 #define FDT_ERR_NOTFOUND 1
/* FDT_ERR_NOTFOUND: The requested node or property does not exist */ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
#define FDT_ERR_EXISTS 2 #define FDT_ERR_EXISTS 2
/* FDT_ERR_EXISTS: Attemped to create a node or property which /* FDT_ERR_EXISTS: Attempted to create a node or property which
* already exists */ * already exists */
#define FDT_ERR_NOSPACE 3 #define FDT_ERR_NOSPACE 3
/* FDT_ERR_NOSPACE: Operation needed to expand the device /* FDT_ERR_NOSPACE: Operation needed to expand the device
@ -79,8 +79,10 @@
* (e.g. missing a leading / for a function which requires an * (e.g. missing a leading / for a function which requires an
* absolute path) */ * absolute path) */
#define FDT_ERR_BADPHANDLE 6 #define FDT_ERR_BADPHANDLE 6
/* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
* value. phandle values of 0 and -1 are not permitted. */ * This can be caused either by an invalid phandle property
* length, or the phandle value was either 0 or -1, which are
* not permitted. */
#define FDT_ERR_BADSTATE 7 #define FDT_ERR_BADSTATE 7
/* FDT_ERR_BADSTATE: Function was passed an incomplete device /* FDT_ERR_BADSTATE: Function was passed an incomplete device
* tree created by the sequential-write functions, which is * tree created by the sequential-write functions, which is
@ -126,7 +128,16 @@
* value. For example: a property expected to contain a string list * value. For example: a property expected to contain a string list
* is not NUL-terminated within the length of its value. */ * is not NUL-terminated within the length of its value. */
#define FDT_ERR_MAX 15 #define FDT_ERR_BADOVERLAY 16
/* FDT_ERR_BADOVERLAY: The device tree overlay, while
* correctly structured, cannot be applied due to some
* unexpected or missing value, property or node. */
#define FDT_ERR_NOPHANDLES 17
/* FDT_ERR_NOPHANDLES: The device tree doesn't have any
* phandle available anymore without causing an overflow */
#define FDT_ERR_MAX 17
/**********************************************************************/ /**********************************************************************/
/* Low-level functions (you probably don't need these) */ /* Low-level functions (you probably don't need these) */
@ -168,27 +179,55 @@ int fdt_first_subnode(const void *fdt, int offset);
*/ */
int fdt_next_subnode(const void *fdt, int offset); int fdt_next_subnode(const void *fdt, int offset);
/**
* fdt_for_each_subnode - iterate over all subnodes of a parent
*
* @node: child node (int, lvalue)
* @fdt: FDT blob (const void *)
* @parent: parent node (int)
*
* This is actually a wrapper around a for loop and would be used like so:
*
* fdt_for_each_subnode(node, fdt, parent) {
* Use node
* ...
* }
*
* if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) {
* Error handling
* }
*
* Note that this is implemented as a macro and @node is used as
* iterator in the loop. The parent variable be constant or even a
* literal.
*
*/
#define fdt_for_each_subnode(node, fdt, parent) \
for (node = fdt_first_subnode(fdt, parent); \
node >= 0; \
node = fdt_next_subnode(fdt, node))
/**********************************************************************/ /**********************************************************************/
/* General functions */ /* General functions */
/**********************************************************************/ /**********************************************************************/
#define fdt_get_header(fdt, field) \ #define fdt_get_header(fdt, field) \
(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) #define fdt_magic(fdt) (fdt_get_header(fdt, magic))
#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) #define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) #define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) #define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) #define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
#define fdt_version(fdt) (fdt_get_header(fdt, version)) #define fdt_version(fdt) (fdt_get_header(fdt, version))
#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) #define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) #define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) #define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) #define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
#define __fdt_set_hdr(name) \ #define __fdt_set_hdr(name) \
static inline void fdt_set_##name(void *fdt, uint32_t val) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \
{ \ { \
struct fdt_header *fdth = (struct fdt_header*)fdt; \ struct fdt_header *fdth = (struct fdt_header *)fdt; \
fdth->name = cpu_to_fdt32(val); \ fdth->name = cpu_to_fdt32(val); \
} }
__fdt_set_hdr(magic); __fdt_set_hdr(magic);
@ -258,6 +297,21 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
*/ */
const char *fdt_string(const void *fdt, int stroffset); const char *fdt_string(const void *fdt, int stroffset);
/**
* fdt_get_max_phandle - retrieves the highest phandle in a tree
* @fdt: pointer to the device tree blob
*
* fdt_get_max_phandle retrieves the highest phandle in the given
* device tree. This will ignore badly formatted phandles, or phandles
* with a value of 0 or -1.
*
* returns:
* the highest phandle on success
* 0, if no phandle was found in the device tree
* -1, if an error occurred
*/
uint32_t fdt_get_max_phandle(const void *fdt);
/** /**
* fdt_num_mem_rsv - retrieve the number of memory reserve map entries * fdt_num_mem_rsv - retrieve the number of memory reserve map entries
* @fdt: pointer to the device tree blob * @fdt: pointer to the device tree blob
@ -318,8 +372,9 @@ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
* returns: * returns:
* structure block offset of the requested subnode (>=0), on success * structure block offset of the requested subnode (>=0), on success
* -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
* -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
* -FDT_ERR_BADMAGIC, * tag
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
* -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADSTRUCTURE,
@ -351,7 +406,8 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
* address). * address).
* *
* returns: * returns:
* structure block offset of the node with the requested path (>=0), on success * structure block offset of the node with the requested path (>=0), on
* success
* -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
* -FDT_ERR_NOTFOUND, if the requested node does not exist * -FDT_ERR_NOTFOUND, if the requested node does not exist
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
@ -375,10 +431,12 @@ int fdt_path_offset(const void *fdt, const char *path);
* *
* returns: * returns:
* pointer to the node's name, on success * pointer to the node's name, on success
* If lenp is non-NULL, *lenp contains the length of that name (>=0) * If lenp is non-NULL, *lenp contains the length of that name
* (>=0)
* NULL, on error * NULL, on error
* if lenp is non-NULL *lenp contains an error code (<0): * if lenp is non-NULL *lenp contains an error code (<0):
* -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
* tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, standard meanings * -FDT_ERR_BADSTATE, standard meanings
@ -426,6 +484,33 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset);
*/ */
int fdt_next_property_offset(const void *fdt, int offset); int fdt_next_property_offset(const void *fdt, int offset);
/**
* fdt_for_each_property_offset - iterate over all properties of a node
*
* @property_offset: property offset (int, lvalue)
* @fdt: FDT blob (const void *)
* @node: node offset (int)
*
* This is actually a wrapper around a for loop and would be used like so:
*
* fdt_for_each_property_offset(property, fdt, node) {
* Use property
* ...
* }
*
* if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) {
* Error handling
* }
*
* Note that this is implemented as a macro and property is used as
* iterator in the loop. The node variable can be constant or even a
* literal.
*/
#define fdt_for_each_property_offset(property, fdt, node) \
for (property = fdt_first_property_offset(fdt, node); \
property >= 0; \
property = fdt_next_property_offset(fdt, property))
/** /**
* fdt_get_property_by_offset - retrieve the property at a given offset * fdt_get_property_by_offset - retrieve the property at a given offset
* @fdt: pointer to the device tree blob * @fdt: pointer to the device tree blob
@ -490,7 +575,8 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt,
* NULL, on error * NULL, on error
* if lenp is non-NULL, *lenp contains an error code (<0): * if lenp is non-NULL, *lenp contains an error code (<0):
* -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_NOTFOUND, node does not have named property
* -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
* tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -554,6 +640,13 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
*/ */
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
const char *name, int namelen, int *lenp); const char *name, int namelen, int *lenp);
static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
const char *name, int namelen,
int *lenp)
{
return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
namelen, lenp);
}
/** /**
* fdt_getprop - retrieve the value of a given property * fdt_getprop - retrieve the value of a given property
@ -575,7 +668,8 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
* NULL, on error * NULL, on error
* if lenp is non-NULL, *lenp contains an error code (<0): * if lenp is non-NULL, *lenp contains an error code (<0):
* -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_NOTFOUND, node does not have named property
* -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
* tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -617,7 +711,7 @@ const char *fdt_get_alias_namelen(const void *fdt,
const char *name, int namelen); const char *name, int namelen);
/** /**
* fdt_get_alias - retreive the path referenced by a given alias * fdt_get_alias - retrieve the path referenced by a given alias
* @fdt: pointer to the device tree blob * @fdt: pointer to the device tree blob
* @name: name of the alias th look up * @name: name of the alias th look up
* *
@ -647,7 +741,7 @@ const char *fdt_get_alias(const void *fdt, const char *name);
* 0, on success * 0, on success
* buf contains the absolute path of the node at * buf contains the absolute path of the node at
* nodeoffset, as a NUL-terminated string. * nodeoffset, as a NUL-terminated string.
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
* characters and will not fit in the given buffer. * characters and will not fit in the given buffer.
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
@ -677,11 +771,11 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
* structure from the start to nodeoffset. * structure from the start to nodeoffset.
* *
* returns: * returns:
* structure block offset of the node at node offset's ancestor * structure block offset of the node at node offset's ancestor
* of depth supernodedepth (>=0), on success * of depth supernodedepth (>=0), on success
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
* nodeoffset
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -703,7 +797,7 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
* *
* returns: * returns:
* depth of the node at nodeoffset (>=0), on success * depth of the node at nodeoffset (>=0), on success
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -726,7 +820,7 @@ int fdt_node_depth(const void *fdt, int nodeoffset);
* returns: * returns:
* structure block offset of the parent of the node at nodeoffset * structure block offset of the parent of the node at nodeoffset
* (>=0), on success * (>=0), on success
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -766,7 +860,7 @@ int fdt_parent_offset(const void *fdt, int nodeoffset);
* on success * on success
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
* tree after startoffset * tree after startoffset
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -813,7 +907,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
* 1, if the node has a 'compatible' property, but it does not list * 1, if the node has a 'compatible' property, but it does not list
* the given string * the given string
* -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
* -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -850,7 +944,7 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
* on success * on success
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
* tree after startoffset * tree after startoffset
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -960,7 +1054,8 @@ const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
* returns: * returns:
* 0 <= n < FDT_MAX_NCELLS, on success * 0 <= n < FDT_MAX_NCELLS, on success
* 2, if the node has no #address-cells property * 2, if the node has no #address-cells property
* -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #address-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
* #address-cells property
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -980,7 +1075,8 @@ int fdt_address_cells(const void *fdt, int nodeoffset);
* returns: * returns:
* 0 <= n < FDT_MAX_NCELLS, on success * 0 <= n < FDT_MAX_NCELLS, on success
* 2, if the node has no #address-cells property * 2, if the node has no #address-cells property
* -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid #size-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
* #size-cells property
* -FDT_ERR_BADMAGIC, * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION, * -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTATE,
@ -994,6 +1090,27 @@ int fdt_size_cells(const void *fdt, int nodeoffset);
/* Write-in-place functions */ /* Write-in-place functions */
/**********************************************************************/ /**********************************************************************/
/**
* fdt_setprop_inplace_namelen_partial - change a property's value,
* but not its size
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to change
* @name: name of the property to change
* @namelen: number of characters of name to consider
* @idx: index of the property to change in the array
* @val: pointer to data to replace the property value with
* @len: length of the property value
*
* Identical to fdt_setprop_inplace(), but modifies the given property
* starting from the given index, and using only the first characters
* of the name. It is useful when you want to manipulate only one value of
* an array and you have a string that doesn't end with \0.
*/
int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
const char *name, int namelen,
uint32_t idx, const void *val,
int len);
/** /**
* fdt_setprop_inplace - change a property's value, but not its size * fdt_setprop_inplace - change a property's value, but not its size
* @fdt: pointer to the device tree blob * @fdt: pointer to the device tree blob
@ -1604,9 +1721,11 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
* change the offsets of some existing nodes. * change the offsets of some existing nodes.
* returns: * returns:
* structure block offset of the created nodeequested subnode (>=0), on success * structure block offset of the created nodeequested subnode (>=0), on
* success
* -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
* -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
* tag
* -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
* the given name * the given name
* -FDT_ERR_NOSPACE, if there is insufficient free space in the * -FDT_ERR_NOSPACE, if there is insufficient free space in the
@ -1644,6 +1763,37 @@ int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
*/ */
int fdt_del_node(void *fdt, int nodeoffset); int fdt_del_node(void *fdt, int nodeoffset);
/**
* fdt_overlay_apply - Applies a DT overlay on a base DT
* @fdt: pointer to the base device tree blob
* @fdto: pointer to the device tree overlay blob
*
* fdt_overlay_apply() will apply the given device tree overlay on the
* given base device tree.
*
* Expect the base device tree to be modified, even if the function
* returns an error.
*
* returns:
* 0, on success
* -FDT_ERR_NOSPACE, there's not enough space in the base device tree
* -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
* properties in the base DT
* -FDT_ERR_BADPHANDLE,
* -FDT_ERR_BADOVERLAY,
* -FDT_ERR_NOPHANDLES,
* -FDT_ERR_INTERNAL,
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADOFFSET,
* -FDT_ERR_BADPATH,
* -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTRUCTURE,
* -FDT_ERR_BADSTATE,
* -FDT_ERR_TRUNCATED, standard meanings
*/
int fdt_overlay_apply(void *fdt, void *fdto);
/**********************************************************************/ /**********************************************************************/
/* Debugging / informational functions */ /* Debugging / informational functions */
/**********************************************************************/ /**********************************************************************/

View file

@ -54,6 +54,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef __CHECKER__ #ifdef __CHECKER__

View file

@ -61,6 +61,7 @@ LIBFDT_1.2 {
fdt_size_cells; fdt_size_cells;
fdt_stringlist_contains; fdt_stringlist_contains;
fdt_resize; fdt_resize;
fdt_overlay_apply;
local: local:
*; *;

View file

@ -204,7 +204,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
} }
} }
/* if no collision occured, add child to the old node. */ /* if no collision occurred, add child to the old node. */
if (new_child) if (new_child)
add_child(old_node, new_child); add_child(old_node, new_child);
} }
@ -216,34 +216,6 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
return old_node; return old_node;
} }
void add_orphan_node(struct node *dt, struct node *new_node, char *ref)
{
static unsigned int next_orphan_fragment = 0;
struct node *ovl = xmalloc(sizeof(*ovl));
struct property *p;
struct data d = empty_data;
char *name;
int ret;
memset(ovl, 0, sizeof(*ovl));
d = data_add_marker(d, REF_PHANDLE, ref);
d = data_append_integer(d, 0xffffffff, 32);
p = build_property("target", d);
add_property(ovl, p);
ret = asprintf(&name, "fragment@%u",
next_orphan_fragment++);
if (ret == -1)
die("asprintf() failed\n");
name_node(ovl, name);
name_node(new_node, "__overlay__");
add_child(dt, ovl);
add_child(ovl, new_node);
}
struct node *chain_node(struct node *first, struct node *list) struct node *chain_node(struct node *first, struct node *list)
{ {
assert(first->next_sibling == NULL); assert(first->next_sibling == NULL);
@ -270,7 +242,7 @@ void delete_property_by_name(struct node *node, char *name)
struct property *prop = node->proplist; struct property *prop = node->proplist;
while (prop) { while (prop) {
if (!strcmp(prop->name, name)) { if (streq(prop->name, name)) {
delete_property(prop); delete_property(prop);
return; return;
} }
@ -303,7 +275,7 @@ void delete_node_by_name(struct node *parent, char *name)
struct node *node = parent->children; struct node *node = parent->children;
while (node) { while (node) {
if (!strcmp(node->name, name)) { if (streq(node->name, name)) {
delete_node(node); delete_node(node);
return; return;
} }
@ -324,6 +296,23 @@ void delete_node(struct node *node)
delete_labels(&node->labels); 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 *build_reserve_entry(uint64_t address, uint64_t size)
{ {
struct reserve_info *new = xmalloc(sizeof(*new)); struct reserve_info *new = xmalloc(sizeof(*new));
@ -363,17 +352,19 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
return list; return list;
} }
struct boot_info *build_boot_info(struct reserve_info *reservelist, struct dt_info *build_dt_info(unsigned int dtsflags,
struct node *tree, uint32_t boot_cpuid_phys) struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys)
{ {
struct boot_info *bi; struct dt_info *dti;
bi = xmalloc(sizeof(*bi)); dti = xmalloc(sizeof(*dti));
bi->reservelist = reservelist; dti->dtsflags = dtsflags;
bi->dt = tree; dti->reservelist = reservelist;
bi->boot_cpuid_phys = boot_cpuid_phys; dti->dt = tree;
dti->boot_cpuid_phys = boot_cpuid_phys;
return bi; return dti;
} }
/* /*
@ -620,12 +611,12 @@ static int cmp_reserve_info(const void *ax, const void *bx)
return 0; return 0;
} }
static void sort_reserve_entries(struct boot_info *bi) static void sort_reserve_entries(struct dt_info *dti)
{ {
struct reserve_info *ri, **tbl; struct reserve_info *ri, **tbl;
int n = 0, i = 0; int n = 0, i = 0;
for (ri = bi->reservelist; for (ri = dti->reservelist;
ri; ri;
ri = ri->next) ri = ri->next)
n++; n++;
@ -635,14 +626,14 @@ static void sort_reserve_entries(struct boot_info *bi)
tbl = xmalloc(n * sizeof(*tbl)); tbl = xmalloc(n * sizeof(*tbl));
for (ri = bi->reservelist; for (ri = dti->reservelist;
ri; ri;
ri = ri->next) ri = ri->next)
tbl[i++] = ri; tbl[i++] = ri;
qsort(tbl, n, sizeof(*tbl), cmp_reserve_info); qsort(tbl, n, sizeof(*tbl), cmp_reserve_info);
bi->reservelist = tbl[0]; dti->reservelist = tbl[0];
for (i = 0; i < (n-1); i++) for (i = 0; i < (n-1); i++)
tbl[i]->next = tbl[i+1]; tbl[i]->next = tbl[i+1];
tbl[n-1]->next = NULL; tbl[n-1]->next = NULL;
@ -732,165 +723,139 @@ static void sort_node(struct node *node)
sort_node(c); sort_node(c);
} }
void sort_tree(struct boot_info *bi) void sort_tree(struct dt_info *dti)
{ {
sort_reserve_entries(bi); sort_reserve_entries(dti);
sort_node(bi->dt); sort_node(dti->dt);
} }
void generate_label_node(struct node *node, struct node *dt) /* utility helper to avoid code duplication */
static struct node *build_and_name_child_node(struct node *parent, char *name)
{ {
struct node *c, *an; 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 property *p;
struct label *l; struct label *l;
int has_label;
char *gen_node_name;
if (auto_label_aliases) /* if there are labels */
gen_node_name = "aliases"; if (node->labels) {
else
gen_node_name = "__symbols__";
/* Make sure the label isn't already there */
has_label = 0;
for_each_label(node->labels, l) {
has_label = 1;
break;
}
if (has_label) {
/* an is the aliases/__symbols__ node */
an = get_subnode(dt, gen_node_name);
/* if no node exists, create it */
if (!an) {
an = build_node(NULL, NULL);
name_node(an, gen_node_name);
add_child(dt, an);
}
/* now add the label in the node */ /* now add the label in the node */
for_each_label(node->labels, l) { for_each_label(node->labels, l) {
/* check whether the label already exists */ /* check whether the label already exists */
p = get_property(an, l->label); p = get_property(an, l->label);
if (p) { if (p) {
fprintf(stderr, "WARNING: label %s already" fprintf(stderr, "WARNING: label %s already"
" exists in /%s", l->label, " exists in /%s", l->label,
gen_node_name); an->name);
continue; continue;
} }
/* insert it */ /* insert it */
p = build_property(l->label, p = build_property(l->label,
data_copy_escape_string(node->fullpath, data_copy_mem(node->fullpath,
strlen(node->fullpath))); strlen(node->fullpath) + 1));
add_property(an, p); add_property(an, p);
} }
/* force allocation of a phandle for this node */ /* force allocation of a phandle for this node */
if (symbol_fixup_support) if (allocph)
(void)get_node_phandle(dt, node); (void)get_node_phandle(dt, node);
} }
for_each_child(node, c) for_each_child(node, c)
generate_label_node(c, dt); generate_label_tree_internal(dti, an, c, allocph);
} }
static void add_fixup_entry(struct node *dt, struct node *node, static bool any_fixup_tree(struct dt_info *dti, struct node *node)
struct property *prop, struct marker *m)
{ {
struct node *fn; /* local fixup node */ struct node *c;
struct property *p; struct property *prop;
char *fixups_name = "__fixups__"; struct marker *m;
struct data d;
char *entry;
int ret;
/* fn is the node we're putting entries in */ for_each_property(node, prop) {
fn = get_subnode(dt, fixups_name); m = prop->val.markers;
/* if no node exists, create it */ for_each_marker_of_type(m, REF_PHANDLE) {
if (!fn) { if (!get_node_by_ref(dti->dt, m->ref))
fn = build_node(NULL, NULL); return true;
name_node(fn, fixups_name);
add_child(dt, fn);
}
ret = asprintf(&entry, "%s:%s:%u",
node->fullpath, prop->name, m->offset);
if (ret == -1)
die("asprintf() failed\n");
p = get_property(fn, m->ref);
d = data_append_data(p ? p->val : empty_data, entry, strlen(entry) + 1);
if (!p)
add_property(fn, build_property(m->ref, d));
else
p->val = d;
}
static void add_local_fixup_entry(struct node *dt, struct node *node,
struct property *prop, struct marker *m,
struct node *refnode)
{
struct node *lfn, *wn, *nwn; /* local fixup node, walk node, new */
struct property *p;
struct data d;
char *local_fixups_name = "__local_fixups__";
char *s, *e, *comp;
int len;
/* fn is the node we're putting entries in */
lfn = get_subnode(dt, local_fixups_name);
/* if no node exists, create it */
if (!lfn) {
lfn = build_node(NULL, NULL);
name_node(lfn, local_fixups_name);
add_child(dt, lfn);
}
/* walk the path components creating nodes if they don't exist */
comp = NULL;
/* start skipping the first / */
s = node->fullpath + 1;
wn = lfn;
while (*s) {
/* retrieve path component */
e = strchr(s, '/');
if (e == NULL)
e = s + strlen(s);
len = e - s;
comp = xrealloc(comp, len + 1);
memcpy(comp, s, len);
comp[len] = '\0';
/* if no node exists, create it */
nwn = get_subnode(wn, comp);
if (!nwn) {
nwn = build_node(NULL, NULL);
name_node(nwn, strdup(comp));
add_child(wn, nwn);
} }
wn = nwn;
/* last path component */
if (!*e)
break;
/* next path component */
s = e + 1;
} }
free(comp);
p = get_property(wn, prop->name); for_each_child(node, c) {
d = data_append_cell(p ? p->val : empty_data, (cell_t)m->offset); if (any_fixup_tree(dti, c))
if (!p) return true;
add_property(wn, build_property(prop->name, d)); }
else
p->val = d; return false;
} }
void generate_fixups_node(struct node *node, struct node *dt) 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 node *c;
struct property *prop; struct property *prop;
struct marker *m; struct marker *m;
@ -901,13 +866,115 @@ void generate_fixups_node(struct node *node, struct node *dt)
for_each_marker_of_type(m, REF_PHANDLE) { for_each_marker_of_type(m, REF_PHANDLE) {
refnode = get_node_by_ref(dt, m->ref); refnode = get_node_by_ref(dt, m->ref);
if (!refnode) if (!refnode)
add_fixup_entry(dt, node, prop, m); add_fixup_entry(dti, fn, node, prop, m);
else
add_local_fixup_entry(dt, node, prop, m,
refnode);
} }
} }
for_each_child(node, c) for_each_child(node, c)
generate_fixups_node(c, dt); 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);
} }

View file

@ -246,46 +246,27 @@ srcpos_copy(struct srcpos *pos)
return pos_new; return pos_new;
} }
void
srcpos_dump(struct srcpos *pos)
{
printf("file : \"%s\"\n",
pos->file ? (char *) pos->file : "<no file>");
printf("first_line : %d\n", pos->first_line);
printf("first_column: %d\n", pos->first_column);
printf("last_line : %d\n", pos->last_line);
printf("last_column : %d\n", pos->last_column);
printf("file : %s\n", pos->file->name);
}
char * char *
srcpos_string(struct srcpos *pos) srcpos_string(struct srcpos *pos)
{ {
const char *fname = "<no-file>"; const char *fname = "<no-file>";
char *pos_str; char *pos_str;
int rc;
if (pos) if (pos->file && pos->file->name)
fname = pos->file->name; fname = pos->file->name;
if (pos->first_line != pos->last_line) if (pos->first_line != pos->last_line)
rc = asprintf(&pos_str, "%s:%d.%d-%d.%d", fname, xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
pos->first_line, pos->first_column, pos->first_line, pos->first_column,
pos->last_line, pos->last_column); pos->last_line, pos->last_column);
else if (pos->first_column != pos->last_column) else if (pos->first_column != pos->last_column)
rc = asprintf(&pos_str, "%s:%d.%d-%d", fname, xasprintf(&pos_str, "%s:%d.%d-%d", fname,
pos->first_line, pos->first_column, pos->first_line, pos->first_column,
pos->last_column); pos->last_column);
else else
rc = asprintf(&pos_str, "%s:%d.%d", fname, xasprintf(&pos_str, "%s:%d.%d", fname,
pos->first_line, pos->first_column); pos->first_line, pos->first_column);
if (rc == -1)
die("Couldn't allocate in srcpos string");
return pos_str; return pos_str;
} }

View file

@ -105,7 +105,6 @@ extern struct srcpos srcpos_empty;
extern void srcpos_update(struct srcpos *pos, const char *text, int len); extern void srcpos_update(struct srcpos *pos, const char *text, int len);
extern struct srcpos *srcpos_copy(struct srcpos *pos); extern struct srcpos *srcpos_copy(struct srcpos *pos);
extern char *srcpos_string(struct srcpos *pos); extern char *srcpos_string(struct srcpos *pos);
extern void srcpos_dump(struct srcpos *pos);
extern void srcpos_verror(struct srcpos *pos, const char *prefix, extern void srcpos_verror(struct srcpos *pos, const char *prefix,
const char *fmt, va_list va) const char *fmt, va_list va)

4
tests/.gitignore vendored
View file

@ -8,6 +8,7 @@ tmp.*
/asm_tree_dump /asm_tree_dump
/boot-cpuid /boot-cpuid
/char_literal /char_literal
/check_path
/del_node /del_node
/del_property /del_property
/dtbs_equal_ordered /dtbs_equal_ordered
@ -35,11 +36,14 @@ tmp.*
/nopulate /nopulate
/notfound /notfound
/open_pack /open_pack
/overlay
/overlay_bad_fixup
/parent_offset /parent_offset
/path-references /path-references
/path_offset /path_offset
/path_offset_aliases /path_offset_aliases
/phandle_format /phandle_format
/property_iterate
/propname_escapes /propname_escapes
/references /references
/root_node /root_node

View file

@ -23,7 +23,10 @@ LIB_TESTS_L = get_mem_rsv \
add_subnode_with_nops path_offset_aliases \ add_subnode_with_nops path_offset_aliases \
utilfdt_test \ utilfdt_test \
integer-expressions \ integer-expressions \
subnode_iterate property_iterate \
subnode_iterate \
overlay overlay_bad_fixup \
check_path
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
LIBTREE_TESTS_L = truncated_property LIBTREE_TESTS_L = truncated_property

83
tests/check_path.c Normal file
View file

@ -0,0 +1,83 @@
/*
* libfdt - Flat Device Tree manipulation
* Testcase for node existence
* Copyright (C) 2016 Konsulko Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
#include <stdio.h>
#include <libfdt.h>
#include "tests.h"
#define CHECK(code) \
{ \
int err = (code); \
if (err) \
FAIL(#code ": %s", fdt_strerror(err)); \
}
/* 4k ought to be enough for anybody */
#define FDT_COPY_SIZE (4 * 1024)
static void *open_dt(char *path)
{
void *dt, *copy;
dt = load_blob(path);
copy = xmalloc(FDT_COPY_SIZE);
/*
* Resize our DTs to 4k so that we have room to operate on
*/
CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE));
return copy;
}
int main(int argc, char *argv[])
{
void *fdt_base;
int fail_config, exists, check_exists;
test_init(argc, argv);
fail_config = 0;
if (argc != 4)
fail_config = 1;
if (!fail_config) {
if (!strcmp(argv[2], "exists"))
check_exists = 1;
else if (!strcmp(argv[2], "not-exists"))
check_exists = 0;
else
fail_config = 1;
}
if (fail_config)
CONFIG("Usage: %s <base dtb> <[exists|not-exists]> <node-path>", argv[0]);
fdt_base = open_dt(argv[1]);
exists = fdt_path_offset(fdt_base, argv[3]) >= 0;
if (exists == check_exists)
PASS();
else
FAIL();
}

View file

@ -44,6 +44,7 @@ static void check_phandle(void *fdt, const char *path, uint32_t checkhandle)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
uint32_t max;
void *fdt; void *fdt;
test_init(argc, argv); test_init(argc, argv);
@ -53,5 +54,10 @@ int main(int argc, char *argv[])
check_phandle(fdt, "/subnode@2", PHANDLE_1); check_phandle(fdt, "/subnode@2", PHANDLE_1);
check_phandle(fdt, "/subnode@2/subsubnode@0", PHANDLE_2); check_phandle(fdt, "/subnode@2/subsubnode@0", PHANDLE_2);
max = fdt_get_max_phandle(fdt);
if (max != PHANDLE_2)
FAIL("fdt_get_max_phandle returned 0x%x instead of 0x%x\n",
max, PHANDLE_2);
PASS(); PASS();
} }

233
tests/overlay.c Normal file
View file

@ -0,0 +1,233 @@
/*
* libfdt - Flat Device Tree manipulation
* Testcase for DT overlays()
* Copyright (C) 2016 Free Electrons
* Copyright (C) 2016 NextThing Co.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
#include <stdio.h>
#include <libfdt.h>
#include "tests.h"
#define CHECK(code) \
{ \
int err = (code); \
if (err) \
FAIL(#code ": %s", fdt_strerror(err)); \
}
/* 4k ought to be enough for anybody */
#define FDT_COPY_SIZE (4 * 1024)
static int fdt_getprop_u32_by_poffset(void *fdt, const char *path,
const char *name, int poffset,
unsigned long *out)
{
const fdt32_t *val;
int node_off;
int len;
node_off = fdt_path_offset(fdt, path);
if (node_off < 0)
return node_off;
val = fdt_getprop(fdt, node_off, name, &len);
if (!val || (len < (sizeof(uint32_t) * (poffset + 1))))
return -FDT_ERR_NOTFOUND;
*out = fdt32_to_cpu(*(val + poffset));
return 0;
}
static int check_getprop_string_by_name(void *fdt, const char *path,
const char *name, const char *val)
{
int node_off;
node_off = fdt_path_offset(fdt, path);
if (node_off < 0)
return node_off;
check_getprop_string(fdt, node_off, name, val);
return 0;
}
static int check_getprop_u32_by_name(void *fdt, const char *path,
const char *name, uint32_t val)
{
int node_off;
node_off = fdt_path_offset(fdt, path);
CHECK(node_off < 0);
check_getprop_cell(fdt, node_off, name, val);
return 0;
}
static int check_getprop_null_by_name(void *fdt, const char *path,
const char *name)
{
int node_off;
node_off = fdt_path_offset(fdt, path);
CHECK(node_off < 0);
check_property(fdt, node_off, name, 0, NULL);
return 0;
}
static int fdt_overlay_change_int_property(void *fdt)
{
return check_getprop_u32_by_name(fdt, "/test-node", "test-int-property",
43);
}
static int fdt_overlay_change_str_property(void *fdt)
{
return check_getprop_string_by_name(fdt, "/test-node",
"test-str-property", "foobar");
}
static int fdt_overlay_add_str_property(void *fdt)
{
return check_getprop_string_by_name(fdt, "/test-node",
"test-str-property-2", "foobar2");
}
static int fdt_overlay_add_node(void *fdt)
{
return check_getprop_null_by_name(fdt, "/test-node/new-node",
"new-property");
}
static int fdt_overlay_add_subnode_property(void *fdt)
{
check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
"sub-test-property");
check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
"new-sub-test-property");
return 0;
}
static int fdt_overlay_local_phandle(void *fdt)
{
uint32_t local_phandle;
unsigned long val = 0;
int off;
off = fdt_path_offset(fdt, "/test-node/new-local-node");
CHECK(off < 0);
local_phandle = fdt_get_phandle(fdt, off);
CHECK(!local_phandle);
CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
"test-several-phandle",
0, &val));
CHECK(val != local_phandle);
CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
"test-several-phandle",
1, &val));
CHECK(val != local_phandle);
return 0;
}
static int fdt_overlay_local_phandles(void *fdt)
{
uint32_t local_phandle, test_phandle;
unsigned long val = 0;
int off;
off = fdt_path_offset(fdt, "/test-node/new-local-node");
CHECK(off < 0);
local_phandle = fdt_get_phandle(fdt, off);
CHECK(!local_phandle);
off = fdt_path_offset(fdt, "/test-node");
CHECK(off < 0);
test_phandle = fdt_get_phandle(fdt, off);
CHECK(!test_phandle);
CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
"test-phandle", 0, &val));
CHECK(test_phandle != val);
CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
"test-phandle", 1, &val));
CHECK(local_phandle != val);
return 0;
}
static void *open_dt(char *path)
{
void *dt, *copy;
dt = load_blob(path);
copy = xmalloc(FDT_COPY_SIZE);
/*
* Resize our DTs to 4k so that we have room to operate on
*/
CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE));
return copy;
}
int main(int argc, char *argv[])
{
void *fdt_base, *fdt_overlay;
test_init(argc, argv);
if (argc != 3)
CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]);
fdt_base = open_dt(argv[1]);
fdt_overlay = open_dt(argv[2]);
/* Apply the overlay */
CHECK(fdt_overlay_apply(fdt_base, fdt_overlay));
fdt_overlay_change_int_property(fdt_base);
fdt_overlay_change_str_property(fdt_base);
fdt_overlay_add_str_property(fdt_base);
fdt_overlay_add_node(fdt_base);
fdt_overlay_add_subnode_property(fdt_base);
/*
* If the base tree has a __symbols__ node, do the tests that
* are only successful with a proper phandle support, and thus
* dtc -@
*/
if (fdt_path_offset(fdt_base, "/__symbols__") >= 0) {
fdt_overlay_local_phandle(fdt_base);
fdt_overlay_local_phandles(fdt_base);
}
PASS();
}

70
tests/overlay_bad_fixup.c Normal file
View file

@ -0,0 +1,70 @@
/*
* libfdt - Flat Device Tree manipulation
* Testcase for DT overlays()
* Copyright (C) 2016 Free Electrons
* Copyright (C) 2016 NextThing Co.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
#include <stdio.h>
#include <libfdt.h>
#include "tests.h"
#define CHECK(code, expected) \
{ \
err = (code); \
if (err != expected) \
FAIL(#code ": %s", fdt_strerror(err)); \
}
/* 4k ought to be enough for anybody */
#define FDT_COPY_SIZE (4 * 1024)
static void *open_dt(char *path)
{
void *dt, *copy;
int err;
dt = load_blob(path);
copy = xmalloc(FDT_COPY_SIZE);
/*
* Resize our DTs to 4k so that we have room to operate on
*/
CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE), 0);
return copy;
}
int main(int argc, char *argv[])
{
void *fdt_base, *fdt_overlay;
int err;
test_init(argc, argv);
if (argc != 3)
CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]);
fdt_base = open_dt(argv[1]);
fdt_overlay = open_dt(argv[2]);
/* Apply the overlay */
CHECK(fdt_overlay_apply(fdt_base, fdt_overlay), -FDT_ERR_BADOVERLAY);
PASS();
}

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0:target:ab";
};
};

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/ {
fragment@0 {
target = <0xffffffff>;
__overlay__ {
test-property;
};
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0:target:";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0:target:0a";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0::";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0:";
};
};

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/include/ "overlay_bad_fixup_base.dtsi"
/ {
__fixups__ {
test = "/fragment@0:target";
};
};

21
tests/overlay_base.dts Normal file
View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/ {
test: test-node {
test-int-property = <42>;
test-str-property = "foo";
subtest: sub-test-node {
sub-test-property;
};
};
};

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/ {
test: test-node {
phandle = <&test>; /* Force phandle generation */
test-int-property = <42>;
test-str-property = "foo";
subtest: sub-test-node {
sub-test-property;
};
};
__symbols__ {
test = &test;
};
};

86
tests/overlay_overlay.dts Normal file
View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
* Copyright (c) 2016 Konsulko Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/plugin/;
/ {
/* Test that we can change an int by another */
fragment@0 {
target = <&test>;
__overlay__ {
test-int-property = <43>;
};
};
/* Test that we can replace a string by a longer one */
fragment@1 {
target = <&test>;
__overlay__ {
test-str-property = "foobar";
};
};
/* Test that we add a new property */
fragment@2 {
target = <&test>;
__overlay__ {
test-str-property-2 = "foobar2";
};
};
/* Test that we add a new node (by phandle) */
fragment@3 {
target = <&test>;
__overlay__ {
new-node {
new-property;
};
};
};
fragment@5 {
target = <&test>;
__overlay__ {
local: new-local-node {
new-property;
};
};
};
fragment@6 {
target = <&test>;
__overlay__ {
test-phandle = <&test>, <&local>;
};
};
fragment@7 {
target = <&test>;
__overlay__ {
test-several-phandle = <&local>, <&local>;
};
};
fragment@8 {
target = <&test>;
__overlay__ {
sub-test-node {
new-sub-test-property;
};
};
};
};

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
* Copyright (c) 2016 Konsulko Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/* Note no /plugin/ tag - we're manually generating the metadata for
testing purposes */
/ {
/* Test that we can change an int by another */
fragment@0 {
target = <0xffffffff /*&test*/>;
__overlay__ {
test-int-property = <43>;
};
};
/* Test that we can replace a string by a longer one */
fragment@1 {
target = <0xffffffff /*&test*/>;
__overlay__ {
test-str-property = "foobar";
};
};
/* Test that we add a new property */
fragment@2 {
target = <0xffffffff /*&test*/>;
__overlay__ {
test-str-property-2 = "foobar2";
};
};
/* Test that we add a new node (by phandle) */
fragment@3 {
target = <0xffffffff /*&test*/>;
__overlay__ {
new-node {
new-property;
};
};
};
fragment@5 {
target = <0xffffffff /*&test*/>;
__overlay__ {
local: new-local-node {
new-property;
};
};
};
fragment@6 {
target = <0xffffffff /*&test*/>;
__overlay__ {
test-phandle = <0xffffffff /*&test*/>, <&local>;
};
};
fragment@7 {
target = <0xffffffff /*&test*/>;
__overlay__ {
test-several-phandle = <&local>, <&local>;
};
};
fragment@8 {
target = <0xffffffff /*&test*/>;
__overlay__ {
sub-test-node {
new-sub-test-property;
};
};
};
__local_fixups__ {
fragment@6 {
__overlay__ {
test-phandle = <4>;
};
};
fragment@7 {
__overlay__ {
test-several-phandle = <0 4>;
};
};
};
__fixups__ {
test = "/fragment@0:target:0",
"/fragment@1:target:0",
"/fragment@2:target:0",
"/fragment@3:target:0",
"/fragment@5:target:0",
"/fragment@6:target:0",
"/fragment@6/__overlay__:test-phandle:0",
"/fragment@7:target:0",
"/fragment@8:target:0";
};
};

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016 NextThing Co
* Copyright (c) 2016 Free Electrons
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/ {
fragment@0 {
target-path = "/test-node";
__overlay__ {
test-int-property = <43>;
};
};
/* Test that we can replace a string by a longer one */
fragment@1 {
target-path = "/test-node";
__overlay__ {
test-str-property = "foobar";
};
};
/* Test that we add a new property */
fragment@2 {
target-path = "/test-node";
__overlay__ {
test-str-property-2 = "foobar2";
};
};
fragment@3 {
target-path = "/test-node";
__overlay__ {
new-node {
new-property;
};
};
};
fragment@4 {
target-path = "/";
__overlay__ {
local: new-local-node {
new-property;
};
};
};
fragment@5 {
target-path = "/";
__overlay__ {
test-several-phandle = <&local>, <&local>;
};
};
fragment@6 {
target-path = "/test-node";
__overlay__ {
sub-test-node {
new-sub-test-property;
};
};
};
__local_fixups__ {
fragment@5 {
__overlay__ {
test-several-phandle = <0 4>;
};
};
};
};

View file

@ -0,0 +1,12 @@
/*
* Copyright (c) 2016 Konsulko Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/dts-v1/;
/plugin/;
&test {
test-int-property = <43>;
};

97
tests/property_iterate.c Normal file
View file

@ -0,0 +1,97 @@
/*
* libfdt - Flat Device Tree manipulation
* Tests that fdt_next_subnode() works as expected
*
* Copyright (C) 2013 Google, Inc
*
* Copyright (C) 2007 David Gibson, IBM Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <libfdt.h>
#include "tests.h"
#include "testdata.h"
static void test_node(void *fdt, int parent_offset)
{
fdt32_t properties;
const fdt32_t *prop;
int offset, property;
int count;
int len;
/*
* This property indicates the number of properties in our
* test node to expect
*/
prop = fdt_getprop(fdt, parent_offset, "test-properties", &len);
if (!prop || len != sizeof(fdt32_t)) {
FAIL("Missing/invalid test-properties property at '%s'",
fdt_get_name(fdt, parent_offset, NULL));
}
properties = cpu_to_fdt32(*prop);
count = 0;
offset = fdt_first_subnode(fdt, parent_offset);
if (offset < 0)
FAIL("Missing test node\n");
fdt_for_each_property_offset(property, fdt, offset)
count++;
if (count != properties) {
FAIL("Node '%s': Expected %d properties, got %d\n",
fdt_get_name(fdt, parent_offset, NULL), properties,
count);
}
}
static void check_fdt_next_subnode(void *fdt)
{
int offset;
int count = 0;
fdt_for_each_subnode(offset, fdt, 0) {
test_node(fdt, offset);
count++;
}
if (count != 2)
FAIL("Expected %d tests, got %d\n", 2, count);
}
int main(int argc, char *argv[])
{
void *fdt;
test_init(argc, argv);
if (argc != 2)
CONFIG("Usage: %s <dtb file>", argv[0]);
fdt = load_blob(argv[1]);
if (!fdt)
FAIL("No device tree available");
check_fdt_next_subnode(fdt);
PASS();
}

View file

@ -0,0 +1,23 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <0>;
test1 {
test-properties = <3>;
test {
linux,phandle = <0x1>;
#address-cells = <1>;
#size-cells = <0>;
};
};
test2 {
test-properties = <0>;
test {
};
};
};

View file

@ -110,6 +110,21 @@ run_wrap_error_test () {
base_run_test wrap_error "$@" base_run_test wrap_error "$@"
} }
# $1: dtb file
# $2: align base
check_align () {
shorten_echo "check_align $@: "
local size=$(stat -c %s "$1")
local align="$2"
(
if [ $(($size % $align)) -eq 0 ] ;then
PASS
else
FAIL "Output size $size is not $align-byte aligned"
fi
)
}
run_dtc_test () { run_dtc_test () {
printf "dtc $*: " printf "dtc $*: "
base_run_test wrap_test $VALGRIND $DTC "$@" base_run_test wrap_test $VALGRIND $DTC "$@"
@ -145,6 +160,79 @@ run_fdtdump_test() {
base_run_test sh fdtdump-runtest.sh "$file" base_run_test sh fdtdump-runtest.sh "$file"
} }
BAD_FIXUP_TREES="bad_index \
empty \
empty_index \
index_trailing \
path_empty_prop \
path_only \
path_only_sep \
path_prop"
# Test to exercise libfdt overlay application without dtc's overlay support
libfdt_overlay_tests () {
# First test a doctored overlay which requires only local fixups
run_dtc_test -I dts -O dtb -o overlay_base_no_symbols.test.dtb overlay_base.dts
run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__symbols__"
run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__fixups__"
run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__local_fixups__"
run_dtc_test -I dts -O dtb -o overlay_overlay_no_fixups.test.dtb overlay_overlay_no_fixups.dts
run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__symbols__"
run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__fixups__"
run_test check_path overlay_overlay_no_fixups.test.dtb exists "/__local_fixups__"
run_test overlay overlay_base_no_symbols.test.dtb overlay_overlay_no_fixups.test.dtb
# Then test with manually constructed fixups
run_dtc_test -I dts -O dtb -o overlay_base_manual_symbols.test.dtb overlay_base_manual_symbols.dts
run_test check_path overlay_base_manual_symbols.test.dtb exists "/__symbols__"
run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__fixups__"
run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__local_fixups__"
run_dtc_test -I dts -O dtb -o overlay_overlay_manual_fixups.test.dtb overlay_overlay_manual_fixups.dts
run_test check_path overlay_overlay_manual_fixups.test.dtb not-exists "/__symbols__"
run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__fixups__"
run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__local_fixups__"
run_test overlay overlay_base_manual_symbols.test.dtb overlay_overlay_manual_fixups.test.dtb
# Bad fixup tests
for test in $BAD_FIXUP_TREES; do
tree="overlay_bad_fixup_$test"
run_dtc_test -I dts -O dtb -o $tree.test.dtb $tree.dts
run_test overlay_bad_fixup overlay_base_no_symbols.test.dtb $tree.test.dtb
done
}
# Tests to exercise dtc's overlay generation support
dtc_overlay_tests () {
# Overlay tests for dtc
run_dtc_test -@ -I dts -O dtb -o overlay_base.test.dtb overlay_base.dts
run_test check_path overlay_base.test.dtb exists "/__symbols__"
run_test check_path overlay_base.test.dtb not-exists "/__fixups__"
run_test check_path overlay_base.test.dtb not-exists "/__local_fixups__"
run_dtc_test -I dts -O dtb -o overlay_overlay.test.dtb overlay_overlay.dts
run_test check_path overlay_overlay.test.dtb not-exists "/__symbols__"
run_test check_path overlay_overlay.test.dtb exists "/__fixups__"
run_test check_path overlay_overlay.test.dtb exists "/__local_fixups__"
run_test overlay overlay_base.test.dtb overlay_overlay.test.dtb
# test plugin source to dtb and back
run_dtc_test -I dtb -O dts -o overlay_overlay_decompile.test.dts overlay_overlay.test.dtb
run_dtc_test -I dts -O dtb -o overlay_overlay_decompile.test.dtb overlay_overlay_decompile.test.dts
run_test dtbs_equal_ordered overlay_overlay.test.dtb overlay_overlay_decompile.test.dtb
# Test generation of aliases insted of symbols
run_dtc_test -A -I dts -O dtb -o overlay_base_with_aliases.dtb overlay_base.dts
run_test check_path overlay_base_with_aliases.dtb exists "/aliases"
run_test check_path overlay_base_with_aliases.dtb not-exists "/__symbols__"
run_test check_path overlay_base_with_aliases.dtb not-exists "/__fixups__"
run_test check_path overlay_base_with_aliases.dtb not-exists "/__local_fixups__"
}
tree1_tests () { tree1_tests () {
TREE=$1 TREE=$1
@ -258,6 +346,7 @@ libfdt_tests () {
run_test appendprop2 appendprop1.test.dtb run_test appendprop2 appendprop1.test.dtb
run_dtc_test -I dts -O dtb -o appendprop.test.dtb appendprop.dts run_dtc_test -I dts -O dtb -o appendprop.test.dtb appendprop.dts
run_test dtbs_equal_ordered appendprop2.test.dtb appendprop.test.dtb run_test dtbs_equal_ordered appendprop2.test.dtb appendprop.test.dtb
libfdt_overlay_tests
for basetree in test_tree1.dtb sw_tree1.test.dtb rw_tree1.test.dtb; do for basetree in test_tree1.dtb sw_tree1.test.dtb rw_tree1.test.dtb; do
run_test nopulate $basetree run_test nopulate $basetree
@ -269,6 +358,9 @@ libfdt_tests () {
run_dtc_test -I dts -O dtb -o subnode_iterate.dtb subnode_iterate.dts run_dtc_test -I dts -O dtb -o subnode_iterate.dtb subnode_iterate.dts
run_test subnode_iterate subnode_iterate.dtb run_test subnode_iterate subnode_iterate.dtb
run_dtc_test -I dts -O dtb -o property_iterate.dtb property_iterate.dts
run_test property_iterate property_iterate.dtb
# Tests for behaviour on various sorts of corrupted trees # Tests for behaviour on various sorts of corrupted trees
run_test truncated_property run_test truncated_property
@ -419,6 +511,7 @@ dtc_tests () {
run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb
run_dtc_test -I dts -O dtb -o dtc_tree1_merge_path.test.dtb test_tree1_merge_path.dts run_dtc_test -I dts -O dtb -o dtc_tree1_merge_path.test.dtb test_tree1_merge_path.dts
tree1_tests dtc_tree1_merge_path.test.dtb test_tree1.dtb tree1_tests dtc_tree1_merge_path.test.dtb test_tree1.dtb
run_wrap_error_test $DTC -I dts -O dtb -o /dev/null test_label_ref.dts
# Check prop/node delete functionality # Check prop/node delete functionality
run_dtc_test -I dts -O dtb -o dtc_tree1_delete.test.dtb test_tree1_delete.dts run_dtc_test -I dts -O dtb -o dtc_tree1_delete.test.dtb test_tree1_delete.dts
@ -458,6 +551,9 @@ dtc_tests () {
run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label5.dts run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label5.dts
run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label6.dts run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label6.dts
run_test check_path test_tree1.dtb exists "/subnode@1"
run_test check_path test_tree1.dtb not-exists "/subnode@10"
# Check warning options # Check warning options
run_sh_test dtc-checkfails.sh address_cells_is_cell interrupt_cells_is_cell -n size_cells_is_cell -- -Wno_size_cells_is_cell -I dts -O dtb bad-ncells.dts run_sh_test dtc-checkfails.sh address_cells_is_cell interrupt_cells_is_cell -n size_cells_is_cell -- -Wno_size_cells_is_cell -I dts -O dtb bad-ncells.dts
run_sh_test dtc-fails.sh -n test-warn-output.test.dtb -I dts -O dtb bad-ncells.dts run_sh_test dtc-fails.sh -n test-warn-output.test.dtb -I dts -O dtb bad-ncells.dts
@ -500,6 +596,19 @@ dtc_tests () {
-o search_paths_b.dtb search_paths_b.dts -o search_paths_b.dtb search_paths_b.dts
run_dtc_test -I dts -O dtb -o search_paths_subdir.dtb \ run_dtc_test -I dts -O dtb -o search_paths_subdir.dtb \
search_dir_b/search_paths_subdir.dts search_dir_b/search_paths_subdir.dts
# Check -a option
for align in 2 4 8 16 32 64; do
# -p -a
run_dtc_test -O dtb -p 1000 -a $align -o align0.dtb subnode_iterate.dts
check_align align0.dtb $align
# -S -a
run_dtc_test -O dtb -S 1999 -a $align -o align1.dtb subnode_iterate.dts
check_align align1.dtb $align
done
# Tests for overlay/plugin generation
dtc_overlay_tests
} }
cmp_tests () { cmp_tests () {

View file

@ -83,5 +83,15 @@ int main(int argc, char *argv[])
strp = check_getprop(fdt, 0, "prop-str", xlen+1, xstr); strp = check_getprop(fdt, 0, "prop-str", xlen+1, xstr);
verbose_printf("New string value is \"%s\"\n", strp); verbose_printf("New string value is \"%s\"\n", strp);
err = fdt_setprop_inplace_namelen_partial(fdt, 0, "compatible",
strlen("compatible"), 4,
TEST_STRING_4_PARTIAL,
strlen(TEST_STRING_4_PARTIAL));
if (err)
FAIL("Failed to set \"compatible\": %s\n", fdt_strerror(err));
check_getprop(fdt, 0, "compatible", strlen(TEST_STRING_4_RESULT) + 1,
TEST_STRING_4_RESULT);
PASS(); PASS();
} }

View file

@ -48,9 +48,7 @@ static void test_node(void *fdt, int parent_offset)
subnodes = cpu_to_fdt32(*prop); subnodes = cpu_to_fdt32(*prop);
count = 0; count = 0;
for (offset = fdt_first_subnode(fdt, parent_offset); fdt_for_each_subnode(offset, fdt, parent_offset)
offset >= 0;
offset = fdt_next_subnode(fdt, offset))
count++; count++;
if (count != subnodes) { if (count != subnodes) {
@ -65,9 +63,7 @@ static void check_fdt_next_subnode(void *fdt)
int offset; int offset;
int count = 0; int count = 0;
for (offset = fdt_first_subnode(fdt, 0); fdt_for_each_subnode(offset, fdt, 0) {
offset >= 0;
offset = fdt_next_subnode(fdt, offset)) {
test_node(fdt, offset); test_node(fdt, offset);
count++; count++;
} }

9
tests/test_label_ref.dts Normal file
View file

@ -0,0 +1,9 @@
/dts-v1/;
/ {
};
label: &handle {
};

View file

@ -1,3 +1,45 @@
/dts-v1/; /dts-v1/;
/include/ "test_tree1_body.dtsi" /memreserve/ 0xdeadbeef00000000 0x100000;
/memreserve/ 123456789 010000;
/ {
compatible = "test_tree1";
prop-int = <0xdeadbeef>;
prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
prop-str = "hello world";
#address-cells = <1>;
#size-cells = <0>;
subnode@1 {
compatible = "subnode1";
reg = <1>;
prop-int = [deadbeef];
subsubnode {
compatible = "subsubnode1", "subsubnode";
prop-int = <0xdeadbeef>;
};
ss1 {
};
};
subnode@2 {
reg = <2>;
linux,phandle = <0x2000>;
prop-int = <123456789>;
#address-cells = <1>;
#size-cells = <0>;
ssn0: subsubnode@0 {
reg = <0>;
phandle = <0x2001>;
compatible = "subsubnode2", "subsubnode";
prop-int = <0726746425>;
};
ss2 {
};
};
};

View file

@ -1,43 +0,0 @@
/memreserve/ 0xdeadbeef00000000 0x100000;
/memreserve/ 123456789 010000;
/ {
compatible = "test_tree1";
prop-int = <0xdeadbeef>;
prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>;
prop-str = "hello world";
#address-cells = <1>;
#size-cells = <0>;
subnode@1 {
compatible = "subnode1";
reg = <1>;
prop-int = [deadbeef];
subsubnode {
compatible = "subsubnode1", "subsubnode";
prop-int = <0xdeadbeef>;
};
ss1 {
};
};
subnode@2 {
reg = <2>;
linux,phandle = <0x2000>;
prop-int = <123456789>;
#address-cells = <1>;
#size-cells = <0>;
ssn0: subsubnode@0 {
reg = <0>;
phandle = <0x2001>;
compatible = "subsubnode2", "subsubnode";
prop-int = <0726746425>;
};
ss2 {
};
};
};

View file

@ -1,6 +1,6 @@
/dts-v1/; /dts-v1/;
/include/ "test_tree1_body.dtsi" /include/ "test_tree1.dts"
/ { / {
nonexistant-property = <0xdeadbeef>; nonexistant-property = <0xdeadbeef>;

View file

@ -21,6 +21,9 @@
#define TEST_STRING_2 "nastystring: \a\b\t\n\v\f\r\\\"" #define TEST_STRING_2 "nastystring: \a\b\t\n\v\f\r\\\""
#define TEST_STRING_3 "\xde\xad\xbe\xef" #define TEST_STRING_3 "\xde\xad\xbe\xef"
#define TEST_STRING_4_PARTIAL "foobar"
#define TEST_STRING_4_RESULT "testfoobar"
#define TEST_CHAR1 '\r' #define TEST_CHAR1 '\r'
#define TEST_CHAR2 'b' #define TEST_CHAR2 'b'
#define TEST_CHAR3 '\0' #define TEST_CHAR3 '\0'

View file

@ -25,15 +25,12 @@ extern FILE *yyin;
extern int yyparse(void); extern int yyparse(void);
extern YYLTYPE yylloc; extern YYLTYPE yylloc;
struct boot_info *the_boot_info; struct dt_info *parser_output;
bool treesource_error; bool treesource_error;
bool source_is_plugin; struct dt_info *dt_from_source(const char *fname)
bool deprecated_plugin_syntax_warning;
struct boot_info *dt_from_source(const char *fname)
{ {
the_boot_info = NULL; parser_output = NULL;
treesource_error = false; treesource_error = false;
srcfile_push(fname); srcfile_push(fname);
@ -46,7 +43,7 @@ struct boot_info *dt_from_source(const char *fname)
if (treesource_error) if (treesource_error)
die("Syntax error parsing input tree\n"); die("Syntax error parsing input tree\n");
return the_boot_info; return parser_output;
} }
static void write_prefix(FILE *f, int level) static void write_prefix(FILE *f, int level)
@ -266,13 +263,13 @@ static void write_tree_source_node(FILE *f, struct node *tree, int level)
} }
void dt_to_source(FILE *f, struct boot_info *bi) void dt_to_source(FILE *f, struct dt_info *dti)
{ {
struct reserve_info *re; struct reserve_info *re;
fprintf(f, "/dts-v1/;\n\n"); fprintf(f, "/dts-v1/;\n\n");
for (re = bi->reservelist; re; re = re->next) { for (re = dti->reservelist; re; re = re->next) {
struct label *l; struct label *l;
for_each_label(re->labels, l) for_each_label(re->labels, l)
@ -282,6 +279,6 @@ void dt_to_source(FILE *f, struct boot_info *bi)
(unsigned long long)re->re.size); (unsigned long long)re->re.size);
} }
write_tree_source_node(f, bi->dt, 0); write_tree_source_node(f, dti->dt, 0);
} }

30
util.c
View file

@ -46,6 +46,36 @@ char *xstrdup(const char *s)
return d; 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) char *join_path(const char *path, const char *name)
{ {
int lenp = strlen(path); int lenp = strlen(path);

1
util.h
View file

@ -59,6 +59,7 @@ static inline void *xrealloc(void *p, size_t len)
} }
extern char *xstrdup(const char *s); extern char *xstrdup(const char *s);
extern int xasprintf(char **strp, const char *fmt, ...);
extern char *join_path(const char *path, const char *name); extern char *join_path(const char *path, const char *name);
/** /**

View file

@ -1 +1 @@
#define DTC_VERSION "DTC 1.4.1-Android-build" #define DTC_VERSION "DTC 1.4.2-Android-build"