[PATCH] of/fdt: Add unflatten_partial_device_tree

Grant Likely grant.likely at secretlab.ca
Tue Jun 29 09:18:27 EST 2010


On Mon, Jun 28, 2010 at 10:47 AM, Stephen Neuendorffer
<stephen.neuendorffer at xilinx.com> wrote:
> This code allows a user to parse a partial device tree blob, which is
> structurally independent of any toplevel blob.
> Previously, this code assumed that the blob comes from initial_boot_params.
> Now, unflatten_partial_device_tree can take a blob from an arbitrary position,
> and the location of the blob gets passed around to the various support functions.
> Some of the functions are still tied to the initial_boot_params blob, although
> perhaps they should get abstracted as well?

At the very least, all the references in drivers/of should probably be
abstracted.  I'm not to worried about the stuff in arch/* unless the
functions there are also needed for partial tree parsing.

The code in unflatten_partial_device_tree is also (as you know) a
blatant copy and paste from unflatten_device_tree.  The two functions
should be consolidated.

Another question is what to do with the unflattened tree once it is
unflattened.  Some of the existing code expects the node to be part of
the global tree.  Those could either be refactored, or the new partial
tree could be grafted into the global tree.  Grafting will have the
least impact, but it probably isn't a good idea in the long term.
Grafting together unrelated trees seems messy to me.

Cheers,
g.

> ---
>  drivers/of/fdt.c       |  147 ++++++++++++++++++++++++++++++++++++------------
>  include/linux/of_fdt.h |   10 ++-
>  2 files changed, 118 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index 66401bc..628ba4f 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -11,10 +11,12 @@
>
>  #include <linux/kernel.h>
>  #include <linux/initrd.h>
> +#include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_fdt.h>
>  #include <linux/string.h>
>  #include <linux/errno.h>
> +#include <linux/slab.h>
>
>  #ifdef CONFIG_PPC
>  #include <asm/machdep.h>
> @@ -22,15 +24,16 @@
>
>  #include <asm/page.h>
>
> -int __initdata dt_root_addr_cells;
> -int __initdata dt_root_size_cells;
> +int dt_root_addr_cells;
> +int dt_root_size_cells;
>
>  struct boot_param_header *initial_boot_params;
>
> -char *find_flat_dt_string(u32 offset)
> +char *find_flat_dt_string(u32 offset,
> +                         struct boot_param_header *blob)
>  {
> -       return ((char *)initial_boot_params) +
> -               be32_to_cpu(initial_boot_params->off_dt_strings) + offset;
> +       return ((char *)blob) +
> +               be32_to_cpu(blob->off_dt_strings) + offset;
>  }
>
>  /**
> @@ -42,7 +45,7 @@ char *find_flat_dt_string(u32 offset)
>  * used to extract the memory information at boot before we can
>  * unflatten the tree
>  */
> -int __init of_scan_flat_dt(int (*it)(unsigned long node,
> +int  of_scan_flat_dt(int (*it)(unsigned long node,
>                                     const char *uname, int depth,
>                                     void *data),
>                           void *data)
> @@ -102,7 +105,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
>  /**
>  * of_get_flat_dt_root - find the root node in the flat blob
>  */
> -unsigned long __init of_get_flat_dt_root(void)
> +unsigned long  of_get_flat_dt_root(void)
>  {
>        unsigned long p = ((unsigned long)initial_boot_params) +
>                be32_to_cpu(initial_boot_params->off_dt_struct);
> @@ -120,8 +123,9 @@ unsigned long __init of_get_flat_dt_root(void)
>  * This function can be used within scan_flattened_dt callback to get
>  * access to properties
>  */
> -void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
> -                                unsigned long *size)
> +void * of_get_flat_dt_prop(unsigned long node, const char *name,
> +                                unsigned long *size,
> +                                struct boot_param_header *blob)
>  {
>        unsigned long p = node;
>
> @@ -142,7 +146,7 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
>                if (be32_to_cpu(initial_boot_params->version) < 0x10)
>                        p = ALIGN(p, sz >= 8 ? 8 : 4);
>
> -               nstr = find_flat_dt_string(noff);
> +               nstr = find_flat_dt_string(noff, blob);
>                if (nstr == NULL) {
>                        pr_warning("Can't find property index name !\n");
>                        return NULL;
> @@ -162,12 +166,13 @@ void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
>  * @node: node to test
>  * @compat: compatible string to compare with compatible list.
>  */
> -int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
> +int  of_flat_dt_is_compatible(unsigned long node, const char *compat,
> +                                struct boot_param_header *blob)
>  {
>        const char *cp;
>        unsigned long cplen, l;
>
> -       cp = of_get_flat_dt_prop(node, "compatible", &cplen);
> +       cp = of_get_flat_dt_prop(node, "compatible", &cplen, blob);
>        if (cp == NULL)
>                return 0;
>        while (cplen > 0) {
> @@ -181,7 +186,7 @@ int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
>        return 0;
>  }
>
> -static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
> +static void * unflatten_dt_alloc(unsigned long *mem, unsigned long size,
>                                       unsigned long align)
>  {
>        void *res;
> @@ -200,11 +205,12 @@ static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
>  * @allnextpp: pointer to ->allnext from last allocated device_node
>  * @fpsize: Size of the node path up at the current depth.
>  */
> -unsigned long __init unflatten_dt_node(unsigned long mem,
> +unsigned long  unflatten_dt_node(unsigned long mem,
>                                        unsigned long *p,
>                                        struct device_node *dad,
>                                        struct device_node ***allnextpp,
> -                                       unsigned long fpsize)
> +                                       unsigned long fpsize,
> +                                struct boot_param_header *blob)
>  {
>        struct device_node *np;
>        struct property *pp, **prev_pp = NULL;
> @@ -300,10 +306,10 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
>                sz = be32_to_cpup((__be32 *)(*p));
>                noff = be32_to_cpup((__be32 *)((*p) + 4));
>                *p += 8;
> -               if (be32_to_cpu(initial_boot_params->version) < 0x10)
> +               if (be32_to_cpu(blob->version) < 0x10)
>                        *p = ALIGN(*p, sz >= 8 ? 8 : 4);
>
> -               pname = find_flat_dt_string(noff);
> +               pname = find_flat_dt_string(noff, blob);
>                if (pname == NULL) {
>                        pr_info("Can't find property name in list !\n");
>                        break;
> @@ -382,7 +388,7 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
>                if (tag == OF_DT_NOP)
>                        *p += 4;
>                else
> -                       mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
> +                       mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize, blob);
>                tag = be32_to_cpup((__be32 *)(*p));
>        }
>        if (tag != OF_DT_END_NODE) {
> @@ -393,24 +399,26 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
>        return mem;
>  }
>
> +#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_POWERPC) || defined(CONFIG_SPARC)
> +
>  #ifdef CONFIG_BLK_DEV_INITRD
>  /**
>  * early_init_dt_check_for_initrd - Decode initrd location from flat tree
>  * @node: reference to node containing initrd location ('chosen')
>  */
> -void __init early_init_dt_check_for_initrd(unsigned long node)
> +void  early_init_dt_check_for_initrd(unsigned long node)
>  {
>        unsigned long start, end, len;
>        __be32 *prop;
>
>        pr_debug("Looking for initrd properties... ");
>
> -       prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
> +       prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len, initial_boot_params);
>        if (!prop)
>                return;
>        start = of_read_ulong(prop, len/4);
>
> -       prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
> +       prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len, initial_boot_params);
>        if (!prop)
>                return;
>        end = of_read_ulong(prop, len/4);
> @@ -427,7 +435,7 @@ inline void early_init_dt_check_for_initrd(unsigned long node)
>  /**
>  * early_init_dt_scan_root - fetch the top level address and size cells
>  */
> -int __init early_init_dt_scan_root(unsigned long node, const char *uname,
> +int  early_init_dt_scan_root(unsigned long node, const char *uname,
>                                   int depth, void *data)
>  {
>        __be32 *prop;
> @@ -438,12 +446,12 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
>        dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
>        dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
>
> -       prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
> +       prop = of_get_flat_dt_prop(node, "#size-cells", NULL, initial_boot_params);
>        if (prop)
>                dt_root_size_cells = be32_to_cpup(prop);
>        pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);
>
> -       prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
> +       prop = of_get_flat_dt_prop(node, "#address-cells", NULL, initial_boot_params);
>        if (prop)
>                dt_root_addr_cells = be32_to_cpup(prop);
>        pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);
> @@ -452,7 +460,7 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
>        return 1;
>  }
>
> -u64 __init dt_mem_next_cell(int s, __be32 **cellp)
> +u64  dt_mem_next_cell(int s, __be32 **cellp)
>  {
>        __be32 *p = *cellp;
>
> @@ -463,10 +471,10 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
>  /**
>  * early_init_dt_scan_memory - Look for an parse memory nodes
>  */
> -int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
> +int  early_init_dt_scan_memory(unsigned long node, const char *uname,
>                                     int depth, void *data)
>  {
> -       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
> +       char *type = of_get_flat_dt_prop(node, "device_type", NULL, initial_boot_params);
>        __be32 *reg, *endp;
>        unsigned long l;
>
> @@ -481,9 +489,9 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
>        } else if (strcmp(type, "memory") != 0)
>                return 0;
>
> -       reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
> +       reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l, initial_boot_params);
>        if (reg == NULL)
> -               reg = of_get_flat_dt_prop(node, "reg", &l);
> +               reg = of_get_flat_dt_prop(node, "reg", &l, initial_boot_params);
>        if (reg == NULL)
>                return 0;
>
> @@ -509,7 +517,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
>        return 0;
>  }
>
> -int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
> +int  early_init_dt_scan_chosen(unsigned long node, const char *uname,
>                                     int depth, void *data)
>  {
>        unsigned long l;
> @@ -523,12 +531,12 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
>
>        early_init_dt_check_for_initrd(node);
>
> +#ifdef CONFIG_CMDLINE
>        /* Retreive command line */
> -       p = of_get_flat_dt_prop(node, "bootargs", &l);
> +       p = of_get_flat_dt_prop(node, "bootargs", &l, initial_boot_params);
>        if (p != NULL && l > 0)
>                strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE));
>
> -#ifdef CONFIG_CMDLINE
>  #ifndef CONFIG_CMDLINE_FORCE
>        if (p == NULL || l == 0 || (l == 1 && (*p) == 0))
>  #endif
> @@ -537,12 +545,14 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
>
>        early_init_dt_scan_chosen_arch(node);
>
> +#ifdef CONFIG_CMDLINE
>        pr_debug("Command line is: %s\n", cmd_line);
> +#endif /* CONFIG_CMDLINE */
>
>        /* break now */
>        return 1;
>  }
> -
> +
>  /**
>  * unflatten_device_tree - create tree of device_nodes from flat blob
>  *
> @@ -551,7 +561,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
>  * pointers of the nodes so the normal device-tree walking functions
>  * can be used.
>  */
> -void __init unflatten_device_tree(void)
> +void unflatten_device_tree(void)
>  {
>        unsigned long start, mem, size;
>        struct device_node **allnextp = &allnodes;
> @@ -576,7 +586,7 @@ void __init unflatten_device_tree(void)
>        /* First pass, scan for size */
>        start = ((unsigned long)initial_boot_params) +
>                be32_to_cpu(initial_boot_params->off_dt_struct);
> -       size = unflatten_dt_node(0, &start, NULL, NULL, 0);
> +       size = unflatten_dt_node(0, &start, NULL, NULL, 0, initial_boot_params);
>        size = (size | 3) + 1;
>
>        pr_debug("  size is %lx, allocating...\n", size);
> @@ -593,7 +603,7 @@ void __init unflatten_device_tree(void)
>        /* Second pass, do actual unflattening */
>        start = ((unsigned long)initial_boot_params) +
>                be32_to_cpu(initial_boot_params->off_dt_struct);
> -       unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
> +       unflatten_dt_node(mem, &start, NULL, &allnextp, 0, initial_boot_params);
>        if (be32_to_cpup((__be32 *)start) != OF_DT_END)
>                pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
>        if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
> @@ -608,3 +618,68 @@ void __init unflatten_device_tree(void)
>
>        pr_debug(" <- unflatten_device_tree()\n");
>  }
> +
> +#endif
> +
> +/**
> + * unflatten_partial_device_tree - create tree of device_nodes from flat blob
> + *
> + * unflattens the device-tree passed by the firmware, creating the
> + * tree of struct device_node. It also fills the "name" and "type"
> + * pointers of the nodes so the normal device-tree walking functions
> + * can be used.
> + */
> +void unflatten_partial_device_tree(unsigned long *blob, struct device_node **mynodes)
> +{
> +  struct boot_param_header *device_tree = (struct boot_param_header *)blob;
> +        unsigned long start, mem, size;
> +       struct device_node **allnextp = mynodes;
> +
> +       pr_debug(" -> unflatten_partial_device_tree()\n");
> +
> +       if (!device_tree) {
> +               pr_debug("No device tree pointer\n");
> +               return;
> +       }
> +
> +       pr_debug("Unflattening device tree:\n");
> +       pr_debug("magic: %08x\n", be32_to_cpu(device_tree->magic));
> +       pr_debug("size: %08x\n", be32_to_cpu(device_tree->totalsize));
> +       pr_debug("version: %08x\n", be32_to_cpu(device_tree->version));
> +
> +       if (be32_to_cpu(device_tree->magic) != OF_DT_HEADER) {
> +         pr_err("Invalid device tree blob header %x \n", be32_to_cpu(device_tree->magic));
> +               return;
> +       }
> +
> +       /* First pass, scan for size */
> +       start = ((unsigned long)device_tree) +
> +               be32_to_cpu(device_tree->off_dt_struct);
> +       size = unflatten_dt_node(0, &start, NULL, NULL, 0, device_tree);
> +       size = (size | 3) + 1;
> +
> +       pr_debug("  size is %lx, allocating...\n", size);
> +
> +       /* Allocate memory for the expanded device tree */
> +       mem = (unsigned long) kzalloc(size + 4,
> +                     __alignof__(struct device_node));
> +
> +       ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
> +
> +       pr_debug("  unflattening %lx...\n", mem);
> +
> +       /* Second pass, do actual unflattening */
> +       start = ((unsigned long)device_tree) +
> +               be32_to_cpu(device_tree->off_dt_struct);
> +       unflatten_dt_node(mem, &start, NULL, &allnextp, 0, device_tree);
> +       if (be32_to_cpup((__be32 *)start) != OF_DT_END)
> +               pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
> +       if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
> +               pr_warning("End of tree marker overwritten: %08x\n",
> +                          be32_to_cpu(((__be32 *)mem)[size / 4]));
> +       *allnextp = NULL;
> +
> +       pr_debug(" <- unflatten_partial_device_tree()\n");
> +}
> +
> +EXPORT_SYMBOL(unflatten_partial_device_tree);
> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
> index 71e1a91..008f520 100644
> --- a/include/linux/of_fdt.h
> +++ b/include/linux/of_fdt.h
> @@ -64,13 +64,16 @@ extern int __initdata dt_root_size_cells;
>  extern struct boot_param_header *initial_boot_params;
>
>  /* For scanning the flat device-tree at boot time */
> -extern char *find_flat_dt_string(u32 offset);
> +extern char *find_flat_dt_string(u32 offset,
> +                         struct boot_param_header *blob);
>  extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname,
>                                     int depth, void *data),
>                           void *data);
>  extern void *of_get_flat_dt_prop(unsigned long node, const char *name,
> -                                unsigned long *size);
> -extern int of_flat_dt_is_compatible(unsigned long node, const char *name);
> +                                unsigned long *size,
> +                         struct boot_param_header *blob);
> +extern int of_flat_dt_is_compatible(unsigned long node, const char *name,
> +                         struct boot_param_header *blob);
>  extern unsigned long of_get_flat_dt_root(void);
>  extern void early_init_dt_scan_chosen_arch(unsigned long node);
>  extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
> @@ -98,6 +101,7 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname,
>
>  /* Other Prototypes */
>  extern void unflatten_device_tree(void);
> +extern void unflatten_partial_device_tree(unsigned long *blob, struct device_node **mynodes);
>  extern void early_init_devtree(void *);
>  #else /* CONFIG_OF_FLATTREE */
>  static inline void unflatten_device_tree(void) {}
> --
> 1.5.6.6
>
>
>
> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
>
>
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.


More information about the devicetree-discuss mailing list