freebsd-dev/usr.bin/dtc/fdt.cc
David Chisnall af0dd31fc4 Import new (BSDL) device tree compiler. Now built by default, so that it can't
be used on the host system (and not installed on the device, if required).  The
GPL'd one is still available if there are any devices that need it (make
universe passes with it, including kernels that use fdt, but there may be some
out-of-tree ones).  WITH_GPL_DTC can be used to select the old one, for now.

Probably won't be MFC'd, but we'll remove the GPL'd version in head after the
new one has had a lot more testing and ship it in 10.0.
2013-01-22 17:49:51 +00:00

1358 lines
29 KiB
C++

/*-
* 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 "fdt.hh"
#include <algorithm>
#include <inttypes.h>
#include <fcntl.h>
#include <libgen.h>
#include "dtb.hh"
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<e ; i++)
{
bytes++;
is_all_printable &= (*i == '\0') || isprint(*i);
if (*i == '\0')
{
nuls++;
}
if (!is_all_printable)
{
break;
}
}
if (is_all_printable && (bytes > nuls))
{
type = STRING;
if (nuls > 0)
{
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)
{
// FIXME Escape tabs, newlines, and so on.
if (*i == '\0')
{
fputs("\", \"", file);
continue;
}
putc(*i, file);
}
}
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)
{
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++)
{
fprintf(file, "%hhx", *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)
{
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('&'))
{
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;
if (!input.consume_integer(val))
{
input.parse_error("Expected numbers in array of cells");
valid = false;
return;
}
push_big_endian(v.byte_data, (uint32_t)val);
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);
// Read the value
uint8_t byte;
property_value v;
for (uint32_t i=0 ; i<length ; i++)
{
if (!(valid = structs.consume_binary(byte)))
{
fprintf(stderr, "Failed to read property value\n");
return;
}
v.byte_data.push_back(byte);
}
values.push_back(v);
}
property::property(input_buffer &input, string k, string l) : key(k), label(l),
valid(true)
{
do {
input.next_token();
switch (input[0])
{
default:
input.parse_error("Invalid property value.");
valid = false;
return;
case '"':
parse_string(input);
break;
case '<':
parse_cells(input);
break;
case '[':
parse_bytes(input);
break;
case '&':
parse_reference(input);
break;
case ';':
{
break;
}
}
input.next_token();
} while (input.consume(','));
if (!input.consume(';'))
{
input.parse_error("Expected ; at end of property");
valid = false;
}
}
property*
property::parse_dtb(input_buffer &structs, input_buffer &strings)
{
property *p = new property(structs, strings);
if (!p->valid)
{
delete p;
p = 0;
}
return p;
}
property*
property::parse(input_buffer &input, string key, string label)
{
property *p = new property(input, key, label);
if (!p->valid)
{
delete p;
p = 0;
}
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);
}
void
property::write_dts(FILE *file, int indent)
{
for (int i=0 ; i<indent ; i++)
{
putc('\t', file);
}
if (label != string())
{
label.print(file);
fputs(": ", file);
}
if (key != string())
{
key.print(file);
}
if (!values.empty())
{
fputs(" = ", file);
for (value_iterator i=begin(), e=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);
if (child == 0)
{
valid = false;
return;
}
children.push_back(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);
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) :
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,
child_label);
if (p == 0)
{
valid = false;
}
else
{
properties.push_back(p);
}
}
else if (!is_property && input[0] == ('{'))
{
node *child = node::parse(input, child_name,
child_label, child_address);
if (child)
{
children.push_back(child);
}
else
{
valid = false;
}
}
else if (input.consume(';'))
{
properties.push_back(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)
{
return p1->get_key() < p2->get_key();
}
bool
node::cmp_children(node *c1, node *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::parse(input_buffer &input, string name, string label, string address)
{
node *n = new node(input, name, label, address);
if (!n->valid)
{
delete n;
n = 0;
}
return n;
}
node*
node::parse_dtb(input_buffer &structs, input_buffer &strings)
{
node *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*
node::get_property(string key)
{
for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
{
if ((*i)->get_key() == key)
{
return *i;
}
}
return 0;
}
void
node::merge_node(node *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())
{
property *p = other->properties.front();
for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
{
if ((*i)->get_key() == p->get_key())
{
delete *i;
properties.erase(i);
break;
}
}
add_property(p);
other->properties.erase(other->properties.begin());
}
while (!other->children.empty())
{
node *c = other->children.front();
bool found = false;
for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
{
if ((*i)->name == c->name && (*i)->unit_address == c->unit_address)
{
(*i)->merge_node(c);
delete c;
found = true;
break;
}
}
if (!found)
{
children.push_back(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)
{
(*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 ; i<indent ; i++)
{
putc('\t', file);
}
if (label != string())
{
label.print(file);
fputs(": ", file);
}
if (name != string())
{
name.print(file);
}
if (unit_address != string())
{
putc('@', file);
unit_address.print(file);
}
fputs(" {\n\n", file);
for (property_iterator i=property_begin(), e=property_end() ; i!=e ; ++i)
{
(*i)->write_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 ; i<indent ; i++)
{
putc('\t', file);
}
fputs("};\n", file);
}
void
device_tree::collect_names_recursive(node* n, node_path &path)
{
string name = n->label;
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_paths.insert(std::make_pair(name, path));
}
else
{
node_names[name] = (node*)-1;
std::map<string, node_path>::iterator 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 (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));
}
}
}
}
void
device_tree::collect_names()
{
node_path p;
collect_names_recursive(root, p);
}
void
device_tree::resolve_cross_references()
{
for (std::vector<property_value*>::iterator i=cross_references.begin(), e=cross_references.end() ; i!=e ; ++i)
{
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)
{
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<property_value*>::iterator i=phandles.begin(), e=phandles.end() ; i!=e ; ++i)
{
string target_name = (*i)->string_data;
node *target = node_names[target_name];
if (target == 0)
{
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");
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->add_value(v);
target->add_property(p);
}
if (phandle_node_name == BOTH || phandle_node_name == EPAPR)
{
p = 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);
}
}
void
device_tree::parse_roots(input_buffer &input, std::vector<node*> &roots)
{
input.next_token();
while (valid && input.consume('/'))
{
input.next_token();
node *n = node::parse(input, string("", 1));
if (n)
{
roots.push_back(n);
}
else
{
valid = false;
}
}
}
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);
return b;
}
int source = open(path, O_RDONLY);
if (source == -1)
{
fprintf(stderr, "Unable to open file %s\n", path);
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);
close(source);
return b;
}
template<class writer> 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<reservation>::iterator i=reservations.begin(),
e=reservations.end() ; i!=e ; ++i)
{
reservation_writer.write_comment(string("Reservation start"));
reservation_writer.write_data(i->first);
reservation_writer.write_comment(string("Reservation length"));
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<blob_padding ; i++)
{
strings_writer.write_data((uint8_t)0);
}
head.totalsize = sizeof(head) + strings_writer.size() +
struct_writer.size() + reservation_writer.size();
while (head.totalsize < minimum_blob_size)
{
head.totalsize++;
strings_writer.write_data((uint8_t)0);
}
head.off_dt_struct = sizeof(head) + reservation_writer.size();;
head.off_dt_strings = head.off_dt_struct + struct_writer.size();
head.off_mem_rsvmap = sizeof(head);
head.boot_cpuid_phys = boot_cpu;
head.size_dt_struct = struct_writer.size();
head.write(head_writer);
head_writer.write_to_file(fd);
reservation_writer.write_to_file(fd);
struct_writer.write_to_file(fd);
strings_writer.write_label(string("dt_blob_end"));
strings_writer.write_to_file(fd);
}
node*
device_tree::referenced_node(property_value &v)
{
if (v.is_phandle())
{
return node_names[v.string_data];
}
if (v.is_binary())
{
return used_phandles[v.get_as_uint32()];
}
return 0;
}
void
device_tree::write_binary(int fd)
{
write<dtb::binary_writer>(fd);
}
void
device_tree::write_asm(int fd)
{
write<dtb::asm_writer>(fd);
}
void
device_tree::write_dts(int fd)
{
FILE *file = fdopen(fd, "w");
fputs("/dtc-v1/;\n\n", file);
if (!reservations.empty())
{
const char msg[] = "/memreserve/";
fwrite(msg, sizeof(msg), 1, file);
for (std::vector<reservation>::iterator i=reservations.begin(),
e=reservations.end() ; i!=e ; ++i)
{
fprintf(stderr, " %" 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);
if (in == 0)
{
valid = false;
return;
}
std::vector<node*> 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/"))
{
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_buffer *include_buffer = buffer_for_file(include_file);
if (include_buffer == 0)
{
for (std::vector<const char*>::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;
input.consume(include_file+dir_length+1);
input.consume('"');
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 /dts-v1/; version string");
}
input.next_token();
input.consume(';');
reservations.push_back(reservation(start, len));
}
parse_roots(input, roots);
switch (roots.size())
{
case 0:
valid = false;
input.parse_error("Failed to find root node /.");
return;
case 1:
root = roots[0];
break;
default:
{
root = roots[0];
for (std::vector<node*>::iterator i=roots.begin()+1,
e=roots.end() ; i!=e ; ++i)
{
root->merge_node(*i);
delete *i;
}
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();
}
}
} // namespace fdt
} // namespace dtc