On Fri, Jul 16, 2010 at 12:13 PM, Stephen Neuendorffer
<[email protected]> 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.
>
> Signed-off-by: Stephen Neuendorffer <[email protected]>
> ---
>  drivers/of/fdt.c       |  169 +++++++++++++++++++++++++++++++++--------------
>  include/linux/of_fdt.h |   11 ++-
>  2 files changed, 126 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index d61fda8..89fb1f3 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,19 @@
>
>  #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;

The fact that these are still here looks wrong.  The code cannot
depend on global symbols, particularly global symbols that it
modified, when it is changed to support processing non-rooted trees.
More rework will be needed here.

>
>  struct boot_param_header *initial_boot_params;
>
> -char *find_flat_dt_string(u32 offset)
> +void __unflatten_device_tree(unsigned long *blob, struct device_node 
> **mynodes,
> +                            unsigned long (*dt_alloc)(u64 size, u64 align));
> +
> +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;
>  }
>
>  /**
> @@ -118,8 +124,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 * __init of_get_flat_dt_prop(unsigned long node, const char *name,
> +                                unsigned long *size,
> +                                struct boot_param_header *blob)
>  {
>        unsigned long p = node;
>
> @@ -140,7 +147,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;
> @@ -160,12 +167,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) {
> @@ -179,7 +187,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;
> @@ -198,11 +206,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 *p,
> -                                       struct device_node *dad,
> -                                       struct device_node ***allnextpp,
> -                                       unsigned long fpsize)
> +unsigned long unflatten_dt_node(unsigned long mem,
> +                               unsigned long *p,
> +                               struct device_node *dad,
> +                               struct device_node ***allnextpp,
> +                               unsigned long fpsize,
> +                               struct boot_param_header *blob)
>  {
>        struct device_node *np;
>        struct property *pp, **prev_pp = NULL;
> @@ -298,10 +307,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;
> @@ -380,7 +389,8 @@ 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) {
> @@ -391,6 +401,8 @@ unsigned long __init unflatten_dt_node(unsigned long mem,
>        return mem;
>  }
>
> +#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_POWERPC) || 
> defined(CONFIG_SPARC)
> +

Rather hacky.. There's got to be a better way.  Might be better to add
a new Kconfig symbol.

Also, the test is wrong.  ARM is going to want this.  OLPC x86 might
want it.  SPARC doesn't use it.


>  #ifdef CONFIG_BLK_DEV_INITRD
>  /**
>  * early_init_dt_check_for_initrd - Decode initrd location from flat tree
> @@ -403,12 +415,14 @@ void __init early_init_dt_check_for_initrd(unsigned 
> long node)
>
>        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);
> @@ -436,12 +450,14 @@ 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);

This is the update to the static globals I was mentioning that must not be done.

> @@ -450,7 +466,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;
>
> @@ -464,7 +480,8 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
>  int __init 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;
>
> @@ -479,9 +496,10 @@ 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;
>
> @@ -521,12 +539,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

Why is this being changed?

>  #ifndef CONFIG_CMDLINE_FORCE
>        if (p == NULL || l == 0 || (l == 1 && (*p) == 0))
>  #endif
> @@ -535,12 +553,21 @@ 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;
>  }
>
> +
> +static unsigned long early_device_tree_alloc(u64 size, u64 align)
> +{
> +       unsigned long mem = early_init_dt_alloc_memory_arch(size, align);
> +       return (unsigned long) __va(mem);
> +}
> +
>  /**
>  * unflatten_device_tree - create tree of device_nodes from flat blob
>  *
> @@ -551,58 +578,98 @@ int __init early_init_dt_scan_chosen(unsigned long 
> node, const char *uname,
>  */
>  void __init unflatten_device_tree(void)
>  {
> +       __unflatten_device_tree(initial_boot_params, &allnodes,
> +                               early_device_tree_alloc);
> +
> +       /* Get pointer to OF "/chosen" node for use everywhere */
> +       of_chosen = of_find_node_by_path("/chosen");
> +       if (of_chosen == NULL)
> +               of_chosen = of_find_node_by_path("/cho...@0");
> +}
> +
> +#endif
> +
> +static unsigned long kernel_tree_alloc(u64 size, u64 align)
> +{
> +       return (unsigned long) kzalloc(size, GFP_KERNEL);
> +}
> +
> +/**
> + * 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)
> +{
> +       __unflatten_device_tree(blob, mynodes, &kernel_tree_alloc);
> +}
> +EXPORT_SYMBOL(unflatten_partial_device_tree);

unflatten_partial_device_tree and unflatten_device_tree should be
defined *below* __unflatten_device_tree().

> +
> +/**
> + * __unflatten_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.  Memory is allocated using the given function.
> + */
> +void __unflatten_device_tree(unsigned long *blob, struct device_node 
> **mynodes,
> +                            unsigned long (*dt_alloc)(u64 size, u64 align))
> +{
> +       struct boot_param_header *device_tree =
> +               (struct boot_param_header *)blob;
>        unsigned long start, mem, size;
> -       struct device_node **allnextp = &allnodes;
> +       struct device_node **allnextp = mynodes;
>
>        pr_debug(" -> unflatten_device_tree()\n");
>
> -       if (!initial_boot_params) {
> +       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(initial_boot_params->magic));
> -       pr_debug("size: %08x\n", be32_to_cpu(initial_boot_params->totalsize));
> -       pr_debug("version: %08x\n", 
> be32_to_cpu(initial_boot_params->version));
> +       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(initial_boot_params->magic) != OF_DT_HEADER) {
> -               pr_err("Invalid device tree blob header\n");
> +       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)initial_boot_params) +
> -               be32_to_cpu(initial_boot_params->off_dt_struct);
> -       size = unflatten_dt_node(0, &start, NULL, NULL, 0);
> +       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 = early_init_dt_alloc_memory_arch(size + 4,
> -                       __alignof__(struct device_node));
> -       mem = (unsigned long) __va(mem);
> +       mem = (unsigned long) dt_alloc(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)initial_boot_params) +
> -               be32_to_cpu(initial_boot_params->off_dt_struct);
> -       unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
> +       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));
> +               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;
>
> -       /* Get pointer to OF "/chosen" node for use everywhere */
> -       of_chosen = of_find_node_by_path("/chosen");
> -       if (of_chosen == NULL)
> -               of_chosen = of_find_node_by_path("/cho...@0");
> -
>        pr_debug(" <- unflatten_device_tree()\n");
>  }
> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
> index 71e1a91..2bc0754 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);

Well, fdt.c is the only user of this.  I think this line can be removed.

>  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);

Need to fix this.  The prototypes for of_get_flat_dt_prop() and
of_flat_dt_is_compatible are changed, but there are users in the
microblaze and powerpc trees which are not updated.

>  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,8 @@ 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.
_______________________________________________
devicetree-discuss mailing list
[email protected]
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to