dtc(1): Update to upstream ea3c233
Highlights of this update: - /__local_fixups__ is now generated to be GPL dtc and libfdt compliant - Compiling with -@ will now cause dtc to assign phandles to all labelled nodes - /include/ and /incbin/ now handle absolute paths correctly - The manpage now has information about overlays, including how to apply them and how to generate them - Syntactic sugar for overlays is now supported, allowing an overlay DTS like: = /dts-v1/; /plugin/; &foo { foo,status = "okay"; }; = to generate a fragment targetting <&foo>.
This commit is contained in:
parent
a409fce80d
commit
748a3d44a1
@ -4,8 +4,6 @@ PROG_CXX=dtc
|
||||
SRCS= dtc.cc input_buffer.cc string.cc dtb.cc fdt.cc checking.cc
|
||||
MAN= dtc.1
|
||||
|
||||
WARNS?= 3
|
||||
|
||||
CXXFLAGS+= -std=c++11 -fno-rtti -fno-exceptions
|
||||
|
||||
NO_SHARED?=NO
|
||||
|
@ -30,7 +30,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"/
|
||||
.Dd January 1, 2013
|
||||
.Dd January 17, 2018
|
||||
.Dt DTC 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -153,9 +153,9 @@ format for property values.
|
||||
These allow property value to be specified on the command line.
|
||||
.It Fl R Ar entries
|
||||
The number of empty reservation table entries to pad the table with.
|
||||
This is
|
||||
useful if you are generating a device tree blob for bootloader or similar that
|
||||
needs to reserve some memory before passing control to the operating system.
|
||||
This is useful if you are generating a device tree blob for bootloader or
|
||||
similar that needs to reserve some memory before passing control to the
|
||||
operating system.
|
||||
.It Fl S Ar bytes
|
||||
The minimum size in bytes of the blob.
|
||||
The blob will be padded after the strings table to ensure that it is the
|
||||
@ -244,6 +244,54 @@ Checks that all
|
||||
.Va /delete-node/
|
||||
statements refer to nodes that are merged.
|
||||
.El
|
||||
.Sh OVERLAYS
|
||||
The utility provides support for generating overlays, also known as plugins.
|
||||
Overlays are a method of patching a base device tree that has been compiled with
|
||||
the
|
||||
.Fl @
|
||||
flag, with some limited support for patching device trees that were not compiled
|
||||
with the
|
||||
.Fl @
|
||||
flag.
|
||||
.Pp
|
||||
To denote that a DTS is intended to be used as an overlay,
|
||||
.Va /plugin/;
|
||||
should be included in the header, following any applicable
|
||||
.Va /dts-v1/;
|
||||
tag.
|
||||
.Pp
|
||||
Conventional overlays are crafted by creating
|
||||
.Va fragment
|
||||
nodes in a root.
|
||||
Each fragment node must have either a
|
||||
.Va target
|
||||
property set to a label reference, or a
|
||||
.Va target-path
|
||||
string property set to a path.
|
||||
It must then have an
|
||||
.Va __overlay__
|
||||
child node, whose properties and child nodes are merged into the base device
|
||||
tree when the overlay is applied.
|
||||
.Pp
|
||||
Much simpler syntactic sugar was later invented to simplify generating overlays.
|
||||
Instead of creating targetted fragments manually, one can instead create a root
|
||||
node that targets a label in the base node using the
|
||||
.Va &label
|
||||
syntax supported in conventional DTS.
|
||||
This will indicate that a fragment should be generated for the node, with the
|
||||
given
|
||||
.Va label
|
||||
being the target, and the properties and child nodes will be used as the
|
||||
__overlay__.
|
||||
.Pp
|
||||
Both conventional overlays and the later-added syntactic sugar are supported.
|
||||
.Pp
|
||||
Overlay blobs can be applied at boot time by setting
|
||||
.Va fdt_overlays
|
||||
in
|
||||
.Xr loader.conf 5 .
|
||||
Multiple overlays may be specified, and they will be applied in the order given.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
The command:
|
||||
.Pp
|
||||
@ -254,8 +302,7 @@ will generate a
|
||||
file from the device tree source
|
||||
.Pa device.dts
|
||||
and print errors if any occur during parsing or property checking.
|
||||
The
|
||||
resulting file can be assembled and linked into a binary.
|
||||
The resulting file can be assembled and linked into a binary.
|
||||
.Pp
|
||||
The command:
|
||||
.Pp
|
||||
@ -265,6 +312,33 @@ will write the device tree source for the device tree blob
|
||||
.Pa device.dtb
|
||||
to the standard output.
|
||||
This is useful when debugging device trees.
|
||||
.Pp
|
||||
The command:
|
||||
.Pp
|
||||
.Dl "dtc -@ -O dtb -I dts -o device.dtb device.dts"
|
||||
.Pp
|
||||
will generate a
|
||||
.Pa device.dtb
|
||||
file from the device tree source
|
||||
.Pa device.dts
|
||||
with a __symbols__ node included so that overlays may be applied to it.
|
||||
.Pp
|
||||
The command:
|
||||
.Pp
|
||||
.Dl "dtc -@ -O dtb -I dts -o device_overlay.dtbo device_overlay.dts"
|
||||
.Pp
|
||||
will generate a
|
||||
.Pa device_overlay.dtbo
|
||||
file, using the standard extension for a device tree overlay, from the device
|
||||
tree source
|
||||
.Pa device_overlay.dts .
|
||||
A __symbols__ node will be included so that overlays may be applied to it.
|
||||
The presence of a
|
||||
.Va /plugin/;
|
||||
directive in
|
||||
.Pa device_overlay.dts
|
||||
will indicate to the utility that it should also generate the underlying
|
||||
metadata required in overlays.
|
||||
.Sh COMPATIBILITY
|
||||
This utility is intended to be compatible with the device tree compiler
|
||||
provided by elinux.org.
|
||||
|
@ -49,6 +49,8 @@
|
||||
using namespace dtc;
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* The current major version of the tool.
|
||||
*/
|
||||
@ -65,7 +67,7 @@ int version_minor_compatible = 4;
|
||||
int version_patch = 0;
|
||||
int version_patch_compatible = 0;
|
||||
|
||||
static void usage(const string &argv0)
|
||||
void usage(const string &argv0)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n"
|
||||
"\t%s\t[-fhsv@] [-b boot_cpu_id] [-d dependency_file]"
|
||||
@ -80,7 +82,7 @@ static void usage(const string &argv0)
|
||||
/**
|
||||
* Prints the current version of this program..
|
||||
*/
|
||||
static void version(const char* progname)
|
||||
void version(const char* progname)
|
||||
{
|
||||
fprintf(stdout, "Version: %s %d.%d.%d compatible with gpl dtc %d.%d.%d\n", progname,
|
||||
version_major, version_minor, version_patch,
|
||||
@ -88,6 +90,8 @@ static void version(const char* progname)
|
||||
version_patch_compatible);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
using fdt::device_tree;
|
||||
|
||||
int
|
||||
|
@ -497,6 +497,29 @@ property::property(text_input_buffer &input,
|
||||
return;
|
||||
case '/':
|
||||
{
|
||||
if (input.consume("/incbin/(\""))
|
||||
{
|
||||
auto loc = input.location();
|
||||
std::string filename = input.parse_to('"');
|
||||
if (!(valid = input.consume('"')))
|
||||
{
|
||||
loc.report_error("Syntax error, expected '\"' to terminate /incbin/(");
|
||||
return;
|
||||
}
|
||||
property_value v;
|
||||
if (!(valid = input.read_binary_file(filename, v.byte_data)))
|
||||
{
|
||||
input.parse_error("Cannot open binary include file");
|
||||
return;
|
||||
}
|
||||
if (!(valid &= input.consume(')')))
|
||||
{
|
||||
input.parse_error("Syntax error, expected ')' to terminate /incbin/(");
|
||||
return;
|
||||
}
|
||||
values.push_back(v);
|
||||
break;
|
||||
}
|
||||
unsigned long long bits = 0;
|
||||
valid = input.consume("/bits/");
|
||||
input.next_token();
|
||||
@ -999,7 +1022,7 @@ node::get_property(const string &key)
|
||||
}
|
||||
|
||||
void
|
||||
node::merge_node(node_ptr other)
|
||||
node::merge_node(node_ptr &other)
|
||||
{
|
||||
for (auto &l : other->labels)
|
||||
{
|
||||
@ -1034,7 +1057,7 @@ node::merge_node(node_ptr other)
|
||||
{
|
||||
if (i->name == c->name && i->unit_address == c->unit_address)
|
||||
{
|
||||
i->merge_node(std::move(c));
|
||||
i->merge_node(c);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -1207,8 +1230,67 @@ device_tree::collect_names()
|
||||
collect_names_recursive(root, p);
|
||||
}
|
||||
|
||||
property_ptr
|
||||
device_tree::assign_phandle(node *n, uint32_t &phandle)
|
||||
{
|
||||
// If there is an existing phandle, use it
|
||||
property_ptr p = n->get_property("phandle");
|
||||
if (p == 0)
|
||||
{
|
||||
p = n->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.reset(new property("linux,phandle"));
|
||||
p->add_value(v);
|
||||
n->add_property(p);
|
||||
}
|
||||
if (phandle_node_name == BOTH || phandle_node_name == EPAPR)
|
||||
{
|
||||
p.reset(new property("phandle"));
|
||||
p->add_value(v);
|
||||
n->add_property(p);
|
||||
}
|
||||
}
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::resolve_cross_references()
|
||||
device_tree::assign_phandles(node_ptr &n, uint32_t &next)
|
||||
{
|
||||
if (!n->labels.empty())
|
||||
{
|
||||
assign_phandle(n.get(), next);
|
||||
}
|
||||
|
||||
for (auto &c : n->child_nodes())
|
||||
{
|
||||
assign_phandles(c, next);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::resolve_cross_references(uint32_t &phandle)
|
||||
{
|
||||
for (auto *pv : cross_references)
|
||||
{
|
||||
@ -1252,7 +1334,6 @@ device_tree::resolve_cross_references()
|
||||
});
|
||||
assert(sorted_phandles.size() == fixups.size());
|
||||
|
||||
uint32_t phandle = 1;
|
||||
for (auto &i : sorted_phandles)
|
||||
{
|
||||
string target_name = i.get().val.string_data;
|
||||
@ -1334,43 +1415,7 @@ device_tree::resolve_cross_references()
|
||||
}
|
||||
}
|
||||
// If there is an existing phandle, use it
|
||||
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.reset(new property("linux,phandle"));
|
||||
p->add_value(v);
|
||||
target->add_property(p);
|
||||
}
|
||||
if (phandle_node_name == BOTH || phandle_node_name == EPAPR)
|
||||
{
|
||||
p.reset(new property("phandle"));
|
||||
p->add_value(v);
|
||||
target->add_property(p);
|
||||
}
|
||||
}
|
||||
property_ptr p = assign_phandle(target, phandle);
|
||||
p->begin()->push_to_buffer(i.get().val.byte_data);
|
||||
assert(i.get().val.byte_data.size() == 4);
|
||||
}
|
||||
@ -1644,6 +1689,72 @@ device_tree::node_path::to_string() const
|
||||
return path;
|
||||
}
|
||||
|
||||
node_ptr
|
||||
device_tree::create_fragment_wrapper(node_ptr &node, int &fragnum)
|
||||
{
|
||||
// In a plugin, we can massage these non-/ root nodes into into a fragment
|
||||
std::string fragment_address = "fragment@" + std::to_string(fragnum);
|
||||
++fragnum;
|
||||
|
||||
std::vector<property_ptr> symbols;
|
||||
|
||||
// Intentionally left empty
|
||||
node_ptr newroot = node::create_special_node("", symbols);
|
||||
node_ptr wrapper = node::create_special_node("__overlay__", symbols);
|
||||
|
||||
// Generate the fragment with target = <&name>
|
||||
property_value v;
|
||||
v.string_data = node->name;
|
||||
v.type = property_value::PHANDLE;
|
||||
auto prop = std::make_shared<property>(std::string("target"));
|
||||
prop->add_value(v);
|
||||
symbols.push_back(prop);
|
||||
|
||||
node_ptr fragment = node::create_special_node(fragment_address, symbols);
|
||||
|
||||
wrapper->merge_node(node);
|
||||
fragment->add_child(std::move(wrapper));
|
||||
newroot->add_child(std::move(fragment));
|
||||
return newroot;
|
||||
}
|
||||
|
||||
node_ptr
|
||||
device_tree::generate_root(node_ptr &node, int &fragnum)
|
||||
{
|
||||
|
||||
string name = node->name;
|
||||
if (name == string())
|
||||
{
|
||||
return std::move(node);
|
||||
}
|
||||
else if (!is_plugin)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return create_fragment_wrapper(node, fragnum);
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::reassign_fragment_numbers(node_ptr &node, int &delta)
|
||||
{
|
||||
|
||||
for (auto &c : node->child_nodes())
|
||||
{
|
||||
if (c->name == std::string("fragment"))
|
||||
{
|
||||
int current_address = std::stoi(c->unit_address, nullptr, 16);
|
||||
std::ostringstream new_address;
|
||||
current_address += delta;
|
||||
// It's possible that we hopped more than one somewhere, so just reset
|
||||
// delta to the next in sequence.
|
||||
delta = current_address + 1;
|
||||
new_address << std::hex << current_address;
|
||||
c->unit_address = new_address.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
{
|
||||
@ -1665,6 +1776,7 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
dirname(fn),
|
||||
depfile);
|
||||
bool read_header = false;
|
||||
int fragnum = 0;
|
||||
parse_file(input, roots, read_header);
|
||||
switch (roots.size())
|
||||
{
|
||||
@ -1673,18 +1785,36 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
input.parse_error("Failed to find root node /.");
|
||||
return;
|
||||
case 1:
|
||||
root = std::move(roots[0]);
|
||||
root = generate_root(roots[0], fragnum);
|
||||
if (!root)
|
||||
{
|
||||
valid = false;
|
||||
input.parse_error("Failed to find root node /.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
root = std::move(roots[0]);
|
||||
root = generate_root(roots[0], fragnum);
|
||||
if (!root)
|
||||
{
|
||||
valid = false;
|
||||
input.parse_error("Failed to find root node /.");
|
||||
return;
|
||||
}
|
||||
for (auto i=++(roots.begin()), e=roots.end() ; i!=e ; ++i)
|
||||
{
|
||||
auto &node = *i;
|
||||
string name = node->name;
|
||||
if (name == string())
|
||||
{
|
||||
root->merge_node(std::move(node));
|
||||
if (is_plugin)
|
||||
{
|
||||
// Re-assign any fragment numbers based on a delta of
|
||||
// fragnum before we merge it
|
||||
reassign_fragment_numbers(node, fragnum);
|
||||
}
|
||||
root->merge_node(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1696,18 +1826,34 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
}
|
||||
if (existing == node_names.end())
|
||||
{
|
||||
fprintf(stderr, "Unable to merge node: %s\n", name.c_str());
|
||||
if (is_plugin)
|
||||
{
|
||||
auto fragment = create_fragment_wrapper(node, fragnum);
|
||||
root->merge_node(fragment);
|
||||
}
|
||||
else
|
||||
{
|
||||
existing->second->merge_node(std::move(node));
|
||||
fprintf(stderr, "Unable to merge node: %s\n", name.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
existing->second->merge_node(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
collect_names();
|
||||
resolve_cross_references();
|
||||
uint32_t phandle = 1;
|
||||
// If we're writing symbols, go ahead and assign phandles to the entire
|
||||
// tree. We'll do this before we resolve cross references, just to keep
|
||||
// order semi-predictable and stable.
|
||||
if (write_symbols)
|
||||
{
|
||||
assign_phandles(root, phandle);
|
||||
}
|
||||
resolve_cross_references(phandle);
|
||||
if (write_symbols)
|
||||
{
|
||||
std::vector<property_ptr> symbols;
|
||||
@ -1769,21 +1915,72 @@ device_tree::parse_dts(const string &fn, FILE *depfile)
|
||||
}
|
||||
symbols.clear();
|
||||
// If we have any resolved phandle references in this plugin, then
|
||||
// we must leave a property in the /__local_fixups__ node whose key
|
||||
// is 'fixup' and whose value is as described above.
|
||||
// we must create a child in the __local_fixups__ node whose path
|
||||
// matches the node path from the root and whose value contains the
|
||||
// location of the reference within a property.
|
||||
|
||||
// Create a local_fixups node that is initially empty.
|
||||
node_ptr local_fixups = node::create_special_node("__local_fixups__", symbols);
|
||||
for (auto &i : fixups)
|
||||
{
|
||||
if (!i.val.is_phandle())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
symbols.push_back(create_fixup_entry(i, "fixup"));
|
||||
node *n = local_fixups.get();
|
||||
for (auto &p : i.path)
|
||||
{
|
||||
// Skip the implicit root
|
||||
if (p.first.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool found = false;
|
||||
for (auto &c : n->child_nodes())
|
||||
{
|
||||
if (c->name == p.first)
|
||||
{
|
||||
n = c.get();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
n->add_child(node::create_special_node(p.first, symbols));
|
||||
n = (--n->child_end())->get();
|
||||
}
|
||||
}
|
||||
assert(n);
|
||||
property_value pv;
|
||||
push_big_endian(pv.byte_data, static_cast<uint32_t>(i.prop->offset_of_value(i.val)));
|
||||
pv.type = property_value::BINARY;
|
||||
auto key = i.prop->get_key();
|
||||
property_ptr prop = n->get_property(key);
|
||||
// If we don't have an existing property then create one and
|
||||
// use this property value
|
||||
if (!prop)
|
||||
{
|
||||
prop = std::make_shared<property>(std::move(key));
|
||||
n->add_property(prop);
|
||||
prop->add_value(pv);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we do have an existing property value, try to append
|
||||
// this value.
|
||||
property_value &old_val = *(--prop->end());
|
||||
if (!old_val.try_to_merge(pv))
|
||||
{
|
||||
prop->add_value(pv);
|
||||
}
|
||||
}
|
||||
}
|
||||
// We've iterated over all fixups, but only emit the
|
||||
// __local_fixups__ if we found some that were resolved internally.
|
||||
if (!symbols.empty())
|
||||
if (local_fixups->child_begin() != local_fixups->child_end())
|
||||
{
|
||||
root->add_child(node::create_special_node("__local_fixups__", symbols));
|
||||
root->add_child(std::move(local_fixups));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ class property
|
||||
/**
|
||||
* Returns the key for this property.
|
||||
*/
|
||||
inline std::string get_key()
|
||||
inline const std::string &get_key()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
@ -620,7 +620,7 @@ class node
|
||||
* Merges a node into this one. Any properties present in both are
|
||||
* overridden, any properties present in only one are preserved.
|
||||
*/
|
||||
void merge_node(node_ptr 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
|
||||
@ -676,12 +676,12 @@ class device_tree
|
||||
/**
|
||||
* The format that we should use for writing phandles.
|
||||
*/
|
||||
phandle_format phandle_node_name;
|
||||
phandle_format phandle_node_name = EPAPR;
|
||||
/**
|
||||
* Flag indicating that this tree is valid. This will be set to false
|
||||
* on parse errors.
|
||||
*/
|
||||
bool valid;
|
||||
bool valid = true;
|
||||
/**
|
||||
* Type used for memory reservations. A reservation is two 64-bit
|
||||
* values indicating a base address and length in memory that the
|
||||
@ -775,23 +775,23 @@ class device_tree
|
||||
/**
|
||||
* The default boot CPU, specified in the device tree header.
|
||||
*/
|
||||
uint32_t boot_cpu;
|
||||
uint32_t boot_cpu = 0;
|
||||
/**
|
||||
* The number of empty reserve map entries to generate in the blob.
|
||||
*/
|
||||
uint32_t spare_reserve_map_entries;
|
||||
uint32_t spare_reserve_map_entries = 0;
|
||||
/**
|
||||
* The minimum size in bytes of the blob.
|
||||
*/
|
||||
uint32_t minimum_blob_size;
|
||||
uint32_t minimum_blob_size = 0;
|
||||
/**
|
||||
* The number of bytes of padding to add to the end of the blob.
|
||||
*/
|
||||
uint32_t blob_padding;
|
||||
uint32_t blob_padding = 0;
|
||||
/**
|
||||
* Is this tree a plugin?
|
||||
*/
|
||||
bool is_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
|
||||
@ -799,6 +799,12 @@ class device_tree
|
||||
* properties that have been explicitly added.
|
||||
*/
|
||||
void collect_names_recursive(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 *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
|
||||
@ -812,9 +818,11 @@ class device_tree
|
||||
/**
|
||||
* Resolves all cross references. Any properties that refer to another
|
||||
* node must have their values replaced by either the node path or
|
||||
* phandle value.
|
||||
* 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.
|
||||
*/
|
||||
void resolve_cross_references();
|
||||
void resolve_cross_references(uint32_t &phandle);
|
||||
/**
|
||||
* 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
|
||||
@ -859,15 +867,33 @@ class device_tree
|
||||
/**
|
||||
* Default constructor. Creates a valid, but empty FDT.
|
||||
*/
|
||||
device_tree() : phandle_node_name(EPAPR), valid(true),
|
||||
boot_cpu(0), spare_reserve_map_entries(0),
|
||||
minimum_blob_size(0), blob_padding(0) {}
|
||||
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.
|
||||
*/
|
||||
@ -905,9 +931,12 @@ class device_tree
|
||||
* 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
|
||||
|
@ -206,9 +206,9 @@ text_input_buffer::handle_include()
|
||||
{
|
||||
next_token();
|
||||
string name = parse_property_name();
|
||||
if (defines.count(name) > 0)
|
||||
if (defines.count(name) == 0)
|
||||
{
|
||||
reallyInclude = true;
|
||||
reallyInclude = false;
|
||||
}
|
||||
consume('/');
|
||||
}
|
||||
@ -252,6 +252,48 @@ text_input_buffer::handle_include()
|
||||
input_stack.push(std::move(include_buffer));
|
||||
}
|
||||
|
||||
bool text_input_buffer::read_binary_file(const std::string &filename, byte_buffer &b)
|
||||
{
|
||||
bool try_include_paths = true;
|
||||
string include_file;
|
||||
if (filename[0] == '/')
|
||||
{
|
||||
include_file = filename;
|
||||
// Don't try include paths if we're given an absolute path.
|
||||
// Failing is better so that we don't accidentally do the wrong thing,
|
||||
// but make it seem like everything is alright.
|
||||
try_include_paths = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
include_file = dir + '/' + filename;
|
||||
}
|
||||
auto include_buffer = input_buffer::buffer_for_file(include_file, false);
|
||||
if (include_buffer == 0 && try_include_paths)
|
||||
{
|
||||
for (auto i : include_paths)
|
||||
{
|
||||
include_file = i + '/' + filename;
|
||||
include_buffer = input_buffer::buffer_for_file(include_file, false);
|
||||
if (include_buffer != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!include_buffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (depfile)
|
||||
{
|
||||
putc(' ', depfile);
|
||||
fputs(include_file.c_str(), depfile);
|
||||
}
|
||||
b.insert(b.begin(), include_buffer->begin(), include_buffer->end());
|
||||
return true;
|
||||
}
|
||||
|
||||
input_buffer
|
||||
input_buffer::buffer_from_offset(int offset, int s)
|
||||
{
|
||||
|
@ -166,6 +166,14 @@ class input_buffer
|
||||
cursor++;
|
||||
return *this;
|
||||
}
|
||||
const char *begin()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
const char *end()
|
||||
{
|
||||
return buffer + size;
|
||||
}
|
||||
/**
|
||||
* Consumes a character. Moves the cursor one character forward if the
|
||||
* next character matches the argument, returning true. If the current
|
||||
@ -525,6 +533,13 @@ class text_input_buffer
|
||||
* Prints a message indicating the location of a parse error.
|
||||
*/
|
||||
void parse_error(const char *msg);
|
||||
/**
|
||||
* Reads the contents of a binary file into `b`. The file name is assumed
|
||||
* to be relative to one of the include paths.
|
||||
*
|
||||
* Returns true if the file exists and can be read, false otherwise.
|
||||
*/
|
||||
bool read_binary_file(const std::string &filename, byte_buffer &b);
|
||||
private:
|
||||
/**
|
||||
* Prints a message indicating the location of a parse error, given a
|
||||
|
Loading…
Reference in New Issue
Block a user