/*- * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #ifndef _FDT_HH_ #define _FDT_HH_ #include #include #include #include #include #include #include "util.hh" #include "input_buffer.hh" namespace dtc { namespace dtb { struct output_writer; class string_table; } namespace fdt { class property; class node; class device_tree; /** * Type for device tree write functions. */ typedef void (device_tree::* tree_write_fn_ptr)(int); /** * Type for device tree read functions. */ typedef void (device_tree::* tree_read_fn_ptr)(const std::string &, FILE *); /** * Type for (owned) pointers to properties. */ typedef std::shared_ptr property_ptr; /** * Owning pointer to a node. */ typedef std::shared_ptr node_ptr; /** * Map from macros to property pointers. */ typedef std::unordered_map define_map; /** * Set of strings used for label names. */ typedef std::unordered_set string_set; /** * 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. */ std::string label; /** * If this value is a string, or something resolved from a string (a * reference) then this contains the source string. */ std::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(std::string l=std::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); /** * Returns the size (in bytes) of this property value. */ size_t size(); 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. */ std::string key; /** * Zero or more labels. */ string_set labels; /** * 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(text_input_buffer &input); /** * Parses one or more 32-bit values enclosed in angle brackets. */ void parse_cells(text_input_buffer &input, int cell_size); /** * Parses an array of bytes, contained within square brackets. */ void parse_bytes(text_input_buffer &input); /** * Parses a reference. This is a node label preceded by an ampersand * symbol, which should expand to the full path to that node. * * 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(text_input_buffer &input); /** * Parse a predefined macro definition for a property. */ void parse_define(text_input_buffer &input, define_map *defines); /** * Constructs a new property from two input buffers, pointing to the * struct and strings tables in the device tree blob, respectively. * 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(text_input_buffer &input, std::string &&k, string_set &&l, bool terminated, define_map *defines); public: /** * Creates an empty property. */ property(std::string &&k, string_set &&l=string_set()) : key(k), labels(l), valid(true) {} /** * Copy constructor. */ property(property &p) : key(p.key), labels(p.labels), values(p.values), valid(p.valid) {} /** * Factory method for constructing a new property. Attempts to parse a * property from the input, and returns it on success. On any parse * error, this will return 0. */ 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_ptr parse(text_input_buffer &input, std::string &&key, string_set &&labels=string_set(), 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 const std::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); /** * Returns the byte offset of the specified property value. */ size_t offset_of_value(property_value &val); }; /** * Class encapsulating a device tree node. Nodes may contain properties and * other nodes. */ class node { public: /** * The labels for this node, if any. Node labels are used as the * targets for cross references. */ std::unordered_set labels; /** * The name of the node. */ std::string name; /** * The name of the node is a path reference. */ bool name_is_path_reference = false; /** * The unit address of the node, which is optionally written after the * name followed by an at symbol. */ std::string unit_address; /** * A flag indicating that this node has been marked /omit-if-no-ref/ and * will be omitted if it is not referenced, either directly or indirectly, * by a node that is not similarly denoted. */ bool omit_if_no_ref = false; /** * A flag indicating that this node has been referenced, either directly * or indirectly, by a node that is not marked /omit-if-no-ref/. */ bool used = false; /** * The type for the property vector. */ typedef std::vector property_vector; /** * Iterator type for child nodes. */ typedef std::vector::iterator child_iterator; /** * Recursion behavior to be observed for visiting */ enum visit_behavior { /** * Recurse as normal through the rest of the tree. */ VISIT_RECURSE, /** * Continue recursing through the device tree, but do not * recurse through this branch of the tree any further. */ VISIT_CONTINUE, /** * Immediately halt the visit. No further nodes will be visited. */ VISIT_BREAK }; private: /** * Adaptor to use children in range-based for loops. */ struct child_range { child_range(node &nd) : n(nd) {} child_iterator begin() { return n.child_begin(); } child_iterator end() { return n.child_end(); } private: node &n; }; /** * Adaptor to use properties in range-based for loops. */ struct property_range { property_range(node &nd) : n(nd) {} property_vector::iterator begin() { return n.property_begin(); } property_vector::iterator end() { return n.property_end(); } private: node &n; }; /** * The properties contained within this node. */ property_vector props; /** * The children of this node. */ std::vector children; /** * Children that should be deleted from this node when merging. */ std::unordered_set deleted_children; /** * Properties that should be deleted from this node when merging. */ std::unordered_set deleted_props; /** * 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. */ std::string parse_name(text_input_buffer &input, bool &is_property, const char *error); /** * Constructs a new node from two input buffers, pointing to the struct * and strings tables in the device tree blob, respectively. */ 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(text_input_buffer &input, device_tree &tree, std::string &&n, std::unordered_set &&l, std::string &&a, define_map*); /** * Creates a special node with the specified name and properties. */ node(const std::string &n, const std::vector &p); /** * Comparison function for properties, used when sorting the properties * vector. Orders the properties based on their names. */ 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_ptr &c1, node_ptr &c2); public: /** * Sorts the node's properties and children into alphabetical order and * recursively sorts the children. */ void sort(); /** * 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(); } /** * Returns a range suitable for use in a range-based for loop describing * the children of this node. */ inline child_range child_nodes() { return child_range(*this); } /** * Accessor for the deleted children. */ inline const std::unordered_set &deleted_child_nodes() { return deleted_children; } /** * Accessor for the deleted properties */ inline const std::unordered_set &deleted_properties() { return deleted_props; } /** * Returns a range suitable for use in a range-based for loop describing * the properties of this node. */ inline property_range properties() { return property_range(*this); } /** * Returns an iterator after the last property of this node. */ inline property_vector::iterator property_begin() { return props.begin(); } /** * Returns an iterator for the first property of this node. */ inline property_vector::iterator property_end() { return props.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_ptr parse(text_input_buffer &input, device_tree &tree, std::string &&name, std::unordered_set &&label=std::unordered_set(), std::string &&address=std::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_ptr parse_dtb(input_buffer &structs, input_buffer &strings); /** * Construct a new special node from a name and set of properties. */ static node_ptr create_special_node(const std::string &name, const std::vector &props); /** * Returns a property corresponding to the specified key, or 0 if this * node does not contain a property of that name. */ property_ptr get_property(const std::string &key); /** * Adds a new property to this node. */ inline void add_property(property_ptr &p) { props.push_back(p); } /** * Adds a new child to this node. */ inline void add_child(node_ptr &&n) { children.push_back(std::move(n)); } /** * Deletes any children from this node. */ inline void delete_children_if(std::function predicate) { children.erase(std::remove_if(children.begin(), children.end(), predicate), children.end()); } /** * 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_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); /** * Recursively visit this node and then its children based on the * callable's return value. The callable may return VISIT_BREAK * immediately halt all recursion and end the visit, VISIT_CONTINUE to * not recurse into the current node's children, or VISIT_RECURSE to recurse * through children as expected. parent will be passed to the callable. */ visit_behavior visit(std::function, node *parent); }; /** * 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. */ class node_path : public std::vector> { public: /** * Converts this to a string representation. */ std::string to_string() const; }; /** * 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 = EPAPR; /** * Flag indicating that this tree is valid. This will be set to false * on parse errors. */ bool valid = true; /** * Flag indicating that this tree requires garbage collection. This will be * set to true if a node marked /omit-if-no-ref/ is encountered. */ bool garbage_collect = false; /** * 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_ptr root; /** * Mapping from names to nodes. Only unambiguous names are recorded, * duplicate names are stored as (node*)-1. */ std::unordered_map node_names; /** * Mapping from names to the nodes that contain them. */ std::unordered_map node_name_parents; /** * A map from labels to node paths. When resolving cross references, * we look up referenced nodes in this and replace the cross reference * with the full path to its target. */ std::unordered_map node_paths; /** * All of the elements in `node_paths` in the order that they were * created. This is used for emitting the `__symbols__` section, where * we want to guarantee stable ordering. */ std::vector> ordered_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; /** * Labels collected from top-level /delete-node/ directives. */ std::vector deletions; /** * The location of something requiring a fixup entry. */ struct fixup { /** * The path to the node. */ node_path path; /** * The property containing the reference. */ property_ptr prop; /** * The property value that contains the reference. */ property_value &val; }; /** * A collection of property values that refer to phandles. These will * be replaced by the value of the phandle property in their * destination. */ std::vector fixups; /** * The locations of all of the values that are supposed to become phandle * references, but refer to things outside of this file. */ std::vector> unresolved_fixups; /** * 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; /** * 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::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; /** * 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 = 0; /** * The number of empty reserve map entries to generate in the blob. */ uint32_t spare_reserve_map_entries = 0; /** * The minimum size in bytes of the blob. */ uint32_t minimum_blob_size = 0; /** * The number of bytes of padding to add to the end of the blob. */ uint32_t blob_padding = 0; /** * Is this tree a plugin? */ bool is_plugin = false; /** * 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_ptr parent, node_ptr n, node_path &path); /** * Assign a phandle property to a single node. The next parameter * holds the phandle to be assigned, and will be incremented upon * assignment. */ property_ptr assign_phandle(node_ptr n, uint32_t &next); /** * 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. The phandle parameter holds the next phandle to be * assigned, should the need arise. It will be incremented upon each * assignment of a phandle. Garbage collection of unreferenced nodes * marked for "delete if unreferenced" will also occur here. */ void resolve_cross_references(uint32_t &phandle); /** * Garbage collects nodes that have been marked /omit-if-no-ref/ and do not * have any references to them from nodes that are similarly marked. This * is a fairly expensive operation. The return value indicates whether the * tree has been dirtied as a result of this operation, so that the caller * may take appropriate measures to bring the device tree into a consistent * state as needed. */ bool garbage_collect_marked_nodes(); /** * Parses a dts file in the given buffer and adds the roots to the parsed * set. The `read_header` argument indicates whether the header has * already been read. Some dts files place the header in an include, * rather than in the top-level file. */ void parse_file(text_input_buffer &input, std::vector &roots, bool &read_header); /** * 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: /** * Should we write the __symbols__ node (to allow overlays to be linked * against this blob)? */ bool write_symbols = false; /** * Returns the node referenced by the property. If this is a tree that * 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_ptr 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() {} /** * Constructs a device tree from the specified file name, referring to * a file that contains a device tree blob. */ void parse_dtb(const std::string &fn, FILE *depfile); /** * Construct a fragment wrapper around node. This will assume that node's * name may be used as the target of the fragment, and the contents are to * be wrapped in an __overlay__ node. The fragment wrapper will be assigned * fragnumas its fragment number, and fragment number will be incremented. */ node_ptr create_fragment_wrapper(node_ptr &node, int &fragnum); /** * Generate a root node from the node passed in. This is sensitive to * whether we're in a plugin context or not, so that if we're in a plugin we * can circumvent any errors that might normally arise from a non-/ root. * fragnum will be assigned to any fragment wrapper generated as a result * of the call, and fragnum will be incremented. */ node_ptr generate_root(node_ptr &node, int &fragnum); /** * Reassign any fragment numbers from this new node, based on the given * delta. */ void reassign_fragment_numbers(node_ptr &node, int &delta); /* * Constructs a device tree from the specified file name, referring to * a file that contains device tree source. */ void parse_dts(const std::string &fn, FILE *depfile); /** * Returns whether this tree is valid. */ inline bool is_valid() { return valid; } /** * Mark this tree as needing garbage collection, because an /omit-if-no-ref/ * node has been encountered. */ void set_needs_garbage_collection() { garbage_collect = true; } /** * 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 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() { if (root) { 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) { 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_