Merge from vendor branch importing dtc 1.4.3
Major new feature in this import is FDT overlay support
This commit is contained in:
commit
d2c9553963
310
contrib/dtc/Documentation/dt-object-internal.txt
Normal file
310
contrib/dtc/Documentation/dt-object-internal.txt
Normal file
@ -0,0 +1,310 @@
|
||||
Device Tree Dynamic Object format internals
|
||||
-------------------------------------------
|
||||
|
||||
The Device Tree for most platforms is a static representation of
|
||||
the hardware capabilities. This is insufficient for platforms
|
||||
that need to dynamically insert Device Tree fragments into the
|
||||
live tree.
|
||||
|
||||
This document explains the the Device Tree object format and
|
||||
modifications made to the Device Tree compiler, which make it possible.
|
||||
|
||||
1. Simplified Problem Definition
|
||||
--------------------------------
|
||||
|
||||
Assume we have a platform which boots using following simplified Device Tree.
|
||||
|
||||
---- foo.dts -----------------------------------------------------------------
|
||||
/* FOO platform */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
};
|
||||
};
|
||||
---- foo.dts -----------------------------------------------------------------
|
||||
|
||||
We have a number of peripherals that after probing (using some undefined method)
|
||||
should result in different Device Tree configuration.
|
||||
|
||||
We cannot boot with this static tree because due to the configuration of the
|
||||
foo platform there exist multiple conficting peripherals DT fragments.
|
||||
|
||||
So for the bar peripheral we would have this:
|
||||
|
||||
---- foo+bar.dts -------------------------------------------------------------
|
||||
/* FOO platform + bar peripheral */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
|
||||
/* bar peripheral */
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
};
|
||||
};
|
||||
};
|
||||
---- foo+bar.dts -------------------------------------------------------------
|
||||
|
||||
While for the baz peripheral we would have this:
|
||||
|
||||
---- foo+baz.dts -------------------------------------------------------------
|
||||
/* FOO platform + baz peripheral */
|
||||
/ {
|
||||
compatible = "corp,foo";
|
||||
|
||||
/* shared resources */
|
||||
res: res {
|
||||
/* baz resources */
|
||||
baz_res: res_baz { ... };
|
||||
};
|
||||
|
||||
/* On chip peripherals */
|
||||
ocp: ocp {
|
||||
/* peripherals that are always instantiated */
|
||||
peripheral1 { ... };
|
||||
|
||||
/* baz peripheral */
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
/* reference to another point in the tree */
|
||||
ref-to-res = <&baz_res>;
|
||||
... /* various properties and child nodes */
|
||||
};
|
||||
};
|
||||
};
|
||||
---- foo+baz.dts -------------------------------------------------------------
|
||||
|
||||
We note that the baz case is more complicated, since the baz peripheral needs to
|
||||
reference another node in the DT tree.
|
||||
|
||||
2. Device Tree Object Format Requirements
|
||||
-----------------------------------------
|
||||
|
||||
Since the Device Tree is used for booting a number of very different hardware
|
||||
platforms it is imperative that we tread very carefully.
|
||||
|
||||
2.a) No changes to the Device Tree binary format for the base tree. We cannot
|
||||
modify the tree format at all and all the information we require should be
|
||||
encoded using Device Tree itself. We can add nodes that can be safely ignored
|
||||
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
|
||||
only be needed for the DT fragment definitions, and not the base boot DT.
|
||||
|
||||
2.c) An explicit option should be used to instruct DTC to generate the required
|
||||
information needed for object resolution. Platforms that don't use the
|
||||
dynamic object format can safely ignore it.
|
||||
|
||||
2.d) Finally, DT syntax changes should be kept to a minimum. It should be
|
||||
possible to express everything using the existing DT syntax.
|
||||
|
||||
3. Implementation
|
||||
-----------------
|
||||
|
||||
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
|
||||
so that it's possible to dynamically convert symbolic references (labels)
|
||||
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.
|
||||
|
||||
3.a) Compilation of the base board DTS file using the '-@' option
|
||||
generates a valid DT blob with an added __symbols__ node at the root node,
|
||||
containing a list of all nodes that are marked with a label.
|
||||
|
||||
Using the foo.dts file above the following node will be generated;
|
||||
|
||||
$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
|
||||
$ fdtdump foo.dtb
|
||||
...
|
||||
/ {
|
||||
...
|
||||
res {
|
||||
...
|
||||
phandle = <0x00000001>;
|
||||
...
|
||||
};
|
||||
ocp {
|
||||
...
|
||||
phandle = <0x00000002>;
|
||||
...
|
||||
};
|
||||
__symbols__ {
|
||||
res="/res";
|
||||
ocp="/ocp";
|
||||
};
|
||||
};
|
||||
|
||||
Notice that all the nodes that had a label have been recorded, and that
|
||||
phandles have been generated for them.
|
||||
|
||||
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 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
|
||||
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
|
||||
loader can fix them.
|
||||
|
||||
So the bar peripheral's DTS format would be of the form:
|
||||
|
||||
/dts-v1/;
|
||||
/plugin/; /* allow undefined references and record them */
|
||||
/ {
|
||||
.... /* various properties for loader use; i.e. part id etc. */
|
||||
fragment@0 {
|
||||
target = <&ocp>;
|
||||
__overlay__ {
|
||||
/* bar peripheral */
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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 node
|
||||
in the foo.dts file.
|
||||
|
||||
$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
|
||||
$ fdtdump bar.dtbo
|
||||
...
|
||||
/ {
|
||||
... /* properties */
|
||||
fragment@0 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
bar {
|
||||
compatible = "corp,bar";
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
__fixups__ {
|
||||
ocp = "/fragment@0:target:0";
|
||||
};
|
||||
};
|
||||
|
||||
No __symbols__ node has been generated (no label in bar.dts).
|
||||
Note that the target's ocp label is undefined, so the phandle
|
||||
value is filled with the illegal value '0xffffffff', while a __fixups__
|
||||
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 format of the __fixups__ node entry is
|
||||
|
||||
<label> = "<local-full-path>:<property-name>:<offset>"
|
||||
[, "<local-full-path>:<property-name>:<offset>"...];
|
||||
|
||||
<label> Is the label we're referring
|
||||
<local-full-path> Is the full path of the node the reference is
|
||||
<property-name> Is the name of the property containing the
|
||||
reference
|
||||
<offset> The offset (in bytes) of where the property's
|
||||
phandle value is located.
|
||||
|
||||
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
|
||||
local fixups.
|
||||
|
||||
/dts-v1/;
|
||||
/plugin/; /* allow undefined label references and record them */
|
||||
/ {
|
||||
.... /* various properties for loader use; i.e. part id etc. */
|
||||
fragment@0 {
|
||||
target = <&res>;
|
||||
__overlay__ {
|
||||
/* baz resources */
|
||||
baz_res: res_baz { ... };
|
||||
};
|
||||
};
|
||||
fragment@1 {
|
||||
target = <&ocp>;
|
||||
__overlay__ {
|
||||
/* baz peripheral */
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
/* reference to another point in the tree */
|
||||
ref-to-res = <&baz_res>;
|
||||
... /* various properties and child nodes */
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Note that &bar_res reference.
|
||||
|
||||
$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
|
||||
$ fdtdump baz.dtbo
|
||||
...
|
||||
/ {
|
||||
... /* properties */
|
||||
fragment@0 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
res_baz {
|
||||
....
|
||||
phandle = <0x00000001>;
|
||||
};
|
||||
};
|
||||
};
|
||||
fragment@1 {
|
||||
target = <0xffffffff>;
|
||||
__overlay__ {
|
||||
baz {
|
||||
compatible = "corp,baz";
|
||||
... /* various properties and child nodes */
|
||||
ref-to-res = <0x00000001>;
|
||||
}
|
||||
};
|
||||
};
|
||||
__fixups__ {
|
||||
res = "/fragment@0:target:0";
|
||||
ocp = "/fragment@1:target:0";
|
||||
};
|
||||
__local_fixups__ {
|
||||
fragment@1 {
|
||||
__overlay__ {
|
||||
baz {
|
||||
ref-to-res = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
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
|
||||
DT object loaded. The __local_fixups__ node records the offset relative to the
|
||||
start of every local reference within that property so that the loader can apply
|
||||
the offset.
|
@ -30,25 +30,23 @@ I - "dtc", the device tree compiler
|
||||
|
||||
1) Sources
|
||||
|
||||
Source code for the Device Tree Compiler can be found at jdl.com.
|
||||
The gitweb interface is:
|
||||
Source code for the Device Tree Compiler can be found at git.kernel.org.
|
||||
|
||||
http://git.jdl.com/gitweb/
|
||||
The upstream repository is here:
|
||||
|
||||
The repository is here:
|
||||
git://git.kernel.org/pub/scm/utils/dtc/dtc.git
|
||||
https://git.kernel.org/pub/scm/utils/dtc/dtc.git
|
||||
|
||||
git://www.jdl.com/software/dtc.git
|
||||
http://www.jdl.com/software/dtc.git
|
||||
The gitweb interface for the upstream respository is:
|
||||
|
||||
Tarballs of the 1.0.0 and latest releases are here:
|
||||
|
||||
http://www.jdl.com/software/dtc-v1.2.0.tgz
|
||||
http://www.jdl.com/software/dtc-latest.tgz
|
||||
https://git.kernel.org/cgit/utils/dtc/dtc.git/
|
||||
|
||||
1.1) Submitting Patches
|
||||
|
||||
Patches should be sent to jdl@jdl.com, and CC'ed to
|
||||
devicetree-discuss@lists.ozlabs.org.
|
||||
Patches should be sent to the maintainers:
|
||||
David Gibson <david@gibson.dropbear.id.au>
|
||||
Jon Loeliger <jdl@jdl.com>
|
||||
and CCed to <devicetree-compiler@vger.kernel.org>.
|
||||
|
||||
2) Description
|
||||
|
||||
@ -121,6 +119,20 @@ Options:
|
||||
Make space for <number> reserve map entries
|
||||
Relevant for dtb and asm output only.
|
||||
|
||||
-@
|
||||
Generates a __symbols__ node at the root node of the resulting blob
|
||||
for any node labels used, and for any local references using phandles
|
||||
it also generates a __local_fixups__ node that tracks them.
|
||||
|
||||
When using the /plugin/ tag all unresolved label references to
|
||||
be tracked in the __fixups__ node, making dynamic resolution possible.
|
||||
|
||||
-A
|
||||
Generate automatically aliases for all node labels. This is similar to
|
||||
the -@ option (the __symbols__ node contain identical information) but
|
||||
the semantics are slightly different since no phandles are automatically
|
||||
generated for labeled nodes.
|
||||
|
||||
-S <bytes>
|
||||
Ensure the blob at least <bytes> long, adding additional
|
||||
space if needed.
|
||||
@ -148,13 +160,18 @@ Additionally, dtc performs various sanity checks on the tree.
|
||||
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 ';'
|
||||
| label 'memreserve' ADDR '-' ADDR ';'
|
||||
|
||||
devicetree: '/' nodedef
|
||||
|
||||
versioninfo: '/' 'dts-v1' '/' ';'
|
||||
|
||||
plugindecl: '/' 'plugin' '/' ';'
|
||||
| /* empty */
|
||||
|
||||
nodedef: '{' list_of_property list_of_subnode '}' ';'
|
||||
|
||||
property: label PROPNAME '=' propdata ';'
|
||||
@ -211,7 +228,7 @@ Node may contain sub-nodes to obtain a hierarchical structure.
|
||||
For example:
|
||||
|
||||
- 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".
|
||||
|
||||
childnode@addresss {
|
||||
|
@ -10,14 +10,14 @@
|
||||
#
|
||||
VERSION = 1
|
||||
PATCHLEVEL = 4
|
||||
SUBLEVEL = 0
|
||||
SUBLEVEL = 3
|
||||
EXTRAVERSION =
|
||||
LOCAL_VERSION =
|
||||
CONFIG_LOCALVERSION =
|
||||
|
||||
CPPFLAGS = -I libfdt -I .
|
||||
WARNINGS = -Werror -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
|
||||
-Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls
|
||||
WARNINGS = -Wall -Wpointer-arith -Wcast-qual -Wnested-externs \
|
||||
-Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wshadow
|
||||
CFLAGS = -g -Os -fPIC -Werror $(WARNINGS)
|
||||
|
||||
BISON = bison
|
||||
@ -196,6 +196,33 @@ fdtget: $(FDTGET_OBJS) $(LIBFDT_archive)
|
||||
|
||||
fdtput: $(FDTPUT_OBJS) $(LIBFDT_archive)
|
||||
|
||||
dist:
|
||||
git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \
|
||||
> ../dtc-$(dtc_version).tar
|
||||
cat ../dtc-$(dtc_version).tar | \
|
||||
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
|
||||
@ -206,6 +233,7 @@ TESTS_BIN += dtc
|
||||
TESTS_BIN += convert-dtsv0
|
||||
TESTS_BIN += fdtput
|
||||
TESTS_BIN += fdtget
|
||||
TESTS_BIN += fdtdump
|
||||
|
||||
include tests/Makefile.tests
|
||||
|
||||
@ -220,6 +248,7 @@ clean: libfdt_clean tests_clean
|
||||
rm -f $(STD_CLEANFILES)
|
||||
rm -f $(VERSION_FILE)
|
||||
rm -f $(BIN)
|
||||
rm -f dtc-*.tar dtc-*.tar.sign dtc-*.tar.asc
|
||||
|
||||
#
|
||||
# Generic compile rules
|
||||
|
16
contrib/dtc/README
Normal file
16
contrib/dtc/README
Normal file
@ -0,0 +1,16 @@
|
||||
The source tree contains the Device Tree Compiler (dtc) toolchain for
|
||||
working with device tree source and binary files and also libfdt, a
|
||||
utility library for reading and manipulating the binary format.
|
||||
|
||||
DTC and LIBFDT are maintained by:
|
||||
|
||||
David Gibson <david@gibson.dropbear.id.au>
|
||||
Jon Loeliger <jdl@jdl.com>
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
The following list is for discussion about dtc and libfdt implementation
|
||||
mailto:devicetree-compiler@vger.kernel.org
|
||||
|
||||
Core device tree bindings are discussed on the devicetree-spec list:
|
||||
mailto:devicetree-spec@vger.kernel.org
|
@ -40,16 +40,11 @@ enum checkstatus {
|
||||
|
||||
struct check;
|
||||
|
||||
typedef void (*tree_check_fn)(struct check *c, struct node *dt);
|
||||
typedef void (*node_check_fn)(struct check *c, struct node *dt, struct node *node);
|
||||
typedef void (*prop_check_fn)(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop);
|
||||
typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node);
|
||||
|
||||
struct check {
|
||||
const char *name;
|
||||
tree_check_fn tree_fn;
|
||||
node_check_fn node_fn;
|
||||
prop_check_fn prop_fn;
|
||||
check_fn fn;
|
||||
void *data;
|
||||
bool warn, error;
|
||||
enum checkstatus status;
|
||||
@ -58,91 +53,68 @@ struct check {
|
||||
struct check **prereq;
|
||||
};
|
||||
|
||||
#define CHECK_ENTRY(nm, tfn, nfn, pfn, d, w, e, ...) \
|
||||
static struct check *nm##_prereqs[] = { __VA_ARGS__ }; \
|
||||
static struct check nm = { \
|
||||
.name = #nm, \
|
||||
.tree_fn = (tfn), \
|
||||
.node_fn = (nfn), \
|
||||
.prop_fn = (pfn), \
|
||||
.data = (d), \
|
||||
.warn = (w), \
|
||||
.error = (e), \
|
||||
#define CHECK_ENTRY(_nm, _fn, _d, _w, _e, ...) \
|
||||
static struct check *_nm##_prereqs[] = { __VA_ARGS__ }; \
|
||||
static struct check _nm = { \
|
||||
.name = #_nm, \
|
||||
.fn = (_fn), \
|
||||
.data = (_d), \
|
||||
.warn = (_w), \
|
||||
.error = (_e), \
|
||||
.status = UNCHECKED, \
|
||||
.num_prereqs = ARRAY_SIZE(nm##_prereqs), \
|
||||
.prereq = nm##_prereqs, \
|
||||
.num_prereqs = ARRAY_SIZE(_nm##_prereqs), \
|
||||
.prereq = _nm##_prereqs, \
|
||||
};
|
||||
#define WARNING(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, true, false, __VA_ARGS__)
|
||||
#define ERROR(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, true, __VA_ARGS__)
|
||||
#define CHECK(nm, tfn, nfn, pfn, d, ...) \
|
||||
CHECK_ENTRY(nm, tfn, nfn, pfn, d, false, false, __VA_ARGS__)
|
||||
|
||||
#define TREE_WARNING(nm, d, ...) \
|
||||
WARNING(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define TREE_ERROR(nm, d, ...) \
|
||||
ERROR(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define TREE_CHECK(nm, d, ...) \
|
||||
CHECK(nm, check_##nm, NULL, NULL, d, __VA_ARGS__)
|
||||
#define NODE_WARNING(nm, d, ...) \
|
||||
WARNING(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define NODE_ERROR(nm, d, ...) \
|
||||
ERROR(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define NODE_CHECK(nm, d, ...) \
|
||||
CHECK(nm, NULL, check_##nm, NULL, d, __VA_ARGS__)
|
||||
#define PROP_WARNING(nm, d, ...) \
|
||||
WARNING(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
#define PROP_ERROR(nm, d, ...) \
|
||||
ERROR(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
#define PROP_CHECK(nm, d, ...) \
|
||||
CHECK(nm, NULL, NULL, check_##nm, d, __VA_ARGS__)
|
||||
#define WARNING(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, true, false, __VA_ARGS__)
|
||||
#define ERROR(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, false, true, __VA_ARGS__)
|
||||
#define CHECK(_nm, _fn, _d, ...) \
|
||||
CHECK_ENTRY(_nm, _fn, _d, false, false, __VA_ARGS__)
|
||||
|
||||
#ifdef __GNUC__
|
||||
static inline void check_msg(struct check *c, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
|
||||
static inline void check_msg(struct check *c, struct dt_info *dti,
|
||||
const char *fmt, ...) __attribute__((format (printf, 3, 4)));
|
||||
#endif
|
||||
static inline void check_msg(struct check *c, const char *fmt, ...)
|
||||
static inline void check_msg(struct check *c, struct dt_info *dti,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if ((c->warn && (quiet < 1))
|
||||
|| (c->error && (quiet < 2))) {
|
||||
fprintf(stderr, "%s (%s): ",
|
||||
fprintf(stderr, "%s: %s (%s): ",
|
||||
strcmp(dti->outname, "-") ? dti->outname : "<stdout>",
|
||||
(c->error) ? "ERROR" : "Warning", c->name);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#define FAIL(c, ...) \
|
||||
do { \
|
||||
TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \
|
||||
(c)->status = FAILED; \
|
||||
check_msg((c), __VA_ARGS__); \
|
||||
#define FAIL(c, dti, ...) \
|
||||
do { \
|
||||
TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__); \
|
||||
(c)->status = FAILED; \
|
||||
check_msg((c), dti, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void check_nodes_props(struct check *c, struct node *dt, struct node *node)
|
||||
static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *child;
|
||||
struct property *prop;
|
||||
|
||||
TRACE(c, "%s", node->fullpath);
|
||||
if (c->node_fn)
|
||||
c->node_fn(c, dt, node);
|
||||
|
||||
if (c->prop_fn)
|
||||
for_each_property(node, prop) {
|
||||
TRACE(c, "%s\t'%s'", node->fullpath, prop->name);
|
||||
c->prop_fn(c, dt, node, prop);
|
||||
}
|
||||
if (c->fn)
|
||||
c->fn(c, dti, node);
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
@ -155,10 +127,10 @@ static bool run_check(struct check *c, struct node *dt)
|
||||
|
||||
for (i = 0; i < c->num_prereqs; i++) {
|
||||
struct check *prq = c->prereq[i];
|
||||
error = error || run_check(prq, dt);
|
||||
error = error || run_check(prq, dti);
|
||||
if (prq->status != PASSED) {
|
||||
c->status = PREREQ;
|
||||
check_msg(c, "Failed prerequisite '%s'",
|
||||
check_msg(c, dti, "Failed prerequisite '%s'",
|
||||
c->prereq[i]->name);
|
||||
}
|
||||
}
|
||||
@ -166,11 +138,8 @@ static bool run_check(struct check *c, struct node *dt)
|
||||
if (c->status != UNCHECKED)
|
||||
goto out;
|
||||
|
||||
if (c->node_fn || c->prop_fn)
|
||||
check_nodes_props(c, dt, dt);
|
||||
check_nodes_props(c, dti, dt);
|
||||
|
||||
if (c->tree_fn)
|
||||
c->tree_fn(c, dt);
|
||||
if (c->status == UNCHECKED)
|
||||
c->status = PASSED;
|
||||
|
||||
@ -188,13 +157,14 @@ static bool run_check(struct check *c, struct node *dt)
|
||||
*/
|
||||
|
||||
/* 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, dti, "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 property *prop;
|
||||
@ -205,15 +175,15 @@ static void check_is_string(struct check *c, struct node *root,
|
||||
return; /* Not present, assumed ok */
|
||||
|
||||
if (!data_is_one_string(prop->val))
|
||||
FAIL(c, "\"%s\" property in %s is not a string",
|
||||
FAIL(c, dti, "\"%s\" property in %s is not a string",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_STRING(nm, propname) \
|
||||
WARNING(nm, NULL, check_is_string, NULL, (propname))
|
||||
WARNING(nm, check_is_string, (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 property *prop;
|
||||
@ -224,19 +194,19 @@ static void check_is_cell(struct check *c, struct node *root,
|
||||
return; /* Not present, assumed ok */
|
||||
|
||||
if (prop->val.len != sizeof(cell_t))
|
||||
FAIL(c, "\"%s\" property in %s is not a single cell",
|
||||
FAIL(c, dti, "\"%s\" property in %s is not a single cell",
|
||||
propname, node->fullpath);
|
||||
}
|
||||
#define WARNING_IF_NOT_CELL(nm, propname) \
|
||||
WARNING(nm, NULL, check_is_cell, NULL, (propname))
|
||||
WARNING(nm, check_is_cell, (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
|
||||
*/
|
||||
|
||||
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 *child, *child2;
|
||||
@ -246,12 +216,12 @@ static void check_duplicate_node_names(struct check *c, struct node *dt,
|
||||
child2;
|
||||
child2 = child2->next_sibling)
|
||||
if (streq(child->name, child2->name))
|
||||
FAIL(c, "Duplicate node name %s",
|
||||
FAIL(c, dti, "Duplicate node name %s",
|
||||
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 property *prop, *prop2;
|
||||
@ -261,48 +231,120 @@ static void check_duplicate_property_names(struct check *c, struct node *dt,
|
||||
if (prop2->deleted)
|
||||
continue;
|
||||
if (streq(prop->name, prop2->name))
|
||||
FAIL(c, "Duplicate property name %s in %s",
|
||||
FAIL(c, dti, "Duplicate property name %s in %s",
|
||||
prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
NODE_ERROR(duplicate_property_names, NULL);
|
||||
ERROR(duplicate_property_names, check_duplicate_property_names, NULL);
|
||||
|
||||
#define LOWERCASE "abcdefghijklmnopqrstuvwxyz"
|
||||
#define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define DIGITS "0123456789"
|
||||
#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-"
|
||||
#define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-"
|
||||
|
||||
static void check_node_name_chars(struct check *c, struct node *dt,
|
||||
static void check_node_name_chars(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
int n = strspn(node->name, c->data);
|
||||
|
||||
if (n < strlen(node->name))
|
||||
FAIL(c, "Bad character '%c' in node %s",
|
||||
FAIL(c, dti, "Bad character '%c' in node %s",
|
||||
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, dti, "Character '%c' not recommended in node %s",
|
||||
node->name[n], node->fullpath);
|
||||
}
|
||||
CHECK(node_name_chars_strict, check_node_name_chars_strict, PROPNODECHARSSTRICT);
|
||||
|
||||
static void check_node_name_format(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
if (strchr(get_unitname(node), '@'))
|
||||
FAIL(c, "Node %s has multiple '@' characters in name",
|
||||
FAIL(c, dti, "Node %s has multiple '@' characters in name",
|
||||
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_property_name_chars(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
int n = strspn(prop->name, c->data);
|
||||
const char *unitname = get_unitname(node);
|
||||
struct property *prop = get_property(node, "reg");
|
||||
|
||||
if (n < strlen(prop->name))
|
||||
FAIL(c, "Bad character '%c' in property name \"%s\", node %s",
|
||||
prop->name[n], prop->name, node->fullpath);
|
||||
if (!prop) {
|
||||
prop = get_property(node, "ranges");
|
||||
if (prop && !prop->val.len)
|
||||
prop = NULL;
|
||||
}
|
||||
|
||||
if (prop) {
|
||||
if (!unitname[0])
|
||||
FAIL(c, dti, "Node %s has a reg or ranges property, but no unit name",
|
||||
node->fullpath);
|
||||
} else {
|
||||
if (unitname[0])
|
||||
FAIL(c, dti, "Node %s has a unit name, but no reg property",
|
||||
node->fullpath);
|
||||
}
|
||||
}
|
||||
PROP_ERROR(property_name_chars, PROPNODECHARS);
|
||||
WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL);
|
||||
|
||||
static void check_property_name_chars(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
int n = strspn(prop->name, c->data);
|
||||
|
||||
if (n < strlen(prop->name))
|
||||
FAIL(c, dti, "Bad character '%c' in property name \"%s\", node %s",
|
||||
prop->name[n], prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS);
|
||||
|
||||
static void check_property_name_chars_strict(struct check *c,
|
||||
struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
const char *name = prop->name;
|
||||
int n = strspn(name, c->data);
|
||||
|
||||
if (n == strlen(prop->name))
|
||||
continue;
|
||||
|
||||
/* Certain names are whitelisted */
|
||||
if (streq(name, "device_type"))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* # is only allowed at the beginning of property names not counting
|
||||
* the vendor prefix.
|
||||
*/
|
||||
if (name[n] == '#' && ((n == 0) || (name[n-1] == ','))) {
|
||||
name += n + 1;
|
||||
n = strspn(name, c->data);
|
||||
}
|
||||
if (n < strlen(name))
|
||||
FAIL(c, dti, "Character '%c' not recommended in property name \"%s\", node %s",
|
||||
name[n], prop->name, node->fullpath);
|
||||
}
|
||||
}
|
||||
CHECK(property_name_chars_strict, check_property_name_chars_strict, PROPNODECHARSSTRICT);
|
||||
|
||||
#define DESCLABEL_FMT "%s%s%s%s%s"
|
||||
#define DESCLABEL_ARGS(node,prop,mark) \
|
||||
@ -311,10 +353,11 @@ PROP_ERROR(property_name_chars, PROPNODECHARS);
|
||||
((prop) ? (prop)->name : ""), \
|
||||
((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,
|
||||
struct property *prop, struct marker *mark)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *othernode = NULL;
|
||||
struct property *otherprop = NULL;
|
||||
struct marker *othermark = NULL;
|
||||
@ -331,50 +374,49 @@ static void check_duplicate_label(struct check *c, struct node *dt,
|
||||
return;
|
||||
|
||||
if ((othernode != node) || (otherprop != prop) || (othermark != mark))
|
||||
FAIL(c, "Duplicate label '%s' on " DESCLABEL_FMT
|
||||
FAIL(c, dti, "Duplicate label '%s' on " DESCLABEL_FMT
|
||||
" and " DESCLABEL_FMT,
|
||||
label, DESCLABEL_ARGS(node, prop, mark),
|
||||
DESCLABEL_ARGS(othernode, otherprop, othermark));
|
||||
}
|
||||
|
||||
static void check_duplicate_label_node(struct check *c, struct node *dt,
|
||||
static void check_duplicate_label_node(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct label *l;
|
||||
struct property *prop;
|
||||
|
||||
for_each_label(node->labels, l)
|
||||
check_duplicate_label(c, dt, l->label, node, NULL, NULL);
|
||||
check_duplicate_label(c, dti, l->label, node, NULL, NULL);
|
||||
|
||||
for_each_property(node, prop) {
|
||||
struct marker *m = prop->val.markers;
|
||||
|
||||
for_each_label(prop->labels, l)
|
||||
check_duplicate_label(c, dti, l->label, node, prop, NULL);
|
||||
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
check_duplicate_label(c, dti, m->ref, node, prop, m);
|
||||
}
|
||||
}
|
||||
static void check_duplicate_label_prop(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct label *l;
|
||||
|
||||
for_each_label(prop->labels, l)
|
||||
check_duplicate_label(c, dt, l->label, node, prop, NULL);
|
||||
|
||||
for_each_marker_of_type(m, LABEL)
|
||||
check_duplicate_label(c, dt, m->ref, node, prop, m);
|
||||
}
|
||||
ERROR(duplicate_label, NULL, check_duplicate_label_node,
|
||||
check_duplicate_label_prop, NULL);
|
||||
|
||||
static void check_explicit_phandles(struct check *c, struct node *root,
|
||||
struct node *node, struct property *prop)
|
||||
ERROR(duplicate_label, check_duplicate_label_node, NULL);
|
||||
|
||||
static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
|
||||
struct node *node, const char *propname)
|
||||
{
|
||||
struct node *root = dti->dt;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
struct node *other;
|
||||
cell_t phandle;
|
||||
|
||||
if (!streq(prop->name, "phandle")
|
||||
&& !streq(prop->name, "linux,phandle"))
|
||||
return;
|
||||
prop = get_property(node, propname);
|
||||
if (!prop)
|
||||
return 0;
|
||||
|
||||
if (prop->val.len != sizeof(cell_t)) {
|
||||
FAIL(c, "%s has bad length (%d) %s property",
|
||||
FAIL(c, dti, "%s has bad length (%d) %s property",
|
||||
node->fullpath, prop->val.len, prop->name);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = prop->val.markers;
|
||||
@ -384,42 +426,65 @@ static void check_explicit_phandles(struct check *c, struct node *root,
|
||||
/* "Set this node's phandle equal to some
|
||||
* other node's phandle". That's nonsensical
|
||||
* by construction. */ {
|
||||
FAIL(c, "%s in %s is a reference to another node",
|
||||
FAIL(c, dti, "%s in %s is a reference to another node",
|
||||
prop->name, node->fullpath);
|
||||
return;
|
||||
}
|
||||
/* But setting this node's phandle equal to its own
|
||||
* phandle is allowed - that means allocate a unique
|
||||
* phandle for this node, even if it's not otherwise
|
||||
* referenced. The value will be filled in later, so
|
||||
* no further checking for now. */
|
||||
return;
|
||||
* we treat it as having no phandle data for now. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
phandle = propval_cell(prop);
|
||||
|
||||
if ((phandle == 0) || (phandle == -1)) {
|
||||
FAIL(c, "%s has bad value (0x%x) in %s property",
|
||||
FAIL(c, dti, "%s has bad value (0x%x) in %s property",
|
||||
node->fullpath, phandle, prop->name);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node->phandle && (node->phandle != phandle))
|
||||
FAIL(c, "%s has %s property which replaces existing phandle information",
|
||||
node->fullpath, prop->name);
|
||||
return phandle;
|
||||
}
|
||||
|
||||
static void check_explicit_phandles(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *root = dti->dt;
|
||||
struct node *other;
|
||||
cell_t phandle, linux_phandle;
|
||||
|
||||
/* Nothing should have assigned phandles yet */
|
||||
assert(!node->phandle);
|
||||
|
||||
phandle = check_phandle_prop(c, dti, node, "phandle");
|
||||
|
||||
linux_phandle = check_phandle_prop(c, dti, node, "linux,phandle");
|
||||
|
||||
if (!phandle && !linux_phandle)
|
||||
/* No valid phandles; nothing further to check */
|
||||
return;
|
||||
|
||||
if (linux_phandle && phandle && (phandle != linux_phandle))
|
||||
FAIL(c, dti, "%s has mismatching 'phandle' and 'linux,phandle'"
|
||||
" properties", node->fullpath);
|
||||
|
||||
if (linux_phandle && !phandle)
|
||||
phandle = linux_phandle;
|
||||
|
||||
other = get_node_by_phandle(root, phandle);
|
||||
if (other && (other != node)) {
|
||||
FAIL(c, "%s has duplicated phandle 0x%x (seen before at %s)",
|
||||
FAIL(c, dti, "%s has duplicated phandle 0x%x (seen before at %s)",
|
||||
node->fullpath, phandle, other->fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
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 property **pp, *prop = NULL;
|
||||
@ -435,7 +500,7 @@ static void check_name_properties(struct check *c, struct node *root,
|
||||
|
||||
if ((prop->val.len != node->basenamelen+1)
|
||||
|| (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) {
|
||||
FAIL(c, "\"name\" property in %s is incorrect (\"%s\" instead"
|
||||
FAIL(c, dti, "\"name\" property in %s is incorrect (\"%s\" instead"
|
||||
" of base node name)", node->fullpath, prop->val.val);
|
||||
} else {
|
||||
/* The name property is correct, and therefore redundant.
|
||||
@ -447,60 +512,73 @@ static void check_name_properties(struct check *c, struct node *root,
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
|
||||
static void fixup_phandle_references(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
static void fixup_phandle_references(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
cell_t phandle;
|
||||
struct node *dt = dti->dt;
|
||||
struct property *prop;
|
||||
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
assert(m->offset + sizeof(cell_t) <= prop->val.len);
|
||||
for_each_property(node, prop) {
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
cell_t phandle;
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (! refnode) {
|
||||
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
|
||||
m->ref);
|
||||
continue;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
assert(m->offset + sizeof(cell_t) <= prop->val.len);
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (! refnode) {
|
||||
if (!(dti->dtsflags & DTSF_PLUGIN))
|
||||
FAIL(c, dti, "Reference to non-existent node or "
|
||||
"label \"%s\"\n", m->ref);
|
||||
else /* mark the entry as unresolved */
|
||||
*((cell_t *)(prop->val.val + m->offset)) =
|
||||
cpu_to_fdt32(0xffffffff);
|
||||
continue;
|
||||
}
|
||||
|
||||
phandle = get_node_phandle(dt, refnode);
|
||||
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
|
||||
}
|
||||
|
||||
phandle = get_node_phandle(dt, refnode);
|
||||
*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
|
||||
}
|
||||
}
|
||||
ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
|
||||
ERROR(phandle_references, fixup_phandle_references, NULL,
|
||||
&duplicate_node_names, &explicit_phandles);
|
||||
|
||||
static void fixup_path_references(struct check *c, struct node *dt,
|
||||
struct node *node, struct property *prop)
|
||||
static void fixup_path_references(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
char *path;
|
||||
struct node *dt = dti->dt;
|
||||
struct property *prop;
|
||||
|
||||
for_each_marker_of_type(m, REF_PATH) {
|
||||
assert(m->offset <= prop->val.len);
|
||||
for_each_property(node, prop) {
|
||||
struct marker *m = prop->val.markers;
|
||||
struct node *refnode;
|
||||
char *path;
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (!refnode) {
|
||||
FAIL(c, "Reference to non-existent node or label \"%s\"\n",
|
||||
m->ref);
|
||||
continue;
|
||||
for_each_marker_of_type(m, REF_PATH) {
|
||||
assert(m->offset <= prop->val.len);
|
||||
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (!refnode) {
|
||||
FAIL(c, dti, "Reference to non-existent node or label \"%s\"\n",
|
||||
m->ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
path = refnode->fullpath;
|
||||
prop->val = data_insert_at_marker(prop->val, m, path,
|
||||
strlen(path) + 1);
|
||||
}
|
||||
|
||||
path = refnode->fullpath;
|
||||
prop->val = data_insert_at_marker(prop->val, m, path,
|
||||
strlen(path) + 1);
|
||||
}
|
||||
}
|
||||
ERROR(path_references, NULL, NULL, fixup_path_references, NULL,
|
||||
&duplicate_node_names);
|
||||
ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names);
|
||||
|
||||
/*
|
||||
* Semantic checks
|
||||
@ -513,7 +591,7 @@ WARNING_IF_NOT_STRING(device_type_is_string, "device_type");
|
||||
WARNING_IF_NOT_STRING(model_is_string, "model");
|
||||
WARNING_IF_NOT_STRING(status_is_string, "status");
|
||||
|
||||
static void fixup_addr_size_cells(struct check *c, struct node *dt,
|
||||
static void fixup_addr_size_cells(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *prop;
|
||||
@ -529,7 +607,7 @@ static void fixup_addr_size_cells(struct check *c, struct node *dt,
|
||||
if (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);
|
||||
|
||||
#define node_addr_cells(n) \
|
||||
@ -537,7 +615,7 @@ WARNING(addr_size_cells, NULL, fixup_addr_size_cells, NULL, NULL,
|
||||
#define node_size_cells(n) \
|
||||
(((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 property *prop;
|
||||
@ -548,25 +626,25 @@ static void check_reg_format(struct check *c, struct node *dt,
|
||||
return; /* No "reg", that's fine */
|
||||
|
||||
if (!node->parent) {
|
||||
FAIL(c, "Root node has a \"reg\" property");
|
||||
FAIL(c, dti, "Root node has a \"reg\" property");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop->val.len == 0)
|
||||
FAIL(c, "\"reg\" property in %s is empty", node->fullpath);
|
||||
FAIL(c, dti, "\"reg\" property in %s is empty", node->fullpath);
|
||||
|
||||
addr_cells = node_addr_cells(node->parent);
|
||||
size_cells = node_size_cells(node->parent);
|
||||
entrylen = (addr_cells + size_cells) * sizeof(cell_t);
|
||||
|
||||
if ((prop->val.len % entrylen) != 0)
|
||||
FAIL(c, "\"reg\" property in %s has invalid length (%d bytes) "
|
||||
if (!entrylen || (prop->val.len % entrylen) != 0)
|
||||
FAIL(c, dti, "\"reg\" property in %s has invalid length (%d bytes) "
|
||||
"(#address-cells == %d, #size-cells == %d)",
|
||||
node->fullpath, prop->val.len, addr_cells, size_cells);
|
||||
}
|
||||
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 property *prop;
|
||||
@ -577,7 +655,7 @@ static void check_ranges_format(struct check *c, struct node *dt,
|
||||
return;
|
||||
|
||||
if (!node->parent) {
|
||||
FAIL(c, "Root node has a \"ranges\" property");
|
||||
FAIL(c, dti, "Root node has a \"ranges\" property");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -589,28 +667,28 @@ static void check_ranges_format(struct check *c, struct node *dt,
|
||||
|
||||
if (prop->val.len == 0) {
|
||||
if (p_addr_cells != c_addr_cells)
|
||||
FAIL(c, "%s has empty \"ranges\" property but its "
|
||||
FAIL(c, dti, "%s has empty \"ranges\" property but its "
|
||||
"#address-cells (%d) differs from %s (%d)",
|
||||
node->fullpath, c_addr_cells, node->parent->fullpath,
|
||||
p_addr_cells);
|
||||
if (p_size_cells != c_size_cells)
|
||||
FAIL(c, "%s has empty \"ranges\" property but its "
|
||||
FAIL(c, dti, "%s has empty \"ranges\" property but its "
|
||||
"#size-cells (%d) differs from %s (%d)",
|
||||
node->fullpath, c_size_cells, node->parent->fullpath,
|
||||
p_size_cells);
|
||||
} else if ((prop->val.len % entrylen) != 0) {
|
||||
FAIL(c, "\"ranges\" property in %s has invalid length (%d bytes) "
|
||||
FAIL(c, dti, "\"ranges\" property in %s has invalid length (%d bytes) "
|
||||
"(parent #address-cells == %d, child #address-cells == %d, "
|
||||
"#size-cells == %d)", node->fullpath, prop->val.len,
|
||||
p_addr_cells, c_addr_cells, c_size_cells);
|
||||
}
|
||||
}
|
||||
NODE_WARNING(ranges_format, NULL, &addr_size_cells);
|
||||
WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells);
|
||||
|
||||
/*
|
||||
* Style checks
|
||||
*/
|
||||
static void check_avoid_default_addr_size(struct check *c, struct node *dt,
|
||||
static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct property *reg, *ranges;
|
||||
@ -625,31 +703,39 @@ static void check_avoid_default_addr_size(struct check *c, struct node *dt,
|
||||
return;
|
||||
|
||||
if (node->parent->addr_cells == -1)
|
||||
FAIL(c, "Relying on default #address-cells value for %s",
|
||||
FAIL(c, dti, "Relying on default #address-cells value for %s",
|
||||
node->fullpath);
|
||||
|
||||
if (node->parent->size_cells == -1)
|
||||
FAIL(c, "Relying on default #size-cells value for %s",
|
||||
FAIL(c, dti, "Relying on default #size-cells value for %s",
|
||||
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,
|
||||
struct node *dt)
|
||||
struct dt_info *dti,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *chosen;
|
||||
struct property *prop;
|
||||
|
||||
if (node != dt)
|
||||
return;
|
||||
|
||||
|
||||
chosen = get_node_by_path(dt, "/chosen");
|
||||
if (!chosen)
|
||||
return;
|
||||
|
||||
prop = get_property(chosen, "interrupt-controller");
|
||||
if (prop)
|
||||
FAIL(c, "/chosen has obsolete \"interrupt-controller\" "
|
||||
FAIL(c, dti, "/chosen has obsolete \"interrupt-controller\" "
|
||||
"property");
|
||||
}
|
||||
TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
|
||||
WARNING(obsolete_chosen_interrupt_controller,
|
||||
check_obsolete_chosen_interrupt_controller, NULL);
|
||||
|
||||
static struct check *check_table[] = {
|
||||
&duplicate_node_names, &duplicate_property_names,
|
||||
@ -664,8 +750,13 @@ static struct check *check_table[] = {
|
||||
&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell,
|
||||
&device_type_is_string, &model_is_string, &status_is_string,
|
||||
|
||||
&property_name_chars_strict,
|
||||
&node_name_chars_strict,
|
||||
|
||||
&addr_size_cells, ®_format, &ranges_format,
|
||||
|
||||
&unit_address_vs_reg,
|
||||
|
||||
&avoid_default_addr_size,
|
||||
&obsolete_chosen_interrupt_controller,
|
||||
|
||||
@ -706,15 +797,15 @@ static void disable_warning_error(struct check *c, bool warn, bool error)
|
||||
c->error = c->error && !error;
|
||||
}
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *optarg)
|
||||
void parse_checks_option(bool warn, bool error, const char *arg)
|
||||
{
|
||||
int i;
|
||||
const char *name = optarg;
|
||||
const char *name = arg;
|
||||
bool enable = true;
|
||||
|
||||
if ((strncmp(optarg, "no-", 3) == 0)
|
||||
|| (strncmp(optarg, "no_", 3) == 0)) {
|
||||
name = optarg + 3;
|
||||
if ((strncmp(arg, "no-", 3) == 0)
|
||||
|| (strncmp(arg, "no_", 3) == 0)) {
|
||||
name = arg + 3;
|
||||
enable = false;
|
||||
}
|
||||
|
||||
@ -733,9 +824,8 @@ void parse_checks_option(bool warn, bool error, const char *optarg)
|
||||
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 error = 0;
|
||||
|
||||
@ -743,7 +833,7 @@ void process_checks(bool force, struct boot_info *bi)
|
||||
struct check *c = check_table[i];
|
||||
|
||||
if (c->warn || c->error)
|
||||
error = error || run_check(c, dt);
|
||||
error = error || run_check(c, dti);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
@ -74,7 +74,7 @@ struct data data_copy_escape_string(const char *s, int len)
|
||||
struct data d;
|
||||
char *q;
|
||||
|
||||
d = data_grow_for(empty_data, strlen(s)+1);
|
||||
d = data_grow_for(empty_data, len + 1);
|
||||
|
||||
q = d.val;
|
||||
while (i < len) {
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
%option noyywrap nounput noinput never-interactive
|
||||
|
||||
%x INCLUDE
|
||||
%x BYTESTRING
|
||||
%x PROPNODENAME
|
||||
%s V1
|
||||
@ -67,7 +66,13 @@ static int dts_version = 1;
|
||||
|
||||
static void push_input_file(const char *filename);
|
||||
static bool pop_input_file(void);
|
||||
#ifdef __GNUC__
|
||||
static void lexical_error(const char *fmt, ...)
|
||||
__attribute__((format (printf, 1, 2)));
|
||||
#else
|
||||
static void lexical_error(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
@ -78,24 +83,32 @@ static void lexical_error(const char *fmt, ...);
|
||||
}
|
||||
|
||||
<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? {
|
||||
char *line, *tmp, *fn;
|
||||
char *line, *fnstart, *fnend;
|
||||
struct data fn;
|
||||
/* skip text before line # */
|
||||
line = yytext;
|
||||
while (!isdigit((unsigned char)*line))
|
||||
line++;
|
||||
/* skip digits in line # */
|
||||
tmp = line;
|
||||
while (!isspace((unsigned char)*tmp))
|
||||
tmp++;
|
||||
/* "NULL"-terminate line # */
|
||||
*tmp = '\0';
|
||||
/* start of filename */
|
||||
fn = strchr(tmp + 1, '"') + 1;
|
||||
/* strip trailing " from filename */
|
||||
tmp = strchr(fn, '"');
|
||||
*tmp = 0;
|
||||
|
||||
/* regexp ensures that first and list "
|
||||
* in the whole yytext are those at
|
||||
* beginning and end of the filename string */
|
||||
fnstart = memchr(yytext, '"', yyleng);
|
||||
for (fnend = yytext + yyleng - 1;
|
||||
*fnend != '"'; fnend--)
|
||||
;
|
||||
assert(fnstart && fnend && (fnend > fnstart));
|
||||
|
||||
fn = data_copy_escape_string(fnstart + 1,
|
||||
fnend - fnstart - 1);
|
||||
|
||||
/* Don't allow nuls in filenames */
|
||||
if (memchr(fn.val, '\0', fn.len - 1))
|
||||
lexical_error("nul in line number directive");
|
||||
|
||||
/* -1 since #line is the number of the next line */
|
||||
srcpos_set_line(xstrdup(fn), atoi(line) - 1);
|
||||
srcpos_set_line(xstrdup(fn.val), atoi(line) - 1);
|
||||
data_free(fn);
|
||||
}
|
||||
|
||||
<*><<EOF>> {
|
||||
@ -118,6 +131,11 @@ static void lexical_error(const char *fmt, ...);
|
||||
return DT_V1;
|
||||
}
|
||||
|
||||
<*>"/plugin/" {
|
||||
DPRINT("Keyword: /plugin/\n");
|
||||
return DT_PLUGIN;
|
||||
}
|
||||
|
||||
<*>"/memreserve/" {
|
||||
DPRINT("Keyword: /memreserve/\n");
|
||||
BEGIN_DEFAULT();
|
||||
@ -158,7 +176,10 @@ static void lexical_error(const char *fmt, ...);
|
||||
errno = 0;
|
||||
yylval.integer = strtoull(yytext, &e, 0);
|
||||
|
||||
assert(!(*e) || !e[strspn(e, "UL")]);
|
||||
if (*e && e[strspn(e, "UL")]) {
|
||||
lexical_error("Bad integer literal '%s'",
|
||||
yytext);
|
||||
}
|
||||
|
||||
if (errno == ERANGE)
|
||||
lexical_error("Integer literal '%s' out of range",
|
||||
@ -178,16 +199,16 @@ static void lexical_error(const char *fmt, ...);
|
||||
if (d.len == 1) {
|
||||
lexical_error("Empty character literal");
|
||||
yylval.integer = 0;
|
||||
return DT_CHAR_LITERAL;
|
||||
} else {
|
||||
yylval.integer = (unsigned char)d.val[0];
|
||||
|
||||
if (d.len > 2)
|
||||
lexical_error("Character literal has %d"
|
||||
" characters instead of 1",
|
||||
d.len - 1);
|
||||
}
|
||||
|
||||
yylval.integer = (unsigned char)d.val[0];
|
||||
|
||||
if (d.len > 2)
|
||||
lexical_error("Character literal has %d"
|
||||
" characters instead of 1",
|
||||
d.len - 1);
|
||||
|
||||
data_free(d);
|
||||
return DT_CHAR_LITERAL;
|
||||
}
|
||||
|
||||
@ -197,7 +218,7 @@ static void lexical_error(const char *fmt, ...);
|
||||
return DT_REF;
|
||||
}
|
||||
|
||||
<*>"&{/"{PATHCHAR}+\} { /* new-style path reference */
|
||||
<*>"&{/"{PATHCHAR}*\} { /* new-style path reference */
|
||||
yytext[yyleng-1] = '\0';
|
||||
DPRINT("Ref: %s\n", yytext+2);
|
||||
yylval.labelref = xstrdup(yytext+2);
|
||||
|
@ -17,9 +17,9 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
@ -27,17 +27,20 @@
|
||||
YYLTYPE yylloc;
|
||||
|
||||
extern int yylex(void);
|
||||
extern void print_error(char const *fmt, ...);
|
||||
extern void yyerror(char const *s);
|
||||
#define ERROR(loc, ...) \
|
||||
do { \
|
||||
srcpos_error((loc), "Error", __VA_ARGS__); \
|
||||
treesource_error = true; \
|
||||
} while (0)
|
||||
|
||||
extern struct boot_info *the_boot_info;
|
||||
extern struct dt_info *parser_output;
|
||||
extern bool treesource_error;
|
||||
%}
|
||||
|
||||
%union {
|
||||
char *propnodename;
|
||||
char *labelref;
|
||||
unsigned int cbase;
|
||||
uint8_t byte;
|
||||
struct data data;
|
||||
|
||||
@ -52,9 +55,11 @@ extern bool treesource_error;
|
||||
struct node *nodelist;
|
||||
struct reserve_info *re;
|
||||
uint64_t integer;
|
||||
unsigned int flags;
|
||||
}
|
||||
|
||||
%token DT_V1
|
||||
%token DT_PLUGIN
|
||||
%token DT_MEMRESERVE
|
||||
%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
|
||||
%token DT_BITS
|
||||
@ -63,7 +68,6 @@ extern bool treesource_error;
|
||||
%token <propnodename> DT_PROPNODENAME
|
||||
%token <integer> DT_LITERAL
|
||||
%token <integer> DT_CHAR_LITERAL
|
||||
%token <cbase> DT_BASE
|
||||
%token <byte> DT_BYTE
|
||||
%token <data> DT_STRING
|
||||
%token <labelref> DT_LABEL
|
||||
@ -72,6 +76,8 @@ extern bool treesource_error;
|
||||
|
||||
%type <data> propdata
|
||||
%type <data> propdataprefix
|
||||
%type <flags> header
|
||||
%type <flags> headers
|
||||
%type <re> memreserve
|
||||
%type <re> memreserves
|
||||
%type <array> arrayprefix
|
||||
@ -102,10 +108,31 @@ extern bool treesource_error;
|
||||
%%
|
||||
|
||||
sourcefile:
|
||||
DT_V1 ';' memreserves devicetree
|
||||
headers memreserves devicetree
|
||||
{
|
||||
the_boot_info = build_boot_info($3, $4,
|
||||
guess_boot_cpuid($4));
|
||||
parser_output = build_dt_info($1, $2, $3,
|
||||
guess_boot_cpuid($3));
|
||||
}
|
||||
;
|
||||
|
||||
header:
|
||||
DT_V1 ';'
|
||||
{
|
||||
$$ = DTSF_V1;
|
||||
}
|
||||
| DT_V1 ';' DT_PLUGIN ';'
|
||||
{
|
||||
$$ = DTSF_V1 | DTSF_PLUGIN;
|
||||
}
|
||||
;
|
||||
|
||||
headers:
|
||||
header
|
||||
| header headers
|
||||
{
|
||||
if ($2 != $1)
|
||||
ERROR(&yylloc, "Header flags don't match earlier ones");
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
@ -141,6 +168,18 @@ devicetree:
|
||||
{
|
||||
$$ = merge_nodes($1, $3);
|
||||
}
|
||||
|
||||
| devicetree DT_LABEL DT_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (target) {
|
||||
add_label(&target->labels, $2);
|
||||
merge_nodes(target, $4);
|
||||
} else
|
||||
ERROR(&yylloc, "Label or path %s not found", $3);
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_REF nodedef
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $2);
|
||||
@ -148,17 +187,18 @@ devicetree:
|
||||
if (target)
|
||||
merge_nodes(target, $3);
|
||||
else
|
||||
print_error("label or path, '%s', not found", $2);
|
||||
ERROR(&yylloc, "Label or path %s not found", $2);
|
||||
$$ = $1;
|
||||
}
|
||||
| devicetree DT_DEL_NODE DT_REF ';'
|
||||
{
|
||||
struct node *target = get_node_by_ref($1, $3);
|
||||
|
||||
if (!target)
|
||||
print_error("label or path, '%s', not found", $3);
|
||||
else
|
||||
if (target)
|
||||
delete_node(target);
|
||||
else
|
||||
ERROR(&yylloc, "Label or path %s not found", $3);
|
||||
|
||||
|
||||
$$ = $1;
|
||||
}
|
||||
@ -274,10 +314,9 @@ arrayprefix:
|
||||
bits = $2;
|
||||
|
||||
if ((bits != 8) && (bits != 16) &&
|
||||
(bits != 32) && (bits != 64))
|
||||
{
|
||||
print_error("Only 8, 16, 32 and 64-bit elements"
|
||||
" are currently supported");
|
||||
(bits != 32) && (bits != 64)) {
|
||||
ERROR(&yylloc, "Array elements must be"
|
||||
" 8, 16, 32 or 64-bits");
|
||||
bits = 32;
|
||||
}
|
||||
|
||||
@ -302,9 +341,8 @@ arrayprefix:
|
||||
* mask), all bits are one.
|
||||
*/
|
||||
if (($2 > mask) && (($2 | mask) != -1ULL))
|
||||
print_error(
|
||||
"integer value out of range "
|
||||
"%016lx (%d bits)", $1.bits);
|
||||
ERROR(&yylloc, "Value out of range for"
|
||||
" %d-bit array element", $1.bits);
|
||||
}
|
||||
|
||||
$$.data = data_append_integer($1.data, $2, $1.bits);
|
||||
@ -318,7 +356,7 @@ arrayprefix:
|
||||
REF_PHANDLE,
|
||||
$2);
|
||||
else
|
||||
print_error("References are only allowed in "
|
||||
ERROR(&yylloc, "References are only allowed in "
|
||||
"arrays with 32-bit elements.");
|
||||
|
||||
$$.data = data_append_integer($1.data, val, $1.bits);
|
||||
@ -400,8 +438,24 @@ integer_add:
|
||||
|
||||
integer_mul:
|
||||
integer_mul '*' integer_unary { $$ = $1 * $3; }
|
||||
| integer_mul '/' integer_unary { $$ = $1 / $3; }
|
||||
| integer_mul '%' integer_unary { $$ = $1 % $3; }
|
||||
| integer_mul '/' integer_unary
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 / $3;
|
||||
} else {
|
||||
ERROR(&yylloc, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| integer_mul '%' integer_unary
|
||||
{
|
||||
if ($3 != 0) {
|
||||
$$ = $1 % $3;
|
||||
} else {
|
||||
ERROR(&yylloc, "Division by zero");
|
||||
$$ = 0;
|
||||
}
|
||||
}
|
||||
| integer_unary
|
||||
;
|
||||
|
||||
@ -438,7 +492,7 @@ subnodes:
|
||||
}
|
||||
| subnode propdef
|
||||
{
|
||||
print_error("syntax error: properties must precede subnodes");
|
||||
ERROR(&yylloc, "Properties must precede subnodes");
|
||||
YYERROR;
|
||||
}
|
||||
;
|
||||
@ -461,17 +515,7 @@ subnode:
|
||||
|
||||
%%
|
||||
|
||||
void print_error(char const *fmt, ...)
|
||||
void yyerror(char const *s)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
srcpos_verror(&yylloc, "Error", fmt, va);
|
||||
va_end(va);
|
||||
|
||||
treesource_error = true;
|
||||
}
|
||||
|
||||
void yyerror(char const *s) {
|
||||
print_error("%s", s);
|
||||
ERROR(&yylloc, "%s", s);
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "dtc.h"
|
||||
#include "srcpos.h"
|
||||
|
||||
@ -28,7 +30,16 @@ int quiet; /* Level of quietness */
|
||||
int reservenum; /* Number of memory reservation slots */
|
||||
int minsize; /* Minimum blob size */
|
||||
int padsize; /* Additional padding to blob */
|
||||
int alignsize; /* Additional padding to blob accroding to the alignsize */
|
||||
int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */
|
||||
int generate_symbols; /* enable symbols & fixup support */
|
||||
int generate_fixups; /* suppress generation of fixups on symbol support */
|
||||
int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
|
||||
static int is_power_of_2(int x)
|
||||
{
|
||||
return (x > 0) && ((x & (x - 1)) == 0);
|
||||
}
|
||||
|
||||
static void fill_fullpaths(struct node *tree, const char *prefix)
|
||||
{
|
||||
@ -48,8 +59,10 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
|
||||
}
|
||||
|
||||
/* Usage related data. */
|
||||
#define FDT_VERSION(version) _FDT_VERSION(version)
|
||||
#define _FDT_VERSION(version) #version
|
||||
static const char usage_synopsis[] = "dtc [options] <input file>";
|
||||
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv";
|
||||
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@Ahv";
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"quiet", no_argument, NULL, 'q'},
|
||||
{"in-format", a_argument, NULL, 'I'},
|
||||
@ -60,6 +73,7 @@ static struct option const usage_long_opts[] = {
|
||||
{"reserve", a_argument, NULL, 'R'},
|
||||
{"space", a_argument, NULL, 'S'},
|
||||
{"pad", a_argument, NULL, 'p'},
|
||||
{"align", a_argument, NULL, 'a'},
|
||||
{"boot-cpu", a_argument, NULL, 'b'},
|
||||
{"force", no_argument, NULL, 'f'},
|
||||
{"include", a_argument, NULL, 'i'},
|
||||
@ -67,6 +81,8 @@ static struct option const usage_long_opts[] = {
|
||||
{"phandle", a_argument, NULL, 'H'},
|
||||
{"warning", a_argument, NULL, 'W'},
|
||||
{"error", a_argument, NULL, 'E'},
|
||||
{"symbols", no_argument, NULL, '@'},
|
||||
{"auto-alias", no_argument, NULL, 'A'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{NULL, no_argument, NULL, 0x0},
|
||||
@ -82,11 +98,12 @@ static const char * const usage_opts_help[] = {
|
||||
"\t\tdts - device tree source text\n"
|
||||
"\t\tdtb - device tree blob\n"
|
||||
"\t\tasm - assembler source",
|
||||
"\n\tBlob version to produce, defaults to %d (for dtb and asm output)", //, DEFAULT_FDT_VERSION);
|
||||
"\n\tBlob version to produce, defaults to "FDT_VERSION(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
|
||||
"\n\tOutput dependency file",
|
||||
"\n\ttMake 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\tAdd padding to the blob of <bytes> long (extra space)",
|
||||
"\n\tMake the blob align to the <bytes> (extra space)",
|
||||
"\n\tSet the physical boot cpu",
|
||||
"\n\tTry to produce output even if the input tree has errors",
|
||||
"\n\tAdd a path to search for include files",
|
||||
@ -97,16 +114,63 @@ static const char * const usage_opts_help[] = {
|
||||
"\t\tboth - Both \"linux,phandle\" and \"phandle\" properties",
|
||||
"\n\tEnable/disable warnings (prefix with \"no-\")",
|
||||
"\n\tEnable/disable errors (prefix with \"no-\")",
|
||||
"\n\tEnable generation of symbols",
|
||||
"\n\tEnable auto-alias of labels",
|
||||
"\n\tPrint this help and exit",
|
||||
"\n\tPrint version and exit",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *guess_type_by_name(const char *fname, const char *fallback)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
s = strrchr(fname, '.');
|
||||
if (s == NULL)
|
||||
return fallback;
|
||||
if (!strcasecmp(s, ".dts"))
|
||||
return "dts";
|
||||
if (!strcasecmp(s, ".dtb"))
|
||||
return "dtb";
|
||||
return fallback;
|
||||
}
|
||||
|
||||
static const char *guess_input_format(const char *fname, const char *fallback)
|
||||
{
|
||||
struct stat statbuf;
|
||||
uint32_t magic;
|
||||
FILE *f;
|
||||
|
||||
if (stat(fname, &statbuf) != 0)
|
||||
return fallback;
|
||||
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
return "fs";
|
||||
|
||||
if (!S_ISREG(statbuf.st_mode))
|
||||
return fallback;
|
||||
|
||||
f = fopen(fname, "r");
|
||||
if (f == NULL)
|
||||
return fallback;
|
||||
if (fread(&magic, 4, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return fallback;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
magic = fdt32_to_cpu(magic);
|
||||
if (magic == FDT_MAGIC)
|
||||
return "dtb";
|
||||
|
||||
return guess_type_by_name(fname, fallback);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct boot_info *bi;
|
||||
const char *inform = "dts";
|
||||
const char *outform = "dts";
|
||||
struct dt_info *dti;
|
||||
const char *inform = NULL;
|
||||
const char *outform = NULL;
|
||||
const char *outname = "-";
|
||||
const char *depname = NULL;
|
||||
bool force = false, sort = false;
|
||||
@ -120,6 +184,7 @@ int main(int argc, char *argv[])
|
||||
reservenum = 0;
|
||||
minsize = 0;
|
||||
padsize = 0;
|
||||
alignsize = 0;
|
||||
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
switch (opt) {
|
||||
@ -147,6 +212,12 @@ int main(int argc, char *argv[])
|
||||
case 'p':
|
||||
padsize = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'a':
|
||||
alignsize = strtol(optarg, NULL, 0);
|
||||
if (!is_power_of_2(alignsize))
|
||||
die("Invalid argument \"%d\" to -a option\n",
|
||||
alignsize);
|
||||
break;
|
||||
case 'f':
|
||||
force = true;
|
||||
break;
|
||||
@ -185,6 +256,13 @@ int main(int argc, char *argv[])
|
||||
parse_checks_option(false, true, optarg);
|
||||
break;
|
||||
|
||||
case '@':
|
||||
generate_symbols = 1;
|
||||
break;
|
||||
case 'A':
|
||||
auto_label_aliases = 1;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage(NULL);
|
||||
default:
|
||||
@ -211,44 +289,73 @@ int main(int argc, char *argv[])
|
||||
fprintf(depfile, "%s:", outname);
|
||||
}
|
||||
|
||||
if (inform == NULL)
|
||||
inform = guess_input_format(arg, "dts");
|
||||
if (outform == NULL) {
|
||||
outform = guess_type_by_name(outname, NULL);
|
||||
if (outform == NULL) {
|
||||
if (streq(inform, "dts"))
|
||||
outform = "dtb";
|
||||
else
|
||||
outform = "dts";
|
||||
}
|
||||
}
|
||||
if (streq(inform, "dts"))
|
||||
bi = dt_from_source(arg);
|
||||
dti = dt_from_source(arg);
|
||||
else if (streq(inform, "fs"))
|
||||
bi = dt_from_fs(arg);
|
||||
dti = dt_from_fs(arg);
|
||||
else if(streq(inform, "dtb"))
|
||||
bi = dt_from_blob(arg);
|
||||
dti = dt_from_blob(arg);
|
||||
else
|
||||
die("Unknown input format \"%s\"\n", inform);
|
||||
|
||||
dti->outname = outname;
|
||||
|
||||
if (depfile) {
|
||||
fputc('\n', depfile);
|
||||
fclose(depfile);
|
||||
}
|
||||
|
||||
if (cmdline_boot_cpuid != -1)
|
||||
bi->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
dti->boot_cpuid_phys = cmdline_boot_cpuid;
|
||||
|
||||
fill_fullpaths(bi->dt, "");
|
||||
process_checks(force, bi);
|
||||
fill_fullpaths(dti->dt, "");
|
||||
process_checks(force, dti);
|
||||
|
||||
/* on a plugin, generate by default */
|
||||
if (dti->dtsflags & DTSF_PLUGIN) {
|
||||
generate_fixups = 1;
|
||||
}
|
||||
|
||||
if (auto_label_aliases)
|
||||
generate_label_tree(dti, "aliases", false);
|
||||
|
||||
if (generate_symbols)
|
||||
generate_label_tree(dti, "__symbols__", true);
|
||||
|
||||
if (generate_fixups) {
|
||||
generate_fixups_tree(dti, "__fixups__");
|
||||
generate_local_fixups_tree(dti, "__local_fixups__");
|
||||
}
|
||||
|
||||
if (sort)
|
||||
sort_tree(bi);
|
||||
sort_tree(dti);
|
||||
|
||||
if (streq(outname, "-")) {
|
||||
outf = stdout;
|
||||
} else {
|
||||
outf = fopen(outname, "w");
|
||||
outf = fopen(outname, "wb");
|
||||
if (! outf)
|
||||
die("Couldn't open output file %s: %s\n",
|
||||
outname, strerror(errno));
|
||||
}
|
||||
|
||||
if (streq(outform, "dts")) {
|
||||
dt_to_source(outf, bi);
|
||||
dt_to_source(outf, dti);
|
||||
} else if (streq(outform, "dtb")) {
|
||||
dt_to_blob(outf, bi, outversion);
|
||||
dt_to_blob(outf, dti, outversion);
|
||||
} else if (streq(outform, "asm")) {
|
||||
dt_to_asm(outf, bi, outversion);
|
||||
dt_to_asm(outf, dti, outversion);
|
||||
} else if (streq(outform, "null")) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
|
@ -38,9 +38,9 @@
|
||||
#include "util.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(fmt,args...) printf(fmt, ##args)
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(fmt,args...)
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
|
||||
@ -53,7 +53,11 @@ extern int quiet; /* Level of quietness */
|
||||
extern int reservenum; /* Number of memory reservation slots */
|
||||
extern int minsize; /* Minimum blob size */
|
||||
extern int padsize; /* Additional padding to blob */
|
||||
extern int alignsize; /* Additional padding to blob accroding to the alignsize */
|
||||
extern int phandle_format; /* Use linux,phandle or phandle properties */
|
||||
extern int generate_symbols; /* generate symbols for nodes with labels */
|
||||
extern int generate_fixups; /* generate fixups */
|
||||
extern int auto_label_aliases; /* auto generate labels -> aliases */
|
||||
|
||||
#define PHANDLE_LEGACY 0x1
|
||||
#define PHANDLE_EPAPR 0x2
|
||||
@ -88,7 +92,7 @@ struct data {
|
||||
};
|
||||
|
||||
|
||||
#define empty_data ((struct data){ /* all .members = 0 or NULL */ })
|
||||
#define empty_data ((struct data){ 0 /* all .members = 0 or NULL */ })
|
||||
|
||||
#define for_each_marker(m) \
|
||||
for (; (m); (m) = (m)->next)
|
||||
@ -201,6 +205,8 @@ void delete_property(struct property *prop);
|
||||
void add_child(struct node *parent, struct node *child);
|
||||
void delete_node_by_name(struct node *parent, char *name);
|
||||
void delete_node(struct node *node);
|
||||
void append_to_property(struct node *node,
|
||||
char *name, const void *data, int len);
|
||||
|
||||
const char *get_unitname(struct node *node);
|
||||
struct property *get_property(struct node *node, const char *propname);
|
||||
@ -235,35 +241,45 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
|
||||
struct reserve_info *new);
|
||||
|
||||
|
||||
struct boot_info {
|
||||
struct dt_info {
|
||||
unsigned int dtsflags;
|
||||
struct reserve_info *reservelist;
|
||||
struct node *dt; /* the device tree */
|
||||
uint32_t boot_cpuid_phys;
|
||||
struct node *dt; /* the device tree */
|
||||
const char *outname; /* filename being written to, "-" for stdout */
|
||||
};
|
||||
|
||||
struct boot_info *build_boot_info(struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys);
|
||||
void sort_tree(struct boot_info *bi);
|
||||
/* DTS version flags definitions */
|
||||
#define DTSF_V1 0x0001 /* /dts-v1/ */
|
||||
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
|
||||
|
||||
struct dt_info *build_dt_info(unsigned int dtsflags,
|
||||
struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys);
|
||||
void sort_tree(struct dt_info *dti);
|
||||
void generate_label_tree(struct dt_info *dti, char *name, bool allocph);
|
||||
void generate_fixups_tree(struct dt_info *dti, char *name);
|
||||
void generate_local_fixups_tree(struct dt_info *dti, char *name);
|
||||
|
||||
/* Checks */
|
||||
|
||||
void parse_checks_option(bool warn, bool error, const char *optarg);
|
||||
void process_checks(bool force, struct boot_info *bi);
|
||||
void parse_checks_option(bool warn, bool error, const char *arg);
|
||||
void process_checks(bool force, struct dt_info *dti);
|
||||
|
||||
/* Flattened trees */
|
||||
|
||||
void dt_to_blob(FILE *f, struct boot_info *bi, int version);
|
||||
void dt_to_asm(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 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 */
|
||||
|
||||
void dt_to_source(FILE *f, struct boot_info *bi);
|
||||
struct boot_info *dt_from_source(const char *f);
|
||||
void dt_to_source(FILE *f, struct dt_info *dti);
|
||||
struct dt_info *dt_from_source(const char *f);
|
||||
|
||||
/* FS trees */
|
||||
|
||||
struct boot_info *dt_from_fs(const char *dirname);
|
||||
struct dt_info *dt_from_fs(const char *dirname);
|
||||
|
||||
#endif /* _DTC_H */
|
||||
|
@ -15,6 +15,9 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#define FDT_MAGIC_SIZE 4
|
||||
#define MAX_VERSION 17
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
|
||||
#define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
|
||||
#define GET_CELL(p) (p += 4, *((const uint32_t *)(p-4)))
|
||||
@ -22,7 +25,7 @@
|
||||
static const char *tagname(uint32_t tag)
|
||||
{
|
||||
static const char * const names[] = {
|
||||
#define TN(t) [t] #t
|
||||
#define TN(t) [t] = #t
|
||||
TN(FDT_BEGIN_NODE),
|
||||
TN(FDT_END_NODE),
|
||||
TN(FDT_PROP),
|
||||
@ -88,7 +91,7 @@ static void dump_blob(void *blob, bool debug)
|
||||
if (addr == 0 && size == 0)
|
||||
break;
|
||||
|
||||
printf("/memreserve/ %llx %llx;\n",
|
||||
printf("/memreserve/ %#llx %#llx;\n",
|
||||
(unsigned long long)addr, (unsigned long long)size);
|
||||
}
|
||||
|
||||
@ -157,6 +160,20 @@ static const char * const usage_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 opt;
|
||||
@ -188,26 +205,21 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* try and locate an embedded fdt in a bigger blob */
|
||||
if (scan) {
|
||||
unsigned char smagic[4];
|
||||
unsigned char smagic[FDT_MAGIC_SIZE];
|
||||
char *p = buf;
|
||||
char *endp = buf + len;
|
||||
|
||||
fdt_set_magic(smagic, FDT_MAGIC);
|
||||
|
||||
/* poor man's memmem */
|
||||
while (true) {
|
||||
p = memchr(p, smagic[0], endp - p - 4);
|
||||
while ((endp - p) >= FDT_MAGIC_SIZE) {
|
||||
p = memchr(p, smagic[0], endp - p - FDT_MAGIC_SIZE);
|
||||
if (!p)
|
||||
break;
|
||||
if (fdt_magic(p) == FDT_MAGIC) {
|
||||
/* try and validate the main struct */
|
||||
off_t this_len = endp - p;
|
||||
fdt32_t max_version = 17;
|
||||
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)
|
||||
if (valid_header(p, this_len))
|
||||
break;
|
||||
if (debug)
|
||||
printf("%s: skipping fdt magic at offset %#zx\n",
|
||||
@ -215,11 +227,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if (!p)
|
||||
if (!p || endp - p < sizeof(struct fdt_header))
|
||||
die("%s: could not locate fdt magic\n", file);
|
||||
printf("%s: found fdt at offset %#zx\n", file, p - buf);
|
||||
buf = p;
|
||||
}
|
||||
} else if (!valid_header(buf, len))
|
||||
die("%s: header is not valid\n", file);
|
||||
|
||||
dump_blob(buf, debug);
|
||||
|
||||
|
@ -266,14 +266,20 @@ static int do_fdtget(struct display_info *disp, const char *filename,
|
||||
continue;
|
||||
} else {
|
||||
report_error(arg[i], node);
|
||||
free(blob);
|
||||
return -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;
|
||||
}
|
||||
}
|
||||
|
||||
free(blob);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
enum oper_type {
|
||||
OPER_WRITE_PROP, /* Write a property in a node */
|
||||
OPER_CREATE_NODE, /* Create a new node */
|
||||
OPER_REMOVE_NODE, /* Delete a node */
|
||||
OPER_DELETE_PROP, /* Delete a property in a node */
|
||||
};
|
||||
|
||||
struct display_info {
|
||||
@ -96,12 +98,7 @@ static int encode_value(struct display_info *disp, char **arg, int arg_count,
|
||||
/* enlarge our value buffer by a suitable margin if needed */
|
||||
if (upto + len > value_size) {
|
||||
value_size = (upto + len) + 500;
|
||||
value = realloc(value, value_size);
|
||||
if (!value) {
|
||||
fprintf(stderr, "Out of mmory: cannot alloc "
|
||||
"%d bytes\n", value_size);
|
||||
return -1;
|
||||
}
|
||||
value = xrealloc(value, value_size);
|
||||
}
|
||||
|
||||
ptr = value + upto;
|
||||
@ -275,11 +272,65 @@ static int create_node(char **blob, const char *node_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a property of a node in the fdt.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Path to node containing the property to delete
|
||||
* @param prop_name Name of property to delete
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
static int delete_prop(char *blob, const char *node_name, const char *prop_name)
|
||||
{
|
||||
int node = 0;
|
||||
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
node = fdt_delprop(blob, node, prop_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a node in the fdt.
|
||||
*
|
||||
* @param blob FDT blob to write into
|
||||
* @param node_name Name of node to delete
|
||||
* @return 0 on success, or -1 on failure
|
||||
*/
|
||||
static int delete_node(char *blob, const char *node_name)
|
||||
{
|
||||
int node = 0;
|
||||
|
||||
node = fdt_path_offset(blob, node_name);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
node = fdt_del_node(blob, node);
|
||||
if (node < 0) {
|
||||
report_error(node_name, -1, node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_fdtput(struct display_info *disp, const char *filename,
|
||||
char **arg, int arg_count)
|
||||
{
|
||||
char *value;
|
||||
char *value = NULL;
|
||||
char *blob;
|
||||
char *node;
|
||||
int len, ret = 0;
|
||||
|
||||
blob = utilfdt_read(filename);
|
||||
@ -307,6 +358,15 @@ static int do_fdtput(struct display_info *disp, const char *filename,
|
||||
ret = create_node(&blob, *arg);
|
||||
}
|
||||
break;
|
||||
case OPER_REMOVE_NODE:
|
||||
for (; ret >= 0 && arg_count--; arg++)
|
||||
ret = delete_node(blob, *arg);
|
||||
break;
|
||||
case OPER_DELETE_PROP:
|
||||
node = *arg;
|
||||
for (arg++; ret >= 0 && arg_count-- > 1; arg++)
|
||||
ret = delete_prop(blob, node, *arg);
|
||||
break;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
fdt_pack(blob);
|
||||
@ -314,6 +374,11 @@ static int do_fdtput(struct display_info *disp, const char *filename,
|
||||
}
|
||||
|
||||
free(blob);
|
||||
|
||||
if (value) {
|
||||
free(value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -322,12 +387,16 @@ static const char usage_synopsis[] =
|
||||
"write a property value to a device tree\n"
|
||||
" fdtput <options> <dt file> <node> <property> [<value>...]\n"
|
||||
" fdtput -c <options> <dt file> [<node>...]\n"
|
||||
" fdtput -r <options> <dt file> [<node>...]\n"
|
||||
" fdtput -d <options> <dt file> <node> [<property>...]\n"
|
||||
"\n"
|
||||
"The command line arguments are joined together into a single value.\n"
|
||||
USAGE_TYPE_MSG;
|
||||
static const char usage_short_opts[] = "cpt:v" USAGE_COMMON_SHORT_OPTS;
|
||||
static const char usage_short_opts[] = "crdpt:v" USAGE_COMMON_SHORT_OPTS;
|
||||
static struct option const usage_long_opts[] = {
|
||||
{"create", no_argument, NULL, 'c'},
|
||||
{"remove", no_argument, NULL, 'r'},
|
||||
{"delete", no_argument, NULL, 'd'},
|
||||
{"auto-path", no_argument, NULL, 'p'},
|
||||
{"type", a_argument, NULL, 't'},
|
||||
{"verbose", no_argument, NULL, 'v'},
|
||||
@ -335,6 +404,8 @@ static struct option const usage_long_opts[] = {
|
||||
};
|
||||
static const char * const usage_opts_help[] = {
|
||||
"Create nodes if they don't already exist",
|
||||
"Delete nodes (and any subnodes) if they already exist",
|
||||
"Delete properties if they already exist",
|
||||
"Automatically create nodes as needed for the node path",
|
||||
"Type of data",
|
||||
"Display each value decoded from command line",
|
||||
@ -353,8 +424,6 @@ int main(int argc, char *argv[])
|
||||
while ((opt = util_getopt_long()) != EOF) {
|
||||
/*
|
||||
* TODO: add options to:
|
||||
* - delete property
|
||||
* - delete node (optionally recursively)
|
||||
* - rename node
|
||||
* - pack fdt before writing
|
||||
* - set amount of free space when writing
|
||||
@ -365,6 +434,12 @@ int main(int argc, char *argv[])
|
||||
case 'c':
|
||||
disp.oper = OPER_CREATE_NODE;
|
||||
break;
|
||||
case 'r':
|
||||
disp.oper = OPER_REMOVE_NODE;
|
||||
break;
|
||||
case 'd':
|
||||
disp.oper = OPER_DELETE_PROP;
|
||||
break;
|
||||
case 'p':
|
||||
disp.auto_path = 1;
|
||||
break;
|
||||
@ -395,6 +470,10 @@ int main(int argc, char *argv[])
|
||||
usage("missing property");
|
||||
}
|
||||
|
||||
if (disp.oper == OPER_DELETE_PROP)
|
||||
if (argc < 1)
|
||||
usage("missing node");
|
||||
|
||||
if (do_fdtput(&disp, filename, argv, argc))
|
||||
return 1;
|
||||
return 0;
|
||||
|
@ -366,7 +366,7 @@ static void make_fdt_header(struct fdt_header *fdt,
|
||||
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;
|
||||
int i;
|
||||
@ -384,29 +384,36 @@ void dt_to_blob(FILE *f, struct boot_info *bi, int version)
|
||||
if (!vi)
|
||||
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);
|
||||
|
||||
reservebuf = flatten_reserve_list(bi->reservelist, vi);
|
||||
reservebuf = flatten_reserve_list(dti->reservelist, vi);
|
||||
|
||||
/* Make header */
|
||||
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 (minsize > 0) {
|
||||
padlen = minsize - fdt32_to_cpu(fdt.totalsize);
|
||||
if ((padlen < 0) && (quiet < 1))
|
||||
fprintf(stderr,
|
||||
"Warning: blob size %d >= minimum size %d\n",
|
||||
fdt32_to_cpu(fdt.totalsize), minsize);
|
||||
if (padlen < 0) {
|
||||
padlen = 0;
|
||||
if (quiet < 1)
|
||||
fprintf(stderr,
|
||||
"Warning: blob size %d >= minimum size %d\n",
|
||||
fdt32_to_cpu(fdt.totalsize), minsize);
|
||||
}
|
||||
}
|
||||
|
||||
if (padsize > 0)
|
||||
padlen = padsize;
|
||||
|
||||
if (alignsize > 0)
|
||||
padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize)
|
||||
- fdt32_to_cpu(fdt.totalsize);
|
||||
|
||||
if (padlen > 0) {
|
||||
int tsize = fdt32_to_cpu(fdt.totalsize);
|
||||
tsize += padlen;
|
||||
@ -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;
|
||||
int i;
|
||||
@ -500,7 +507,7 @@ void dt_to_asm(FILE *f, struct boot_info *bi, int version)
|
||||
|
||||
if (vi->flags & FTF_BOOTCPUID) {
|
||||
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) {
|
||||
@ -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
|
||||
* 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;
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
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) {
|
||||
fprintf(f, "\t.space\t%d, 0\n", padsize);
|
||||
}
|
||||
if (alignsize > 0)
|
||||
asm_emit_align(f, alignsize);
|
||||
emit_label(f, symprefix, "blob_abs_end");
|
||||
|
||||
data_free(strbuf);
|
||||
@ -797,11 +806,15 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
|
||||
}
|
||||
} while (val != FDT_END_NODE);
|
||||
|
||||
if (node->name != flatname) {
|
||||
free(flatname);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
struct boot_info *dt_from_blob(const char *fname)
|
||||
struct dt_info *dt_from_blob(const char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys;
|
||||
@ -889,7 +902,7 @@ struct boot_info *dt_from_blob(const char *fname)
|
||||
|
||||
if (version >= 3) {
|
||||
uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings);
|
||||
if (off_str+size_str > totalsize)
|
||||
if ((off_str+size_str < off_str) || (off_str+size_str > totalsize))
|
||||
die("String table extends past total size\n");
|
||||
inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str);
|
||||
} else {
|
||||
@ -898,7 +911,7 @@ struct boot_info *dt_from_blob(const char *fname)
|
||||
|
||||
if (version >= 17) {
|
||||
size_dt = fdt32_to_cpu(fdt->size_dt_struct);
|
||||
if (off_dt+size_dt > totalsize)
|
||||
if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize))
|
||||
die("Structure block extends past total size\n");
|
||||
}
|
||||
|
||||
@ -929,5 +942,5 @@ struct boot_info *dt_from_blob(const char *fname)
|
||||
|
||||
fclose(f);
|
||||
|
||||
return build_boot_info(reservelist, tree, boot_cpuid_phys);
|
||||
return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
|
||||
}
|
||||
|
@ -37,26 +37,26 @@ static struct node *read_fstree(const char *dirname)
|
||||
tree = build_node(NULL, NULL);
|
||||
|
||||
while ((de = readdir(d)) != NULL) {
|
||||
char *tmpnam;
|
||||
char *tmpname;
|
||||
|
||||
if (streq(de->d_name, ".")
|
||||
|| streq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
tmpnam = join_path(dirname, de->d_name);
|
||||
tmpname = join_path(dirname, de->d_name);
|
||||
|
||||
if (lstat(tmpnam, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpnam, strerror(errno));
|
||||
if (lstat(tmpname, &st) < 0)
|
||||
die("stat(%s): %s\n", tmpname, strerror(errno));
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
struct property *prop;
|
||||
FILE *pfile;
|
||||
|
||||
pfile = fopen(tmpnam, "r");
|
||||
pfile = fopen(tmpname, "rb");
|
||||
if (! pfile) {
|
||||
fprintf(stderr,
|
||||
"WARNING: Cannot open %s: %s\n",
|
||||
tmpnam, strerror(errno));
|
||||
tmpname, strerror(errno));
|
||||
} else {
|
||||
prop = build_property(xstrdup(de->d_name),
|
||||
data_copy_file(pfile,
|
||||
@ -67,25 +67,24 @@ static struct node *read_fstree(const char *dirname)
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
struct node *newchild;
|
||||
|
||||
newchild = read_fstree(tmpnam);
|
||||
newchild = read_fstree(tmpname);
|
||||
newchild = name_node(newchild, xstrdup(de->d_name));
|
||||
add_child(tree, newchild);
|
||||
}
|
||||
|
||||
free(tmpnam);
|
||||
free(tmpname);
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return tree;
|
||||
}
|
||||
|
||||
struct boot_info *dt_from_fs(const char *dirname)
|
||||
struct dt_info *dt_from_fs(const char *dirname)
|
||||
{
|
||||
struct node *tree;
|
||||
|
||||
tree = read_fstree(dirname);
|
||||
tree = name_node(tree, "");
|
||||
|
||||
return build_boot_info(NULL, tree, guess_boot_cpuid(tree));
|
||||
return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
|
||||
}
|
||||
|
||||
|
@ -6,5 +6,6 @@
|
||||
LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
|
||||
LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
|
||||
LIBFDT_VERSION = version.lds
|
||||
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c
|
||||
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
|
||||
fdt_addresses.c fdt_overlay.c
|
||||
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
|
||||
|
@ -76,18 +76,19 @@ int fdt_check_header(const void *fdt)
|
||||
|
||||
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
|
||||
{
|
||||
const char *p;
|
||||
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
|
||||
|
||||
if ((absoffset < offset)
|
||||
|| ((absoffset + len) < absoffset)
|
||||
|| (absoffset + len) > fdt_totalsize(fdt))
|
||||
return NULL;
|
||||
|
||||
if (fdt_version(fdt) >= 0x11)
|
||||
if (((offset + len) < offset)
|
||||
|| ((offset + len) > fdt_size_dt_struct(fdt)))
|
||||
return NULL;
|
||||
|
||||
p = _fdt_offset_ptr(fdt, offset);
|
||||
|
||||
if (p + len < p)
|
||||
return NULL;
|
||||
return p;
|
||||
return _fdt_offset_ptr(fdt, offset);
|
||||
}
|
||||
|
||||
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
|
||||
|
96
contrib/dtc/libfdt/fdt_addresses.c
Normal file
96
contrib/dtc/libfdt/fdt_addresses.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* libfdt - Flat Device Tree manipulation
|
||||
* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
|
||||
*
|
||||
* libfdt is dual licensed: you can use it either under the terms of
|
||||
* the GPL, or the BSD license, at your option.
|
||||
*
|
||||
* a) This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* Alternatively,
|
||||
*
|
||||
* b) Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "libfdt_env.h"
|
||||
|
||||
#include <fdt.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_address_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
const fdt32_t *ac;
|
||||
int val;
|
||||
int len;
|
||||
|
||||
ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len);
|
||||
if (!ac)
|
||||
return 2;
|
||||
|
||||
if (len != sizeof(*ac))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
val = fdt32_to_cpu(*ac);
|
||||
if ((val <= 0) || (val > FDT_MAX_NCELLS))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int fdt_size_cells(const void *fdt, int nodeoffset)
|
||||
{
|
||||
const fdt32_t *sc;
|
||||
int val;
|
||||
int len;
|
||||
|
||||
sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len);
|
||||
if (!sc)
|
||||
return 2;
|
||||
|
||||
if (len != sizeof(*sc))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
val = fdt32_to_cpu(*sc);
|
||||
if ((val < 0) || (val > FDT_MAX_NCELLS))
|
||||
return -FDT_ERR_BADNCELLS;
|
||||
|
||||
return val;
|
||||
}
|
676
contrib/dtc/libfdt/fdt_overlay.c
Normal file
676
contrib/dtc/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);
|
||||
}
|
||||
|
||||
uint32_t fdt_get_max_phandle(const void *fdt)
|
||||
{
|
||||
uint32_t max_phandle = 0;
|
||||
int offset;
|
||||
|
||||
for (offset = fdt_next_node(fdt, -1, NULL);;
|
||||
offset = fdt_next_node(fdt, offset, NULL)) {
|
||||
uint32_t phandle;
|
||||
|
||||
if (offset == -FDT_ERR_NOTFOUND)
|
||||
return max_phandle;
|
||||
|
||||
if (offset < 0)
|
||||
return (uint32_t)-1;
|
||||
|
||||
phandle = fdt_get_phandle(fdt, offset);
|
||||
if (phandle == (uint32_t)-1)
|
||||
continue;
|
||||
|
||||
if (phandle > max_phandle)
|
||||
max_phandle = phandle;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
|
||||
{
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
@ -154,9 +180,9 @@ int fdt_subnode_offset(const void *fdt, int parentoffset,
|
||||
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
|
||||
}
|
||||
|
||||
int fdt_path_offset(const void *fdt, const char *path)
|
||||
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
|
||||
{
|
||||
const char *end = path + strlen(path);
|
||||
const char *end = path + namelen;
|
||||
const char *p = path;
|
||||
int offset = 0;
|
||||
|
||||
@ -164,7 +190,7 @@ int fdt_path_offset(const void *fdt, const char *path)
|
||||
|
||||
/* see if we have an alias */
|
||||
if (*path != '/') {
|
||||
const char *q = strchr(path, '/');
|
||||
const char *q = memchr(path, '/', end - p);
|
||||
|
||||
if (!q)
|
||||
q = end;
|
||||
@ -177,14 +203,15 @@ int fdt_path_offset(const void *fdt, const char *path)
|
||||
p = q;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
while (p < end) {
|
||||
const char *q;
|
||||
|
||||
while (*p == '/')
|
||||
while (*p == '/') {
|
||||
p++;
|
||||
if (! *p)
|
||||
return offset;
|
||||
q = strchr(p, '/');
|
||||
if (p == end)
|
||||
return offset;
|
||||
}
|
||||
q = memchr(p, '/', end - p);
|
||||
if (! q)
|
||||
q = end;
|
||||
|
||||
@ -198,6 +225,11 @@ int fdt_path_offset(const void *fdt, const char *path)
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fdt_path_offset(const void *fdt, const char *path)
|
||||
{
|
||||
return fdt_path_offset_namelen(fdt, path, strlen(path));
|
||||
}
|
||||
|
||||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|
||||
{
|
||||
const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
|
||||
@ -532,6 +564,106 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length, count = 0;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
list += length;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
||||
const char *string)
|
||||
{
|
||||
int length, len, idx = 0;
|
||||
const char *list, *end;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list)
|
||||
return length;
|
||||
|
||||
len = strlen(string) + 1;
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end)
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
if (length == len && memcmp(list, string, length) == 0)
|
||||
return idx;
|
||||
|
||||
list += length;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
||||
const char *property, int idx,
|
||||
int *lenp)
|
||||
{
|
||||
const char *list, *end;
|
||||
int length;
|
||||
|
||||
list = fdt_getprop(fdt, nodeoffset, property, &length);
|
||||
if (!list) {
|
||||
if (lenp)
|
||||
*lenp = length;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
end = list + length;
|
||||
|
||||
while (list < end) {
|
||||
length = strnlen(list, end - list) + 1;
|
||||
|
||||
/* Abort if the last string isn't properly NUL-terminated. */
|
||||
if (list + length > end) {
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_BADVALUE;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
if (lenp)
|
||||
*lenp = length - 1;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
list += length;
|
||||
idx--;
|
||||
}
|
||||
|
||||
if (lenp)
|
||||
*lenp = -FDT_ERR_NOTFOUND;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
||||
const char *compatible)
|
||||
{
|
||||
@ -541,10 +673,8 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
||||
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
|
||||
if (!prop)
|
||||
return len;
|
||||
if (fdt_stringlist_contains(prop, len, compatible))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
|
||||
return !fdt_stringlist_contains(prop, len, compatible);
|
||||
}
|
||||
|
||||
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
||||
|
@ -84,9 +84,9 @@ static int _fdt_rw_check_header(void *fdt)
|
||||
|
||||
#define FDT_RW_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = _fdt_rw_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
int __err; \
|
||||
if ((__err = _fdt_rw_check_header(fdt)) != 0) \
|
||||
return __err; \
|
||||
}
|
||||
|
||||
static inline int _fdt_data_size(void *fdt)
|
||||
@ -101,6 +101,8 @@ static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
|
||||
|
||||
if (((p + oldlen) < p) || ((p + oldlen) > end))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
|
||||
return -FDT_ERR_BADOFFSET;
|
||||
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
|
||||
return -FDT_ERR_NOSPACE;
|
||||
memmove(p + newlen, p + oldlen, end - p - oldlen);
|
||||
@ -189,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)
|
||||
{
|
||||
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
|
||||
int err;
|
||||
|
||||
FDT_RW_CHECK_HEADER(fdt);
|
||||
|
||||
if (n >= fdt_num_mem_rsv(fdt))
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
err = _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
return _fdt_splice_mem_rsv(fdt, re, 1, 0);
|
||||
}
|
||||
|
||||
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
|
||||
@ -285,7 +283,8 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(prop->data, val, len);
|
||||
if (len)
|
||||
memcpy(prop->data, val, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ static struct fdt_errtabent fdt_errtable[] = {
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_BADOFFSET),
|
||||
FDT_ERRTABENT(FDT_ERR_BADPATH),
|
||||
FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTATE),
|
||||
|
||||
FDT_ERRTABENT(FDT_ERR_TRUNCATED),
|
||||
@ -76,6 +77,11 @@ static struct fdt_errtabent fdt_errtable[] = {
|
||||
FDT_ERRTABENT(FDT_ERR_BADVERSION),
|
||||
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
|
||||
FDT_ERRTABENT(FDT_ERR_INTERNAL),
|
||||
FDT_ERRTABENT(FDT_ERR_BADNCELLS),
|
||||
FDT_ERRTABENT(FDT_ERR_BADVALUE),
|
||||
FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
|
||||
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
|
||||
};
|
||||
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
|
||||
|
||||
|
@ -55,21 +55,42 @@
|
||||
|
||||
#include "libfdt_internal.h"
|
||||
|
||||
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
|
||||
const void *val, int len)
|
||||
int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
|
||||
const char *name, int namelen,
|
||||
uint32_t idx, const void *val,
|
||||
int len)
|
||||
{
|
||||
void *propval;
|
||||
int proplen;
|
||||
|
||||
propval = fdt_getprop_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)
|
||||
return proplen;
|
||||
|
||||
if (proplen != len)
|
||||
return -FDT_ERR_NOSPACE;
|
||||
|
||||
memcpy(propval, val, len);
|
||||
return 0;
|
||||
return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
|
||||
strlen(name), 0,
|
||||
val, len);
|
||||
}
|
||||
|
||||
static void _fdt_nop_region(void *start, int len)
|
||||
|
@ -61,7 +61,7 @@
|
||||
#define FDT_ERR_NOTFOUND 1
|
||||
/* FDT_ERR_NOTFOUND: The requested node or property does not exist */
|
||||
#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 */
|
||||
#define FDT_ERR_NOSPACE 3
|
||||
/* FDT_ERR_NOSPACE: Operation needed to expand the device
|
||||
@ -79,8 +79,10 @@
|
||||
* (e.g. missing a leading / for a function which requires an
|
||||
* absolute path) */
|
||||
#define FDT_ERR_BADPHANDLE 6
|
||||
/* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle
|
||||
* value. phandle values of 0 and -1 are not permitted. */
|
||||
/* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
|
||||
* 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
|
||||
/* FDT_ERR_BADSTATE: Function was passed an incomplete device
|
||||
* tree created by the sequential-write functions, which is
|
||||
@ -116,7 +118,26 @@
|
||||
* Should never be returned, if it is, it indicates a bug in
|
||||
* libfdt itself. */
|
||||
|
||||
#define FDT_ERR_MAX 13
|
||||
/* Errors in device tree content */
|
||||
#define FDT_ERR_BADNCELLS 14
|
||||
/* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
|
||||
* or similar property with a bad format or value */
|
||||
|
||||
#define FDT_ERR_BADVALUE 15
|
||||
/* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
|
||||
* value. For example: a property expected to contain a string list
|
||||
* is not NUL-terminated within the length of its value. */
|
||||
|
||||
#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) */
|
||||
@ -158,27 +179,55 @@ int fdt_first_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 */
|
||||
/**********************************************************************/
|
||||
|
||||
#define fdt_get_header(fdt, field) \
|
||||
(fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
|
||||
#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
|
||||
#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
|
||||
#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
|
||||
#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
|
||||
#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
|
||||
#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
|
||||
#define fdt_version(fdt) (fdt_get_header(fdt, version))
|
||||
#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
|
||||
#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
|
||||
#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
|
||||
#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
|
||||
#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
|
||||
#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
|
||||
#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
|
||||
|
||||
#define __fdt_set_hdr(name) \
|
||||
static inline void fdt_set_##name(void *fdt, uint32_t val) \
|
||||
{ \
|
||||
struct fdt_header *fdth = (struct fdt_header*)fdt; \
|
||||
struct fdt_header *fdth = (struct fdt_header *)fdt; \
|
||||
fdth->name = cpu_to_fdt32(val); \
|
||||
}
|
||||
__fdt_set_hdr(magic);
|
||||
@ -248,6 +297,21 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
|
||||
*/
|
||||
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: pointer to the device tree blob
|
||||
@ -308,8 +372,9 @@ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
|
||||
* returns:
|
||||
* structure block offset of the requested subnode (>=0), on success
|
||||
* -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_BADMAGIC,
|
||||
* -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
|
||||
* tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
* -FDT_ERR_BADSTRUCTURE,
|
||||
@ -317,6 +382,17 @@ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
|
||||
*/
|
||||
int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
|
||||
|
||||
/**
|
||||
* fdt_path_offset_namelen - find a tree node by its full path
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @path: full path of the node to locate
|
||||
* @namelen: number of characters of path to consider
|
||||
*
|
||||
* Identical to fdt_path_offset(), but only consider the first namelen
|
||||
* characters of path as the path name.
|
||||
*/
|
||||
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
|
||||
|
||||
/**
|
||||
* fdt_path_offset - find a tree node by its full path
|
||||
* @fdt: pointer to the device tree blob
|
||||
@ -330,7 +406,8 @@ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
|
||||
* address).
|
||||
*
|
||||
* 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_NOTFOUND, if the requested node does not exist
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
@ -354,10 +431,12 @@ int fdt_path_offset(const void *fdt, const char *path);
|
||||
*
|
||||
* returns:
|
||||
* 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
|
||||
* 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_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE, standard meanings
|
||||
@ -405,6 +484,33 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset);
|
||||
*/
|
||||
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: pointer to the device tree blob
|
||||
@ -441,8 +547,8 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
|
||||
* @namelen: number of characters of name to consider
|
||||
* @lenp: pointer to an integer variable (will be overwritten) or NULL
|
||||
*
|
||||
* Identical to fdt_get_property_namelen(), but only examine the first
|
||||
* namelen characters of name for matching the property name.
|
||||
* Identical to fdt_get_property(), but only examine the first namelen
|
||||
* characters of name for matching the property name.
|
||||
*/
|
||||
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
||||
int nodeoffset,
|
||||
@ -469,7 +575,8 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt,
|
||||
* NULL, on error
|
||||
* if lenp is non-NULL, *lenp contains an error code (<0):
|
||||
* -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_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -533,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 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
|
||||
@ -554,7 +668,8 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
|
||||
* NULL, on error
|
||||
* if lenp is non-NULL, *lenp contains an error code (<0):
|
||||
* -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_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -596,7 +711,7 @@ const char *fdt_get_alias_namelen(const void *fdt,
|
||||
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
|
||||
* @name: name of the alias th look up
|
||||
*
|
||||
@ -626,7 +741,7 @@ const char *fdt_get_alias(const void *fdt, const char *name);
|
||||
* 0, on success
|
||||
* buf contains the absolute path of the node at
|
||||
* nodeoffset, as a NUL-terminated string.
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
|
||||
* characters and will not fit in the given buffer.
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
@ -656,11 +771,11 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
|
||||
* structure from the start to nodeoffset.
|
||||
*
|
||||
* returns:
|
||||
|
||||
* structure block offset of the node at node offset's ancestor
|
||||
* of depth supernodedepth (>=0), on success
|
||||
* -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_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
|
||||
* nodeoffset
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -682,7 +797,7 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
|
||||
*
|
||||
* returns:
|
||||
* depth of the node at nodeoffset (>=0), on success
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -705,7 +820,7 @@ int fdt_node_depth(const void *fdt, int nodeoffset);
|
||||
* returns:
|
||||
* structure block offset of the parent of the node at nodeoffset
|
||||
* (>=0), on success
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -745,7 +860,7 @@ int fdt_parent_offset(const void *fdt, int nodeoffset);
|
||||
* on success
|
||||
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
|
||||
* tree after startoffset
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -792,7 +907,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
|
||||
* 1, if the node has a 'compatible' property, but it does not list
|
||||
* the given string
|
||||
* -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
|
||||
* -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -829,7 +944,7 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset,
|
||||
* on success
|
||||
* -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
|
||||
* tree after startoffset
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
@ -852,10 +967,150 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
|
||||
*/
|
||||
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
|
||||
|
||||
/**
|
||||
* fdt_stringlist_count - count the number of strings in a string list
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of a tree node
|
||||
* @property: name of the property containing the string list
|
||||
* @return:
|
||||
* the number of strings in the given property
|
||||
* -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
||||
* -FDT_ERR_NOTFOUND if the property does not exist
|
||||
*/
|
||||
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
|
||||
|
||||
/**
|
||||
* fdt_stringlist_search - find a string in a string list and return its index
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of a tree node
|
||||
* @property: name of the property containing the string list
|
||||
* @string: string to look up in the string list
|
||||
*
|
||||
* Note that it is possible for this function to succeed on property values
|
||||
* that are not NUL-terminated. That's because the function will stop after
|
||||
* finding the first occurrence of @string. This can for example happen with
|
||||
* small-valued cell properties, such as #address-cells, when searching for
|
||||
* the empty string.
|
||||
*
|
||||
* @return:
|
||||
* the index of the string in the list of strings
|
||||
* -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
||||
* -FDT_ERR_NOTFOUND if the property does not exist or does not contain
|
||||
* the given string
|
||||
*/
|
||||
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
|
||||
const char *string);
|
||||
|
||||
/**
|
||||
* fdt_stringlist_get() - obtain the string at a given index in a string list
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of a tree node
|
||||
* @property: name of the property containing the string list
|
||||
* @index: index of the string to return
|
||||
* @lenp: return location for the string length or an error code on failure
|
||||
*
|
||||
* Note that this will successfully extract strings from properties with
|
||||
* non-NUL-terminated values. For example on small-valued cell properties
|
||||
* this function will return the empty string.
|
||||
*
|
||||
* If non-NULL, the length of the string (on success) or a negative error-code
|
||||
* (on failure) will be stored in the integer pointer to by lenp.
|
||||
*
|
||||
* @return:
|
||||
* A pointer to the string at the given index in the string list or NULL on
|
||||
* failure. On success the length of the string will be stored in the memory
|
||||
* location pointed to by the lenp parameter, if non-NULL. On failure one of
|
||||
* the following negative error codes will be returned in the lenp parameter
|
||||
* (if non-NULL):
|
||||
* -FDT_ERR_BADVALUE if the property value is not NUL-terminated
|
||||
* -FDT_ERR_NOTFOUND if the property does not exist
|
||||
*/
|
||||
const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
|
||||
const char *property, int index,
|
||||
int *lenp);
|
||||
|
||||
/**********************************************************************/
|
||||
/* Read-only functions (addressing related) */
|
||||
/**********************************************************************/
|
||||
|
||||
/**
|
||||
* FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
|
||||
*
|
||||
* This is the maximum value for #address-cells, #size-cells and
|
||||
* similar properties that will be processed by libfdt. IEE1275
|
||||
* requires that OF implementations handle values up to 4.
|
||||
* Implementations may support larger values, but in practice higher
|
||||
* values aren't used.
|
||||
*/
|
||||
#define FDT_MAX_NCELLS 4
|
||||
|
||||
/**
|
||||
* fdt_address_cells - retrieve address size for a bus represented in the tree
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of the node to find the address size for
|
||||
*
|
||||
* When the node has a valid #address-cells property, returns its value.
|
||||
*
|
||||
* returns:
|
||||
* 0 <= n < FDT_MAX_NCELLS, on success
|
||||
* 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_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
* -FDT_ERR_BADSTRUCTURE,
|
||||
* -FDT_ERR_TRUNCATED, standard meanings
|
||||
*/
|
||||
int fdt_address_cells(const void *fdt, int nodeoffset);
|
||||
|
||||
/**
|
||||
* fdt_size_cells - retrieve address range size for a bus represented in the
|
||||
* tree
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of the node to find the address range size for
|
||||
*
|
||||
* When the node has a valid #size-cells property, returns its value.
|
||||
*
|
||||
* returns:
|
||||
* 0 <= n < FDT_MAX_NCELLS, on success
|
||||
* 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_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
* -FDT_ERR_BADSTRUCTURE,
|
||||
* -FDT_ERR_TRUNCATED, standard meanings
|
||||
*/
|
||||
int fdt_size_cells(const void *fdt, int nodeoffset);
|
||||
|
||||
|
||||
/**********************************************************************/
|
||||
/* 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: pointer to the device tree blob
|
||||
@ -1272,6 +1527,36 @@ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
|
||||
#define fdt_setprop_string(fdt, nodeoffset, name, str) \
|
||||
fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
|
||||
|
||||
|
||||
/**
|
||||
* fdt_setprop_empty - set a property to an empty value
|
||||
* @fdt: pointer to the device tree blob
|
||||
* @nodeoffset: offset of the node whose property to change
|
||||
* @name: name of the property to change
|
||||
*
|
||||
* fdt_setprop_empty() sets the value of the named property in the
|
||||
* given node to an empty (zero length) value, or creates a new empty
|
||||
* property if it does not already exist.
|
||||
*
|
||||
* This function may insert or delete data from the blob, and will
|
||||
* therefore change the offsets of some existing nodes.
|
||||
*
|
||||
* returns:
|
||||
* 0, on success
|
||||
* -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
|
||||
* contain the new property value
|
||||
* -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
|
||||
* -FDT_ERR_BADLAYOUT,
|
||||
* -FDT_ERR_BADMAGIC,
|
||||
* -FDT_ERR_BADVERSION,
|
||||
* -FDT_ERR_BADSTATE,
|
||||
* -FDT_ERR_BADSTRUCTURE,
|
||||
* -FDT_ERR_BADLAYOUT,
|
||||
* -FDT_ERR_TRUNCATED, standard meanings
|
||||
*/
|
||||
#define fdt_setprop_empty(fdt, nodeoffset, name) \
|
||||
fdt_setprop((fdt), (nodeoffset), (name), NULL, 0)
|
||||
|
||||
/**
|
||||
* fdt_appendprop - append to or create a property
|
||||
* @fdt: pointer to the device tree blob
|
||||
@ -1466,9 +1751,11 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
||||
* change the offsets of some existing nodes.
|
||||
|
||||
* 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_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
|
||||
* the given name
|
||||
* -FDT_ERR_NOSPACE, if there is insufficient free space in the
|
||||
@ -1506,6 +1793,37 @@ int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
|
||||
*/
|
||||
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 */
|
||||
/**********************************************************************/
|
||||
|
@ -54,6 +54,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __CHECKER__
|
||||
|
@ -57,9 +57,9 @@
|
||||
|
||||
#define FDT_CHECK_HEADER(fdt) \
|
||||
{ \
|
||||
int err; \
|
||||
if ((err = fdt_check_header(fdt)) != 0) \
|
||||
return err; \
|
||||
int __err; \
|
||||
if ((__err = fdt_check_header(fdt)) != 0) \
|
||||
return __err; \
|
||||
}
|
||||
|
||||
int _fdt_check_node_offset(const void *fdt, int offset);
|
||||
|
@ -8,6 +8,7 @@ LIBFDT_1.2 {
|
||||
fdt_get_mem_rsv;
|
||||
fdt_subnode_offset_namelen;
|
||||
fdt_subnode_offset;
|
||||
fdt_path_offset_namelen;
|
||||
fdt_path_offset;
|
||||
fdt_get_name;
|
||||
fdt_get_property_namelen;
|
||||
@ -54,6 +55,13 @@ LIBFDT_1.2 {
|
||||
fdt_get_property_by_offset;
|
||||
fdt_getprop_by_offset;
|
||||
fdt_next_property_offset;
|
||||
fdt_first_subnode;
|
||||
fdt_next_subnode;
|
||||
fdt_address_cells;
|
||||
fdt_size_cells;
|
||||
fdt_stringlist_contains;
|
||||
fdt_resize;
|
||||
fdt_overlay_apply;
|
||||
|
||||
local:
|
||||
*;
|
||||
|
@ -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)
|
||||
add_child(old_node, new_child);
|
||||
}
|
||||
@ -242,7 +242,7 @@ void delete_property_by_name(struct node *node, char *name)
|
||||
struct property *prop = node->proplist;
|
||||
|
||||
while (prop) {
|
||||
if (!strcmp(prop->name, name)) {
|
||||
if (streq(prop->name, name)) {
|
||||
delete_property(prop);
|
||||
return;
|
||||
}
|
||||
@ -275,7 +275,7 @@ void delete_node_by_name(struct node *parent, char *name)
|
||||
struct node *node = parent->children;
|
||||
|
||||
while (node) {
|
||||
if (!strcmp(node->name, name)) {
|
||||
if (streq(node->name, name)) {
|
||||
delete_node(node);
|
||||
return;
|
||||
}
|
||||
@ -296,6 +296,23 @@ void delete_node(struct node *node)
|
||||
delete_labels(&node->labels);
|
||||
}
|
||||
|
||||
void append_to_property(struct node *node,
|
||||
char *name, const void *data, int len)
|
||||
{
|
||||
struct data d;
|
||||
struct property *p;
|
||||
|
||||
p = get_property(node, name);
|
||||
if (p) {
|
||||
d = data_append_data(p->val, data, len);
|
||||
p->val = d;
|
||||
} else {
|
||||
d = data_append_data(empty_data, data, len);
|
||||
p = build_property(name, d);
|
||||
add_property(node, p);
|
||||
}
|
||||
}
|
||||
|
||||
struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size)
|
||||
{
|
||||
struct reserve_info *new = xmalloc(sizeof(*new));
|
||||
@ -335,17 +352,19 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
|
||||
return list;
|
||||
}
|
||||
|
||||
struct boot_info *build_boot_info(struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys)
|
||||
struct dt_info *build_dt_info(unsigned int dtsflags,
|
||||
struct reserve_info *reservelist,
|
||||
struct node *tree, uint32_t boot_cpuid_phys)
|
||||
{
|
||||
struct boot_info *bi;
|
||||
struct dt_info *dti;
|
||||
|
||||
bi = xmalloc(sizeof(*bi));
|
||||
bi->reservelist = reservelist;
|
||||
bi->dt = tree;
|
||||
bi->boot_cpuid_phys = boot_cpuid_phys;
|
||||
dti = xmalloc(sizeof(*dti));
|
||||
dti->dtsflags = dtsflags;
|
||||
dti->reservelist = reservelist;
|
||||
dti->dt = tree;
|
||||
dti->boot_cpuid_phys = boot_cpuid_phys;
|
||||
|
||||
return bi;
|
||||
return dti;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -511,7 +530,9 @@ struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
|
||||
|
||||
struct node *get_node_by_ref(struct node *tree, const char *ref)
|
||||
{
|
||||
if (ref[0] == '/')
|
||||
if (streq(ref, "/"))
|
||||
return tree;
|
||||
else if (ref[0] == '/')
|
||||
return get_node_by_path(tree, ref);
|
||||
else
|
||||
return get_node_by_label(tree, ref);
|
||||
@ -590,12 +611,12 @@ static int cmp_reserve_info(const void *ax, const void *bx)
|
||||
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;
|
||||
int n = 0, i = 0;
|
||||
|
||||
for (ri = bi->reservelist;
|
||||
for (ri = dti->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
n++;
|
||||
@ -605,14 +626,14 @@ static void sort_reserve_entries(struct boot_info *bi)
|
||||
|
||||
tbl = xmalloc(n * sizeof(*tbl));
|
||||
|
||||
for (ri = bi->reservelist;
|
||||
for (ri = dti->reservelist;
|
||||
ri;
|
||||
ri = ri->next)
|
||||
tbl[i++] = ri;
|
||||
|
||||
qsort(tbl, n, sizeof(*tbl), cmp_reserve_info);
|
||||
|
||||
bi->reservelist = tbl[0];
|
||||
dti->reservelist = tbl[0];
|
||||
for (i = 0; i < (n-1); i++)
|
||||
tbl[i]->next = tbl[i+1];
|
||||
tbl[n-1]->next = NULL;
|
||||
@ -702,8 +723,258 @@ static void sort_node(struct node *node)
|
||||
sort_node(c);
|
||||
}
|
||||
|
||||
void sort_tree(struct boot_info *bi)
|
||||
void sort_tree(struct dt_info *dti)
|
||||
{
|
||||
sort_reserve_entries(bi);
|
||||
sort_node(bi->dt);
|
||||
sort_reserve_entries(dti);
|
||||
sort_node(dti->dt);
|
||||
}
|
||||
|
||||
/* utility helper to avoid code duplication */
|
||||
static struct node *build_and_name_child_node(struct node *parent, char *name)
|
||||
{
|
||||
struct node *node;
|
||||
|
||||
node = build_node(NULL, NULL);
|
||||
name_node(node, xstrdup(name));
|
||||
add_child(parent, node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static struct node *build_root_node(struct node *dt, char *name)
|
||||
{
|
||||
struct node *an;
|
||||
|
||||
an = get_subnode(dt, name);
|
||||
if (!an)
|
||||
an = build_and_name_child_node(dt, name);
|
||||
|
||||
if (!an)
|
||||
die("Could not build root node /%s\n", name);
|
||||
|
||||
return an;
|
||||
}
|
||||
|
||||
static bool any_label_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
|
||||
if (node->labels)
|
||||
return true;
|
||||
|
||||
for_each_child(node, c)
|
||||
if (any_label_tree(dti, c))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void generate_label_tree_internal(struct dt_info *dti,
|
||||
struct node *an, struct node *node,
|
||||
bool allocph)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *p;
|
||||
struct label *l;
|
||||
|
||||
/* if there are labels */
|
||||
if (node->labels) {
|
||||
|
||||
/* now add the label in the node */
|
||||
for_each_label(node->labels, l) {
|
||||
|
||||
/* check whether the label already exists */
|
||||
p = get_property(an, l->label);
|
||||
if (p) {
|
||||
fprintf(stderr, "WARNING: label %s already"
|
||||
" exists in /%s", l->label,
|
||||
an->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* insert it */
|
||||
p = build_property(l->label,
|
||||
data_copy_mem(node->fullpath,
|
||||
strlen(node->fullpath) + 1));
|
||||
add_property(an, p);
|
||||
}
|
||||
|
||||
/* force allocation of a phandle for this node */
|
||||
if (allocph)
|
||||
(void)get_node_phandle(dt, node);
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_label_tree_internal(dti, an, c, allocph);
|
||||
}
|
||||
|
||||
static bool any_fixup_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
if (!get_node_by_ref(dti->dt, m->ref))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c) {
|
||||
if (any_fixup_tree(dti, c))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_fixup_entry(struct dt_info *dti, struct node *fn,
|
||||
struct node *node, struct property *prop,
|
||||
struct marker *m)
|
||||
{
|
||||
char *entry;
|
||||
|
||||
/* m->ref can only be a REF_PHANDLE, but check anyway */
|
||||
assert(m->type == REF_PHANDLE);
|
||||
|
||||
/* there shouldn't be any ':' in the arguments */
|
||||
if (strchr(node->fullpath, ':') || strchr(prop->name, ':'))
|
||||
die("arguments should not contain ':'\n");
|
||||
|
||||
xasprintf(&entry, "%s:%s:%u",
|
||||
node->fullpath, prop->name, m->offset);
|
||||
append_to_property(fn, m->ref, entry, strlen(entry) + 1);
|
||||
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static void generate_fixups_tree_internal(struct dt_info *dti,
|
||||
struct node *fn,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
struct node *refnode;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (!refnode)
|
||||
add_fixup_entry(dti, fn, node, prop, m);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_fixups_tree_internal(dti, fn, c);
|
||||
}
|
||||
|
||||
static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
|
||||
{
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
if (get_node_by_ref(dti->dt, m->ref))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c) {
|
||||
if (any_local_fixup_tree(dti, c))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void add_local_fixup_entry(struct dt_info *dti,
|
||||
struct node *lfn, struct node *node,
|
||||
struct property *prop, struct marker *m,
|
||||
struct node *refnode)
|
||||
{
|
||||
struct node *wn, *nwn; /* local fixup node, walk node, new */
|
||||
uint32_t value_32;
|
||||
char **compp;
|
||||
int i, depth;
|
||||
|
||||
/* walk back retreiving depth */
|
||||
depth = 0;
|
||||
for (wn = node; wn; wn = wn->parent)
|
||||
depth++;
|
||||
|
||||
/* allocate name array */
|
||||
compp = xmalloc(sizeof(*compp) * depth);
|
||||
|
||||
/* store names in the array */
|
||||
for (wn = node, i = depth - 1; wn; wn = wn->parent, i--)
|
||||
compp[i] = wn->name;
|
||||
|
||||
/* walk the path components creating nodes if they don't exist */
|
||||
for (wn = lfn, i = 1; i < depth; i++, wn = nwn) {
|
||||
/* if no node exists, create it */
|
||||
nwn = get_subnode(wn, compp[i]);
|
||||
if (!nwn)
|
||||
nwn = build_and_name_child_node(wn, compp[i]);
|
||||
}
|
||||
|
||||
free(compp);
|
||||
|
||||
value_32 = cpu_to_fdt32(m->offset);
|
||||
append_to_property(wn, prop->name, &value_32, sizeof(value_32));
|
||||
}
|
||||
|
||||
static void generate_local_fixups_tree_internal(struct dt_info *dti,
|
||||
struct node *lfn,
|
||||
struct node *node)
|
||||
{
|
||||
struct node *dt = dti->dt;
|
||||
struct node *c;
|
||||
struct property *prop;
|
||||
struct marker *m;
|
||||
struct node *refnode;
|
||||
|
||||
for_each_property(node, prop) {
|
||||
m = prop->val.markers;
|
||||
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||
refnode = get_node_by_ref(dt, m->ref);
|
||||
if (refnode)
|
||||
add_local_fixup_entry(dti, lfn, node, prop, m, refnode);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child(node, c)
|
||||
generate_local_fixups_tree_internal(dti, lfn, c);
|
||||
}
|
||||
|
||||
void generate_label_tree(struct dt_info *dti, char *name, bool allocph)
|
||||
{
|
||||
if (!any_label_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_label_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt, allocph);
|
||||
}
|
||||
|
||||
void generate_fixups_tree(struct dt_info *dti, char *name)
|
||||
{
|
||||
if (!any_fixup_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_fixups_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt);
|
||||
}
|
||||
|
||||
void generate_local_fixups_tree(struct dt_info *dti, char *name)
|
||||
{
|
||||
if (!any_local_fixup_tree(dti, dti->dt))
|
||||
return;
|
||||
generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name),
|
||||
dti->dt);
|
||||
}
|
||||
|
31
contrib/dtc/scripts/kup-dtc
Executable file
31
contrib/dtc/scripts/kup-dtc
Executable file
@ -0,0 +1,31 @@
|
||||
#! /bin/sh
|
||||
|
||||
REMOTE_GIT=/pub/scm/utils/dtc/dtc.git
|
||||
REMOTE_PATH=/pub/software/utils/dtc
|
||||
|
||||
set -e
|
||||
|
||||
kup_one () {
|
||||
VERSION="$1"
|
||||
|
||||
TAG="v$VERSION"
|
||||
|
||||
PREFIX="dtc-$VERSION/"
|
||||
TAR="dtc-$VERSION.tar"
|
||||
SIG="$TAR.sign"
|
||||
|
||||
git archive --format=tar --prefix="$PREFIX" -o "$TAR" "$TAG"
|
||||
gpg --detach-sign --armor -o "$SIG" "$TAR"
|
||||
|
||||
ls -l "$TAR"*
|
||||
|
||||
# Verify the signature as a sanity check
|
||||
gpg --verify "$SIG" "$TAR"
|
||||
|
||||
kup put --tar --prefix="$PREFIX" "$REMOTE_GIT" "$TAG" "$SIG" "$REMOTE_PATH/$TAR.gz"
|
||||
}
|
||||
|
||||
for version; do
|
||||
kup_one $version
|
||||
done
|
||||
|
@ -34,7 +34,7 @@ struct search_path {
|
||||
static struct search_path *search_path_head, **search_path_tail;
|
||||
|
||||
|
||||
static char *dirname(const char *path)
|
||||
static char *get_dirname(const char *path)
|
||||
{
|
||||
const char *slash = strrchr(path, '/');
|
||||
|
||||
@ -77,7 +77,7 @@ static char *try_open(const char *dirname, const char *fname, FILE **fp)
|
||||
else
|
||||
fullname = join_path(dirname, fname);
|
||||
|
||||
*fp = fopen(fullname, "r");
|
||||
*fp = fopen(fullname, "rb");
|
||||
if (!*fp) {
|
||||
free(fullname);
|
||||
fullname = NULL;
|
||||
@ -150,7 +150,7 @@ void srcfile_push(const char *fname)
|
||||
srcfile = xmalloc(sizeof(*srcfile));
|
||||
|
||||
srcfile->f = srcfile_relative_open(fname, &srcfile->name);
|
||||
srcfile->dir = dirname(srcfile->name);
|
||||
srcfile->dir = get_dirname(srcfile->name);
|
||||
srcfile->prev = current_srcfile;
|
||||
|
||||
srcfile->lineno = 1;
|
||||
@ -246,46 +246,27 @@ srcpos_copy(struct srcpos *pos)
|
||||
return pos_new;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
srcpos_dump(struct srcpos *pos)
|
||||
{
|
||||
printf("file : \"%s\"\n",
|
||||
pos->file ? (char *) pos->file : "<no file>");
|
||||
printf("first_line : %d\n", pos->first_line);
|
||||
printf("first_column: %d\n", pos->first_column);
|
||||
printf("last_line : %d\n", pos->last_line);
|
||||
printf("last_column : %d\n", pos->last_column);
|
||||
printf("file : %s\n", pos->file->name);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
srcpos_string(struct srcpos *pos)
|
||||
{
|
||||
const char *fname = "<no-file>";
|
||||
char *pos_str;
|
||||
int rc;
|
||||
|
||||
if (pos)
|
||||
if (pos->file && pos->file->name)
|
||||
fname = pos->file->name;
|
||||
|
||||
|
||||
if (pos->first_line != pos->last_line)
|
||||
rc = asprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_line, pos->last_column);
|
||||
xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_line, pos->last_column);
|
||||
else if (pos->first_column != pos->last_column)
|
||||
rc = asprintf(&pos_str, "%s:%d.%d-%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_column);
|
||||
xasprintf(&pos_str, "%s:%d.%d-%d", fname,
|
||||
pos->first_line, pos->first_column,
|
||||
pos->last_column);
|
||||
else
|
||||
rc = asprintf(&pos_str, "%s:%d.%d", fname,
|
||||
pos->first_line, pos->first_column);
|
||||
|
||||
if (rc == -1)
|
||||
die("Couldn't allocate in srcpos string");
|
||||
xasprintf(&pos_str, "%s:%d.%d", fname,
|
||||
pos->first_line, pos->first_column);
|
||||
|
||||
return pos_str;
|
||||
}
|
||||
|
@ -105,7 +105,6 @@ extern struct srcpos srcpos_empty;
|
||||
extern void srcpos_update(struct srcpos *pos, const char *text, int len);
|
||||
extern struct srcpos *srcpos_copy(struct srcpos *pos);
|
||||
extern char *srcpos_string(struct srcpos *pos);
|
||||
extern void srcpos_dump(struct srcpos *pos);
|
||||
|
||||
extern void srcpos_verror(struct srcpos *pos, const char *prefix,
|
||||
const char *fmt, va_list va)
|
||||
|
@ -25,12 +25,12 @@ extern FILE *yyin;
|
||||
extern int yyparse(void);
|
||||
extern YYLTYPE yylloc;
|
||||
|
||||
struct boot_info *the_boot_info;
|
||||
struct dt_info *parser_output;
|
||||
bool treesource_error;
|
||||
|
||||
struct boot_info *dt_from_source(const char *fname)
|
||||
struct dt_info *dt_from_source(const char *fname)
|
||||
{
|
||||
the_boot_info = NULL;
|
||||
parser_output = NULL;
|
||||
treesource_error = false;
|
||||
|
||||
srcfile_push(fname);
|
||||
@ -43,7 +43,7 @@ struct boot_info *dt_from_source(const char *fname)
|
||||
if (treesource_error)
|
||||
die("Syntax error parsing input tree\n");
|
||||
|
||||
return the_boot_info;
|
||||
return parser_output;
|
||||
}
|
||||
|
||||
static void write_prefix(FILE *f, int level)
|
||||
@ -109,7 +109,7 @@ static void write_propval_string(FILE *f, struct data val)
|
||||
break;
|
||||
case '\0':
|
||||
fprintf(f, "\", ");
|
||||
while (m && (m->offset < i)) {
|
||||
while (m && (m->offset <= (i + 1))) {
|
||||
if (m->type == LABEL) {
|
||||
assert(m->offset == (i+1));
|
||||
fprintf(f, "%s: ", m->ref);
|
||||
@ -178,7 +178,7 @@ static void write_propval_bytes(FILE *f, struct data val)
|
||||
m = m->next;
|
||||
}
|
||||
|
||||
fprintf(f, "%02hhx", *bp++);
|
||||
fprintf(f, "%02hhx", (unsigned char)(*bp++));
|
||||
if ((const void *)bp >= propend)
|
||||
break;
|
||||
fprintf(f, " ");
|
||||
@ -263,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;
|
||||
|
||||
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;
|
||||
|
||||
for_each_label(re->labels, l)
|
||||
@ -279,6 +279,6 @@ void dt_to_source(FILE *f, struct boot_info *bi)
|
||||
(unsigned long long)re->re.size);
|
||||
}
|
||||
|
||||
write_tree_source_node(f, bi->dt, 0);
|
||||
write_tree_source_node(f, dti->dt, 0);
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,41 @@
|
||||
char *xstrdup(const char *s)
|
||||
{
|
||||
int len = strlen(s) + 1;
|
||||
char *dup = xmalloc(len);
|
||||
char *d = xmalloc(len);
|
||||
|
||||
memcpy(dup, s, len);
|
||||
memcpy(d, s, len);
|
||||
|
||||
return dup;
|
||||
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)
|
||||
@ -152,7 +182,6 @@ char get_escape_char(const char *s, int *i)
|
||||
int j = *i + 1;
|
||||
char val;
|
||||
|
||||
assert(c);
|
||||
switch (c) {
|
||||
case 'a':
|
||||
val = '\a';
|
||||
@ -219,10 +248,6 @@ int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len)
|
||||
if (offset == bufsize) {
|
||||
bufsize *= 2;
|
||||
buf = xrealloc(buf, bufsize);
|
||||
if (!buf) {
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = read(fd, &buf[offset], bufsize - offset);
|
||||
@ -353,7 +378,6 @@ int utilfdt_decode_type(const char *fmt, int *type, int *size)
|
||||
void utilfdt_print_data(const char *data, int len)
|
||||
{
|
||||
int i;
|
||||
const char *p = data;
|
||||
const char *s;
|
||||
|
||||
/* no data, don't print */
|
||||
@ -375,11 +399,12 @@ void utilfdt_print_data(const char *data, int len)
|
||||
const uint32_t *cell = (const uint32_t *)data;
|
||||
|
||||
printf(" = <");
|
||||
for (i = 0; i < len; i += 4)
|
||||
printf("0x%08x%s", fdt32_to_cpu(cell[i / 4]),
|
||||
i < (len - 4) ? " " : "");
|
||||
for (i = 0, len /= 4; i < len; i++)
|
||||
printf("0x%08x%s", fdt32_to_cpu(cell[i]),
|
||||
i < (len - 1) ? " " : "");
|
||||
printf(">");
|
||||
} else {
|
||||
const unsigned char *p = (const unsigned char *)data;
|
||||
printf(" = [");
|
||||
for (i = 0; i < len; i++)
|
||||
printf("%02x%s", *p++, i < len - 1 ? " " : "");
|
||||
|
@ -27,13 +27,20 @@
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
static inline void __attribute__((noreturn)) die(const char *str, ...)
|
||||
#ifdef __GNUC__
|
||||
static inline void
|
||||
__attribute__((noreturn)) __attribute__((format (printf, 1, 2)))
|
||||
die(const char *str, ...)
|
||||
#else
|
||||
static inline void die(const char *str, ...)
|
||||
#endif
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
vfprintf(stderr, str, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -52,12 +59,19 @@ static inline void *xrealloc(void *p, size_t len)
|
||||
void *new = realloc(p, len);
|
||||
|
||||
if (!new)
|
||||
die("realloc() failed (len=%d)\n", len);
|
||||
die("realloc() failed (len=%zd)\n", len);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
extern char *xstrdup(const char *s);
|
||||
|
||||
#ifdef __GNUC__
|
||||
extern int __attribute__((format (printf, 2, 3)))
|
||||
xasprintf(char **strp, const char *fmt, ...);
|
||||
#else
|
||||
extern int xasprintf(char **strp, const char *fmt, ...);
|
||||
#endif
|
||||
extern char *join_path(const char *path, const char *name);
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user