Hi Maxime,

Interesting patch set! I am not a dt (overlay) expert, but some comments
below...

On 2016-04-02 14:06, Maxime Ripard 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 or the raspberry pi).
> 
> However, so far, the usual mechanism to deal with it was to have in Linux
> some driver detecting the expansion boards plugged in and then request
> these overlays using the firmware interface.
> 
> That works in most cases, but in some cases, you might want to have the
> overlays applied before the userspace comes in. Either because the new
> board requires some kind of an early initialization, or because your root
> filesystem is accessed through that expansion board.
> 
> The easiest solution in such a case is to simply have the component before
> Linux applying that overlay, removing all these drawbacks.
> 
> Signed-off-by: Maxime Ripard <maxime.rip...@free-electrons.com>
> ---
>  cmd/Makefile          |   2 +-
>  cmd/fdt.c             |  19 +++
>  cmd/fdt_overlay.c     | 464 
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/fdt_support.h |   2 +-
>  4 files changed, 485 insertions(+), 2 deletions(-)
>  create mode 100644 cmd/fdt_overlay.c
> 
> diff --git a/cmd/Makefile b/cmd/Makefile
> index 03f7e0a21d2f..09f993971d93 100644
> --- a/cmd/Makefile
> +++ b/cmd/Makefile
> @@ -52,7 +52,7 @@ obj-$(CONFIG_CMD_EXT4) += ext4.o
>  obj-$(CONFIG_CMD_EXT2) += ext2.o
>  obj-$(CONFIG_CMD_FAT) += fat.o
>  obj-$(CONFIG_CMD_FDC) += fdc.o
> -obj-$(CONFIG_OF_LIBFDT) += fdt.o
> +obj-$(CONFIG_OF_LIBFDT) += fdt.o fdt_overlay.o
>  obj-$(CONFIG_CMD_FITUPD) += fitupd.o
>  obj-$(CONFIG_CMD_FLASH) += flash.o
>  ifdef CONFIG_FPGA
> diff --git a/cmd/fdt.c b/cmd/fdt.c
> index ca6c707dfb48..c875ba688d3b 100644
> --- a/cmd/fdt.c
> +++ b/cmd/fdt.c
> @@ -639,6 +639,24 @@ static int do_fdt(cmd_tbl_t *cmdtp, int flag, int
> argc, char * const argv[])
>  #endif
>  
>       }
> +     /* apply an overlay */
> +     else if (strncmp(argv[1], "ap", 2) == 0) {
> +             unsigned long addr;
> +             struct fdt_header *blob;
> +
> +             if (argc != 3)
> +                     return CMD_RET_USAGE;
> +
> +             if (!working_fdt)
> +                     return CMD_RET_FAILURE;
> +
> +             addr = simple_strtoul(argv[2], NULL, 16);
> +             blob = map_sysmem(addr, 0);
> +             if (!fdt_valid(&blob))
> +                     return CMD_RET_FAILURE;
> +
> +             fdt_overlay_apply(working_fdt, blob);

I guess we should check the return value here and return
CMD_RET_FAILURE. This would allow to abort boot if we apply a DT overlay
in a script. 


> +     }
>       /* resize the fdt */
>       else if (strncmp(argv[1], "re", 2) == 0) {
>               fdt_shrink_to_minimum(working_fdt);
> @@ -1025,6 +1043,7 @@ static int fdt_print(const char *pathp, char
> *prop, int depth)
>  #ifdef CONFIG_SYS_LONGHELP
>  static char fdt_help_text[] =
>       "addr [-c]  <addr> [<length>]   - Set the [control] fdt location to 
> <addr>\n"
> +     "fdt apply <addr>                    - Apply overlay to the DT\n"
>  #ifdef CONFIG_OF_BOARD_SETUP
>       "fdt boardsetup                      - Do board-specific set up\n"
>  #endif
> diff --git a/cmd/fdt_overlay.c b/cmd/fdt_overlay.c
> new file mode 100644
> index 000000000000..d2784d791a09
> --- /dev/null
> +++ b/cmd/fdt_overlay.c

Shouldn't this be in lib/libfdt/?

> @@ -0,0 +1,464 @@
> +#include <common.h>
> +#include <malloc.h>
> +#include <libfdt.h>
> +
> +#define fdt_for_each_property(fdt, node, property)           \
> +     for (property = fdt_first_property_offset(fdt, node);   \
> +          property >= 0;                                     \
> +          property = fdt_next_property_offset(fdt, property))
> +
> +struct fdt_overlay_fixup {
> +     char    label[64];
> +     char    name[64];
> +     char    path[64];
> +     int     index;
> +};
> +
> +static int fdt_get_max_phandle(const void *dt)
> +{
> +     int offset, phandle = 0, new_phandle;

phandle should be uint32_t I think.

Nit: call phandle max_phandle and new_phandle phandle. When reading the
code, new_phandle somehow suggests that something new gets generated or
assigned...

> +
> +     for (offset = fdt_next_node(dt, -1, NULL); offset >= 0;
> +          offset = fdt_next_node(dt, offset, NULL)) {
> +             new_phandle = fdt_get_phandle(dt, offset);
> +             if (new_phandle > phandle)
> +                     phandle = new_phandle;
> +     }
> +
> +     return phandle;
> +}
> +
> +static uint32_t fdt_overlay_get_target_phandle(const void *dt, int node)
> +{
> +     const uint32_t *val;
> +     int len;
> +
> +     val = fdt_getprop(dt, node, "target", &len);
> +     if (!val || (len != sizeof(*val)))
> +             return 0;
> +
> +     return fdt32_to_cpu(*val);
> +}
> +
> +static int fdt_overlay_get_target(const void *dt, const void *dto,
> int fragment)
> +{
> +     uint32_t phandle;
> +     const char *path;
> +
> +     /* Try first to do a phandle based lookup */
> +     phandle = fdt_overlay_get_target_phandle(dto, fragment);
> +     if (phandle)
> +             return fdt_node_offset_by_phandle(dt, phandle);
> +
> +     /* And then a path based lookup */
> +     path = fdt_getprop(dto, fragment, "target-path", NULL);
> +     if (!path)
> +             return -FDT_ERR_NOTFOUND;
> +
> +     return fdt_path_offset(dt, path);
> +}
> +
> +static int fdt_overlay_adjust_node_phandles(void *dto, int node,
> +                                         uint32_t delta)
> +{
> +     int property;
> +     int child;
> +
> +     fdt_for_each_property(dto, node, property) {
> +             const uint32_t *val;
> +             const char *name;
> +             uint32_t adj_val;
> +             int len;
> +             int ret;
> +
> +             val = fdt_getprop_by_offset(dto, property,
> +                                         &name, &len);
> +             if (!val || (len != 4))

As above, sizeof(*val) instead of a hardcoded 4

> +                     continue;
> +
> +             if (strcmp(name, "phandle") && strcmp(name, "linux,phandle"))
> +                     continue;
> +
> +             adj_val = fdt32_to_cpu(*val);
> +             adj_val += delta;
> +             ret = fdt_setprop_inplace_u32(dto, node, name, adj_val);
> +             if (ret) {
> +                     printf("Failed to ajust property %s phandle\n", name);

s/ajust/adjust

> +                     return ret;
> +             }
> +     }
> +
> +     fdt_for_each_subnode(dto, child, node)
> +             fdt_overlay_adjust_node_phandles(dto, child, delta);
> +
> +     return 0;
> +}
> +
> +static int fdt_overlay_adjust_local_phandles(void *overlay, uint32_t delta)
> +{
> +     int root;
> +
> +     root = fdt_path_offset(overlay, "/");
> +     if (root < 0) {
> +             printf("Couldn't locate the root of the overlay\n");
> +             return root;
> +     }
> +
> +     return fdt_overlay_adjust_node_phandles(overlay, root, delta);
> +}
> +
> +static int _fdt_overlay_update_local_references(void *dto,
> +                                             int tree_node,
> +                                             int fixup_node,
> +                                             uint32_t delta)
> +{
> +     int fixup_prop;
> +     int fixup_child;
> +
> +     fdt_for_each_property(dto, fixup_node, fixup_prop) {
> +             const char *fixup_name, *tree_name;
> +             const uint32_t *val;
> +             uint32_t adj_val;
> +             int fixup_len;
> +             int tree_prop;
> +             int ret;
> +
> +             fdt_getprop_by_offset(dto, fixup_prop,
> +                                   &fixup_name, &fixup_len);
> +
> +             if (!strcmp(fixup_name, "phandle") ||
> +                 !strcmp(fixup_name, "linux,phandle"))
> +                     continue;
> +
> +             if (fixup_len != 4) {
> +                     printf("Illegal property size (%d) %s\n",
> +                            fixup_len, fixup_name);
> +                     return -FDT_ERR_NOTFOUND;
> +             }
> +
> +             fdt_for_each_property(dto, tree_node, tree_prop) {
> +                     int tree_len;
> +
> +                     val = fdt_getprop_by_offset(dto, tree_prop,
> +                                                 &tree_name, &tree_len);
> +
> +                     if (!strcmp(tree_name, fixup_name))
> +                             break;
> +             }
> +
> +             if (!tree_name) {
> +                     printf("Couldn't find target property %s\n",
> +                            fixup_name);
> +                     return -FDT_ERR_NOTFOUND;
> +             }
> +

As a matter of consistency, shouldn't we check tree_len here too?

> +             adj_val = fdt32_to_cpu(*val);
> +             adj_val += delta;
> +             ret = fdt_setprop_inplace_u32(dto, tree_node, fixup_name,
> +                                           adj_val);
> +             if (ret) {
> +                     printf("Failed to ajust property %s phandle\n",

s/ajust/adjust

> +                            fixup_name);
> +                     return ret;
> +             }
> +     }
> +
> +     fdt_for_each_subnode(dto, fixup_child, fixup_node) {
> +             const char *fixup_child_name = fdt_get_name(dto,
> +                                                         fixup_child, NULL);
> +             int tree_child;
> +
> +             fdt_for_each_subnode(dto, tree_child, tree_node) {
> +                     const char *tree_child_name = fdt_get_name(dto,
> +                                                                tree_child,
> +                                                                NULL);
> +
> +                     if (!strcmp(fixup_child_name, tree_child_name))
> +                             break;
> +             }
> +
> +             _fdt_overlay_update_local_references(dto,
> +                                                  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) {
> +             printf("Couldn't locate the root of the overlay\n");
> +             return root;
> +     }
> +
> +     fixups = fdt_path_offset(dto, "/__local_fixups__");
> +     if (root < 0) {
> +             printf("Couldn't locate the local fixups in the overlay\n");
> +             return root;
> +     }
> +
> +     return _fdt_overlay_update_local_references(dto, root, fixups,
> +                                                 delta);
> +}
> +
> +static struct fdt_overlay_fixup *fdt_fixups_parse_property(void *dto,
> +                                                        int property,
> +                                                        int *number)
> +{
> +     struct fdt_overlay_fixup *fixups;
> +     const char *value, *tmp_value;
> +     const char *name;
> +     int tmp_len, len, next;
> +     int table = 0;
> +     int i;
> +
> +     value = fdt_getprop_by_offset(dto, property,
> +                                   &name, &len);
> +     tmp_value = value;
> +     tmp_len = len;
> +
> +     do {
> +             next = strlen(tmp_value) + 1;
> +             tmp_len -= next;
> +             tmp_value += next;
> +             table++;
> +     } while (tmp_len > 0);
> +
> +     fixups = malloc(table * sizeof(*fixups));
> +     if (!fixups)
> +             return NULL;

Is this table really needed?

As far as I see we could easily get rid of this malloc by encapsulating
the "fixup for-loop" in fdt_overlay_fixup_phandles into a function
(fdt_fixup_property), allocate label, path and name as character arrays
on stack and pass those three pointers to the new function...

Maybe I miss something though...

> +
> +     for (i = 0; i < table; i++) {
> +             struct fdt_overlay_fixup *fixup = fixups + i;
> +             const char *prop_string = value;
> +             char *sep;
> +             int stlen;
> +
> +             stlen = strlen(prop_string);
> +
> +             sep = strchr(prop_string, ':');
> +             strncpy(fixup->path, prop_string, sep - prop_string);
> +             fixup->path[sep - prop_string] = '\0';
> +             prop_string = sep + 1;
> +
> +             sep = strchr(prop_string, ':');
> +             strncpy(fixup->name, prop_string, sep - prop_string);
> +             fixup->name[sep - prop_string] = '\0';
> +             prop_string = sep + 1;
> +
> +             fixup->index = simple_strtol(prop_string, NULL, 10);
> +             strncpy(fixup->label, name, 64);
> +
> +             value += stlen + 1;
> +             len -= stlen + 1;
> +     }
> +
> +     *number = table;
> +     return fixups;
> +}
> +
> +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) {
> +             struct fdt_overlay_fixup *fixups;
> +             int n_fixups;
> +             int i;
> +
> +             fixups = fdt_fixups_parse_property(dto, property, &n_fixups);
> +             if (!fixups || n_fixups == 0)
> +                     continue;
> +
> +             for (i = 0; i < n_fixups; i++) {

see above, this for loop would go into something like
fdt_overlay_fixup_phandles(dt, property, label, path, name)..

> +                     struct fdt_overlay_fixup *fixup = fixups + i;
> +                     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(dt, symbols_off, fixup->label,
> +                                               &prop_len);
> +                     if (!symbol_path) {
> +                             printf("Couldn't lookup symbol %s in the main 
> DT... Skipping\n",
> +                                    fixup->label);
> +                             continue;
> +                     }
> +
> +                     symbol_off = fdt_path_offset(dt, symbol_path);
> +                     if (symbol_off < 0) {
> +                             printf("Couldn't match the symbol %s to node 
> %s... Skipping\n",
> +                                    fixup->label, symbol_path);
> +                             continue;
> +                     }
> +
> +                     phandle = fdt_get_phandle(dt, symbol_off);
> +                     if (!phandle) {
> +                             printf("Symbol node %s has no phandle... 
> Skipping\n",
> +                                    symbol_path);
> +                             continue;
> +                     }
> +
> +                     fixup_off = fdt_path_offset(dto, fixup->path);
> +                     if (fixup_off < 0) {
> +                             printf("Invalid overlay node %s to fixup... 
> Skipping\n",
> +                                    fixup->path);
> +                             continue;
> +                     }
> +
> +                     prop_val = fdt_getprop(dto, fixup_off, fixup->name,
> +                                            &prop_len);
> +                     if (!prop_val) {
> +                             printf("Couldn't retrieve property %s/%s 
> value... Skipping\n",
> +                                    fixup->path, fixup->name);
> +                             continue;
> +                     }
> +
> +                     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[fixup->index]) != 
> 0xdeadbeef) {
> +                             printf("Value pointed (0x%x) is not supposed to 
> be fixed up... Skipping\n",
> +                                    fdt32_to_cpu(fixup_val[fixup->index]));
> +                             continue;
> +                     }
> +
> +                     fixup_val[fixup->index] = cpu_to_fdt32(phandle);
> +                     ret = fdt_setprop_inplace(dto, fixup_off,
> +                                               fixup->name, fixup_val,
> +                                               prop_len);
> +                     if (ret) {
> +                             printf("Couldn't fixup phandle in overlay 
> property %s/%s (%d)...
> Skipping\n",
> +                                    fixup->path, fixup->name, ret);
> +                     }
> +             }
> +
> +             free(fixups);
> +     }
> +
> +     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) {
> +                     printf("Couldn't set property %s\n", name);
> +                     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) {
> +                     printf("Couldn't add subnode %s (%d)\n", name, nnode);
> +                     return nnode;
> +             }
> +
> +             ret = fdt_apply_overlay_node(dt, dto, nnode, node);
> +             if (ret) {
> +                     printf("Couldn't apply sub-overlay (%d)\n", 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) {
> +             printf("Couldn't locate the root of our overlay\n");
> +             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;
> +
> +             target = fdt_overlay_get_target(dt, dto, fragment);
> +             if (target < 0) {
> +                     printf("Couldn't locate %s target\n", name);
> +                     return target;
> +             }
> +
> +             overlay = fdt_subnode_offset(dto, fragment, "__overlay__");
> +             if (overlay < 0) {
> +                     printf("Couldn't locate %s overlay\n", name);
> +                     return overlay;
> +             }
> +
> +             ret = fdt_apply_overlay_node(dt, dto, target, overlay);
> +             if (ret) {
> +                     printf("Couldn't apply %s overlay\n", name);
> +                     return ret;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +int fdt_overlay_apply(void *dt, void *dto)
> +{
> +     uint32_t delta = fdt_get_max_phandle(dt) + 1;
> +     int ret;
> +
> +     ret = fdt_overlay_adjust_local_phandles(dto, delta);
> +     if (ret) {
> +             printf("Couldn't adjust local phandles\n");
> +             return ret;
> +     }
> +
> +     ret = fdt_overlay_update_local_references(dto, delta);
> +     if (ret) {
> +             printf("Couldn't update our local references\n");
> +             return ret;
> +     }
> +
> +     ret = fdt_overlay_fixup_phandles(dt, dto);
> +     if (ret) {
> +             printf("Couldn't resolve the global phandles\n");
> +             return ret;
> +     }

As far as I can see, all three of this functions already print error
messages on errors, so IMHO there is no need to do this here again.

--
Stefan

> +
> +     return fdt_overlay_merge(dt, dto);
> +}
> diff --git a/include/fdt_support.h b/include/fdt_support.h
> index 296add01f34f..b4184a767e28 100644
> --- a/include/fdt_support.h
> +++ b/include/fdt_support.h
> @@ -240,7 +240,7 @@ int arch_fixup_memory_node(void *blob);
>  
>  int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
>                           u32 height, u32 stride, const char *format);
> -
> +int fdt_overlay_apply(void *fdt, void *overlay);
>  #endif /* ifdef CONFIG_OF_LIBFDT */
>  
>  #ifdef USE_HOSTCC
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to