Hi Maxime, > On May 27, 2016, at 12:13 , Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx> wrote: > > The device tree overlays are a good way to deal with user-modifyable > boards or boards with some kind of an expansion mechanism where we can > easily plug new board in (like the BBB, the Raspberry Pi or the CHIP). > > Add a new function to merge overlays with a base device tree. > > Signed-off-by: Maxime Ripard <maxime.ripard@xxxxxxxxxxxxxxxxxx> > --- > include/libfdt.h | 30 ++++ > lib/libfdt/Makefile | 2 +- > lib/libfdt/fdt_overlay.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 445 insertions(+), 1 deletion(-) > create mode 100644 lib/libfdt/fdt_overlay.c > > diff --git a/include/libfdt.h b/include/libfdt.h > index 1e01b2c7ebdf..783067e841a1 100644 > --- a/include/libfdt.h > +++ b/include/libfdt.h > @@ -1698,6 +1698,36 @@ int fdt_add_subnode(void *fdt, int parentoffset, const char *name); > */ > int fdt_del_node(void *fdt, int nodeoffset); > > +/** > + * fdt_overlay_apply - Applies a DT overlay on a base DT > + * @fdt: pointer to the base device tree blob > + * @fdto: pointer to the device tree overlay blob > + * > + * fdt_overlay_apply() will apply the given device tree overlay on the > + * given base device tree. > + * > + * Expect the base device tree to be modified, even if the function > + * returns an error. > + * If the base tree has been modified on an error it is unsafe to use it for booting. A valid strategy would be to scribble over the DT magic number so that that blob is invalidated. What are the other people’s opinion on this? > + * returns: > + * 0, on success > + * -FDT_ERR_NOSPACE, there's not enough space in the base device tree > + * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or > + * properties in the base DT > + * -FDT_ERR_BADPHANDLE, the phandles in the overlay do not have the right > + * magic > + * -FDT_ERR_INTERNAL, > + * -FDT_ERR_BADLAYOUT, > + * -FDT_ERR_BADMAGIC, > + * -FDT_ERR_BADOFFSET, > + * -FDT_ERR_BADPATH, > + * -FDT_ERR_BADVERSION, > + * -FDT_ERR_BADSTRUCTURE, > + * -FDT_ERR_BADSTATE, > + * -FDT_ERR_TRUNCATED, standard meanings > + */ > +int fdt_overlay_apply(void *fdt, void *fdto); > + > /**********************************************************************/ > /* Debugging / informational functions */ > /**********************************************************************/ > diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile > index 934d6142c3e9..4935bf012a75 100644 > --- a/lib/libfdt/Makefile > +++ b/lib/libfdt/Makefile > @@ -6,4 +6,4 @@ > # > > obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \ > - fdt_empty_tree.o fdt_addresses.o fdt_region.o > + fdt_empty_tree.o fdt_addresses.o fdt_region.o fdt_overlay.o > diff --git a/lib/libfdt/fdt_overlay.c b/lib/libfdt/fdt_overlay.c > new file mode 100644 > index 000000000000..1e68e903c734 > --- /dev/null > +++ b/lib/libfdt/fdt_overlay.c > @@ -0,0 +1,414 @@ > +#include "libfdt_env.h" > + > +#include <fdt.h> > +#include <libfdt.h> > + > +#include "libfdt_internal.h" > + > +static uint32_t fdt_overlay_get_target_phandle(const void *fdto, int node) > +{ > + const uint32_t *val; > + int len; > + > + val = fdt_getprop(fdto, node, "target", &len); > + if (!val || (len != sizeof(*val))) > + return 0; > + > + return fdt32_to_cpu(*val); > +} > + > +static int fdt_overlay_get_target(const void *fdt, const void *fdto, > + int fragment) > +{ > + uint32_t phandle; > + const char *path; > + > + /* Try first to do a phandle based lookup */ > + phandle = fdt_overlay_get_target_phandle(fdto, fragment); > + if (phandle) > + return fdt_node_offset_by_phandle(fdt, phandle); > + > + /* And then a path based lookup */ > + path = fdt_getprop(fdto, fragment, "target-path", NULL); > + if (!path) > + return -FDT_ERR_NOTFOUND; > + > + return fdt_path_offset(fdt, path); > +} > + Those targets are fine; beware there are patches with more target options. > +static int fdt_overlay_adjust_node_phandles(void *fdto, int node, > + uint32_t delta) > +{ > + int property; > + int child; > + > + fdt_for_each_property(fdto, node, property) { > + const uint32_t *val; > + const char *name; > + uint32_t adj_val; > + int len; > + int ret; > + > + val = fdt_getprop_by_offset(fdto, property, > + &name, &len); > + if (!val || (len != sizeof(*val))) Superfluous parentheses. > + continue; > + > + if (strcmp(name, "phandle") && strcmp(name, "linux,phandle")) > + continue; > + > + adj_val = fdt32_to_cpu(*val); > + adj_val += delta; > + ret = fdt_setprop_inplace_u32(fdto, node, name, adj_val); > + if (ret) > + return ret; > + } > + > + fdt_for_each_subnode(fdto, child, node) > + fdt_overlay_adjust_node_phandles(fdto, child, delta); > + > + return 0; > +} > + > +static int fdt_overlay_adjust_local_phandles(void *fdto, uint32_t delta) > +{ > + int root; > + > + root = fdt_path_offset(fdto, "/"); > + if (root < 0) > + return root; > + > + return fdt_overlay_adjust_node_phandles(fdto, root, delta); > +} > + > +static int _fdt_overlay_update_local_references(void *fdto, > + int tree_node, > + int fixup_node, > + uint32_t delta) > +{ > + int fixup_prop; > + int fixup_child; > + > + fdt_for_each_property(fdto, fixup_node, fixup_prop) { > + const char *fixup_name, *tree_name; > + const uint32_t *val = NULL; > + uint32_t adj_val; > + int fixup_len; > + int tree_prop; > + int tree_len; > + int ret; > + > + fdt_getprop_by_offset(fdto, fixup_prop, > + &fixup_name, &fixup_len); > + > + if (!strcmp(fixup_name, "phandle") || > + !strcmp(fixup_name, "linux,phandle")) > + continue; > + > + fdt_for_each_property(fdto, tree_node, tree_prop) { > + val = fdt_getprop_by_offset(fdto, tree_prop, > + &tree_name, &tree_len); > + > + if (!strcmp(tree_name, fixup_name)) > + break; > + } > + > + if (!val || !tree_len) > + return -FDT_ERR_NOTFOUND; > + > + if (!tree_name) > + return -FDT_ERR_NOTFOUND; > + > + if (tree_len != 4) > + return -FDT_ERR_NOTFOUND; > + > + adj_val = fdt32_to_cpu(*val); > + adj_val += delta; > + ret = fdt_setprop_inplace_u32(fdto, tree_node, fixup_name, > + adj_val); > + if (ret) > + return ret; > + } > + > + fdt_for_each_subnode(fdto, fixup_child, fixup_node) { > + const char *fixup_child_name = fdt_get_name(fdto, > + fixup_child, NULL); > + int tree_child; > + > + fdt_for_each_subnode(fdto, tree_child, tree_node) { > + const char *tree_child_name = fdt_get_name(fdto, > + tree_child, > + NULL); > + > + if (!strcmp(fixup_child_name, tree_child_name)) > + break; > + } > + > + _fdt_overlay_update_local_references(fdto, > + tree_child, > + fixup_child, > + delta); > + } > + > + return 0; > +} > + > +static int fdt_overlay_update_local_references(void *dto, uint32_t delta) > +{ > + int root, fixups; > + > + root = fdt_path_offset(dto, "/"); > + if (root < 0) > + return root; > + > + fixups = fdt_path_offset(dto, "/__local_fixups__"); > + if (root < 0) > + return root; > + > + return _fdt_overlay_update_local_references(dto, root, fixups, > + delta); > +} > + > +static int _fdt_overlay_fixup_phandle(void *fdt, void *fdto, > + int symbols_off, > + const char *path, const char *name, > + int index, const char *label) > +{ > + const uint32_t *prop_val; > + const char *symbol_path; > + uint32_t *fixup_val; > + uint32_t phandle; > + int symbol_off, fixup_off; > + int prop_len; > + int ret; > + > + symbol_path = fdt_getprop(fdt, symbols_off, label, > + &prop_len); > + if (!symbol_path) > + return -FDT_ERR_NOTFOUND; > + > + symbol_off = fdt_path_offset(fdt, symbol_path); > + if (symbol_off < 0) > + return symbol_off; > + > + phandle = fdt_get_phandle(fdt, symbol_off); > + if (!phandle) > + return -FDT_ERR_NOTFOUND; > + > + fixup_off = fdt_path_offset(fdto, path); > + if (fixup_off < 0) > + return fixup_off; > + > + prop_val = fdt_getprop(fdto, fixup_off, name, > + &prop_len); > + if (!prop_val) > + return -FDT_ERR_NOTFOUND; > + > + fixup_val = malloc(prop_len); > + if (!fixup_val) > + return -FDT_ERR_INTERNAL; > + memcpy(fixup_val, prop_val, prop_len); > + > + if (fdt32_to_cpu(fixup_val[index]) != 0xdeadbeef) { > + ret = -FDT_ERR_BADPHANDLE; > + goto out; > + } > + > + fixup_val[index] = cpu_to_fdt32(phandle); > + ret = fdt_setprop_inplace(fdto, fixup_off, > + name, fixup_val, > + prop_len); > + > +out: > + free(fixup_val); > + return ret; > +}; > + > +static int fdt_overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, > + int property) > +{ > + const char *value, *tmp_value; > + const char *label; > + int tmp_len, len, next; > + int table = 0; > + int i; > + > + value = fdt_getprop_by_offset(fdto, property, > + &label, &len); > + tmp_value = value; > + tmp_len = len; > + > + do { > + next = strlen(tmp_value) + 1; > + tmp_len -= next; > + tmp_value += next; > + table++; > + } while (tmp_len > 0); > + > + for (i = 0; i < table; i++) { > + const char *prop_string = value; > + const char *path, *name; > + char *prop_cpy, *prop_tmp; > + int index, stlen; > + char *sep; > + > + stlen = strlen(prop_string); > + prop_cpy = malloc(stlen + 1); > + if (!prop_cpy) > + return -FDT_ERR_INTERNAL; > + > + strncpy(prop_cpy, prop_string, stlen); > + prop_cpy[stlen] = '\0'; > + > + for (prop_tmp = prop_cpy; (sep = strchr(prop_tmp, ':')); > + prop_tmp += ((sep - prop_tmp) + 1)) > + *sep = '\0'; > + > + prop_tmp = prop_cpy; > + path = prop_tmp; > + prop_tmp += strlen(prop_tmp) + 1; > + > + name = prop_tmp; > + prop_tmp += strlen(prop_tmp) + 1; > + > + index = strtol(prop_tmp, NULL, 10); > + prop_tmp += strlen(prop_tmp) + 1; > + > + value += stlen + 1; > + len -= stlen + 1; > + > + _fdt_overlay_fixup_phandle(fdt, fdto, symbols_off, > + path, name, index, label); > + > + free(prop_cpy); > + } > + > + return 0; > +} > + > +static int fdt_overlay_fixup_phandles(void *dt, void *dto) > +{ > + int fixups_off, symbols_off; > + int property; > + > + symbols_off = fdt_path_offset(dt, "/__symbols__"); > + fixups_off = fdt_path_offset(dto, "/__fixups__"); > + > + fdt_for_each_property(dto, fixups_off, property) > + fdt_overlay_fixup_phandle(dt, dto, symbols_off, property); > + > + return 0; > +} > + > +static int fdt_apply_overlay_node(void *dt, void *dto, > + int target, int overlay) > +{ > + int property; > + int node; > + > + fdt_for_each_property(dto, overlay, property) { > + const char *name; > + const void *prop; > + int prop_len; > + int ret; > + > + prop = fdt_getprop_by_offset(dto, property, &name, > + &prop_len); > + if (!prop) > + return -FDT_ERR_INTERNAL; > + > + ret = fdt_setprop(dt, target, name, prop, prop_len); > + if (ret) > + return ret; > + } > + > + fdt_for_each_subnode(dto, node, overlay) { > + const char *name = fdt_get_name(dto, node, NULL); > + int nnode; > + int ret; > + > + nnode = fdt_add_subnode(dt, target, name); > + if (nnode < 0) > + return nnode; > + > + ret = fdt_apply_overlay_node(dt, dto, nnode, node); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int fdt_overlay_merge(void *dt, void *dto) > +{ > + int root, fragment; > + > + root = fdt_path_offset(dto, "/"); > + if (root < 0) > + return root; > + > + fdt_for_each_subnode(dto, fragment, root) { > + const char *name = fdt_get_name(dto, fragment, NULL); > + uint32_t target; > + int overlay; > + int ret; > + > + if (strncmp(name, "fragment", 8)) > + continue; > + This is incorrect. The use of “fragment” is a convention only. The real test whether the node is an overlay fragment is that it contains a target property. > + target = fdt_overlay_get_target(dt, dto, fragment); > + if (target < 0) > + return target; > + So you could do ‘if (target < 0) continue;’ or handle a more complex error code. > + overlay = fdt_subnode_offset(dto, fragment, "__overlay__"); > + if (overlay < 0) > + return overlay; > + > + ret = fdt_apply_overlay_node(dt, dto, target, overlay); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +int fdt_overlay_apply(void *fdt, void *fdto) > +{ > + uint32_t delta = fdt_get_max_phandle(fdt) + 1; > + void *fdto_copy; > + int ret; > + > + FDT_CHECK_HEADER(fdt); > + FDT_CHECK_HEADER(fdto); > + > + /* > + * We're going to modify the overlay so that we can apply it. > + * > + * Make sure sure we don't destroy the original > + */ > + fdto_copy = malloc(fdt_totalsize(fdto)); > + if (!fdto_copy) > + return -FDT_ERR_INTERNAL; > + > + ret = fdt_move(fdto, fdto_copy, fdt_totalsize(fdto)); > + if (ret) > + goto out; > + > + ret = fdt_overlay_adjust_local_phandles(fdto_copy, delta); > + if (ret) > + goto out; > + > + ret = fdt_overlay_update_local_references(fdto_copy, delta); > + if (ret) > + goto out; > + > + ret = fdt_overlay_fixup_phandles(fdt, fdto_copy); > + if (ret) > + goto out; > + > + ret = fdt_overlay_merge(fdt, fdto_copy); > + > +out: > + free(fdto_copy); > + return ret; > +} > -- > 2.8.2 > I would caution against the liberal use of malloc in libfdt. We’re possibly running in a constrained environment; a custom extents based (non freeing) allocator should be better. We need some figures about memory consumption when this is enabled, and a CONFIG option to disable it. Regards — Pantelis -- To unsubscribe from this list: send the line "unsubscribe devicetree-compiler" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html