diff options
author | Kyle Evans <kevans@FreeBSD.org> | 2019-03-28 03:48:51 +0000 |
---|---|---|
committer | Kyle Evans <kevans@FreeBSD.org> | 2019-03-28 03:48:51 +0000 |
commit | d37eb02eb903368c118892826eebd24b2731e9f9 (patch) | |
tree | 8eb2846d76fe5148ed745596b9f349de1cef14fe /usr.bin/dtc/fdt.cc | |
parent | 93c9d319184db1ebe7cdac24c8ddf98dfc682dcf (diff) | |
download | src-d37eb02eb903368c118892826eebd24b2731e9f9.tar.gz src-d37eb02eb903368c118892826eebd24b2731e9f9.zip |
dtc(1): Update to 1a79f5f26631
Highlights:
- Bugfix for order in which /delete-node/ and /delete-property/ are
processed [0]
- /omit-if-no-ref/ support has been added (used only by U-Boot at this
point, in theory)
- GPL dtc compat version bumped to 1.4.7
- Various small fixes and compatibility improvements
Reported by: strejda [0]
MFC after: 1 week
Notes
Notes:
svn path=/head/; revision=345628
Diffstat (limited to 'usr.bin/dtc/fdt.cc')
-rw-r--r-- | usr.bin/dtc/fdt.cc | 202 |
1 files changed, 166 insertions, 36 deletions
diff --git a/usr.bin/dtc/fdt.cc b/usr.bin/dtc/fdt.cc index 3148ec35a414..fa4125ffe6ef 100644 --- a/usr.bin/dtc/fdt.cc +++ b/usr.bin/dtc/fdt.cc @@ -46,6 +46,7 @@ #include <libgen.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> @@ -491,6 +492,7 @@ property::property(text_input_buffer &input, break; } } + [[fallthrough]]; default: input.parse_error("Invalid property value."); valid = false; @@ -622,6 +624,7 @@ property_value::try_to_merge(property_value &other) return false; case EMPTY: *this = other; + [[fallthrough]]; case STRING: case STRING_LIST: case CROSS_REFERENCE: @@ -846,6 +849,7 @@ node_ptr node::create_special_node(const string &name, } node::node(text_input_buffer &input, + device_tree &tree, string &&n, std::unordered_set<string> &&l, string &&a, @@ -862,6 +866,9 @@ node::node(text_input_buffer &input, // flag set if we find any characters that are only in // the property name character set, not the node bool is_property = false; + // flag set if our node is marked as /omit-if-no-ref/ to be + // garbage collected later if nothing references it + bool marked_omit_if_no_ref = false; string child_name, child_address; std::unordered_set<string> child_labels; auto parse_delete = [&](const char *expected, bool at) @@ -908,6 +915,12 @@ node::node(text_input_buffer &input, } continue; } + if (input.consume("/omit-if-no-ref/")) + { + input.next_token(); + marked_omit_if_no_ref = true; + tree.set_needs_garbage_collection(); + } child_name = parse_name(input, is_property, "Expected property or node name"); while (input.consume(':')) @@ -943,10 +956,11 @@ node::node(text_input_buffer &input, } else if (!is_property && *input == ('{')) { - node_ptr child = node::parse(input, std::move(child_name), + node_ptr child = node::parse(input, tree, std::move(child_name), std::move(child_labels), std::move(child_address), defines); if (child) { + child->omit_if_no_ref = marked_omit_if_no_ref; children.push_back(std::move(child)); } else @@ -998,12 +1012,14 @@ node::sort() node_ptr node::parse(text_input_buffer &input, + device_tree &tree, string &&name, string_set &&label, string &&address, define_map *defines) { node_ptr n(new node(input, + tree, std::move(name), std::move(label), std::move(address), @@ -1046,6 +1062,30 @@ node::merge_node(node_ptr &other) { labels.insert(l); } + children.erase(std::remove_if(children.begin(), children.end(), + [&](const node_ptr &p) { + string full_name = p->name; + if (p->unit_address != string()) + { + full_name += '@'; + full_name += p->unit_address; + } + if (other->deleted_children.count(full_name) > 0) + { + other->deleted_children.erase(full_name); + return true; + } + return false; + }), children.end()); + props.erase(std::remove_if(props.begin(), props.end(), + [&](const property_ptr &p) { + if (other->deleted_props.count(p->get_key()) > 0) + { + other->deleted_props.erase(p->get_key()); + return true; + } + return false; + }), props.end()); // Note: this is an O(n*m) operation. It might be sensible to // optimise this if we find that there are nodes with very // large numbers of properties, but for typical usage the @@ -1085,30 +1125,6 @@ node::merge_node(node_ptr &other) children.push_back(std::move(c)); } } - children.erase(std::remove_if(children.begin(), children.end(), - [&](const node_ptr &p) { - string full_name = p->name; - if (p->unit_address != string()) - { - full_name += '@'; - full_name += p->unit_address; - } - if (other->deleted_children.count(full_name) > 0) - { - other->deleted_children.erase(full_name); - return true; - } - return false; - }), children.end()); - props.erase(std::remove_if(props.begin(), props.end(), - [&](const property_ptr &p) { - if (other->deleted_props.count(p->get_key()) > 0) - { - other->deleted_props.erase(p->get_key()); - return true; - } - return false; - }), props.end()); } void @@ -1187,6 +1203,7 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path) { node_names.insert(std::make_pair(name, n.get())); node_paths.insert(std::make_pair(name, path)); + ordered_node_paths.push_back({name, path}); } else { @@ -1243,6 +1260,7 @@ device_tree::collect_names() node_path p; node_names.clear(); node_paths.clear(); + ordered_node_paths.clear(); cross_references.clear(); fixups.clear(); collect_names_recursive(root, p); @@ -1353,7 +1371,6 @@ device_tree::resolve_cross_references(uint32_t &phandle) return node::VISIT_RECURSE; }, nullptr); assert(sorted_phandles.size() == fixups.size()); - for (auto &i : sorted_phandles) { string target_name = i.get().val.string_data; @@ -1441,6 +1458,103 @@ device_tree::resolve_cross_references(uint32_t &phandle) } } +bool +device_tree::garbage_collect_marked_nodes() +{ + std::unordered_set<node*> previously_referenced_nodes; + std::unordered_set<node*> newly_referenced_nodes; + + auto mark_referenced_nodes_used = [&](node &n) + { + for (auto &p : n.properties()) + { + for (auto &v : *p) + { + if (v.is_phandle()) + { + node *nx = node_names[v.string_data]; + if (nx == nullptr) + { + // Try it again, but as a path + for (auto &s : node_paths) + { + if (v.string_data == s.second.to_string()) + { + nx = node_names[s.first]; + break; + } + } + } + if (nx == nullptr) + { + // Couldn't resolve this one? + continue; + } + // Only mark those currently unmarked + if (!nx->used) + { + nx->used = 1; + newly_referenced_nodes.insert(nx); + } + } + } + } + }; + + // Seed our referenced nodes with those that have been seen by a node that + // either will not be omitted if it's unreferenced or has a symbol. + // Nodes with symbols are explicitly not garbage collected because they may + // be expected for referencing by an overlay, and we do not want surprises + // there. + root->visit([&](node &n, node *) { + if (!n.omit_if_no_ref || (write_symbols && !n.labels.empty())) + { + mark_referenced_nodes_used(n); + } + // Recurse as normal + return node::VISIT_RECURSE; + }, nullptr); + + while (!newly_referenced_nodes.empty()) + { + previously_referenced_nodes = std::move(newly_referenced_nodes); + for (auto *n : previously_referenced_nodes) + { + mark_referenced_nodes_used(*n); + } + } + + previously_referenced_nodes.clear(); + bool children_deleted = false; + + // Delete + root->visit([&](node &n, node *) { + bool gc_children = false; + + for (auto &cn : n.child_nodes()) + { + if (cn->omit_if_no_ref && !cn->used) + { + gc_children = true; + break; + } + } + + if (gc_children) + { + children_deleted = true; + n.delete_children_if([](node_ptr &nx) { + return (nx->omit_if_no_ref && !nx->used); + }); + + return node::VISIT_CONTINUE; + } + + return node::VISIT_RECURSE; + }, nullptr); + + return children_deleted; +} void device_tree::parse_file(text_input_buffer &input, @@ -1486,7 +1600,7 @@ device_tree::parse_file(text_input_buffer &input, if (input.consume('/')) { input.next_token(); - n = node::parse(input, string(), string_set(), string(), &defines); + n = node::parse(input, *this, string(), string_set(), string(), &defines); } else if (input.consume('&')) { @@ -1507,7 +1621,7 @@ device_tree::parse_file(text_input_buffer &input, name = input.parse_node_name(); } input.next_token(); - n = node::parse(input, std::move(name), string_set(), string(), &defines); + n = node::parse(input, *this, std::move(name), string_set(), string(), &defines); n->name_is_path_reference = name_is_path_reference; } else @@ -1890,6 +2004,12 @@ device_tree::parse_dts(const string &fn, FILE *depfile) } } collect_names(); + // Return value indicates whether we've dirtied the tree or not and need to + // recollect names + if (garbage_collect && garbage_collect_marked_nodes()) + { + collect_names(); + } uint32_t phandle = 1; // If we're writing symbols, go ahead and assign phandles to the entire // tree. We'll do this before we resolve cross references, just to keep @@ -1906,8 +2026,14 @@ device_tree::parse_dts(const string &fn, FILE *depfile) // referenced by other plugins, so we create a __symbols__ node inside // the root that contains mappings (properties) from label names to // paths. - for (auto &s : node_paths) + for (auto i=ordered_node_paths.rbegin(), e=ordered_node_paths.rend() ; i!=e ; ++i) { + auto &s = *i; + if (node_paths.find(s.first) == node_paths.end()) + { + // Erased node, skip it. + continue; + } property_value v; v.string_data = s.second.to_string(); v.type = property_value::STRING; @@ -1986,19 +2112,23 @@ device_tree::parse_dts(const string &fn, FILE *depfile) { if (c->name == p.first) { - string path = p.first; - if (!(p.second.empty())) + if (c->unit_address == p.second) { - path += '@'; - path += p.second; + n = c.get(); + found = true; + break; } - n->add_child(node::create_special_node(path, symbols)); - n = (--n->child_end())->get(); } } if (!found) { - n->add_child(node::create_special_node(p.first, symbols)); + string path = p.first; + if (!(p.second.empty())) + { + path += '@'; + path += p.second; + } + n->add_child(node::create_special_node(path, symbols)); n = (--n->child_end())->get(); } } |