diff options
author | Oleksandr Tymoshenko <gonzo@FreeBSD.org> | 2017-03-10 03:28:43 +0000 |
---|---|---|
committer | Oleksandr Tymoshenko <gonzo@FreeBSD.org> | 2017-03-10 03:28:43 +0000 |
commit | f059bd1ebfc4cf2e96c4639ad7fa6cf3a3198a2f (patch) | |
tree | 74d52c3db99ba80520d6142e9bc77e2802093021 | |
parent | b903311b940763cafe4fb3d8ab3da9c135c17b0c (diff) |
Import dtc 1.4.3vendor/dtc/1.4.3
Notes
Notes:
svn path=/vendor/dtc/dist/; revision=314985
svn path=/vendor/dtc/1.4.3/; revision=314986; tag=vendor/dtc/1.4.3
99 files changed, 4621 insertions, 853 deletions
diff --git a/Documentation/dt-object-internal.txt b/Documentation/dt-object-internal.txt new file mode 100644 index 000000000000..51d68ab93ac9 --- /dev/null +++ b/Documentation/dt-object-internal.txt @@ -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. diff --git a/Documentation/manual.txt b/Documentation/manual.txt index 65c8540be71b..2f073501f82b 100644 --- a/Documentation/manual.txt +++ b/Documentation/manual.txt @@ -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 diff --git a/Makefile.ftdump b/Makefile.ftdump deleted file mode 100644 index b70905ad10c9..000000000000 --- a/Makefile.ftdump +++ /dev/null @@ -1,12 +0,0 @@ -# -# This is not a complete Makefile of itself. -# Instead, it is designed to be easily embeddable -# into other systems of Makefiles. -# - -FTDUMP_SRCS = \ - ftdump.c - -FTDUMP_GEN_SRCS = - -FTDUMP_OBJS = $(FTDUMP_SRCS:%.c=%.o) $(FTDUMP_GEN_SRCS:%.c=%.o) diff --git a/README b/README new file mode 100644 index 000000000000..f92008f645f6 --- /dev/null +++ b/README @@ -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 @@ out: */ /* 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); -} -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; + 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, dt, l->label, node, prop, NULL); + 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, dt, m->ref, node, prop, m); + for_each_marker_of_type(m, LABEL) + check_duplicate_label(c, dti, m->ref, node, prop, m); + } } -ERROR(duplicate_label, NULL, check_duplicate_label_node, - check_duplicate_label_prop, NULL); +ERROR(duplicate_label, check_duplicate_label_node, NULL); -static void check_explicit_phandles(struct check *c, struct node *root, - struct node *node, struct property *prop) +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; + + 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; + } - refnode = get_node_by_ref(dt, m->ref); - if (! refnode) { - FAIL(c, "Reference to non-existent node or label \"%s\"\n", - m->ref); - 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_property(node, prop) { + struct marker *m = prop->val.markers; + struct node *refnode; + char *path; - for_each_marker_of_type(m, REF_PATH) { - assert(m->offset <= prop->val.len); + 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, "Reference to non-existent node or label \"%s\"\n", - m->ref); - continue; - } + 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; @@ -624,32 +702,40 @@ static void check_avoid_default_addr_size(struct check *c, struct node *dt, if (!reg && !ranges) return; - if ((node->parent->addr_cells == -1)) - FAIL(c, "Relying on default #address-cells value for %s", + if (node->parent->addr_cells == -1) + FAIL(c, dti, "Relying on default #address-cells value for %s", node->fullpath); - if ((node->parent->size_cells == -1)) - FAIL(c, "Relying on default #size-cells value for %s", + if (node->parent->size_cells == -1) + 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) { diff --git a/convert-dtsv0-lexer.l b/convert-dtsv0-lexer.l index 548e7199a0c4..aa32dc8cf81b 100644 --- a/convert-dtsv0-lexer.l +++ b/convert-dtsv0-lexer.l @@ -19,7 +19,6 @@ %option noyywrap nounput noinput never-interactive -%x INCLUDE %x BYTESTRING %x PROPNODENAME @@ -224,6 +223,8 @@ static void convert_file(const char *fname) while(yylex()) ; + + free(newname); } int main(int argc, char *argv[]) @@ -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) { diff --git a/dtc-lexer.l b/dtc-lexer.l index 0821bde31249..52bed7b74940 100644 --- a/dtc-lexer.l +++ b/dtc-lexer.l @@ -20,7 +20,6 @@ %option noyywrap nounput noinput never-interactive -%x INCLUDE %x BYTESTRING %x PROPNODENAME %s V1 @@ -63,7 +62,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 + %} %% @@ -74,24 +79,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>> { @@ -114,6 +127,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(); @@ -154,7 +172,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", @@ -174,16 +195,16 @@ static void lexical_error(const char *fmt, ...); if (d.len == 1) { lexical_error("Empty character literal"); yylval.integer = 0; - return DT_CHAR_LITERAL; - } - - yylval.integer = (unsigned char)d.val[0]; + } else { + yylval.integer = (unsigned char)d.val[0]; - if (d.len > 2) - lexical_error("Character literal has %d" - " characters instead of 1", - d.len - 1); + if (d.len > 2) + lexical_error("Character literal has %d" + " characters instead of 1", + d.len - 1); + } + data_free(d); return DT_CHAR_LITERAL; } @@ -193,7 +214,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); diff --git a/dtc-parser.y b/dtc-parser.y index bed857e72793..ca3f5003427c 100644 --- a/dtc-parser.y +++ b/dtc-parser.y @@ -17,27 +17,28 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ - %{ #include <stdio.h> +#include <inttypes.h> #include "dtc.h" #include "srcpos.h" -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 +53,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 +66,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 +74,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 +106,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(&@2, "Header flags don't match earlier ones"); + $$ = $1; } ; @@ -141,6 +166,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(&@3, "Label or path %s not found", $3); + $$ = $1; + } | devicetree DT_REF nodedef { struct node *target = get_node_by_ref($1, $2); @@ -148,17 +185,18 @@ devicetree: if (target) merge_nodes(target, $3); else - print_error("label or path, '%s', not found", $2); + ERROR(&@2, "Label or path %s not found", $2); $$ = $1; } | devicetree DT_DEL_NODE DT_REF ';' { struct node *target = get_node_by_ref($1, $3); - if (!target) - print_error("label or path, '%s', not found", $3); - else + if (target) delete_node(target); + else + ERROR(&@3, "Label or path %s not found", $3); + $$ = $1; } @@ -274,10 +312,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(&@2, "Array elements must be" + " 8, 16, 32 or 64-bits"); bits = 32; } @@ -302,9 +339,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(&@2, "Value out of range for" + " %d-bit array element", $1.bits); } $$.data = data_append_integer($1.data, $2, $1.bits); @@ -318,7 +354,7 @@ arrayprefix: REF_PHANDLE, $2); else - print_error("References are only allowed in " + ERROR(&@2, "References are only allowed in " "arrays with 32-bit elements."); $$.data = data_append_integer($1.data, val, $1.bits); @@ -400,8 +436,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(&@$, "Division by zero"); + $$ = 0; + } + } + | integer_mul '%' integer_unary + { + if ($3 != 0) { + $$ = $1 % $3; + } else { + ERROR(&@$, "Division by zero"); + $$ = 0; + } + } | integer_unary ; @@ -438,7 +490,7 @@ subnodes: } | subnode propdef { - print_error("syntax error: properties must precede subnodes"); + ERROR(&@2, "Properties must precede subnodes"); YYERROR; } ; @@ -461,17 +513,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(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); - fill_fullpaths(bi->dt, ""); - process_checks(force, bi); + 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 */ diff --git a/fdtdump.c b/fdtdump.c index 723770d6d337..194e9d62c6c5 100644 --- a/fdtdump.c +++ b/fdtdump.c @@ -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; diff --git a/flattree.c b/flattree.c index bd99fa2d33b8..ebac548b3fa8 100644 --- a/flattree.c +++ b/flattree.c @@ -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)); } - diff --git a/ftdump.c b/ftdump.c deleted file mode 100644 index bce653573edc..000000000000 --- a/ftdump.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * ftdump.c - Contributed by Pantelis Antoniou <pantelis.antoniou AT gmail.com> - */ - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#include <fdt.h> -#include <libfdt_env.h> - -#define FTDUMP_BUF_SIZE 65536 - -#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))) - -static int is_printable_string(const void *data, int len) -{ - const char *s = data; - const char *ss; - - /* zero length is not */ - if (len == 0) - return 0; - - /* must terminate with zero */ - if (s[len - 1] != '\0') - return 0; - - ss = s; - while (*s && isprint(*s)) - s++; - - /* not zero, or not done yet */ - if (*s != '\0' || (s + 1 - ss) < len) - return 0; - - return 1; -} - -static void print_data(const char *data, int len) -{ - int i; - const char *p = data; - - /* no data, don't print */ - if (len == 0) - return; - - if (is_printable_string(data, len)) { - printf(" = \"%s\"", (const char *)data); - } else if ((len % 4) == 0) { - printf(" = <"); - for (i = 0; i < len; i += 4) - printf("0x%08x%s", fdt32_to_cpu(GET_CELL(p)), - i < (len - 4) ? " " : ""); - printf(">"); - } else { - printf(" = ["); - for (i = 0; i < len; i++) - printf("%02x%s", *p++, i < len - 1 ? " " : ""); - printf("]"); - } -} - -static void dump_blob(void *blob) -{ - struct fdt_header *bph = blob; - uint32_t off_mem_rsvmap = fdt32_to_cpu(bph->off_mem_rsvmap); - uint32_t off_dt = fdt32_to_cpu(bph->off_dt_struct); - uint32_t off_str = fdt32_to_cpu(bph->off_dt_strings); - struct fdt_reserve_entry *p_rsvmap = - (struct fdt_reserve_entry *)((char *)blob + off_mem_rsvmap); - const char *p_struct = (const char *)blob + off_dt; - const char *p_strings = (const char *)blob + off_str; - uint32_t version = fdt32_to_cpu(bph->version); - uint32_t totalsize = fdt32_to_cpu(bph->totalsize); - uint32_t tag; - const char *p, *s, *t; - int depth, sz, shift; - int i; - uint64_t addr, size; - - depth = 0; - shift = 4; - - printf("/dts-v1/;\n"); - printf("// magic:\t\t0x%x\n", fdt32_to_cpu(bph->magic)); - printf("// totalsize:\t\t0x%x (%d)\n", totalsize, totalsize); - printf("// off_dt_struct:\t0x%x\n", off_dt); - printf("// off_dt_strings:\t0x%x\n", off_str); - printf("// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap); - printf("// version:\t\t%d\n", version); - printf("// last_comp_version:\t%d\n", - fdt32_to_cpu(bph->last_comp_version)); - if (version >= 2) - printf("// boot_cpuid_phys:\t0x%x\n", - fdt32_to_cpu(bph->boot_cpuid_phys)); - - if (version >= 3) - printf("// size_dt_strings:\t0x%x\n", - fdt32_to_cpu(bph->size_dt_strings)); - if (version >= 17) - printf("// size_dt_struct:\t0x%x\n", - fdt32_to_cpu(bph->size_dt_struct)); - printf("\n"); - - for (i = 0; ; i++) { - addr = fdt64_to_cpu(p_rsvmap[i].address); - size = fdt64_to_cpu(p_rsvmap[i].size); - if (addr == 0 && size == 0) - break; - - printf("/memreserve/ %llx %llx;\n", - (unsigned long long)addr, (unsigned long long)size); - } - - p = p_struct; - while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) { - - /* printf("tag: 0x%08x (%d)\n", tag, p - p_struct); */ - - if (tag == FDT_BEGIN_NODE) { - s = p; - p = PALIGN(p + strlen(s) + 1, 4); - - if (*s == '\0') - s = "/"; - - printf("%*s%s {\n", depth * shift, "", s); - - depth++; - continue; - } - - if (tag == FDT_END_NODE) { - depth--; - - printf("%*s};\n", depth * shift, ""); - continue; - } - - if (tag == FDT_NOP) { - printf("%*s// [NOP]\n", depth * shift, ""); - continue; - } - - if (tag != FDT_PROP) { - fprintf(stderr, "%*s ** Unknown tag 0x%08x\n", depth * shift, "", tag); - break; - } - sz = fdt32_to_cpu(GET_CELL(p)); - s = p_strings + fdt32_to_cpu(GET_CELL(p)); - if (version < 16 && sz >= 8) - p = PALIGN(p, 8); - t = p; - - p = PALIGN(p + sz, 4); - - printf("%*s%s", depth * shift, "", s); - print_data(t, sz); - printf(";\n"); - } -} - - -int main(int argc, char *argv[]) -{ - FILE *fp; - char *buf; - int size; - - if (argc < 2) { - fprintf(stderr, "supply input filename\n"); - return 5; - } - - if (strcmp(argv[1], "-") == 0) { - fp = stdin; - } else { - fp = fopen(argv[1], "rb"); - if (fp == NULL) { - fprintf(stderr, "unable to open %s\n", argv[1]); - return 10; - } - } - - buf = malloc(FTDUMP_BUF_SIZE); - if (!buf) { - fprintf(stderr, "Couldn't allocate %d byte buffer\n", FTDUMP_BUF_SIZE); - return 10; - } - - size = fread(buf, 1, FTDUMP_BUF_SIZE, fp); - if (size == FTDUMP_BUF_SIZE) { - fprintf(stderr, "file too large (maximum is %d bytes)\n", FTDUMP_BUF_SIZE); - return 10; - } - - dump_blob(buf); - - fclose(fp); - - return 0; -} diff --git a/libfdt/Makefile.libfdt b/libfdt/Makefile.libfdt index 91126c000a1e..098b3f36e668 100644 --- a/libfdt/Makefile.libfdt +++ b/libfdt/Makefile.libfdt @@ -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) diff --git a/libfdt/fdt.c b/libfdt/fdt.c index 2ce6a44179de..22286a1aaeaf 100644 --- a/libfdt/fdt.c +++ b/libfdt/fdt.c @@ -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) diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c new file mode 100644 index 000000000000..eff4dbcc729d --- /dev/null +++ b/libfdt/fdt_addresses.c @@ -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; +} diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c new file mode 100644 index 000000000000..56cb70ed4445 --- /dev/null +++ b/libfdt/fdt_overlay.c @@ -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; +} diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c index 50007f61ce66..3d00d2eee0e3 100644 --- a/libfdt/fdt_ro.c +++ b/libfdt/fdt_ro.c @@ -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, diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index fdba618f12ae..3fd5847377c9 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -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; } diff --git a/libfdt/fdt_strerror.c b/libfdt/fdt_strerror.c index e6c3ceee8c58..9677a1887e57 100644 --- a/libfdt/fdt_strerror.c +++ b/libfdt/fdt_strerror.c @@ -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])) diff --git a/libfdt/fdt_wip.c b/libfdt/fdt_wip.c index c5bbb68d3273..6aaab399929c 100644 --- a/libfdt/fdt_wip.c +++ b/libfdt/fdt_wip.c @@ -55,21 +55,42 @@ #include "libfdt_internal.h" +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, + &proplen); + if (!propval) + return proplen; + + if (proplen < (len + idx)) + return -FDT_ERR_NOSPACE; + + memcpy((char *)propval + idx, val, len); + return 0; +} + int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len) { - void *propval; + const void *propval; int proplen; - propval = fdt_getprop_w(fdt, nodeoffset, name, &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) diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index c4d5a9134bfc..ac42e0459524 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -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); @@ -249,6 +298,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, @@ -318,6 +383,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 * @path: full path of the node to locate @@ -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 @@ -406,6 +485,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 * @offset: offset of the property to retrieve @@ -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,11 +967,151 @@ 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 * @nodeoffset: offset of the node whose property to change @@ -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 */ /**********************************************************************/ diff --git a/libfdt/libfdt_env.h b/libfdt/libfdt_env.h index 9dea97dfff81..99f936dacc60 100644 --- a/libfdt/libfdt_env.h +++ b/libfdt/libfdt_env.h @@ -54,6 +54,7 @@ #include <stddef.h> #include <stdint.h> +#include <stdlib.h> #include <string.h> #ifdef __CHECKER__ diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h index 381133ba81df..02cfa6fb612d 100644 --- a/libfdt/libfdt_internal.h +++ b/libfdt/libfdt_internal.h @@ -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); diff --git a/libfdt/version.lds b/libfdt/version.lds index 80b322bed6b9..cff0358f2314 100644 --- a/libfdt/version.lds +++ b/libfdt/version.lds @@ -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: *; diff --git a/livetree.c b/livetree.c index b61465fb2f33..36be9afefd53 100644 --- a/livetree.c +++ b/livetree.c @@ -204,7 +204,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node) } } - /* if no collision occured, add child to the old node. */ + /* if no collision occurred, add child to the old node. */ if (new_child) 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(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) { - sort_reserve_entries(bi); - sort_node(bi->dt); + 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); } diff --git a/scripts/kup-dtc b/scripts/kup-dtc new file mode 100755 index 000000000000..e18abbb8c966 --- /dev/null +++ b/scripts/kup-dtc @@ -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) diff --git a/tests/Makefile.tests b/tests/Makefile.tests index dafb61848744..3d7a4f82f067 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -8,6 +8,8 @@ LIB_TESTS_L = get_mem_rsv \ char_literal \ sized_cells \ notfound \ + addr_size_cells \ + stringlist \ setprop_inplace nop_property nop_node \ sw_tree1 \ move_and_save mangle-layout nopulate \ @@ -21,7 +23,10 @@ LIB_TESTS_L = get_mem_rsv \ add_subnode_with_nops path_offset_aliases \ utilfdt_test \ integer-expressions \ - subnode_iterate + property_iterate \ + subnode_iterate \ + overlay overlay_bad_fixup \ + check_path LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) LIBTREE_TESTS_L = truncated_property diff --git a/tests/addr_size_cells.c b/tests/addr_size_cells.c new file mode 100644 index 000000000000..6090d93b3345 --- /dev/null +++ b/tests/addr_size_cells.c @@ -0,0 +1,64 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for #address-cells and #size-cells handling + * Copyright (C) 2014 David Gibson, <david@gibson.dropbear.id.au> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +static void check_node(const void *fdt, const char *path, int ac, int sc) +{ + int offset; + int xac, xsc; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) + FAIL("Couldn't find path %s", path); + + xac = fdt_address_cells(fdt, offset); + xsc = fdt_size_cells(fdt, offset); + + if (xac != ac) + FAIL("Address cells for %s is %d instead of %d\n", + path, xac, ac); + if (xsc != sc) + FAIL("Size cells for %s is %d instead of %d\n", + path, xsc, sc); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + + if (argc != 2) + CONFIG("Usage: %s <dtb file>\n", argv[0]); + + test_init(argc, argv); + fdt = load_blob(argv[1]); + + check_node(fdt, "/", 2, 2); + check_node(fdt, "/identity-bus@0", 2, 2); + check_node(fdt, "/simple-bus@1000000", 2, 1); + PASS(); +} diff --git a/tests/addresses.dts b/tests/addresses.dts new file mode 100644 index 000000000000..a2faaf59fd7a --- /dev/null +++ b/tests/addresses.dts @@ -0,0 +1,15 @@ +/dts-v1/; + +/ { + compatible = "test_addresses"; + #address-cells = <2>; + #size-cells = <2>; + + identity-bus@0 { + }; + + simple-bus@1000000 { + #address-cells = <2>; + #size-cells = <1>; + }; +}; diff --git a/tests/bad-octal-literal.dts b/tests/bad-octal-literal.dts new file mode 100644 index 000000000000..26558a2740f4 --- /dev/null +++ b/tests/bad-octal-literal.dts @@ -0,0 +1,5 @@ +/dts-v1/; + +/ { + x = <09>; +}; diff --git a/tests/bad-size-cells.dts b/tests/bad-size-cells.dts new file mode 100644 index 000000000000..515c0cc7ba45 --- /dev/null +++ b/tests/bad-size-cells.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + mangled { + #address-cells = <0x0>; + #size-cells = <0x0>; + + valid { + reg = <0x0 0x4000000>; + }; + }; +}; diff --git a/tests/check_path.c b/tests/check_path.c new file mode 100644 index 000000000000..f12f9503b22d --- /dev/null +++ b/tests/check_path.c @@ -0,0 +1,83 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for node existence + * Copyright (C) 2016 Konsulko Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> + +#include <libfdt.h> + +#include "tests.h" + +#define CHECK(code) \ + { \ + int err = (code); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +/* 4k ought to be enough for anybody */ +#define FDT_COPY_SIZE (4 * 1024) + +static void *open_dt(char *path) +{ + void *dt, *copy; + + dt = load_blob(path); + copy = xmalloc(FDT_COPY_SIZE); + + /* + * Resize our DTs to 4k so that we have room to operate on + */ + CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE)); + + return copy; +} + +int main(int argc, char *argv[]) +{ + void *fdt_base; + int fail_config, exists, check_exists; + + test_init(argc, argv); + fail_config = 0; + + if (argc != 4) + fail_config = 1; + + if (!fail_config) { + if (!strcmp(argv[2], "exists")) + check_exists = 1; + else if (!strcmp(argv[2], "not-exists")) + check_exists = 0; + else + fail_config = 1; + } + + if (fail_config) + CONFIG("Usage: %s <base dtb> <[exists|not-exists]> <node-path>", argv[0]); + + fdt_base = open_dt(argv[1]); + + exists = fdt_path_offset(fdt_base, argv[3]) >= 0; + + if (exists == check_exists) + PASS(); + else + FAIL(); +} diff --git a/tests/division-by-zero.dts b/tests/division-by-zero.dts new file mode 100644 index 000000000000..2984b29ff62b --- /dev/null +++ b/tests/division-by-zero.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + prop-div = < (1/0) >; + prop-mod = < (1%0) >; +}; diff --git a/tests/dumptrees.c b/tests/dumptrees.c index bebf553c9bfe..a49dbfab1567 100644 --- a/tests/dumptrees.c +++ b/tests/dumptrees.c @@ -36,6 +36,7 @@ struct { #define TREE(name) { &_##name, #name ".dtb" } TREE(test_tree1), TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char), + TREE(ovf_size_strings), }; #define NUM_TREES (sizeof(trees) / sizeof(trees[0])) diff --git a/tests/embedded_nul.dts b/tests/embedded_nul.dts Binary files differnew file mode 100644 index 000000000000..7b4993cc5452 --- /dev/null +++ b/tests/embedded_nul.dts diff --git a/tests/embedded_nul_equiv.dts b/tests/embedded_nul_equiv.dts new file mode 100644 index 000000000000..e978204f063d --- /dev/null +++ b/tests/embedded_nul_equiv.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + reserved-names = "aaaaaaaaaaaaaaaaaa\0bbbbbb\0ccccccccccccc"; + reserved-ranges = < 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 >; +}; diff --git a/tests/fdtdump-runtest.sh b/tests/fdtdump-runtest.sh new file mode 100755 index 000000000000..77593cf6cd1e --- /dev/null +++ b/tests/fdtdump-runtest.sh @@ -0,0 +1,30 @@ +#! /bin/sh + +# Arguments: +# $1 - source file to compile and compare with fdtdump output of the +# compiled file. + +. ./tests.sh + +dts="$1" +dtb="${dts}.dtb" +out="${dts}.out" +LOG=tmp.log.$$ + +files="$dtb $out $LOG" + +rm -f $files +trap "rm -f $files" 0 + +verbose_run_log_check "$LOG" $VALGRIND $DTC -O dtb $dts -o $dtb +$FDTDUMP ${dtb} | grep -v "//" >${out} + +if diff -w $dts $out >/dev/null; then + PASS +else + if [ -z "$QUIET_TEST" ]; then + echo "DIFF :-:" + diff -u -w $dts $out + fi + FAIL "Results differ from expected" +fi diff --git a/tests/fdtdump.dts b/tests/fdtdump.dts new file mode 100644 index 000000000000..b83b7dffab97 --- /dev/null +++ b/tests/fdtdump.dts @@ -0,0 +1,38 @@ +/dts-v1/; + +/memreserve/ 0 0xe; +/ { + model = "MyBoardName"; + compatible = "MyBoardName", "MyBoardFamilyName"; + #address-cells = <0x00000002>; + #size-cells = <0x00000002>; + cpus { + linux,phandle = <0x00000001>; + #address-cells = <0x00000001>; + #size-cells = <0x00000000>; + PowerPC,970@0 { + device_type = "cpu"; + reg = <0x00000000>; + linux,boot-cpu; + }; + PowerPC,970@1 { + device_type = "cpu"; + reg = <0x00000001>; + }; + }; + randomnode { + string = "foo", "stuff"; + bytes = [61 62 63 64 65]; + nbytes = [80 ff]; + child { + }; + }; + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x00000123 0x00000456 0x87654321>; + }; + chosen { + bootargs = "root=/dev/sda2"; + linux,platform = <0x00000600>; + }; +}; diff --git a/tests/get_phandle.c b/tests/get_phandle.c index 2079591d4c49..22bd7b81b3f0 100644 --- a/tests/get_phandle.c +++ b/tests/get_phandle.c @@ -44,6 +44,7 @@ static void check_phandle(void *fdt, const char *path, uint32_t checkhandle) int main(int argc, char *argv[]) { + uint32_t max; void *fdt; test_init(argc, argv); @@ -53,5 +54,10 @@ int main(int argc, char *argv[]) check_phandle(fdt, "/subnode@2", PHANDLE_1); check_phandle(fdt, "/subnode@2/subsubnode@0", PHANDLE_2); + max = fdt_get_max_phandle(fdt); + if (max != PHANDLE_2) + FAIL("fdt_get_max_phandle returned 0x%x instead of 0x%x\n", + max, PHANDLE_2); + PASS(); } diff --git a/tests/line_directives.dts b/tests/line_directives.dts index 046ef3715ad6..67b5e084f15c 100644 --- a/tests/line_directives.dts +++ b/tests/line_directives.dts @@ -18,4 +18,9 @@ # 10 "qux.dts" 0x12345678 >; +/* + * Check processing of escapes in filenames + */ +# 100 "\".dts" +# 200 "\\.dts" }; diff --git a/tests/multilabel.dts b/tests/multilabel.dts index 31116ceab2d6..77da06cc4174 100644 --- a/tests/multilabel.dts +++ b/tests/multilabel.dts @@ -5,6 +5,8 @@ m1: mq: /memreserve/ 0 0x1000; / { p0: pw: prop = "foo"; + rref = <&{/}>; + /* Explicit phandles */ n1: nx: node1 { linux,phandle = <0x2000>; diff --git a/tests/multilabel_merge.dts b/tests/multilabel_merge.dts index 1632300e6aca..3e8029897405 100644 --- a/tests/multilabel_merge.dts +++ b/tests/multilabel_merge.dts @@ -64,3 +64,7 @@ m1: mq: /memreserve/ 0 0x1000; }; }; + +/ { + rref = <&{/}>; +}; diff --git a/tests/nul-in-escape.dts b/tests/nul-in-escape.dts Binary files differnew file mode 100644 index 000000000000..9bed351cf021 --- /dev/null +++ b/tests/nul-in-escape.dts diff --git a/tests/nul-in-line-info1.dts b/tests/nul-in-line-info1.dts Binary files differnew file mode 100644 index 000000000000..ceb7261b7ef6 --- /dev/null +++ b/tests/nul-in-line-info1.dts diff --git a/tests/nul-in-line-info2.dts b/tests/nul-in-line-info2.dts new file mode 100644 index 000000000000..1157d2324f37 --- /dev/null +++ b/tests/nul-in-line-info2.dts @@ -0,0 +1 @@ +# 0 "\0" diff --git a/tests/overlay.c b/tests/overlay.c new file mode 100644 index 000000000000..3093eec67a91 --- /dev/null +++ b/tests/overlay.c @@ -0,0 +1,233 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for DT overlays() + * Copyright (C) 2016 Free Electrons + * Copyright (C) 2016 NextThing Co. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> + +#include <libfdt.h> + +#include "tests.h" + +#define CHECK(code) \ + { \ + int err = (code); \ + if (err) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +/* 4k ought to be enough for anybody */ +#define FDT_COPY_SIZE (4 * 1024) + +static int fdt_getprop_u32_by_poffset(void *fdt, const char *path, + const char *name, int poffset, + unsigned long *out) +{ + const fdt32_t *val; + int node_off; + int len; + + node_off = fdt_path_offset(fdt, path); + if (node_off < 0) + return node_off; + + val = fdt_getprop(fdt, node_off, name, &len); + if (!val || (len < (sizeof(uint32_t) * (poffset + 1)))) + return -FDT_ERR_NOTFOUND; + + *out = fdt32_to_cpu(*(val + poffset)); + + return 0; +} + +static int check_getprop_string_by_name(void *fdt, const char *path, + const char *name, const char *val) +{ + int node_off; + + node_off = fdt_path_offset(fdt, path); + if (node_off < 0) + return node_off; + + check_getprop_string(fdt, node_off, name, val); + + return 0; +} + +static int check_getprop_u32_by_name(void *fdt, const char *path, + const char *name, uint32_t val) +{ + int node_off; + + node_off = fdt_path_offset(fdt, path); + CHECK(node_off < 0); + + check_getprop_cell(fdt, node_off, name, val); + + return 0; +} + +static int check_getprop_null_by_name(void *fdt, const char *path, + const char *name) +{ + int node_off; + + node_off = fdt_path_offset(fdt, path); + CHECK(node_off < 0); + + check_property(fdt, node_off, name, 0, NULL); + + return 0; +} + +static int fdt_overlay_change_int_property(void *fdt) +{ + return check_getprop_u32_by_name(fdt, "/test-node", "test-int-property", + 43); +} + +static int fdt_overlay_change_str_property(void *fdt) +{ + return check_getprop_string_by_name(fdt, "/test-node", + "test-str-property", "foobar"); +} + +static int fdt_overlay_add_str_property(void *fdt) +{ + return check_getprop_string_by_name(fdt, "/test-node", + "test-str-property-2", "foobar2"); +} + +static int fdt_overlay_add_node(void *fdt) +{ + return check_getprop_null_by_name(fdt, "/test-node/new-node", + "new-property"); +} + +static int fdt_overlay_add_subnode_property(void *fdt) +{ + check_getprop_null_by_name(fdt, "/test-node/sub-test-node", + "sub-test-property"); + check_getprop_null_by_name(fdt, "/test-node/sub-test-node", + "new-sub-test-property"); + + return 0; +} + +static int fdt_overlay_local_phandle(void *fdt) +{ + uint32_t local_phandle; + unsigned long val = 0; + int off; + + off = fdt_path_offset(fdt, "/test-node/new-local-node"); + CHECK(off < 0); + + local_phandle = fdt_get_phandle(fdt, off); + CHECK(!local_phandle); + + CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node", + "test-several-phandle", + 0, &val)); + CHECK(val != local_phandle); + + CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node", + "test-several-phandle", + 1, &val)); + CHECK(val != local_phandle); + + return 0; +} + +static int fdt_overlay_local_phandles(void *fdt) +{ + uint32_t local_phandle, test_phandle; + unsigned long val = 0; + int off; + + off = fdt_path_offset(fdt, "/test-node/new-local-node"); + CHECK(off < 0); + + local_phandle = fdt_get_phandle(fdt, off); + CHECK(!local_phandle); + + off = fdt_path_offset(fdt, "/test-node"); + CHECK(off < 0); + + test_phandle = fdt_get_phandle(fdt, off); + CHECK(!test_phandle); + + CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node", + "test-phandle", 0, &val)); + CHECK(test_phandle != val); + + CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node", + "test-phandle", 1, &val)); + CHECK(local_phandle != val); + + return 0; +} + +static void *open_dt(char *path) +{ + void *dt, *copy; + + dt = load_blob(path); + copy = xmalloc(FDT_COPY_SIZE); + + /* + * Resize our DTs to 4k so that we have room to operate on + */ + CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE)); + + return copy; +} + +int main(int argc, char *argv[]) +{ + void *fdt_base, *fdt_overlay; + + test_init(argc, argv); + if (argc != 3) + CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]); + + fdt_base = open_dt(argv[1]); + fdt_overlay = open_dt(argv[2]); + + /* Apply the overlay */ + CHECK(fdt_overlay_apply(fdt_base, fdt_overlay)); + + fdt_overlay_change_int_property(fdt_base); + fdt_overlay_change_str_property(fdt_base); + fdt_overlay_add_str_property(fdt_base); + fdt_overlay_add_node(fdt_base); + fdt_overlay_add_subnode_property(fdt_base); + + /* + * If the base tree has a __symbols__ node, do the tests that + * are only successful with a proper phandle support, and thus + * dtc -@ + */ + if (fdt_path_offset(fdt_base, "/__symbols__") >= 0) { + fdt_overlay_local_phandle(fdt_base); + fdt_overlay_local_phandles(fdt_base); + } + + PASS(); +} diff --git a/tests/overlay_bad_fixup.c b/tests/overlay_bad_fixup.c new file mode 100644 index 000000000000..5014f5ec0868 --- /dev/null +++ b/tests/overlay_bad_fixup.c @@ -0,0 +1,70 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for DT overlays() + * Copyright (C) 2016 Free Electrons + * Copyright (C) 2016 NextThing Co. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> + +#include <libfdt.h> + +#include "tests.h" + +#define CHECK(code, expected) \ + { \ + err = (code); \ + if (err != expected) \ + FAIL(#code ": %s", fdt_strerror(err)); \ + } + +/* 4k ought to be enough for anybody */ +#define FDT_COPY_SIZE (4 * 1024) + +static void *open_dt(char *path) +{ + void *dt, *copy; + int err; + + dt = load_blob(path); + copy = xmalloc(FDT_COPY_SIZE); + + /* + * Resize our DTs to 4k so that we have room to operate on + */ + CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE), 0); + + return copy; +} + +int main(int argc, char *argv[]) +{ + void *fdt_base, *fdt_overlay; + int err; + + test_init(argc, argv); + if (argc != 3) + CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]); + + fdt_base = open_dt(argv[1]); + fdt_overlay = open_dt(argv[2]); + + /* Apply the overlay */ + CHECK(fdt_overlay_apply(fdt_base, fdt_overlay), -FDT_ERR_BADOVERLAY); + + PASS(); +} diff --git a/tests/overlay_bad_fixup_bad_index.dts b/tests/overlay_bad_fixup_bad_index.dts new file mode 100644 index 000000000000..b5cf13137169 --- /dev/null +++ b/tests/overlay_bad_fixup_bad_index.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0:target:ab"; + }; +}; diff --git a/tests/overlay_bad_fixup_base.dtsi b/tests/overlay_bad_fixup_base.dtsi new file mode 100644 index 000000000000..216bcab52263 --- /dev/null +++ b/tests/overlay_bad_fixup_base.dtsi @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +/ { + fragment@0 { + target = <0xffffffff>; + + __overlay__ { + test-property; + }; + }; +}; diff --git a/tests/overlay_bad_fixup_empty.dts b/tests/overlay_bad_fixup_empty.dts new file mode 100644 index 000000000000..e111db4c8527 --- /dev/null +++ b/tests/overlay_bad_fixup_empty.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = ""; + }; +}; diff --git a/tests/overlay_bad_fixup_empty_index.dts b/tests/overlay_bad_fixup_empty_index.dts new file mode 100644 index 000000000000..9e12e2177ad5 --- /dev/null +++ b/tests/overlay_bad_fixup_empty_index.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0:target:"; + }; +}; diff --git a/tests/overlay_bad_fixup_index_trailing.dts b/tests/overlay_bad_fixup_index_trailing.dts new file mode 100644 index 000000000000..f586bef4d374 --- /dev/null +++ b/tests/overlay_bad_fixup_index_trailing.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0:target:0a"; + }; +}; diff --git a/tests/overlay_bad_fixup_path_empty_prop.dts b/tests/overlay_bad_fixup_path_empty_prop.dts new file mode 100644 index 000000000000..608b5f9247b5 --- /dev/null +++ b/tests/overlay_bad_fixup_path_empty_prop.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0::"; + }; +}; diff --git a/tests/overlay_bad_fixup_path_only.dts b/tests/overlay_bad_fixup_path_only.dts new file mode 100644 index 000000000000..2485dd965ee5 --- /dev/null +++ b/tests/overlay_bad_fixup_path_only.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0"; + }; +}; diff --git a/tests/overlay_bad_fixup_path_only_sep.dts b/tests/overlay_bad_fixup_path_only_sep.dts new file mode 100644 index 000000000000..3cbf6c40fba5 --- /dev/null +++ b/tests/overlay_bad_fixup_path_only_sep.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0:"; + }; +}; diff --git a/tests/overlay_bad_fixup_path_prop.dts b/tests/overlay_bad_fixup_path_prop.dts new file mode 100644 index 000000000000..ca79b52bcb22 --- /dev/null +++ b/tests/overlay_bad_fixup_path_prop.dts @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/include/ "overlay_bad_fixup_base.dtsi" + +/ { + __fixups__ { + test = "/fragment@0:target"; + }; +}; diff --git a/tests/overlay_base.dts b/tests/overlay_base.dts new file mode 100644 index 000000000000..2603adb6821e --- /dev/null +++ b/tests/overlay_base.dts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +/ { + test: test-node { + test-int-property = <42>; + test-str-property = "foo"; + + subtest: sub-test-node { + sub-test-property; + }; + }; +}; + + diff --git a/tests/overlay_base_manual_symbols.dts b/tests/overlay_base_manual_symbols.dts new file mode 100644 index 000000000000..7e4d17dcb06c --- /dev/null +++ b/tests/overlay_base_manual_symbols.dts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +/ { + test: test-node { + phandle = <&test>; /* Force phandle generation */ + test-int-property = <42>; + test-str-property = "foo"; + + subtest: sub-test-node { + sub-test-property; + }; + }; + __symbols__ { + test = &test; + }; +}; + + diff --git a/tests/overlay_overlay.dts b/tests/overlay_overlay.dts new file mode 100644 index 000000000000..b6d841b1f011 --- /dev/null +++ b/tests/overlay_overlay.dts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * Copyright (c) 2016 Konsulko Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; +/plugin/; + +/ { + /* Test that we can change an int by another */ + fragment@0 { + target = <&test>; + + __overlay__ { + test-int-property = <43>; + }; + }; + + /* Test that we can replace a string by a longer one */ + fragment@1 { + target = <&test>; + + __overlay__ { + test-str-property = "foobar"; + }; + }; + + /* Test that we add a new property */ + fragment@2 { + target = <&test>; + + __overlay__ { + test-str-property-2 = "foobar2"; + }; + }; + + /* Test that we add a new node (by phandle) */ + fragment@3 { + target = <&test>; + + __overlay__ { + new-node { + new-property; + }; + }; + }; + + fragment@5 { + target = <&test>; + + __overlay__ { + local: new-local-node { + new-property; + }; + }; + }; + + fragment@6 { + target = <&test>; + + __overlay__ { + test-phandle = <&test>, <&local>; + }; + }; + + fragment@7 { + target = <&test>; + + __overlay__ { + test-several-phandle = <&local>, <&local>; + }; + }; + + fragment@8 { + target = <&test>; + + __overlay__ { + sub-test-node { + new-sub-test-property; + }; + }; + }; +}; diff --git a/tests/overlay_overlay_manual_fixups.dts b/tests/overlay_overlay_manual_fixups.dts new file mode 100644 index 000000000000..e34c4fc63085 --- /dev/null +++ b/tests/overlay_overlay_manual_fixups.dts @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * Copyright (c) 2016 Konsulko Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +/* Note no /plugin/ tag - we're manually generating the metadata for + testing purposes */ + +/ { + /* Test that we can change an int by another */ + fragment@0 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + test-int-property = <43>; + }; + }; + + /* Test that we can replace a string by a longer one */ + fragment@1 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + test-str-property = "foobar"; + }; + }; + + /* Test that we add a new property */ + fragment@2 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + test-str-property-2 = "foobar2"; + }; + }; + + /* Test that we add a new node (by phandle) */ + fragment@3 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + new-node { + new-property; + }; + }; + }; + + fragment@5 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + local: new-local-node { + new-property; + }; + }; + }; + + fragment@6 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + test-phandle = <0xffffffff /*&test*/>, <&local>; + }; + }; + + fragment@7 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + test-several-phandle = <&local>, <&local>; + }; + }; + + fragment@8 { + target = <0xffffffff /*&test*/>; + + __overlay__ { + sub-test-node { + new-sub-test-property; + }; + }; + }; + + __local_fixups__ { + fragment@6 { + __overlay__ { + test-phandle = <4>; + }; + }; + fragment@7 { + __overlay__ { + test-several-phandle = <0 4>; + }; + }; + }; + __fixups__ { + test = "/fragment@0:target:0", + "/fragment@1:target:0", + "/fragment@2:target:0", + "/fragment@3:target:0", + "/fragment@5:target:0", + "/fragment@6:target:0", + "/fragment@6/__overlay__:test-phandle:0", + "/fragment@7:target:0", + "/fragment@8:target:0"; + }; +}; diff --git a/tests/overlay_overlay_no_fixups.dts b/tests/overlay_overlay_no_fixups.dts new file mode 100644 index 000000000000..e8d0f96d889c --- /dev/null +++ b/tests/overlay_overlay_no_fixups.dts @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +/ { + fragment@0 { + target-path = "/test-node"; + + __overlay__ { + test-int-property = <43>; + }; + }; + + /* Test that we can replace a string by a longer one */ + fragment@1 { + target-path = "/test-node"; + + __overlay__ { + test-str-property = "foobar"; + }; + }; + + /* Test that we add a new property */ + fragment@2 { + target-path = "/test-node"; + + __overlay__ { + test-str-property-2 = "foobar2"; + }; + }; + + fragment@3 { + target-path = "/test-node"; + + __overlay__ { + new-node { + new-property; + }; + }; + }; + + fragment@4 { + target-path = "/"; + + __overlay__ { + local: new-local-node { + new-property; + }; + }; + }; + + fragment@5 { + target-path = "/"; + + __overlay__ { + test-several-phandle = <&local>, <&local>; + }; + }; + + fragment@6 { + target-path = "/test-node"; + + __overlay__ { + sub-test-node { + new-sub-test-property; + }; + }; + }; + + __local_fixups__ { + fragment@5 { + __overlay__ { + test-several-phandle = <0 4>; + }; + }; + }; +}; diff --git a/tests/overlay_overlay_simple.dts b/tests/overlay_overlay_simple.dts new file mode 100644 index 000000000000..8657e1e7a15a --- /dev/null +++ b/tests/overlay_overlay_simple.dts @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2016 Konsulko Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; +/plugin/; + +&test { + test-int-property = <43>; +}; diff --git a/tests/path-references.c b/tests/path-references.c index 0746b3f7e324..c8d25fb85fdd 100644 --- a/tests/path-references.c +++ b/tests/path-references.c @@ -47,6 +47,20 @@ static void check_ref(const void *fdt, int node, const char *checkpath) node, p, checkpath); } +static void check_rref(const void *fdt) +{ + const char *p; + int len; + + /* Check reference to root node */ + p = fdt_getprop(fdt, 0, "rref", &len); + if (!p) + FAIL("fdt_getprop(0, \"rref\"): %s", fdt_strerror(len)); + if (!streq(p, "/")) + FAIL("'rref' in root node has value \"%s\" instead of \"/\"", + p); +} + int main(int argc, char *argv[]) { void *fdt; @@ -78,5 +92,7 @@ int main(int argc, char *argv[]) if ((!streq(p, "/node1") || !streq(p + strlen("/node1") + 1, "/node2"))) FAIL("multiref has wrong value"); + check_rref(fdt); + PASS(); } diff --git a/tests/path-references.dts b/tests/path-references.dts index 91e7ef745aae..b00fd79061fa 100644 --- a/tests/path-references.dts +++ b/tests/path-references.dts @@ -1,6 +1,7 @@ /dts-v1/; / { + rref = &{/}; /* Check multiple references case */ multiref = &n1 , &n2; n1: node1 { diff --git a/tests/path_offset.c b/tests/path_offset.c index 4e5b7a11f70d..bfebe9fff853 100644 --- a/tests/path_offset.c +++ b/tests/path_offset.c @@ -54,58 +54,84 @@ static int check_subnode(void *fdt, int parent, const char *name) return offset; } +static void check_path_offset(void *fdt, char *path, int offset) +{ + int rc; + + verbose_printf("Checking offset of \"%s\" is %d...\n", path, offset); + + rc = fdt_path_offset(fdt, path); + if (rc < 0) + FAIL("fdt_path_offset(\"%s\") failed: %s", + path, fdt_strerror(rc)); + if (rc != offset) + FAIL("fdt_path_offset(\"%s\") returned incorrect offset" + " %d instead of %d", path, rc, offset); +} + +static void check_path_offset_namelen(void *fdt, char *path, int namelen, + int offset) +{ + int rc; + + verbose_printf("Checking offset of \"%s\" [first %d characters]" + " is %d...\n", path, namelen, offset); + + rc = fdt_path_offset_namelen(fdt, path, namelen); + if (rc == offset) + return; + + if (rc < 0) + FAIL("fdt_path_offset_namelen(\"%s\", %d) failed: %s", + path, namelen, fdt_strerror(rc)); + else + FAIL("fdt_path_offset_namelen(\"%s\", %d) returned incorrect" + " offset %d instead of %d", path, namelen, rc, offset); +} + int main(int argc, char *argv[]) { void *fdt; - int root_offset; int subnode1_offset, subnode2_offset; - int subnode1_offset_p, subnode2_offset_p; int subsubnode1_offset, subsubnode2_offset, subsubnode2_offset2; - int subsubnode1_offset_p, subsubnode2_offset_p, subsubnode2_offset2_p; test_init(argc, argv); fdt = load_blob_arg(argc, argv); - root_offset = fdt_path_offset(fdt, "/"); - if (root_offset < 0) - FAIL("fdt_path_offset(\"/\") failed: %s", - fdt_strerror(root_offset)); - else if (root_offset != 0) - FAIL("fdt_path_offset(\"/\") returns incorrect offset %d", - root_offset); + check_path_offset(fdt, "/", 0); + subnode1_offset = check_subnode(fdt, 0, "subnode@1"); subnode2_offset = check_subnode(fdt, 0, "subnode@2"); - subnode1_offset_p = fdt_path_offset(fdt, "/subnode@1"); - subnode2_offset_p = fdt_path_offset(fdt, "/subnode@2"); - - if (subnode1_offset != subnode1_offset_p) - FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", - subnode1_offset, subnode1_offset_p); - - if (subnode2_offset != subnode2_offset_p) - FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", - subnode2_offset, subnode2_offset_p); + check_path_offset(fdt, "/subnode@1", subnode1_offset); + check_path_offset(fdt, "/subnode@2", subnode2_offset); subsubnode1_offset = check_subnode(fdt, subnode1_offset, "subsubnode"); subsubnode2_offset = check_subnode(fdt, subnode2_offset, "subsubnode@0"); subsubnode2_offset2 = check_subnode(fdt, subnode2_offset, "subsubnode"); - subsubnode1_offset_p = fdt_path_offset(fdt, "/subnode@1/subsubnode"); - subsubnode2_offset_p = fdt_path_offset(fdt, "/subnode@2/subsubnode@0"); - subsubnode2_offset2_p = fdt_path_offset(fdt, "/subnode@2/subsubnode"); - - if (subsubnode1_offset != subsubnode1_offset_p) - FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", - subsubnode1_offset, subsubnode1_offset_p); - - if (subsubnode2_offset != subsubnode2_offset_p) - FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", - subsubnode2_offset, subsubnode2_offset_p); - - if (subsubnode2_offset2 != subsubnode2_offset2_p) - FAIL("Mismatch between subnode_offset (%d) and path_offset (%d)", - subsubnode2_offset2, subsubnode2_offset2_p); + check_path_offset(fdt, "/subnode@1/subsubnode", subsubnode1_offset); + check_path_offset(fdt, "/subnode@2/subsubnode@0", subsubnode2_offset); + check_path_offset(fdt, "/subnode@2/subsubnode", subsubnode2_offset2); + + /* Test paths with extraneous separators */ + check_path_offset(fdt, "//", 0); + check_path_offset(fdt, "///", 0); + check_path_offset(fdt, "//subnode@1", subnode1_offset); + check_path_offset(fdt, "/subnode@1/", subnode1_offset); + check_path_offset(fdt, "//subnode@1///", subnode1_offset); + check_path_offset(fdt, "/subnode@2////subsubnode", subsubnode2_offset2); + + /* Test fdt_path_offset_namelen() */ + check_path_offset_namelen(fdt, "/subnode@1", 1, 0); + check_path_offset_namelen(fdt, "/subnode@1/subsubnode", 10, subnode1_offset); + check_path_offset_namelen(fdt, "/subnode@1/subsubnode", 11, subnode1_offset); + check_path_offset_namelen(fdt, "/subnode@2TRAILINGGARBAGE", 10, subnode2_offset); + check_path_offset_namelen(fdt, "/subnode@2TRAILINGGARBAGE", 11, -FDT_ERR_NOTFOUND); + check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 23, subsubnode2_offset2); + check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 22, -FDT_ERR_NOTFOUND); + check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 24, subsubnode2_offset2); + check_path_offset_namelen(fdt, "/subnode@2/subsubnode@0/more", 25, -FDT_ERR_NOTFOUND); PASS(); } diff --git a/tests/property_iterate.c b/tests/property_iterate.c new file mode 100644 index 000000000000..0f3959cb8c22 --- /dev/null +++ b/tests/property_iterate.c @@ -0,0 +1,97 @@ +/* + * libfdt - Flat Device Tree manipulation + * Tests that fdt_next_subnode() works as expected + * + * Copyright (C) 2013 Google, Inc + * + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +static void test_node(void *fdt, int parent_offset) +{ + fdt32_t properties; + const fdt32_t *prop; + int offset, property; + int count; + int len; + + /* + * This property indicates the number of properties in our + * test node to expect + */ + prop = fdt_getprop(fdt, parent_offset, "test-properties", &len); + if (!prop || len != sizeof(fdt32_t)) { + FAIL("Missing/invalid test-properties property at '%s'", + fdt_get_name(fdt, parent_offset, NULL)); + } + properties = cpu_to_fdt32(*prop); + + count = 0; + offset = fdt_first_subnode(fdt, parent_offset); + if (offset < 0) + FAIL("Missing test node\n"); + + fdt_for_each_property_offset(property, fdt, offset) + count++; + + if (count != properties) { + FAIL("Node '%s': Expected %d properties, got %d\n", + fdt_get_name(fdt, parent_offset, NULL), properties, + count); + } +} + +static void check_fdt_next_subnode(void *fdt) +{ + int offset; + int count = 0; + + fdt_for_each_subnode(offset, fdt, 0) { + test_node(fdt, offset); + count++; + } + + if (count != 2) + FAIL("Expected %d tests, got %d\n", 2, count); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + + test_init(argc, argv); + if (argc != 2) + CONFIG("Usage: %s <dtb file>", argv[0]); + + fdt = load_blob(argv[1]); + if (!fdt) + FAIL("No device tree available"); + + check_fdt_next_subnode(fdt); + + PASS(); +} diff --git a/tests/property_iterate.dts b/tests/property_iterate.dts new file mode 100644 index 000000000000..e8f5f8f4fedd --- /dev/null +++ b/tests/property_iterate.dts @@ -0,0 +1,23 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <0>; + + test1 { + test-properties = <3>; + + test { + linux,phandle = <0x1>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + test2 { + test-properties = <0>; + + test { + }; + }; +}; diff --git a/tests/references.c b/tests/references.c index c9d05a2f2962..46662fc0faa7 100644 --- a/tests/references.c +++ b/tests/references.c @@ -56,6 +56,23 @@ static void check_ref(const void *fdt, int node, uint32_t checkref) node, ref, checkref); } +static void check_rref(const void *fdt) +{ + const uint32_t *p; + uint32_t ref; + int len; + + p = fdt_getprop(fdt, 0, "rref", &len); + if (!p) + FAIL("fdt_getprop(0, \"rref\"): %s", fdt_strerror(len)); + if (len != sizeof(*p)) + FAIL("'rref' in root node has wrong size (%d instead of %zd)", + len, sizeof(*p)); + ref = fdt32_to_cpu(*p); + if (ref != fdt_get_phandle(fdt, 0)) + FAIL("'rref' in root node has value 0x%x instead of 0x0", ref); +} + int main(int argc, char *argv[]) { void *fdt; @@ -104,5 +121,7 @@ int main(int argc, char *argv[]) check_ref(fdt, n2, h1); check_ref(fdt, n3, h4); + check_rref(fdt); + PASS(); } diff --git a/tests/references.dts b/tests/references.dts index 640c9315911c..f783e8bc8643 100644 --- a/tests/references.dts +++ b/tests/references.dts @@ -1,6 +1,8 @@ /dts-v1/; / { + rref = <&{/}>; + /* Explicit phandles */ n1: node1 { linux,phandle = <0x2000>; diff --git a/tests/references_dts0.dts b/tests/references_dts0.dts deleted file mode 100644 index d34dbb2913fd..000000000000 --- a/tests/references_dts0.dts +++ /dev/null @@ -1,26 +0,0 @@ -/dts-v1/; - -/ { - /* Explicit phandles */ - n1: node1 { - linux,phandle = <0x2000>; - ref = <&{/node2}>; /* reference precedes target */ - lref = <&n2>; - }; - n2: node2 { - linux,phandle = <0x1>; - ref = <&{/node1}>; /* reference after target */ - lref = <&n1>; - }; - - /* Implicit phandles */ - n3: node3 { - ref = <&{/node4}>; - lref = <&n4>; - }; - n4: node4 { - }; - n5: node5 { - linux,phandle = <&n5>; - }; -}; diff --git a/tests/reg-without-unit-addr.dts b/tests/reg-without-unit-addr.dts new file mode 100644 index 000000000000..aaf8af7a8cd2 --- /dev/null +++ b/tests/reg-without-unit-addr.dts @@ -0,0 +1,10 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + node { + reg = <0 1>; + }; +}; diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 97e016b1aef9..ed489dbdd269 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -42,20 +42,20 @@ base_run_test() { shorten_echo () { limit=32 - echo -n "$1" + printf "$1" shift for x; do if [ ${#x} -le $limit ]; then - echo -n " $x" + printf " $x" else short=$(echo "$x" | head -c$limit) - echo -n " \"$short\"...<${#x} bytes>" + printf " \"$short\"...<${#x} bytes>" fi done } run_test () { - echo -n "$@: " + printf "$*: " if [ -n "$VALGRIND" -a -f $1.supp ]; then VGSUPP="--suppressions=$1.supp" fi @@ -63,7 +63,7 @@ run_test () { } run_sh_test () { - echo -n "$@: " + printf "$*: " base_run_test sh "$@" } @@ -106,12 +106,27 @@ wrap_error () { run_wrap_error_test () { shorten_echo "$@" - echo -n " {!= 0}: " + printf " {!= 0}: " base_run_test wrap_error "$@" } +# $1: dtb file +# $2: align base +check_align () { + shorten_echo "check_align $@: " + local size=$(stat -c %s "$1") + local align="$2" + ( + if [ $(($size % $align)) -eq 0 ] ;then + PASS + else + FAIL "Output size $size is not $align-byte aligned" + fi + ) +} + run_dtc_test () { - echo -n "dtc $@: " + printf "dtc $*: " base_run_test wrap_test $VALGRIND $DTC "$@" } @@ -126,7 +141,7 @@ asm_to_so_test () { run_fdtget_test () { expect="$1" shift - echo -n "fdtget-runtest.sh "$expect" $@: " + printf "fdtget-runtest.sh %s $*: " "$(echo $expect)" base_run_test sh fdtget-runtest.sh "$expect" "$@" } @@ -134,10 +149,90 @@ run_fdtput_test () { expect="$1" shift shorten_echo fdtput-runtest.sh "$expect" "$@" - echo -n ": " + printf ": " base_run_test sh fdtput-runtest.sh "$expect" "$@" } +run_fdtdump_test() { + file="$1" + shorten_echo fdtdump-runtest.sh "$file" + printf ": " + base_run_test sh fdtdump-runtest.sh "$file" +} + +BAD_FIXUP_TREES="bad_index \ + empty \ + empty_index \ + index_trailing \ + path_empty_prop \ + path_only \ + path_only_sep \ + path_prop" + +# Test to exercise libfdt overlay application without dtc's overlay support +libfdt_overlay_tests () { + # First test a doctored overlay which requires only local fixups + run_dtc_test -I dts -O dtb -o overlay_base_no_symbols.test.dtb overlay_base.dts + run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__symbols__" + run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__fixups__" + run_test check_path overlay_base_no_symbols.test.dtb not-exists "/__local_fixups__" + + run_dtc_test -I dts -O dtb -o overlay_overlay_no_fixups.test.dtb overlay_overlay_no_fixups.dts + run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__symbols__" + run_test check_path overlay_overlay_no_fixups.test.dtb not-exists "/__fixups__" + run_test check_path overlay_overlay_no_fixups.test.dtb exists "/__local_fixups__" + + run_test overlay overlay_base_no_symbols.test.dtb overlay_overlay_no_fixups.test.dtb + + # Then test with manually constructed fixups + run_dtc_test -I dts -O dtb -o overlay_base_manual_symbols.test.dtb overlay_base_manual_symbols.dts + run_test check_path overlay_base_manual_symbols.test.dtb exists "/__symbols__" + run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__fixups__" + run_test check_path overlay_base_manual_symbols.test.dtb not-exists "/__local_fixups__" + + run_dtc_test -I dts -O dtb -o overlay_overlay_manual_fixups.test.dtb overlay_overlay_manual_fixups.dts + run_test check_path overlay_overlay_manual_fixups.test.dtb not-exists "/__symbols__" + run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__fixups__" + run_test check_path overlay_overlay_manual_fixups.test.dtb exists "/__local_fixups__" + + run_test overlay overlay_base_manual_symbols.test.dtb overlay_overlay_manual_fixups.test.dtb + + # Bad fixup tests + for test in $BAD_FIXUP_TREES; do + tree="overlay_bad_fixup_$test" + run_dtc_test -I dts -O dtb -o $tree.test.dtb $tree.dts + run_test overlay_bad_fixup overlay_base_no_symbols.test.dtb $tree.test.dtb + done +} + +# Tests to exercise dtc's overlay generation support +dtc_overlay_tests () { + # Overlay tests for dtc + run_dtc_test -@ -I dts -O dtb -o overlay_base.test.dtb overlay_base.dts + run_test check_path overlay_base.test.dtb exists "/__symbols__" + run_test check_path overlay_base.test.dtb not-exists "/__fixups__" + run_test check_path overlay_base.test.dtb not-exists "/__local_fixups__" + + run_dtc_test -I dts -O dtb -o overlay_overlay.test.dtb overlay_overlay.dts + run_test check_path overlay_overlay.test.dtb not-exists "/__symbols__" + run_test check_path overlay_overlay.test.dtb exists "/__fixups__" + run_test check_path overlay_overlay.test.dtb exists "/__local_fixups__" + + run_test overlay overlay_base.test.dtb overlay_overlay.test.dtb + + # test plugin source to dtb and back + run_dtc_test -I dtb -O dts -o overlay_overlay_decompile.test.dts overlay_overlay.test.dtb + run_dtc_test -I dts -O dtb -o overlay_overlay_decompile.test.dtb overlay_overlay_decompile.test.dts + run_test dtbs_equal_ordered overlay_overlay.test.dtb overlay_overlay_decompile.test.dtb + + # Test generation of aliases insted of symbols + run_dtc_test -A -I dts -O dtb -o overlay_base_with_aliases.dtb overlay_base.dts + run_test check_path overlay_base_with_aliases.dtb exists "/aliases" + run_test check_path overlay_base_with_aliases.dtb not-exists "/__symbols__" + run_test check_path overlay_base_with_aliases.dtb not-exists "/__fixups__" + run_test check_path overlay_base_with_aliases.dtb not-exists "/__local_fixups__" +} + tree1_tests () { TREE=$1 @@ -188,6 +283,12 @@ ALL_LAYOUTS="mts mst tms tsm smt stm" libfdt_tests () { tree1_tests test_tree1.dtb + run_dtc_test -I dts -O dtb -o addresses.test.dtb addresses.dts + run_test addr_size_cells addresses.test.dtb + + run_dtc_test -I dts -O dtb -o stringlist.test.dtb stringlist.dts + run_test stringlist stringlist.test.dtb + # Sequential write tests run_test sw_tree1 tree1_tests sw_tree1.test.dtb @@ -245,6 +346,7 @@ libfdt_tests () { run_test appendprop2 appendprop1.test.dtb run_dtc_test -I dts -O dtb -o appendprop.test.dtb appendprop.dts run_test dtbs_equal_ordered appendprop2.test.dtb appendprop.test.dtb + libfdt_overlay_tests for basetree in test_tree1.dtb sw_tree1.test.dtb rw_tree1.test.dtb; do run_test nopulate $basetree @@ -256,11 +358,37 @@ libfdt_tests () { run_dtc_test -I dts -O dtb -o subnode_iterate.dtb subnode_iterate.dts run_test subnode_iterate subnode_iterate.dtb + run_dtc_test -I dts -O dtb -o property_iterate.dtb property_iterate.dts + run_test property_iterate property_iterate.dtb + # Tests for behaviour on various sorts of corrupted trees run_test truncated_property + # Check aliases support in fdt_path_offset + run_dtc_test -I dts -O dtb -o aliases.dtb aliases.dts + run_test get_alias aliases.dtb + run_test path_offset_aliases aliases.dtb + # Specific bug tests run_test add_subnode_with_nops + run_dtc_test -I dts -O dts -o sourceoutput.test.dts sourceoutput.dts + run_dtc_test -I dts -O dtb -o sourceoutput.test.dtb sourceoutput.dts + run_dtc_test -I dts -O dtb -o sourceoutput.test.dts.test.dtb sourceoutput.test.dts + run_test dtbs_equal_ordered sourceoutput.test.dtb sourceoutput.test.dts.test.dtb + + run_dtc_test -I dts -O dtb -o embedded_nul.test.dtb embedded_nul.dts + run_dtc_test -I dts -O dtb -o embedded_nul_equiv.test.dtb embedded_nul_equiv.dts + run_test dtbs_equal_ordered embedded_nul.test.dtb embedded_nul_equiv.test.dtb + + run_dtc_test -I dts -O dtb bad-size-cells.dts + + run_wrap_error_test $DTC division-by-zero.dts + run_wrap_error_test $DTC bad-octal-literal.dts + run_dtc_test -I dts -O dtb nul-in-escape.dts + run_wrap_error_test $DTC nul-in-line-info1.dts + run_wrap_error_test $DTC nul-in-line-info2.dts + + run_wrap_error_test $DTC -I dtb -O dts -o /dev/null ovf_size_strings.dtb } dtc_tests () { @@ -307,11 +435,6 @@ dtc_tests () { run_dtc_test -I dts -O dtb -o dtc_comments-cmp.test.dtb comments-cmp.dts run_test dtbs_equal_ordered dtc_comments.test.dtb dtc_comments-cmp.test.dtb - # Check aliases support in fdt_path_offset - run_dtc_test -I dts -O dtb -o aliases.dtb aliases.dts - run_test get_alias aliases.dtb - run_test path_offset_aliases aliases.dtb - # Check /include/ directive run_dtc_test -I dts -O dtb -o includes.test.dtb include0.dts run_test dtbs_equal_ordered includes.test.dtb test_tree1.dtb @@ -381,11 +504,14 @@ dtc_tests () { tree1_tests dtc_tree1_merge.test.dtb test_tree1.dtb run_dtc_test -I dts -O dtb -o dtc_tree1_merge_labelled.test.dtb test_tree1_merge_labelled.dts tree1_tests dtc_tree1_merge_labelled.test.dtb test_tree1.dtb + run_dtc_test -I dts -O dtb -o dtc_tree1_label_noderef.test.dtb test_tree1_label_noderef.dts + run_test dtbs_equal_unordered dtc_tree1_label_noderef.test.dtb test_tree1.dtb run_dtc_test -I dts -O dtb -o multilabel_merge.test.dtb multilabel_merge.dts run_test references multilabel.test.dtb run_test dtbs_equal_ordered multilabel.test.dtb multilabel_merge.test.dtb run_dtc_test -I dts -O dtb -o dtc_tree1_merge_path.test.dtb test_tree1_merge_path.dts tree1_tests dtc_tree1_merge_path.test.dtb test_tree1.dtb + run_wrap_error_test $DTC -I dts -O dtb -o /dev/null test_label_ref.dts # Check prop/node delete functionality run_dtc_test -I dts -O dtb -o dtc_tree1_delete.test.dtb test_tree1_delete.dts @@ -412,6 +538,8 @@ dtc_tests () { check_tests reg-ranges-root.dts reg_format ranges_format check_tests default-addr-size.dts avoid_default_addr_size check_tests obsolete-chosen-interrupt-controller.dts obsolete_chosen_interrupt_controller + check_tests reg-without-unit-addr.dts unit_address_vs_reg + check_tests unit-addr-without-reg.dts unit_address_vs_reg run_sh_test dtc-checkfails.sh node_name_chars -- -I dtb -O dtb bad_node_char.dtb run_sh_test dtc-checkfails.sh node_name_format -- -I dtb -O dtb bad_node_format.dtb run_sh_test dtc-checkfails.sh prop_name_chars -- -I dtb -O dtb bad_prop_char.dtb @@ -423,6 +551,9 @@ dtc_tests () { run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label5.dts run_sh_test dtc-checkfails.sh duplicate_label -- -I dts -O dtb reuse-label6.dts + run_test check_path test_tree1.dtb exists "/subnode@1" + run_test check_path test_tree1.dtb not-exists "/subnode@10" + # Check warning options run_sh_test dtc-checkfails.sh address_cells_is_cell interrupt_cells_is_cell -n size_cells_is_cell -- -Wno_size_cells_is_cell -I dts -O dtb bad-ncells.dts run_sh_test dtc-fails.sh -n test-warn-output.test.dtb -I dts -O dtb bad-ncells.dts @@ -465,6 +596,19 @@ dtc_tests () { -o search_paths_b.dtb search_paths_b.dts run_dtc_test -I dts -O dtb -o search_paths_subdir.dtb \ search_dir_b/search_paths_subdir.dts + + # Check -a option + for align in 2 4 8 16 32 64; do + # -p -a + run_dtc_test -O dtb -p 1000 -a $align -o align0.dtb subnode_iterate.dts + check_align align0.dtb $align + # -S -a + run_dtc_test -O dtb -S 1999 -a $align -o align1.dtb subnode_iterate.dts + check_align align1.dtb $align + done + + # Tests for overlay/plugin generation + dtc_overlay_tests } cmp_tests () { @@ -592,6 +736,28 @@ fdtput_tests () { run_wrap_test $DTPUT $dtb -cp /chosen run_wrap_test $DTPUT $dtb -cp /chosen/son + # Start again with a fresh dtb + run_dtc_test -O dtb -p $(stat -c %s $text) -o $dtb $dts + + # Node delete + run_wrap_test $DTPUT $dtb -c /chosen/node1 /chosen/node2 /chosen/node3 + run_fdtget_test "node3\nnode2\nnode1" $dtb -l /chosen + run_wrap_test $DTPUT $dtb -r /chosen/node1 /chosen/node2 + run_fdtget_test "node3" $dtb -l /chosen + + # Delete the non-existent node + run_wrap_error_test $DTPUT $dtb -r /non-existent/node + + # Property delete + run_fdtput_test "eva" $dtb /chosen/ name "" -ts "eva" + run_fdtput_test "016" $dtb /chosen/ age "" -ts "016" + run_fdtget_test "age\nname\nbootargs\nlinux,platform" $dtb -p /chosen + run_wrap_test $DTPUT $dtb -d /chosen/ name age + run_fdtget_test "bootargs\nlinux,platform" $dtb -p /chosen + + # Delete the non-existent property + run_wrap_error_test $DTPUT $dtb -d /chosen non-existent-prop + # TODO: Add tests for verbose mode? } @@ -599,6 +765,10 @@ utilfdt_tests () { run_test utilfdt_test } +fdtdump_tests () { + run_fdtdump_test fdtdump.dts +} + while getopts "vt:me" ARG ; do case $ARG in "v") @@ -617,7 +787,7 @@ while getopts "vt:me" ARG ; do done if [ -z "$TESTSETS" ]; then - TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput" + TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump" fi # Make sure we don't have stale blobs lying around @@ -643,6 +813,9 @@ for set in $TESTSETS; do "fdtput") fdtput_tests ;; + "fdtdump") + fdtdump_tests + ;; esac done diff --git a/tests/setprop.c b/tests/setprop.c index d089f8d5ab5e..be1b1478f7f0 100644 --- a/tests/setprop.c +++ b/tests/setprop.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) TEST_STRING_1); verbose_printf("Old string value was \"%s\"\n", strp); - err = fdt_setprop(fdt, 0, "prop-str", NULL, 0); + err = fdt_setprop_empty(fdt, 0, "prop-str"); if (err) FAIL("Failed to empty \"prop-str\": %s", fdt_strerror(err)); diff --git a/tests/setprop_inplace.c b/tests/setprop_inplace.c index daef182d0b28..80447a0b13ab 100644 --- a/tests/setprop_inplace.c +++ b/tests/setprop_inplace.c @@ -83,5 +83,15 @@ int main(int argc, char *argv[]) strp = check_getprop(fdt, 0, "prop-str", xlen+1, xstr); verbose_printf("New string value is \"%s\"\n", strp); + err = fdt_setprop_inplace_namelen_partial(fdt, 0, "compatible", + strlen("compatible"), 4, + TEST_STRING_4_PARTIAL, + strlen(TEST_STRING_4_PARTIAL)); + if (err) + FAIL("Failed to set \"compatible\": %s\n", fdt_strerror(err)); + + check_getprop(fdt, 0, "compatible", strlen(TEST_STRING_4_RESULT) + 1, + TEST_STRING_4_RESULT); + PASS(); } diff --git a/tests/sourceoutput.dts b/tests/sourceoutput.dts new file mode 100644 index 000000000000..477762f5aaa7 --- /dev/null +++ b/tests/sourceoutput.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + /* Some versions had an off-by-2 bug which caused an abort + * when outputing labels within strings like this in source + * format */ + prop1: prop1 = start1: "foo", mid1: "bar" end1: ; + + /* Make sure that we correctly handle source output of things + * which could almost be expressed as strings, except for the + * embedded labels */ + prop2 = start2: [66 6f 6f], mid2: "bar" end2: ; +}; + diff --git a/tests/stringlist.c b/tests/stringlist.c new file mode 100644 index 000000000000..a9d3e73920ca --- /dev/null +++ b/tests/stringlist.c @@ -0,0 +1,154 @@ +/* + * libfdt - Flat Device Tree manipulation + * Testcase for string handling + * Copyright (C) 2015 NVIDIA Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <libfdt.h> + +#include "tests.h" +#include "testdata.h" + +static void check_expected_failure(const void *fdt, const char *path, + const char *property) +{ + int offset, err; + + offset = fdt_path_offset(fdt, "/"); + if (offset < 0) + FAIL("Couldn't find path %s", path); + + err = fdt_stringlist_count(fdt, offset, "#address-cells"); + if (err != -FDT_ERR_BADVALUE) + FAIL("unexpectedly succeeded in parsing #address-cells\n"); + + err = fdt_stringlist_search(fdt, offset, "#address-cells", "foo"); + if (err != -FDT_ERR_BADVALUE) + FAIL("found string in #address-cells: %d\n", err); + + /* + * Note that the #address-cells property contains a small 32-bit + * unsigned integer, hence some bytes will be zero, and searching for + * the empty string will succeed. + * + * The reason for this oddity is that the function will exit when the + * first occurrence of the string is found, but in order to determine + * that the property does not contain a valid string list it would + * need to process the whole value. + */ + err = fdt_stringlist_search(fdt, offset, "#address-cells", ""); + if (err != 0) + FAIL("empty string not found in #address-cells: %d\n", err); + + /* + * fdt_get_string() can successfully extract strings from non-string + * properties. This is because it doesn't necessarily parse the whole + * property value, which would be necessary for it to determine if a + * valid string or string list is present. + */ +} + +static void check_string_count(const void *fdt, const char *path, + const char *property, int count) +{ + int offset, err; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) + FAIL("Couldn't find path %s", path); + + err = fdt_stringlist_count(fdt, offset, property); + if (err < 0) + FAIL("Couldn't count strings in property %s of node %s: %d\n", + property, path, err); + + if (err != count) + FAIL("String count for property %s of node %s is %d instead of %d\n", + path, property, err, count); +} + +static void check_string_index(const void *fdt, const char *path, + const char *property, const char *string, + int idx) +{ + int offset, err; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) + FAIL("Couldn't find path %s", path); + + err = fdt_stringlist_search(fdt, offset, property, string); + + if (err != idx) + FAIL("Index of %s in property %s of node %s is %d, expected %d\n", + string, property, path, err, idx); +} + +static void check_string(const void *fdt, const char *path, + const char *property, int idx, + const char *string) +{ + const char *result; + int offset, len; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) + FAIL("Couldn't find path %s", path); + + result = fdt_stringlist_get(fdt, offset, property, idx, &len); + if (!result) + FAIL("Couldn't extract string %d from property %s of node %s: %d\n", + idx, property, path, len); + + if (strcmp(string, result) != 0) + FAIL("String %d in property %s of node %s is %s, expected %s\n", + idx, property, path, result, string); +} + +int main(int argc, char *argv[]) +{ + void *fdt; + + if (argc != 2) + CONFIG("Usage: %s <dtb file>\n", argv[0]); + + test_init(argc, argv); + fdt = load_blob(argv[1]); + + check_expected_failure(fdt, "/", "#address-cells"); + check_expected_failure(fdt, "/", "#size-cells"); + + check_string_count(fdt, "/", "compatible", 1); + check_string_count(fdt, "/device", "compatible", 2); + check_string_count(fdt, "/device", "big-endian", 0); + + check_string_index(fdt, "/", "compatible", "test-strings", 0); + check_string_index(fdt, "/device", "compatible", "foo", 0); + check_string_index(fdt, "/device", "compatible", "bar", 1); + check_string_index(fdt, "/device", "big-endian", "baz", -1); + + check_string(fdt, "/", "compatible", 0, "test-strings"); + check_string(fdt, "/device", "compatible", 0, "foo"); + check_string(fdt, "/device", "compatible", 1, "bar"); + + PASS(); +} diff --git a/tests/stringlist.dts b/tests/stringlist.dts new file mode 100644 index 000000000000..1e4d3140458f --- /dev/null +++ b/tests/stringlist.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/ { + compatible = "test-strings"; + #address-cells = <2>; + #size-cells = <2>; + + device { + compatible = "foo", "bar"; + big-endian; + }; +}; diff --git a/tests/subnode_iterate.c b/tests/subnode_iterate.c index b9f379d5963c..0fb5c901ebd7 100644 --- a/tests/subnode_iterate.c +++ b/tests/subnode_iterate.c @@ -48,9 +48,7 @@ static void test_node(void *fdt, int parent_offset) subnodes = cpu_to_fdt32(*prop); count = 0; - for (offset = fdt_first_subnode(fdt, parent_offset); - offset >= 0; - offset = fdt_next_subnode(fdt, offset)) + fdt_for_each_subnode(offset, fdt, parent_offset) count++; if (count != subnodes) { @@ -65,9 +63,7 @@ static void check_fdt_next_subnode(void *fdt) int offset; int count = 0; - for (offset = fdt_first_subnode(fdt, 0); - offset >= 0; - offset = fdt_next_subnode(fdt, offset)) { + fdt_for_each_subnode(offset, fdt, 0) { test_node(fdt, offset); count++; } diff --git a/tests/test_label_ref.dts b/tests/test_label_ref.dts new file mode 100644 index 000000000000..7009c79531a7 --- /dev/null +++ b/tests/test_label_ref.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + +}; + +label: &handle { + +}; diff --git a/tests/test_tree1.dts b/tests/test_tree1.dts index c7b170c5af60..67ecfd0ea28f 100644 --- a/tests/test_tree1.dts +++ b/tests/test_tree1.dts @@ -1,3 +1,45 @@ /dts-v1/; -/include/ "test_tree1_body.dtsi" +/memreserve/ 0xdeadbeef00000000 0x100000; +/memreserve/ 123456789 010000; + +/ { + compatible = "test_tree1"; + prop-int = <0xdeadbeef>; + prop-int64 = /bits/ 64 <0xdeadbeef01abcdef>; + prop-str = "hello world"; + #address-cells = <1>; + #size-cells = <0>; + + subnode@1 { + compatible = "subnode1"; + reg = <1>; + prop-int = [deadbeef]; + + subsubnode { + compatible = "subsubnode1", "subsubnode"; + prop-int = <0xdeadbeef>; + }; + + ss1 { + }; + }; + + subnode@2 { + reg = <2>; + linux,phandle = <0x2000>; + prop-int = <123456789>; + #address-cells = <1>; + #size-cells = <0>; + + ssn0: subsubnode@0 { + reg = <0>; + phandle = <0x2001>; + compatible = "subsubnode2", "subsubnode"; + prop-int = <0726746425>; + }; + + ss2 { + }; + }; +}; diff --git a/tests/test_tree1_delete.dts b/tests/test_tree1_delete.dts index a2f1bfdc51c9..b95ef1e7185f 100644 --- a/tests/test_tree1_delete.dts +++ b/tests/test_tree1_delete.dts @@ -1,6 +1,6 @@ /dts-v1/; -/include/ "test_tree1_body.dtsi" +/include/ "test_tree1.dts" / { nonexistant-property = <0xdeadbeef>; diff --git a/tests/test_tree1_dts0.dts b/tests/test_tree1_dts0.dts deleted file mode 100644 index 032d540abc08..000000000000 --- a/tests/test_tree1_dts0.dts +++ /dev/null @@ -1,37 +0,0 @@ -/dts-v1/; - -/memreserve/ 0xdeadbeef00000000 0x0000000000100000; -/memreserve/ 0x00000000075bcd15 0x0000000000001000; - -/ { - compatible = "test_tree1"; - prop-int = <0xdeadbeef>; - prop-str = "hello world"; - - subnode@1 { - compatible = "subnode1"; - prop-int = [deadbeef]; - - subsubnode { - compatible = "subsubnode1", "subsubnode"; - prop-int = < 0xdeadbeef>; - }; - - ss1 { - }; - }; - - subnode@2 { - linux,phandle = <0x2000>; - prop-int = < 123456789>; - - subsubnode@0 { - linux,phandle = <0x2001>; - compatible = "subsubnode2", "subsubnode"; - prop-int = < 0726746425>; - }; - - ss2 { - }; - }; -}; diff --git a/tests/test_tree1_body.dtsi b/tests/test_tree1_label_noderef.dts index 24a5e1ee35d0..b2b194c6df64 100644 --- a/tests/test_tree1_body.dtsi +++ b/tests/test_tree1_label_noderef.dts @@ -1,3 +1,5 @@ +/dts-v1/; + /memreserve/ 0xdeadbeef00000000 0x100000; /memreserve/ 123456789 010000; @@ -31,13 +33,23 @@ #size-cells = <0>; ssn0: subsubnode@0 { - reg = <0>; phandle = <0x2001>; - compatible = "subsubnode2", "subsubnode"; - prop-int = <0726746425>; + prop-int = <0xbad>; }; ss2 { }; }; }; + +/* Add label to a noderef */ +ssn1: &ssn0 { + reg = <0>; + prop-int = <123456789>; +}; + +/* Use the new label for merging */ +&ssn1 { + prop-int = <0726746425>; + compatible = "subsubnode2", "subsubnode"; +}; diff --git a/tests/testdata.h b/tests/testdata.h index ce715e4c679e..3588778ad159 100644 --- a/tests/testdata.h +++ b/tests/testdata.h @@ -21,6 +21,9 @@ #define TEST_STRING_2 "nastystring: \a\b\t\n\v\f\r\\\"" #define TEST_STRING_3 "\xde\xad\xbe\xef" +#define TEST_STRING_4_PARTIAL "foobar" +#define TEST_STRING_4_RESULT "testfoobar" + #define TEST_CHAR1 '\r' #define TEST_CHAR2 'b' #define TEST_CHAR3 '\0' @@ -33,4 +36,5 @@ extern struct fdt_header _truncated_property; extern struct fdt_header _bad_node_char; extern struct fdt_header _bad_node_format; extern struct fdt_header _bad_prop_char; +extern struct fdt_header _ovf_size_strings; #endif /* ! __ASSEMBLY */ diff --git a/tests/tests.sh b/tests/tests.sh index 31530d5e228f..818fd09c2ff0 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -21,6 +21,7 @@ FAIL_IF_SIGNAL () { DTC=../dtc DTGET=../fdtget DTPUT=../fdtput +FDTDUMP=../fdtdump verbose_run () { if [ -z "$QUIET_TEST" ]; then diff --git a/tests/testutils.c b/tests/testutils.c index f185133a82b5..521f4f1eaf99 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -144,7 +144,6 @@ int nodename_eq(const char *s1, const char *s2) { int len = strlen(s2); - len = strlen(s2); if (strncmp(s1, s2, len) != 0) return 0; if (s1[len] == '\0') diff --git a/tests/trees.S b/tests/trees.S index 2389cd379621..3d24aa2dcbc8 100644 --- a/tests/trees.S +++ b/tests/trees.S @@ -209,3 +209,34 @@ bad_prop_char_strings: STRING(bad_prop_char, prop, "prop$erty") bad_prop_char_strings_end: bad_prop_char_end: + + + /* overflow_size_strings */ + .balign 8 + .globl _ovf_size_strings +_ovf_size_strings: +ovf_size_strings: + FDTLONG(FDT_MAGIC) + FDTLONG(ovf_size_strings_end - ovf_size_strings) + FDTLONG(ovf_size_strings_struct - ovf_size_strings) + FDTLONG(ovf_size_strings_strings - ovf_size_strings) + FDTLONG(ovf_size_strings_rsvmap - ovf_size_strings) + FDTLONG(0x11) + FDTLONG(0x10) + FDTLONG(0) + FDTLONG(0xffffffff) + FDTLONG(ovf_size_strings_struct_end - ovf_size_strings_struct) + EMPTY_RSVMAP(ovf_size_strings) + +ovf_size_strings_struct: + BEGIN_NODE("") + PROP_INT(ovf_size_strings, bad_string, 0) + END_NODE + FDTLONG(FDT_END) +ovf_size_strings_struct_end: + +ovf_size_strings_strings: + STRING(ovf_size_strings, x, "x") + ovf_size_strings_bad_string = ovf_size_strings_strings + 0x10000000 +ovf_size_strings_strings_end: +ovf_size_strings_end: diff --git a/tests/unit-addr-without-reg.dts b/tests/unit-addr-without-reg.dts new file mode 100644 index 000000000000..ac786ebb6bb8 --- /dev/null +++ b/tests/unit-addr-without-reg.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + node@1 { + }; +}; diff --git a/treesource.c b/treesource.c index bf7a626e18ab..c9d8967969f9 100644 --- a/treesource.c +++ b/treesource.c @@ -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); /** |