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:
commit
507cd9883a
64 changed files with 2861 additions and 667 deletions
|
@ -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,15 +213,16 @@ $ 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
|
||||||
|
@ -227,6 +235,7 @@ 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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
32
Makefile
32
Makefile
|
@ -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
4
README
|
@ -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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
330
checks.c
330
checks.c
|
@ -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,18 +250,29 @@ 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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
struct property *prop;
|
||||||
|
|
||||||
|
for_each_property(node, prop) {
|
||||||
int n = strspn(prop->name, c->data);
|
int n = strspn(prop->name, c->data);
|
||||||
|
|
||||||
if (n < strlen(prop->name))
|
if (n < strlen(prop->name))
|
||||||
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
|
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
|
||||||
prop->name[n], prop->name, node->fullpath);
|
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);
|
||||||
}
|
|
||||||
static void check_duplicate_label_prop(struct check *c, struct node *dt,
|
for_each_property(node, prop) {
|
||||||
struct node *node, struct property *prop)
|
|
||||||
{
|
|
||||||
struct marker *m = prop->val.markers;
|
struct marker *m = prop->val.markers;
|
||||||
struct label *l;
|
|
||||||
|
|
||||||
for_each_label(prop->labels, l)
|
for_each_label(prop->labels, l)
|
||||||
check_duplicate_label(c, dt, l->label, node, prop, NULL);
|
check_duplicate_label(c, dti, l->label, node, prop, NULL);
|
||||||
|
|
||||||
for_each_marker_of_type(m, LABEL)
|
for_each_marker_of_type(m, LABEL)
|
||||||
check_duplicate_label(c, dt, m->ref, node, prop, m);
|
check_duplicate_label(c, dti, m->ref, node, prop, m);
|
||||||
}
|
}
|
||||||
ERROR(duplicate_label, NULL, check_duplicate_label_node,
|
}
|
||||||
check_duplicate_label_prop, NULL);
|
ERROR(duplicate_label, check_duplicate_label_node, NULL);
|
||||||
|
|
||||||
static void check_explicit_phandles(struct check *c, struct node *root,
|
static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
|
||||||
struct node *node, struct property *prop)
|
struct node *node, const char *propname)
|
||||||
{
|
{
|
||||||
|
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,15 +509,19 @@ 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 node *dt = dti->dt;
|
||||||
|
struct property *prop;
|
||||||
|
|
||||||
|
for_each_property(node, prop) {
|
||||||
struct marker *m = prop->val.markers;
|
struct marker *m = prop->val.markers;
|
||||||
struct node *refnode;
|
struct node *refnode;
|
||||||
cell_t phandle;
|
cell_t phandle;
|
||||||
|
@ -490,7 +531,7 @@ static void fixup_phandle_references(struct check *c, struct node *dt,
|
||||||
|
|
||||||
refnode = get_node_by_ref(dt, m->ref);
|
refnode = get_node_by_ref(dt, m->ref);
|
||||||
if (! refnode) {
|
if (! refnode) {
|
||||||
if (!source_is_plugin)
|
if (!(dti->dtsflags & DTSF_PLUGIN))
|
||||||
FAIL(c, "Reference to non-existent node or "
|
FAIL(c, "Reference to non-existent node or "
|
||||||
"label \"%s\"\n", m->ref);
|
"label \"%s\"\n", m->ref);
|
||||||
else /* mark the entry as unresolved */
|
else /* mark the entry as unresolved */
|
||||||
|
@ -503,12 +544,17 @@ static void fixup_phandle_references(struct check *c, struct node *dt,
|
||||||
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
|
*((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 node *dt = dti->dt;
|
||||||
|
struct property *prop;
|
||||||
|
|
||||||
|
for_each_property(node, prop) {
|
||||||
struct marker *m = prop->val.markers;
|
struct marker *m = prop->val.markers;
|
||||||
struct node *refnode;
|
struct node *refnode;
|
||||||
char *path;
|
char *path;
|
||||||
|
@ -528,8 +574,8 @@ static void fixup_path_references(struct check *c, struct node *dt,
|
||||||
strlen(path) + 1);
|
strlen(path) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ERROR(path_references, NULL, NULL, fixup_path_references, NULL,
|
}
|
||||||
&duplicate_node_names);
|
ERROR(path_references, fixup_path_references, NULL, &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, ®_format, &ranges_format,
|
&addr_size_cells, ®_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) {
|
||||||
|
|
|
@ -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[])
|
||||||
|
|
|
@ -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];
|
yylval.integer = (unsigned char)d.val[0];
|
||||||
|
|
||||||
if (d.len > 2)
|
if (d.len > 2)
|
||||||
lexical_error("Character literal has %d"
|
lexical_error("Character literal has %d"
|
||||||
" characters instead of 1",
|
" characters instead of 1",
|
||||||
d.len - 1);
|
d.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_free(d);
|
||||||
return DT_CHAR_LITERAL;
|
return DT_CHAR_LITERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
dtc-parser.y
58
dtc-parser.y
|
@ -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);
|
||||||
|
|
||||||
|
if (target) {
|
||||||
add_label(&target->labels, $2);
|
add_label(&target->labels, $2);
|
||||||
if (target)
|
|
||||||
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 {
|
|
||||||
if (symbol_fixup_support)
|
|
||||||
add_orphan_node($1, $3, $2);
|
|
||||||
else
|
else
|
||||||
ERROR(&@2, "Label or path %s not found", $2);
|
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
72
dtc.c
|
@ -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 {
|
||||||
|
|
48
dtc.h
48
dtc.h
|
@ -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 */
|
||||||
|
#define DTSF_V1 0x0001 /* /dts-v1/ */
|
||||||
|
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
|
||||||
|
|
||||||
|
struct dt_info *build_dt_info(unsigned int dtsflags,
|
||||||
|
struct reserve_info *reservelist,
|
||||||
struct node *tree, uint32_t boot_cpuid_phys);
|
struct node *tree, uint32_t boot_cpuid_phys);
|
||||||
void sort_tree(struct boot_info *bi);
|
void sort_tree(struct dt_info *dti);
|
||||||
void generate_label_node(struct node *node, struct node *dt);
|
void generate_label_tree(struct dt_info *dti, char *name, bool allocph);
|
||||||
void generate_fixups_node(struct node *node, struct node *dt);
|
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 */
|
||||||
|
|
35
fdtdump.c
35
fdtdump.c
|
@ -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);
|
||||||
|
|
||||||
|
|
8
fdtget.c
8
fdtget.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
fdtput.c
7
fdtput.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
35
flattree.c
35
flattree.c
|
@ -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) {
|
||||||
|
padlen = 0;
|
||||||
|
if (quiet < 1)
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Warning: blob size %d >= minimum size %d\n",
|
"Warning: blob size %d >= minimum size %d\n",
|
||||||
fdt32_to_cpu(fdt.totalsize), minsize);
|
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);
|
||||||
}
|
}
|
||||||
|
|
5
fstree.c
5
fstree.c
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
|
|
|
@ -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
676
libfdt/fdt_overlay.c
Normal 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;
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
184
libfdt/libfdt.h
184
libfdt/libfdt.h
|
@ -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,6 +179,34 @@ 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 */
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
@ -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,7 +372,8 @@ 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
|
||||||
|
* tag
|
||||||
* -FDT_ERR_BADMAGIC,
|
* -FDT_ERR_BADMAGIC,
|
||||||
* -FDT_ERR_BADVERSION,
|
* -FDT_ERR_BADVERSION,
|
||||||
* -FDT_ERR_BADSTATE,
|
* -FDT_ERR_BADSTATE,
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
@ -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,
|
||||||
|
@ -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 */
|
||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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:
|
||||||
*;
|
*;
|
||||||
|
|
401
livetree.c
401
livetree.c
|
@ -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 reserve_info *reservelist,
|
||||||
struct node *tree, uint32_t boot_cpuid_phys)
|
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 *c;
|
||||||
|
struct property *prop;
|
||||||
|
struct marker *m;
|
||||||
|
|
||||||
|
for_each_property(node, prop) {
|
||||||
|
m = prop->val.markers;
|
||||||
|
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||||
|
if (!get_node_by_ref(dti->dt, m->ref))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_child(node, c) {
|
||||||
|
if (any_fixup_tree(dti, c))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_fixup_entry(struct dt_info *dti, struct node *fn,
|
||||||
|
struct node *node, struct property *prop,
|
||||||
|
struct marker *m)
|
||||||
{
|
{
|
||||||
struct node *fn; /* local fixup node */
|
|
||||||
struct property *p;
|
|
||||||
char *fixups_name = "__fixups__";
|
|
||||||
struct data d;
|
|
||||||
char *entry;
|
char *entry;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* fn is the node we're putting entries in */
|
/* m->ref can only be a REF_PHANDLE, but check anyway */
|
||||||
fn = get_subnode(dt, fixups_name);
|
assert(m->type == REF_PHANDLE);
|
||||||
/* if no node exists, create it */
|
|
||||||
if (!fn) {
|
|
||||||
fn = build_node(NULL, NULL);
|
|
||||||
name_node(fn, fixups_name);
|
|
||||||
add_child(dt, fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = asprintf(&entry, "%s:%s:%u",
|
/* 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);
|
node->fullpath, prop->name, m->offset);
|
||||||
if (ret == -1)
|
append_to_property(fn, m->ref, entry, strlen(entry) + 1);
|
||||||
die("asprintf() failed\n");
|
|
||||||
|
|
||||||
p = get_property(fn, m->ref);
|
free(entry);
|
||||||
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,
|
static void generate_fixups_tree_internal(struct dt_info *dti,
|
||||||
struct property *prop, struct marker *m,
|
struct node *fn,
|
||||||
struct node *refnode)
|
struct node *node)
|
||||||
{
|
|
||||||
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);
|
|
||||||
d = data_append_cell(p ? p->val : empty_data, (cell_t)m->offset);
|
|
||||||
if (!p)
|
|
||||||
add_property(wn, build_property(prop->name, d));
|
|
||||||
else
|
|
||||||
p->val = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void generate_fixups_node(struct node *node, struct node *dt)
|
|
||||||
{
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
27
srcpos.c
27
srcpos.c
|
@ -246,47 +246,28 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
srcpos.h
1
srcpos.h
|
@ -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
4
tests/.gitignore
vendored
|
@ -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
|
||||||
|
|
|
@ -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
83
tests/check_path.c
Normal 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();
|
||||||
|
}
|
|
@ -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
233
tests/overlay.c
Normal 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
70
tests/overlay_bad_fixup.c
Normal 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();
|
||||||
|
}
|
14
tests/overlay_bad_fixup_bad_index.dts
Normal file
14
tests/overlay_bad_fixup_bad_index.dts
Normal 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";
|
||||||
|
};
|
||||||
|
};
|
18
tests/overlay_bad_fixup_base.dtsi
Normal file
18
tests/overlay_bad_fixup_base.dtsi
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_empty.dts
Normal file
14
tests/overlay_bad_fixup_empty.dts
Normal 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 = "";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_empty_index.dts
Normal file
14
tests/overlay_bad_fixup_empty_index.dts
Normal 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:";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_index_trailing.dts
Normal file
14
tests/overlay_bad_fixup_index_trailing.dts
Normal 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";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_path_empty_prop.dts
Normal file
14
tests/overlay_bad_fixup_path_empty_prop.dts
Normal 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::";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_path_only.dts
Normal file
14
tests/overlay_bad_fixup_path_only.dts
Normal 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";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_path_only_sep.dts
Normal file
14
tests/overlay_bad_fixup_path_only_sep.dts
Normal 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:";
|
||||||
|
};
|
||||||
|
};
|
14
tests/overlay_bad_fixup_path_prop.dts
Normal file
14
tests/overlay_bad_fixup_path_prop.dts
Normal 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
21
tests/overlay_base.dts
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
25
tests/overlay_base_manual_symbols.dts
Normal file
25
tests/overlay_base_manual_symbols.dts
Normal 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
86
tests/overlay_overlay.dts
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
112
tests/overlay_overlay_manual_fixups.dts
Normal file
112
tests/overlay_overlay_manual_fixups.dts
Normal 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";
|
||||||
|
};
|
||||||
|
};
|
82
tests/overlay_overlay_no_fixups.dts
Normal file
82
tests/overlay_overlay_no_fixups.dts
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
12
tests/overlay_overlay_simple.dts
Normal file
12
tests/overlay_overlay_simple.dts
Normal 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
97
tests/property_iterate.c
Normal 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();
|
||||||
|
}
|
23
tests/property_iterate.dts
Normal file
23
tests/property_iterate.dts
Normal 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 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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 () {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
9
tests/test_label_ref.dts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
label: &handle {
|
||||||
|
|
||||||
|
};
|
|
@ -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 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -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 {
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,6 +1,6 @@
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
|
|
||||||
/include/ "test_tree1_body.dtsi"
|
/include/ "test_tree1.dts"
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
nonexistant-property = <0xdeadbeef>;
|
nonexistant-property = <0xdeadbeef>;
|
||||||
|
|
|
@ -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'
|
||||||
|
|
17
treesource.c
17
treesource.c
|
@ -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
30
util.c
|
@ -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
1
util.h
|
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#define DTC_VERSION "DTC 1.4.1-Android-build"
|
#define DTC_VERSION "DTC 1.4.2-Android-build"
|
||||||
|
|
Loading…
Reference in a new issue