diff options
author | Ed Maste <emaste@FreeBSD.org> | 2016-10-07 12:57:35 +0000 |
---|---|---|
committer | Ed Maste <emaste@FreeBSD.org> | 2016-10-07 12:57:35 +0000 |
commit | bbe31b709a653884e18995a1c97cdafd7392999a (patch) | |
tree | 2b60bb0dec872fb21ab458d982666a493b40e7ee /usr.bin | |
parent | f71d08566cb81e8f76c1bae56cbbfd51d2b2a784 (diff) | |
download | src-bbe31b709a653884e18995a1c97cdafd7392999a.tar.gz src-bbe31b709a653884e18995a1c97cdafd7392999a.zip |
Improvements to BSD-licensed DTC.
- Numerous crash and bug fixes
- Improved warning and error messages
- Permit multiple labels on nodes and properties
- Fix node@address references
- Add support for /delete-node/
- Consume whitespace after a node
- Read the next token before the second /memreserve/
- Fix parsing of whitespace
- Clean up /delete-node/ and add support for /delete-property/
- Handle /delete-node/ specifying a unit address
Obtained from: https://github.com/davidchisnall/dtc @df5ede4
Notes
Notes:
svn path=/head/; revision=306806
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/dtc/checking.cc | 46 | ||||
-rw-r--r-- | usr.bin/dtc/checking.hh | 42 | ||||
-rw-r--r-- | usr.bin/dtc/dtb.cc | 52 | ||||
-rw-r--r-- | usr.bin/dtc/dtb.hh | 36 | ||||
-rw-r--r-- | usr.bin/dtc/dtc.1 | 4 | ||||
-rw-r--r-- | usr.bin/dtc/dtc.cc | 41 | ||||
-rw-r--r-- | usr.bin/dtc/fdt.cc | 551 | ||||
-rw-r--r-- | usr.bin/dtc/fdt.hh | 142 | ||||
-rw-r--r-- | usr.bin/dtc/input_buffer.cc | 624 | ||||
-rw-r--r-- | usr.bin/dtc/input_buffer.hh | 363 | ||||
-rw-r--r-- | usr.bin/dtc/string.cc | 182 | ||||
-rw-r--r-- | usr.bin/dtc/util.hh | 25 |
12 files changed, 1373 insertions, 735 deletions
diff --git a/usr.bin/dtc/checking.cc b/usr.bin/dtc/checking.cc index 26cbbe2caa5e..13f14399365b 100644 --- a/usr.bin/dtc/checking.cc +++ b/usr.bin/dtc/checking.cc @@ -33,7 +33,7 @@ #include "checking.hh" #include <stdio.h> - +using std::string; namespace dtc { @@ -44,6 +44,30 @@ namespace checking namespace { + struct deleted_node_checker : public checker + { + deleted_node_checker(const char *name) : checker(name) {} + virtual bool check_node(device_tree *, const node_ptr &n) + { + auto &deleted = n->deleted_child_nodes(); + if (deleted.empty()) + { + return true; + } + bool plural = deleted.size() > 1; + string errmsg("Attempts to delete "); + errmsg += plural ? "nodes" : "node"; + errmsg += " that "; + errmsg += plural ? "were" : "was"; + errmsg += " not added in merge: "; + for (auto &d : deleted) + { + errmsg += d; + } + report_error(errmsg.c_str()); + return false; + } + }; /** * Checker that verifies that every node that has children has * #address-cells and #size-cells properties. @@ -73,16 +97,16 @@ namespace } if (found_size && found_address) { - break; + break; } } if (!found_address) { - report_error("Missing #address-cells property"); + report_error("Missing #address-cells property"); } if (!found_size) { - report_error("Missing #size-cells property"); + report_error("Missing #size-cells property"); } return found_address && found_size; } @@ -126,11 +150,11 @@ checker::report_error(const char *errmsg) for (auto &p : path) { putc('/', stderr); - p.first.dump(); + puts(p.first.c_str()); if (!(p.second.empty())) { putc('@', stderr); - p.second.dump(); + puts(p.second.c_str()); } } fprintf(stderr, " [-W%s]\n", checker_name); @@ -167,7 +191,7 @@ property_size_checker::check(device_tree *, const node_ptr &, property_ptr p) template<property_value::value_type T> void -check_manager::add_property_type_checker(const char *name, string prop) +check_manager::add_property_type_checker(const char *name, const string &prop) { checkers.insert(std::make_pair(string(name), new property_type_checker<T>(name, prop))); @@ -175,7 +199,7 @@ check_manager::add_property_type_checker(const char *name, string prop) void check_manager::add_property_size_checker(const char *name, - string prop, + const string &prop, uint32_t size) { checkers.insert(std::make_pair(string(name), @@ -207,6 +231,8 @@ check_manager::check_manager() add_property_size_checker("type-phandle", string("phandle"), 4); disabled_checkers.insert(std::make_pair(string("cells-attributes"), new address_cells_checker("cells-attributes"))); + checkers.insert(std::make_pair(string("deleted-nodes"), + new deleted_node_checker("deleted-nodes"))); } bool @@ -225,7 +251,7 @@ check_manager::run_checks(device_tree *tree, bool keep_going) } bool -check_manager::disable_checker(string name) +check_manager::disable_checker(const string &name) { auto checker = checkers.find(name); if (checker != checkers.end()) @@ -239,7 +265,7 @@ check_manager::disable_checker(string name) } bool -check_manager::enable_checker(string name) +check_manager::enable_checker(const string &name) { auto checker = disabled_checkers.find(name); if (checker != disabled_checkers.end()) diff --git a/usr.bin/dtc/checking.hh b/usr.bin/dtc/checking.hh index e3b3d451789f..d833ffd64a18 100644 --- a/usr.bin/dtc/checking.hh +++ b/usr.bin/dtc/checking.hh @@ -32,7 +32,7 @@ #ifndef _CHECKING_HH_ #define _CHECKING_HH_ -#include "string.hh" +#include <string> #include "fdt.hh" namespace dtc @@ -58,7 +58,7 @@ class checker /** * The name of the checker. This is used for printing error messages * and for enabling / disabling specific checkers from the command - * line. + * line. */ const char *checker_name; /** @@ -118,18 +118,18 @@ class property_checker : public checker /** * The name of the property that this checker is looking for. */ - string key; + std::string key; public: /** * Implementation of the generic property-checking method that checks - * for a property with the name specified in the constructor + * for a property with the name specified in the constructor. */ virtual bool check_property(device_tree *tree, const node_ptr &n, property_ptr p); /** * Constructor. Takes the name of the checker and the name of the * property to check. */ - property_checker(const char* name, string property_name) + property_checker(const char* name, const std::string &property_name) : checker(name), key(property_name) {} /** * The check method, which subclasses should implement. @@ -147,7 +147,7 @@ struct property_type_checker : public property_checker * Constructor, takes the name of the checker and the name of the * property to check as arguments. */ - property_type_checker(const char* name, string property_name) : + property_type_checker(const char* name, const std::string &property_name) : property_checker(name, property_name) {} virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0; }; @@ -158,7 +158,7 @@ struct property_type_checker : public property_checker template<> struct property_type_checker <property_value::EMPTY> : public property_checker { - property_type_checker(const char* name, string property_name) : + property_type_checker(const char* name, const std::string &property_name) : property_checker(name, property_name) {} virtual bool check(device_tree *, const node_ptr &, property_ptr p) { @@ -173,7 +173,7 @@ struct property_type_checker <property_value::EMPTY> : public property_checker template<> struct property_type_checker <property_value::STRING> : public property_checker { - property_type_checker(const char* name, string property_name) : + property_type_checker(const char* name, const std::string &property_name) : property_checker(name, property_name) {} virtual bool check(device_tree *, const node_ptr &, property_ptr p) { @@ -188,7 +188,7 @@ template<> struct property_type_checker <property_value::STRING_LIST> : public property_checker { - property_type_checker(const char* name, string property_name) : + property_type_checker(const char* name, const std::string &property_name) : property_checker(name, property_name) {} virtual bool check(device_tree *, const node_ptr &, property_ptr p) { @@ -211,11 +211,11 @@ struct property_type_checker <property_value::STRING_LIST> : template<> struct property_type_checker <property_value::PHANDLE> : public property_checker { - property_type_checker(const char* name, string property_name) : + property_type_checker(const char* name, const std::string &property_name) : property_checker(name, property_name) {} virtual bool check(device_tree *tree, const node_ptr &, property_ptr p) { - return (p->begin() + 1 == p->end()) && + return (p->begin() + 1 == p->end()) && (tree->referenced_node(*p->begin()) != 0); } }; @@ -234,7 +234,9 @@ struct property_size_checker : public property_checker * Constructor, takes the name of the checker, the name of the property * to check, and its expected size as arguments. */ - property_size_checker(const char* name, string property_name, uint32_t bytes) + property_size_checker(const char* name, + const std::string &property_name, + uint32_t bytes) : property_checker(name, property_name), size(bytes) {} /** * Check, validates that the property has the correct size. @@ -254,26 +256,26 @@ class check_manager * disabling checkers from the command line. When this manager runs, * it will only run the checkers from this map. */ - std::unordered_map<string, checker*> checkers; + std::unordered_map<std::string, checker*> checkers; /** * The disabled checkers. Moving checkers to this list disables them, * but allows them to be easily moved back. */ - std::unordered_map<string, checker*> disabled_checkers; + std::unordered_map<std::string, checker*> disabled_checkers; /** * Helper function for adding a property value checker. */ template<property_value::value_type T> - void add_property_type_checker(const char *name, string prop); + void add_property_type_checker(const char *name, const std::string &prop); /** * Helper function for adding a simple type checker. */ - void add_property_type_checker(const char *name, string prop); + void add_property_type_checker(const char *name, const std::string &prop); /** * Helper function for adding a property value checker. */ void add_property_size_checker(const char *name, - string prop, + const std::string &prop, uint32_t size); public: /** @@ -292,11 +294,11 @@ class check_manager /** * Disables the named checker. */ - bool disable_checker(string name); + bool disable_checker(const std::string &name); /** - * Enables the named checker. + * Enables the named checker. */ - bool enable_checker(string name); + bool enable_checker(const std::string &name); }; } // namespace checking diff --git a/usr.bin/dtc/dtb.cc b/usr.bin/dtc/dtb.cc index bbcf76d751aa..b18674c7fa22 100644 --- a/usr.bin/dtc/dtb.cc +++ b/usr.bin/dtc/dtb.cc @@ -36,6 +36,7 @@ #include <stdio.h> #include <unistd.h> +using std::string; namespace dtc { @@ -51,9 +52,9 @@ void output_writer::write_data(byte_buffer b) } void -binary_writer::write_string(string name) +binary_writer::write_string(const string &name) { - name.push_to_buffer(buffer); + push_string(buffer, name); // Trailing nul buffer.push_back(0); } @@ -98,15 +99,6 @@ binary_writer::size() } void -asm_writer::write_string(const char *c) -{ - while (*c) - { - buffer.push_back((uint8_t)*(c++)); - } -} - -void asm_writer::write_line(const char *c) { if (byte_count != 0) @@ -142,34 +134,44 @@ asm_writer::write_byte(uint8_t b) } void -asm_writer::write_label(string name) +asm_writer::write_label(const string &name) { write_line("\t.globl "); - name.push_to_buffer(buffer); + push_string(buffer, name); buffer.push_back('\n'); - name.push_to_buffer(buffer); + push_string(buffer, name); buffer.push_back(':'); buffer.push_back('\n'); buffer.push_back('_'); - name.push_to_buffer(buffer); + push_string(buffer, name); buffer.push_back(':'); buffer.push_back('\n'); } void -asm_writer::write_comment(string name) +asm_writer::write_comment(const string &name) { write_line("\t/* "); - name.push_to_buffer(buffer); + push_string(buffer, name); write_string(" */\n"); } void -asm_writer::write_string(string name) +asm_writer::write_string(const char *c) +{ + while (*c) + { + buffer.push_back((uint8_t)*(c++)); + } +} + + +void +asm_writer::write_string(const string &name) { write_line("\t.string \""); - name.push_to_buffer(buffer); + push_string(buffer, name); write_line("\"\n"); bytes_written += name.size() + 1; } @@ -231,8 +233,8 @@ asm_writer::size() void header::write(output_writer &out) { - out.write_label(string("dt_blob_start")); - out.write_label(string("dt_header")); + out.write_label("dt_blob_start"); + out.write_label("dt_header"); out.write_comment("magic"); out.write_data(magic); out.write_comment("totalsize"); @@ -275,7 +277,7 @@ header::read_dtb(input_buffer &input) input.consume_binary(size_dt_struct); } uint32_t -string_table::add_string(string str) +string_table::add_string(const string &str) { auto old = string_offsets.find(str); if (old == string_offsets.end()) @@ -296,13 +298,13 @@ string_table::add_string(string str) void string_table::write(dtb::output_writer &writer) { - writer.write_comment(string("Strings table.")); - writer.write_label(string("dt_strings_start")); + writer.write_comment("Strings table."); + writer.write_label("dt_strings_start"); for (auto &i : strings) { writer.write_string(i); } - writer.write_label(string("dt_strings_end")); + writer.write_label("dt_strings_end"); } } // namespace dtb diff --git a/usr.bin/dtc/dtb.hh b/usr.bin/dtc/dtb.hh index 184369ba4c15..4fece0d39747 100644 --- a/usr.bin/dtc/dtb.hh +++ b/usr.bin/dtc/dtb.hh @@ -33,10 +33,13 @@ #ifndef _DTB_HH_ #define _DTB_HH_ #include <map> -#include "string.hh" +#include <string> #include <assert.h> +#include "input_buffer.hh" +#include "util.hh" + namespace dtc { /** @@ -121,16 +124,16 @@ struct output_writer * assembly output, where the labels become symbols that can be * resolved at link time. */ - virtual void write_label(string name) = 0; + virtual void write_label(const std::string &name) = 0; /** * Writes a comment into the output stream. Useful only when debugging * the output. */ - virtual void write_comment(string name) = 0; + virtual void write_comment(const std::string &name) = 0; /** * Writes a string. A nul terminator is implicitly added. */ - virtual void write_string(string name) = 0; + virtual void write_string(const std::string &name) = 0; /** * Writes a single 8-bit value. */ @@ -186,12 +189,12 @@ class binary_writer : public output_writer * The binary format does not support labels, so this method * does nothing. */ - virtual void write_label(string) {} + virtual void write_label(const std::string &) {} /** * Comments are ignored by the binary writer. */ - virtual void write_comment(string) {} - virtual void write_string(string name); + virtual void write_comment(const std::string&) {} + virtual void write_string(const std::string &name); virtual void write_data(uint8_t v); virtual void write_data(uint32_t v); virtual void write_data(uint64_t v); @@ -224,11 +227,15 @@ class asm_writer : public output_writer uint32_t bytes_written; /** - * Writes a C string directly to the output as-is. This is mainly used - * for writing directives. + * Writes a string directly to the output as-is. This is the function that + * performs the real output. */ void write_string(const char *c); /** + * Write a string to the output. + */ + void write_string(const std::string &c); + /** * Writes the string, starting on a new line. */ void write_line(const char *c); @@ -239,9 +246,8 @@ class asm_writer : public output_writer void write_byte(uint8_t b); public: asm_writer() : byte_count(0), bytes_written(0) {} - virtual void write_label(string name); - virtual void write_comment(string name); - virtual void write_string(string name); + virtual void write_label(const std::string &name); + virtual void write_comment(const std::string &name); virtual void write_data(uint8_t v); virtual void write_data(uint32_t v); virtual void write_data(uint64_t v); @@ -328,14 +334,14 @@ class string_table { /** * Map from strings to their offset. */ - std::map<string, uint32_t> string_offsets; + std::map<std::string, uint32_t> string_offsets; /** * The strings, in the order in which they should be written to the * output. The order must be stable - adding another string must not * change the offset of any that we have already referenced - and so we * simply write the strings in the order that they are passed. */ - std::vector<string> strings; + std::vector<std::string> strings; /** * The current size of the strings section. */ @@ -351,7 +357,7 @@ class string_table { * will return its existing offset, otherwise it will return a new * offset. */ - uint32_t add_string(string str); + uint32_t add_string(const std::string &str); /** * Writes the strings table to the specified output. */ diff --git a/usr.bin/dtc/dtc.1 b/usr.bin/dtc/dtc.1 index afb94f763e08..c2fd68155f25 100644 --- a/usr.bin/dtc/dtc.1 +++ b/usr.bin/dtc/dtc.1 @@ -237,6 +237,10 @@ Checks that all nodes with children have both and .Va #size-cells properties. +.It deleted-nodes +Checks that all +.Va /delete-node/ +statements refer to nodes that are merged. .El .Sh EXAMPLES The command: diff --git a/usr.bin/dtc/dtc.cc b/usr.bin/dtc/dtc.cc index 269c90e5e274..3b1867c1f273 100644 --- a/usr.bin/dtc/dtc.cc +++ b/usr.bin/dtc/dtc.cc @@ -42,8 +42,10 @@ #include "fdt.hh" #include "checking.hh" +#include "util.hh" using namespace dtc; +using std::string; /** * The current major version of the tool. @@ -58,7 +60,7 @@ int version_minor = 4; */ int version_patch = 0; -static void usage(const char* argv0) +static void usage(const string &argv0) { fprintf(stderr, "Usage:\n" "\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]" @@ -67,7 +69,7 @@ static void usage(const char* argv0) "[-O output_format]\n" "\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]" "[-V blob_version]\n" - "\t\t-W [no-]checker_name] input_file\n", basename((char*)argv0)); + "\t\t-W [no-]checker_name] input_file\n", basename(argv0).c_str()); } /** @@ -90,9 +92,8 @@ main(int argc, char **argv) const char *in_file = "-"; FILE *depfile = 0; bool debug_mode = false; - void (device_tree::*write_fn)(int) = &device_tree::write_binary; - void (device_tree::*read_fn)(const char*, FILE*) = - &device_tree::parse_dts; + auto write_fn = &device_tree::write_binary; + auto read_fn = &device_tree::parse_dts; uint32_t boot_cpu; bool boot_cpu_specified = false; bool keep_going = false; @@ -115,12 +116,12 @@ main(int argc, char **argv) return EXIT_SUCCESS; case 'I': { - string arg = string(optarg); - if (arg == string("dtb")) + string arg(optarg); + if (arg == "dtb") { read_fn = &device_tree::parse_dtb; } - else if (arg == string("dts")) + else if (arg == "dts") { read_fn = &device_tree::parse_dts; } @@ -133,16 +134,16 @@ main(int argc, char **argv) } case 'O': { - string arg = string(optarg); - if (arg == string("dtb")) + string arg(optarg); + if (arg == "dtb") { write_fn = &device_tree::write_binary; } - else if (arg == string("asm")) + else if (arg == "asm") { write_fn = &device_tree::write_asm; } - else if (arg == string("dts")) + else if (arg == "dts") { write_fn = &device_tree::write_dts; } @@ -168,7 +169,7 @@ main(int argc, char **argv) debug_mode = true; break; case 'V': - if (string(optarg) != string("17")) + if (string(optarg) != "17") { fprintf(stderr, "Unknown output format version: %s\n", optarg); return EXIT_FAILURE; @@ -180,7 +181,7 @@ main(int argc, char **argv) { fclose(depfile); } - if (string(optarg) == string("-")) + if (string(optarg) == "-") { depfile = stdout; } @@ -197,16 +198,16 @@ main(int argc, char **argv) } case 'H': { - string arg = string(optarg); - if (arg == string("both")) + string arg(optarg); + if (arg == "both") { tree.set_phandle_format(device_tree::BOTH); } - else if (arg == string("epapr")) + else if (arg == "epapr") { tree.set_phandle_format(device_tree::EPAPR); } - else if (arg == string("linux")) + else if (arg == "linux") { tree.set_phandle_format(device_tree::LINUX); } @@ -229,7 +230,7 @@ main(int argc, char **argv) case 'W': case 'E': { - string arg = string(optarg); + string arg(optarg); if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0)) { arg = string(optarg+3); @@ -307,7 +308,7 @@ main(int argc, char **argv) } if (!(tree.is_valid() || keep_going)) { - fprintf(stderr, "Failed to parse tree. Unhappy face!\n"); + fprintf(stderr, "Failed to parse tree.\n"); return EXIT_FAILURE; } clock_t c2 = clock(); diff --git a/usr.bin/dtc/fdt.cc b/usr.bin/dtc/fdt.cc index 23476ae53dc4..41c627d3fd90 100644 --- a/usr.bin/dtc/fdt.cc +++ b/usr.bin/dtc/fdt.cc @@ -36,6 +36,7 @@ #include "dtb.hh" #include <algorithm> +#include <sstream> #include <ctype.h> #include <fcntl.h> @@ -48,6 +49,8 @@ #include <sys/stat.h> #include <errno.h> +using std::string; + namespace dtc { @@ -78,7 +81,7 @@ property_value::push_to_buffer(byte_buffer &buffer) } else { - string_data.push_to_buffer(buffer, true); + push_string(buffer, string_data, true); // Trailing nul buffer.push_back(0); } @@ -172,7 +175,7 @@ property_value::write_as_string(FILE *file) putc('"', file); if (byte_data.empty()) { - string_data.print(file); + fputs(string_data.c_str(), file); } else { @@ -240,31 +243,32 @@ property_value::write_as_bytes(FILE *file) } void -property::parse_string(input_buffer &input) +property::parse_string(text_input_buffer &input) { property_value v; - assert(input[0] == '"'); + assert(*input == '"'); ++input; - const char *start = (const char*)input; - int length = 0; - while (char c = input[0]) + std::vector<char> bytes; + bool isEscaped = false; + while (char c = *input) { - if (c == '"' && input[-1] != '\\') + if (c == '"' && !isEscaped) { input.consume('"'); break; } + isEscaped = (c == '\\'); + bytes.push_back(c); ++input; - ++length; } - v.string_data = string(start, length); + v.string_data = string(bytes.begin(), bytes.end()); values.push_back(v); } void -property::parse_cells(input_buffer &input, int cell_size) +property::parse_cells(text_input_buffer &input, int cell_size) { - assert(input[0] == '<'); + assert(*input == '<'); ++input; property_value v; input.next_token(); @@ -282,9 +286,18 @@ property::parse_cells(input_buffer &input, int cell_size) return; } input.next_token(); - // FIXME: We should support full paths here, but we - // don't. - string referenced = string::parse_node_name(input); + bool isPath = false; + string referenced; + if (!input.consume('{')) + { + referenced = input.parse_node_name(); + } + else + { + referenced = input.parse_to('}'); + input.consume('}'); + isPath = true; + } if (referenced.empty()) { input.parse_error("Expected node name"); @@ -343,9 +356,9 @@ property::parse_cells(input_buffer &input, int cell_size) } void -property::parse_bytes(input_buffer &input) +property::parse_bytes(text_input_buffer &input) { - assert(input[0] == '['); + assert(*input == '['); ++input; property_value v; input.next_token(); @@ -370,13 +383,13 @@ property::parse_bytes(input_buffer &input) } void -property::parse_reference(input_buffer &input) +property::parse_reference(text_input_buffer &input) { - assert(input[0] == '&'); + assert(*input == '&'); ++input; input.next_token(); property_value v; - v.string_data = string::parse_node_name(input); + v.string_data = input.parse_node_name(); if (v.string_data.empty()) { input.parse_error("Expected node name"); @@ -400,7 +413,7 @@ property::property(input_buffer &structs, input_buffer &strings) } // Find the name input_buffer name_buffer = strings.buffer_from_offset(name_offset); - if (name_buffer.empty()) + if (name_buffer.finished()) { fprintf(stderr, "Property name offset %" PRIu32 " is past the end of the strings table\n", @@ -408,7 +421,7 @@ property::property(input_buffer &structs, input_buffer &strings) valid = false; return; } - key = string(name_buffer); + key = name_buffer.parse_to(0); // If we're empty, do not push anything as value. if (!length) @@ -429,7 +442,7 @@ property::property(input_buffer &structs, input_buffer &strings) values.push_back(v); } -void property::parse_define(input_buffer &input, define_map *defines) +void property::parse_define(text_input_buffer &input, define_map *defines) { input.consume('$'); if (!defines) @@ -438,7 +451,7 @@ void property::parse_define(input_buffer &input, define_map *defines) valid = false; return; } - string name = string::parse_property_name(input); + string name = input.parse_property_name(); define_map::iterator found; if ((name == string()) || ((found = defines->find(name)) == defines->end())) @@ -450,15 +463,15 @@ void property::parse_define(input_buffer &input, define_map *defines) values.push_back((*found).second->values[0]); } -property::property(input_buffer &input, - string k, - string l, +property::property(text_input_buffer &input, + string &&k, + string_set &&l, bool semicolonTerminated, - define_map *defines) : key(k), label(l), valid(true) + define_map *defines) : key(k), labels(l), valid(true) { do { input.next_token(); - switch (input[0]) + switch (*input) { case '$': { @@ -487,7 +500,7 @@ property::property(input_buffer &input, } if (!valid) return; input.next_token(); - if (input[0] != '<') + if (*input != '<') { input.parse_error("/bits/ directive is only valid on arrays"); valid = false; @@ -534,10 +547,14 @@ property::parse_dtb(input_buffer &structs, input_buffer &strings) } property_ptr -property::parse(input_buffer &input, string key, string label, +property::parse(text_input_buffer &input, string &&key, string_set &&label, bool semicolonTerminated, define_map *defines) { - property_ptr p(new property(input, key, label, semicolonTerminated, defines)); + property_ptr p(new property(input, + std::move(key), + std::move(label), + semicolonTerminated, + defines)); if (!p->valid) { p = nullptr; @@ -596,14 +613,16 @@ property::write_dts(FILE *file, int indent) { putc('\t', file); } - if (label != string()) +#ifdef PRINT_LABELS + for (auto &l : labels) { - label.print(file); + fputs(l.c_str(), file); fputs(": ", file); } +#endif if (key != string()) { - key.print(file); + fputs(key.c_str(), file); } if (!values.empty()) { @@ -637,7 +656,7 @@ property::write_dts(FILE *file, int indent) } string -node::parse_name(input_buffer &input, bool &is_property, const char *error) +node::parse_name(text_input_buffer &input, bool &is_property, const char *error) { if (!valid) { @@ -646,9 +665,9 @@ node::parse_name(input_buffer &input, bool &is_property, const char *error) input.next_token(); if (is_property) { - return string::parse_property_name(input); + return input.parse_property_name(); } - string n = string::parse_node_or_property_name(input, is_property); + string n = input.parse_node_or_property_name(is_property); if (n.empty()) { if (n.empty()) @@ -672,25 +691,23 @@ node::visit(std::function<void(node&)> fn) node::node(input_buffer &structs, input_buffer &strings) : valid(true) { - const char *name_start = (const char*)structs; - int name_length = 0; + std::vector<char> bytes; while (structs[0] != '\0' && structs[0] != '@') { - name_length++; + bytes.push_back(structs[0]); ++structs; } - name = string(name_start, name_length); + name = string(bytes.begin(), bytes.end()); + bytes.clear(); if (structs[0] == '@') { ++structs; - name_start = (const char*)structs; - name_length = 0; while (structs[0] != '\0') { - name_length++; + bytes.push_back(structs[0]); ++structs; } - unit_address = string(name_start, name_length); + unit_address = string(bytes.begin(), bytes.end()); } ++structs; uint32_t token; @@ -747,8 +764,12 @@ node::node(input_buffer &structs, input_buffer &strings) : valid(true) return; } -node::node(input_buffer &input, string n, string l, string a, define_map *defines) : - label(l), name(n), unit_address(a), valid(true) +node::node(text_input_buffer &input, + string &&n, + std::unordered_set<string> &&l, + string &&a, + define_map *defines) + : labels(l), name(n), unit_address(a), valid(true) { if (!input.consume('{')) { @@ -760,15 +781,60 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define // flag set if we find any characters that are only in // the property name character set, not the node bool is_property = false; - string child_name, child_label, child_address; + string child_name, child_address; + std::unordered_set<string> child_labels; + auto parse_delete = [&](const char *expected, bool at) + { + if (child_name == string()) + { + input.parse_error(expected); + valid = false; + return; + } + input.next_token(); + if (at && input.consume('@')) + { + child_name += '@'; + child_name += parse_name(input, is_property, "Expected unit address"); + } + if (!input.consume(';')) + { + input.parse_error("Expected semicolon"); + valid = false; + return; + } + input.next_token(); + }; + if (input.consume("/delete-node/")) + { + input.next_token(); + child_name = input.parse_node_name(); + parse_delete("Expected node name", true); + if (valid) + { + deleted_children.insert(child_name); + } + continue; + } + if (input.consume("/delete-property/")) + { + input.next_token(); + child_name = input.parse_property_name(); + parse_delete("Expected property name", false); + if (valid) + { + deleted_props.insert(child_name); + } + continue; + } child_name = parse_name(input, is_property, "Expected property or node name"); - if (input.consume(':')) + while (input.consume(':')) { // Node labels can contain any characters? The // spec doesn't say, so we guess so... is_property = false; - child_label = child_name; + child_labels.insert(std::move(child_name)); child_name = parse_name(input, is_property, "Expected property or node name"); } if (input.consume('@')) @@ -783,8 +849,8 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define // If we're parsing a property, then we must actually do that. if (input.consume('=')) { - property_ptr p = property::parse(input, child_name, - child_label, true, defines); + property_ptr p = property::parse(input, std::move(child_name), + std::move(child_labels), true, defines); if (p == 0) { valid = false; @@ -794,10 +860,10 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define props.push_back(p); } } - else if (!is_property && input[0] == ('{')) + else if (!is_property && *input == ('{')) { - node_ptr child = node::parse(input, child_name, - child_label, child_address, defines); + node_ptr child = node::parse(input, std::move(child_name), + std::move(child_labels), std::move(child_address), defines); if (child) { children.push_back(std::move(child)); @@ -809,15 +875,16 @@ node::node(input_buffer &input, string n, string l, string a, define_map *define } else if (input.consume(';')) { - props.push_back(property_ptr(new property(child_name, child_label))); + props.push_back(property_ptr(new property(std::move(child_name), std::move(child_labels)))); } else { - input.parse_error("Error parsing property."); + input.parse_error("Error parsing property. Expected property value"); valid = false; } input.next_token(); } + input.next_token(); input.consume(';'); } @@ -849,13 +916,17 @@ node::sort() } node_ptr -node::parse(input_buffer &input, - string name, - string label, - string address, +node::parse(text_input_buffer &input, + string &&name, + string_set &&label, + string &&address, define_map *defines) { - node_ptr n(new node(input, name, label, address, defines)); + node_ptr n(new node(input, + std::move(name), + std::move(label), + std::move(address), + defines)); if (!n->valid) { n = 0; @@ -875,7 +946,7 @@ node::parse_dtb(input_buffer &structs, input_buffer &strings) } property_ptr -node::get_property(string key) +node::get_property(const string &key) { for (auto &i : props) { @@ -890,9 +961,9 @@ node::get_property(string key) void node::merge_node(node_ptr other) { - if (!other->label.empty()) + for (auto &l : other->labels) { - label = other->label; + labels.insert(l); } // Note: this is an O(n*m) operation. It might be sensible to // optimise this if we find that there are nodes with very @@ -933,6 +1004,30 @@ 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 @@ -940,11 +1035,11 @@ node::write(dtb::output_writer &writer, dtb::string_table &strings) { writer.write_token(dtb::FDT_BEGIN_NODE); byte_buffer name_buffer; - name.push_to_buffer(name_buffer); + push_string(name_buffer, name); if (unit_address != string()) { name_buffer.push_back('@'); - unit_address.push_to_buffer(name_buffer); + push_string(name_buffer, unit_address); } writer.write_comment(name); writer.write_data(name_buffer); @@ -968,20 +1063,19 @@ node::write_dts(FILE *file, int indent) putc('\t', file); } #ifdef PRINT_LABELS - if (label != string()) + for (auto &label : labels) { - label.print(file); - fputs(": ", file); + fprintf(file, "%s: ", label.c_str()); } #endif if (name != string()) { - name.print(file); + fputs(name.c_str(), file); } if (unit_address != string()) { putc('@', file); - unit_address.print(file); + fputs(unit_address.c_str(), file); } fputs(" {\n\n", file); for (auto p : properties()) @@ -1002,26 +1096,27 @@ node::write_dts(FILE *file, int indent) void device_tree::collect_names_recursive(node_ptr &n, node_path &path) { - string name = n->label; path.push_back(std::make_pair(n->name, n->unit_address)); - if (name != string()) + for (const string &name : n->labels) { - if (node_names.find(name) == node_names.end()) + if (name != string()) { - node_names.insert(std::make_pair(name, n.get())); - node_paths.insert(std::make_pair(name, path)); - } - else - { - node_names[name] = (node*)-1; - auto i = node_paths.find(name); - if (i != node_paths.end()) + auto iter = node_names.find(name); + if (iter == node_names.end()) + { + node_names.insert(std::make_pair(name, n.get())); + node_paths.insert(std::make_pair(name, path)); + } + else { - node_paths.erase(name); + node_names.erase(iter); + auto i = node_paths.find(name); + if (i != node_paths.end()) + { + node_paths.erase(name); + } + fprintf(stderr, "Label not unique: %s. References to this label will not be resolved.\n", name.c_str()); } - fprintf(stderr, "Label not unique: "); - name.dump(); - fprintf(stderr, ". References to this label will not be resolved."); } } for (auto &c : n->child_nodes()) @@ -1044,14 +1139,12 @@ device_tree::collect_names_recursive(node_ptr &n, node_path &path) cross_references.push_back(&v); } } - if (p->get_key() == string("phandle") || - p->get_key() == string("linux,phandle")) + if ((p->get_key() == "phandle") || + (p->get_key() == "linux,phandle")) { if (p->begin()->byte_data.size() != 4) { - fprintf(stderr, "Invalid phandle value for node "); - n->name.dump(); - fprintf(stderr, ". Should be a 4-byte value.\n"); + fprintf(stderr, "Invalid phandle value for node %s. Should be a 4-byte value.\n", n->name.c_str()); valid = false; } else @@ -1080,16 +1173,21 @@ device_tree::resolve_cross_references() for (auto *pv : cross_references) { node_path path = node_paths[pv->string_data]; - // Skip the first name in the path. It's always "", and implicitly / - for (auto p=path.begin()+1, pe=path.end() ; p!=pe ; ++p) + auto p = path.begin(); + auto pe = path.end(); + if (p != pe) { - pv->byte_data.push_back('/'); - p->first.push_to_buffer(pv->byte_data); - if (!(p->second.empty())) + // Skip the first name in the path. It's always "", and implicitly / + for (++p ; p!=pe ; ++p) { - pv->byte_data.push_back('@'); - p->second.push_to_buffer(pv->byte_data); - pv->byte_data.push_back(0); + pv->byte_data.push_back('/'); + push_string(pv->byte_data, p->first); + if (!(p->second.empty())) + { + pv->byte_data.push_back('@'); + push_string(pv->byte_data, p->second); + pv->byte_data.push_back(0); + } } } } @@ -1117,12 +1215,72 @@ device_tree::resolve_cross_references() for (auto &i : sorted_phandles) { string target_name = i->string_data; - node *target = node_names[target_name]; - if (target == 0) + node *target = nullptr; + string possible; + // If the node name is a path, then look it up by following the path, + // otherwise jump directly to the named node. + if (target_name[0] == '/') + { + std::string path; + target = root.get(); + std::istringstream ss(target_name); + string path_element; + // Read the leading / + std::getline(ss, path_element, '/'); + // Iterate over path elements + while (!ss.eof()) + { + path += '/'; + std::getline(ss, path_element, '/'); + std::istringstream nss(path_element); + string node_name, node_address; + std::getline(nss, node_name, '@'); + std::getline(nss, node_address, '@'); + node *next = nullptr; + for (auto &c : target->child_nodes()) + { + if (c->name == node_name) + { + if (c->unit_address == node_address) + { + next = c.get(); + break; + } + else + { + possible = path + c->name; + if (c->unit_address != string()) + { + possible += '@'; + possible += c->unit_address; + } + } + } + } + path += node_name; + if (node_address != string()) + { + path += '@'; + path += node_address; + } + target = next; + if (target == nullptr) + { + break; + } + } + } + else + { + target = node_names[target_name]; + } + if (target == nullptr) { - fprintf(stderr, "Failed to find node with label: "); - target_name.dump(); - fprintf(stderr, "\n"); + 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; } @@ -1153,13 +1311,13 @@ device_tree::resolve_cross_references() push_big_endian(v.byte_data, phandle++); if (phandle_node_name == BOTH || phandle_node_name == LINUX) { - p.reset(new property(string("linux,phandle"))); + p.reset(new property("linux,phandle")); p->add_value(v); target->add_property(p); } if (phandle_node_name == BOTH || phandle_node_name == EPAPR) { - p.reset(new property(string("phandle"))); + p.reset(new property("phandle")); p->add_value(v); target->add_property(p); } @@ -1169,87 +1327,10 @@ device_tree::resolve_cross_references() } } -bool -device_tree::parse_include(input_buffer &input, - const std::string &dir, - std::vector<node_ptr> &roots, - FILE *depfile, - bool &read_header) -{ - if (!input.consume("/include/")) - { - return false; - } - bool reallyInclude = true; - if (input.consume("if ")) - { - input.next_token(); - string name = string::parse_property_name(input); - // XXX: Error handling - if (defines.find(name) == defines.end()) - { - reallyInclude = false; - } - input.consume('/'); - } - input.next_token(); - if (!input.consume('"')) - { - input.parse_error("Expected quoted filename"); - valid = false; - return false; - } - int length = 0; - while (input[length] != '"') length++; - - std::string file((const char*)input, length); - std::string include_file = dir + '/' + file; - input.consume(file.c_str()); - if (!reallyInclude) - { - input.consume('"'); - input.next_token(); - return true; - } - - input_buffer *include_buffer = buffer_for_file(include_file.c_str(), false); - - if (include_buffer == 0) - { - for (auto i : include_paths) - { - include_file = i + '/' + file; - include_buffer = buffer_for_file(include_file.c_str()); - if (include_buffer != 0) - { - break; - } - } - } - if (depfile != 0) - { - putc(' ', depfile); - fputs(include_file.c_str(), depfile); - } - if (include_buffer == 0) - { - input.parse_error("Unable to locate input file"); - input.consume('"'); - input.next_token(); - valid = false; - return true; - } - input.consume('"'); - input.next_token(); - parse_file(*include_buffer, dir, roots, depfile, read_header); - return true; -} void -device_tree::parse_file(input_buffer &input, - const std::string &dir, +device_tree::parse_file(text_input_buffer &input, std::vector<node_ptr> &roots, - FILE *depfile, bool &read_header) { input.next_token(); @@ -1264,9 +1345,8 @@ device_tree::parse_file(input_buffer &input, { input.parse_error("Expected /dts-v1/; version string"); } - while(parse_include(input, dir, roots, depfile, read_header)) {} // Read any memory reservations - while(input.consume("/memreserve/")) + while (input.consume("/memreserve/")) { unsigned long long start, len; input.next_token(); @@ -1280,23 +1360,22 @@ device_tree::parse_file(input_buffer &input, input.next_token(); input.consume(';'); reservations.push_back(reservation(start, len)); + input.next_token(); } - input.next_token(); - while(parse_include(input, dir, roots, depfile, read_header)) {} while (valid && !input.finished()) { node_ptr n; if (input.consume('/')) { input.next_token(); - n = node::parse(input, string(), string(), string(), &defines); + n = node::parse(input, string(), string_set(), string(), &defines); } else if (input.consume('&')) { input.next_token(); - string name = string::parse_node_name(input); + string name = input.parse_node_name(); input.next_token(); - n = node::parse(input, name, string(), string(), &defines); + n = node::parse(input, std::move(name), string_set(), string(), &defines); } else { @@ -1311,50 +1390,7 @@ device_tree::parse_file(input_buffer &input, valid = false; } input.next_token(); - while(parse_include(input, dir, roots, depfile, read_header)) {} - } -} - -input_buffer* -device_tree::buffer_for_file(const char *path, bool warn) -{ - if (string(path) == string("-")) - { - input_buffer *b = new stream_input_buffer(); - if (b) - { - std::unique_ptr<input_buffer> ptr(b); - buffers.push_back(std::move(ptr)); - } - return b; } - int source = open(path, O_RDONLY); - if (source == -1) - { - if (warn) - { - fprintf(stderr, "Unable to open file '%s'. %s\n", path, strerror(errno)); - } - return 0; - } - struct stat st; - if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode)) - { - fprintf(stderr, "File %s is a directory\n", path); - close(source); - return 0; - } - input_buffer *b = new mmap_input_buffer(source); - // Keep the buffer that owns the memory around for the lifetime - // of this FDT. Ones simply referring to it may have shorter - // lifetimes. - if (b) - { - std::unique_ptr<input_buffer> ptr(b); - buffers.push_back(std::move(ptr)); - } - close(source); - return b; } template<class writer> void @@ -1475,9 +1511,9 @@ device_tree::write_dts(int fd) } void -device_tree::parse_dtb(const char *fn, FILE *) +device_tree::parse_dtb(const string &fn, FILE *) { - input_buffer *in = buffer_for_file(fn); + auto in = input_buffer::buffer_for_file(fn); if (in == 0) { valid = false; @@ -1532,19 +1568,27 @@ device_tree::parse_dtb(const char *fn, FILE *) } void -device_tree::parse_dts(const char *fn, FILE *depfile) +device_tree::parse_dts(const string &fn, FILE *depfile) { - input_buffer *in = buffer_for_file(fn); - std::string dir(dirname((char*)fn)); - if (in == 0) + auto in = input_buffer::buffer_for_file(fn); + if (!in) { valid = false; return; } std::vector<node_ptr> roots; - input_buffer &input = *in; + std::unordered_set<string> defnames; + for (auto &i : defines) + { + defnames.insert(i.first); + } + text_input_buffer input(std::move(in), + std::move(defnames), + std::vector<string>(include_paths), + dirname(fn), + depfile); bool read_header = false; - parse_file(input, dir, roots, depfile, read_header); + parse_file(input, roots, read_header); switch (roots.size()) { case 0: @@ -1575,11 +1619,12 @@ device_tree::parse_dts(const char *fn, FILE *depfile) } if (existing == node_names.end()) { - fprintf(stderr, "Unable to merge node: "); - name.dump(); - fprintf(stderr, "\n"); + fprintf(stderr, "Unable to merge node: %s\n", name.c_str()); + } + else + { + existing->second->merge_node(std::move(node)); } - existing->second->merge_node(std::move(node)); } } } @@ -1602,9 +1647,15 @@ bool device_tree::parse_define(const char *def) return false; } string name(def, val-def); + string name_copy = name; val++; - input_buffer in = input_buffer(val, strlen(val)); - property_ptr p = property::parse(in, name, string(), false); + std::unique_ptr<input_buffer> raw(new input_buffer(val, strlen(val))); + text_input_buffer in(std::move(raw), + std::unordered_set<string>(), + std::vector<string>(), + std::string(), + nullptr); + property_ptr p = property::parse(in, std::move(name_copy), string_set(), false); if (p) defines[name] = p; return (bool)p; diff --git a/usr.bin/dtc/fdt.hh b/usr.bin/dtc/fdt.hh index 5f53f5a52e0c..993403b49910 100644 --- a/usr.bin/dtc/fdt.hh +++ b/usr.bin/dtc/fdt.hh @@ -39,7 +39,7 @@ #include <functional> #include "util.hh" -#include "string.hh" +#include "input_buffer.hh" namespace dtc { @@ -65,7 +65,11 @@ typedef std::unique_ptr<node> node_ptr; /** * Map from macros to property pointers. */ -typedef std::unordered_map<string, property_ptr> define_map; +typedef std::unordered_map<std::string, property_ptr> define_map; +/** + * Set of strings used for label names. + */ +typedef std::unordered_set<std::string> string_set; /** * Properties may contain a number of different value, each with a different * label. This class encapsulates a single value. @@ -75,12 +79,12 @@ struct property_value /** * The label for this data. This is usually empty. */ - string label; + std::string label; /** * If this value is a string, or something resolved from a string (a * reference) then this contains the source string. */ - string string_data; + std::string string_data; /** * The data that should be written to the final output. */ @@ -186,7 +190,7 @@ struct property_value /** * Default constructor, specifying the label of the value. */ - property_value(string l=string()) : label(l), type(UNKNOWN) {} + property_value(std::string l=std::string()) : label(l), type(UNKNOWN) {} /** * Writes the data for this value into an output buffer. */ @@ -250,11 +254,11 @@ class property /** * The name of this property. */ - string key; + std::string key; /** - * An optional label. + * Zero or more labels. */ - string label; + string_set labels; /** * The values in this property. */ @@ -267,15 +271,15 @@ class property /** * Parses a string property value, i.e. a value enclosed in double quotes. */ - void parse_string(input_buffer &input); + void parse_string(text_input_buffer &input); /** * Parses one or more 32-bit values enclosed in angle brackets. */ - void parse_cells(input_buffer &input, int cell_size); + void parse_cells(text_input_buffer &input, int cell_size); /** * Parses an array of bytes, contained within square brackets. */ - void parse_bytes(input_buffer &input); + void parse_bytes(text_input_buffer &input); /** * Parses a reference. This is a node label preceded by an ampersand * symbol, which should expand to the full path to that node. @@ -284,11 +288,11 @@ class property * a node name, however dtc assumes that it is a label, and so we * follow their interpretation for compatibility. */ - void parse_reference(input_buffer &input); + void parse_reference(text_input_buffer &input); /** * Parse a predefined macro definition for a property. */ - void parse_define(input_buffer &input, define_map *defines); + void parse_define(text_input_buffer &input, define_map *defines); /** * Constructs a new property from two input buffers, pointing to the * struct and strings tables in the device tree blob, respectively. @@ -299,21 +303,21 @@ class property /** * Parses a new property from the input buffer. */ - property(input_buffer &input, - string k, - string l, + property(text_input_buffer &input, + std::string &&k, + string_set &&l, bool terminated, define_map *defines); public: /** * Creates an empty property. */ - property(string k, string l=string()) : key(k), label(l), valid(true) - {} + property(std::string &&k, string_set &&l=string_set()) + : key(k), labels(l), valid(true) {} /** * Copy constructor. */ - property(property &p) : key(p.key), label(p.label), values(p.values), + property(property &p) : key(p.key), labels(p.labels), values(p.values), valid(p.valid) {} /** * Factory method for constructing a new property. Attempts to parse a @@ -321,15 +325,15 @@ class property * error, this will return 0. */ static property_ptr parse_dtb(input_buffer &structs, - input_buffer &strings); + input_buffer &strings); /** * Factory method for constructing a new property. Attempts to parse a * property from the input, and returns it on success. On any parse * error, this will return 0. */ - static property_ptr parse(input_buffer &input, - string key, - string label=string(), + static property_ptr parse(text_input_buffer &input, + std::string &&key, + string_set &&labels=string_set(), bool semicolonTerminated=true, define_map *defines=0); /** @@ -360,7 +364,7 @@ class property /** * Returns the key for this property. */ - inline string get_key() + inline std::string get_key() { return key; } @@ -386,19 +390,19 @@ class node { public: /** - * The label for this node, if any. Node labels are used as the + * The labels for this node, if any. Node labels are used as the * targets for cross references. */ - string label; + std::unordered_set<std::string> labels; /** * The name of the node. */ - string name; + std::string name; /** * The unit address of the node, which is optionally written after the * name followed by an at symbol. */ - string unit_address; + std::string unit_address; /** * The type for the property vector. */ @@ -439,6 +443,14 @@ class node */ std::vector<node_ptr> children; /** + * Children that should be deleted from this node when merging. + */ + std::unordered_set<std::string> deleted_children; + /** + * Properties that should be deleted from this node when merging. + */ + std::unordered_set<std::string> deleted_props; + /** * A flag indicating whether this node is valid. This is set to false * if an error occurs during parsing. */ @@ -447,9 +459,9 @@ class node * Parses a name inside a node, writing the string passed as the last * argument as an error if it fails. */ - string parse_name(input_buffer &input, - bool &is_property, - const char *error); + std::string parse_name(text_input_buffer &input, + bool &is_property, + const char *error); /** * Constructs a new node from two input buffers, pointing to the struct * and strings tables in the device tree blob, respectively. @@ -461,7 +473,11 @@ class node * node. The name, and optionally label and unit address, should have * already been parsed. */ - node(input_buffer &input, string n, string l, string a, define_map*); + node(text_input_buffer &input, + std::string &&n, + std::unordered_set<std::string> &&l, + std::string &&a, + define_map*); /** * Comparison function for properties, used when sorting the properties * vector. Orders the properties based on their names. @@ -498,10 +514,32 @@ class node { return children.end(); } + /** + * Returns a range suitable for use in a range-based for loop describing + * the children of this node. + */ inline child_range child_nodes() { return child_range(*this); } + /** + * Accessor for the deleted children. + */ + inline const std::unordered_set<std::string> &deleted_child_nodes() + { + return deleted_children; + } + /** + * Accessor for the deleted properties + */ + inline const std::unordered_set<std::string> &deleted_properties() + { + return deleted_props; + } + /** + * Returns a range suitable for use in a range-based for loop describing + * the properties of this node. + */ inline property_range properties() { return property_range(*this); @@ -527,10 +565,10 @@ class node * cursor on the open brace of the property, after the name and so on * have been parsed. */ - static node_ptr parse(input_buffer &input, - string name, - string label=string(), - string address=string(), + static node_ptr parse(text_input_buffer &input, + std::string &&name, + std::unordered_set<std::string> &&label=std::unordered_set<std::string>(), + std::string &&address=std::string(), define_map *defines=0); /** * Factory method for constructing a new node. Attempts to parse a @@ -544,7 +582,7 @@ class node * Returns a property corresponding to the specified key, or 0 if this * node does not contain a property of that name. */ - property_ptr get_property(string key); + property_ptr get_property(const std::string &key); /** * Adds a new property to this node. */ @@ -588,7 +626,7 @@ class device_tree * Type used for node paths. A node path is sequence of names and unit * addresses. */ - typedef std::vector<std::pair<string,string> > node_path; + typedef std::vector<std::pair<std::string,std::string> > node_path; /** * Name that we should use for phandle nodes. */ @@ -630,13 +668,13 @@ class device_tree * Mapping from names to nodes. Only unambiguous names are recorded, * duplicate names are stored as (node*)-1. */ - std::unordered_map<string, node*> node_names; + std::unordered_map<std::string, node*> node_names; /** * A map from labels to node paths. When resolving cross references, * we look up referenced nodes in this and replace the cross reference * with the full path to its target. */ - std::unordered_map<string, node_path> node_paths; + std::unordered_map<std::string, node_path> node_paths; /** * A collection of property values that are references to other nodes. * These should be expanded to the full path of their targets. @@ -651,7 +689,7 @@ class device_tree /** * The names of nodes that target phandles. */ - std::unordered_set<string> phandle_targets; + std::unordered_set<std::string> phandle_targets; /** * A collection of input buffers that we are using. These input * buffers are the ones that own their memory, and so we must preserve @@ -718,31 +756,15 @@ class device_tree */ void resolve_cross_references(); /** - * Parse a top-level include directive. - */ - bool parse_include(input_buffer &input, - const std::string &dir, - std::vector<node_ptr> &roots, - FILE *depfile, - bool &read_header); - /** * Parses a dts file in the given buffer and adds the roots to the parsed * set. The `read_header` argument indicates whether the header has * already been read. Some dts files place the header in an include, * rather than in the top-level file. */ - void parse_file(input_buffer &input, - const std::string &dir, + void parse_file(text_input_buffer &input, std::vector<node_ptr> &roots, - FILE *depfile, bool &read_header); /** - * Allocates a new mmap()'d input buffer for use in parsing. This - * object then keeps a reference to it, ensuring that it is not - * deallocated until the device tree is destroyed. - */ - input_buffer *buffer_for_file(const char *path, bool warn=true); - /** * Template function that writes a dtb blob using the specified writer. * The writer defines the output format (assembly, blob). */ @@ -779,12 +801,12 @@ class device_tree * Constructs a device tree from the specified file name, referring to * a file that contains a device tree blob. */ - void parse_dtb(const char *fn, FILE *depfile); + void parse_dtb(const std::string &fn, FILE *depfile); /** * Constructs a device tree from the specified file name, referring to * a file that contains device tree source. */ - void parse_dts(const char *fn, FILE *depfile); + void parse_dts(const std::string &fn, FILE *depfile); /** * Returns whether this tree is valid. */ diff --git a/usr.bin/dtc/input_buffer.cc b/usr.bin/dtc/input_buffer.cc index 6d48adc66847..e8fcdf6c5b8d 100644 --- a/usr.bin/dtc/input_buffer.cc +++ b/usr.bin/dtc/input_buffer.cc @@ -46,37 +46,215 @@ #include <sys/stat.h> #include <sys/mman.h> #include <assert.h> +#include <fcntl.h> +#include <unistd.h> #ifndef MAP_PREFAULT_READ #define MAP_PREFAULT_READ 0 #endif +using std::string; + +namespace +{ +/** + * Subclass of input_buffer that mmap()s a file and owns the resulting memory. + * When this object is destroyed, the memory is unmapped. + */ +struct mmap_input_buffer : public dtc::input_buffer +{ + string fn; + const string &filename() const override + { + return fn; + } + /** + * Constructs a new buffer from the file passed in as a file + * descriptor. + */ + mmap_input_buffer(int fd, string &&filename); + /** + * Unmaps the buffer, if one exists. + */ + virtual ~mmap_input_buffer(); +}; +/** + * Input buffer read from standard input. This is used for reading device tree + * blobs and source from standard input. It reads the entire input into + * malloc'd memory, so will be very slow for large inputs. DTS and DTB files + * are very rarely more than 10KB though, so this is probably not a problem. + */ +struct stream_input_buffer : public dtc::input_buffer +{ + const string &filename() const override + { + static string n = "<standard input>"; + return n; + } + /** + * The buffer that will store the data read from the standard input. + */ + std::vector<char> b; + /** + * Constructs a new buffer from the standard input. + */ + stream_input_buffer(); +}; + +mmap_input_buffer::mmap_input_buffer(int fd, std::string &&filename) + : input_buffer(0, 0), fn(filename) +{ + struct stat sb; + if (fstat(fd, &sb)) + { + perror("Failed to stat file"); + } + size = sb.st_size; + buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE | + MAP_PREFAULT_READ, fd, 0); + if (buffer == MAP_FAILED) + { + perror("Failed to mmap file"); + exit(EXIT_FAILURE); + } +} + +mmap_input_buffer::~mmap_input_buffer() +{ + if (buffer != 0) + { + munmap((void*)buffer, size); + } +} + +stream_input_buffer::stream_input_buffer() : input_buffer(0, 0) +{ + int c; + while ((c = fgetc(stdin)) != EOF) + { + b.push_back(c); + } + buffer = b.data(); + size = b.size(); +} + +} // Anonymous namespace + + namespace dtc { + +void +input_buffer::skip_to(char c) +{ + while ((cursor < size) && (buffer[cursor] != c)) + { + cursor++; + } +} + +void +text_input_buffer::skip_to(char c) +{ + while (!finished() && (*(*this) != c)) + { + ++(*this); + } +} + void -input_buffer::skip_spaces() +text_input_buffer::skip_spaces() { - if (cursor >= size) { return; } - if (cursor < 0) { return; } - char c = buffer[cursor]; + if (finished()) { return; } + char c = *(*this); + bool last_nl = false; while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f') || (c == '\v') || (c == '\r')) { - cursor++; - if (cursor > size) + last_nl = ((c == '\n') || (c == '\r')); + ++(*this); + if (finished()) { c = '\0'; } else { - c = buffer[cursor]; + c = *(*this); + } + } + // Skip C preprocessor leftovers + if ((c == '#') && ((cursor == 0) || last_nl)) + { + skip_to('\n'); + skip_spaces(); + } + if (consume("/include/")) + { + handle_include(); + skip_spaces(); + } +} + +void +text_input_buffer::handle_include() +{ + bool reallyInclude = true; + if (consume("if ")) + { + next_token(); + string name = parse_property_name(); + if (defines.count(name) > 0) + { + reallyInclude = true; + } + consume('/'); + } + next_token(); + if (!consume('"')) + { + parse_error("Expected quoted filename"); + return; + } + string file = parse_to('"'); + consume('"'); + if (!reallyInclude) + { + return; + } + string include_file = dir + '/' + file; + auto include_buffer = input_buffer::buffer_for_file(include_file, false); + if (include_buffer == 0) + { + for (auto i : include_paths) + { + include_file = i + '/' + file; + include_buffer = input_buffer::buffer_for_file(include_file, false); + if (include_buffer != 0) + { + break; + } } } + if (depfile) + { + putc(' ', depfile); + fputs(include_file.c_str(), depfile); + } + if (!include_buffer) + { + parse_error("Unable to locate input file"); + return; + } + input_stack.push(std::move(include_buffer)); } input_buffer input_buffer::buffer_from_offset(int offset, int s) { + if (offset < 0) + { + return input_buffer(); + } if (s == 0) { s = size - offset; @@ -104,7 +282,7 @@ input_buffer::consume(const char *str) { for (int i=0 ; i<len ; ++i) { - if (str[i] != buffer[cursor + i]) + if (str[i] != (*this)[i]) { return false; } @@ -124,7 +302,7 @@ input_buffer::consume_integer(unsigned long long &outInt) { return false; } - char *end=0; + char *end= const_cast<char*>(&buffer[size]); outInt = strtoull(&buffer[cursor], &end, 0); if (end == &buffer[cursor]) { @@ -146,15 +324,27 @@ typedef unsigned long long valty; */ struct expression { + typedef text_input_buffer::source_location source_location; + /** + * The type that is returned when computing the result. The boolean value + * indicates whether this is a valid expression. + * + * FIXME: Once we can use C++17, this should be `std::optional`. + */ + typedef std::pair<valty, bool> result; /** * Evaluate this node, taking into account operator precedence. */ - virtual valty operator()() = 0; + virtual result operator()() = 0; /** * Returns the precedence of this node. Lower values indicate higher * precedence. */ virtual int precedence() = 0; + /** + * Constructs an expression, storing the location where it was created. + */ + expression(source_location l) : loc(l) {} virtual ~expression() {} #ifndef NDEBUG /** @@ -163,7 +353,8 @@ struct expression */ void dump(bool nl=false) { - if (this == nullptr) + void *ptr = this; + if (ptr == nullptr) { std::cerr << "{nullptr}\n"; return; @@ -180,6 +371,8 @@ struct expression */ virtual void dump_impl() = 0; #endif + protected: + source_location loc; }; /** @@ -194,9 +387,9 @@ class terminal_expr : public expression /** * Evaluate. Trivially returns the value that this class wraps. */ - valty operator()() override + result operator()() override { - return val; + return {val, true}; } int precedence() override { @@ -206,7 +399,7 @@ class terminal_expr : public expression /** * Constructor. */ - terminal_expr(valty v) : val(v) {} + terminal_expr(source_location l, valty v) : expression(l), val(v) {} #ifndef NDEBUG void dump_impl() override { std::cerr << val; } #endif @@ -224,7 +417,8 @@ struct paren_expression : public expression /** * Constructor. Takes the child expression as the only argument. */ - paren_expression(expression_ptr p) : subexpr(std::move(p)) {} + paren_expression(source_location l, expression_ptr p) : expression(l), + subexpr(std::move(p)) {} int precedence() override { return 0; @@ -232,7 +426,7 @@ struct paren_expression : public expression /** * Evaluate - just forwards to the underlying expression. */ - valty operator()() override + result operator()() override { return (*subexpr)(); } @@ -260,10 +454,15 @@ class unary_operator : public expression * The subexpression for this unary operator. */ expression_ptr subexpr; - valty operator()() override + result operator()() override { Op op; - return op((*subexpr)()); + result s = (*subexpr)(); + if (!s.second) + { + return s; + } + return {op(s.first), true}; } /** * All unary operators have the same precedence. They are all evaluated @@ -274,7 +473,8 @@ class unary_operator : public expression return 3; } public: - unary_operator(expression_ptr p) : subexpr(std::move(p)) {} + unary_operator(source_location l, expression_ptr p) : + expression(l), subexpr(std::move(p)) {} #ifndef NDEBUG void dump_impl() override { @@ -290,6 +490,7 @@ class unary_operator : public expression */ struct binary_operator_base : public expression { + using expression::expression; /** * The left side of the expression. */ @@ -323,10 +524,16 @@ struct binary_operator_base : public expression template<int Precedence, class Op> struct binary_operator : public binary_operator_base { - valty operator()() override + result operator()() override { Op op; - return op((*lhs)(), (*rhs)()); + result l = (*lhs)(); + result r = (*rhs)(); + if (!(l.second && r.second)) + { + return {0, false}; + } + return {op(l.first, r.first), true}; } int precedence() override { @@ -337,10 +544,11 @@ struct binary_operator : public binary_operator_base * Constructor. Takes the name of the operator as an argument, for * debugging. Only stores it in debug mode. */ - binary_operator(const char *) {} + binary_operator(source_location l, const char *) : expression(l) {} #else const char *opName; - binary_operator(const char *o) : opName(o) {} + binary_operator(source_location l, const char *o) : + binary_operator_base(l), opName(o) {} void dump_impl() override { lhs->dump(); @@ -368,9 +576,16 @@ class ternary_conditional_operator : public expression * The expression that this evaluates to if the condition is false. */ expression_ptr rhs; - valty operator()() override + result operator()() override { - return (*cond)() ? (*lhs)() : (*rhs)(); + result c = (*cond)(); + result l = (*lhs)(); + result r = (*rhs)(); + if (!(l.second && r.second && c.second)) + { + return {0, false}; + } + return c.first ? l : r; } int precedence() override { @@ -390,10 +605,12 @@ class ternary_conditional_operator : public expression } #endif public: - ternary_conditional_operator(expression_ptr c, + ternary_conditional_operator(source_location sl, + expression_ptr c, expression_ptr l, expression_ptr r) : - cond(std::move(c)), lhs(std::move(l)), rhs(std::move(r)) {} + expression(sl), cond(std::move(c)), lhs(std::move(l)), + rhs(std::move(r)) {} }; template<typename T> @@ -430,36 +647,53 @@ struct bit_not } }; +template<typename T> +struct divmod : public binary_operator<5, T> +{ + using binary_operator<5, T>::binary_operator; + using binary_operator_base::result; + result operator()() override + { + result r = (*binary_operator_base::rhs)(); + if (r.second && (r.first == 0)) + { + expression::loc.report_error("Division by zero"); + return {0, false}; + } + return binary_operator<5, T>::operator()(); + } +}; + } // anonymous namespace -expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs) +expression_ptr text_input_buffer::parse_binary_expression(expression_ptr lhs) { next_token(); binary_operator_base *expr = nullptr; - char op = ((*this)[0]); + char op = *(*this); + source_location l = location(); switch (op) { default: return lhs; case '+': - expr = new binary_operator<6, std::plus<valty>>("+"); + expr = new binary_operator<6, std::plus<valty>>(l, "+"); break; case '-': - expr = new binary_operator<6, std::minus<valty>>("-"); + expr = new binary_operator<6, std::minus<valty>>(l, "-"); break; case '%': - expr = new binary_operator<5, std::modulus<valty>>("%"); + expr = new divmod<std::modulus<valty>>(l, "/"); break; case '*': - expr = new binary_operator<5, std::multiplies<valty>>("*"); + expr = new binary_operator<5, std::multiplies<valty>>(l, "*"); break; case '/': - expr = new binary_operator<5, std::divides<valty>>("/"); + expr = new divmod<std::divides<valty>>(l, "/"); break; case '<': - cursor++; - switch ((*this)[0]) + switch (peek()) { default: parse_error("Invalid operator"); @@ -467,20 +701,20 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs) case ' ': case '(': case '0'...'9': - cursor--; - expr = new binary_operator<8, std::less<valty>>("<"); + expr = new binary_operator<8, std::less<valty>>(l, "<"); break; case '=': - expr = new binary_operator<8, std::less_equal<valty>>("<="); + ++(*this); + expr = new binary_operator<8, std::less_equal<valty>>(l, "<="); break; case '<': - expr = new binary_operator<7, lshift<valty>>("<<"); + ++(*this); + expr = new binary_operator<7, lshift<valty>>(l, "<<"); break; } break; case '>': - cursor++; - switch ((*this)[0]) + switch (peek()) { default: parse_error("Invalid operator"); @@ -488,54 +722,54 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs) case '(': case ' ': case '0'...'9': - cursor--; - expr = new binary_operator<8, std::greater<valty>>(">"); + expr = new binary_operator<8, std::greater<valty>>(l, ">"); break; case '=': - expr = new binary_operator<8, std::greater_equal<valty>>(">="); + ++(*this); + expr = new binary_operator<8, std::greater_equal<valty>>(l, ">="); break; case '>': - expr = new binary_operator<7, rshift<valty>>(">>"); + ++(*this); + expr = new binary_operator<7, rshift<valty>>(l, ">>"); break; return lhs; } break; case '=': - if ((*this)[1] != '=') + if (peek() != '=') { parse_error("Invalid operator"); return nullptr; } - consume('='); - expr = new binary_operator<9, std::equal_to<valty>>("=="); + expr = new binary_operator<9, std::equal_to<valty>>(l, "=="); break; case '!': - if ((*this)[1] != '=') + if (peek() != '=') { parse_error("Invalid operator"); return nullptr; } cursor++; - expr = new binary_operator<9, std::not_equal_to<valty>>("!="); + expr = new binary_operator<9, std::not_equal_to<valty>>(l, "!="); break; case '&': - if ((*this)[1] == '&') + if (peek() == '&') { - expr = new binary_operator<13, std::logical_and<valty>>("&&"); + expr = new binary_operator<13, std::logical_and<valty>>(l, "&&"); } else { - expr = new binary_operator<10, std::bit_and<valty>>("&"); + expr = new binary_operator<10, std::bit_and<valty>>(l, "&"); } break; case '|': - if ((*this)[1] == '|') + if (peek() == '|') { - expr = new binary_operator<12, std::logical_or<valty>>("||"); + expr = new binary_operator<12, std::logical_or<valty>>(l, "||"); } else { - expr = new binary_operator<14, std::bit_or<valty>>("|"); + expr = new binary_operator<14, std::bit_or<valty>>(l, "|"); } break; case '?': @@ -554,11 +788,11 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs) parse_error("Expected false condition for ternary operator"); return nullptr; } - return expression_ptr(new ternary_conditional_operator(std::move(lhs), + return expression_ptr(new ternary_conditional_operator(l, std::move(lhs), std::move(true_case), std::move(false_case))); } } - cursor++; + ++(*this); next_token(); expression_ptr e(expr); expression_ptr rhs(parse_expression()); @@ -584,19 +818,20 @@ expression_ptr input_buffer::parse_binary_expression(expression_ptr lhs) return e; } -expression_ptr input_buffer::parse_expression(bool stopAtParen) +expression_ptr text_input_buffer::parse_expression(bool stopAtParen) { next_token(); unsigned long long leftVal; expression_ptr lhs; - switch ((*this)[0]) + source_location l = location(); + switch (*(*this)) { case '0'...'9': if (!consume_integer(leftVal)) { return nullptr; } - lhs.reset(new terminal_expr(leftVal)); + lhs.reset(new terminal_expr(l, leftVal)); break; case '(': { @@ -606,7 +841,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) { return nullptr; } - lhs.reset(new paren_expression(std::move(subexpr))); + lhs.reset(new paren_expression(l, std::move(subexpr))); if (!consume(')')) { return nullptr; @@ -625,7 +860,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) { return nullptr; } - lhs.reset(new unary_operator<'+', unary_plus<valty>>(std::move(subexpr))); + lhs.reset(new unary_operator<'+', unary_plus<valty>>(l, std::move(subexpr))); break; } case '-': @@ -636,7 +871,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) { return nullptr; } - lhs.reset(new unary_operator<'-', std::negate<valty>>(std::move(subexpr))); + lhs.reset(new unary_operator<'-', std::negate<valty>>(l, std::move(subexpr))); break; } case '!': @@ -647,7 +882,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) { return nullptr; } - lhs.reset(new unary_operator<'!', std::logical_not<valty>>(std::move(subexpr))); + lhs.reset(new unary_operator<'!', std::logical_not<valty>>(l, std::move(subexpr))); break; } case '~': @@ -658,7 +893,7 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) { return nullptr; } - lhs.reset(new unary_operator<'~', bit_not<valty>>(std::move(subexpr))); + lhs.reset(new unary_operator<'~', bit_not<valty>>(l, std::move(subexpr))); break; } } @@ -670,9 +905,9 @@ expression_ptr input_buffer::parse_expression(bool stopAtParen) } bool -input_buffer::consume_integer_expression(unsigned long long &outInt) +text_input_buffer::consume_integer_expression(unsigned long long &outInt) { - switch ((*this)[0]) + switch (*(*this)) { case '(': { @@ -681,8 +916,13 @@ input_buffer::consume_integer_expression(unsigned long long &outInt) { return false; } - outInt = (*e)(); - return true; + auto r = (*e)(); + if (r.second) + { + outInt = r.first; + return true; + } + return false; } case '0'...'9': return consume_integer(outInt); @@ -703,58 +943,80 @@ input_buffer::consume_hex_byte(uint8_t &outByte) return true; } -input_buffer& -input_buffer::next_token() +text_input_buffer& +text_input_buffer::next_token() { + auto &self = *this; int start; do { start = cursor; skip_spaces(); + if (finished()) + { + return self; + } // Parse /* comments - if ((*this)[0] == '/' && (*this)[1] == '*') + if (*self == '/' && peek() == '*') { // eat the start of the comment - ++(*this); - ++(*this); + ++self; + ++self; do { // Find the ending * of */ - while ((**this != '\0') && (**this != '*')) + while ((*self != '\0') && (*self != '*') && !finished()) { - ++(*this); + ++self; } // Eat the * - ++(*this); - } while ((**this != '\0') && (**this != '/')); + ++self; + } while ((*self != '\0') && (*self != '/') && !finished()); // Eat the / - ++(*this); + ++self; } // Parse // comments - if (((*this)[0] == '/' && (*this)[1] == '/')) + if ((*self == '/' && peek() == '/')) { // eat the start of the comment - ++(*this); - ++(*this); + ++self; + ++self; // Find the ending of the line - while (**this != '\n') + while (*self != '\n' && !finished()) { - ++(*this); + ++self; } // Eat the \n - ++(*this); + ++self; } } while (start != cursor); - return *this; + return self; } void -input_buffer::parse_error(const char *msg) +text_input_buffer::parse_error(const char *msg) +{ + if (input_stack.empty()) + { + fprintf(stderr, "Error: %s\n", msg); + return; + } + input_buffer &b = *input_stack.top(); + parse_error(msg, b, b.cursor); +} +void +text_input_buffer::parse_error(const char *msg, + input_buffer &b, + int loc) { int line_count = 1; int line_start = 0; - int line_end = cursor; - for (int i=cursor ; i>0 ; --i) + int line_end = loc; + if (loc < 0 || loc > b.size) + { + return; + } + for (int i=loc ; i>0 ; --i) { - if (buffer[i] == '\n') + if (b.buffer[i] == '\n') { line_count++; if (line_start == 0) @@ -763,20 +1025,20 @@ input_buffer::parse_error(const char *msg) } } } - for (int i=cursor+1 ; i<size ; ++i) + for (int i=loc+1 ; i<b.size ; ++i) { - if (buffer[i] == '\n') + if (b.buffer[i] == '\n') { line_end = i; break; } } - fprintf(stderr, "Error on line %d: %s\n", line_count, msg); - fwrite(&buffer[line_start], line_end-line_start, 1, stderr); + fprintf(stderr, "Error at %s:%d:%d: %s\n", b.filename().c_str(), line_count, loc - line_start, msg); + fwrite(&b.buffer[line_start], line_end-line_start, 1, stderr); putc('\n', stderr); - for (int i=0 ; i<(cursor-line_start) ; ++i) + for (int i=0 ; i<(loc-line_start) ; ++i) { - char c = (buffer[i+line_start] == '\t') ? '\t' : ' '; + char c = (b.buffer[i+line_start] == '\t') ? '\t' : ' '; putc(c, stderr); } putc('^', stderr); @@ -791,40 +1053,168 @@ input_buffer::dump() } #endif -mmap_input_buffer::mmap_input_buffer(int fd) : input_buffer(0, 0) + +namespace { - struct stat sb; - if (fstat(fd, &sb)) +/** + * The source files are ASCII, so we provide a non-locale-aware version of + * isalpha. This is a class so that it can be used with a template function + * for parsing strings. + */ +struct is_alpha +{ + static inline bool check(const char c) { - perror("Failed to stat file"); + return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && + (c <= 'Z')); } - size = sb.st_size; - buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE | - MAP_PREFAULT_READ, fd, 0); - if (buffer == MAP_FAILED) +}; +/** + * Check whether a character is in the set allowed for node names. This is a + * class so that it can be used with a template function for parsing strings. + */ +struct is_node_name_character +{ + static inline bool check(const char c) { - perror("Failed to mmap file"); - exit(EXIT_FAILURE); + switch(c) + { + default: + return false; + case 'a'...'z': case 'A'...'Z': case '0'...'9': + case ',': case '.': case '+': case '-': + case '_': + return true; + } + } +}; +/** + * Check whether a character is in the set allowed for property names. This is + * a class so that it can be used with a template function for parsing strings. + */ +struct is_property_name_character +{ + static inline bool check(const char c) + { + switch(c) + { + default: + return false; + case 'a'...'z': case 'A'...'Z': case '0'...'9': + case ',': case '.': case '+': case '-': + case '_': case '#': + return true; + } + } +}; + +template<class T> +string parse(text_input_buffer &s) +{ + std::vector<char> bytes; + for (char c=*s ; T::check(c) ; c=*(++s)) + { + bytes.push_back(c); } + return string(bytes.begin(), bytes.end()); } -mmap_input_buffer::~mmap_input_buffer() +} + +string +text_input_buffer::parse_node_name() { - if (buffer != 0) + return parse<is_node_name_character>(*this); +} + +string +text_input_buffer::parse_property_name() +{ + return parse<is_property_name_character>(*this); +} + +string +text_input_buffer::parse_node_or_property_name(bool &is_property) +{ + if (is_property) { - munmap((void*)buffer, size); + return parse_property_name(); + } + std::vector<char> bytes; + for (char c=*(*this) ; is_node_name_character::check(c) ; c=*(++(*this))) + { + bytes.push_back(c); + } + for (char c=*(*this) ; is_property_name_character::check(c) ; c=*(++(*this))) + { + bytes.push_back(c); + is_property = true; } + return string(bytes.begin(), bytes.end()); } -stream_input_buffer::stream_input_buffer() : input_buffer(0, 0) +string +input_buffer::parse_to(char stop) { - int c; - while ((c = fgetc(stdin)) != EOF) + std::vector<char> bytes; + for (char c=*(*this) ; c != stop ; c=*(++(*this))) { - b.push_back(c); + bytes.push_back(c); } - buffer = b.data(); - size = b.size(); + return string(bytes.begin(), bytes.end()); +} + +string +text_input_buffer::parse_to(char stop) +{ + std::vector<char> bytes; + for (char c=*(*this) ; c != stop ; c=*(++(*this))) + { + if (finished()) + { + break; + } + bytes.push_back(c); + } + return string(bytes.begin(), bytes.end()); +} + +char +text_input_buffer::peek() +{ + return (*input_stack.top())[1]; +} + +std::unique_ptr<input_buffer> +input_buffer::buffer_for_file(const string &path, bool warn) +{ + if (path == "-") + { + std::unique_ptr<input_buffer> b(new stream_input_buffer()); + return b; + } + int source = open(path.c_str(), O_RDONLY); + if (source == -1) + { + if (warn) + { + fprintf(stderr, "Unable to open file '%s'. %s\n", path.c_str(), strerror(errno)); + } + return 0; + } + struct stat st; + if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode)) + { + if (warn) + { + fprintf(stderr, "File %s is a directory\n", path.c_str()); + } + close(source); + return 0; + } + std::unique_ptr<input_buffer> b(new mmap_input_buffer(source, std::string(path))); + close(source); + return b; } } // namespace dtc diff --git a/usr.bin/dtc/input_buffer.hh b/usr.bin/dtc/input_buffer.hh index 1a87911dc157..fada1e46d286 100644 --- a/usr.bin/dtc/input_buffer.hh +++ b/usr.bin/dtc/input_buffer.hh @@ -34,6 +34,9 @@ #define _INPUT_BUFFER_HH_ #include "util.hh" #include <assert.h> +#include <stack> +#include <string> +#include <unordered_set> namespace dtc { @@ -55,6 +58,7 @@ typedef std::unique_ptr<expression> expression_ptr; */ class input_buffer { + friend class text_input_buffer; protected: /** * The buffer. This class doesn't own the buffer, but the @@ -67,17 +71,6 @@ class input_buffer int size; private: /** - * Parse an expression. If `stopAtParen` is set, then only parse a number - * or a parenthetical expression, otherwise assume that either is the - * left-hand side of a binary expression and try to parse the right-hand - * side. - */ - expression_ptr parse_expression(bool stopAtParen=false); - /** - * Parse a binary expression, having already parsed the right-hand side. - */ - expression_ptr parse_binary_expression(expression_ptr lhs); - /** * The current place in the buffer where we are reading. This class * keeps a separate size, pointer, and cursor so that we can move * forwards and backwards and still have checks that we haven't fallen @@ -90,12 +83,27 @@ class input_buffer */ input_buffer(const char* b, int s, int c) : buffer(b), size(s), cursor(c) {} + public: /** - * Reads forward past any spaces. The DTS format is not whitespace - * sensitive and so we want to scan past whitespace when reading it. + * Returns the file name associated with this buffer. */ - void skip_spaces(); - public: + virtual const std::string &filename() const + { + static std::string s; + return s; + } + static std::unique_ptr<input_buffer> buffer_for_file(const std::string &path, + bool warn=true); + /** + * Skips all characters in the input until the specified character is + * encountered. + */ + void skip_to(char); + /** + * Parses up to a specified character and returns the intervening + * characters as a string. + */ + std::string parse_to(char); /** * Return whether all input has been consumed. */ @@ -125,13 +133,6 @@ class input_buffer */ input_buffer buffer_from_offset(int offset, int s=0); /** - * Returns true if this buffer has no unconsumed space in it. - */ - inline bool empty() - { - return cursor >= size; - } - /** * Dereferencing operator, allows the buffer to be treated as a char* * and dereferenced to give a character. This returns a null byte if * the cursor is out of range. @@ -164,23 +165,13 @@ class input_buffer return *this; } /** - * Cast to char* operator. Returns a pointer into the buffer that can - * be used for constructing strings. - */ - inline operator const char*() - { - if (cursor >= size) { return 0; } - if (cursor < 0) { return 0; } - return &buffer[cursor]; - } - /** * Consumes a character. Moves the cursor one character forward if the * next character matches the argument, returning true. If the current * character does not match the argument, returns false. */ inline bool consume(char c) { - if ((*this)[0] == c) + if (*(*this) == c) { ++(*this); return true; @@ -208,6 +199,13 @@ class input_buffer */ bool consume_integer_expression(unsigned long long &outInt); /** + * Consumes two hex digits and return the resulting byte via the first + * argument. If the next two characters are hex digits, returns true + * and advances the cursor. If not, then returns false and leaves the + * cursor in place. + */ + bool consume_hex_byte(uint8_t &outByte); + /** * Template function that consumes a binary value in big-endian format * from the input stream. Returns true and advances the cursor if * there is a value of the correct size. This function assumes that @@ -232,28 +230,15 @@ class input_buffer out = 0; for (int i=0 ; i<type_size ; ++i) { + if (size < cursor) + { + return false; + } out <<= 8; out |= (((T)buffer[cursor++]) & 0xff); } return true; } - /** - * Consumes two hex digits and return the resulting byte via the first - * argument. If the next two characters are hex digits, returns true - * and advances the cursor. If not, then returns false and leaves the - * cursor in place. - */ - bool consume_hex_byte(uint8_t &outByte); - /** - * Advances the cursor to the start of the next token, skipping - * comments and whitespace. If the cursor already points to the start - * of a token, then this function does nothing. - */ - input_buffer &next_token(); - /** - * Prints a message indicating the location of a parse error. - */ - void parse_error(const char *msg); #ifndef NDEBUG /** * Dumps the current cursor value and the unconsumed values in the @@ -278,37 +263,273 @@ inline bool input_buffer::consume_binary(uint8_t &out) } /** - * Subclass of input_buffer that mmap()s a file and owns the resulting memory. - * When this object is destroyed, the memory is unmapped. + * An input buffer subclass used for parsing DTS files. This manages a stack + * of input buffers to handle /input/ operations. */ -struct mmap_input_buffer : public input_buffer +class text_input_buffer { + std::unordered_set<std::string> defines; /** - * Constructs a new buffer from the file passed in as a file - * descriptor. + * The cursor is the input into the input stream where we are currently reading. */ - mmap_input_buffer(int fd); + int cursor = 0; /** - * Unmaps the buffer, if one exists. + * The current stack of includes. The current input is always from the top + * of the stack. */ - virtual ~mmap_input_buffer(); -}; -/** - * Input buffer read from standard input. This is used for reading device tree - * blobs and source from standard input. It reads the entire input into - * malloc'd memory, so will be very slow for large inputs. DTS and DTB files - * are very rarely more than 10KB though, so this is probably not a problem. - */ -struct stream_input_buffer : public input_buffer -{ + std::stack<std::shared_ptr<input_buffer>> input_stack; + /** + * + */ + const std::vector<std::string> include_paths; + /** + * Reads forward past any spaces. The DTS format is not whitespace + * sensitive and so we want to scan past whitespace when reading it. + */ + void skip_spaces(); + /** + * Returns the character immediately after the current one. + * + * This method does not look between files. + */ + char peek(); + /** + * If a /include/ token is encountered, then look up the corresponding + * input file, push it onto the input stack, and continue. + */ + void handle_include(); + /** + * The base directory for this file. + */ + const std::string dir; + /** + * The file where dependencies should be output. + */ + FILE *depfile; + public: + /** + * Construct a new text input buffer with the specified buffer as the start + * of parsing and the specified set of input paths for handling new + * inclusions. + */ + text_input_buffer(std::unique_ptr<input_buffer> &&b, + std::unordered_set<std::string> &&d, + std::vector<std::string> &&i, + const std::string directory, + FILE *deps) + : defines(d), include_paths(i), dir(directory), depfile(deps) + { + input_stack.push(std::move(b)); + } + /** + * Skips all characters in the input until the specified character is + * encountered. + */ + void skip_to(char); + /** + * Parse an expression. If `stopAtParen` is set, then only parse a number + * or a parenthetical expression, otherwise assume that either is the + * left-hand side of a binary expression and try to parse the right-hand + * side. + */ + expression_ptr parse_expression(bool stopAtParen=false); + /** + * Parse a binary expression, having already parsed the right-hand side. + */ + expression_ptr parse_binary_expression(expression_ptr lhs); + /** + * Return whether all input has been consumed. + */ + bool finished() + { + return input_stack.empty() || + ((input_stack.size() == 1) && input_stack.top()->finished()); + } /** - * The buffer that will store the data read from the standard input. + * Dereferencing operator. Returns the current character in the top input buffer. */ - std::vector<char> b; + inline char operator*() + { + if (input_stack.empty()) + { + return 0; + } + return *(*input_stack.top()); + } + /** + * Increments the cursor, iterating forward in the buffer. + */ + inline text_input_buffer &operator++() + { + if (input_stack.empty()) + { + return *this; + } + cursor++; + auto &top = *input_stack.top(); + ++top; + if (top.finished()) + { + input_stack.pop(); + } + return *this; + } + /** + * Consumes a character. Moves the cursor one character forward if the + * next character matches the argument, returning true. If the current + * character does not match the argument, returns false. + */ + inline bool consume(char c) + { + if (*(*this) == c) + { + ++(*this); + return true; + } + return false; + } + /** + * Consumes a string. If the (null-terminated) string passed as the + * argument appears in the input, advances the cursor to the end and + * returns true. Returns false if the string does not appear at the + * current point in the input. + * + * This method does not scan between files. + */ + bool consume(const char *str) + { + if (input_stack.empty()) + { + return false; + } + return input_stack.top()->consume(str); + } + /** + * Reads an integer in base 8, 10, or 16. Returns true and advances + * the cursor to the end of the integer if the cursor points to an + * integer, returns false and does not move the cursor otherwise. + * + * The parsed value is returned via the argument. + * + * This method does not scan between files. + */ + bool consume_integer(unsigned long long &outInt) + { + if (input_stack.empty()) + { + return false; + } + return input_stack.top()->consume_integer(outInt); + } + /** + * Reads an arithmetic expression (containing any of the normal C + * operators), evaluates it, and returns the result. + */ + bool consume_integer_expression(unsigned long long &outInt); + /** + * Consumes two hex digits and return the resulting byte via the first + * argument. If the next two characters are hex digits, returns true + * and advances the cursor. If not, then returns false and leaves the + * cursor in place. + * + * This method does not scan between files. + */ + bool consume_hex_byte(uint8_t &outByte) + { + if (input_stack.empty()) + { + return false; + } + return input_stack.top()->consume_hex_byte(outByte); + } + /** + * Returns the longest string in the input buffer starting at the + * current cursor and composed entirely of characters that are valid in + * node names. + */ + std::string parse_node_name(); + /** + * Returns the longest string in the input buffer starting at the + * current cursor and composed entirely of characters that are valid in + * property names. + */ + std::string parse_property_name(); + /** + * Parses either a node or a property name. If is_property is true on + * entry, then only property names are parsed. If it is false, then it + * will be set, on return, to indicate whether the parsed name is only + * valid as a property. + */ + std::string parse_node_or_property_name(bool &is_property); + /** + * Parses up to a specified character and returns the intervening + * characters as a string. + */ + std::string parse_to(char); + /** + * Advances the cursor to the start of the next token, skipping + * comments and whitespace. If the cursor already points to the start + * of a token, then this function does nothing. + */ + text_input_buffer &next_token(); + /** + * Location in the source file. This should never be interpreted by + * anything other than error reporting functions of this class. It will + * eventually become something more complex than an `int`. + */ + class source_location + { + friend class text_input_buffer; + /** + * The text buffer object that included `b`. + */ + text_input_buffer &buffer; + /** + * The underlying buffer that contains this location. + */ + std::shared_ptr<input_buffer> b; + /** + * The offset within the current buffer of the source location. + */ + int cursor; + source_location(text_input_buffer &buf) + : buffer(buf), + b(buf.input_stack.empty() ? nullptr : buf.input_stack.top()), + cursor(b ? b->cursor : 0) {} + public: + /** + * Report an error at this location. + */ + void report_error(const char *msg) + { + if (b) + { + buffer.parse_error(msg, *b, cursor); + } + else + { + buffer.parse_error(msg); + } + } + }; + /** + * Returns the current source location. + */ + source_location location() + { + return { *this }; + } + /** + * Prints a message indicating the location of a parse error. + */ + void parse_error(const char *msg); + private: /** - * Constructs a new buffer from the standard input. + * Prints a message indicating the location of a parse error, given a + * specified location. This is used when input has already moved beyond + * the location that caused the failure. */ - stream_input_buffer(); + void parse_error(const char *msg, input_buffer &b, int loc); }; } // namespace dtc diff --git a/usr.bin/dtc/string.cc b/usr.bin/dtc/string.cc index 283bafab2f69..afc7bfc456d6 100644 --- a/usr.bin/dtc/string.cc +++ b/usr.bin/dtc/string.cc @@ -30,150 +30,29 @@ * $FreeBSD$ */ -#include "string.hh" +#include <string> +#include <cstdio> +#include <cstdlib> #include <ctype.h> -#include <stdio.h> +#include <libgen.h> -namespace -{ -/** - * The source files are ASCII, so we provide a non-locale-aware version of - * isalpha. This is a class so that it can be used with a template function - * for parsing strings. - */ -struct is_alpha -{ - static inline bool check(const char c) - { - return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && - (c <= 'Z')); - } -}; -/** - * Check whether a character is in the set allowed for node names. This is a - * class so that it can be used with a template function for parsing strings. - */ -struct is_node_name_character -{ - static inline bool check(const char c) - { - switch(c) - { - default: - return false; - case 'a'...'z': case 'A'...'Z': case '0'...'9': - case ',': case '.': case '+': case '-': - case '_': - return true; - } - } -}; -/** - * Check whether a character is in the set allowed for property names. This is - * a class so that it can be used with a template function for parsing strings. - */ -struct is_property_name_character -{ - static inline bool check(const char c) - { - switch(c) - { - default: - return false; - case 'a'...'z': case 'A'...'Z': case '0'...'9': - case ',': case '.': case '+': case '-': - case '_': case '#': - return true; - } - } -}; +#include "util.hh" -} +using std::string; namespace dtc { -template<class T> string -string::parse(input_buffer &s) -{ - const char *start = s; - int l=0; - while (T::check(*s)) { l++; ++s; } - return string(start, l); -} - -string::string(input_buffer &s) : start((const char*)s), length(0) -{ - while(s[length] != '\0') - { - length++; - } -} - -string -string::parse_node_name(input_buffer &s) -{ - return parse<is_node_name_character>(s); -} - -string -string::parse_property_name(input_buffer &s) -{ - return parse<is_property_name_character>(s); -} -string -string::parse_node_or_property_name(input_buffer &s, bool &is_property) -{ - if (is_property) - { - return parse_property_name(s); - } - const char *start = s; - int l=0; - while (is_node_name_character::check(*s)) - { - l++; - ++s; - } - while (is_property_name_character::check(*s)) - { - l++; - ++s; - is_property = true; - } - return string(start, l); -} - -bool -string::operator==(const string& other) const -{ - return (length == other.length) && - (memcmp(start, other.start, length) == 0); -} - -bool -string::operator==(const char *other) const -{ - return strncmp(other, start, length) == 0; -} - -bool -string::operator<(const string& other) const -{ - if (length < other.length) { return true; } - if (length > other.length) { return false; } - return memcmp(start, other.start, length) < 0; -} - void -string::push_to_buffer(byte_buffer &buffer, bool escapes) +push_string(byte_buffer &buffer, const string &s, bool escapes) { - for (int i=0 ; i<length ; ++i) + size_t length = s.size(); + for (size_t i=0 ; i<length ; ++i) { - uint8_t c = start[i]; + uint8_t c = s[i]; if (escapes && c == '\\' && i+1 < length) { - c = start[++i]; + c = s[++i]; switch (c) { // For now, we just ignore invalid escape sequences. @@ -206,15 +85,15 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes) case '0'...'7': { int v = digittoint(c); - if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0') + if (i+1 < length && s[i+1] <= '7' && s[i+1] >= '0') { v <<= 3; - v |= digittoint(start[i+1]); + v |= digittoint(s[i+1]); i++; - if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0') + if (i+1 < length && s[i+1] <= '7' && s[i+1] >= '0') { v <<= 3; - v |= digittoint(start[i+1]); + v |= digittoint(s[i+1]); } } c = (uint8_t)v; @@ -227,11 +106,11 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes) { break; } - int v = digittoint(start[i]); - if (i+1 < length && ishexdigit(start[i+1])) + int v = digittoint(s[i]); + if (i+1 < length && ishexdigit(s[i+1])) { v <<= 4; - v |= digittoint(start[++i]); + v |= digittoint(s[++i]); } c = (uint8_t)v; break; @@ -242,17 +121,28 @@ string::push_to_buffer(byte_buffer &buffer, bool escapes) } } -void -string::print(FILE *file) +std::string dirname(const string &s) { - fwrite(start, length, 1, file); + if (s == string()) + { + return string(); + } + char *str = strdup(s.c_str()); + string dn(::dirname(str)); + free(str); + return dn; } -void -string::dump() +std::string basename(const string &s) { - print(stderr); + if (s == string()) + { + return string(); + } + char *str = strdup(s.c_str()); + string bn(::basename(str)); + free(str); + return bn; } - } // namespace dtc diff --git a/usr.bin/dtc/util.hh b/usr.bin/dtc/util.hh index 7f2ec674fc1c..91f4fa88a816 100644 --- a/usr.bin/dtc/util.hh +++ b/usr.bin/dtc/util.hh @@ -68,6 +68,8 @@ inline void push_big_endian(byte_buffer &v, T val) } } +void push_string(byte_buffer &v, const std::string &s, bool escapes=false); + /** * Simple inline non-locale-aware check that this is a valid ASCII * digit. @@ -84,9 +86,30 @@ inline bool isdigit(char c) inline bool ishexdigit(char c) { return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || - ((c >= 'A') && (c <= 'Z')); + ((c >= 'A') && (c <= 'F')); +} + +/** + * Simple inline non-locale-aware check that this is a valid ASCII + * letter. + */ +inline bool isalpha(char c) +{ + return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); } +/** + * A wrapper around dirname(3) that handles inconsistencies relating to memory + * management between platforms and provides a std::string interface. + */ +std::string dirname(const std::string&); + +/** + * A wrapper around basename(3) that handles inconsistencies relating to memory + * management between platforms and provides a std::string interface. + */ +std::string basename(const std::string&); + }// namespace dtc #endif // !_UTIL_HH_ |