dtc: update to upstream 227d6a3

- Report missing includes at the correct location.
- Add initial support for the -@ option emitting a symbol table.
- Add support for running tests with and without -@
- Add support for generating __fixups__ and __local_fixups__
- Attach the to-string transform to the node path.
This commit is contained in:
Ed Maste 2017-04-17 17:23:19 +00:00
parent ca148cda3b
commit 21d5d37ba4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=317060
8 changed files with 287 additions and 63 deletions

View File

@ -97,16 +97,16 @@ namespace
} }
if (found_size && found_address) if (found_size && found_address)
{ {
break; break;
} }
} }
if (!found_address) if (!found_address)
{ {
report_error("Missing #address-cells property"); report_error("Missing #address-cells property");
} }
if (!found_size) if (!found_size)
{ {
report_error("Missing #size-cells property"); report_error("Missing #size-cells property");
} }
return found_address && found_size; return found_address && found_size;
} }

View File

@ -189,17 +189,17 @@ class binary_writer : public output_writer
* The binary format does not support labels, so this method * The binary format does not support labels, so this method
* does nothing. * does nothing.
*/ */
virtual void write_label(const std::string &) {} void write_label(const std::string &) override {}
/** /**
* Comments are ignored by the binary writer. * Comments are ignored by the binary writer.
*/ */
virtual void write_comment(const std::string&) {} void write_comment(const std::string&) override {}
virtual void write_string(const std::string &name); void write_string(const std::string &name) override;
virtual void write_data(uint8_t v); void write_data(uint8_t v) override;
virtual void write_data(uint32_t v); void write_data(uint32_t v) override;
virtual void write_data(uint64_t v); void write_data(uint64_t v) override;
virtual void write_to_file(int fd); void write_to_file(int fd) override;
virtual uint32_t size(); uint32_t size() override;
}; };
/** /**
* Assembly writer. This class is responsible for writing the output in an * Assembly writer. This class is responsible for writing the output in an
@ -234,7 +234,7 @@ class asm_writer : public output_writer
/** /**
* Write a string to the output. * Write a string to the output.
*/ */
void write_string(const std::string &c); void write_string(const std::string &c) override;
/** /**
* Writes the string, starting on a new line. * Writes the string, starting on a new line.
*/ */
@ -246,13 +246,13 @@ class asm_writer : public output_writer
void write_byte(uint8_t b); void write_byte(uint8_t b);
public: public:
asm_writer() : byte_count(0), bytes_written(0) {} asm_writer() : byte_count(0), bytes_written(0) {}
virtual void write_label(const std::string &name); void write_label(const std::string &name) override;
virtual void write_comment(const std::string &name); void write_comment(const std::string &name) override;
virtual void write_data(uint8_t v); void write_data(uint8_t v) override;
virtual void write_data(uint32_t v); void write_data(uint32_t v) override;
virtual void write_data(uint64_t v); void write_data(uint64_t v) override;
virtual void write_to_file(int fd); void write_to_file(int fd) override;
virtual uint32_t size(); uint32_t size() override;
}; };
/** /**

View File

@ -38,7 +38,7 @@
.Nd device tree compiler .Nd device tree compiler
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl fhsv .Op Fl @fhsv
.Op Fl b Ar boot_cpu_id .Op Fl b Ar boot_cpu_id
.Op Fl d Ar dependency_file .Op Fl d Ar dependency_file
.Op Fl E Ar [no-]checker_name .Op Fl E Ar [no-]checker_name
@ -84,6 +84,8 @@ Enable or disable a specified checker.
The argument is the name of the checker. The argument is the name of the checker.
The full list of checkers is given in The full list of checkers is given in
.Sx CHECKERS . .Sx CHECKERS .
.It Fl @
Emit a __symbols__ node to allow plugins to be loaded.
.It Fl f .It Fl f
Force the tool to attempt to generate the output, even if the input had errors. Force the tool to attempt to generate the output, even if the input had errors.
.It Fl h .It Fl h

View File

@ -54,7 +54,7 @@ int version_major = 0;
/** /**
* The current minor version of the tool. * The current minor version of the tool.
*/ */
int version_minor = 4; int version_minor = 5;
/** /**
* The current patch level of the tool. * The current patch level of the tool.
*/ */
@ -63,7 +63,7 @@ int version_patch = 0;
static void usage(const string &argv0) static void usage(const string &argv0)
{ {
fprintf(stderr, "Usage:\n" fprintf(stderr, "Usage:\n"
"\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]" "\t%s\t[-fhsv@] [-b boot_cpu_id] [-d dependency_file]"
"[-E [no-]checker_name]\n" "[-E [no-]checker_name]\n"
"\t\t[-H phandle_format] [-I input_format]" "\t\t[-H phandle_format] [-I input_format]"
"[-O output_format]\n" "[-O output_format]\n"
@ -101,7 +101,7 @@ main(int argc, char **argv)
clock_t c0 = clock(); clock_t c0 = clock();
class device_tree tree; class device_tree tree;
fdt::checking::check_manager checks; fdt::checking::check_manager checks;
const char *options = "hqI:O:o:V:d:R:S:p:b:fi:svH:W:E:DP:"; const char *options = "@hqI:O:o:V:d:R:S:p:b:fi:svH:W:E:DP:";
// Don't forget to update the man page if any more options are added. // Don't forget to update the man page if any more options are added.
while ((ch = getopt(argc, argv, options)) != -1) while ((ch = getopt(argc, argv, options)) != -1)
@ -114,6 +114,9 @@ main(int argc, char **argv)
case 'v': case 'v':
version(argv[0]); version(argv[0]);
return EXIT_SUCCESS; return EXIT_SUCCESS;
case '@':
tree.write_symbols = true;
break;
case 'I': case 'I':
{ {
string arg(optarg); string arg(optarg);

View File

@ -169,6 +169,16 @@ property_value::resolve_type()
type = BINARY; type = BINARY;
} }
size_t
property_value::size()
{
if (!byte_data.empty())
{
return byte_data.size();
}
return string_data.size() + 1;
}
void void
property_value::write_as_string(FILE *file) property_value::write_as_string(FILE *file)
{ {
@ -286,7 +296,6 @@ property::parse_cells(text_input_buffer &input, int cell_size)
return; return;
} }
input.next_token(); input.next_token();
bool isPath = false;
string referenced; string referenced;
if (!input.consume('{')) if (!input.consume('{'))
{ {
@ -296,7 +305,6 @@ property::parse_cells(text_input_buffer &input, int cell_size)
{ {
referenced = input.parse_to('}'); referenced = input.parse_to('}');
input.consume('}'); input.consume('}');
isPath = true;
} }
if (referenced.empty()) if (referenced.empty())
{ {
@ -655,6 +663,21 @@ property::write_dts(FILE *file, int indent)
fputs(";\n", file); fputs(";\n", file);
} }
size_t
property::offset_of_value(property_value &val)
{
size_t off = 0;
for (auto &v : values)
{
if (&v == &val)
{
return off;
}
off += v.size();
}
return -1;
}
string string
node::parse_name(text_input_buffer &input, bool &is_property, const char *error) node::parse_name(text_input_buffer &input, bool &is_property, const char *error)
{ {
@ -764,6 +787,21 @@ node::node(input_buffer &structs, input_buffer &strings) : valid(true)
return; return;
} }
node::node(const string &n,
const std::vector<property_ptr> &p)
: name(n)
{
props.insert(props.begin(), p.begin(), p.end());
}
node_ptr node::create_special_node(const string &name,
const std::vector<property_ptr> &props)
{
node_ptr n(new node(name, props));
return n;
}
node::node(text_input_buffer &input, node::node(text_input_buffer &input,
string &&n, string &&n,
std::unordered_set<string> &&l, std::unordered_set<string> &&l,
@ -1123,7 +1161,6 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path)
{ {
collect_names_recursive(c, path); collect_names_recursive(c, path);
} }
path.pop_back();
// Now we collect the phandles and properties that reference // Now we collect the phandles and properties that reference
// other nodes. // other nodes.
for (auto &p : n->properties()) for (auto &p : n->properties())
@ -1132,7 +1169,7 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path)
{ {
if (v.is_phandle()) if (v.is_phandle())
{ {
phandles.push_back(&v); fixups.push_back({path, p, v});
} }
if (v.is_cross_reference()) if (v.is_cross_reference())
{ {
@ -1154,6 +1191,7 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path)
} }
} }
} }
path.pop_back();
} }
void void
@ -1163,7 +1201,7 @@ device_tree::collect_names()
node_names.clear(); node_names.clear();
node_paths.clear(); node_paths.clear();
cross_references.clear(); cross_references.clear();
phandles.clear(); fixups.clear();
collect_names_recursive(root, p); collect_names_recursive(root, p);
} }
@ -1191,37 +1229,38 @@ device_tree::resolve_cross_references()
} }
} }
} }
std::unordered_set<property_value*> phandle_set; std::unordered_map<property_value*, fixup&> phandle_set;
for (auto &i : phandles) for (auto &i : fixups)
{ {
phandle_set.insert(i); phandle_set.insert({&i.val, i});
} }
std::vector<property_value*> sorted_phandles; std::vector<std::reference_wrapper<fixup>> sorted_phandles;
root->visit([&](node &n) { root->visit([&](node &n) {
for (auto &p : n.properties()) for (auto &p : n.properties())
{ {
for (auto &v : *p) for (auto &v : *p)
{ {
if (phandle_set.count(&v)) auto i = phandle_set.find(&v);
if (i != phandle_set.end())
{ {
sorted_phandles.push_back(&v); sorted_phandles.push_back(i->second);
} }
} }
} }
}); });
assert(sorted_phandles.size() == phandles.size()); assert(sorted_phandles.size() == fixups.size());
uint32_t phandle = 1; uint32_t phandle = 1;
for (auto &i : sorted_phandles) for (auto &i : sorted_phandles)
{ {
string target_name = i->string_data; string target_name = i.get().val.string_data;
node *target = nullptr; node *target = nullptr;
string possible; string possible;
// If the node name is a path, then look it up by following the path, // If the node name is a path, then look it up by following the path,
// otherwise jump directly to the named node. // otherwise jump directly to the named node.
if (target_name[0] == '/') if (target_name[0] == '/')
{ {
std::string path; string path;
target = root.get(); target = root.get();
std::istringstream ss(target_name); std::istringstream ss(target_name);
string path_element; string path_element;
@ -1276,13 +1315,21 @@ device_tree::resolve_cross_references()
} }
if (target == nullptr) if (target == nullptr)
{ {
fprintf(stderr, "Failed to find node with label: %s\n", target_name.c_str()); if (is_plugin)
if (possible != string())
{ {
fprintf(stderr, "Possible intended match: %s\n", possible.c_str()); unresolved_fixups.push_back(i);
continue;
}
else
{
fprintf(stderr, "Failed to find node with label: %s\n", target_name.c_str());
if (possible != string())
{
fprintf(stderr, "Possible intended match: %s\n", possible.c_str());
}
valid = 0;
return;
} }
valid = 0;
return;
} }
// If there is an existing phandle, use it // If there is an existing phandle, use it
property_ptr p = target->get_property("phandle"); property_ptr p = target->get_property("phandle");
@ -1322,8 +1369,8 @@ device_tree::resolve_cross_references()
target->add_property(p); target->add_property(p);
} }
} }
p->begin()->push_to_buffer(i->byte_data); p->begin()->push_to_buffer(i.get().val.byte_data);
assert(i->byte_data.size() == 4); assert(i.get().val.byte_data.size() == 4);
} }
} }
@ -1340,6 +1387,10 @@ device_tree::parse_file(text_input_buffer &input,
read_header = true; read_header = true;
} }
input.next_token(); input.next_token();
if (input.consume("/plugin/;"))
{
is_plugin = true;
}
input.next_token(); input.next_token();
if (!read_header) if (!read_header)
{ {
@ -1567,6 +1618,30 @@ device_tree::parse_dtb(const string &fn, FILE *)
valid = (root != 0); valid = (root != 0);
} }
string
device_tree::node_path::to_string() const
{
string path;
auto p = begin();
auto pe = end();
if ((p == pe) || (p+1 == pe))
{
return string("/");
}
// Skip the first name in the path. It's always "", and implicitly /
for (++p ; p!=pe ; ++p)
{
path += '/';
path += p->first;
if (!(p->second.empty()))
{
path += '@';
path += p->second;
}
}
return path;
}
void void
device_tree::parse_dts(const string &fn, FILE *depfile) device_tree::parse_dts(const string &fn, FILE *depfile)
{ {
@ -1631,6 +1706,85 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
} }
collect_names(); collect_names();
resolve_cross_references(); resolve_cross_references();
if (write_symbols)
{
std::vector<property_ptr> symbols;
// Create a symbol table. Each label in this device tree may be
// 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)
{
property_value v;
v.string_data = s.second.to_string();
v.type = property_value::STRING;
string name = s.first;
auto prop = std::make_shared<property>(std::move(name));
prop->add_value(v);
symbols.push_back(prop);
}
root->add_child(node::create_special_node("__symbols__", symbols));
// If this is a plugin, then we also need to create two extra nodes.
// Internal phandles will need to be renumbered to avoid conflicts with
// already-loaded nodes and external references will need to be
// resolved.
if (is_plugin)
{
// Create the fixups entry. This is of the form:
// {target} = {path}:{property name}:{offset}
auto create_fixup_entry = [&](fixup &i, string target)
{
string value = i.path.to_string();
value += ':';
value += i.prop->get_key();
value += ':';
value += std::to_string(i.prop->offset_of_value(i.val));
property_value v;
v.string_data = value;
v.type = property_value::STRING;
auto prop = std::make_shared<property>(std::move(target));
prop->add_value(v);
return prop;
};
// If we have any unresolved phandle references in this plugin,
// then we must update them to 0xdeadbeef and leave a property in
// the /__fixups__ node whose key is the label and whose value is
// as described above.
if (!unresolved_fixups.empty())
{
symbols.clear();
for (auto &i : unresolved_fixups)
{
auto &val = i.get().val;
symbols.push_back(create_fixup_entry(i, val.string_data));
val.byte_data.push_back(0xde);
val.byte_data.push_back(0xad);
val.byte_data.push_back(0xbe);
val.byte_data.push_back(0xef);
val.type = property_value::BINARY;
}
root->add_child(node::create_special_node("__fixups__", symbols));
}
symbols.clear();
// If we have any resolved phandle references in this plugin, then
// we must leave a property in the /__local_fixups__ node whose key
// is 'fixup' and whose value is as described above.
for (auto &i : fixups)
{
if (!i.val.is_phandle())
{
continue;
}
symbols.push_back(create_fixup_entry(i, "fixup"));
}
// We've iterated over all fixups, but only emit the
// __local_fixups__ if we found some that were resolved internally.
if (!symbols.empty())
{
root->add_child(node::create_special_node("__local_fixups__", symbols));
}
}
}
} }
bool device_tree::parse_define(const char *def) bool device_tree::parse_define(const char *def)
@ -1653,7 +1807,7 @@ bool device_tree::parse_define(const char *def)
text_input_buffer in(std::move(raw), text_input_buffer in(std::move(raw),
std::unordered_set<string>(), std::unordered_set<string>(),
std::vector<string>(), std::vector<string>(),
std::string(), string(),
nullptr); nullptr);
property_ptr p = property::parse(in, std::move(name_copy), string_set(), false); property_ptr p = property::parse(in, std::move(name_copy), string_set(), false);
if (p) if (p)

View File

@ -211,6 +211,10 @@ struct property_value
* false otherwise. * false otherwise.
*/ */
bool try_to_merge(property_value &other); bool try_to_merge(property_value &other);
/**
* Returns the size (in bytes) of this property value.
*/
size_t size();
private: private:
/** /**
* Returns whether the value is of the specified type. If the type of * Returns whether the value is of the specified type. If the type of
@ -380,6 +384,10 @@ class property
* applicable way that it can determine. * applicable way that it can determine.
*/ */
void write_dts(FILE *file, int indent); void write_dts(FILE *file, int indent);
/**
* Returns the byte offset of the specified property value.
*/
size_t offset_of_value(property_value &val);
}; };
/** /**
@ -478,6 +486,10 @@ class node
std::unordered_set<std::string> &&l, std::unordered_set<std::string> &&l,
std::string &&a, std::string &&a,
define_map*); define_map*);
/**
* Creates a special node with the specified name and properties.
*/
node(const std::string &n, const std::vector<property_ptr> &p);
/** /**
* Comparison function for properties, used when sorting the properties * Comparison function for properties, used when sorting the properties
* vector. Orders the properties based on their names. * vector. Orders the properties based on their names.
@ -578,6 +590,11 @@ class node
* have been parsed. * have been parsed.
*/ */
static node_ptr parse_dtb(input_buffer &structs, input_buffer &strings); static node_ptr parse_dtb(input_buffer &structs, input_buffer &strings);
/**
* Construct a new special node from a name and set of properties.
*/
static node_ptr create_special_node(const std::string &name,
const std::vector<property_ptr> &props);
/** /**
* Returns a property corresponding to the specified key, or 0 if this * Returns a property corresponding to the specified key, or 0 if this
* node does not contain a property of that name. * node does not contain a property of that name.
@ -590,6 +607,13 @@ class node
{ {
props.push_back(p); props.push_back(p);
} }
/**
* Adds a new child to this node.
*/
inline void add_child(node_ptr &&n)
{
children.push_back(std::move(n));
}
/** /**
* Merges a node into this one. Any properties present in both are * Merges a node into this one. Any properties present in both are
* overridden, any properties present in only one are preserved. * overridden, any properties present in only one are preserved.
@ -626,7 +650,14 @@ class device_tree
* Type used for node paths. A node path is sequence of names and unit * Type used for node paths. A node path is sequence of names and unit
* addresses. * addresses.
*/ */
typedef std::vector<std::pair<std::string,std::string> > node_path; class node_path : public std::vector<std::pair<std::string,std::string>>
{
public:
/**
* Converts this to a string representation.
*/
std::string to_string() const;
};
/** /**
* Name that we should use for phandle nodes. * Name that we should use for phandle nodes.
*/ */
@ -680,12 +711,35 @@ class device_tree
* These should be expanded to the full path of their targets. * These should be expanded to the full path of their targets.
*/ */
std::vector<property_value*> cross_references; std::vector<property_value*> cross_references;
/**
* The location of something requiring a fixup entry.
*/
struct fixup
{
/**
* The path to the node.
*/
node_path path;
/**
* The property containing the reference.
*/
property_ptr prop;
/**
* The property value that contains the reference.
*/
property_value &val;
};
/** /**
* A collection of property values that refer to phandles. These will * A collection of property values that refer to phandles. These will
* be replaced by the value of the phandle property in their * be replaced by the value of the phandle property in their
* destination. * destination.
*/ */
std::vector<property_value*> phandles; std::vector<fixup> fixups;
/**
* The locations of all of the values that are supposed to become phandle
* references, but refer to things outside of this file.
*/
std::vector<std::reference_wrapper<fixup>> unresolved_fixups;
/** /**
* The names of nodes that target phandles. * The names of nodes that target phandles.
*/ */
@ -732,6 +786,10 @@ class device_tree
* The number of bytes of padding to add to the end of the blob. * The number of bytes of padding to add to the end of the blob.
*/ */
uint32_t blob_padding; uint32_t blob_padding;
/**
* Is this tree a plugin?
*/
bool is_plugin;
/** /**
* Visit all of the nodes recursively, and if they have labels then add * Visit all of the nodes recursively, and if they have labels then add
* them to the node_paths and node_names vectors so that they can be * them to the node_paths and node_names vectors so that they can be
@ -771,6 +829,11 @@ class device_tree
template<class writer> template<class writer>
void write(int fd); void write(int fd);
public: public:
/**
* Should we write the __symbols__ node (to allow overlays to be linked
* against this blob)?
*/
bool write_symbols = false;
/** /**
* Returns the node referenced by the property. If this is a tree that * Returns the node referenced by the property. If this is a tree that
* is in source form, then we have a string that we can use to index * is in source form, then we have a string that we can use to index

View File

@ -102,7 +102,7 @@ struct stream_input_buffer : public dtc::input_buffer
stream_input_buffer(); stream_input_buffer();
}; };
mmap_input_buffer::mmap_input_buffer(int fd, std::string &&filename) mmap_input_buffer::mmap_input_buffer(int fd, string &&filename)
: input_buffer(0, 0), fn(filename) : input_buffer(0, 0), fn(filename)
{ {
struct stat sb; struct stat sb;
@ -216,6 +216,7 @@ text_input_buffer::handle_include()
parse_error("Expected quoted filename"); parse_error("Expected quoted filename");
return; return;
} }
auto loc = location();
string file = parse_to('"'); string file = parse_to('"');
consume('"'); consume('"');
if (!reallyInclude) if (!reallyInclude)
@ -243,7 +244,7 @@ text_input_buffer::handle_include()
} }
if (!include_buffer) if (!include_buffer)
{ {
parse_error("Unable to locate input file"); loc.report_error("Unable to locate input file");
return; return;
} }
input_stack.push(std::move(include_buffer)); input_stack.push(std::move(include_buffer));
@ -1214,7 +1215,7 @@ input_buffer::buffer_for_file(const string &path, bool warn)
close(source); close(source);
return 0; return 0;
} }
std::unique_ptr<input_buffer> b(new mmap_input_buffer(source, std::string(path))); std::unique_ptr<input_buffer> b(new mmap_input_buffer(source, string(path)));
close(source); close(source);
return b; return b;
} }

View File

@ -31,6 +31,7 @@
*/ */
#include <string> #include <string>
#include <functional>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <ctype.h> #include <ctype.h>
@ -121,28 +122,28 @@ push_string(byte_buffer &buffer, const string &s, bool escapes)
} }
} }
std::string dirname(const string &s) namespace {
string
dirbasename(std::function<char*(char*)> fn, const string &s)
{ {
if (s == string()) if (s == string())
{ {
return string(); return string();
} }
char *str = strdup(s.c_str()); std::unique_ptr<char, decltype(free)*> str = {strdup(s.c_str()), free};
string dn(::dirname(str)); string dn(fn(str.get()));
free(str);
return dn; return dn;
} }
}
std::string basename(const string &s) string dirname(const string &s)
{ {
if (s == string()) return dirbasename(::dirname, s);
{ }
return string();
} string basename(const string &s)
char *str = strdup(s.c_str()); {
string bn(::basename(str)); return dirbasename(::basename, s);
free(str);
return bn;
} }
} // namespace dtc } // namespace dtc