On Wed, 16 Jul 2014 23:09:39 -0700, Gaurav Minocha 
<[email protected]> wrote:
> This patch attaches selftest's device tree data (required by 
> /drivers/of/selftest.c)
> dynamically into live device tree. First, it links selftest device tree data 
> into the
> kernel image and then iterates over all the nodes and attaches them into the 
> live tree.
> Once the testcases are complete, it removes the data attached.
> 
> This patch will remove the manual process of addition and removal of selftest 
> device
> tree data into the machine's dts file. Also, it can be build as a loadable 
> kernel
> module by setting the config symbol OF_SELFTEST=m.
> 
> Tested successfully with current selftest's testcases.
> 
> Signed-off-by: Gaurav Minocha <[email protected]>

Merged, thanks. I've run tested on QEMU PowerPC pseries and ARM
Versatile Express models. One of the testcases fails on powerpc, but
that is a problem with the testcase itself, and not with this code.

g.

> ---
>  arch/arm/boot/dts/versatile-pb.dts                 |    2 -
>  drivers/of/Kconfig                                 |    3 +-
>  drivers/of/Makefile                                |    3 +-
>  drivers/of/base.c                                  |    5 +
>  drivers/of/platform.c                              |    1 +
>  drivers/of/selftest.c                              |  157 
> ++++++++++++++++++++
>  .../{testcases.dtsi => testcases.dts}              |    1 +
>  7 files changed, 168 insertions(+), 4 deletions(-)
>  rename drivers/of/testcase-data/{testcases.dtsi => testcases.dts} (92%)
> 
> diff --git a/arch/arm/boot/dts/versatile-pb.dts 
> b/arch/arm/boot/dts/versatile-pb.dts
> index 65f6577..8d39677 100644
> --- a/arch/arm/boot/dts/versatile-pb.dts
> +++ b/arch/arm/boot/dts/versatile-pb.dts
> @@ -46,5 +46,3 @@
>               };
>       };
>  };
> -
> -#include <testcases.dtsi>
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index 2dcb054..4e4f6f3 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -8,8 +8,9 @@ menu "Device Tree and Open Firmware support"
>       depends on OF
>  
>  config OF_SELFTEST
> -     bool "Device Tree Runtime self tests"
> +     tristate "Device Tree Runtime self tests"
>       depends on OF_IRQ
> +     select OF_DYNAMIC
>       help
>         This option builds in test cases for the device tree infrastructure
>         that are executed once at boot time, and the results dumped to the
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index 099b1fb..b9e753b 100644
> --- a/drivers/of/Makefile
> +++ b/drivers/of/Makefile
> @@ -5,7 +5,8 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
>  obj-$(CONFIG_OF_ADDRESS)  += address.o
>  obj-$(CONFIG_OF_IRQ)    += irq.o
>  obj-$(CONFIG_OF_NET) += of_net.o
> -obj-$(CONFIG_OF_SELFTEST) += selftest.o
> +obj-$(CONFIG_OF_SELFTEST) += of_selftest.o
> +of_selftest-objs := selftest.o testcase-data/testcases.dtb.o
>  obj-$(CONFIG_OF_MDIO)        += of_mdio.o
>  obj-$(CONFIG_OF_PCI) += of_pci.o
>  obj-$(CONFIG_OF_PCI_IRQ)  += of_pci_irq.o
> diff --git a/drivers/of/base.c b/drivers/of/base.c
> index b986480..b47c1d8 100644
> --- a/drivers/of/base.c
> +++ b/drivers/of/base.c
> @@ -1814,6 +1814,7 @@ int of_add_property(struct device_node *np, struct 
> property *prop)
>  
>       return rc;
>  }
> +EXPORT_SYMBOL(of_add_property);
>  
>  /**
>   * of_remove_property - Remove a property from a node.
> @@ -1860,6 +1861,7 @@ int of_remove_property(struct device_node *np, struct 
> property *prop)
>  
>       return 0;
>  }
> +EXPORT_SYMBOL(of_remove_property);
>  
>  /*
>   * of_update_property - Update a property in a node, if the property does
> @@ -1915,6 +1917,7 @@ int of_update_property(struct device_node *np, struct 
> property *newprop)
>  
>       return 0;
>  }
> +EXPORT_SYMBOL(of_update_property);
>  
>  #if defined(CONFIG_OF_DYNAMIC)
>  /*
> @@ -1970,6 +1973,7 @@ int of_attach_node(struct device_node *np)
>       of_node_add(np);
>       return 0;
>  }
> +EXPORT_SYMBOL(of_attach_node);
>  
>  /**
>   * of_detach_node - "Unplug" a node from the device tree.
> @@ -2029,6 +2033,7 @@ int of_detach_node(struct device_node *np)
>       of_node_remove(np);
>       return rc;
>  }
> +EXPORT_SYMBOL(of_detach_node);
>  #endif /* defined(CONFIG_OF_DYNAMIC) */
>  
>  static void of_alias_add(struct alias_prop *ap, struct device_node *np,
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index 500436f..b7a82d6 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -30,6 +30,7 @@ const struct of_device_id of_default_bus_match_table[] = {
>  #endif /* CONFIG_ARM_AMBA */
>       {} /* Empty terminated list */
>  };
> +EXPORT_SYMBOL(of_default_bus_match_table);
>  
>  static int of_dev_node_match(struct device *dev, void *data)
>  {
> diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
> index 077314e..3a1c5b5 100644
> --- a/drivers/of/selftest.c
> +++ b/drivers/of/selftest.c
> @@ -9,6 +9,7 @@
>  #include <linux/errno.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/of_fdt.h>
>  #include <linux/of_irq.h>
>  #include <linux/of_platform.h>
>  #include <linux/list.h>
> @@ -21,6 +22,10 @@ static struct selftest_results {
>       int failed;
>  } selftest_results;
>  
> +#define NO_OF_NODES 2
> +static struct device_node *nodes[NO_OF_NODES];
> +static int last_node_index;
> +
>  #define selftest(result, fmt, ...) { \
>       if (!(result)) { \
>               selftest_results.failed++; \
> @@ -517,9 +522,156 @@ static void __init of_selftest_platform_populate(void)
>       }
>  }
>  
> +/**
> + *   update_node_properties - adds the properties
> + *   of np into dup node (present in live tree) and
> + *   updates parent of children of np to dup.
> + *
> + *   @np:    node already present in live tree
> + *   @dup:   node present in live tree to be updated
> + */
> +static void update_node_properties(struct device_node *np,
> +                                     struct device_node *dup)
> +{
> +     struct property *prop;
> +     struct device_node *child;
> +
> +     for_each_property_of_node(np, prop)
> +             of_add_property(dup, prop);
> +
> +     for_each_child_of_node(np, child)
> +             child->parent = dup;
> +}
> +
> +/**
> + *   attach_node_and_children - attaches nodes
> + *   and its children to live tree
> + *
> + *   @np:    Node to attach to live tree
> + */
> +static int attach_node_and_children(struct device_node *np)
> +{
> +     struct device_node *next, *root = np, *dup;
> +
> +     if (!np) {
> +             pr_warn("%s: No tree to attach; not running tests\n",
> +                     __func__);
> +             return -ENODATA;
> +     }
> +
> +
> +     /* skip root node */
> +     np = np->child;
> +     /* storing a copy in temporary node */
> +     dup = np;
> +
> +     while (dup) {
> +             nodes[last_node_index++] = dup;
> +             dup = dup->sibling;
> +     }
> +     dup = NULL;
> +
> +     while (np) {
> +             next = np->allnext;
> +             dup = of_find_node_by_path(np->full_name);
> +             if (dup)
> +                     update_node_properties(np, dup);
> +             else {
> +                     np->child = NULL;
> +                     if (np->parent == root)
> +                             np->parent = of_allnodes;
> +                     of_attach_node(np);
> +             }
> +             np = next;
> +     }
> +
> +     return 0;
> +}
> +
> +/**
> + *   selftest_data_add - Reads, copies data from
> + *   linked tree and attaches it to the live tree
> + */
> +static int __init selftest_data_add(void)
> +{
> +     void *selftest_data;
> +     struct device_node *selftest_data_node;
> +     extern uint8_t __dtb_testcases_begin[];
> +     extern uint8_t __dtb_testcases_end[];
> +     const int size = __dtb_testcases_end - __dtb_testcases_begin;
> +
> +     if (!size) {
> +             pr_warn("%s: No testcase data to attach; not running tests\n",
> +                     __func__);
> +             return -ENODATA;
> +     }
> +
> +     /* creating copy */
> +     selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
> +
> +     if (!selftest_data) {
> +             pr_warn("%s: Failed to allocate memory for selftest_data; "
> +                     "not running tests\n", __func__);
> +             return -ENOMEM;
> +     }
> +     of_fdt_unflatten_tree(selftest_data, &selftest_data_node);
> +
> +     /* attach the sub-tree to live tree */
> +     return attach_node_and_children(selftest_data_node);
> +}
> +
> +/**
> + *   detach_node_and_children - detaches node
> + *   and its children from live tree
> + *
> + *   @np:    Node to detach from live tree
> + */
> +static void detach_node_and_children(struct device_node *np)
> +{
> +     while (np->child)
> +             detach_node_and_children(np->child);
> +
> +     while (np->sibling)
> +             detach_node_and_children(np->sibling);
> +
> +     of_detach_node(np);
> +}
> +
> +/**
> + *   selftest_data_remove - removes the selftest data
> + *   nodes from the live tree
> + */
> +static void selftest_data_remove(void)
> +{
> +     struct device_node *np;
> +     struct property *prop;
> +
> +     while (last_node_index >= 0) {
> +             if (nodes[last_node_index]) {
> +                     np = 
> of_find_node_by_path(nodes[last_node_index]->full_name);
> +                     if (strcmp(np->full_name, "/aliases") != 0) {
> +                             detach_node_and_children(np->child);
> +                             of_detach_node(np);
> +                     } else {
> +                             for_each_property_of_node(np, prop) {
> +                                     if (strcmp(prop->name, 
> "testcase-alias") == 0)
> +                                             of_remove_property(np, prop);
> +                             }
> +                     }
> +             }
> +             last_node_index--;
> +     }
> +}
> +
>  static int __init of_selftest(void)
>  {
>       struct device_node *np;
> +     int res;
> +
> +     /* adding data for selftest */
> +     res = selftest_data_add();
> +     if (res)
> +             return res;
>  
>       np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
>       if (!np) {
> @@ -539,6 +691,11 @@ static int __init of_selftest(void)
>       of_selftest_platform_populate();
>       pr_info("end of selftest - %i passed, %i failed\n",
>               selftest_results.passed, selftest_results.failed);
> +
> +     /* removing selftest data from live tree */
> +     selftest_data_remove();
> +
>       return 0;
>  }
>  late_initcall(of_selftest);
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/of/testcase-data/testcases.dtsi 
> b/drivers/of/testcase-data/testcases.dts
> similarity index 92%
> rename from drivers/of/testcase-data/testcases.dtsi
> rename to drivers/of/testcase-data/testcases.dts
> index 6d8d980a..8e7568e 100644
> --- a/drivers/of/testcase-data/testcases.dtsi
> +++ b/drivers/of/testcase-data/testcases.dts
> @@ -1,3 +1,4 @@
> +/dts-v1/;
>  #include "tests-phandle.dtsi"
>  #include "tests-interrupts.dtsi"
>  #include "tests-match.dtsi"
> -- 
> 1.7.9.5
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to