David Gibson
20 years ago
commit
fc14dad769
16 changed files with 2985 additions and 0 deletions
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
TARGETS = dtc |
||||
CFLAGS = -Wall -g |
||||
|
||||
OBJS = dtc.o livetree.o flattree.o data.o treesource.o fstree.o \ |
||||
y.tab.o lex.yy.o |
||||
|
||||
all: $(TARGETS) |
||||
|
||||
dtc: $(OBJS) |
||||
$(LINK.c) -o $@ $^ |
||||
|
||||
$(OBJS): dtc.h |
||||
|
||||
y.tab.c y.tab.h: dtc-parser.y |
||||
$(YACC) -d $< |
||||
|
||||
lex.yy.c: dtc-lexer.l |
||||
$(LEX) $< |
||||
|
||||
lex.yy.o: lex.yy.c y.tab.h |
||||
|
||||
dtc-parser.c: dtc-lexer.c |
||||
|
||||
check: all |
||||
cd tests && $(MAKE) check |
||||
|
||||
clean: |
||||
rm -f *~ *.o a.out core $(TARGETS) |
||||
rm -f *.tab.[ch] lex.yy.c |
||||
rm -f *.i *.output vgcore.* |
||||
cd tests && $(MAKE) clean |
||||
|
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
- Bugfixes: |
||||
|
||||
- Error handling / reporting |
||||
* Report line/column numbers for syntax errors |
||||
* Better categorization of errors into severity levels |
||||
- Generate mem reserve map |
||||
* Command line options to place a number of blank entries to be |
||||
filled in by bootloader |
||||
* memory reserve section in source |
||||
- Testsuite |
||||
- Actually number releases, revision control, all that kind of jazz |
||||
|
||||
- Labels: |
||||
Allow labels in tree source, which will be propogated and |
||||
exported as symbols in asm output |
||||
- Autogenerate phandles |
||||
Allow property values to contain a reference to another node |
||||
(by path or label) which will be turned into a phandle for that node |
||||
in the output. |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
/* regexps for lexing comments are.. tricky. Check if we've actually |
||||
* got it right */ |
||||
|
||||
/ { |
||||
// line comment |
||||
prop1; |
||||
/* comment */ |
||||
prop2; |
||||
/* multiline |
||||
|
||||
notaprop1; |
||||
|
||||
comment */ |
||||
prop3; |
||||
/**/ |
||||
prop4; |
||||
/***/ |
||||
prop5; |
||||
/****/ |
||||
prop6; |
||||
/* another |
||||
* multiline |
||||
* comment */ |
||||
prop7; |
||||
/* yet |
||||
* another |
||||
* multline |
||||
* comment |
||||
*/ |
||||
prop8; |
||||
/** try this */ |
||||
prop9; |
||||
/* and this **/ |
||||
}; |
||||
/* final comment */ |
@ -0,0 +1,250 @@
@@ -0,0 +1,250 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
#if 0 |
||||
static struct data data_init_buf(char *buf, int len) |
||||
{ |
||||
struct data d; |
||||
|
||||
d.asize = 0; |
||||
d.len = len; |
||||
d.val = buf; |
||||
|
||||
return d; |
||||
} |
||||
|
||||
struct data data_ref_string(char *str) |
||||
{ |
||||
return data_init_buf(str, strlen(str)+1); |
||||
} |
||||
#endif |
||||
|
||||
void data_free(struct data d) |
||||
{ |
||||
assert(!d.val || d.asize); |
||||
|
||||
if (d.val) |
||||
free(d.val); |
||||
} |
||||
|
||||
struct data data_grow_for(struct data d, int xlen) |
||||
{ |
||||
struct data nd; |
||||
int newsize; |
||||
|
||||
/* we must start with an allocated datum */ |
||||
assert(!d.val || d.asize); |
||||
|
||||
if (xlen == 0) |
||||
return d; |
||||
|
||||
newsize = xlen; |
||||
|
||||
while ((d.len + xlen) > newsize) |
||||
newsize *= 2; |
||||
|
||||
nd.asize = newsize; |
||||
nd.val = xrealloc(d.val, newsize); |
||||
nd.len = d.len; |
||||
|
||||
assert(nd.asize >= (d.len + xlen)); |
||||
|
||||
return nd; |
||||
} |
||||
|
||||
struct data data_copy_mem(char *mem, int len) |
||||
{ |
||||
struct data d; |
||||
|
||||
d = data_grow_for(empty_data, len); |
||||
|
||||
d.len = len; |
||||
memcpy(d.val, mem, len); |
||||
|
||||
return d; |
||||
} |
||||
|
||||
char hexval(char c) |
||||
{ |
||||
switch (c) { |
||||
case '0' ... '9': |
||||
return c - '0'; |
||||
case 'a' ... 'f': |
||||
return c - 'a'; |
||||
case 'A' ... 'F': |
||||
return c - 'A'; |
||||
default: |
||||
assert(0); |
||||
} |
||||
} |
||||
|
||||
char get_oct_char(char *s, int *i) |
||||
{ |
||||
char x[4]; |
||||
char *endx; |
||||
long val; |
||||
|
||||
x[3] = '\0'; |
||||
x[0] = s[(*i)]; |
||||
if (x[0]) { |
||||
x[1] = s[(*i)+1]; |
||||
if (x[1]) |
||||
x[2] = s[(*i)+2]; |
||||
} |
||||
|
||||
val = strtol(x, &endx, 8); |
||||
if ((endx - x) == 0) |
||||
fprintf(stderr, "Empty \\nnn escape\n"); |
||||
|
||||
(*i) += endx - x; |
||||
return val; |
||||
} |
||||
|
||||
char get_hex_char(char *s, int *i) |
||||
{ |
||||
char x[3]; |
||||
char *endx; |
||||
long val; |
||||
|
||||
x[2] = '\0'; |
||||
x[0] = s[(*i)]; |
||||
if (x[0]) |
||||
x[1] = s[(*i)+1]; |
||||
|
||||
val = strtol(x, &endx, 16); |
||||
if ((endx - x) == 0) |
||||
fprintf(stderr, "Empty \\x escape\n"); |
||||
|
||||
(*i) += endx - x; |
||||
return val; |
||||
} |
||||
|
||||
struct data data_copy_escape_string(char *s, int len) |
||||
{ |
||||
int i = 0; |
||||
struct data d; |
||||
char *q; |
||||
|
||||
d = data_grow_for(empty_data, strlen(s)+1); |
||||
|
||||
q = d.val; |
||||
while (i < len) { |
||||
char c = s[i++]; |
||||
|
||||
if (c != '\\') { |
||||
q[d.len++] = c; |
||||
continue; |
||||
} |
||||
|
||||
c = s[i++]; |
||||
assert(c); |
||||
switch (c) { |
||||
case 't': |
||||
q[d.len++] = '\t'; |
||||
break; |
||||
case 'n': |
||||
q[d.len++] = '\n'; |
||||
break; |
||||
case 'r': |
||||
q[d.len++] = '\r'; |
||||
break; |
||||
case '0' ... '7': |
||||
i--; /* need to re-read the first digit as |
||||
* part of the octal value */ |
||||
q[d.len++] = get_oct_char(s, &i); |
||||
break; |
||||
case 'x': |
||||
q[d.len++] = get_hex_char(s, &i); |
||||
break; |
||||
default: |
||||
q[d.len++] = c; |
||||
} |
||||
} |
||||
|
||||
q[d.len++] = '\0'; |
||||
return d; |
||||
} |
||||
|
||||
struct data data_copy_file(FILE *f, size_t len) |
||||
{ |
||||
struct data d; |
||||
|
||||
d = data_grow_for(empty_data, len); |
||||
|
||||
d.len = len; |
||||
fread(d.val, len, 1, f); |
||||
|
||||
return d; |
||||
} |
||||
|
||||
struct data data_append_data(struct data d, void *p, int len) |
||||
{ |
||||
d = data_grow_for(d, len); |
||||
memcpy(d.val + d.len, p, len); |
||||
d.len += len; |
||||
return d; |
||||
} |
||||
|
||||
struct data data_append_cell(struct data d, cell_t word) |
||||
{ |
||||
cell_t beword = cpu_to_be32(word); |
||||
|
||||
return data_append_data(d, &beword, sizeof(beword)); |
||||
} |
||||
|
||||
struct data data_append_byte(struct data d, uint8_t byte) |
||||
{ |
||||
return data_append_data(d, &byte, 1); |
||||
} |
||||
|
||||
struct data data_append_zeroes(struct data d, int len) |
||||
{ |
||||
d = data_grow_for(d, len); |
||||
|
||||
memset(d.val + d.len, 0, len); |
||||
d.len += len; |
||||
return d; |
||||
} |
||||
|
||||
struct data data_append_align(struct data d, int align) |
||||
{ |
||||
int newlen = ALIGN(d.len, align); |
||||
return data_append_zeroes(d, newlen - d.len); |
||||
} |
||||
|
||||
int data_is_one_string(struct data d) |
||||
{ |
||||
int i; |
||||
int len = d.len; |
||||
|
||||
if (len == 0) |
||||
return 0; |
||||
|
||||
for (i = 0; i < len-1; i++) |
||||
if (d.val[i] == '\0') |
||||
return 0; |
||||
|
||||
if (d.val[len-1] != '\0') |
||||
return 0; |
||||
|
||||
return 1; |
||||
} |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
%option noyywrap |
||||
|
||||
%x CELLDATA |
||||
%x BYTESTRING |
||||
|
||||
PROPCHAR [a-zA-Z0-9,._+*#?-] |
||||
UNITCHAR [0-9a-f,] |
||||
WS [ \t\n] |
||||
|
||||
%% |
||||
|
||||
%{ |
||||
#include "dtc.h" |
||||
|
||||
#include "y.tab.h" |
||||
|
||||
#undef LEXDEBUG 1 |
||||
|
||||
%} |
||||
|
||||
\"[^"]*\" { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "String: %s\n", yytext); |
||||
#endif |
||||
yylval.data = data_copy_escape_string(yytext+1, |
||||
yyleng-2); |
||||
return DT_STRING; |
||||
} |
||||
|
||||
<CELLDATA>[0-9a-fA-F]+ { |
||||
if (yyleng > 2*sizeof(yylval.cval)) { |
||||
fprintf(stderr, |
||||
"Cell value %s too long\n", yytext); |
||||
} |
||||
yylval.cval = strtol(yytext, NULL, 16); |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "Cell: %x\n", yylval.cval); |
||||
#endif |
||||
return DT_CELL; |
||||
} |
||||
|
||||
<CELLDATA>">" { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "/CELLDATA\n"); |
||||
#endif |
||||
BEGIN(INITIAL); |
||||
return '>'; |
||||
} |
||||
|
||||
<BYTESTRING>[0-9a-fA-F]{2} { |
||||
yylval.byte = strtol(yytext, NULL, 16); |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "Byte: %02x\n", (int)yylval.byte); |
||||
#endif |
||||
return DT_BYTE; |
||||
} |
||||
|
||||
<BYTESTRING>"]" { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "/BYTESTRING\n"); |
||||
#endif |
||||
BEGIN(INITIAL); |
||||
return ']'; |
||||
} |
||||
|
||||
{PROPCHAR}+(@{UNITCHAR}+)?/{WS}*\{ { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "NodeName: %s\n", yytext); |
||||
#endif |
||||
yylval.str = strdup(yytext); |
||||
return DT_NODENAME; |
||||
} |
||||
|
||||
{PROPCHAR}+ { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "PropName: %s\n", yytext); |
||||
#endif |
||||
yylval.str = strdup(yytext); |
||||
return DT_PROPNAME; |
||||
} |
||||
|
||||
|
||||
<*>{WS}+ /* eat whitespace */ |
||||
|
||||
<*>"/*"([^*]|\*+[^*/])*\*+"/" { |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "Comment: %s\n", yytext); |
||||
/* eat comments */ |
||||
#endif |
||||
} |
||||
|
||||
<*>"//".*\n /* eat line comments */ |
||||
|
||||
. { |
||||
switch (yytext[0]) { |
||||
case '<': |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "CELLDATA\n"); |
||||
#endif |
||||
BEGIN(CELLDATA); |
||||
break; |
||||
case '[': |
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "BYTESTRING\n"); |
||||
#endif |
||||
BEGIN(BYTESTRING); |
||||
break; |
||||
default: |
||||
|
||||
#ifdef LEXDEBUG |
||||
fprintf(stderr, "Char: %c (\\x%02x)\n", yytext[0], |
||||
(unsigned)yytext[0]); |
||||
#endif |
||||
break; |
||||
} |
||||
|
||||
return yytext[0]; |
||||
} |
||||
|
||||
%% |
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
%{ |
||||
#include "dtc.h" |
||||
|
||||
int yylex (void); |
||||
void yyerror (char const *); |
||||
|
||||
extern struct node *device_tree; |
||||
|
||||
%} |
||||
|
||||
%union { |
||||
cell_t cval; |
||||
uint8_t byte; |
||||
char *str; |
||||
struct data data; |
||||
struct property *prop; |
||||
struct property *proplist; |
||||
struct node *node; |
||||
struct node *nodelist; |
||||
int datalen; |
||||
int hexlen; |
||||
} |
||||
|
||||
%token <str> DT_PROPNAME |
||||
%token <str> DT_NODENAME |
||||
%token <cval> DT_CELL |
||||
%token <byte> DT_BYTE |
||||
%token <data> DT_STRING |
||||
%token <str> DT_UNIT |
||||
|
||||
%type <data> propdata |
||||
%type <data> celllist |
||||
%type <data> bytestring |
||||
%type <prop> propdef |
||||
%type <proplist> proplist |
||||
|
||||
%type <node> nodedef |
||||
%type <node> subnode |
||||
%type <nodelist> subnodes |
||||
|
||||
%% |
||||
|
||||
devicetree: { |
||||
assert(device_tree == NULL); |
||||
} '/' nodedef { device_tree = name_node($3, ""); } |
||||
; |
||||
|
||||
nodedef: '{' proplist subnodes '}' ';' { |
||||
$$ = build_node($2, $3); |
||||
} |
||||
; |
||||
|
||||
proplist: propdef proplist { |
||||
$$ = chain_property($1, $2); |
||||
} |
||||
| /* empty */ { |
||||
$$ = NULL; |
||||
} |
||||
; |
||||
|
||||
propdef: DT_PROPNAME '=' propdata ';' { |
||||
$$ = build_property($1, $3); |
||||
} |
||||
| DT_PROPNAME ';' { |
||||
$$ = build_empty_property($1); |
||||
} |
||||
; |
||||
|
||||
propdata: DT_STRING { $$ = $1; } |
||||
| '<' celllist '>' { $$ = $2; } |
||||
| '[' bytestring ']' { $$ = $2; } |
||||
; |
||||
|
||||
celllist: celllist DT_CELL { $$ = data_append_cell($1, $2); } |
||||
| /* empty */ { $$ = empty_data; } |
||||
; |
||||
|
||||
bytestring: bytestring DT_BYTE { $$ = data_append_byte($1, $2); } |
||||
| /* empty */ { $$ = empty_data; } |
||||
; |
||||
|
||||
subnodes: subnode subnodes { |
||||
$$ = chain_node($1, $2); |
||||
} |
||||
| /* empty */ { $$ = NULL; } |
||||
; |
||||
|
||||
subnode: DT_NODENAME nodedef { $$ = name_node($2, $1); } |
||||
; |
||||
|
||||
%% |
||||
|
||||
void yyerror (char const *s) |
||||
{ |
||||
fprintf (stderr, "%s\n", s); |
||||
} |
@ -0,0 +1,198 @@
@@ -0,0 +1,198 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
char *join_path(char *path, char *name) |
||||
{ |
||||
int lenp = strlen(path); |
||||
int lenn = strlen(name); |
||||
int len; |
||||
int needslash = 1; |
||||
char *str; |
||||
|
||||
len = lenp + lenn + 2; |
||||
if ((lenp > 0) && (path[lenp-1] == '/')) { |
||||
needslash = 0; |
||||
len--; |
||||
} |
||||
|
||||
str = xmalloc(len); |
||||
memcpy(str, path, lenp); |
||||
if (needslash) { |
||||
str[lenp] = '/'; |
||||
lenp++; |
||||
} |
||||
memcpy(str+lenp, name, lenn+1); |
||||
return str; |
||||
} |
||||
|
||||
void fill_fullpaths(struct node *tree, char *prefix) |
||||
{ |
||||
struct node *child; |
||||
char *unit; |
||||
|
||||
tree->fullpath = join_path(prefix, tree->name); |
||||
|
||||
unit = strchr(tree->name, '@'); |
||||
if (unit) |
||||
tree->basenamelen = unit - tree->name; |
||||
else |
||||
tree->basenamelen = strlen(tree->name); |
||||
|
||||
for_each_child(tree, child) |
||||
fill_fullpaths(child, tree->fullpath); |
||||
} |
||||
|
||||
static FILE *dtc_open_file(char *fname) |
||||
{ |
||||
FILE *f; |
||||
|
||||
if (streq(fname, "-")) |
||||
f = stdin; |
||||
else |
||||
f = fopen(fname, "r"); |
||||
|
||||
if (! f) |
||||
die("Couldn't open \"%s\": %s\n", fname, strerror(errno)); |
||||
|
||||
return f; |
||||
} |
||||
|
||||
static void usage(void) |
||||
{ |
||||
fprintf(stderr, "Usage:\n"); |
||||
fprintf(stderr, "\tdtc [options] <input file>\n"); |
||||
fprintf(stderr, "\nOptions:\n"); |
||||
fprintf(stderr, "\t-I <input format>\n"); |
||||
fprintf(stderr, "\t\tInput formats are:\n"); |
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n"); |
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n"); |
||||
fprintf(stderr, "\t\t\tfs - /proc/device-tree style directory\n"); |
||||
fprintf(stderr, "\t-O <output format>\n"); |
||||
fprintf(stderr, "\t\tOutput formats are:\n"); |
||||
fprintf(stderr, "\t\t\tdts - device tree source text\n"); |
||||
fprintf(stderr, "\t\t\tdtb - device tree blob\n"); |
||||
fprintf(stderr, "\t\t\tasm - assembler source\n"); |
||||
fprintf(stderr, "\t-V <output version>\n"); |
||||
fprintf(stderr, "\t\tBlob version to produce, defaults to 3 (relevant for dtb\n\t\tand asm output only)\n"); |
||||
fprintf(stderr, "\t-R <number>\n"); |
||||
fprintf(stderr, "\t\tMake space for <number> reserve map entries (relevant for \n\t\tdtb and asm output only)\n"); |
||||
fprintf(stderr, "\t-f\n"); |
||||
fprintf(stderr, "\t\tForce - try to produce output even if the input tree has errors\n"); |
||||
exit(2); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
struct node *dt; |
||||
char *inform = "dts"; |
||||
char *outform = "dts"; |
||||
char *outname = "-"; |
||||
int force = 0; |
||||
char *arg; |
||||
int opt; |
||||
FILE *inf = NULL; |
||||
FILE *outf = NULL; |
||||
int outversion = 3; |
||||
int reservenum = 1; |
||||
|
||||
while ((opt = getopt(argc, argv, "I:O:o:V:R:f")) != EOF) { |
||||
switch (opt) { |
||||
case 'I': |
||||
inform = optarg; |
||||
break; |
||||
case 'O': |
||||
outform = optarg; |
||||
break; |
||||
case 'o': |
||||
outname = optarg; |
||||
break; |
||||
case 'V': |
||||
outversion = strtol(optarg, NULL, 0); |
||||
break; |
||||
case 'R': |
||||
reservenum = strtol(optarg, NULL, 0); |
||||
break; |
||||
case 'f': |
||||
force = 1; |
||||
break; |
||||
default: |
||||
usage(); |
||||
} |
||||
} |
||||
|
||||
if (argc > (optind+1)) |
||||
usage(); |
||||
else if (argc < (optind+1)) |
||||
arg = "-"; |
||||
else |
||||
arg = argv[optind]; |
||||
|
||||
fprintf(stderr, "DTC: %s->%s on file \"%s\"\n", |
||||
inform, outform, arg); |
||||
|
||||
if (streq(inform, "dts")) { |
||||
inf = dtc_open_file(arg); |
||||
dt = dt_from_source(inf); |
||||
} else if (streq(inform, "fs")) { |
||||
dt = dt_from_fs(arg); |
||||
} else if(streq(inform, "dtb")) { |
||||
inf = dtc_open_file(arg); |
||||
dt = dt_from_blob(inf); |
||||
} else { |
||||
die("Unknown input format \"%s\"\n", inform); |
||||
} |
||||
|
||||
if (inf && (inf != stdin)) |
||||
fclose(inf); |
||||
|
||||
if (! dt) |
||||
die("Couldn't read input tree\n"); |
||||
|
||||
if (! check_device_tree(dt)) { |
||||
fprintf(stderr, "Input tree has errors\n"); |
||||
if (! force) |
||||
exit(1); |
||||
} |
||||
|
||||
if (streq(outname, "-")) { |
||||
outf = stdout; |
||||
} else { |
||||
outf = fopen(outname, "w"); |
||||
if (! outf) |
||||
die("Couldn't open output file %s: %s\n", |
||||
outname, strerror(errno)); |
||||
} |
||||
|
||||
if (streq(outform, "dts")) { |
||||
write_tree_source(outf, dt, 0); |
||||
} else if (streq(outform, "dtb")) { |
||||
write_dt_blob(outf, dt, outversion, reservenum); |
||||
} else if (streq(outform, "asm")) { |
||||
write_dt_asm(outf, dt, outversion, reservenum); |
||||
} else if (streq(outform, "null")) { |
||||
/* do nothing */ |
||||
} else { |
||||
die("Unknown output format \"%s\"\n", outform); |
||||
} |
||||
|
||||
exit(0); |
||||
} |
@ -0,0 +1,181 @@
@@ -0,0 +1,181 @@
|
||||
#ifndef _DTC_H |
||||
#define _DTC_H |
||||
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdint.h> |
||||
#include <stdarg.h> |
||||
#include <assert.h> |
||||
#include <ctype.h> |
||||
#include <errno.h> |
||||
#include <unistd.h> |
||||
#include <netinet/in.h> |
||||
|
||||
static inline void die(char * str, ...) |
||||
{ |
||||
va_list ap; |
||||
|
||||
va_start(ap, str); |
||||
fprintf(stderr, "FATAL ERROR: "); |
||||
vfprintf(stderr, str, ap); |
||||
exit(1); |
||||
} |
||||
|
||||
static inline void *xmalloc(size_t len) |
||||
{ |
||||
void *new = malloc(len); |
||||
|
||||
if (! new) |
||||
die("malloc() failed\n"); |
||||
|
||||
return new; |
||||
} |
||||
|
||||
static inline void *xrealloc(void *p, size_t len) |
||||
{ |
||||
void *new = realloc(p, len); |
||||
|
||||
if (! new) |
||||
die("realloc() failed (len=%d)\n", len); |
||||
|
||||
return new; |
||||
} |
||||
|
||||
typedef uint16_t u16; |
||||
typedef uint32_t u32; |
||||
typedef uint64_t u64; |
||||
typedef u32 cell_t; |
||||
|
||||
#define cpu_to_be16(x) htons(x) |
||||
#define be16_to_cpu(x) ntohs(x) |
||||
|
||||
#define cpu_to_be32(x) htonl(x) |
||||
#define be32_to_cpu(x) ntohl(x) |
||||
|
||||
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0) |
||||
#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) |
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
||||
|
||||
/* Data blobs */ |
||||
struct data { |
||||
int len; |
||||
char *val; |
||||
int asize; |
||||
}; |
||||
|
||||
#define empty_data ((struct data){.len = 0, .val = NULL, .asize = 0}) |
||||
|
||||
void data_free(struct data d); |
||||
|
||||
struct data data_grow_for(struct data d, int xlen); |
||||
|
||||
struct data data_copy_mem(char *mem, int len); |
||||
struct data data_copy_escape_string(char *s, int len); |
||||
struct data data_copy_file(FILE *f, size_t len); |
||||
|
||||
struct data data_append_data(struct data d, void *p, int len); |
||||
struct data data_append_cell(struct data d, cell_t word); |
||||
struct data data_append_byte(struct data d, uint8_t byte); |
||||
struct data data_append_zeroes(struct data d, int len); |
||||
struct data data_append_align(struct data d, int align); |
||||
|
||||
int data_is_one_string(struct data d); |
||||
|
||||
/* DT constraints */ |
||||
|
||||
#define MAX_PROPNAME_LEN 31 |
||||
#define MAX_NODENAME_LEN 31 |
||||
|
||||
/* Live trees */ |
||||
struct property { |
||||
char *name; |
||||
struct data val; |
||||
|
||||
struct property *next; |
||||
}; |
||||
|
||||
struct node { |
||||
char *name; |
||||
struct property *proplist; |
||||
struct node *children; |
||||
|
||||
struct node *parent; |
||||
struct node *next_sibling; |
||||
|
||||
char *fullpath; |
||||
int basenamelen; |
||||
|
||||
cell_t phandle; |
||||
int addr_cells, size_cells; |
||||
}; |
||||
|
||||
#define for_each_property(n, p) \ |
||||
for ((p) = (n)->proplist; (p); (p) = (p)->next) |
||||
|
||||
#define for_each_child(n, c) \ |
||||
for ((c) = (n)->children; (c); (c) = (c)->next_sibling) |
||||
|
||||
struct property *build_property(char *name, struct data val); |
||||
struct property *build_empty_property(char *name); |
||||
struct property *chain_property(struct property *first, struct property *list); |
||||
|
||||
struct node *build_node(struct property *proplist, struct node *children); |
||||
struct node *name_node(struct node *node, char *name); |
||||
struct node *chain_node(struct node *first, struct node *list); |
||||
|
||||
void add_property(struct node *node, struct property *prop); |
||||
void add_child(struct node *parent, struct node *child); |
||||
|
||||
int check_device_tree(struct node *dt); |
||||
|
||||
/* Flattened trees */ |
||||
|
||||
enum flat_dt_format { |
||||
FFMT_BIN, |
||||
FFMT_ASM, |
||||
}; |
||||
|
||||
void write_dt_blob(FILE *f, struct node *tree, int version, int reservenum); |
||||
void write_dt_asm(FILE *f, struct node *tree, int version, int reservenum); |
||||
|
||||
struct node *dt_from_blob(FILE *f); |
||||
|
||||
/* Tree source */ |
||||
|
||||
void write_tree_source(FILE *f, struct node *tree, int level); |
||||
|
||||
struct node *dt_from_source(FILE *f); |
||||
|
||||
/* FS trees */ |
||||
|
||||
struct node *dt_from_fs(char *dirname); |
||||
|
||||
/* misc */ |
||||
|
||||
char *join_path(char *path, char *name); |
||||
void fill_fullpaths(struct node *tree, char *prefix); |
||||
|
||||
#endif /* _DTC_H */ |
@ -0,0 +1,799 @@
@@ -0,0 +1,799 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
#define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ |
||||
|
||||
#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name */ |
||||
#define OF_DT_END_NODE 0x2 /* End node */ |
||||
#define OF_DT_PROP 0x3 /* Property: name off, |
||||
size, content */ |
||||
#define OF_DT_END 0x9 |
||||
|
||||
struct boot_param_header { |
||||
u32 magic; /* magic word OF_DT_HEADER */ |
||||
u32 totalsize; /* total size of DT block */ |
||||
u32 off_dt_struct; /* offset to structure */ |
||||
u32 off_dt_strings; /* offset to strings */ |
||||
u32 off_mem_rsvmap; /* offset to memory reserve map */ |
||||
u32 version; /* format version */ |
||||
u32 last_comp_version; /* last compatible version */ |
||||
|
||||
/* version 2 fields below */ |
||||
u32 boot_cpuid_phys; /* Which physical CPU id we're |
||||
booting on */ |
||||
/* version 3 fields below */ |
||||
u32 size_dt_strings; /* size of the strings block */ |
||||
}; |
||||
|
||||
#define BPH_V1_SIZE (7*sizeof(u32)) |
||||
#define BPH_V2_SIZE (BPH_V1_SIZE + sizeof(u32)) |
||||
#define BPH_V3_SIZE (BPH_V2_SIZE + sizeof(u32)) |
||||
|
||||
struct reserve_entry { |
||||
u64 address; |
||||
u64 size; |
||||
}; |
||||
|
||||
#define FTF_FULLPATH 0x1 |
||||
#define FTF_VARALIGN 0x2 |
||||
#define FTF_NAMEPROPS 0x4 |
||||
#define FTF_BOOTCPUID 0x8 |
||||
#define FTF_STRTABSIZE 0x10 |
||||
|
||||
struct version_info { |
||||
int version; |
||||
int last_comp_version; |
||||
int hdr_size; |
||||
int flags; |
||||
} version_table[] = { |
||||
{1, 1, BPH_V1_SIZE, |
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS}, |
||||
{2, 1, BPH_V2_SIZE, |
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID}, |
||||
{3, 1, BPH_V3_SIZE, |
||||
FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE}, |
||||
{0x10, 0x10, BPH_V3_SIZE, |
||||
FTF_BOOTCPUID|FTF_STRTABSIZE}, |
||||
}; |
||||
|
||||
struct emitter { |
||||
void (*cell)(void *, cell_t); |
||||
void (*string)(void *, char *, int); |
||||
void (*align)(void *, int); |
||||
void (*data)(void *, struct data); |
||||
void (*beginnode)(void *); |
||||
void (*endnode)(void *); |
||||
void (*property)(void *); |
||||
}; |
||||
|
||||
static void bin_emit_cell(void *e, cell_t val) |
||||
{ |
||||
struct data *dtbuf = e; |
||||
|
||||
*dtbuf = data_append_cell(*dtbuf, val); |
||||
} |
||||
|
||||
static void bin_emit_string(void *e, char *str, int len) |
||||
{ |
||||
struct data *dtbuf = e; |
||||
|
||||
if (len == 0) |
||||
len = strlen(str); |
||||
|
||||
*dtbuf = data_append_data(*dtbuf, str, len); |
||||
*dtbuf = data_append_byte(*dtbuf, '\0'); |
||||
} |
||||
|
||||
static void bin_emit_align(void *e, int a) |
||||
{ |
||||
struct data *dtbuf = e; |
||||
|
||||
*dtbuf = data_append_align(*dtbuf, a); |
||||
} |
||||
|
||||
static void bin_emit_data(void *e, struct data d) |
||||
{ |
||||
struct data *dtbuf = e; |
||||
|
||||
*dtbuf = data_append_data(*dtbuf, d.val, d.len); |
||||
} |
||||
|
||||
static void bin_emit_beginnode(void *e) |
||||
{ |
||||
bin_emit_cell(e, OF_DT_BEGIN_NODE); |
||||
} |
||||
|
||||
static void bin_emit_endnode(void *e) |
||||
{ |
||||
bin_emit_cell(e, OF_DT_END_NODE); |
||||
} |
||||
|
||||
static void bin_emit_property(void *e) |
||||
{ |
||||
bin_emit_cell(e, OF_DT_PROP); |
||||
} |
||||
|
||||
struct emitter bin_emitter = { |
||||
.cell = bin_emit_cell, |
||||
.string = bin_emit_string, |
||||
.align = bin_emit_align, |
||||
.data = bin_emit_data, |
||||
.beginnode = bin_emit_beginnode, |
||||
.endnode = bin_emit_endnode, |
||||
.property = bin_emit_property, |
||||
}; |
||||
|
||||
static void asm_emit_cell(void *e, cell_t val) |
||||
{ |
||||
FILE *f = e; |
||||
|
||||
fprintf(f, "\t.long\t0x%x\n", be32_to_cpu(val)); |
||||
} |
||||
|
||||
static void asm_emit_string(void *e, char *str, int len) |
||||
{ |
||||
FILE *f = e; |
||||
char c; |
||||
|
||||
if (len != 0) { |
||||
/* XXX: ewww */ |
||||
c = str[len]; |
||||
str[len] = '\0'; |
||||
} |
||||
|
||||
fprintf(f, "\t.string\t\"%s\"\n", str); |
||||
|
||||
if (len != 0) { |
||||
str[len] = c; |
||||
} |
||||
} |
||||
|
||||
static void asm_emit_align(void *e, int a) |
||||
{ |
||||
FILE *f = e; |
||||
|
||||
fprintf(f, "\t.balign\t%d\n", a); |
||||
} |
||||
|
||||
static void asm_emit_data(void *e, struct data d) |
||||
{ |
||||
FILE *f = e; |
||||
int off = 0; |
||||
|
||||
while ((d.len - off) >= sizeof(u32)) { |
||||
fprintf(f, "\t.long\t0x%x\n", |
||||
be32_to_cpu(*((u32 *)(d.val+off)))); |
||||
off += sizeof(u32); |
||||
} |
||||
|
||||
if ((d.len - off) >= sizeof(u16)) { |
||||
fprintf(f, "\t.short\t0x%hx\n", |
||||
be16_to_cpu(*((u16 *)(d.val+off)))); |
||||
off += sizeof(u16); |
||||
} |
||||
|
||||
if ((d.len - off) >= 1) { |
||||
fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]); |
||||
off += 1; |
||||
} |
||||
|
||||
assert(off == d.len); |
||||
} |
||||
|
||||
static void asm_emit_beginnode(void *e) |
||||
{ |
||||
FILE *f = e; |
||||
|
||||
fprintf(f, "\t.long\tOF_DT_BEGIN_NODE\n"); |
||||
} |
||||
|
||||
static void asm_emit_endnode(void *e) |
||||
{ |
||||
FILE *f = e; |
||||
|
||||
fprintf(f, "\t.long\tOF_DT_END_NODE\n"); |
||||
} |
||||
|
||||
static void asm_emit_property(void *e) |
||||
{ |
||||
FILE *f = e; |
||||
|
||||
fprintf(f, "\t.long\tOF_DT_PROP\n"); |
||||
} |
||||
|
||||
struct emitter asm_emitter = { |
||||
.cell = asm_emit_cell, |
||||
.string = asm_emit_string, |
||||
.align = asm_emit_align, |
||||
.data = asm_emit_data, |
||||
.beginnode = asm_emit_beginnode, |
||||
.endnode = asm_emit_endnode, |
||||
.property = asm_emit_property, |
||||
}; |
||||
|
||||
static int stringtable_insert(struct data *d, char *str) |
||||
{ |
||||
int i; |
||||
|
||||
/* FIXME: do this more efficiently? */ |
||||
|
||||
for (i = 0; i < d->len; i++) { |
||||
if (streq(str, d->val + i)) |
||||
return i; |
||||
} |
||||
|
||||
*d = data_append_data(*d, str, strlen(str)+1); |
||||
return i; /* i equals the old data length */ |
||||
} |
||||
|
||||
static void flatten_tree(struct node *tree, struct emitter *emit, |
||||
void *etarget, struct data *strbuf, |
||||
struct version_info *vi) |
||||
{ |
||||
struct property *prop; |
||||
struct node *child; |
||||
int seen_name_prop = 0; |
||||
|
||||
emit->beginnode(etarget); |
||||
|
||||
if (vi->flags & FTF_FULLPATH) |
||||
emit->string(etarget, tree->fullpath, 0); |
||||
else |
||||
emit->string(etarget, tree->name, 0); |
||||
|
||||
emit->align(etarget, sizeof(cell_t)); |
||||
|
||||
for_each_property(tree, prop) { |
||||
int nameoff; |
||||
|
||||
if (streq(prop->name, "name")) |
||||
seen_name_prop = 1; |
||||
|
||||
nameoff = stringtable_insert(strbuf, prop->name); |
||||
|
||||
emit->property(etarget); |
||||
emit->cell(etarget, prop->val.len); |
||||
emit->cell(etarget, nameoff); |
||||
|
||||
if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8)) |
||||
emit->align(etarget, 8); |
||||
|
||||
emit->data(etarget, prop->val); |
||||
emit->align(etarget, sizeof(cell_t)); |
||||
} |
||||
|
||||
if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { |
||||
emit->property(etarget); |
||||
emit->cell(etarget, tree->basenamelen+1); |
||||
emit->cell(etarget, stringtable_insert(strbuf, "name")); |
||||
|
||||
if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8)) |
||||
emit->align(etarget, 8); |
||||
|
||||
emit->string(etarget, tree->name, tree->basenamelen); |
||||
} |
||||
|
||||
for_each_child(tree, child) { |
||||
flatten_tree(child, emit, etarget, strbuf, vi); |
||||
} |
||||
|
||||
emit->endnode(etarget); |
||||
} |
||||
|
||||
static void make_bph(struct boot_param_header *bph, |
||||
struct version_info *vi, |
||||
int reservenum, |
||||
int dtsize, int strsize) |
||||
{ |
||||
int reservesize = (reservenum+1) * sizeof(struct reserve_entry); |
||||
|
||||
memset(bph, 0xff, sizeof(*bph)); |
||||
|
||||
bph->magic = cpu_to_be32(OF_DT_HEADER); |
||||
bph->version = vi->version; |
||||
bph->last_comp_version = vi->last_comp_version; |
||||
|
||||
bph->off_mem_rsvmap = cpu_to_be32(vi->hdr_size); |
||||
bph->off_dt_struct = cpu_to_be32(vi->hdr_size + reservesize); |
||||
bph->off_dt_strings = cpu_to_be32(vi->hdr_size + reservesize |
||||
+ dtsize); |
||||
bph->totalsize = cpu_to_be32(vi->hdr_size + reservesize |
||||
+ dtsize + strsize); |
||||
|
||||
if (vi->flags & FTF_BOOTCPUID) |
||||
bph->boot_cpuid_phys = 0xfeedbeef; |
||||
if (vi->flags & FTF_STRTABSIZE) |
||||
bph->size_dt_strings = cpu_to_be32(strsize); |
||||
} |
||||
|
||||
void write_dt_blob(FILE *f, struct node *tree, int version, int reservenum) |
||||
{ |
||||
struct version_info *vi = NULL; |
||||
int i; |
||||
struct data dtbuf = empty_data; |
||||
struct data strbuf = empty_data; |
||||
struct boot_param_header bph; |
||||
struct reserve_entry re = {.address = 0, .size = 0}; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(version_table); i++) { |
||||
if (version_table[i].version == version) |
||||
vi = &version_table[i]; |
||||
} |
||||
if (!vi) |
||||
die("Unknown device tree blob version %d\n", version); |
||||
|
||||
dtbuf = empty_data; |
||||
strbuf = empty_data; |
||||
|
||||
flatten_tree(tree, &bin_emitter, &dtbuf, &strbuf, vi); |
||||
bin_emit_cell(&dtbuf, OF_DT_END); |
||||
|
||||
make_bph(&bph, vi, reservenum, dtbuf.len, strbuf.len); |
||||
|
||||
fwrite(&bph, vi->hdr_size, 1, f); |
||||
for (i = 0; i < reservenum+1; i++) |
||||
fwrite(&re, sizeof(re), 1, f); |
||||
|
||||
fwrite(dtbuf.val, dtbuf.len, 1, f); |
||||
fwrite(strbuf.val, strbuf.len, 1, f); |
||||
|
||||
if (ferror(f)) |
||||
die("Error writing device tree blob: %s\n", strerror(errno)); |
||||
|
||||
data_free(dtbuf); |
||||
data_free(strbuf); |
||||
} |
||||
|
||||
void dump_stringtable_asm(FILE *f, struct data strbuf) |
||||
{ |
||||
char *p; |
||||
int len; |
||||
|
||||
p = strbuf.val; |
||||
|
||||
while (p < (strbuf.val + strbuf.len)) { |
||||
len = strlen(p); |
||||
fprintf(f, "\t.string \"%s\"\n", p); |
||||
p += len+1; |
||||
} |
||||
} |
||||
|
||||
void emit_label(FILE *f, char *prefix, char *label) |
||||
{ |
||||
fprintf(f, "\t.globl\t%s_%s\n", prefix, label); |
||||
fprintf(f, "%s_%s:\n", prefix, label); |
||||
fprintf(f, "_%s_%s:\n", prefix, label); |
||||
} |
||||
|
||||
void write_dt_asm(FILE *f, struct node *tree, int version, int reservenum) |
||||
{ |
||||
struct version_info *vi = NULL; |
||||
int i; |
||||
struct data strbuf = empty_data; |
||||
char *symprefix = "dt"; |
||||
|
||||
for (i = 0; i < ARRAY_SIZE(version_table); i++) { |
||||
if (version_table[i].version == version) |
||||
vi = &version_table[i]; |
||||
} |
||||
if (!vi) |
||||
die("Unknown device tree blob version %d\n", version); |
||||
|
||||
fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); |
||||
fprintf(f, "#define OF_DT_HEADER 0x%x\n", OF_DT_HEADER); |
||||
fprintf(f, "#define OF_DT_BEGIN_NODE 0x%x\n", OF_DT_BEGIN_NODE); |
||||
fprintf(f, "#define OF_DT_END_NODE 0x%x\n", OF_DT_END_NODE); |
||||
fprintf(f, "#define OF_DT_PROP 0x%x\n", OF_DT_PROP); |
||||
fprintf(f, "#define OF_DT_END 0x%x\n", OF_DT_END); |
||||
fprintf(f, "\n"); |
||||
|
||||
emit_label(f, symprefix, "blob_start"); |
||||
emit_label(f, symprefix, "header"); |
||||
fprintf(f, "\t.long\tOF_DT_HEADER /* magic */\n"); |
||||
fprintf(f, "\t.long\t_%s_blob_end - _%s_blob_start /* totalsize */\n", |
||||
symprefix, symprefix); |
||||
fprintf(f, "\t.long\t_%s_struct_start - _%s_blob_start /* off_dt_struct */\n", |
||||
symprefix, symprefix); |
||||
fprintf(f, "\t.long\t_%s_strings_start - _%s_blob_start /* off_dt_strings */\n", |
||||
symprefix, symprefix); |
||||
fprintf(f, "\t.long\t_%s_reserve_map - _%s_blob_start /* off_dt_strings */\n", |
||||
symprefix, symprefix); |
||||
fprintf(f, "\t.long\t%d /* version */\n", vi->version); |
||||
fprintf(f, "\t.long\t%d /* last_comp_version */\n", |
||||
vi->last_comp_version); |
||||
|
||||
if (vi->flags & FTF_BOOTCPUID) |
||||
fprintf(f, "\t.long\t0xdeadbeef\t/*boot_cpuid_phys*/\n"); |
||||
|
||||
if (vi->flags & FTF_STRTABSIZE) |
||||
fprintf(f, "\t.long\t_%s_strings_end - _%s_strings_start\t/* size_dt_strings */\n", |
||||
symprefix, symprefix); |
||||
|
||||
emit_label(f, symprefix, "reserve_map"); |
||||
/* reserve map entry for the device tree itself */ |
||||
fprintf(f, "\t.long\t0, _%s_blob_start\n", symprefix); |
||||
fprintf(f, "\t.long\t0, _%s_blob_end - _%s_blob_start\n", |
||||
symprefix, symprefix); |
||||
for (i = 0; i < reservenum+1; i++) { |
||||
fprintf(f, "\t.llong\t0\n"); |
||||
fprintf(f, "\t.llong\t0\n"); |
||||
} |
||||
|
||||
emit_label(f, symprefix, "struct_start"); |
||||
flatten_tree(tree, &asm_emitter, f, &strbuf, vi); |
||||
fprintf(f, "\t.long\tOF_DT_END\n"); |
||||
emit_label(f, symprefix, "struct_end"); |
||||
|
||||
emit_label(f, symprefix, "strings_start"); |
||||
dump_stringtable_asm(f, strbuf); |
||||
emit_label(f, symprefix, "strings_end"); |
||||
|
||||
emit_label(f, symprefix, "blob_end"); |
||||
|
||||
data_free(strbuf); |
||||
} |
||||
|
||||
struct inbuf { |
||||
char *base, *limit, *ptr; |
||||
}; |
||||
|
||||
static void inbuf_init(struct inbuf *inb, void *base, void *limit) |
||||
{ |
||||
inb->base = base; |
||||
inb->limit = limit; |
||||
inb->ptr = inb->base; |
||||
} |
||||
|
||||
static void flat_read_chunk(struct inbuf *inb, void *p, int len) |
||||
{ |
||||
if ((inb->ptr + len) > inb->limit) |
||||
die("Premature end of data parsing flat device tree\n"); |
||||
|
||||
memcpy(p, inb->ptr, len); |
||||
|
||||
inb->ptr += len; |
||||
} |
||||
|
||||
static u32 flat_read_word(struct inbuf *inb) |
||||
{ |
||||
u32 val; |
||||
|
||||
assert(((inb->ptr - inb->base) % sizeof(val)) == 0); |
||||
|
||||
flat_read_chunk(inb, &val, sizeof(val)); |
||||
|
||||
return be32_to_cpu(val); |
||||
} |
||||
|
||||
static void flat_realign(struct inbuf *inb, int align) |
||||
{ |
||||
int off = inb->ptr - inb->base; |
||||
|
||||
inb->ptr = inb->base + ALIGN(off, align); |
||||
if (inb->ptr > inb->limit) |
||||
die("Premature end of data parsing flat device tree\n"); |
||||
} |
||||
|
||||
static char *flat_read_string(struct inbuf *inb) |
||||
{ |
||||
int len = 0; |
||||
char *p = inb->ptr; |
||||
char *str; |
||||
|
||||
do { |
||||
if (p >= inb->limit) |
||||
die("Premature end of data parsing flat device tree\n"); |
||||
len++; |
||||
} while ((*p++) != '\0'); |
||||
|
||||
str = strdup(inb->ptr); |
||||
|
||||
inb->ptr += len; |
||||
|
||||
flat_realign(inb, sizeof(u32)); |
||||
|
||||
return str; |
||||
} |
||||
|
||||
static struct data flat_read_data(struct inbuf *inb, int len) |
||||
{ |
||||
struct data d = empty_data; |
||||
|
||||
if (len == 0) |
||||
return empty_data; |
||||
|
||||
d = data_grow_for(d, len); |
||||
d.len = len; |
||||
|
||||
flat_read_chunk(inb, d.val, len); |
||||
|
||||
flat_realign(inb, sizeof(u32)); |
||||
|
||||
return d; |
||||
} |
||||
|
||||
static char *flat_read_stringtable(struct inbuf *inb, int offset) |
||||
{ |
||||
char *p; |
||||
|
||||
p = inb->base + offset; |
||||
while (1) { |
||||
if (p >= inb->limit) |
||||
die("String offset %d overruns string table\n"); |
||||
|
||||
if (*p == '\0') |
||||
break; |
||||
|
||||
p++; |
||||
} |
||||
|
||||
return strdup(inb->base + offset); |
||||
} |
||||
|
||||
struct property *flat_read_property(struct inbuf *dtbuf, struct inbuf *strbuf, |
||||
int flags) |
||||
{ |
||||
u32 proplen, stroff; |
||||
char *name; |
||||
struct data val; |
||||
|
||||
proplen = flat_read_word(dtbuf); |
||||
stroff = flat_read_word(dtbuf); |
||||
|
||||
name = flat_read_stringtable(strbuf, stroff); |
||||
|
||||
if ((flags & FTF_VARALIGN) && (proplen >= 8)) |
||||
flat_realign(dtbuf, 8); |
||||
|
||||
val = flat_read_data(dtbuf, proplen); |
||||
|
||||
return build_property(name, val); |
||||
} |
||||
|
||||
static char *nodename_from_path(char *ppath, char *cpath) |
||||
{ |
||||
char *lslash; |
||||
int plen; |
||||
|
||||
lslash = strrchr(cpath, '/'); |
||||
if (! lslash) |
||||
return NULL; |
||||
|
||||
plen = lslash - cpath; |
||||
|
||||
if (streq(cpath, "/") && streq(ppath, "")) |
||||
return ""; |
||||
|
||||
if ((plen == 0) && streq(ppath, "/")) |
||||
return strdup(lslash+1); |
||||
|
||||
if (strncmp(ppath, cpath, plen) != 0) |
||||
return NULL; |
||||
|
||||
return strdup(lslash+1); |
||||
} |
||||
|
||||
static const char PROPCHAR[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,._+*#?-"; |
||||
static const char UNITCHAR[] = "0123456789abcdef,"; |
||||
|
||||
static int check_node_name(char *name) |
||||
{ |
||||
char *atpos; |
||||
int basenamelen; |
||||
|
||||
atpos = strrchr(name, '@'); |
||||
|
||||
if (atpos) |
||||
basenamelen = atpos - name; |
||||
else |
||||
basenamelen = strlen(name); |
||||
|
||||
if (strspn(name, PROPCHAR) < basenamelen) |
||||
return -1; |
||||
|
||||
if (atpos |
||||
&& ((basenamelen + 1 + strspn(atpos+1, UNITCHAR)) < strlen(name))) |
||||
return -1; |
||||
|
||||
return basenamelen; |
||||
} |
||||
|
||||
static struct node *unflatten_tree(struct inbuf *dtbuf, |
||||
struct inbuf *strbuf, |
||||
char *parent_path, int flags) |
||||
{ |
||||
struct node *node; |
||||
u32 val; |
||||
|
||||
node = build_node(NULL, NULL); |
||||
|
||||
if (flags & FTF_FULLPATH) { |
||||
node->fullpath = flat_read_string(dtbuf); |
||||
node->name = nodename_from_path(parent_path, node->fullpath); |
||||
|
||||
if (! node->name) |
||||
die("Path \"%s\" is not valid as a child of \"%s\"\n", |
||||
node->fullpath, parent_path); |
||||
} else { |
||||
node->name = flat_read_string(dtbuf); |
||||
node->fullpath = join_path(parent_path, node->name); |
||||
} |
||||
|
||||
node->basenamelen = check_node_name(node->name); |
||||
if (node->basenamelen < 0) { |
||||
fprintf(stderr, "Warning \"%s\" has incorrect format\n", node->name); |
||||
} |
||||
|
||||
do { |
||||
struct property *prop; |
||||
struct node *child; |
||||
|
||||
val = flat_read_word(dtbuf); |
||||
switch (val) { |
||||
case OF_DT_PROP: |
||||
prop = flat_read_property(dtbuf, strbuf, flags); |
||||
add_property(node, prop); |
||||
break; |
||||
|
||||
case OF_DT_BEGIN_NODE: |
||||
child = unflatten_tree(dtbuf,strbuf, node->fullpath, |
||||
flags); |
||||
add_child(node, child); |
||||
break; |
||||
|
||||
case OF_DT_END_NODE: |
||||
break; |
||||
|
||||
case OF_DT_END: |
||||
die("Premature OF_DT_END in device tree blob\n"); |
||||
break; |
||||
|
||||
default: |
||||
die("Invalid opcode word %08x in device tree blob\n", |
||||
val); |
||||
} |
||||
} while (val != OF_DT_END_NODE); |
||||
|
||||
return node; |
||||
} |
||||
|
||||
struct node *dt_from_blob(FILE *f) |
||||
{ |
||||
u32 magic, totalsize, off_dt, off_str, version, size_str; |
||||
int rc; |
||||
char *blob; |
||||
struct boot_param_header *bph; |
||||
char *p; |
||||
struct inbuf dtbuf, strbuf; |
||||
int sizeleft; |
||||
struct node *tree; |
||||
u32 val; |
||||
int flags = 0; |
||||
|
||||
rc = fread(&magic, sizeof(magic), 1, f); |
||||
if (ferror(f)) |
||||
die("Error reading DT blob magic number: %s\n", |
||||
strerror(errno)); |
||||
if (rc < 1) { |
||||
if (feof(f)) |
||||
die("EOF reading DT blob magic number\n"); |
||||
else |
||||
die("Mysterious short read reading magic number\n"); |
||||
} |
||||
|
||||
magic = be32_to_cpu(magic); |
||||
if (magic != OF_DT_HEADER) |
||||
die("Blob has incorrect magic number\n"); |
||||
|
||||
rc = fread(&totalsize, sizeof(totalsize), 1, f); |
||||
if (ferror(f)) |
||||
die("Error reading DT blob size: %s\n", strerror(errno)); |
||||
if (rc < 1) { |
||||
if (feof(f)) |
||||
die("EOF reading DT blob size\n"); |
||||
else |
||||
die("Mysterious short read reading blob size\n"); |
||||
} |
||||
|
||||
totalsize = be32_to_cpu(totalsize); |
||||
if (totalsize < BPH_V1_SIZE) |
||||
die("DT blob size (%d) is too small\n", totalsize); |
||||
|
||||
blob = xmalloc(totalsize); |
||||
|
||||
bph = (struct boot_param_header *)blob; |
||||
bph->magic = cpu_to_be32(magic); |
||||
bph->totalsize = cpu_to_be32(totalsize); |
||||
|
||||
sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); |
||||
p = blob + sizeof(magic) + sizeof(totalsize); |
||||
|
||||
while (sizeleft) { |
||||
if (feof(f)) |
||||
die("EOF before reading %d bytes of DT blob\n", |
||||
totalsize); |
||||
|
||||
rc = fread(p, 1, sizeleft, f); |
||||
if (ferror(f)) |
||||
die("Error reading DT blob: %s\n", |
||||
strerror(errno)); |
||||
|
||||
sizeleft -= rc; |
||||
p += rc; |
||||
} |
||||
|
||||
off_dt = be32_to_cpu(bph->off_dt_struct); |
||||
off_str = be32_to_cpu(bph->off_dt_strings); |
||||
version = be32_to_cpu(bph->version); |
||||
|
||||
fprintf(stderr, "\tmagic:\t\t\t0x%x\n", magic); |
||||
fprintf(stderr, "\ttotalsize:\t\t%d\n", totalsize); |
||||
fprintf(stderr, "\toff_dt_struct:\t\t0x%x\n", off_dt); |
||||
fprintf(stderr, "\toff_dt_strings:\t\t0x%x\n", off_str); |
||||
fprintf(stderr, "\toff_mem_rsvmap:\t\t0x%x\n", |
||||
be32_to_cpu(bph->off_mem_rsvmap)); |
||||
fprintf(stderr, "\tversion:\t\t0x%x\n", version ); |
||||
fprintf(stderr, "\tlast_comp_version:\t0x%x\n", |
||||
be32_to_cpu(bph->last_comp_version)); |
||||
|
||||
if (off_dt >= totalsize) |
||||
die("DT structure offset exceeds total size\n"); |
||||
|
||||
if (off_str > totalsize) |
||||
die("String table offset exceeds total size\n"); |
||||
|
||||
if (version >= 2) |
||||
fprintf(stderr, "\tboot_cpuid_phys:\t0x%x\n", |
||||
be32_to_cpu(bph->boot_cpuid_phys)); |
||||
|
||||
if (version >= 3) { |
||||
size_str = be32_to_cpu(bph->size_dt_strings); |
||||
fprintf(stderr, "\tsize_dt_strings:\t%d\n", size_str); |
||||
if (off_str+size_str > totalsize) |
||||
die("String table extends past total size\n"); |
||||
} |
||||
|
||||
if (version < 0x10) { |
||||
flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; |
||||
} |
||||
|
||||
inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); |
||||
inbuf_init(&strbuf, blob + off_str, blob + totalsize); |
||||
|
||||
if (version >= 3) |
||||
strbuf.limit = strbuf.base + size_str; |
||||
|
||||
val = flat_read_word(&dtbuf); |
||||
|
||||
if (val != OF_DT_BEGIN_NODE) |
||||
die("Device tree blob doesn't begin with OF_DT_BEGIN_NODE\n"); |
||||
|
||||
tree = unflatten_tree(&dtbuf, &strbuf, "", flags); |
||||
|
||||
val = flat_read_word(&dtbuf); |
||||
if (val != OF_DT_END) |
||||
die("Device tree blob doesn't end with OF_DT_END\n"); |
||||
|
||||
free(blob); |
||||
|
||||
return tree; |
||||
} |
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
#include <dirent.h> |
||||
#include <sys/stat.h> |
||||
|
||||
static struct node *read_fstree(char *dirname) |
||||
{ |
||||
DIR *d; |
||||
struct dirent *de; |
||||
struct stat st; |
||||
struct node *tree; |
||||
|
||||
d = opendir(dirname); |
||||
if (! d) |
||||
die("opendir(): %s\n", strerror(errno)); |
||||
|
||||
tree = build_node(NULL, NULL); |
||||
|
||||
while ((de = readdir(d)) != NULL) { |
||||
char *tmpnam; |
||||
|
||||
if (streq(de->d_name, ".") |
||||
|| streq(de->d_name, "..")) |
||||
continue; |
||||
|
||||
tmpnam = join_path(dirname, de->d_name); |
||||
|
||||
if (lstat(tmpnam, &st) < 0) |
||||
die("stat(%s): %s\n", tmpnam, strerror(errno)); |
||||
|
||||
if (S_ISREG(st.st_mode)) { |
||||
struct property *prop; |
||||
FILE *pfile; |
||||
|
||||
pfile = fopen(tmpnam, "r"); |
||||
if (! pfile) { |
||||
fprintf(stderr, |
||||
"WARNING: Cannot open %s: %s\n", |
||||
tmpnam, strerror(errno)); |
||||
} else { |
||||
prop = build_property(strdup(de->d_name), |
||||
data_copy_file(pfile, |
||||
st.st_size)); |
||||
add_property(tree, prop); |
||||
fclose(pfile); |
||||
} |
||||
} else if (S_ISDIR(st.st_mode)) { |
||||
struct node *newchild; |
||||
|
||||
newchild = read_fstree(tmpnam); |
||||
newchild = name_node(newchild, strdup(de->d_name)); |
||||
add_child(tree, newchild); |
||||
} |
||||
|
||||
free(tmpnam); |
||||
} |
||||
|
||||
return tree; |
||||
} |
||||
|
||||
struct node *dt_from_fs(char *dirname) |
||||
{ |
||||
struct node *tree; |
||||
|
||||
tree = read_fstree(dirname); |
||||
tree = name_node(tree, ""); |
||||
|
||||
fill_fullpaths(tree, ""); |
||||
|
||||
return tree; |
||||
} |
||||
|
@ -0,0 +1,590 @@
@@ -0,0 +1,590 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
/* |
||||
* Tree building functions |
||||
*/ |
||||
|
||||
struct property *build_property(char *name, struct data val) |
||||
{ |
||||
struct property *new = xmalloc(sizeof(*new)); |
||||
|
||||
new->name = name; |
||||
new->val = val; |
||||
|
||||
new->next = NULL; |
||||
|
||||
return new; |
||||
} |
||||
|
||||
struct property *build_empty_property(char *name) |
||||
{ |
||||
struct property *new = xmalloc(sizeof(*new)); |
||||
|
||||
new->name = name; |
||||
new->val.len = 0; |
||||
new->val.val = NULL; |
||||
|
||||
new->next = NULL; |
||||
|
||||
return new; |
||||
} |
||||
|
||||
struct property *chain_property(struct property *first, struct property *list) |
||||
{ |
||||
assert(first->next == NULL); |
||||
|
||||
first->next = list; |
||||
return first; |
||||
} |
||||
|
||||
struct node *build_node(struct property *proplist, struct node *children) |
||||
{ |
||||
struct node *new = xmalloc(sizeof(*new)); |
||||
struct node *child; |
||||
|
||||
memset(new, 0, sizeof(*new)); |
||||
|
||||
new->proplist = proplist; |
||||
new->children = children; |
||||
|
||||
for_each_child(new, child) { |
||||
child->parent = new; |
||||
} |
||||
|
||||
return new; |
||||
} |
||||
|
||||
struct node *name_node(struct node *node, char *name) |
||||
{ |
||||
assert(node->name == NULL); |
||||
|
||||
node->name = name; |
||||
return node; |
||||
} |
||||
|
||||
struct node *chain_node(struct node *first, struct node *list) |
||||
{ |
||||
assert(first->next_sibling == NULL); |
||||
|
||||
first->next_sibling = list; |
||||
return first; |
||||
} |
||||
|
||||
void add_property(struct node *node, struct property *prop) |
||||
{ |
||||
node->proplist = chain_property(prop, node->proplist); |
||||
} |
||||
|
||||
void add_child(struct node *parent, struct node *child) |
||||
{ |
||||
parent->children = chain_node(child, parent->children); |
||||
} |
||||
|
||||
|
||||
/* |
||||
* Tree accessor functions |
||||
*/ |
||||
|
||||
char *get_unitname(struct node *node) |
||||
{ |
||||
if (node->name[node->basenamelen] == '\0') |
||||
return ""; |
||||
else |
||||
return node->name + node->basenamelen + 1; |
||||
} |
||||
|
||||
struct property *get_property(struct node *node, char *propname) |
||||
{ |
||||
struct property *prop; |
||||
|
||||
for_each_property(node, prop) |
||||
if (streq(prop->name, propname)) |
||||
return prop; |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static cell_t propval_cell(struct property *prop) |
||||
{ |
||||
assert(prop->val.len == sizeof(cell_t)); |
||||
return be32_to_cpu(*((cell_t *)prop->val.val)); |
||||
} |
||||
|
||||
static struct node *get_subnode(struct node *node, char *nodename) |
||||
{ |
||||
struct node *child; |
||||
|
||||
for_each_child(node, child) { |
||||
if (strcmp(child->name, nodename) == 0) |
||||
return child; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static struct node *get_node_by_phandle(struct node *tree, cell_t phandle) |
||||
{ |
||||
struct node *child, *node; |
||||
|
||||
assert((phandle != 0) && (phandle != -1)); |
||||
|
||||
if (tree->phandle == phandle) |
||||
return tree; |
||||
|
||||
for_each_child(tree, child) { |
||||
node = get_node_by_phandle(child, phandle); |
||||
if (node) |
||||
return node; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
/* |
||||
* Tree checking functions |
||||
*/ |
||||
|
||||
#define ERRMSG(...) fprintf(stderr, "ERROR: " __VA_ARGS__) |
||||
#define WARNMSG(...) fprintf(stderr, "Warning: " __VA_ARGS__) |
||||
|
||||
static int must_be_one_cell(struct property *prop, struct node *node) |
||||
{ |
||||
if (prop->val.len != sizeof(cell_t)) { |
||||
ERRMSG("\"%s\" property in %s has the wrong length (should be 1 cell)\n", |
||||
prop->name, node->fullpath); |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static int must_be_cells(struct property *prop, struct node *node) |
||||
{ |
||||
if ((prop->val.len % sizeof(cell_t)) != 0) { |
||||
ERRMSG("\"%s\" property in %s is not a multiple of cell size\n", |
||||
prop->name, node->fullpath); |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static int must_be_string(struct property *prop, struct node *node) |
||||
{ |
||||
if (! data_is_one_string(prop->val)) { |
||||
ERRMSG("\"%s\" property in %s is not a string\n", |
||||
prop->name, node->fullpath); |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static int name_prop_check(struct property *prop, struct node *node) |
||||
{ |
||||
if ((prop->val.len != node->basenamelen+1) |
||||
|| (strncmp(prop->val.val, node->name, node->basenamelen) != 0)) { |
||||
ERRMSG("name property \"%s\" does not match node basename in %s\n", |
||||
prop->val.val, |
||||
node->fullpath); |
||||
return 0; |
||||
} |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
struct { |
||||
char *propname; |
||||
int (*check_fn)(struct property *prop, struct node *node); |
||||
} prop_checker_table[] = { |
||||
{"name", must_be_string}, |
||||
{"name", name_prop_check}, |
||||
{"linux,phandle", must_be_one_cell}, |
||||
{"#address-cells", must_be_one_cell}, |
||||
{"#size-cells", must_be_one_cell}, |
||||
{"reg", must_be_cells}, |
||||
{"model", must_be_string}, |
||||
{"device_type", must_be_string}, |
||||
}; |
||||
|
||||
#define DO_ERR(...) do {ERRMSG(__VA_ARGS__); ok = 0; } while (0) |
||||
|
||||
static int check_properties(struct node *node) |
||||
{ |
||||
struct property *prop, *prop2; |
||||
int ok = 1; |
||||
|
||||
for_each_property(node, prop) { |
||||
int i; |
||||
|
||||
/* check for duplicates */ |
||||
/* FIXME: do this more efficiently */ |
||||
for (prop2 = prop->next; prop2; prop2 = prop2->next) { |
||||
if (streq(prop->name, prop2->name)) { |
||||
DO_ERR("Duplicate propertyname %s in node %s\n", |
||||
prop->name, node->fullpath); |
||||
} |
||||
} |
||||
|
||||
|
||||
/* check name length */ |
||||
if (strlen(prop->name) > MAX_PROPNAME_LEN) |
||||
DO_ERR("Property name %s is too long in %s\n", |
||||
prop->name, node->fullpath); |
||||
|
||||
/* check this property */ |
||||
for (i = 0; i < ARRAY_SIZE(prop_checker_table); i++) { |
||||
if (streq(prop->name, prop_checker_table[i].propname)) |
||||
if (! prop_checker_table[i].check_fn(prop, node)) { |
||||
ok = 0; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_node_name(struct node *node) |
||||
{ |
||||
int ok = 1; |
||||
int len = strlen(node->name); |
||||
|
||||
if (len == 0 && node->parent) |
||||
DO_ERR("Empty, non-root nodename at %s\n", node->fullpath); |
||||
|
||||
if (len > MAX_NODENAME_LEN) |
||||
DO_ERR("Overlength nodename at %s\n", node->fullpath); |
||||
|
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_structure(struct node *tree) |
||||
{ |
||||
struct node *child, *child2; |
||||
int ok = 1; |
||||
|
||||
if (! check_node_name(tree)) |
||||
ok = 0; |
||||
|
||||
if (! check_properties(tree)) |
||||
ok = 0; |
||||
|
||||
for_each_child(tree, child) { |
||||
/* Check for duplicates */ |
||||
|
||||
for (child2 = child->next_sibling; |
||||
child2; |
||||
child2 = child2->next_sibling) { |
||||
if (streq(child->name, child2->name)) |
||||
DO_ERR("Duplicate node name %s\n", |
||||
child->fullpath); |
||||
} |
||||
if (! check_structure(child)) |
||||
ok = 0; |
||||
} |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
#define CHECK_HAVE(node, propname) \ |
||||
do { \ |
||||
if (! (prop = get_property((node), (propname)))) \ |
||||
DO_ERR("Missing \"%s\" property in %s\n", (propname), \ |
||||
(node)->fullpath); \ |
||||
} while (0); |
||||
|
||||
#define CHECK_HAVE_WARN(node, propname) \ |
||||
do { \ |
||||
if (! (prop = get_property((node), (propname)))) \ |
||||
WARNMSG("%s has no \"%s\" property\n", \ |
||||
(node)->fullpath, (propname)); \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_STRING(node, propname) \ |
||||
do { \ |
||||
CHECK_HAVE((node), (propname)); \ |
||||
if (prop && !data_is_one_string(prop->val)) \ |
||||
DO_ERR("\"%s\" property in %s is not a string\n", \ |
||||
(propname), (node)->fullpath); \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_STREQ(node, propname, value) \ |
||||
do { \ |
||||
CHECK_HAVE_STRING((node), (propname)); \ |
||||
if (prop && !streq(prop->val.val, (value))) \ |
||||
DO_ERR("%s has wrong %s, %s (should be %s\n", \ |
||||
(node)->fullpath, (propname), \ |
||||
prop->val.val, (value)); \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_ONECELL(node, propname) \ |
||||
do { \ |
||||
CHECK_HAVE((node), (propname)); \ |
||||
if (prop && (prop->val.len != sizeof(cell_t))) \ |
||||
DO_ERR("\"%s\" property in %s has wrong size %d (should be 1 cell)\n", (propname), (node)->fullpath, prop->val.len); \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_WARN_ONECELL(node, propname) \ |
||||
do { \ |
||||
CHECK_HAVE_WARN((node), (propname)); \ |
||||
if (prop && (prop->val.len != sizeof(cell_t))) \ |
||||
DO_ERR("\"%s\" property in %s has wrong size %d (should be 1 cell)\n", (propname), (node)->fullpath, prop->val.len); \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_WARN_PHANDLE(xnode, propname, root) \ |
||||
do { \ |
||||
struct node *ref; \ |
||||
CHECK_HAVE_WARN_ONECELL((xnode), (propname)); \ |
||||
if (prop) {\ |
||||
ref = get_node_by_phandle((root), propval_cell(prop)); \ |
||||
if (! ref) \ |
||||
DO_ERR("\"%s\" property in %s refers to non-existant phandle %x\n", (propname), (xnode)->fullpath, propval_cell(prop)); \ |
||||
} \ |
||||
} while (0) |
||||
|
||||
#define CHECK_HAVE_WARN_STRING(node, propname) \ |
||||
do { \ |
||||
CHECK_HAVE_WARN((node), (propname)); \ |
||||
if (prop && !data_is_one_string(prop->val)) \ |
||||
DO_ERR("\"%s\" property in %s is not a string\n", \ |
||||
(propname), (node)->fullpath); \ |
||||
} while (0) |
||||
|
||||
static int check_root(struct node *root) |
||||
{ |
||||
struct property *prop; |
||||
int ok = 1; |
||||
|
||||
CHECK_HAVE_STRING(root, "model"); |
||||
|
||||
CHECK_HAVE(root, "#address-cells"); |
||||
CHECK_HAVE(root, "#size-cells"); |
||||
|
||||
CHECK_HAVE_WARN(root, "compatible"); |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_cpus(struct node *root) |
||||
{ |
||||
struct node *cpus, *cpu; |
||||
struct property *prop; |
||||
struct node *bootcpu = NULL; |
||||
int ok = 1; |
||||
|
||||
cpus = get_subnode(root, "cpus"); |
||||
if (! cpus) { |
||||
ERRMSG("Missing /cpus node\n"); |
||||
return 0; |
||||
} |
||||
|
||||
CHECK_HAVE_WARN(cpus, "#address-cells"); |
||||
CHECK_HAVE_WARN(cpus, "#size-cells"); |
||||
|
||||
for_each_child(cpus, cpu) { |
||||
CHECK_HAVE_STREQ(cpu, "device_type", "cpu"); |
||||
|
||||
if (cpu->addr_cells != 1) |
||||
DO_ERR("%s has bad #address-cells value %d (should be 1)\n", |
||||
cpu->fullpath, cpu->addr_cells); |
||||
if (cpu->size_cells != 0) |
||||
DO_ERR("%s has bad #size-cells value %d (should be 0)\n", |
||||
cpu->fullpath, cpu->size_cells); |
||||
|
||||
CHECK_HAVE_ONECELL(cpu, "reg"); |
||||
if (prop) { |
||||
cell_t unitnum; |
||||
char *eptr; |
||||
|
||||
unitnum = strtol(get_unitname(cpu), &eptr, 16); |
||||
if (*eptr) |
||||
WARNMSG("%s has bad format unit name %s (should be CPU number\n", |
||||
cpu->fullpath, get_unitname(cpu)); |
||||
else if (unitnum != propval_cell(prop)) |
||||
WARNMSG("%s unit name \"%s\" does not match \"reg\" property <%x>\n", |
||||
cpu->fullpath, get_unitname(cpu), |
||||
propval_cell(prop)); |
||||
} |
||||
|
||||
/* CHECK_HAVE_ONECELL(cpu, "d-cache-line-size"); */ |
||||
/* CHECK_HAVE_ONECELL(cpu, "i-cache-line-size"); */ |
||||
CHECK_HAVE_ONECELL(cpu, "d-cache-size"); |
||||
CHECK_HAVE_ONECELL(cpu, "i-cache-size"); |
||||
|
||||
CHECK_HAVE_WARN_ONECELL(cpu, "clock-frequency"); |
||||
CHECK_HAVE_WARN_ONECELL(cpu, "timebase-frequency"); |
||||
|
||||
prop = get_property(cpu, "linux,boot-cpu"); |
||||
if (prop) { |
||||
if (prop->val.len) |
||||
WARNMSG("\"linux,boot-cpu\" property in %s is non-empty\n", |
||||
cpu->fullpath); |
||||
if (bootcpu) |
||||
DO_ERR("Multiple boot cpus (%s and %s)\n", |
||||
bootcpu->fullpath, cpu->fullpath); |
||||
else |
||||
bootcpu = cpu; |
||||
} |
||||
} |
||||
|
||||
if (! bootcpu) |
||||
WARNMSG("No cpu has \"linux,boot-cpu\" property\n"); |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_memory(struct node *root) |
||||
{ |
||||
struct node *mem; |
||||
struct property *prop; |
||||
int nnodes = 0; |
||||
int ok = 1; |
||||
|
||||
for_each_child(root, mem) { |
||||
if (strncmp(mem->name, "memory", mem->basenamelen) != 0) |
||||
continue; |
||||
|
||||
nnodes++; |
||||
|
||||
CHECK_HAVE_STREQ(mem, "device_type", "memory"); |
||||
CHECK_HAVE(mem, "reg"); |
||||
} |
||||
|
||||
if (nnodes == 0) { |
||||
ERRMSG("No memory nodes\n"); |
||||
return 0; |
||||
} |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_chosen(struct node *root) |
||||
{ |
||||
struct node *chosen; |
||||
struct property *prop; |
||||
int ok = 1; |
||||
|
||||
chosen = get_subnode(root, "chosen"); |
||||
if (! chosen) { |
||||
ERRMSG("Missing /chosen node\n"); |
||||
return 0; |
||||
} |
||||
|
||||
CHECK_HAVE_ONECELL(chosen, "linux,platform"); |
||||
|
||||
CHECK_HAVE_WARN_STRING(chosen, "bootargs"); |
||||
CHECK_HAVE_WARN_STRING(chosen, "linux,stdout-path"); |
||||
CHECK_HAVE_WARN_PHANDLE(chosen, "interrupt-controller", root); |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_addr_size_reg(struct node *node, |
||||
int p_addr_cells, int p_size_cells) |
||||
{ |
||||
int addr_cells = p_addr_cells; |
||||
int size_cells = p_size_cells; |
||||
struct property *prop; |
||||
struct node *child; |
||||
int ok = 1; |
||||
|
||||
node->addr_cells = addr_cells; |
||||
node->size_cells = size_cells; |
||||
|
||||
prop = get_property(node, "reg"); |
||||
if (prop) { |
||||
int len = prop->val.len / 4; |
||||
|
||||
if ((len % (addr_cells+size_cells)) != 0) |
||||
DO_ERR("\"reg\" property in %s has invalid length (%d) for given #address-cells (%d) and #size-cells (%d)\n", |
||||
node->fullpath, prop->val.len, |
||||
addr_cells, size_cells); |
||||
} |
||||
|
||||
prop = get_property(node, "#address-cells"); |
||||
if (prop) |
||||
addr_cells = propval_cell(prop); |
||||
|
||||
prop = get_property(node, "#size-cells"); |
||||
if (prop) |
||||
size_cells = propval_cell(prop); |
||||
|
||||
for_each_child(node, child) { |
||||
ok = ok && check_addr_size_reg(child, addr_cells, size_cells); |
||||
} |
||||
|
||||
return ok; |
||||
} |
||||
|
||||
static int check_phandles(struct node *root, struct node *node) |
||||
{ |
||||
struct property *prop; |
||||
struct node *child, *other; |
||||
cell_t phandle; |
||||
int ok = 1; |
||||
|
||||
prop = get_property(node, "linux,phandle"); |
||||
if (prop) { |
||||
phandle = propval_cell(prop); |
||||
if ((phandle == 0) || (phandle == -1)) { |
||||
DO_ERR("%s has invalid linux,phandle %x\n", |
||||
node->fullpath, phandle); |
||||
} else { |
||||
other = get_node_by_phandle(root, phandle); |
||||
if (other) |
||||
DO_ERR("%s has duplicated phandle %x (seen before at %s)\n", |
||||
node->fullpath, phandle, other->fullpath); |
||||
|
||||
node->phandle = phandle; |
||||
} |
||||
} |
||||
|
||||
for_each_child(node, child) |
||||
ok = ok && check_phandles(root, child); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
int check_device_tree(struct node *dt) |
||||
{ |
||||
int ok = 1; |
||||
|
||||
if (! check_structure(dt)) |
||||
return 0; |
||||
|
||||
ok = ok && check_addr_size_reg(dt, -1, -1); |
||||
ok = ok && check_phandles(dt, dt); |
||||
|
||||
if (! ok) |
||||
return 0; |
||||
|
||||
ok = ok && check_root(dt); |
||||
ok = ok && check_cpus(dt); |
||||
ok = ok && check_memory(dt); |
||||
ok = ok && check_chosen(dt); |
||||
if (! ok) |
||||
return 0; |
||||
|
||||
return 1; |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
/ { |
||||
model = "MyBoardName"; |
||||
compatible = "MyBoardFamilyName"; |
||||
#address-cells = <2>; |
||||
#size-cells = <2>; |
||||
|
||||
cpus { |
||||
linux,phandle = <1>; |
||||
#address-cells = <1>; |
||||
#size-cells = <0>; |
||||
PowerPC,970@0 { |
||||
name = "PowerPC,970"; |
||||
device_type = "cpu"; |
||||
reg = <0>; |
||||
clock-frequency = <5f5e1000>; |
||||
linux,boot-cpu; |
||||
linux,phandle = <2>; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
randomnode { |
||||
string = "\xff\0stuffstuff\t\t\t\n\n\n\n"; |
||||
blob = [0a 0b 0c 0d de ea ad be ef]; |
||||
}; |
||||
|
||||
memory@0 { |
||||
device_type = "memory"; |
||||
reg = <00000000 00000000 00000000 20000000>; |
||||
linux,phandle = <3>; |
||||
}; |
||||
|
||||
chosen { |
||||
bootargs = "root=/dev/sda2"; |
||||
linux,platform = <00000600>; |
||||
linux,phandle = <4>; |
||||
}; |
||||
|
||||
}; |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
DTC = ../dtc |
||||
VG_DTC = valgrind --tool=memcheck ../dtc |
||||
|
||||
check: all |
||||
./run_all_tests.sh |
||||
|
||||
all: |
||||
|
||||
clean: |
||||
rm -f *~ |
||||
|
||||
|
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/* |
||||
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
||||
* |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
||||
* USA |
||||
*/ |
||||
|
||||
#include "dtc.h" |
||||
|
||||
struct node *device_tree; |
||||
|
||||
extern FILE *yyin; |
||||
extern int yyparse(void); |
||||
|
||||
struct node *dt_from_source(FILE *f) |
||||
{ |
||||
yyin = f; |
||||
if (yyparse() != 0) |
||||
return NULL; |
||||
|
||||
fill_fullpaths(device_tree, ""); |
||||
|
||||
return device_tree; |
||||
} |
||||
|
||||
static void write_prefix(FILE *f, int level) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < level; i++) |
||||
fputc('\t', f); |
||||
} |
||||
|
||||
enum proptype { |
||||
PROP_EMPTY, |
||||
PROP_STRING, |
||||
PROP_CELLS, |
||||
PROP_BYTES, |
||||
}; |
||||
|
||||
static enum proptype guess_type(struct property *prop) |
||||
{ |
||||
int len = prop->val.len; |
||||
char *p = prop->val.val; |
||||
int nnoprint = 0; |
||||
int i; |
||||
|
||||
if (len == 0) |
||||
return PROP_EMPTY; |
||||
|
||||
for (i = 0; i < len; i++) { |
||||
if (! isprint(p[i])) |
||||
nnoprint++; |
||||
} |
||||
|
||||
if ((nnoprint == 1) && (p[len-1] == '\0')) |
||||
return PROP_STRING; |
||||
else if ((len % sizeof(cell_t)) == 0) |
||||
return PROP_CELLS; |
||||
else |
||||
return PROP_BYTES; |
||||
|
||||
} |
||||
|
||||
void write_tree_source(FILE *f, struct node *tree, int level) |
||||
{ |
||||
struct property *prop; |
||||
struct node *child; |
||||
|
||||
write_prefix(f, level); |
||||
if (tree->name && (*tree->name)) |
||||
fprintf(f, "%s {\n", tree->name); |
||||
else |
||||
fprintf(f, "/ {\n"); |
||||
|
||||
for_each_property(tree, prop) { |
||||
cell_t *cp; |
||||
char *bp; |
||||
void *propend; |
||||
enum proptype type; |
||||
|
||||
write_prefix(f, level); |
||||
fprintf(f, "\t%s", prop->name); |
||||
type = guess_type(prop); |
||||
propend = prop->val.val + prop->val.len; |
||||
|
||||
switch (type) { |
||||
case PROP_EMPTY: |
||||
fprintf(f, ";\n"); |
||||
break; |
||||
|
||||
case PROP_STRING: |
||||
fprintf(f, " = \"%s\";\n", (char *)prop->val.val); |
||||
break; |
||||
|
||||
case PROP_CELLS: |
||||
fprintf(f, " = <"); |
||||
cp = (cell_t *)prop->val.val; |
||||
for (;;) { |
||||
fprintf(f, "%x", be32_to_cpu(*cp++)); |
||||
if ((void *)cp >= propend) |
||||
break; |
||||
fprintf(f, " "); |
||||
} |
||||
fprintf(f, ">;\n"); |
||||
break; |
||||
|
||||
case PROP_BYTES: |
||||
fprintf(f, " = ["); |
||||
bp = prop->val.val; |
||||
for (;;) { |
||||
fprintf(f, "%02hhx", *bp++); |
||||
if ((void *)bp >= propend) |
||||
break; |
||||
fprintf(f, " "); |
||||
} |
||||
fprintf(f, "];\n"); |
||||
break; |
||||
} |
||||
} |
||||
for_each_child(tree, child) { |
||||
fprintf(f, "\n"); |
||||
write_tree_source(f, child, level+1); |
||||
} |
||||
write_prefix(f, level); |
||||
fprintf(f, "};\n"); |
||||
} |
Loading…
Reference in new issue