|
@@ -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 || !of_allnodes) {
|
|
|
+ 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,10 @@ 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);
|