Add support for parameterised device tree sources to the device tree compiler.

Reviewed by:	brooks
Sponsored by:	DARPA, AFRL
This commit is contained in:
David Chisnall 2013-08-19 12:37:13 +00:00
parent 678d7b9461
commit 88169e0388
4 changed files with 145 additions and 18 deletions

View File

@ -51,6 +51,7 @@
.Op Fl p Ar bytes
.Op Fl V Ar blob_version
.Op Fl W Ar [no-]checker_name
.Op Fl P Ar predefined_properties
.Ar input_file
.Sh DESCRIPTION
The
@ -132,6 +133,22 @@ The ASCII representation of the FDT.
.El
.It Fl o Ar output_file
The file to which to write the output.
.It Fl P Ar predefined_macro
Defines a macro, in the form
.Ar name=value
or
.Ar name
to be used for device tree source files that contain conditional components.
This tool supports two extensions to the standard to support conditional
compilation of device trees.
The first is an
.Ar /include/if [property]/ "file.dts"
directive that is allowed at the start of a file and which will only include
the specified file if it the specified property is passed with this flag.
The second is the
.Ar $NAME
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

View File

@ -100,7 +100,7 @@ main(int argc, char **argv)
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:fisvH:W:E:D";
const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:DP:";
// Don't forget to update the man page if any more options are added.
while ((ch = getopt(argc, argv, options)) != -1)
@ -267,6 +267,13 @@ main(int argc, char **argv)
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;

View File

@ -382,13 +382,45 @@ property::property(input_buffer &structs, input_buffer &strings)
values.push_back(v);
}
property::property(input_buffer &input, string k, string l) : key(k), label(l),
valid(true)
void property::parse_define(input_buffer &input, define_map *defines)
{
input.consume('$');
if (!defines)
{
input.parse_error("No predefined properties to match name\n");
valid = false;
return;
}
string name = string::parse_property_name(input);
define_map::iterator found;
if ((name == string()) ||
((found = defines->find(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;
@ -412,7 +444,7 @@ property::property(input_buffer &input, string k, string l) : key(k), label(l),
}
input.next_token();
} while (input.consume(','));
if (!input.consume(';'))
if (semicolonTerminated && !input.consume(';'))
{
input.parse_error("Expected ; at end of property");
valid = false;
@ -432,9 +464,10 @@ property::parse_dtb(input_buffer &structs, input_buffer &strings)
}
property*
property::parse(input_buffer &input, string key, string label)
property::parse(input_buffer &input, string key, string label,
bool semicolonTerminated, define_map *defines)
{
property *p = new property(input, key, label);
property *p = new property(input, key, label, semicolonTerminated, defines);
if (!p->valid)
{
delete p;
@ -591,7 +624,7 @@ node::node(input_buffer &structs, input_buffer &strings) : valid(true)
return;
}
node::node(input_buffer &input, string n, string l, string a) :
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('{'))
@ -628,7 +661,7 @@ node::node(input_buffer &input, string n, string l, string a) :
if (input.consume('='))
{
property *p= property::parse(input, child_name,
child_label);
child_label, true, defines);
if (p == 0)
{
valid = false;
@ -641,7 +674,7 @@ node::node(input_buffer &input, string n, string l, string a) :
else if (!is_property && input[0] == ('{'))
{
node *child = node::parse(input, child_name,
child_label, child_address);
child_label, child_address, defines);
if (child)
{
children.push_back(child);
@ -693,9 +726,13 @@ node::sort()
}
node*
node::parse(input_buffer &input, string name, string label, string address)
node::parse(input_buffer &input,
string name,
string label,
string address,
define_map *defines)
{
node *n = new node(input, name, label, address);
node *n = new node(input, name, label, address, defines);
if (!n->valid)
{
delete n;
@ -1008,7 +1045,7 @@ device_tree::parse_roots(input_buffer &input, std::vector<node*> &roots)
while (valid && input.consume('/'))
{
input.next_token();
node *n = node::parse(input, string("", 1));
node *n = node::parse(input, string("", 1), string(), string(), &defines);
if (n)
{
roots.push_back(n);
@ -1241,6 +1278,18 @@ device_tree::parse_dts(const char *fn, FILE *depfile)
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('"'))
{
@ -1259,6 +1308,14 @@ device_tree::parse_dts(const char *fn, FILE *depfile)
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)
@ -1292,8 +1349,6 @@ device_tree::parse_dts(const char *fn, FILE *depfile)
return;
}
input_buffer &include = *include_buffer;
input.consume(include_file+dir_length+1);
input.consume('"');
free((void*)include_file);
if (!read_header)
@ -1361,6 +1416,33 @@ device_tree::~device_tree()
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);
if (p)
defines[name] = p;
return p;
}
} // namespace fdt

View File

@ -48,6 +48,8 @@ class string_table;
namespace fdt
{
class property;
typedef std::map<string, property*> define_map;
/**
* Properties may contain a number of different value, each with a different
* label. This class encapsulates a single value.
@ -262,6 +264,10 @@ class property
* 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.
@ -272,7 +278,11 @@ class property
/**
* Parses a new property from the input buffer.
*/
property(input_buffer &input, string k, string l);
property(input_buffer &input,
string k,
string l,
bool terminated,
define_map *defines);
public:
/**
* Creates an empty property.
@ -298,7 +308,9 @@ class property
*/
static property* parse(input_buffer &input,
string key,
string label=string());
string label=string(),
bool semicolonTerminated=true,
define_map *defines=0);
/**
* Iterator type used for accessing the values of a property.
*/
@ -398,7 +410,7 @@ class node
* node. The name, and optionally label and unit address, should have
* already been parsed.
*/
node(input_buffer &input, string n, string l, string a);
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.
@ -476,7 +488,8 @@ class node
static node* parse(input_buffer &input,
string name,
string label=string(),
string address=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
@ -616,6 +629,10 @@ class device_tree
* must be freed separately.
*/
std::vector<const char*> include_paths;
/**
* Dictionary of predefined macros provided on the command line.
*/
define_map defines;
/**
* The default boot CPU, specified in the device tree header.
*/
@ -773,6 +790,10 @@ class device_tree
{
blob_padding = p;
}
/**
* Parses a predefined macro value.
*/
bool parse_define(const char *def);
};
} // namespace fdt