diff --git a/contrib/dtc/Documentation/dts-format.txt b/contrib/dtc/Documentation/dts-format.txt index 41741df..4da515c 100644 --- a/contrib/dtc/Documentation/dts-format.txt +++ b/contrib/dtc/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/contrib/dtc/Documentation/manual.txt b/contrib/dtc/Documentation/manual.txt index 65c8540..d313715 100644 --- a/contrib/dtc/Documentation/manual.txt +++ b/contrib/dtc/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/contrib/dtc/checks.c b/contrib/dtc/checks.c index f704694..4402096 100644 --- a/contrib/dtc/checks.c +++ b/contrib/dtc/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/contrib/dtc/dtc-lexer.l b/contrib/dtc/dtc-lexer.l index 1f21dbe..b19c0fd 100644 --- a/contrib/dtc/dtc-lexer.l +++ b/contrib/dtc/dtc-lexer.l @@ -118,6 +118,11 @@ static void lexical_error(const char *fmt, ...); return DT_V1; } +<*>"/plugin/" { + DPRINT("Keyword: /plugin/\n"); + return DT_PLUGIN; + } + <*>"/memreserve/" { DPRINT("Keyword: /memreserve/\n"); BEGIN_DEFAULT(); diff --git a/contrib/dtc/dtc-parser.y b/contrib/dtc/dtc-parser.y index bed857e..0eca8c7 100644 --- a/contrib/dtc/dtc-parser.y +++ b/contrib/dtc/dtc-parser.y @@ -20,6 +20,7 @@ %{ #include +#include #include "dtc.h" #include "srcpos.h" @@ -52,9 +53,11 @@ extern bool treesource_error; 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 @@ -72,6 +75,7 @@ extern bool treesource_error; %type propdata %type propdataprefix +%type plugindecl %type memreserve %type memreserves %type arrayprefix @@ -102,10 +106,23 @@ extern bool treesource_error; %% 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/contrib/dtc/dtc.c b/contrib/dtc/dtc.c index d36ccdc..15d8d99 100644 --- a/contrib/dtc/dtc.c +++ b/contrib/dtc/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/contrib/dtc/dtc.h b/contrib/dtc/dtc.h index 20e4d56..5258024 100644 --- a/contrib/dtc/dtc.h +++ b/contrib/dtc/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 { bool 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/contrib/dtc/flattree.c b/contrib/dtc/flattree.c index bd99fa2..7f3df74 100644 --- a/contrib/dtc/flattree.c +++ b/contrib/dtc/flattree.c @@ -262,6 +262,12 @@ static void flatten_tree(struct node *tree, struct emitter *emit, struct property *prop; struct node *child; bool seen_name_prop = false; + 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); } diff --git a/sys/boot/fdt/Makefile b/sys/boot/fdt/Makefile index e7b939d..8434975 100644 --- a/sys/boot/fdt/Makefile +++ b/sys/boot/fdt/Makefile @@ -10,7 +10,7 @@ SRCS+= fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c \ fdt_empty_tree.c # Loader's fdt commands extension sources. -SRCS+= fdt_loader_cmd.c +SRCS+= fdt_loader_cmd.c fdt_overlay.c CFLAGS+= -I${.CURDIR}/../../contrib/libfdt/ -I${.CURDIR}/../common/ diff --git a/sys/boot/fdt/fdt_loader_cmd.c b/sys/boot/fdt/fdt_loader_cmd.c index 0ce8f15..f58f311 100644 --- a/sys/boot/fdt/fdt_loader_cmd.c +++ b/sys/boot/fdt/fdt_loader_cmd.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include "bootstrap.h" #include "fdt_platform.h" +#include "fdt_overlay.h" #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ @@ -276,6 +277,128 @@ fdt_load_dtb_file(const char * filename) return (0); } +static int +fdt_load_dtb_overlay(const char * filename) +{ + struct preloaded_file *bfp, *oldbfp; + struct fdt_header header; + int err; + + debugf("fdt_load_dtb_overlay(%s)\n", filename); + + oldbfp = file_findfile(filename, "dtbo"); + + /* Attempt to load and validate a new dtb from a file. */ + if ((bfp = file_loadraw(filename, "dtbo", 1)) == NULL) { + printf("failed to load file '%s'\n", filename); + return (1); + } + + COPYOUT(bfp->f_addr, &header, sizeof(header)); + err = fdt_check_header(&header); + + if (err < 0) { + file_discard(bfp); + if (err == -FDT_ERR_BADVERSION) + printf("incompatible blob version: %d, should be: %d\n", + fdt_version(fdtp), FDT_LAST_SUPPORTED_VERSION); + + else + printf("error validating blob: %s\n", + fdt_strerror(err)); + return (1); + } + + /* A new dtb was validated, discard any previous file. */ + if (oldbfp) + file_discard(oldbfp); + + return (0); +} + +int +fdt_load_dtb_overlays(const char * filenames) +{ + char *names; + char *name; + char *comaptr; + + debugf("fdt_load_dtb_overlay(%s)\n", filenames); + + names = strdup(filenames); + if (names == NULL) + return (1); + name = names; + do { + comaptr = strchr(name, ','); + if (comaptr) + *comaptr = '\0'; + fdt_load_dtb_overlay(name); + name = comaptr + 1; + } while(comaptr); + + free(names); + return (0); +} + +void +fdt_apply_overlays() +{ + struct preloaded_file *fp; + size_t overlays_size, max_overlay_size, new_fdtp_size; + void *new_fdtp; + void *overlay; + int rv; + + if ((fdtp == NULL) || (fdtp_size == 0)) + return; + + overlays_size = 0; + max_overlay_size = 0; + for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) { + if (max_overlay_size < fp->f_size) + max_overlay_size = fp->f_size; + overlays_size += fp->f_size; + } + + /* Nothing to apply */ + if (overlays_size == 0) + return; + + /* It's actually more than enough */ + new_fdtp_size = fdtp_size + overlays_size; + new_fdtp = malloc(new_fdtp_size); + if (new_fdtp == NULL) { + printf("failed to allocate memory for DTB blob with overlays\n"); + return; + } + + overlay = malloc(max_overlay_size); + if (overlay == NULL) { + printf("failed to allocate memory for DTB blob with overlays\n"); + free(new_fdtp); + return; + } + + rv = fdt_open_into(fdtp, new_fdtp, new_fdtp_size); + if (rv != 0) { + printf("failed to open DTB blob for applying overlays\n"); + return; + } + + for (fp = file_findfile(NULL, "dtbo"); fp != NULL; fp = fp->f_next) { + printf("applying DTB overlay '%s'\n", fp->f_name); + COPYOUT(fp->f_addr, overlay, fp->f_size); + fdt_overlay_apply(new_fdtp, overlay, fp->f_size); + } + + free(fdtp); + fdtp = new_fdtp; + fdtp_size = new_fdtp_size; + + free(overlay); +} + int fdt_setup_fdtp() { diff --git a/sys/boot/fdt/fdt_overlay.c b/sys/boot/fdt/fdt_overlay.c new file mode 100644 index 0000000..f4c8083 --- /dev/null +++ b/sys/boot/fdt/fdt_overlay.c @@ -0,0 +1,441 @@ +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "fdt_overlay.h" + +/* + * Get max phandle + */ +static uint32_t +fdt_max_phandle(void *fdtp) +{ + int o, depth; + uint32_t max_phandle, phandle; + + depth = 1; + o = fdt_path_offset(fdtp, "/"); + max_phandle = fdt_get_phandle(fdtp, o); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) { + phandle = fdt_get_phandle(fdtp, o); + if (max_phandle < phandle) + max_phandle = phandle; + } + + return max_phandle; +} + +/* + * Returns exact memory location specified by fixup in format + * /path/to/node:property:offset + */ +static void * +fdt_get_fixup_location(void *fdtp, const char *fixup) +{ + char *path, *prop, *offsetp, *endp; + int prop_offset, o, proplen; + void *result; + + result = 0; + + path = strdup(fixup); + prop = strchr(path, ':'); + if (prop == NULL) { + printf("missing property part in \"%s\"\n", fixup); + result = NULL; + goto out; + } + + *prop = 0; + prop++; + + offsetp = strchr(prop, ':'); + if (offsetp == NULL) { + printf("missing offset part in \"%s\"\n", fixup); + result = NULL; + goto out; + } + + *offsetp = 0; + offsetp++; + + prop_offset = strtoul(offsetp, &endp, 10); + if (*endp != '\0') { + printf("\"%s\" is not valid number\n", offsetp); + result = NULL; + goto out; + } + + o = fdt_path_offset(fdtp, path); + if (o < 0) { + printf("path \"%s\" not found\n", path); + result = NULL; + goto out; + } + + result = fdt_getprop_w(fdtp, o, prop, &proplen); + if (result == NULL){ + printf("property \"%s\" not found in \"%s\" node\n", prop, path); + result = NULL; + goto out; + } + + if (proplen < prop_offset + sizeof(uint32_t)) { + printf("%s: property length is too small for fixup\n", fixup); + result = NULL; + goto out; + } + + result = (char*)result + prop_offset; + +out: + free(path); + return (result); +} + +/* + * Process one entry in __fixups__ { } node + * @fixups is property value, array of NUL-terminated strings + * with fixup locations + * @fixups_len length of the fixups array in bytes + * @phandle is value for these locations + */ +static int +fdt_do_one_fixup(void *fdtp, const char *fixups, int fixups_len, int phandle) +{ + void *fixup_pos; + uint32_t val; + + val = cpu_to_fdt32(phandle); + + while (fixups_len > 0) { + fixup_pos = fdt_get_fixup_location(fdtp, fixups); + if (fixup_pos != NULL) + memcpy(fixup_pos, &val, sizeof(val)); + + fixups_len -= strlen(fixups) + 1; + fixups += strlen(fixups) + 1; + } + + return (0); +} + +/* + * Increase u32 value at pos by offset + */ +static void +fdt_increase_u32(void *pos, uint32_t offset) +{ + uint32_t val; + + memcpy(&val, pos, sizeof(val)); + val = cpu_to_fdt32(fdt32_to_cpu(val) + offset); + memcpy(pos, &val, sizeof(val)); +} + +/* + * Process local fixups + * @fixups is property value, array of NUL-terminated strings + * with fixup locations + * @fixups_len length of the fixups array in bytes + * @offset value these locations should be increased by + */ +static int +fdt_do_local_fixup(void *fdtp, const char *fixups, int fixups_len, int offset) +{ + void *fixup_pos; + + while (fixups_len > 0) { + fixup_pos = fdt_get_fixup_location(fdtp, fixups); + if (fixup_pos != NULL) + fdt_increase_u32(fixup_pos, offset); + + fixups_len -= strlen(fixups) + 1; + fixups += strlen(fixups) + 1; + } + + return (0); +} + +/* + * Increase node phandle by phandle_offset + */ +static void +fdt_increase_phandle(void *fdtp, int node_offset, uint32_t phandle_offset) +{ + int proplen; + void *phandle_pos, *node_pos; + + node_pos = (char*)fdtp + node_offset; + + phandle_pos = fdt_getprop_w(fdtp, node_offset, "phandle", &proplen); + if (phandle_pos) + fdt_increase_u32(phandle_pos, phandle_offset); + phandle_pos = fdt_getprop_w(fdtp, node_offset, "linux,phandle", &proplen); + if (phandle_pos) + fdt_increase_u32(phandle_pos, phandle_offset); +} + +/* + * Increase all phandles by offset + */ +static void +fdt_increase_phandles(void *fdtp, uint32_t offset) +{ + int o, depth; + + o = fdt_path_offset(fdtp, "/"); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) { + fdt_increase_phandle(fdtp, o, offset); + } +} + +/* + * Overlay one node deviced by over + */ +static void +fdt_overlay_node(void *main_fdtp, int target_o, void *overlay_fdtp, int overlay_o) +{ + int len, o, depth; + const char *name; + const void *val; + int target_subnode_o; + + /* Overlay properties */ + for (o = fdt_first_property_offset(overlay_fdtp, overlay_o); + o >= 0; o = fdt_next_property_offset(overlay_fdtp, o)) { + val = fdt_getprop_by_offset(overlay_fdtp, o, &name, &len); + if (val) + fdt_setprop(main_fdtp, target_o, name, val, len); + } + + /* Now overlay nodes */ + o = overlay_o; + for (depth = 0; (o >= 0) && (depth >= 0); + o = fdt_next_node(overlay_fdtp, o, &depth)) { + if (depth != 1) + continue; + /* Check if there is node with the same name */ + name = fdt_get_name(overlay_fdtp, o, NULL); + target_subnode_o = fdt_subnode_offset(main_fdtp, target_o, name); + if (target_subnode_o < 0) { + /* create new subnode and run merge recursively */ + target_subnode_o = fdt_add_subnode(main_fdtp, target_o, name); + if (target_subnode_o < 0) { + printf("failed to create subnode \"%s\": %d\n", + name, target_subnode_o); + return; + } + } + + fdt_overlay_node(main_fdtp, target_subnode_o, + overlay_fdtp, o); + } +} + +/* + * Apply one overlay fragment + */ +static void +fdt_apply_fragment(void *main_fdtp, void *overlay_fdtp, int fragment_o) +{ + uint32_t target; + const char *target_path; + const void *val; + int target_node_o, overlay_node_o; + + target_node_o = -1; + val = fdt_getprop(overlay_fdtp, fragment_o, "target", NULL); + if (val) { + memcpy(&target, val, sizeof(target)); + target = fdt32_to_cpu(target); + target_node_o = fdt_node_offset_by_phandle(main_fdtp, target); + if (target_node_o < 0) { + printf("failed to find target %04x\n", target); + return; + } + } + + if (target_node_o < 0) { + target_path = fdt_getprop(overlay_fdtp, fragment_o, "target-path", NULL); + if (target_path == NULL) + return; + + target_node_o = fdt_path_offset(main_fdtp, target_path); + if (target_node_o < 0) { + printf("failed to find target-path %s\n", target_path); + return; + } + } + + if (target_node_o < 0) + return; + + overlay_node_o = fdt_subnode_offset(overlay_fdtp, fragment_o, "__overlay__"); + if (overlay_node_o < 0) { + printf("missing __overlay__ sub-node\n"); + return; + } + + fdt_overlay_node(main_fdtp, target_node_o, overlay_fdtp, overlay_node_o); +} + +/* + * Handle __fixups__ node in overlay DTB + */ +static int +fdt_overlay_do_fixups(void *main_fdtp, void *overlay_fdtp) +{ + int main_symbols_o, symbol_o, overlay_fixups_o; + int fixup_prop_o; + int len; + const char *fixups, *name; + const char *symbol_path; + uint32_t phandle; + + main_symbols_o = fdt_path_offset(main_fdtp, "/__symbols__"); + overlay_fixups_o = fdt_path_offset(overlay_fdtp, "/__fixups__"); + + if (main_symbols_o < 0) + return (-1); + if (overlay_fixups_o < 0) + return (-1); + + for (fixup_prop_o = fdt_first_property_offset(overlay_fdtp, overlay_fixups_o); + fixup_prop_o >= 0; + fixup_prop_o = fdt_next_property_offset(overlay_fdtp, fixup_prop_o)) { + fixups = fdt_getprop_by_offset(overlay_fdtp, fixup_prop_o, &name, &len); + symbol_path = fdt_getprop(main_fdtp, main_symbols_o, name, NULL); + if (symbol_path == NULL) { + printf("couldn't find \"%s\" symbol in main dtb\n", name); + return (-1); + } + symbol_o = fdt_path_offset(main_fdtp, symbol_path); + if (symbol_o < 0) { + printf("couldn't find \"%s\" path in main dtb\n", symbol_path); + return (-1); + } + phandle = fdt_get_phandle(main_fdtp, symbol_o); + if (fdt_do_one_fixup(overlay_fdtp, fixups, len, phandle) < 0) + return (-1); + } + + return (0); +} + +/* + * Handle __local_fixups__ node in overlay DTB + */ +static int +fdt_overlay_do_local_fixups(void *main_fdtp, void *overlay_fdtp) +{ + int overlay_local_fixups_o; + int len; + const char *fixups; + uint32_t phandle_offset; + + overlay_local_fixups_o = fdt_path_offset(overlay_fdtp, "/__local_fixups__"); + + if (overlay_local_fixups_o < 0) + return (-1); + + phandle_offset = fdt_max_phandle(main_fdtp); + fdt_increase_phandles(overlay_fdtp, phandle_offset); + fixups = fdt_getprop_w(overlay_fdtp, overlay_local_fixups_o, "fixup", &len); + if (fixups) { + if (fdt_do_local_fixup(overlay_fdtp, fixups, len, phandle_offset) < 0) + return (-1); + } + + return (0); +} + +/* + * Apply all fragments to main DTB + */ +static int +fdt_overlay_apply_fragments(void *main_fdtp, void *overlay_fdtp) +{ + int o, depth; + + o = fdt_path_offset(overlay_fdtp, "/"); + for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(overlay_fdtp, o, &depth)) { + if (depth != 1) + continue; + + fdt_apply_fragment(main_fdtp, overlay_fdtp, o); + } + + return (0); +} + +int +fdt_overlay_apply(void *main_fdtp, void *overlay_fdtp, size_t overlay_length) +{ + void *overlay_copy; + int rv; + + rv = 0; + + /* We modify overlay in-place, so we need writable copy */ + overlay_copy = malloc(overlay_length); + if (overlay_copy == NULL) { + printf("failed to allocate memory for overlay copy\n"); + return (-1); + } + + memcpy(overlay_copy, overlay_fdtp, overlay_length); + + if (fdt_overlay_do_fixups(main_fdtp, overlay_copy) < 0) { + printf("failed to perform fixups in overlay\n"); + rv = -1; + goto out; + } + + if (fdt_overlay_do_local_fixups(main_fdtp, overlay_copy) < 0) { + printf("failed to perform local fixups in overlay\n"); + rv = -1; + goto out; + } + + if (fdt_overlay_apply_fragments(main_fdtp, overlay_copy) < 0) { + printf("failed to apply fragments\n"); + rv = -1; + } + +out: + free(overlay_copy); + + return (rv); +} diff --git a/sys/boot/fdt/fdt_overlay.h b/sys/boot/fdt/fdt_overlay.h new file mode 100644 index 0000000..0f91343 --- /dev/null +++ b/sys/boot/fdt/fdt_overlay.h @@ -0,0 +1,34 @@ +/*- + * Copyright (c) 2015 Oleksandr Tymoshenko + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef FDT_OVERLAY_H +#define FDT_OVERLAY_H + +int fdt_overlay_apply(void *main_fdtp, void *overlay_fdtp, size_t overlay_length); + +#endif /* FDT_OVERLAY_H */ diff --git a/sys/boot/fdt/fdt_platform.h b/sys/boot/fdt/fdt_platform.h index cad7602..eae940a 100644 --- a/sys/boot/fdt/fdt_platform.h +++ b/sys/boot/fdt/fdt_platform.h @@ -43,8 +43,10 @@ void fdt_fixup_cpubusfreqs(unsigned long, unsigned long); void fdt_fixup_ethernet(const char *, char *, int); void fdt_fixup_memory(struct fdt_mem_region *, size_t); void fdt_fixup_stdout(const char *); +void fdt_apply_overlays(void); int fdt_load_dtb_addr(struct fdt_header *); int fdt_load_dtb_file(const char *); +int fdt_load_dtb_overlays(const char *); int fdt_setup_fdtp(void); /* The platform library needs to implement these functions */ diff --git a/sys/boot/uboot/fdt/uboot_fdt.c b/sys/boot/uboot/fdt/uboot_fdt.c index 86f46e9..5caef8f 100644 --- a/sys/boot/uboot/fdt/uboot_fdt.c +++ b/sys/boot/uboot/fdt/uboot_fdt.c @@ -45,6 +45,7 @@ fdt_platform_load_dtb(void) struct fdt_header *hdr; const char *s; char *p; + int rv; /* * If the U-boot environment contains a variable giving the address of a @@ -68,6 +69,8 @@ fdt_platform_load_dtb(void) } } + rv = 1; + /* * If the U-boot environment contains a variable giving the name of a * file, use it if we can load and validate it. @@ -78,11 +81,21 @@ fdt_platform_load_dtb(void) if (s != NULL && *s != '\0') { if (fdt_load_dtb_file(s) == 0) { printf("Loaded DTB from file '%s'.\n", s); - return (0); + rv = 0; } } - return (1); + if (rv == 0) { + s = getenv("fdt_overlays"); + if (s == NULL) + s = ub_env_get("fdt_overlays"); + if (s != NULL && *s != '\0') { + printf("Loading DTB overlays: '%s'\n", s); + fdt_load_dtb_overlays(s); + } + } + + return (rv); } void @@ -98,6 +111,9 @@ fdt_platform_fixups(void) eth_no = 0; ethstr = NULL; + /* Apply overlays before anything else */ + fdt_apply_overlays(); + /* Acquire sys_info */ si = ub_get_sys_info(); diff --git a/sys/tools/fdt/make_dtb.sh b/sys/tools/fdt/make_dtb.sh index 643fdd6..bfd87ab 100755 --- a/sys/tools/fdt/make_dtb.sh +++ b/sys/tools/fdt/make_dtb.sh @@ -20,5 +20,5 @@ for d in ${dts}; do dtb=${dtb_path}/`basename $d .dts`.dtb echo "converting $d -> $dtb" cpp -P -x assembler-with-cpp -I $S/gnu/dts/include -I $S/boot/fdt/dts/${MACHINE} -I $S/gnu/dts/${MACHINE} -include $d /dev/null | - dtc -O dtb -o $dtb -b 0 -p 1024 -i $S/boot/fdt/dts/${MACHINE} -i $S/gnu/dts/${MACHINE} + dtc -O dtb -o $dtb -b 0 -@ -p 1024 -i $S/boot/fdt/dts/${MACHINE} -i $S/gnu/dts/${MACHINE} done