Lots of improvements to the BSD-licensed dtc

- Various fixes to includes (including recursive includes)
- Lots of testing that the output exactly matches GPL'd dtc
- Lots of bug fixes to merging
- Fix incorrect mmap usage
- Ad-hoc memory management replaced with C++11 unique_ptr and similar

Patrick Wildt has successfully run many (all?) of the GPL dtc test suite.
This commit is contained in:
theraven 2015-10-25 14:52:16 +00:00
parent d68c757503
commit f89934e0d7
10 changed files with 519 additions and 360 deletions

View File

@ -210,8 +210,10 @@ SUBDIR.${MK_GAMES}+= pom
SUBDIR.${MK_GAMES}+= primes
SUBDIR.${MK_GAMES}+= random
.if ${MK_GPL_DTC} != "yes"
.if ${COMPILER_FEATURES:Mc++11}
SUBDIR+= dtc
.endif
.endif
SUBDIR.${MK_GROFF}+= vgrind
SUBDIR.${MK_HESIOD}+= hesinfo
SUBDIR.${MK_ICONV}+= iconv

View File

@ -51,7 +51,7 @@ namespace
struct address_cells_checker : public checker
{
address_cells_checker(const char *name) : checker(name) {}
virtual bool check_node(device_tree *tree, node *n)
virtual bool check_node(device_tree *tree, const node_ptr &n)
{
// If this has no children, it trivially meets the
// conditions.
@ -61,8 +61,7 @@ namespace
}
bool found_address = false;
bool found_size = false;
for (node::property_iterator i=n->property_begin(),
e=n->property_end() ; i!=e ; ++i)
for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
{
if (!found_address)
{
@ -91,7 +90,7 @@ namespace
} // anonymous namespace
bool
checker::visit_node(device_tree *tree, node *n)
checker::visit_node(device_tree *tree, const node_ptr &n)
{
path.push_back(std::make_pair(n->name, n->unit_address));
// Check this node
@ -100,8 +99,7 @@ checker::visit_node(device_tree *tree, node *n)
return false;
}
// Now check its properties
for (node::property_iterator i=n->property_begin(), e=n->property_end()
; i!=e ; ++i)
for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
{
if (!check_property(tree, n, *i))
{
@ -125,22 +123,21 @@ void
checker::report_error(const char *errmsg)
{
fprintf(stderr, "Error: %s, while checking node: ", errmsg);
for (device_tree::node_path::iterator p=path.begin()+1, pe=path.end() ;
p!=pe ; ++p)
for (auto &p : path)
{
putc('/', stderr);
p->first.dump();
if (!(p->second.empty()))
p.first.dump();
if (!(p.second.empty()))
{
putc('@', stderr);
p->second.dump();
p.second.dump();
}
}
fprintf(stderr, " [-W%s]\n", checker_name);
}
bool
property_checker::check_property(device_tree *tree, node *n, property *p)
property_checker::check_property(device_tree *tree, const node_ptr &n, property_ptr p)
{
if (p->get_key() == key)
{
@ -154,7 +151,7 @@ property_checker::check_property(device_tree *tree, node *n, property *p)
}
bool
property_size_checker::check(device_tree *tree, node *n, property *p)
property_size_checker::check(device_tree *tree, const node_ptr &n, property_ptr p)
{
uint32_t psize = 0;
for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ; ++i)
@ -216,10 +213,9 @@ bool
check_manager::run_checks(device_tree *tree, bool keep_going)
{
bool success = true;
for (std::map<string, checker*>::iterator i=checkers.begin(),
e=checkers.end() ; i!=e ; ++i)
for (auto &i : checkers)
{
success &= i->second->check_tree(tree);
success &= i.second->check_tree(tree);
if (!(success || keep_going))
{
break;
@ -231,7 +227,7 @@ check_manager::run_checks(device_tree *tree, bool keep_going)
bool
check_manager::disable_checker(string name)
{
std::map<string, checker*>::iterator checker = checkers.find(name);
auto checker = checkers.find(name);
if (checker != checkers.end())
{
disabled_checkers.insert(std::make_pair(name,
@ -245,8 +241,7 @@ check_manager::disable_checker(string name)
bool
check_manager::enable_checker(string name)
{
std::map<string, checker*>::iterator checker =
disabled_checkers.find(name);
auto checker = disabled_checkers.find(name);
if (checker != disabled_checkers.end())
{
checkers.insert(std::make_pair(name, checker->second));

View File

@ -65,7 +65,7 @@ class checker
* Visits each node, calling the checker functions on properties and
* nodes.
*/
bool visit_node(device_tree *tree, node *n);
bool visit_node(device_tree *tree, const node_ptr &n);
protected:
/**
* Prints the error message, along with the path to the node that
@ -86,7 +86,7 @@ class checker
* Method for checking that a node is valid. The root class version
* does nothing, subclasses should override this.
*/
virtual bool check_node(device_tree *tree, node *n)
virtual bool check_node(device_tree *tree, const node_ptr &n)
{
return true;
}
@ -94,7 +94,7 @@ class checker
* Method for checking that a property is valid. The root class
* version does nothing, subclasses should override this.
*/
virtual bool check_property(device_tree *tree, node *n, property *p)
virtual bool check_property(device_tree *tree, const node_ptr &n, property_ptr p)
{
return true;
}
@ -124,7 +124,7 @@ class property_checker : public checker
* Implementation of the generic property-checking method that checks
* for a property with the name specified in the constructor
*/
virtual bool check_property(device_tree *tree, node *n, property *p);
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.
@ -134,7 +134,7 @@ class property_checker : public checker
/**
* The check method, which subclasses should implement.
*/
virtual bool check(device_tree *tree, node *n, property *p) = 0;
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0;
};
/**
@ -149,7 +149,7 @@ struct property_type_checker : public property_checker
*/
property_type_checker(const char* name, string property_name) :
property_checker(name, property_name) {}
virtual bool check(device_tree *tree, node *n, property *p) = 0;
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) = 0;
};
/**
@ -160,7 +160,7 @@ struct property_type_checker <property_value::EMPTY> : public property_checker
{
property_type_checker(const char* name, string property_name) :
property_checker(name, property_name) {}
virtual bool check(device_tree *tree, node *n, property *p)
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
{
return p->begin() == p->end();
}
@ -175,7 +175,7 @@ struct property_type_checker <property_value::STRING> : public property_checker
{
property_type_checker(const char* name, string property_name) :
property_checker(name, property_name) {}
virtual bool check(device_tree *tree, node *n, property *p)
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
{
return (p->begin() + 1 == p->end()) && p->begin()->is_string();
}
@ -190,7 +190,7 @@ struct property_type_checker <property_value::STRING_LIST> :
{
property_type_checker(const char* name, string property_name) :
property_checker(name, property_name) {}
virtual bool check(device_tree *tree, node *n, property *p)
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
{
for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ;
++i)
@ -213,7 +213,7 @@ struct property_type_checker <property_value::PHANDLE> : public property_checker
{
property_type_checker(const char* name, string property_name) :
property_checker(name, property_name) {}
virtual bool check(device_tree *tree, node *n, property *p)
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
{
return (p->begin() + 1 == p->end()) &&
(tree->referenced_node(*p->begin()) != 0);
@ -239,7 +239,7 @@ struct property_size_checker : public property_checker
/**
* Check, validates that the property has the correct size.
*/
virtual bool check(device_tree *tree, node *n, property *p);
virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p);
};
@ -254,12 +254,12 @@ class check_manager
* disabling checkers from the command line. When this manager runs,
* it will only run the checkers from this map.
*/
std::map<string, checker*> checkers;
std::unordered_map<string, checker*> checkers;
/**
* The disabled checkers. Moving checkers to this list disables them,
* but allows them to be easily moved back.
*/
std::map<string, checker*> disabled_checkers;
std::unordered_map<string, checker*> disabled_checkers;
/**
* Helper function for adding a property value checker.
*/

View File

@ -44,9 +44,9 @@ namespace dtb
void output_writer::write_data(byte_buffer b)
{
for (byte_buffer::iterator i=b.begin(), e=b.end(); i!=e ; i++)
for (auto i : b)
{
write_data(*i);
write_data(i);
}
}
@ -277,7 +277,7 @@ header::read_dtb(input_buffer &input)
uint32_t
string_table::add_string(string str)
{
std::map<string, uint32_t>::iterator old = string_offsets.find(str);
auto old = string_offsets.find(str);
if (old == string_offsets.end())
{
uint32_t start = size;
@ -298,10 +298,9 @@ string_table::write(dtb::output_writer &writer)
{
writer.write_comment(string("Strings table."));
writer.write_label(string("dt_strings_start"));
for (std::vector<string>::iterator i=strings.begin(), e=strings.end() ;
i!=e ; ++i)
for (auto &i : strings)
{
writer.write_string(*i);
writer.write_string(i);
}
writer.write_label(string("dt_strings_end"));
}

View File

@ -67,7 +67,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(argv0));
"\t\t-W [no-]checker_name] input_file\n", basename((char*)argv0));
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,10 @@
#ifndef _FDT_HH_
#define _FDT_HH_
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <string>
#include "util.hh"
#include "string.hh"
@ -49,7 +52,19 @@ class string_table;
namespace fdt
{
class property;
typedef std::map<string, property*> define_map;
class node;
/**
* Type for (owned) pointers to properties.
*/
typedef std::shared_ptr<property> property_ptr;
/**
* Owning pointer to a node.
*/
typedef std::unique_ptr<node> node_ptr;
/**
* Map from macros to property pointers.
*/
typedef std::unordered_map<string, property_ptr> define_map;
/**
* Properties may contain a number of different value, each with a different
* label. This class encapsulates a single value.
@ -186,6 +201,11 @@ struct property_value
* - Otherwise, it is printed as a byte buffer.
*/
void write_dts(FILE *file);
/**
* Tries to merge adjacent property values, returns true if it succeeds and
* false otherwise.
*/
bool try_to_merge(property_value &other);
private:
/**
* Returns whether the value is of the specified type. If the type of
@ -250,7 +270,7 @@ class property
/**
* Parses one or more 32-bit values enclosed in angle brackets.
*/
void parse_cells(input_buffer &input);
void parse_cells(input_buffer &input, int cell_size);
/**
* Parses an array of bytes, contained within square brackets.
*/
@ -299,18 +319,18 @@ class property
* property from the input, and returns it on success. On any parse
* error, this will return 0.
*/
static property* parse_dtb(input_buffer &structs,
static property_ptr parse_dtb(input_buffer &structs,
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* parse(input_buffer &input,
string key,
string label=string(),
bool semicolonTerminated=true,
define_map *defines=0);
static property_ptr parse(input_buffer &input,
string key,
string label=string(),
bool semicolonTerminated=true,
define_map *defines=0);
/**
* Iterator type used for accessing the values of a property.
*/
@ -378,15 +398,19 @@ class node
* name followed by an at symbol.
*/
string unit_address;
/**
* The type for the property vector.
*/
typedef std::vector<property_ptr> property_vector;
private:
/**
* The properties contained within this node.
*/
std::vector<property*> properties;
property_vector properties;
/**
* The children of this node.
*/
std::vector<node*> children;
std::vector<node_ptr> children;
/**
* A flag indicating whether this node is valid. This is set to false
* if an error occurs during parsing.
@ -415,7 +439,7 @@ class node
* Comparison function for properties, used when sorting the properties
* vector. Orders the properties based on their names.
*/
static inline bool cmp_properties(property *p1, property *p2);
static inline bool cmp_properties(property_ptr &p1, property_ptr &p2);
/*
{
return p1->get_key() < p2->get_key();
@ -426,16 +450,7 @@ class node
* vector. Orders the nodes based on their names or, if the names are
* the same, by the unit addresses.
*/
static inline bool cmp_children(node *c1, node *c2);
/*
{
if (c1->name == c2->name)
{
return c1->unit_address < c2->unit_address;
}
return c1->name < c2->name;
}
*/
static inline bool cmp_children(node_ptr &c1, node_ptr &c2);
public:
/**
* Sorts the node's properties and children into alphabetical order and
@ -445,7 +460,7 @@ class node
/**
* Iterator type for child nodes.
*/
typedef std::vector<node*>::iterator child_iterator;
typedef std::vector<node_ptr>::iterator child_iterator;
/**
* Returns an iterator for the first child of this node.
*/
@ -460,21 +475,17 @@ class node
{
return children.end();
}
/**
* Iterator type for properties of a node.
*/
typedef std::vector<property*>::iterator property_iterator;
/**
* Returns an iterator after the last property of this node.
*/
inline property_iterator property_begin()
inline property_vector::iterator property_begin()
{
return properties.begin();
}
/**
* Returns an iterator for the first property of this node.
*/
inline property_iterator property_end()
inline property_vector::iterator property_end()
{
return properties.end();
}
@ -485,11 +496,11 @@ class node
* cursor on the open brace of the property, after the name and so on
* have been parsed.
*/
static node* parse(input_buffer &input,
string name,
string label=string(),
string address=string(),
define_map *defines=0);
static node_ptr parse(input_buffer &input,
string name,
string label=string(),
string address=string(),
define_map *defines=0);
/**
* Factory method for constructing a new node. Attempts to parse a
* node in DTB format from the input, and returns it on success. On
@ -497,21 +508,16 @@ class node
* cursor on the open brace of the property, after the name and so on
* have been parsed.
*/
static node* parse_dtb(input_buffer &structs, input_buffer &strings);
/**
* Destroys the node, recursively deleting all of its properties and
* children.
*/
~node();
static node_ptr parse_dtb(input_buffer &structs, input_buffer &strings);
/**
* Returns a property corresponding to the specified key, or 0 if this
* node does not contain a property of that name.
*/
property *get_property(string key);
property_ptr get_property(string key);
/**
* Adds a new property to this node.
*/
inline void add_property(property *p)
inline void add_property(property_ptr &p)
{
properties.push_back(p);
}
@ -519,7 +525,7 @@ class node
* Merges a node into this one. Any properties present in both are
* overridden, any properties present in only one are preserved.
*/
void merge_node(node *other);
void merge_node(node_ptr other);
/**
* Write this node to the specified output. Although nodes do not
* refer to a string table directly, their properties do. The string
@ -584,18 +590,18 @@ class device_tree
/**
* Root node. All other nodes are children of this node.
*/
node *root;
node_ptr root;
/**
* Mapping from names to nodes. Only unambiguous names are recorded,
* duplicate names are stored as (node*)-1.
*/
std::map<string, node*> node_names;
std::unordered_map<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::map<string, node_path> node_paths;
std::unordered_map<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.
@ -607,12 +613,16 @@ class device_tree
* destination.
*/
std::vector<property_value*> phandles;
/**
* The names of nodes that target phandles.
*/
std::unordered_set<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
* them for the lifetime of the device tree.
*/
std::vector<input_buffer*> buffers;
std::vector<std::unique_ptr<input_buffer>> buffers;
/**
* A map of used phandle values to nodes. All phandles must be unique,
* so we keep a set of ones that the user explicitly provides in the
@ -622,13 +632,13 @@ class device_tree
* find phandles that were provided by the user explicitly when we are
* doing checking.
*/
std::map<uint32_t, node*> used_phandles;
std::unordered_map<uint32_t, node*> used_phandles;
/**
* Paths to search for include files. This contains a set of
* nul-terminated strings, which are not owned by this class and so
* must be freed separately.
*/
std::vector<const char*> include_paths;
std::vector<std::string> include_paths;
/**
* Dictionary of predefined macros provided on the command line.
*/
@ -655,7 +665,13 @@ class device_tree
* used in resolving cross references. Also collects phandle
* properties that have been explicitly added.
*/
void collect_names_recursive(node* n, node_path &path);
void collect_names_recursive(node_ptr &n, node_path &path);
/**
* Assign phandle properties to all nodes that have been referenced and
* require one. This method will recursively visit the tree starting at
* the node that it is passed.
*/
void assign_phandles(node_ptr &n, uint32_t &next);
/**
* Calls the recursive version of this method on every root node.
*/
@ -667,9 +683,16 @@ class device_tree
*/
void resolve_cross_references();
/**
* Parses root nodes from the top level of a dts file.
* 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_roots(input_buffer &input, std::vector<node*> &roots);
void parse_file(input_buffer &input,
const std::string &dir,
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
@ -706,7 +729,7 @@ class device_tree
/**
* Default constructor. Creates a valid, but empty FDT.
*/
device_tree() : phandle_node_name(EPAPR), valid(true), root(0),
device_tree() : phandle_node_name(EPAPR), valid(true),
boot_cpu(0), spare_reserve_map_entries(0),
minimum_blob_size(0), blob_padding(0) {}
/**
@ -719,10 +742,6 @@ class device_tree
* a file that contains device tree source.
*/
void parse_dts(const char *fn, FILE *depfile);
/**
* Destroy the tree and any input buffers that it holds.
*/
~device_tree();
/**
* Returns whether this tree is valid.
*/
@ -741,7 +760,7 @@ class device_tree
* Returns a pointer to the root node of this tree. No ownership
* transfer.
*/
inline node *get_root() const
inline const node_ptr &get_root() const
{
return root;
}
@ -767,7 +786,8 @@ class device_tree
*/
void add_include_path(const char *path)
{
include_paths.push_back(path);
std::string p(path);
include_paths.push_back(std::move(p));
}
/**
* Sets the number of empty reserve map entries to add.

View File

@ -113,7 +113,7 @@ input_buffer::consume(const char *str)
}
bool
input_buffer::consume_integer(long long &outInt)
input_buffer::consume_integer(unsigned long long &outInt)
{
// The first character must be a digit. Hex and octal strings
// are prefixed by 0 and 0x, respectively.
@ -122,7 +122,7 @@ input_buffer::consume_integer(long long &outInt)
return false;
}
char *end=0;
outInt = strtoll(&buffer[cursor], &end, 0);
outInt = strtoull(&buffer[cursor], &end, 0);
if (end == &buffer[cursor])
{
return false;
@ -168,9 +168,8 @@ input_buffer::next_token()
// Eat the /
++(*this);
}
// Parse // comments and # comments
if (((*this)[0] == '/' && (*this)[1] == '/') ||
(*this)[0] == '#')
// Parse // comments
if (((*this)[0] == '/' && (*this)[1] == '/'))
{
// eat the start of the comment
++(*this);
@ -238,11 +237,12 @@ mmap_input_buffer::mmap_input_buffer(int fd) : input_buffer(0, 0)
perror("Failed to stat file");
}
size = sb.st_size;
buffer = (const char*)mmap(0, size, PROT_READ,
MAP_PREFAULT_READ, fd, 0);
if (buffer == 0)
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);
}
}

View File

@ -80,6 +80,10 @@ class input_buffer
*/
void skip_spaces();
public:
/**
* Return whether all input has been consumed.
*/
bool finished() { return cursor >= size; }
/**
* Virtual destructor. Does nothing, but exists so that subclasses
* that own the memory can run cleanup code for deallocating it.
@ -181,7 +185,7 @@ class input_buffer
*
* The parsed value is returned via the argument.
*/
bool consume_integer(long long &outInt);
bool consume_integer(unsigned long long &outInt);
/**
* Template function that consumes a binary value in big-endian format
* from the input stream. Returns true and advances the cursor if

View File

@ -33,6 +33,8 @@
#ifndef _STRING_HH_
#define _STRING_HH_
#include "input_buffer.hh"
#include <string>
#include <functional>
namespace dtc
{
@ -48,6 +50,7 @@ namespace dtc
*/
class string
{
friend std::hash<string>;
/** Start address. Contained within the mmap()'d input file and not
* owned by this object. */
const char *start;
@ -143,5 +146,19 @@ class string
};
} // namespace dtc
namespace std
{
template<>
struct hash<dtc::string>
{
std::size_t operator()(dtc::string const& s) const
{
std::string str(s.start, s.length);
std::hash<std::string> h;
return h(str);
}
};
}
#endif // !_STRING_HH_