From e66c98c7a0eacc33a9369a3ec086740044eb986c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 1 Oct 2014 16:57:07 +0100 Subject: of: Fix NULL dereference in selftest removal code The selftest code removes its testcase data from the live tree when exiting, but if the testcases data tree contains an empty child of the root, then it causes an oops due to a NULL dereference. The reason is that the code tries to directly dereference the child pointer without checking first if a child is actually there. The solution is to pass the parent node into detach_node_and_children() instead of trying to pass the child. This required removing the code that attempts to remove all of the sibling nodes in detach_node_and_children(), which was never sensible in the first place. At the same time add a check to make sure the bounds of the nodes list are not exceeded by the testdata tree. If they are then abort. Signed-off-by: Grant Likely Cc: Gaurav Minocha --- drivers/of/selftest.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index a737cb5974de..883e60b04eb5 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -637,6 +637,8 @@ static int attach_node_and_children(struct device_node *np) dup = np; while (dup) { + if (WARN_ON(last_node_index >= NO_OF_NODES)) + return -EINVAL; nodes[last_node_index++] = dup; dup = dup->sibling; } @@ -717,10 +719,6 @@ 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); } @@ -749,8 +747,7 @@ static void selftest_data_remove(void) 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); + detach_node_and_children(np); } else { for_each_property_of_node(np, prop) { if (strcmp(prop->name, "testcase-alias") == 0) -- cgit v1.2.3 From f2051d6a88cd03f74221da887f56d778a1b2f1f1 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 1 Oct 2014 17:40:22 +0100 Subject: of/selftest: Test structure of device tree Add a testcase to verify that the device tree is properly constructed and the lists are in a correct order. The new testcase gets run twice; once after adding the testcase data, and once after removing it again. It is run twice to make sure adding and removing the testcase data doesn't corrupt the data structure. Signed-off-by: Grant Likely Cc: Gaurav Minocha --- drivers/of/selftest.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 883e60b04eb5..252a7eda8d6a 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -145,6 +145,53 @@ static void __init of_selftest_dynamic(void) "Adding a large property should have passed\n"); } +static int __init of_selftest_check_node_linkage(struct device_node *np) +{ + struct device_node *child, *allnext_index = np; + int count = 0, rc; + + for_each_child_of_node(np, child) { + if (child->parent != np) { + pr_err("Child node %s links to wrong parent %s\n", + child->name, np->name); + return -EINVAL; + } + + while (allnext_index && allnext_index != child) + allnext_index = allnext_index->allnext; + if (allnext_index != child) { + pr_err("Node %s is ordered differently in sibling and allnode lists\n", + child->name); + return -EINVAL; + } + + rc = of_selftest_check_node_linkage(child); + if (rc < 0) + return rc; + count += rc; + } + + return count + 1; +} + +static void __init of_selftest_check_tree_linkage(void) +{ + struct device_node *np; + int allnode_count = 0, child_count; + + if (!of_allnodes) + return; + + for_each_of_allnodes(np) + allnode_count++; + child_count = of_selftest_check_node_linkage(of_allnodes); + + selftest(child_count > 0, "Device node data structure is corrupted\n"); + selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match" + "sibling lists size (%i)\n", allnode_count, child_count); + pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count); +} + static void __init of_selftest_parse_phandle_with_args(void) { struct device_node *np; @@ -777,6 +824,7 @@ static int __init of_selftest(void) of_node_put(np); pr_info("start of selftest - you will see error messages\n"); + of_selftest_check_tree_linkage(); of_selftest_find_node_by_name(); of_selftest_dynamic(); of_selftest_parse_phandle_with_args(); @@ -787,12 +835,16 @@ static int __init of_selftest(void) of_selftest_parse_interrupts_extended(); of_selftest_match_node(); 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(); + /* Double check linkage after removing testcase data */ + of_selftest_check_tree_linkage(); + + pr_info("end of selftest - %i passed, %i failed\n", + selftest_results.passed, selftest_results.failed); + return 0; } late_initcall(of_selftest); -- cgit v1.2.3 From fc59b4479c172e413df615cea1635247265e07a0 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 2 Oct 2014 13:08:02 +0100 Subject: of: Don't try to search when phandle == 0 A value of '0' isn't a valid phandle, so searching for a node with that phandle is pointless. It will result in nothing but false positives. Signed-off-by: Grant Likely --- drivers/of/base.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/base.c b/drivers/of/base.c index 293ed4b687ba..2305dc0382bc 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1021,6 +1021,9 @@ struct device_node *of_find_node_by_phandle(phandle handle) struct device_node *np; unsigned long flags; + if (!handle) + return NULL; + raw_spin_lock_irqsave(&devtree_lock, flags); for (np = of_allnodes; np; np = np->allnext) if (np->phandle == handle) -- cgit v1.2.3 From 841ec21357eee222416e3b7f1b6ef23cfc6ee43f Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 2 Oct 2014 13:09:15 +0100 Subject: of/selftest: Add a test for duplicate phandles All phandles in the tree should be unique. Add a testcase to make sure that this is so. Note: this testcase fails on the current kernel because the selftest code itself ends up adding duplicate phandles. Before this testcase is merged the selftest code needs to be modified to resolve phandles before adding them. Signed-off-by: Grant Likely Cc: Pantelis Antoniou --- drivers/of/selftest.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 252a7eda8d6a..4f83e97f8788 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -192,6 +193,51 @@ static void __init of_selftest_check_tree_linkage(void) pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count); } +struct node_hash { + struct hlist_node node; + struct device_node *np; +}; + +static void __init of_selftest_check_phandles(void) +{ + struct device_node *np; + struct node_hash *nh; + struct hlist_node *tmp; + int i, dup_count = 0, phandle_count = 0; + DECLARE_HASHTABLE(ht, 8); + + hash_init(ht); + for_each_of_allnodes(np) { + if (!np->phandle) + continue; + + hash_for_each_possible(ht, nh, node, np->phandle) { + if (nh->np->phandle == np->phandle) { + pr_info("Duplicate phandle! %i used by %s and %s\n", + np->phandle, nh->np->full_name, np->full_name); + dup_count++; + break; + } + } + + nh = kzalloc(sizeof(*nh), GFP_KERNEL); + if (WARN_ON(!nh)) + return; + + nh->np = np; + hash_add(ht, &nh->node, np->phandle); + phandle_count++; + } + selftest(dup_count == 0, "Found %i duplicates in %i phandles\n", + dup_count, phandle_count); + + /* Clean up */ + hash_for_each_safe(ht, i, tmp, nh, node) { + hash_del(&nh->node); + kfree(nh); + } +} + static void __init of_selftest_parse_phandle_with_args(void) { struct device_node *np; @@ -825,6 +871,7 @@ static int __init of_selftest(void) pr_info("start of selftest - you will see error messages\n"); of_selftest_check_tree_linkage(); + of_selftest_check_phandles(); of_selftest_find_node_by_name(); of_selftest_dynamic(); of_selftest_parse_phandle_with_args(); -- cgit v1.2.3 From 7941b27b16e3282f6ec8817e36492f1deec753a7 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:59:20 +0300 Subject: of: Introduce Device Tree resolve support. Introduce support for dynamic device tree resolution. Using it, it is possible to prepare a device tree that's been loaded on runtime to be modified and inserted at the kernel live tree. Export of of_resolve and bug fix of double free by Guenter Roeck Signed-off-by: Pantelis Antoniou [grant.likely: Don't need to select CONFIG_OF_DYNAMIC and CONFIG_OF_DEVICE] [grant.likely: Don't need to depend on OF or !SPARC] [grant.likely: Factor out duplicate code blocks into single function] Signed-off-by: Grant Likely --- .../devicetree/dynamic-resolution-notes.txt | 25 ++ drivers/of/Kconfig | 3 + drivers/of/Makefile | 1 + drivers/of/resolver.c | 336 +++++++++++++++++++++ include/linux/of.h | 3 + 5 files changed, 368 insertions(+) create mode 100644 Documentation/devicetree/dynamic-resolution-notes.txt create mode 100644 drivers/of/resolver.c (limited to 'drivers/of') diff --git a/Documentation/devicetree/dynamic-resolution-notes.txt b/Documentation/devicetree/dynamic-resolution-notes.txt new file mode 100644 index 000000000000..083d23262abe --- /dev/null +++ b/Documentation/devicetree/dynamic-resolution-notes.txt @@ -0,0 +1,25 @@ +Device Tree Dynamic Resolver Notes +---------------------------------- + +This document describes the implementation of the in-kernel +Device Tree resolver, residing in drivers/of/resolver.c and is a +companion document to Documentation/devicetree/dt-object-internal.txt[1] + +How the resolver works +---------------------- + +The resolver is given as an input an arbitrary tree compiled with the +proper dtc option and having a /plugin/ tag. This generates the +appropriate __fixups__ & __local_fixups__ nodes as described in [1]. + +In sequence the resolver works by the following steps: + +1. Get the maximum device tree phandle value from the live tree + 1. +2. Adjust all the local phandles of the tree to resolve by that amount. +3. Using the __local__fixups__ node information adjust all local references + by the same amount. +4. For each property in the __fixups__ node locate the node it references + in the live tree. This is the label used to tag the node. +5. Retrieve the phandle of the target of the fixup. +6. For each fixup in the property locate the node:property:offset location + and replace it with the phandle value. diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 5160c4eb73c2..6b81a36f6420 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -79,4 +79,7 @@ config OF_RESERVED_MEM help Helpers to allow for reservation of memory regions +config OF_RESOLVE + bool + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 2b6a7b129d10..ca9209ce50cd 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o +obj-$(CONFIG_OF_RESOLVE) += resolver.o CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c new file mode 100644 index 000000000000..aed7959f800d --- /dev/null +++ b/drivers/of/resolver.c @@ -0,0 +1,336 @@ +/* + * Functions for dealing with DT resolution + * + * Copyright (C) 2012 Pantelis Antoniou + * Copyright (C) 2012 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* illegal phandle value (set when unresolved) */ +#define OF_PHANDLE_ILLEGAL 0xdeadbeef + +/** + * Find a node with the give full name by recursively following any of + * the child node links. + */ +static struct device_node *__of_find_node_by_full_name(struct device_node *node, + const char *full_name) +{ + struct device_node *child, *found; + + if (node == NULL) + return NULL; + + /* check */ + if (of_node_cmp(node->full_name, full_name) == 0) + return node; + + for_each_child_of_node(node, child) { + found = __of_find_node_by_full_name(child, full_name); + if (found != NULL) + return found; + } + + return NULL; +} + +/* + * Find live tree's maximum phandle value. + */ +static phandle of_get_tree_max_phandle(void) +{ + struct device_node *node; + phandle phandle; + unsigned long flags; + + /* now search recursively */ + raw_spin_lock_irqsave(&devtree_lock, flags); + phandle = 0; + for_each_of_allnodes(node) { + if (node->phandle != OF_PHANDLE_ILLEGAL && + node->phandle > phandle) + phandle = node->phandle; + } + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + return phandle; +} + +/* + * Adjust a subtree's phandle values by a given delta. + * Makes sure not to just adjust the device node's phandle value, + * but modify the phandle properties values as well. + */ +static void __of_adjust_tree_phandles(struct device_node *node, + int phandle_delta) +{ + struct device_node *child; + struct property *prop; + phandle phandle; + + /* first adjust the node's phandle direct value */ + if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL) + node->phandle += phandle_delta; + + /* now adjust phandle & linux,phandle values */ + for_each_property_of_node(node, prop) { + + /* only look for these two */ + if (of_prop_cmp(prop->name, "phandle") != 0 && + of_prop_cmp(prop->name, "linux,phandle") != 0) + continue; + + /* must be big enough */ + if (prop->length < 4) + continue; + + /* read phandle value */ + phandle = be32_to_cpup(prop->value); + if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */ + continue; + + /* adjust */ + *(uint32_t *)prop->value = cpu_to_be32(node->phandle); + } + + /* now do the children recursively */ + for_each_child_of_node(node, child) + __of_adjust_tree_phandles(child, phandle_delta); +} + +static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta) +{ + phandle phandle; + struct device_node *refnode; + struct property *sprop; + char *propval, *propcur, *propend, *nodestr, *propstr, *s; + int offset, propcurlen; + int err = 0; + + /* make a copy */ + propval = kmalloc(rprop->length, GFP_KERNEL); + if (!propval) { + pr_err("%s: Could not copy value of '%s'\n", + __func__, rprop->name); + return -ENOMEM; + } + memcpy(propval, rprop->value, rprop->length); + + propend = propval + rprop->length; + for (propcur = propval; propcur < propend; propcur += propcurlen + 1) { + propcurlen = strlen(propcur); + + nodestr = propcur; + s = strchr(propcur, ':'); + if (!s) { + pr_err("%s: Illegal symbol entry '%s' (1)\n", + __func__, propcur); + err = -EINVAL; + goto err_fail; + } + *s++ = '\0'; + + propstr = s; + s = strchr(s, ':'); + if (!s) { + pr_err("%s: Illegal symbol entry '%s' (2)\n", + __func__, (char *)rprop->value); + err = -EINVAL; + goto err_fail; + } + + *s++ = '\0'; + err = kstrtoint(s, 10, &offset); + if (err != 0) { + pr_err("%s: Could get offset '%s'\n", + __func__, (char *)rprop->value); + goto err_fail; + } + + /* look into the resolve node for the full path */ + refnode = __of_find_node_by_full_name(node, nodestr); + if (!refnode) { + pr_warn("%s: Could not find refnode '%s'\n", + __func__, (char *)rprop->value); + continue; + } + + /* now find the property */ + for_each_property_of_node(refnode, sprop) { + if (of_prop_cmp(sprop->name, propstr) == 0) + break; + } + + if (!sprop) { + pr_err("%s: Could not find property '%s'\n", + __func__, (char *)rprop->value); + err = -ENOENT; + goto err_fail; + } + + phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value; + *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle); + } + +err_fail: + kfree(propval); + return err; +} + +/* + * Adjust the local phandle references by the given phandle delta. + * Assumes the existances of a __local_fixups__ node at the root + * of the tree. Does not take any devtree locks so make sure you + * call this on a tree which is at the detached state. + */ +static int __of_adjust_tree_phandle_references(struct device_node *node, + int phandle_delta) +{ + struct device_node *child; + struct property *rprop; + int err; + + /* locate the symbols & fixups nodes on resolve */ + for_each_child_of_node(node, child) + if (of_node_cmp(child->name, "__local_fixups__") == 0) + break; + + /* no local fixups */ + if (!child) + return 0; + + /* find the local fixups property */ + for_each_property_of_node(child, rprop) { + /* skip properties added automatically */ + if (of_prop_cmp(rprop->name, "name") == 0) + continue; + + err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true); + if (err) + return err; + } + + return 0; +} + +/** + * of_resolve - Resolve the given node against the live tree. + * + * @resolve: Node to resolve + * + * Perform dynamic Device Tree resolution against the live tree + * to the given node to resolve. This depends on the live tree + * having a __symbols__ node, and the resolve node the __fixups__ & + * __local_fixups__ nodes (if needed). + * The result of the operation is a resolve node that it's contents + * are fit to be inserted or operate upon the live tree. + * Returns 0 on success or a negative error value on error. + */ +int of_resolve_phandles(struct device_node *resolve) +{ + struct device_node *child, *refnode; + struct device_node *root_sym, *resolve_sym, *resolve_fix; + struct property *rprop; + const char *refpath; + phandle phandle, phandle_delta; + int err; + + /* the resolve node must exist, and be detached */ + if (!resolve || !of_node_check_flag(resolve, OF_DETACHED)) + return -EINVAL; + + /* first we need to adjust the phandles */ + phandle_delta = of_get_tree_max_phandle() + 1; + __of_adjust_tree_phandles(resolve, phandle_delta); + err = __of_adjust_tree_phandle_references(resolve, phandle_delta); + if (err != 0) + return err; + + root_sym = NULL; + resolve_sym = NULL; + resolve_fix = NULL; + + /* this may fail (if no fixups are required) */ + root_sym = of_find_node_by_path("/__symbols__"); + + /* locate the symbols & fixups nodes on resolve */ + for_each_child_of_node(resolve, child) { + + if (!resolve_sym && + of_node_cmp(child->name, "__symbols__") == 0) + resolve_sym = child; + + if (!resolve_fix && + of_node_cmp(child->name, "__fixups__") == 0) + resolve_fix = child; + + /* both found, don't bother anymore */ + if (resolve_sym && resolve_fix) + break; + } + + /* we do allow for the case where no fixups are needed */ + if (!resolve_fix) { + err = 0; /* no error */ + goto out; + } + + /* we need to fixup, but no root symbols... */ + if (!root_sym) { + err = -EINVAL; + goto out; + } + + for_each_property_of_node(resolve_fix, rprop) { + + /* skip properties added automatically */ + if (of_prop_cmp(rprop->name, "name") == 0) + continue; + + err = of_property_read_string(root_sym, + rprop->name, &refpath); + if (err != 0) { + pr_err("%s: Could not find symbol '%s'\n", + __func__, rprop->name); + goto out; + } + + refnode = of_find_node_by_path(refpath); + if (!refnode) { + pr_err("%s: Could not find node by path '%s'\n", + __func__, refpath); + err = -ENOENT; + goto out; + } + + phandle = refnode->phandle; + of_node_put(refnode); + + pr_debug("%s: %s phandle is 0x%08x\n", + __func__, rprop->name, phandle); + + err = __of_adjust_phandle_ref(resolve, rprop, phandle, false); + if (err) + break; + } + +out: + /* NULL is handled by of_node_put as NOP */ + of_node_put(root_sym); + + return err; +} +EXPORT_SYMBOL_GPL(of_resolve_phandles); diff --git a/include/linux/of.h b/include/linux/of.h index 6c4363b8ddc3..6545e7aec7bb 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -863,4 +863,7 @@ static inline int of_changeset_update_property(struct of_changeset *ocs, } #endif +/* CONFIG_OF_RESOLVE api */ +extern int of_resolve_phandles(struct device_node *tree); + #endif /* _LINUX_OF_H */ -- cgit v1.2.3 From 2eb46da2a760e5764c48b752a5ef320e02b96b21 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 2 Oct 2014 14:36:46 +0100 Subject: of/selftest: Use the resolver to fixup phandles The selftest data ends up causing duplicate phandles in the live tree for the time that the testcase data is inserted into the live tree. This is obviously a bad situation because anything attempting to read the tree while the selftests are running make resolve phandles to one of the testcase data nodes. Fix the problem by using the of_resolve_phandles() function to eliminate duplicates. Signed-off-by: Grant Likely Cc: Pantelis Antoniou Cc: Gaurav Minocha --- drivers/of/Kconfig | 1 + drivers/of/selftest.c | 9 ++++++++- drivers/of/testcase-data/testcases.dts | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers/of') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 6b81a36f6420..1a13f5b722c5 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -11,6 +11,7 @@ config OF_SELFTEST bool "Device Tree Runtime self tests" depends on OF_IRQ && OF_EARLY_FLATTREE select OF_DYNAMIC + select OF_RESOLVE 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/selftest.c b/drivers/of/selftest.c index 4f83e97f8788..4fed34bff5cf 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -25,7 +25,7 @@ static struct selftest_results { int failed; } selftest_results; -#define NO_OF_NODES 2 +#define NO_OF_NODES 3 static struct device_node *nodes[NO_OF_NODES]; static int last_node_index; static bool selftest_live_tree; @@ -765,6 +765,7 @@ static int __init selftest_data_add(void) extern uint8_t __dtb_testcases_begin[]; extern uint8_t __dtb_testcases_end[]; const int size = __dtb_testcases_end - __dtb_testcases_begin; + int rc; if (!size) { pr_warn("%s: No testcase data to attach; not running tests\n", @@ -785,6 +786,12 @@ static int __init selftest_data_add(void) pr_warn("%s: No tree to attach; not running tests\n", __func__); return -ENODATA; } + of_node_set_flag(selftest_data_node, OF_DETACHED); + rc = of_resolve_phandles(selftest_data_node); + if (rc) { + pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc); + return -EINVAL; + } if (!of_allnodes) { /* enabling flag for removing nodes */ diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/testcase-data/testcases.dts index 219ef9324e9c..6994e15c24bf 100644 --- a/drivers/of/testcase-data/testcases.dts +++ b/drivers/of/testcase-data/testcases.dts @@ -13,3 +13,38 @@ #include "tests-interrupts.dtsi" #include "tests-match.dtsi" #include "tests-platform.dtsi" + +/* + * phandle fixup data - generated by dtc patches that aren't upstream. + * This data must be regenerated whenever phandle references are modified in + * the testdata tree. + * + * The format of this data may be subject to change. For the time being consider + * this a kernel-internal data format. + */ +/ { __local_fixups__ { + fixup = "/testcase-data/testcase-device2:interrupt-parent:0", + "/testcase-data/testcase-device1:interrupt-parent:0", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8", + "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0", + "/testcase-data/interrupts/interrupts1:interrupt-parent:0", + "/testcase-data/interrupts/interrupts0:interrupt-parent:0", + "/testcase-data/interrupts/intmap1:interrupt-map:12", + "/testcase-data/interrupts/intmap0:interrupt-map:52", + "/testcase-data/interrupts/intmap0:interrupt-map:36", + "/testcase-data/interrupts/intmap0:interrupt-map:16", + "/testcase-data/interrupts/intmap0:interrupt-map:4", + "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12", + "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0", + "/testcase-data/phandle-tests/consumer-a:phandle-list:56", + "/testcase-data/phandle-tests/consumer-a:phandle-list:52", + "/testcase-data/phandle-tests/consumer-a:phandle-list:40", + "/testcase-data/phandle-tests/consumer-a:phandle-list:24", + "/testcase-data/phandle-tests/consumer-a:phandle-list:8", + "/testcase-data/phandle-tests/consumer-a:phandle-list:0"; +}; }; -- cgit v1.2.3 From 2118f4b8dfc666c3e4a9e262beca79636a0852fe Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 7 Oct 2014 11:30:31 +0100 Subject: of/selftest: Move hash table off stack to fix large frame size The new testcase that checks phandle consistency was using a hash table on the stack which made the frame size much large than it should be. Fix the problem by moving the hash table into the file scope. Signed-off-by: Grant Likely --- drivers/of/selftest.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/of') diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 4fed34bff5cf..78001270a598 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -198,20 +198,19 @@ struct node_hash { struct device_node *np; }; +static DEFINE_HASHTABLE(phandle_ht, 8); static void __init of_selftest_check_phandles(void) { struct device_node *np; struct node_hash *nh; struct hlist_node *tmp; int i, dup_count = 0, phandle_count = 0; - DECLARE_HASHTABLE(ht, 8); - hash_init(ht); for_each_of_allnodes(np) { if (!np->phandle) continue; - hash_for_each_possible(ht, nh, node, np->phandle) { + hash_for_each_possible(phandle_ht, nh, node, np->phandle) { if (nh->np->phandle == np->phandle) { pr_info("Duplicate phandle! %i used by %s and %s\n", np->phandle, nh->np->full_name, np->full_name); @@ -225,14 +224,14 @@ static void __init of_selftest_check_phandles(void) return; nh->np = np; - hash_add(ht, &nh->node, np->phandle); + hash_add(phandle_ht, &nh->node, np->phandle); phandle_count++; } selftest(dup_count == 0, "Found %i duplicates in %i phandles\n", dup_count, phandle_count); /* Clean up */ - hash_for_each_safe(ht, i, tmp, nh, node) { + hash_for_each_safe(phandle_ht, i, tmp, nh, node) { hash_del(&nh->node); kfree(nh); } -- cgit v1.2.3