Index: head/usr.bin/Makefile =================================================================== --- head/usr.bin/Makefile (revision 289934) +++ head/usr.bin/Makefile (revision 289935) @@ -1,301 +1,303 @@ # From: @(#)Makefile 8.3 (Berkeley) 1/7/94 # $FreeBSD$ .include # XXX MISSING: deroff diction graph learn plot # spell spline struct xsend # XXX Use GNU versions: diff ld patch # Moved to secure: bdes # SUBDIR= alias \ apply \ asa \ awk \ banner \ basename \ brandelf \ bsdiff \ bzip2 \ bzip2recover \ cap_mkdb \ chat \ chpass \ cksum \ cmp \ col \ colldef \ colrm \ column \ comm \ compress \ cpuset \ csplit \ ctlstat \ cut \ dirname \ dpv \ du \ elf2aout \ elfdump \ enigma \ env \ expand \ false \ fetch \ find \ fmt \ fold \ fstat \ fsync \ gcore \ gencat \ getconf \ getent \ getopt \ grep \ gzip \ head \ hexdump \ id \ ident \ ipcrm \ ipcs \ join \ jot \ keylogin \ keylogout \ killall \ ktrace \ ktrdump \ lam \ lastcomm \ ldd \ leave \ less \ lessecho \ lesskey \ limits \ locale \ lock \ lockf \ logger \ login \ logins \ logname \ look \ lorder \ lsvfs \ lzmainfo \ m4 \ mandoc \ mesg \ minigzip \ ministat \ mkdep \ mkfifo \ mkimg \ mklocale \ mktemp \ mkulzma \ mkuzip \ mt \ ncal \ netstat \ newgrp \ nfsstat \ nice \ nl \ numactl \ nohup \ opieinfo \ opiekey \ opiepasswd \ pagesize \ passwd \ paste \ patch \ pathchk \ perror \ pr \ printenv \ printf \ procstat \ protect \ rctl \ renice \ rev \ revoke \ rpcinfo \ rs \ rup \ rusers \ rwall \ script \ sed \ send-pr \ seq \ shar \ showmount \ sockstat \ soelim \ sort \ split \ stat \ stdbuf \ su \ systat \ tabs \ tail \ tar \ tcopy \ tee \ time \ timeout \ tip \ top \ touch \ tput \ tr \ true \ truncate \ tset \ tsort \ tty \ uname \ unexpand \ uniq \ unzip \ units \ unvis \ uudecode \ uuencode \ vis \ vmstat \ w \ wall \ wc \ what \ whereis \ which \ whois \ write \ xargs \ xinstall \ xo \ xz \ xzdec \ yes # NB: keep these sorted by MK_* knobs SUBDIR.${MK_AT}+= at SUBDIR.${MK_ATM}+= atm SUBDIR.${MK_BLUETOOTH}+= bluetooth SUBDIR.${MK_BSD_CPIO}+= cpio SUBDIR.${MK_CALENDAR}+= calendar SUBDIR.${MK_CLANG}+= clang SUBDIR.${MK_EE}+= ee SUBDIR.${MK_FILE}+= file SUBDIR.${MK_FINGER}+= finger SUBDIR.${MK_FTP}+= ftp SUBDIR.${MK_GAMES}+= caesar SUBDIR.${MK_GAMES}+= factor SUBDIR.${MK_GAMES}+= fortune SUBDIR.${MK_GAMES}+= grdc SUBDIR.${MK_GAMES}+= morse SUBDIR.${MK_GAMES}+= number 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 SUBDIR.${MK_ICONV}+= mkcsmapper SUBDIR.${MK_ICONV}+= mkesdb SUBDIR.${MK_ISCSI}+= iscsictl SUBDIR.${MK_KDUMP}+= kdump SUBDIR.${MK_KDUMP}+= truss SUBDIR.${MK_KERBEROS_SUPPORT}+= compile_et SUBDIR.${MK_LDNS_UTILS}+= drill SUBDIR.${MK_LDNS_UTILS}+= host SUBDIR.${MK_LOCATE}+= locate # XXX msgs? SUBDIR.${MK_MAIL}+= biff SUBDIR.${MK_MAIL}+= from SUBDIR.${MK_MAIL}+= mail SUBDIR.${MK_MAIL}+= msgs SUBDIR.${MK_MAKE}+= bmake SUBDIR.${MK_MAN_UTILS}+= catman .if ${MK_MANDOCDB} == "no" # AND SUBDIR.${MK_MAN_UTILS}+= makewhatis .endif SUBDIR.${MK_MAN_UTILS}+= man SUBDIR.${MK_NETCAT}+= nc SUBDIR.${MK_NIS}+= ypcat SUBDIR.${MK_NIS}+= ypmatch SUBDIR.${MK_NIS}+= ypwhich SUBDIR.${MK_OPENSSH}+= ssh-copy-id SUBDIR.${MK_OPENSSL}+= bc SUBDIR.${MK_OPENSSL}+= chkey SUBDIR.${MK_OPENSSL}+= dc SUBDIR.${MK_OPENSSL}+= newkey SUBDIR.${MK_QUOTAS}+= quota SUBDIR.${MK_RCMDS}+= rlogin SUBDIR.${MK_RCMDS}+= rsh SUBDIR.${MK_RCMDS}+= ruptime SUBDIR.${MK_RCMDS}+= rwho SUBDIR.${MK_SENDMAIL}+= vacation SUBDIR.${MK_TALK}+= talk SUBDIR.${MK_TELNET}+= telnet SUBDIR.${MK_TESTS}+= tests SUBDIR.${MK_TEXTPROC}+= checknr SUBDIR.${MK_TEXTPROC}+= colcrt SUBDIR.${MK_TEXTPROC}+= ul SUBDIR.${MK_TFTP}+= tftp SUBDIR.${MK_TOOLCHAIN}+= addr2line SUBDIR.${MK_TOOLCHAIN}+= ar SUBDIR.${MK_TOOLCHAIN}+= c89 SUBDIR.${MK_TOOLCHAIN}+= c99 SUBDIR.${MK_TOOLCHAIN}+= ctags SUBDIR.${MK_TOOLCHAIN}+= cxxfilt SUBDIR.${MK_TOOLCHAIN}+= elfcopy SUBDIR.${MK_TOOLCHAIN}+= file2c .if ${MACHINE_ARCH} != "aarch64" # ARM64TODO gprof does not build SUBDIR.${MK_TOOLCHAIN}+= gprof .endif SUBDIR.${MK_TOOLCHAIN}+= indent SUBDIR.${MK_TOOLCHAIN}+= lex SUBDIR.${MK_TOOLCHAIN}+= mkstr SUBDIR.${MK_TOOLCHAIN}+= nm SUBDIR.${MK_TOOLCHAIN}+= readelf SUBDIR.${MK_TOOLCHAIN}+= rpcgen SUBDIR.${MK_TOOLCHAIN}+= unifdef SUBDIR.${MK_TOOLCHAIN}+= size SUBDIR.${MK_TOOLCHAIN}+= strings .if ${MACHINE_ARCH} != "aarch64" # ARM64TODO xlint does not build SUBDIR.${MK_TOOLCHAIN}+= xlint .endif SUBDIR.${MK_TOOLCHAIN}+= xstr SUBDIR.${MK_TOOLCHAIN}+= yacc SUBDIR.${MK_VI}+= vi SUBDIR.${MK_VT}+= vtfontcvt SUBDIR.${MK_USB}+= usbhidaction SUBDIR.${MK_USB}+= usbhidctl SUBDIR.${MK_UTMPX}+= last SUBDIR.${MK_UTMPX}+= users SUBDIR.${MK_UTMPX}+= who SUBDIR.${MK_SVN}+= svn SUBDIR.${MK_SVNLITE}+= svn .include SUBDIR:= ${SUBDIR:O:u} SUBDIR_PARALLEL= .include Index: head/usr.bin/dtc/checking.cc =================================================================== --- head/usr.bin/dtc/checking.cc (revision 289934) +++ head/usr.bin/dtc/checking.cc (revision 289935) @@ -1,264 +1,259 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "checking.hh" #include namespace dtc { namespace fdt { namespace checking { namespace { /** * Checker that verifies that every node that has children has * #address-cells and #size-cells properties. */ 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. if (n->child_begin() == n->child_end()) { return true; } 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) { found_address = ((*i)->get_key() == "#address-cells"); } if (!found_size) { found_size = ((*i)->get_key() == "#size-cells"); } if (found_size && found_address) { break; } } if (!found_address) { report_error("Missing #address-cells property"); } if (!found_size) { report_error("Missing #size-cells property"); } return found_address && found_size; } }; } // 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 if (!check_node(tree, 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)) { return false; } } // And then recursively check the children for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ; ++i) { if (!visit_node(tree, *i)) { return false; } } path.pop_back(); return true; } 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) { if (!check(tree, n, p)) { report_error("property check failed"); return false; } } return true; } 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) { if (!i->is_binary()) { return false; } psize += i->byte_data.size(); } return psize == size; } template void check_manager::add_property_type_checker(const char *name, string prop) { checkers.insert(std::make_pair(string(name), new property_type_checker(name, prop))); } void check_manager::add_property_size_checker(const char *name, string prop, uint32_t size) { checkers.insert(std::make_pair(string(name), new property_size_checker(name, prop, size))); } check_manager::~check_manager() { while (checkers.begin() != checkers.end()) { delete checkers.begin()->second; checkers.erase(checkers.begin()); } while (disabled_checkers.begin() != disabled_checkers.end()) { delete disabled_checkers.begin()->second; disabled_checkers.erase(disabled_checkers.begin()); } } check_manager::check_manager() { // NOTE: All checks listed here MUST have a corresponding line // in the man page! add_property_type_checker( "type-compatible", string("compatible")); add_property_type_checker( "type-model", string("model")); add_property_size_checker("type-phandle", string("phandle"), 4); disabled_checkers.insert(std::make_pair(string("cells-attributes"), new address_cells_checker("cells-attributes"))); } bool check_manager::run_checks(device_tree *tree, bool keep_going) { bool success = true; - for (std::map::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; } } return success; } bool check_manager::disable_checker(string name) { - std::map::iterator checker = checkers.find(name); + auto checker = checkers.find(name); if (checker != checkers.end()) { disabled_checkers.insert(std::make_pair(name, checker->second)); checkers.erase(checker); return true; } return false; } bool check_manager::enable_checker(string name) { - std::map::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)); disabled_checkers.erase(checker); return true; } return false; } } // namespace checking } // namespace fdt } // namespace dtc Index: head/usr.bin/dtc/checking.hh =================================================================== --- head/usr.bin/dtc/checking.hh (revision 289934) +++ head/usr.bin/dtc/checking.hh (revision 289935) @@ -1,308 +1,308 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _CHECKING_HH_ #define _CHECKING_HH_ #include "string.hh" #include "fdt.hh" namespace dtc { namespace fdt { namespace checking { /** * Base class for all checkers. This will visit the entire tree and perform * semantic checks defined in subclasses. Note that device trees are generally * small (a few dozen nodes at most) and so we optimise for flexibility and * extensibility here, not for performance. Each checker will visit the entire * tree. */ class checker { /** * The path to the current node being checked. This is used for * printing error messages. */ device_tree::node_path path; /** * The name of the checker. This is used for printing error messages * and for enabling / disabling specific checkers from the command * line. */ const char *checker_name; /** * 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 * caused the error and the name of the checker. */ void report_error(const char *errmsg); public: /** * Constructor. Takes the name of this checker, which is which is used * when reporting errors. */ checker(const char *name) : checker_name(name) {} /** * Virtual destructor in case any subclasses need to do cleanup. */ virtual ~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; } /** * 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; } /** * Runs the checker on the specified device tree. */ bool check_tree(fdt::device_tree *tree) { return visit_node(tree, tree->get_root()); } }; /** * Abstract base class for simple property checks. This class defines a check * method for subclasses, which is invoked only when it finds a property with * the matching name. To define simple property checkers, just subclass this * and override the check() method. */ class property_checker : public checker { /** * The name of the property that this checker is looking for. */ string key; public: /** * 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. */ property_checker(const char* name, string property_name) : checker(name), key(property_name) {} /** * 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; }; /** * Property type checker. */ template 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_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; }; /** * Empty property checker. This checks that the property has no value. */ template<> 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) + virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) { return p->begin() == p->end(); } }; /** * String property checker. This checks that the property has exactly one * value, which is a string. */ template<> 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) + virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p) { return (p->begin() + 1 == p->end()) && p->begin()->is_string(); } }; /** * String list property checker. This checks that the property has at least * one value, all of which are strings. */ template<> 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) + 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) { if (!(i->is_string() || i->is_string_list())) { return false; } } return p->begin() != p->end(); } }; /** * Phandle property checker. This checks that the property has exactly one * value, which is a valid phandle. */ template<> 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) + 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); } }; /** * Check that a property has the correct size. */ struct property_size_checker : public property_checker { /** * The expected size of the property. */ uint32_t size; public: /** * 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_checker(name, property_name), size(bytes) {} /** * 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); }; /** * The check manager is the interface to running the checks. This allows * default checks to be enabled, non-default checks to be enabled, and so on. */ class check_manager { /** * The enabled checkers, indexed by their names. The name is used when * disabling checkers from the command line. When this manager runs, * it will only run the checkers from this map. */ - std::map checkers; + std::unordered_map checkers; /** * The disabled checkers. Moving checkers to this list disables them, * but allows them to be easily moved back. */ - std::map disabled_checkers; + std::unordered_map disabled_checkers; /** * Helper function for adding a property value checker. */ template void add_property_type_checker(const char *name, string prop); /** * Helper function for adding a simple type checker. */ void add_property_type_checker(const char *name, string prop); /** * Helper function for adding a property value checker. */ void add_property_size_checker(const char *name, string prop, uint32_t size); public: /** * Delete all of the checkers that are part of this checker manager. */ ~check_manager(); /** * Default constructor, creates check manager containing all of the * default checks. */ check_manager(); /** * Run all of the checks on the specified tree. */ bool run_checks(device_tree *tree, bool keep_going); /** * Disables the named checker. */ bool disable_checker(string name); /** * Enables the named checker. */ bool enable_checker(string name); }; } // namespace checking } // namespace fdt } // namespace dtc #endif // !_CHECKING_HH_ Index: head/usr.bin/dtc/dtb.cc =================================================================== --- head/usr.bin/dtc/dtb.cc (revision 289934) +++ head/usr.bin/dtc/dtb.cc (revision 289935) @@ -1,312 +1,311 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "dtb.hh" #include #include #include #include namespace dtc { 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); } } void binary_writer::write_string(string name) { name.push_to_buffer(buffer); // Trailing nul buffer.push_back(0); } void binary_writer::write_data(uint8_t v) { buffer.push_back(v); } void binary_writer::write_data(uint32_t v) { while (buffer.size() % 4 != 0) { buffer.push_back(0); } push_big_endian(buffer, v); } void binary_writer::write_data(uint64_t v) { while (buffer.size() % 8 != 0) { buffer.push_back(0); } push_big_endian(buffer, v); } void binary_writer::write_to_file(int fd) { // FIXME: Check return write(fd, buffer.data(), buffer.size()); } uint32_t binary_writer::size() { return buffer.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) { byte_count = 0; buffer.push_back('\n'); } write_string(c); } void asm_writer::write_byte(uint8_t b) { char out[3] = {0}; if (byte_count++ == 0) { buffer.push_back('\t'); } write_string(".byte 0x"); snprintf(out, 3, "%.2hhx", b); buffer.push_back(out[0]); buffer.push_back(out[1]); if (byte_count == 4) { buffer.push_back('\n'); byte_count = 0; } else { buffer.push_back(';'); buffer.push_back(' '); } } void asm_writer::write_label(string name) { write_line("\t.globl "); name.push_to_buffer(buffer); buffer.push_back('\n'); name.push_to_buffer(buffer); buffer.push_back(':'); buffer.push_back('\n'); buffer.push_back('_'); name.push_to_buffer(buffer); buffer.push_back(':'); buffer.push_back('\n'); } void asm_writer::write_comment(string name) { write_line("\t/* "); name.push_to_buffer(buffer); write_string(" */\n"); } void asm_writer::write_string(string name) { write_line("\t.string \""); name.push_to_buffer(buffer); write_line("\"\n"); bytes_written += name.size() + 1; } void asm_writer::write_data(uint8_t v) { write_byte(v); bytes_written++; } void asm_writer::write_data(uint32_t v) { if (bytes_written % 4 != 0) { write_line("\t.balign 4\n"); bytes_written += (4 - (bytes_written % 4)); } write_byte((v >> 24) & 0xff); write_byte((v >> 16) & 0xff); write_byte((v >> 8) & 0xff); write_byte((v >> 0) & 0xff); bytes_written += 4; } void asm_writer::write_data(uint64_t v) { if (bytes_written % 8 != 0) { write_line("\t.balign 8\n"); bytes_written += (8 - (bytes_written % 8)); } write_byte((v >> 56) & 0xff); write_byte((v >> 48) & 0xff); write_byte((v >> 40) & 0xff); write_byte((v >> 32) & 0xff); write_byte((v >> 24) & 0xff); write_byte((v >> 16) & 0xff); write_byte((v >> 8) & 0xff); write_byte((v >> 0) & 0xff); bytes_written += 8; } void asm_writer::write_to_file(int fd) { // FIXME: Check return write(fd, buffer.data(), buffer.size()); } uint32_t asm_writer::size() { return bytes_written; } void header::write(output_writer &out) { out.write_label(string("dt_blob_start")); out.write_label(string("dt_header")); out.write_comment("magic"); out.write_data(magic); out.write_comment("totalsize"); out.write_data(totalsize); out.write_comment("off_dt_struct"); out.write_data(off_dt_struct); out.write_comment("off_dt_strings"); out.write_data(off_dt_strings); out.write_comment("off_mem_rsvmap"); out.write_data(off_mem_rsvmap); out.write_comment("version"); out.write_data(version); out.write_comment("last_comp_version"); out.write_data(last_comp_version); out.write_comment("boot_cpuid_phys"); out.write_data(boot_cpuid_phys); out.write_comment("size_dt_strings"); out.write_data(size_dt_strings); out.write_comment("size_dt_struct"); out.write_data(size_dt_struct); } bool header::read_dtb(input_buffer &input) { if (!(input.consume_binary(magic) && magic == 0xd00dfeed)) { fprintf(stderr, "Missing magic token in header. Got %" PRIx32 " expected 0xd00dfeed\n", magic); return false; } return input.consume_binary(totalsize) && input.consume_binary(off_dt_struct) && input.consume_binary(off_dt_strings) && input.consume_binary(off_mem_rsvmap) && input.consume_binary(version) && input.consume_binary(last_comp_version) && input.consume_binary(boot_cpuid_phys) && input.consume_binary(size_dt_strings) && input.consume_binary(size_dt_struct); } uint32_t string_table::add_string(string str) { - std::map::iterator old = string_offsets.find(str); + auto old = string_offsets.find(str); if (old == string_offsets.end()) { uint32_t start = size; // Don't forget the trailing nul size += str.size() + 1; string_offsets.insert(std::make_pair(str, start)); strings.push_back(str); return start; } else { return old->second; } } void string_table::write(dtb::output_writer &writer) { writer.write_comment(string("Strings table.")); writer.write_label(string("dt_strings_start")); - for (std::vector::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")); } } // namespace dtb } // namespace dtc Index: head/usr.bin/dtc/dtc.cc =================================================================== --- head/usr.bin/dtc/dtc.cc (revision 289934) +++ head/usr.bin/dtc/dtc.cc (revision 289935) @@ -1,344 +1,344 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include "fdt.hh" #include "checking.hh" using namespace dtc; /** * The current major version of the tool. */ int version_major = 0; /** * The current minor version of the tool. */ int version_minor = 4; /** * The current patch level of the tool. */ int version_patch = 0; static void usage(const char* argv0) { fprintf(stderr, "Usage:\n" "\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]" "[-E [no-]checker_name]\n" "\t\t[-H phandle_format] [-I input_format]" "[-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)); } /** * Prints the current version of this program.. */ static void version(const char* progname) { fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major, version_minor, version_patch); } using fdt::device_tree; int main(int argc, char **argv) { int ch; int outfile = fileno(stdout); const char *outfile_name = "-"; 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; uint32_t boot_cpu; bool boot_cpu_specified = false; bool keep_going = false; bool sort = false; clock_t c0 = clock(); class device_tree tree; fdt::checking::check_manager checks; const char *options = "hqI:O:o:V:d:R:S:p:b:fi:svH:W:E:DP:"; // Don't forget to update the man page if any more options are added. while ((ch = getopt(argc, argv, options)) != -1) { switch (ch) { case 'h': usage(argv[0]); return EXIT_SUCCESS; case 'v': version(argv[0]); return EXIT_SUCCESS; case 'I': { string arg = string(optarg); if (arg == string("dtb")) { read_fn = &device_tree::parse_dtb; } else if (arg == string("dts")) { read_fn = &device_tree::parse_dts; } else { fprintf(stderr, "Unknown input format: %s\n", optarg); return EXIT_FAILURE; } break; } case 'O': { string arg = string(optarg); if (arg == string("dtb")) { write_fn = &device_tree::write_binary; } else if (arg == string("asm")) { write_fn = &device_tree::write_asm; } else if (arg == string("dts")) { write_fn = &device_tree::write_dts; } else { fprintf(stderr, "Unknown output format: %s\n", optarg); return EXIT_FAILURE; } break; } case 'o': { outfile_name = optarg; outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666); if (outfile == -1) { perror("Unable to open output file"); return EXIT_FAILURE; } break; } case 'D': debug_mode = true; break; case 'V': if (string(optarg) != string("17")) { fprintf(stderr, "Unknown output format version: %s\n", optarg); return EXIT_FAILURE; } break; case 'd': { if (depfile != 0) { fclose(depfile); } if (string(optarg) == string("-")) { depfile = stdout; } else { depfile = fdopen(open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666), "w"); if (depfile == 0) { perror("Unable to open dependency file"); return EXIT_FAILURE; } } break; } case 'H': { string arg = string(optarg); if (arg == string("both")) { tree.set_phandle_format(device_tree::BOTH); } else if (arg == string("epapr")) { tree.set_phandle_format(device_tree::EPAPR); } else if (arg == string("linux")) { tree.set_phandle_format(device_tree::LINUX); } else { fprintf(stderr, "Unknown phandle format: %s\n", optarg); return EXIT_FAILURE; } break; } case 'b': // Don't bother to check if strtoll fails, just // use the 0 it returns. boot_cpu = (uint32_t)strtoll(optarg, 0, 10); boot_cpu_specified = true; break; case 'f': keep_going = true; break; case 'W': case 'E': { string arg = string(optarg); if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0)) { arg = string(optarg+3); if (!checks.disable_checker(arg)) { fprintf(stderr, "Checker %s either does not exist or is already disabled\n", optarg+3); } break; } if (!checks.enable_checker(arg)) { fprintf(stderr, "Checker %s either does not exist or is already enabled\n", optarg); } break; } case 's': { sort = true; break; } case 'i': { tree.add_include_path(optarg); break; } // Should quiet warnings, but for now is silently ignored. case 'q': break; case 'R': tree.set_empty_reserve_map_entries(strtoll(optarg, 0, 10)); break; case 'S': tree.set_blob_minimum_size(strtoll(optarg, 0, 10)); break; case 'p': tree.set_blob_padding(strtoll(optarg, 0, 10)); break; case 'P': if (!tree.parse_define(optarg)) { fprintf(stderr, "Invalid predefine value %s\n", optarg); } break; default: fprintf(stderr, "Unknown option %c\n", ch); return EXIT_FAILURE; } } if (optind < argc) { in_file = argv[optind]; } if (depfile != 0) { fputs(outfile_name, depfile); fputs(": ", depfile); fputs(in_file, depfile); } clock_t c1 = clock(); (tree.*read_fn)(in_file, depfile); // Override the boot CPU found in the header, if we're loading from dtb if (boot_cpu_specified) { tree.set_boot_cpu(boot_cpu); } if (sort) { tree.sort(); } if (depfile != 0) { putc('\n', depfile); fclose(depfile); } if (!(tree.is_valid() || keep_going)) { fprintf(stderr, "Failed to parse tree. Unhappy face!\n"); return EXIT_FAILURE; } clock_t c2 = clock(); if (!(checks.run_checks(&tree, true) || keep_going)) { return EXIT_FAILURE; } clock_t c3 = clock(); (tree.*write_fn)(outfile); close(outfile); clock_t c4 = clock(); if (debug_mode) { struct rusage r; getrusage(RUSAGE_SELF, &r); fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss); fprintf(stderr, "Setup and option parsing took %f seconds\n", ((double)(c1-c0))/CLOCKS_PER_SEC); fprintf(stderr, "Parsing took %f seconds\n", ((double)(c2-c1))/CLOCKS_PER_SEC); fprintf(stderr, "Checking took %f seconds\n", ((double)(c3-c2))/CLOCKS_PER_SEC); fprintf(stderr, "Generating output took %f seconds\n", ((double)(c4-c3))/CLOCKS_PER_SEC); fprintf(stderr, "Total time: %f seconds\n", ((double)(c4-c0))/CLOCKS_PER_SEC); // This is not needed, but keeps valgrind quiet. fclose(stdin); } return EXIT_SUCCESS; } Index: head/usr.bin/dtc/fdt.cc =================================================================== --- head/usr.bin/dtc/fdt.cc (revision 289934) +++ head/usr.bin/dtc/fdt.cc (revision 289935) @@ -1,1466 +1,1588 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #define __STDC_LIMIT_MACROS 1 #include "fdt.hh" +#include "dtb.hh" #include + #include #include #include #include #include #include #include #include #include -#include "dtb.hh" +#include namespace dtc { namespace fdt { uint32_t property_value::get_as_uint32() { if (byte_data.size() != 4) { return 0; } uint32_t v = 0; v &= byte_data[0] << 24; v &= byte_data[1] << 16; v &= byte_data[2] << 8; v &= byte_data[3] << 0; return v; } void property_value::push_to_buffer(byte_buffer &buffer) { if (!byte_data.empty()) { buffer.insert(buffer.end(), byte_data.begin(), byte_data.end()); } else { string_data.push_to_buffer(buffer, true); // Trailing nul buffer.push_back(0); } } void property_value::write_dts(FILE *file) { resolve_type(); switch (type) { default: assert(0 && "Invalid type"); case STRING: case STRING_LIST: case CROSS_REFERENCE: write_as_string(file); break; case PHANDLE: write_as_cells(file); break; case BINARY: if (byte_data.size() % 4 == 0) { write_as_cells(file); break; } write_as_bytes(file); break; } } void property_value::resolve_type() { if (type != UNKNOWN) { return; } if (byte_data.empty()) { type = STRING; return; } if (byte_data.back() == 0) { bool is_all_printable = true; int nuls = 0; int bytes = 0; - for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end()-1; i nuls)) || bytes == 0) { type = STRING; - if (nuls > 0) + if (nuls > 1) { type = STRING_LIST; } return; } } type = BINARY; } void property_value::write_as_string(FILE *file) { putc('"', file); if (byte_data.empty()) { string_data.print(file); } else { - for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end()-1; i!=e ; ++i) + bool hasNull = (byte_data.back() == '\0'); + // Remove trailing null bytes from the string before printing as dts. + if (hasNull) { + byte_data.pop_back(); + } + for (auto i : byte_data) + { // FIXME Escape tabs, newlines, and so on. - if (*i == '\0') + if (i == '\0') { fputs("\", \"", file); continue; } - putc(*i, file); + putc(i, file); } + if (hasNull) + { + byte_data.push_back('\0'); + } } putc('"', file); } void property_value::write_as_cells(FILE *file) { putc('<', file); assert((byte_data.size() % 4) == 0); - for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end(); i!=e ; ++i) + for (auto i=byte_data.begin(), e=byte_data.end(); i!=e ; ++i) { uint32_t v = 0; v = (v << 8) | *i; ++i; v = (v << 8) | *i; ++i; v = (v << 8) | *i; ++i; v = (v << 8) | *i; fprintf(file, "0x%" PRIx32, v); if (i+1 != e) { putc(' ', file); } } putc('>', file); } void property_value::write_as_bytes(FILE *file) { putc('[', file); - for (byte_buffer::iterator i=byte_data.begin(), e=byte_data.end(); i!=e ; i++) + for (auto i=byte_data.begin(), e=byte_data.end(); i!=e ; i++) { fprintf(file, "%02hhx", *i); if (i+1 != e) { putc(' ', file); } } putc(']', file); } void property::parse_string(input_buffer &input) { property_value v; assert(input[0] == '"'); ++input; const char *start = (const char*)input; int length = 0; while (char c = input[0]) { if (c == '"' && input[-1] != '\\') { input.consume('"'); break; } ++input; ++length; } v.string_data = string(start, length); values.push_back(v); } void -property::parse_cells(input_buffer &input) +property::parse_cells(input_buffer &input, int cell_size) { + unsigned long long cell_max; + switch (cell_size) + { + case 8: + cell_max = UINT8_MAX; + break; + case 16: + cell_max = UINT16_MAX; + break; + case 32: + cell_max = UINT32_MAX; + break; + case 64: + cell_max = UINT64_MAX; + break; + default: + assert(0 && "Invalid cell size!"); + } assert(input[0] == '<'); ++input; property_value v; input.next_token(); while (!input.consume('>')) { input.next_token(); // If this is a phandle then we need to get the name of the // referenced node if (input.consume('&')) { + if (cell_size != 32) + { + input.parse_error("reference only permitted in 32-bit arrays"); + valid = false; + return; + } input.next_token(); // FIXME: We should support full paths here, but we // don't. string referenced = string::parse_node_name(input); if (referenced.empty()) { input.parse_error("Expected node name"); valid = false; return; } input.next_token(); // If we already have some bytes, make the phandle a // separate component. if (!v.byte_data.empty()) { values.push_back(v); v = property_value(); } v.string_data = referenced; v.type = property_value::PHANDLE; values.push_back(v); v = property_value(); } else { //FIXME: We should support labels in the middle //of these, but we don't. - long long val; + unsigned long long val; if (!input.consume_integer(val)) { input.parse_error("Expected numbers in array of cells"); valid = false; return; } - if ((val < 0) || (val > UINT32_MAX)) + if (val > cell_max) { + fprintf(stderr, "%lld > %lld\n", val, cell_max); input.parse_error("Value out of range"); valid = false; return; } - push_big_endian(v.byte_data, (uint32_t)val); + switch (cell_size) + { + case 8: + v.byte_data.push_back(val); + break; + case 16: + push_big_endian(v.byte_data, (uint16_t)val); + break; + case 32: + push_big_endian(v.byte_data, (uint32_t)val); + break; + case 64: + push_big_endian(v.byte_data, (uint64_t)val); + break; + default: + assert(0 && "Invalid cell size!"); + } input.next_token(); } } // Don't store an empty string value here. if (v.byte_data.size() > 0) { values.push_back(v); } } void property::parse_bytes(input_buffer &input) { assert(input[0] == '['); ++input; property_value v; input.next_token(); while (!input.consume(']')) { { //FIXME: We should support //labels in the middle of //these, but we don't. uint8_t val; if (!input.consume_hex_byte(val)) { input.parse_error("Expected hex bytes in array of bytes"); valid = false; return; } v.byte_data.push_back(val); input.next_token(); } } values.push_back(v); } void property::parse_reference(input_buffer &input) { assert(input[0] == '&'); ++input; input.next_token(); property_value v; v.string_data = string::parse_node_name(input); if (v.string_data.empty()) { input.parse_error("Expected node name"); valid = false; return; } v.type = property_value::CROSS_REFERENCE; values.push_back(v); } property::property(input_buffer &structs, input_buffer &strings) { uint32_t name_offset; uint32_t length; valid = structs.consume_binary(length) && structs.consume_binary(name_offset); if (!valid) { fprintf(stderr, "Failed to read property\n"); return; } // Find the name input_buffer name_buffer = strings.buffer_from_offset(name_offset); if (name_buffer.empty()) { fprintf(stderr, "Property name offset %" PRIu32 " is past the end of the strings table\n", name_offset); valid = false; return; } key = string(name_buffer); // If we're empty, do not push anything as value. if (!length) return; // Read the value uint8_t byte; property_value v; for (uint32_t i=0 ; ifind(name)) == defines->end())) { input.parse_error("Undefined property name\n"); valid = false; return; } values.push_back((*found).second->values[0]); } property::property(input_buffer &input, string k, string l, bool semicolonTerminated, define_map *defines) : key(k), label(l), valid(true) { do { input.next_token(); switch (input[0]) { case '$': { parse_define(input, defines); if (valid) { break; } } default: input.parse_error("Invalid property value."); valid = false; return; + case '/': + { + unsigned long long bits = 0; + valid = input.consume("/bits/"); + input.next_token(); + valid &= input.consume_integer(bits); + if ((bits != 8) && + (bits != 16) && + (bits != 32) && + (bits != 64)) { + input.parse_error("Invalid size for elements"); + valid = false; + } + if (!valid) return; + input.next_token(); + if (input[0] != '<') + { + input.parse_error("/bits/ directive is only valid on arrays"); + valid = false; + return; + } + parse_cells(input, bits); + break; + } case '"': parse_string(input); break; case '<': - parse_cells(input); + parse_cells(input, 32); break; case '[': parse_bytes(input); break; case '&': parse_reference(input); break; case ';': { break; } } input.next_token(); } while (input.consume(',')); if (semicolonTerminated && !input.consume(';')) { input.parse_error("Expected ; at end of property"); valid = false; } } -property* +property_ptr property::parse_dtb(input_buffer &structs, input_buffer &strings) { - property *p = new property(structs, strings); + property_ptr p(new property(structs, strings)); if (!p->valid) { - delete p; - p = 0; + p = nullptr; } return p; } -property* +property_ptr property::parse(input_buffer &input, string key, string label, bool semicolonTerminated, define_map *defines) { - property *p = new property(input, key, label, semicolonTerminated, defines); + property_ptr p(new property(input, key, label, semicolonTerminated, defines)); if (!p->valid) { - delete p; - p = 0; + p = nullptr; } return p; } void property::write(dtb::output_writer &writer, dtb::string_table &strings) { writer.write_token(dtb::FDT_PROP); byte_buffer value_buffer; for (value_iterator i=begin(), e=end() ; i!=e ; ++i) { i->push_to_buffer(value_buffer); } writer.write_data((uint32_t)value_buffer.size()); writer.write_comment(key); writer.write_data(strings.add_string(key)); writer.write_data(value_buffer); } +bool +property_value::try_to_merge(property_value &other) +{ + resolve_type(); + switch (type) + { + case UNKNOWN: + __builtin_unreachable(); + assert(0); + return false; + case EMPTY: + *this = other; + case STRING: + case STRING_LIST: + case CROSS_REFERENCE: + return false; + case PHANDLE: + case BINARY: + if (other.type == PHANDLE || other.type == BINARY) + { + type = BINARY; + byte_data.insert(byte_data.end(), other.byte_data.begin(), + other.byte_data.end()); + return true; + } + } + return false; +} + void property::write_dts(FILE *file, int indent) { for (int i=0 ; i *vals = &values; + std::vector v; + // If we've got multiple values then try to merge them all together. + if (values.size() > 1) + { + vals = &v; + v.push_back(values.front()); + for (auto i=(++begin()), e=end() ; i!=e ; ++i) + { + if (!v.back().try_to_merge(*i)) + { + v.push_back(*i); + } + } + } fputs(" = ", file); - for (value_iterator i=begin(), e=end() ; i!=e ; ++i) + for (auto i=vals->begin(), e=vals->end() ; i!=e ; ++i) { i->write_dts(file); if (i+1 != e) { putc(',', file); putc(' ', file); } } } fputs(";\n", file); } string node::parse_name(input_buffer &input, bool &is_property, const char *error) { if (!valid) { return string(); } input.next_token(); if (is_property) { return string::parse_property_name(input); } string n = string::parse_node_or_property_name(input, is_property); if (n.empty()) { if (n.empty()) { input.parse_error(error); valid = false; } } return n; } node::node(input_buffer &structs, input_buffer &strings) : valid(true) { const char *name_start = (const char*)structs; int name_length = 0; while (structs[0] != '\0' && structs[0] != '@') { name_length++; ++structs; } name = string(name_start, name_length); if (structs[0] == '@') { ++structs; name_start = (const char*)structs; name_length = 0; while (structs[0] != '\0') { name_length++; ++structs; } unit_address = string(name_start, name_length); } ++structs; uint32_t token; while (structs.consume_binary(token)) { switch (token) { default: fprintf(stderr, "Unexpected token 0x%" PRIx32 " while parsing node.\n", token); valid = false; return; // Child node, parse it. case dtb::FDT_BEGIN_NODE: { - node *child = node::parse_dtb(structs, strings); + node_ptr child = node::parse_dtb(structs, strings); if (child == 0) { valid = false; return; } - children.push_back(child); + children.push_back(std::move(child)); break; } // End of this node, no errors. case dtb::FDT_END_NODE: return; // Property, parse it. case dtb::FDT_PROP: { - property *prop = property::parse_dtb(structs, strings); + property_ptr prop = property::parse_dtb(structs, strings); if (prop == 0) { valid = false; return; } properties.push_back(prop); break; } break; // End of structs table. Should appear after // the end of the last node. case dtb::FDT_END: fprintf(stderr, "Unexpected FDT_END token while parsing node.\n"); valid = false; return; // NOPs are padding. Ignore them. case dtb::FDT_NOP: break; } } fprintf(stderr, "Failed to read token from structs table while parsing node.\n"); valid = false; return; } node::node(input_buffer &input, string n, string l, string a, define_map *defines) : label(l), name(n), unit_address(a), valid(true) { if (!input.consume('{')) { input.parse_error("Expected { to start new device tree node.\n"); } input.next_token(); while (valid && !input.consume('}')) { // 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; child_name = parse_name(input, is_property, "Expected property or node name"); if (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_name = parse_name(input, is_property, "Expected property or node name"); } if (input.consume('@')) { child_address = parse_name(input, is_property, "Expected unit address"); } if (!valid) { return; } input.next_token(); // If we're parsing a property, then we must actually do that. if (input.consume('=')) { - property *p= property::parse(input, child_name, + property_ptr p = property::parse(input, child_name, child_label, true, defines); if (p == 0) { valid = false; } else { properties.push_back(p); } } else if (!is_property && input[0] == ('{')) { - node *child = node::parse(input, child_name, + node_ptr child = node::parse(input, child_name, child_label, child_address, defines); if (child) { - children.push_back(child); + children.push_back(std::move(child)); } else { valid = false; } } else if (input.consume(';')) { - properties.push_back(new property(child_name, child_label)); + properties.push_back(property_ptr(new property(child_name, child_label))); } else { input.parse_error("Error parsing property."); valid = false; } input.next_token(); } input.consume(';'); } bool -node::cmp_properties(property *p1, property *p2) +node::cmp_properties(property_ptr &p1, property_ptr &p2) { return p1->get_key() < p2->get_key(); } bool -node::cmp_children(node *c1, node *c2) +node::cmp_children(node_ptr &c1, node_ptr &c2) { if (c1->name == c2->name) { return c1->unit_address < c2->unit_address; } return c1->name < c2->name; } void node::sort() { std::sort(property_begin(), property_end(), cmp_properties); std::sort(child_begin(), child_end(), cmp_children); for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i) { (*i)->sort(); } } -node* +node_ptr node::parse(input_buffer &input, string name, string label, string address, define_map *defines) { - node *n = new node(input, name, label, address, defines); + node_ptr n(new node(input, name, label, address, defines)); if (!n->valid) { - delete n; n = 0; } return n; } -node* +node_ptr node::parse_dtb(input_buffer &structs, input_buffer &strings) { - node *n = new node(structs, strings); + node_ptr n(new node(structs, strings)); if (!n->valid) { - delete n; n = 0; } return n; } -node::~node() -{ - while (!children.empty()) - { - delete children.back(); - children.pop_back(); - } - while (!properties.empty()) - { - delete properties.back(); - properties.pop_back(); - } -} - -property* +property_ptr node::get_property(string key) { - for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i) + for (auto &i : properties) { - if ((*i)->get_key() == key) + if (i->get_key() == key) { - return *i; + return i; } } return 0; } void -node::merge_node(node *other) +node::merge_node(node_ptr other) { if (!other->label.empty()) { label = other->label; } // Note: this is an O(n*m) operation. It might be sensible to // optimise this if we find that there are nodes with very // large numbers of properties, but for typical usage the // entire vector will fit (easily) into cache, so iterating // over it repeatedly isn't that expensive. - while (!other->properties.empty()) + for (auto &p : other->properties) { - property *p = other->properties.front(); - for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i) + bool found = false; + for (auto i=property_begin(), e=property_end() ; i!=e ; ++i) { if ((*i)->get_key() == p->get_key()) { - delete *i; - properties.erase(i); + *i = p; + found = true; break; } } - add_property(p); - other->properties.erase(other->properties.begin()); + if (!found) + { + add_property(p); + } } - while (!other->children.empty()) + for (auto &c : other->children) { - node *c = other->children.front(); bool found = false; - for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i) + for (auto &i : children) { - if ((*i)->name == c->name && (*i)->unit_address == c->unit_address) + if (i->name == c->name && i->unit_address == c->unit_address) { - (*i)->merge_node(c); - delete c; + i->merge_node(std::move(c)); found = true; break; } } if (!found) { - children.push_back(c); + children.push_back(std::move(c)); } - other->children.erase(other->children.begin()); } } void 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); if (unit_address != string()) { name_buffer.push_back('@'); unit_address.push_to_buffer(name_buffer); } writer.write_comment(name); writer.write_data(name_buffer); writer.write_data((uint8_t)0); - for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i) + for (auto i=property_begin(), e=property_end() ; i!=e ; ++i) { (*i)->write(writer, strings); } for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i) { (*i)->write(writer, strings); } writer.write_token(dtb::FDT_END_NODE); } void node::write_dts(FILE *file, int indent) { for (int i=0 ; iwrite_dts(file, indent+1); } for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i) { (*i)->write_dts(file, indent+1); } for (int i=0 ; ilabel; path.push_back(std::make_pair(n->name, n->unit_address)); if (name != string()) { if (node_names.find(name) == node_names.end()) { - node_names.insert(std::make_pair(name, n)); + node_names.insert(std::make_pair(name, n.get())); node_paths.insert(std::make_pair(name, path)); } else { node_names[name] = (node*)-1; - std::map::iterator i = node_paths.find(name); + auto i = node_paths.find(name); if (i != node_paths.end()) { node_paths.erase(name); } fprintf(stderr, "Label not unique: "); name.dump(); fprintf(stderr, ". References to this label will not be resolved."); } } for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ; ++i) { collect_names_recursive(*i, path); } path.pop_back(); // Now we collect the phandles and properties that reference // other nodes. - 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) { for (property::value_iterator p=(*i)->begin(),pe=(*i)->end() ; p!=pe ; ++p) { if (p->is_phandle()) { phandles.push_back(&*p); } if (p->is_cross_reference()) { cross_references.push_back(&*p); } } if ((*i)->get_key() == string("phandle") || (*i)->get_key() == string("linux,phandle")) { if ((*i)->begin()->byte_data.size() != 4) { fprintf(stderr, "Invalid phandle value for node "); n->name.dump(); fprintf(stderr, ". Should be a 4-byte value.\n"); valid = false; } else { uint32_t phandle = (*i)->begin()->get_as_uint32(); - used_phandles.insert(std::make_pair(phandle, n)); + used_phandles.insert(std::make_pair(phandle, n.get())); } } } } void device_tree::collect_names() { node_path p; + node_names.clear(); + node_paths.clear(); + cross_references.clear(); + phandles.clear(); collect_names_recursive(root, p); } void device_tree::resolve_cross_references() { - for (std::vector::iterator i=cross_references.begin(), e=cross_references.end() ; i!=e ; ++i) + for (auto *pv : cross_references) { - property_value* pv = *i; node_path path = node_paths[pv->string_data]; // Skip the first name in the path. It's always "", and implicitly / - for (node_path::iterator p=path.begin()+1, pe=path.end() ; p!=pe ; ++p) + for (auto p=path.begin()+1, pe=path.end() ; p!=pe ; ++p) { pv->byte_data.push_back('/'); p->first.push_to_buffer(pv->byte_data); if (!(p->second.empty())) { pv->byte_data.push_back('@'); p->second.push_to_buffer(pv->byte_data); } } - pv->byte_data.push_back(0); } uint32_t phandle = 1; - for (std::vector::iterator i=phandles.begin(), e=phandles.end() ; i!=e ; ++i) + for (auto &i : phandles) { - string target_name = (*i)->string_data; + string target_name = i->string_data; node *target = node_names[target_name]; if (target == 0) { - fprintf(stderr, "Failed to find node with label:"); + fprintf(stderr, "Failed to find node with label: "); target_name.dump(); fprintf(stderr, "\n"); valid = 0; return; } // If there is an existing phandle, use it - property *p = target->get_property("phandle"); + property_ptr p = target->get_property("phandle"); if (p == 0) { p = target->get_property("linux,phandle"); } if (p == 0) { // Otherwise insert a new phandle node property_value v; while (used_phandles.find(phandle) != used_phandles.end()) { // Note that we only don't need to // store this phandle in the set, // because we are monotonically // increasing the value of phandle and // so will only ever revisit this value // if we have used 2^32 phandles, at // which point our blob won't fit in // any 32-bit system and we've done // something badly wrong elsewhere // already. phandle++; } push_big_endian(v.byte_data, phandle++); if (phandle_node_name == BOTH || phandle_node_name == LINUX) { - p = new property(string("linux,phandle")); + p.reset(new property(string("linux,phandle"))); p->add_value(v); target->add_property(p); } if (phandle_node_name == BOTH || phandle_node_name == EPAPR) { - p = new property(string("phandle")); + p.reset(new property(string("phandle"))); p->add_value(v); target->add_property(p); } } - p->begin()->push_to_buffer((*i)->byte_data); - assert((*i)->byte_data.size() == 4); + p->begin()->push_to_buffer(i->byte_data); + assert(i->byte_data.size() == 4); } } void -device_tree::parse_roots(input_buffer &input, std::vector &roots) +device_tree::parse_file(input_buffer &input, + const std::string &dir, + std::vector &roots, + FILE *depfile, + bool &read_header) { input.next_token(); - while (valid && input.consume('/')) + // Read the header + if (input.consume("/dts-v1/;")) { + read_header = true; + } + input.next_token(); + while(input.consume("/include/")) + { + 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(); - node *n = node::parse(input, string("", 1), string(), string(), &defines); + if (!input.consume('"')) + { + input.parse_error("Expected quoted filename"); + valid = false; + return; + } + int length = 0; + while (input[length] != '"') length++; + + std::string file((const char*)input, length); + std::string include_file = dir + '/' + file; + assert(input.consume(file.c_str())); + input.consume('"'); + input.next_token(); + if (!reallyInclude) + { + continue; + } + + input_buffer *include_buffer = buffer_for_file(include_file.c_str()); + + 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) + { + valid = false; + return; + } + parse_file(*include_buffer, dir, roots, depfile, read_header); + } + input.next_token(); + if (!read_header) + { + input.parse_error("Expected /dts-v1/; version string"); + } + // Read any memory reservations + while(input.consume("/memreserve/")) + { + unsigned long long start, len; + input.next_token(); + // Read the start and length. + if (!(input.consume_integer(start) && + (input.next_token(), + input.consume_integer(len)))) + { + input.parse_error("Expected size on /memreserve/ node."); + } + input.next_token(); + input.consume(';'); + reservations.push_back(reservation(start, len)); + } + input.next_token(); + while (valid && !input.finished()) + { + node_ptr n; + if (input.consume('/')) + { + input.next_token(); + n = node::parse(input, string(), string(), string(), &defines); + } + else if (input.consume('&')) + { + input.next_token(); + string name = string::parse_node_name(input); + input.next_token(); + n = node::parse(input, name, string(), string(), &defines); + } + else + { + input.parse_error("Failed to find root node /."); + } if (n) { - roots.push_back(n); + roots.push_back(std::move(n)); } else { valid = false; } input.next_token(); } } input_buffer* device_tree::buffer_for_file(const char *path) { if (string(path) == string("-")) { input_buffer *b = new stream_input_buffer(); - buffers.push_back(b); + if (b) + { + std::unique_ptr ptr(b); + buffers.push_back(std::move(ptr)); + } return b; } int source = open(path, O_RDONLY); if (source == -1) { - fprintf(stderr, "Unable to open file %s\n", path); + 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. - buffers.push_back(b); + if (b) + { + std::unique_ptr ptr(b); + buffers.push_back(std::move(ptr)); + } close(source); return b; } template void device_tree::write(int fd) { dtb::string_table st; dtb::header head; writer head_writer; writer reservation_writer; writer struct_writer; writer strings_writer; // Build the reservation table reservation_writer.write_comment(string("Memory reservations")); reservation_writer.write_label(string("dt_reserve_map")); - for (std::vector::iterator i=reservations.begin(), - e=reservations.end() ; i!=e ; ++i) + for (auto &i : reservations) { reservation_writer.write_comment(string("Reservation start")); - reservation_writer.write_data(i->first); + reservation_writer.write_data(i.first); reservation_writer.write_comment(string("Reservation length")); - reservation_writer.write_data(i->first); + reservation_writer.write_data(i.first); } // Write n spare reserve map entries, plus the trailing 0. for (uint32_t i=0 ; i<=spare_reserve_map_entries ; i++) { reservation_writer.write_data((uint64_t)0); reservation_writer.write_data((uint64_t)0); } struct_writer.write_comment(string("Device tree")); struct_writer.write_label(string("dt_struct_start")); root->write(struct_writer, st); struct_writer.write_token(dtb::FDT_END); struct_writer.write_label(string("dt_struct_end")); st.write(strings_writer); // Find the strings size before we stick padding on the end. // Note: We should possibly use a new writer for the padding. head.size_dt_strings = strings_writer.size(); // Stick the padding in the strings writer, but after the // marker indicating that it's the end. // Note: We probably should add a padding call to the writer so // that the asm back end can write padding directives instead // of a load of 0 bytes. for (uint32_t i=0 ; i(fd); } void device_tree::write_asm(int fd) { write(fd); } void device_tree::write_dts(int fd) { FILE *file = fdopen(fd, "w"); fputs("/dts-v1/;\n\n", file); if (!reservations.empty()) { const char msg[] = "/memreserve/"; fwrite(msg, sizeof(msg), 1, file); - for (std::vector::iterator i=reservations.begin(), - e=reservations.end() ; i!=e ; ++i) + for (auto &i : reservations) { - fprintf(file, " %" PRIx64 " %" PRIx64, i->first, i->second); + fprintf(file, " %" PRIx64 " %" PRIx64, i.first, i.second); } fputs(";\n\n", file); } putc('/', file); putc(' ', file); root->write_dts(file, 0); fclose(file); } void device_tree::parse_dtb(const char *fn, FILE *depfile) { input_buffer *in = buffer_for_file(fn); if (in == 0) { valid = false; return; } input_buffer &input = *in; dtb::header h; valid = h.read_dtb(input); boot_cpu = h.boot_cpuid_phys; if (h.last_comp_version > 17) { fprintf(stderr, "Don't know how to read this version of the device tree blob"); valid = false; } if (!valid) { return; } input_buffer reservation_map = input.buffer_from_offset(h.off_mem_rsvmap, 0); uint64_t start, length; do { if (!(reservation_map.consume_binary(start) && reservation_map.consume_binary(length))) { fprintf(stderr, "Failed to read memory reservation table\n"); valid = false; return; } } while (!((start == 0) && (length == 0))); input_buffer struct_table = input.buffer_from_offset(h.off_dt_struct, h.size_dt_struct); input_buffer strings_table = input.buffer_from_offset(h.off_dt_strings, h.size_dt_strings); uint32_t token; if (!(struct_table.consume_binary(token) && (token == dtb::FDT_BEGIN_NODE))) { fprintf(stderr, "Expected FDT_BEGIN_NODE token.\n"); valid = false; return; } root = node::parse_dtb(struct_table, strings_table); if (!(struct_table.consume_binary(token) && (token == dtb::FDT_END))) { fprintf(stderr, "Expected FDT_END token after parsing root node.\n"); valid = false; return; } valid = (root != 0); } void device_tree::parse_dts(const char *fn, FILE *depfile) { input_buffer *in = buffer_for_file(fn); + std::string dir(dirname((char*)fn)); if (in == 0) { valid = false; return; } - std::vector roots; + std::vector roots; input_buffer &input = *in; - input.next_token(); bool read_header = false; - // Read the header - if (input.consume("/dts-v1/;")) - { - read_header = true; - } - input.next_token(); - while(input.consume("/include/")) - { - 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; - } - int length = 0; - while (input[length] != '"') length++; - - const char *file = (const char*)input; - const char *dir = dirname(fn); - int dir_length = strlen(dir); - char *include_file = (char*)malloc(strlen(dir) + length + 2); - memcpy(include_file, dir, dir_length); - include_file[dir_length] = '/'; - memcpy(include_file+dir_length+1, file, length); - include_file[dir_length+length+1] = 0; - - input.consume(include_file+dir_length+1); - input.consume('"'); - if (!reallyInclude) - { - continue; - } - - input_buffer *include_buffer = buffer_for_file(include_file); - - if (include_buffer == 0) - { - for (std::vector::iterator i=include_paths.begin(), e=include_paths.end() ; e!=i ; ++i) - { - free(include_file); - dir = *i; - dir_length = strlen(dir); - include_file = (char*)malloc(strlen(dir) + - length + 2); - memcpy(include_file, dir, dir_length); - include_file[dir_length] = '/'; - memcpy(include_file+dir_length+1, file, length); - include_file[dir_length+length+1] = 0; - include_buffer = buffer_for_file(include_file); - if (include_buffer != 0) - { - break; - } - } - } - if (depfile != 0) - { - putc(' ', depfile); - fputs(include_file, depfile); - } - if (include_buffer == 0) - { - valid = false; - return; - } - input_buffer &include = *include_buffer; - free((void*)include_file); - - if (!read_header) - { - include.next_token(); - read_header = include.consume("/dts-v1/;"); - } - parse_roots(include, roots); - } - input.next_token(); - if (!read_header) - { - input.parse_error("Expected /dts-v1/; version string"); - } - // Read any memory reservations - while(input.consume("/memreserve/")) - { - long long start, len; - input.next_token(); - // Read the start and length. - if (!(input.consume_integer(start) && - (input.next_token(), - input.consume_integer(len)))) - { - input.parse_error("Expected size on /memreserve/ node."); - } - input.next_token(); - input.consume(';'); - reservations.push_back(reservation(start, len)); - } - parse_roots(input, roots); + parse_file(input, dir, roots, depfile, read_header); switch (roots.size()) { case 0: valid = false; input.parse_error("Failed to find root node /."); return; case 1: - root = roots[0]; + root = std::move(roots[0]); break; default: { - root = roots[0]; - for (std::vector::iterator i=roots.begin()+1, - e=roots.end() ; i!=e ; ++i) + root = std::move(roots[0]); + for (auto i=++(roots.begin()), e=roots.end() ; i!=e ; ++i) { - root->merge_node(*i); - delete *i; + auto &node = *i; + string name = node->name; + if (name == string()) + { + root->merge_node(std::move(node)); + } + else + { + auto existing = node_names.find(name); + if (existing == node_names.end()) + { + collect_names(); + existing = node_names.find(name); + } + if (existing == node_names.end()) + { + fprintf(stderr, "Unable to merge node: "); + name.dump(); + fprintf(stderr, "\n"); + } + existing->second->merge_node(std::move(node)); + } } - roots.resize(1); } } collect_names(); resolve_cross_references(); } -device_tree::~device_tree() -{ - if (root != 0) - { - delete root; - } - while (!buffers.empty()) - { - delete buffers.back(); - buffers.pop_back(); - } - for (define_map::iterator i=defines.begin(), e=defines.end() ; - i!=e ; ++i) - { - delete i->second; - } -} - bool device_tree::parse_define(const char *def) { char *val = strchr(def, '='); if (!val) { if (strlen(def) != 0) { string name(def); defines[name]; return true; } return false; } string name(def, val-def); val++; input_buffer in = input_buffer(val, strlen(val)); - property *p = property::parse(in, name, string(), false); + property_ptr p = property::parse(in, name, string(), false); if (p) defines[name] = p; - return p; + return (bool)p; } } // namespace fdt } // namespace dtc Index: head/usr.bin/dtc/fdt.hh =================================================================== --- head/usr.bin/dtc/fdt.hh (revision 289934) +++ head/usr.bin/dtc/fdt.hh (revision 289935) @@ -1,803 +1,823 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _FDT_HH_ #define _FDT_HH_ -#include +#include +#include +#include +#include #include "util.hh" #include "string.hh" namespace dtc { namespace dtb { struct output_writer; class string_table; } namespace fdt { class property; -typedef std::map define_map; +class node; /** + * Type for (owned) pointers to properties. + */ +typedef std::shared_ptr property_ptr; +/** + * Owning pointer to a node. + */ +typedef std::unique_ptr node_ptr; +/** + * Map from macros to property pointers. + */ +typedef std::unordered_map define_map; +/** * Properties may contain a number of different value, each with a different * label. This class encapsulates a single value. */ struct property_value { /** * The label for this data. This is usually empty. */ 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; /** * The data that should be written to the final output. */ byte_buffer byte_data; /** * Enumeration describing the possible types of a value. Note that * property-coded arrays will appear simply as binary (or possibly * string, if they happen to be nul-terminated and printable), and must * be checked separately. */ enum value_type { /** * This is a list of strings. When read from source, string * lists become one property value for each string, however * when read from binary we have a single property value * incorporating the entire text, with nul bytes separating the * strings. */ STRING_LIST, /** * This property contains a single string. */ STRING, /** * This is a binary value. Check the size of byte_data to * determine how many bytes this contains. */ BINARY, /** This contains a short-form address that should be replaced * by a fully-qualified version. This will only appear when * the input is a device tree source. When parsed from a * device tree blob, the cross reference will have already been * resolved and the property value will be a string containing * the full path of the target node. */ CROSS_REFERENCE, /** * This is a phandle reference. When parsed from source, the * string_data will contain the node label for the target and, * after cross references have been resolved, the binary data * will contain a 32-bit integer that should match the phandle * property of the target node. */ PHANDLE, /** * An empty property value. This will never appear on a real * property value, it is used by checkers to indicate that no * property values should exist for a property. */ EMPTY, /** * The type of this property has not yet been determined. */ UNKNOWN }; /** * The type of this property. */ value_type type; /** * Returns true if this value is a cross reference, false otherwise. */ inline bool is_cross_reference() { return is_type(CROSS_REFERENCE); } /** * Returns true if this value is a phandle reference, false otherwise. */ inline bool is_phandle() { return is_type(PHANDLE); } /** * Returns true if this value is a string, false otherwise. */ inline bool is_string() { return is_type(STRING); } /** * Returns true if this value is a string list (a nul-separated * sequence of strings), false otherwise. */ inline bool is_string_list() { return is_type(STRING_LIST); } /** * Returns true if this value is binary, false otherwise. */ inline bool is_binary() { return is_type(BINARY); } /** * Returns this property value as a 32-bit integer. Returns 0 if this * property value is not 32 bits long. The bytes in the property value * are assumed to be in big-endian format, but the return value is in * the host native endian. */ uint32_t get_as_uint32(); /** * Default constructor, specifying the label of the value. */ property_value(string l=string()) : label(l), type(UNKNOWN) {} /** * Writes the data for this value into an output buffer. */ void push_to_buffer(byte_buffer &buffer); /** * Writes the property value to the standard output. This uses the * following heuristics for deciding how to print the output: * * - If the value is nul-terminated and only contains printable * characters, it is written as a string. * - If it is a multiple of 4 bytes long, then it is printed as cells. * - 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 * the value has not yet been determined, then this calculates it. */ inline bool is_type(value_type v) { if (type == UNKNOWN) { resolve_type(); } return type == v; } /** * Determines the type of the value based on its contents. */ void resolve_type(); /** * Writes the property value to the specified file as a quoted string. * This is used when generating DTS. */ void write_as_string(FILE *file); /** * Writes the property value to the specified file as a sequence of * 32-bit big-endian cells. This is used when generating DTS. */ void write_as_cells(FILE *file); /** * Writes the property value to the specified file as a sequence of * bytes. This is used when generating DTS. */ void write_as_bytes(FILE *file); }; /** * A value encapsulating a single property. This contains a key, optionally a * label, and optionally one or more values. */ class property { /** * The name of this property. */ string key; /** * An optional label. */ string label; /** * The values in this property. */ std::vector values; /** * Value indicating that this is a valid property. If a parse error * occurs, then this value is false. */ bool valid; /** * Parses a string property value, i.e. a value enclosed in double quotes. */ void parse_string(input_buffer &input); /** * 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. */ void parse_bytes(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. * * Note: The specification says that the target of such a reference is * 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); /** * Parse a predefined macro definition for a property. */ void parse_define(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. * The structs input buffer is assumed to have just consumed the * FDT_PROP token. */ property(input_buffer &structs, input_buffer &strings); /** * Parses a new property from the input buffer. */ property(input_buffer &input, string k, string l, bool terminated, define_map *defines); public: /** * Creates an empty property. */ property(string k, string l=string()) : key(k), label(l), valid(true) {} /** * Copy constructor. */ property(property &p) : key(p.key), label(p.label), values(p.values), valid(p.valid) {} /** * 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_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. */ typedef std::vector::iterator value_iterator; /** * Returns an iterator referring to the first value in this property. */ inline value_iterator begin() { return values.begin(); } /** * Returns an iterator referring to the last value in this property. */ inline value_iterator end() { return values.end(); } /** * Adds a new value to an existing property. */ inline void add_value(property_value v) { values.push_back(v); } /** * Returns the key for this property. */ inline string get_key() { return key; } /** * Writes the property to the specified writer. The property name is a * reference into the strings table. */ void write(dtb::output_writer &writer, dtb::string_table &strings); /** * Writes in DTS format to the specified file, at the given indent * level. This will begin the line with the number of tabs specified * as the indent level and then write the property in the most * applicable way that it can determine. */ void write_dts(FILE *file, int indent); }; /** * Class encapsulating a device tree node. Nodes may contain properties and * other nodes. */ class node { public: /** * The label for this node, if any. Node labels are used as the * targets for cross references. */ string label; /** * The name of the node. */ string name; /** * The unit address of the node, which is optionally written after the * name followed by an at symbol. */ string unit_address; + /** + * The type for the property vector. + */ + typedef std::vector property_vector; private: /** * The properties contained within this node. */ - std::vector properties; + property_vector properties; /** * The children of this node. */ - std::vector children; + std::vector children; /** * A flag indicating whether this node is valid. This is set to false * if an error occurs during parsing. */ bool valid; /** * 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); /** * Constructs a new node from two input buffers, pointing to the struct * and strings tables in the device tree blob, respectively. */ node(input_buffer &structs, input_buffer &strings); /** * Parses a new node from the specified input buffer. This is called * when the input cursor is on the open brace for the start of the * 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*); /** * 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(); } */ /** * Comparison function for nodes, used when sorting the children * 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 * recursively sorts the children. */ void sort(); /** * Iterator type for child nodes. */ - typedef std::vector::iterator child_iterator; + typedef std::vector::iterator child_iterator; /** * Returns an iterator for the first child of this node. */ inline child_iterator child_begin() { return children.begin(); } /** * Returns an iterator after the last child of this node. */ inline child_iterator child_end() { return children.end(); } /** - * Iterator type for properties of a node. - */ - typedef std::vector::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(); } /** * Factory method for constructing a new node. Attempts to parse a * node in DTS format from the input, and returns it on success. On * any parse error, this will return 0. This should be called with the * 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 * any parse error, this will return 0. This should be called with the * 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); + static node_ptr parse_dtb(input_buffer &structs, input_buffer &strings); /** - * Destroys the node, recursively deleting all of its properties and - * children. - */ - ~node(); - /** * 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); } /** * 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 * table passed as the second argument is used for the names of * properties within this node and its children. */ void write(dtb::output_writer &writer, dtb::string_table &strings); /** * Writes the current node as DTS to the specified file. The second * parameter is the indent level. This function will start every line * with this number of tabs. */ void write_dts(FILE *file, int indent); }; /** * Class encapsulating the entire parsed FDT. This is the top-level class, * which parses the entire DTS representation and write out the finished * version. */ class device_tree { public: /** * Type used for node paths. A node path is sequence of names and unit * addresses. */ typedef std::vector > node_path; /** * Name that we should use for phandle nodes. */ enum phandle_format { /** linux,phandle */ LINUX, /** phandle */ EPAPR, /** Create both nodes. */ BOTH }; private: /** * The format that we should use for writing phandles. */ phandle_format phandle_node_name; /** * Flag indicating that this tree is valid. This will be set to false * on parse errors. */ bool valid; /** * Type used for memory reservations. A reservation is two 64-bit * values indicating a base address and length in memory that the * kernel should not use. The high 32 bits are ignored on 32-bit * platforms. */ typedef std::pair reservation; /** * The memory reserves table. */ std::vector reservations; /** * 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 node_names; + std::unordered_map 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 node_paths; + std::unordered_map node_paths; /** * A collection of property values that are references to other nodes. * These should be expanded to the full path of their targets. */ std::vector cross_references; /** * A collection of property values that refer to phandles. These will * be replaced by the value of the phandle property in their * destination. */ std::vector phandles; /** + * The names of nodes that target phandles. + */ + std::unordered_set 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 buffers; + std::vector> 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 * input to ensure that we don't reuse them. * * This is a map, rather than a set, because we also want to be able to * find phandles that were provided by the user explicitly when we are * doing checking. */ - std::map used_phandles; + std::unordered_map 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 include_paths; + std::vector include_paths; /** * Dictionary of predefined macros provided on the command line. */ define_map defines; /** * The default boot CPU, specified in the device tree header. */ uint32_t boot_cpu; /** * The number of empty reserve map entries to generate in the blob. */ uint32_t spare_reserve_map_entries; /** * The minimum size in bytes of the blob. */ uint32_t minimum_blob_size; /** * The number of bytes of padding to add to the end of the blob. */ uint32_t blob_padding; /** * Visit all of the nodes recursively, and if they have labels then add * them to the node_paths and node_names vectors so that they can be * 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. */ void collect_names(); /** * Resolves all cross references. Any properties that refer to another * node must have their values replaced by either the node path or * phandle value. */ 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 &roots); + void parse_file(input_buffer &input, + const std::string &dir, + std::vector &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); /** * Template function that writes a dtb blob using the specified writer. * The writer defines the output format (assembly, blob). */ template void write(int fd); public: /** * Returns the node referenced by the property. If this is a tree that * is in source form, then we have a string that we can use to index * the cross_references array and so we can just look that up. */ node *referenced_node(property_value &v); /** * Writes this FDT as a DTB to the specified output. */ void write_binary(int fd); /** * Writes this FDT as an assembly representation of the DTB to the * specified output. The result can then be assembled and linked into * a program. */ void write_asm(int fd); /** * Writes the tree in DTS (source) format. */ void write_dts(int fd); /** * 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) {} /** * 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); /** * 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); /** - * Destroy the tree and any input buffers that it holds. - */ - ~device_tree(); - /** * Returns whether this tree is valid. */ inline bool is_valid() { return valid; } /** * Sets the format for writing phandle properties. */ inline void set_phandle_format(phandle_format f) { phandle_node_name = f; } /** * 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; } /** * Sets the physical boot CPU. */ void set_boot_cpu(uint32_t cpu) { boot_cpu = cpu; } /** * Sorts the tree. Useful for debugging device trees. */ void sort() { root->sort(); } /** * Adds a path to search for include files. The argument must be a * nul-terminated string representing the path. The device tree keeps * a pointer to this string, but does not own it: the caller is * responsible for freeing it if required. */ 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. */ void set_empty_reserve_map_entries(uint32_t e) { spare_reserve_map_entries = e; } /** * Sets the minimum size, in bytes, of the blob. */ void set_blob_minimum_size(uint32_t s) { minimum_blob_size = s; } /** * Sets the amount of padding to add to the blob. */ void set_blob_padding(uint32_t p) { blob_padding = p; } /** * Parses a predefined macro value. */ bool parse_define(const char *def); }; } // namespace fdt } // namespace dtc #endif // !_FDT_HH_ Index: head/usr.bin/dtc/input_buffer.cc =================================================================== --- head/usr.bin/dtc/input_buffer.cc (revision 289934) +++ head/usr.bin/dtc/input_buffer.cc (revision 289935) @@ -1,269 +1,269 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "input_buffer.hh" #include #include #include #include #include #include #include #include #include #ifndef MAP_PREFAULT_READ #define MAP_PREFAULT_READ 0 #endif namespace dtc { void input_buffer::skip_spaces() { if (cursor >= size) { return; } if (cursor < 0) { return; } char c = buffer[cursor]; while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f') || (c == '\v') || (c == '\r')) { cursor++; if (cursor > size) { c = '\0'; } else { c = buffer[cursor]; } } } input_buffer input_buffer::buffer_from_offset(int offset, int s) { if (s == 0) { s = size - offset; } if (offset > size) { return input_buffer(); } if (s > (size-offset)) { return input_buffer(); } return input_buffer(&buffer[offset], s); } bool input_buffer::consume(const char *str) { int len = strlen(str); if (len > size - cursor) { return false; } else { for (int i=0 ; i0 ; --i) { if (buffer[i] == '\n') { line_count++; if (line_start == 0) { line_start = i+1; } } } for (int i=cursor+1 ; i namespace dtc { /** * Class encapsulating the input file. Can be used as a const char*, but has * range checking. Attempting to access anything out of range will return a 0 * byte. The input buffer can be cheaply copied, without copying the * underlying memory, however it is the user's responsibility to ensure that * such copies do not persist beyond the lifetime of the underlying memory. * * This also contains methods for reporting errors and for consuming the token * stream. */ class input_buffer { protected: /** * The buffer. This class doesn't own the buffer, but the * mmap_input_buffer subclass does. */ const char* buffer; /** * The size of the buffer. */ int size; private: /** * 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 * off either end. */ int cursor; /** * Private constructor. This is used to create input buffers that * refer to the same memory, but have different cursors. */ input_buffer(const char* b, int s, int c) : buffer(b), size(s), cursor(c) {} /** * 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(); 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. */ virtual ~input_buffer() {}; /** * Constructs an empty buffer. */ input_buffer() : buffer(0), size(0), cursor(0) {} /** * Constructs a new buffer with a specified memory region and size. */ input_buffer(const char* b, int s) : buffer(b), size(s), cursor(0){} /** * Returns a new input buffer referring into this input, clamped to the * specified size. If the requested buffer would fall outside the * range of this one, then it returns an empty buffer. * * The returned buffer shares the same underlying storage as the * original. This is intended to be used for splitting up the various * sections of a device tree blob. Requesting a size of 0 will give a * buffer that extends to the end of the available memory. */ 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. */ inline char operator*() { if (cursor >= size) { return '\0'; } if (cursor < 0) { return '\0'; } return buffer[cursor]; } /** * Array subscripting operator, returns a character at the specified * index offset from the current cursor. The offset may be negative, * to reread characters that have already been read. If the current * cursor plus offset is outside of the range, this returns a nul * byte. */ inline char operator[](int offset) { if (cursor + offset >= size) { return '\0'; } if (cursor + offset < 0) { return '\0'; } return buffer[cursor + offset]; } /** * Increments the cursor, iterating forward in the buffer. */ inline input_buffer &operator++() { cursor++; 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) { ++(*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. */ bool consume(const char *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. */ - 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 * there is a value of the correct size. This function assumes that * all values must be natively aligned, and so advances the cursor to * the correct alignment before reading. */ template bool consume_binary(T &out) { int align = 0; int type_size = sizeof(T); if (cursor % type_size != 0) { align = type_size - (cursor % type_size); } if (size < cursor + align + type_size) { return false; } cursor += align; assert(cursor % type_size == 0); out = 0; for (int i=0 ; i inline bool input_buffer::consume_binary(uint8_t &out) { if (size < cursor + 1) { return false; } out = buffer[cursor++]; return true; } /** * 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 input_buffer { /** * Constructs a new buffer from the file passed in as a file * descriptor. */ mmap_input_buffer(int fd); /** * 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 input_buffer { /** * The buffer that will store the data read from the standard input. */ std::vector b; /** * Constructs a new buffer from the standard input. */ stream_input_buffer(); }; } // namespace dtc #endif // !_INPUT_BUFFER_HH_ Index: head/usr.bin/dtc/string.hh =================================================================== --- head/usr.bin/dtc/string.hh (revision 289934) +++ head/usr.bin/dtc/string.hh (revision 289935) @@ -1,147 +1,164 @@ /*- * Copyright (c) 2013 David Chisnall * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _STRING_HH_ #define _STRING_HH_ #include "input_buffer.hh" +#include +#include namespace dtc { /** * String, referring to a place in the input file. We don't bother copying * strings until we write them to the final output. These strings should be * two words long: a start and a length. They are intended to be cheap to copy * and store in collections. Copying the string object does not copy the * underlying storage. * * Strings are not nul-terminated. */ class string { + friend std::hash; /** Start address. Contained within the mmap()'d input file and not * owned by this object. */ const char *start; /** length of the string. DTS strings are allowed to contain nuls */ int length; /** Generic function for parsing strings matching the character set * defined by the template argument. */ template static string parse(input_buffer &s); public: /** * Constructs a string referring into another buffer. */ string(const char *s, int l) : start(s), length(l) {} /** Constructs a string from a C string. */ string(const char *s) : start(s), length(strlen(s)) {} /** Default constructor, returns an empty string. */ string() : start(0), length(0) {} /** Construct a from an input buffer, ending with a nul terminator. */ string(input_buffer &s); /** * Returns the longest string in the input buffer starting at the * current cursor and composed entirely of characters that are valid in * node names. */ static string parse_node_name(input_buffer &s); /** * Returns the longest string in the input buffer starting at the * current cursor and composed entirely of characters that are valid in * property names. */ static string parse_property_name(input_buffer &s); /** * 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. */ static string parse_node_or_property_name(input_buffer &s, bool &is_property); /** * Compares two strings for equality. Strings are equal if they refer * to identical byte sequences. */ bool operator==(const string& other) const; /** * Compares a string against a C string. The trailing nul in the C * string is ignored for the purpose of comparison, so this will always * fail if the string contains nul bytes. */ bool operator==(const char *other) const; /** * Inequality operator, defined as the inverse of the equality * operator. */ template inline bool operator!=(T other) { return !(*this == other); } /** * Comparison operator, defined to allow strings to be used as keys in * maps. */ bool operator<(const string& other) const; /** * Returns true if this is the empty string, false otherwise. */ inline bool empty() const { return length == 0; } /** * Returns the size of the string, in bytes. */ inline size_t size() { return length; } /** * Writes the string to the specified buffer. */ void push_to_buffer(byte_buffer &buffer, bool escapes=false); /** * Prints the string to the specified output stream. */ void print(FILE *file); /** * Dumps the string to the standard error stream. Intended to be used * for debugging. */ void dump(); }; } // namespace dtc +namespace std +{ + template<> + struct hash + { + std::size_t operator()(dtc::string const& s) const + { + std::string str(s.start, s.length); + std::hash h; + return h(str); + } + }; +} + #endif // !_STRING_HH_