diff --git a/usr.bin/dtc/dtc.1 b/usr.bin/dtc/dtc.1 index 17bf50946dfc..2458ac55f1d2 100644 --- a/usr.bin/dtc/dtc.1 +++ b/usr.bin/dtc/dtc.1 @@ -30,7 +30,7 @@ .\" .\" $FreeBSD$ .\"/ -.Dd January 17, 2018 +.Dd April 7, 2018 .Dt DTC 1 .Os .Sh NAME @@ -275,7 +275,7 @@ tree when the overlay is applied. .Pp Much simpler syntactic sugar was later invented to simplify generating overlays. Instead of creating targetted fragments manually, one can instead create a root -node that targets a label in the base node using the +node that targets a label in the base FDT using the .Va &label syntax supported in conventional DTS. This will indicate that a fragment should be generated for the node, with the @@ -284,6 +284,19 @@ given being the target, and the properties and child nodes will be used as the __overlay__. .Pp +Additionally, a path-based version of this syntactic sugar is supported. +A root node may target a path in the base FDT using a name of the form +.Va &{/path} . +A fragment will be generated for the node as it is in the +.Va &label +case, except the +.Va target-path +property will be set to +.Va /path +and no +.Va target +will be set. +.Pp Both conventional overlays and the later-added syntactic sugar are supported. .Pp Overlay blobs can be applied at boot time by setting diff --git a/usr.bin/dtc/dtc.cc b/usr.bin/dtc/dtc.cc index 4a34419b7a0a..aec5c3a4ab14 100644 --- a/usr.bin/dtc/dtc.cc +++ b/usr.bin/dtc/dtc.cc @@ -171,11 +171,14 @@ main(int argc, char **argv) case 'o': { outfile_name = optarg; - outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666); - if (outfile == -1) + if (strcmp(outfile_name, "-") != 0) { - perror("Unable to open output file"); - return EXIT_FAILURE; + outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (outfile == -1) + { + perror("Unable to open output file"); + return EXIT_FAILURE; + } } break; } diff --git a/usr.bin/dtc/fdt.cc b/usr.bin/dtc/fdt.cc index a153756574f0..a3bc29793e55 100644 --- a/usr.bin/dtc/fdt.cc +++ b/usr.bin/dtc/fdt.cc @@ -727,14 +727,32 @@ node::parse_name(text_input_buffer &input, bool &is_property, const char *error) return n; } -void -node::visit(std::function fn) +node::visit_behavior +node::visit(std::function fn, node *parent) { - fn(*this); - for (auto &&c : children) + visit_behavior behavior; + behavior = fn(*this, parent); + if (behavior == VISIT_BREAK) { - c->visit(fn); + return VISIT_BREAK; } + else if (behavior != VISIT_CONTINUE) + { + for (auto &&c : children) + { + behavior = c->visit(fn, this); + // Any status other than VISIT_RECURSE stops our execution and + // bubbles up to our caller. The caller may then either continue + // visiting nodes that are siblings to this one or completely halt + // visiting. + if (behavior != VISIT_RECURSE) + { + return behavior; + } + } + } + // Continue recursion by default + return VISIT_RECURSE; } node::node(input_buffer &structs, input_buffer &strings) : valid(true) @@ -1319,7 +1337,7 @@ device_tree::resolve_cross_references(uint32_t &phandle) phandle_set.insert({&i.val, i}); } std::vector> sorted_phandles; - root->visit([&](node &n) { + root->visit([&](node &n, node *parent) { for (auto &p : n.properties()) { for (auto &v : *p) @@ -1331,7 +1349,9 @@ device_tree::resolve_cross_references(uint32_t &phandle) } } } - }); + // Allow recursion + return node::VISIT_RECURSE; + }, nullptr); assert(sorted_phandles.size() == fixups.size()); for (auto &i : sorted_phandles) @@ -1471,9 +1491,24 @@ device_tree::parse_file(text_input_buffer &input, else if (input.consume('&')) { input.next_token(); - string name = input.parse_node_name(); + string name; + bool name_is_path_reference = false; + // This is to deal with names intended as path references, e.g. &{/path}. + // While it may make sense in a non-plugin context, we don't support such + // usage at this time. + if (input.consume('{') && is_plugin) + { + name = input.parse_to('}'); + input.consume('}'); + name_is_path_reference = true; + } + else + { + name = input.parse_node_name(); + } input.next_token(); n = node::parse(input, std::move(name), string_set(), string(), &defines); + n->name_is_path_reference = name_is_path_reference; } else { @@ -1702,11 +1737,21 @@ device_tree::create_fragment_wrapper(node_ptr &node, int &fragnum) node_ptr newroot = node::create_special_node("", symbols); node_ptr wrapper = node::create_special_node("__overlay__", symbols); - // Generate the fragment with target = <&name> + // Generate the fragment with $propname = <&name> property_value v; + std::string propname; v.string_data = node->name; - v.type = property_value::PHANDLE; - auto prop = std::make_shared(std::string("target")); + if (!node->name_is_path_reference) + { + propname = "target"; + v.type = property_value::PHANDLE; + } + else + { + propname = "target-path"; + v.type = property_value::STRING; + } + auto prop = std::make_shared(std::string(propname)); prop->add_value(v); symbols.push_back(prop); diff --git a/usr.bin/dtc/fdt.hh b/usr.bin/dtc/fdt.hh index cc80aede23b6..3781d13c617f 100644 --- a/usr.bin/dtc/fdt.hh +++ b/usr.bin/dtc/fdt.hh @@ -408,6 +408,10 @@ class node * The name of the node. */ std::string name; + /** + * The name of the node is a path reference. + */ + bool name_is_path_reference = false; /** * The unit address of the node, which is optionally written after the * name followed by an at symbol. @@ -421,6 +425,25 @@ class node * Iterator type for child nodes. */ typedef std::vector::iterator child_iterator; + /** + * Recursion behavior to be observed for visiting + */ + enum visit_behavior + { + /** + * Recurse as normal through the rest of the tree. + */ + VISIT_RECURSE, + /** + * Continue recursing through the device tree, but do not + * recurse through this branch of the tree any further. + */ + VISIT_CONTINUE, + /** + * Immediately halt the visit. No further nodes will be visited. + */ + VISIT_BREAK + }; private: /** * Adaptor to use children in range-based for loops. @@ -635,9 +658,13 @@ class node */ void write_dts(FILE *file, int indent); /** - * Recursively visit this node and then its children. + * Recursively visit this node and then its children based on the + * callable's return value. The callable may return VISIT_BREAK + * immediately halt all recursion and end the visit, VISIT_CONTINUE to + * not recurse into the current node's children, or VISIT_RECURSE to recurse + * through children as expected. parent will be passed to the callable. */ - void visit(std::function); + visit_behavior visit(std::function, node *parent); }; /**