From 61c5cdfd8e611d3a34497394098555b0b8644a31 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jan 2013 21:16:21 +0200 Subject: [PATCH] dtc: Dynamic symbols & fixup support Enable the generation of symbol & fixup information for usage with dynamic DT loading. Passing the -@ option generates a __symbols__ node at the root node of the resulting blob for any node labels used. When using the /plugin/ tag all unresolved label references be tracked in the __fixups__ node, while all local phandle references will the tracked in the __local_fixups__ node. This is sufficient to implement a dynamic DT object loader. Signed-off-by: Pantelis Antoniou Signed-off-by: Stefan Agner --- Documentation/dts-format.txt | 7 +++ Documentation/manual.txt | 8 +++ checks.c | 120 +++++++++++++++++++++++++++++++++++-- dtc-lexer.l | 5 ++ dtc-parser.y | 23 ++++++- dtc.c | 9 ++- dtc.h | 38 ++++++++++++ flattree.c | 139 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 9 deletions(-) diff --git a/Documentation/dts-format.txt b/Documentation/dts-format.txt index 41741df..4da515c 100644 --- a/Documentation/dts-format.txt +++ b/Documentation/dts-format.txt @@ -115,7 +115,14 @@ Version 1 DTS files have the overall layout: * C style (/* ... */) and C++ style (// ...) comments are supported. +Device Tree Objects +------------------- +Using the plugin tag enables dynamic tree objects. + + /plugin/; + +For the full details please see Documentation/dt-object-internal.txt -- David Gibson -- Yoder Stuart diff --git a/Documentation/manual.txt b/Documentation/manual.txt index 65c8540..d313715 100644 --- a/Documentation/manual.txt +++ b/Documentation/manual.txt @@ -133,6 +133,14 @@ Options: By default the most recent version is generated. Relevant for dtb and asm output only. + -@ + Dynamic resolution mode. For non /plugin/ compilations generate + a __symbols__ node containing a list of all nodes with a label. + When /plugin/ is used, unresolved references are recorded in + a __fixups__ node, while local phandle references are recorded + in a __local_fixups__ node. + See Documentation/dt-object-internal.txt + The defines what version of the "blob" format will be generated. Supported versions are 1, 2, 3, 16 and 17. The default is diff --git a/checks.c b/checks.c index ee96a25..970c0a3 100644 --- a/checks.c +++ b/checks.c @@ -457,22 +457,93 @@ static void fixup_phandle_references(struct check *c, struct node *dt, struct node *node, struct property *prop) { struct marker *m = prop->val.markers; + struct fixup *f, **fp; + struct fixup_entry *fe, **fep; struct node *refnode; cell_t phandle; + int has_phandle_refs; + + has_phandle_refs = 0; + for_each_marker_of_type(m, REF_PHANDLE) { + has_phandle_refs = 1; + break; + } + + if (!has_phandle_refs) + return; for_each_marker_of_type(m, REF_PHANDLE) { assert(m->offset + sizeof(cell_t) <= prop->val.len); refnode = get_node_by_ref(dt, m->ref); - if (! refnode) { + if (!refnode && !symbol_fixup_support) { FAIL(c, "Reference to non-existent node or label \"%s\"\n", - m->ref); + m->ref); continue; } - phandle = get_node_phandle(dt, refnode); - *((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); + if (!refnode) { + /* allocate fixup entry */ + fe = xmalloc(sizeof(*fe)); + + fe->node = node; + fe->prop = prop; + fe->offset = m->offset; + fe->next = NULL; + + /* search for an already existing fixup */ + for_each_fixup(dt, f) + if (strcmp(f->ref, m->ref) == 0) + break; + + /* no fixup found, add new */ + if (f == NULL) { + f = xmalloc(sizeof(*f)); + f->ref = m->ref; + f->entries = NULL; + f->next = NULL; + + /* add it to the tree */ + fp = &dt->fixups; + while (*fp) + fp = &(*fp)->next; + *fp = f; + } + + /* and now append fixup entry */ + fep = &f->entries; + while (*fep) + fep = &(*fep)->next; + *fep = fe; + + /* mark the entry as unresolved */ + phandle = 0xdeadbeef; + } else { + phandle = get_node_phandle(dt, refnode); + + /* if it's a plugin, we need to record it */ + if (symbol_fixup_support && dt->is_plugin) { + + /* allocate a new local fixup entry */ + fe = xmalloc(sizeof(*fe)); + + fe->node = node; + fe->prop = prop; + fe->offset = m->offset; + fe->next = NULL; + + /* append it to the local fixups */ + fep = &dt->local_fixups; + while (*fep) + fep = &(*fep)->next; + *fep = fe; + } + } + + *((cell_t *)(prop->val.val + m->offset)) = + cpu_to_fdt32(phandle); } + } ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL, &duplicate_node_names, &explicit_phandles); @@ -651,6 +722,45 @@ static void check_obsolete_chosen_interrupt_controller(struct check *c, } TREE_WARNING(obsolete_chosen_interrupt_controller, NULL); +static void check_auto_label_phandles(struct check *c, struct node *dt, + struct node *node) +{ + struct label *l; + struct symbol *s, **sp; + int has_label; + + if (!symbol_fixup_support) + return; + + has_label = 0; + for_each_label(node->labels, l) { + has_label = 1; + break; + } + + if (!has_label) + return; + + /* force allocation of a phandle for this node */ + (void)get_node_phandle(dt, node); + + /* add the symbol */ + for_each_label(node->labels, l) { + + s = xmalloc(sizeof(*s)); + s->label = l; + s->node = node; + s->next = NULL; + + /* add it to the symbols list */ + sp = &dt->symbols; + while (*sp) + sp = &((*sp)->next); + *sp = s; + } +} +NODE_WARNING(auto_label_phandles, NULL); + static struct check *check_table[] = { &duplicate_node_names, &duplicate_property_names, &node_name_chars, &node_name_format, &property_name_chars, @@ -669,6 +779,8 @@ static struct check *check_table[] = { &avoid_default_addr_size, &obsolete_chosen_interrupt_controller, + &auto_label_phandles, + &always_fail, }; diff --git a/dtc-lexer.l b/dtc-lexer.l index 3b41bfc..78d5132 100644 --- a/dtc-lexer.l +++ b/dtc-lexer.l @@ -112,6 +112,11 @@ static int pop_input_file(void); return DT_V1; } +<*>"/plugin/" { + DPRINT("Keyword: /plugin/\n"); + return DT_PLUGIN; + } + <*>"/memreserve/" { DPRINT("Keyword: /memreserve/\n"); BEGIN_DEFAULT(); diff --git a/dtc-parser.y b/dtc-parser.y index f412460..e444acf 100644 --- a/dtc-parser.y +++ b/dtc-parser.y @@ -20,6 +20,7 @@ %{ #include +#include #include "dtc.h" #include "srcpos.h" @@ -56,9 +57,11 @@ static unsigned char eval_char_literal(const char *s); struct node *nodelist; struct reserve_info *re; uint64_t integer; + int is_plugin; } %token DT_V1 +%token DT_PLUGIN %token DT_MEMRESERVE %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR %token DT_BITS @@ -76,6 +79,7 @@ static unsigned char eval_char_literal(const char *s); %type propdata %type propdataprefix +%type plugindecl %type memreserve %type memreserves %type arrayprefix @@ -106,10 +110,23 @@ static unsigned char eval_char_literal(const char *s); %% sourcefile: - DT_V1 ';' memreserves devicetree + DT_V1 ';' plugindecl memreserves devicetree { - the_boot_info = build_boot_info($3, $4, - guess_boot_cpuid($4)); + $5->is_plugin = $3; + $5->is_root = 1; + the_boot_info = build_boot_info($4, $5, + guess_boot_cpuid($5)); + } + ; + +plugindecl: + /* empty */ + { + $$ = 0; + } + | DT_PLUGIN ';' + { + $$ = 1; } ; diff --git a/dtc.c b/dtc.c index e3c9653..d2f9647 100644 --- a/dtc.c +++ b/dtc.c @@ -29,6 +29,7 @@ int reservenum; /* Number of memory reservation slots */ int minsize; /* Minimum blob size */ int padsize; /* Additional padding to blob */ int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ +int symbol_fixup_support = 0; static void fill_fullpaths(struct node *tree, const char *prefix) { @@ -49,7 +50,7 @@ static void fill_fullpaths(struct node *tree, const char *prefix) /* Usage related data. */ static const char usage_synopsis[] = "dtc [options] "; -static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv"; +static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv:@"; static struct option const usage_long_opts[] = { {"quiet", no_argument, NULL, 'q'}, {"in-format", a_argument, NULL, 'I'}, @@ -67,6 +68,7 @@ static struct option const usage_long_opts[] = { {"phandle", a_argument, NULL, 'H'}, {"warning", a_argument, NULL, 'W'}, {"error", a_argument, NULL, 'E'}, + {"symbols", a_argument, NULL, '@'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {NULL, no_argument, NULL, 0x0}, @@ -97,6 +99,7 @@ static const char * const usage_opts_help[] = { "\t\tboth - Both \"linux,phandle\" and \"phandle\" properties", "\n\tEnable/disable warnings (prefix with \"no-\")", "\n\tEnable/disable errors (prefix with \"no-\")", + "\n\tSymbols and Fixups support", "\n\tPrint this help and exit", "\n\tPrint version and exit", NULL, @@ -184,7 +187,9 @@ int main(int argc, char *argv[]) case 'E': parse_checks_option(false, true, optarg); break; - + case '@': + symbol_fixup_support = 1; + break; case 'h': usage(NULL); default: diff --git a/dtc.h b/dtc.h index 264a20c..8c9059b 100644 --- a/dtc.h +++ b/dtc.h @@ -54,6 +54,7 @@ extern int reservenum; /* Number of memory reservation slots */ extern int minsize; /* Minimum blob size */ extern int padsize; /* Additional padding to blob */ extern int phandle_format; /* Use linux,phandle or phandle properties */ +extern int symbol_fixup_support;/* enable symbols & fixup support */ #define PHANDLE_LEGACY 0x1 #define PHANDLE_EPAPR 0x2 @@ -132,6 +133,25 @@ struct label { struct label *next; }; +struct fixup_entry { + int offset; + struct node *node; + struct property *prop; + struct fixup_entry *next; +}; + +struct fixup { + char *ref; + struct fixup_entry *entries; + struct fixup *next; +}; + +struct symbol { + struct label *label; + struct node *node; + struct symbol *next; +}; + struct property { int deleted; char *name; @@ -158,6 +178,12 @@ struct node { int addr_cells, size_cells; struct label *labels; + + int is_root; + int is_plugin; + struct fixup *fixups; + struct symbol *symbols; + struct fixup_entry *local_fixups; }; #define for_each_label_withdel(l0, l) \ @@ -181,6 +207,18 @@ struct node { for_each_child_withdel(n, c) \ if (!(c)->deleted) +#define for_each_fixup(n, f) \ + for ((f) = (n)->fixups; (f); (f) = (f)->next) + +#define for_each_fixup_entry(f, fe) \ + for ((fe) = (f)->entries; (fe); (fe) = (fe)->next) + +#define for_each_symbol(n, s) \ + for ((s) = (n)->symbols; (s); (s) = (s)->next) + +#define for_each_local_fixup_entry(n, fe) \ + for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next) + void add_label(struct label **labels, char *label); void delete_labels(struct label **labels); diff --git a/flattree.c b/flattree.c index 665dad7..6237715 100644 --- a/flattree.c +++ b/flattree.c @@ -262,6 +262,12 @@ static void flatten_tree(struct node *tree, struct emitter *emit, struct property *prop; struct node *child; int seen_name_prop = 0; + struct symbol *sym; + struct fixup *f; + struct fixup_entry *fe; + char *name, *s; + const char *fullpath; + int namesz, nameoff, vallen; if (tree->deleted) return; @@ -310,6 +316,139 @@ static void flatten_tree(struct node *tree, struct emitter *emit, flatten_tree(child, emit, etarget, strbuf, vi); } + if (!symbol_fixup_support) + goto no_symbols; + + /* add the symbol nodes (if any) */ + if (tree->symbols) { + + emit->beginnode(etarget, NULL); + emit->string(etarget, "__symbols__", 0); + emit->align(etarget, sizeof(cell_t)); + + for_each_symbol(tree, sym) { + + vallen = strlen(sym->node->fullpath); + + nameoff = stringtable_insert(strbuf, sym->label->label); + + emit->property(etarget, NULL); + emit->cell(etarget, vallen + 1); + emit->cell(etarget, nameoff); + + if ((vi->flags & FTF_VARALIGN) && vallen >= 8) + emit->align(etarget, 8); + + emit->string(etarget, sym->node->fullpath, + strlen(sym->node->fullpath)); + emit->align(etarget, sizeof(cell_t)); + } + + emit->endnode(etarget, NULL); + } + + /* add the fixup nodes */ + if (tree->fixups) { + + /* emit the external fixups */ + emit->beginnode(etarget, NULL); + emit->string(etarget, "__fixups__", 0); + emit->align(etarget, sizeof(cell_t)); + + for_each_fixup(tree, f) { + + namesz = 0; + for_each_fixup_entry(f, fe) { + fullpath = fe->node->fullpath; + if (fullpath[0] == '\0') + fullpath = "/"; + namesz += strlen(fullpath) + 1; + namesz += strlen(fe->prop->name) + 1; + namesz += 32; /* space for : + '\0' */ + } + + name = xmalloc(namesz); + + s = name; + for_each_fixup_entry(f, fe) { + fullpath = fe->node->fullpath; + if (fullpath[0] == '\0') + fullpath = "/"; + snprintf(s, name + namesz - s, "%s:%s:%d", + fullpath, + fe->prop->name, fe->offset); + s += strlen(s) + 1; + } + + nameoff = stringtable_insert(strbuf, f->ref); + vallen = s - name - 1; + + emit->property(etarget, NULL); + emit->cell(etarget, vallen + 1); + emit->cell(etarget, nameoff); + + if ((vi->flags & FTF_VARALIGN) && vallen >= 8) + emit->align(etarget, 8); + + emit->string(etarget, name, vallen); + emit->align(etarget, sizeof(cell_t)); + + free(name); + } + + emit->endnode(etarget, tree->labels); + } + + /* add the local fixup property */ + if (tree->local_fixups) { + + /* emit the external fixups */ + emit->beginnode(etarget, NULL); + emit->string(etarget, "__local_fixups__", 0); + emit->align(etarget, sizeof(cell_t)); + + namesz = 0; + for_each_local_fixup_entry(tree, fe) { + fullpath = fe->node->fullpath; + if (fullpath[0] == '\0') + fullpath = "/"; + namesz += strlen(fullpath) + 1; + namesz += strlen(fe->prop->name) + 1; + namesz += 32; /* space for : + '\0' */ + } + + name = xmalloc(namesz); + + s = name; + for_each_local_fixup_entry(tree, fe) { + fullpath = fe->node->fullpath; + if (fullpath[0] == '\0') + fullpath = "/"; + snprintf(s, name + namesz - s, "%s:%s:%d", + fullpath, fe->prop->name, + fe->offset); + s += strlen(s) + 1; + } + + nameoff = stringtable_insert(strbuf, "fixup"); + vallen = s - name - 1; + + emit->property(etarget, NULL); + emit->cell(etarget, vallen + 1); + emit->cell(etarget, nameoff); + + if ((vi->flags & FTF_VARALIGN) && vallen >= 8) + emit->align(etarget, 8); + + emit->string(etarget, name, vallen); + emit->align(etarget, sizeof(cell_t)); + + free(name); + + emit->endnode(etarget, tree->labels); + } + +no_symbols: emit->endnode(etarget, tree->labels); } -- 1.8.3.1