summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2018-02-09 13:42:38 +1100
committerStewart Smith <stewart@linux.vnet.ibm.com>2018-02-13 01:30:10 -0600
commit55c13bd1231a51e0109eeadc17cbbf46fa649f02 (patch)
treec9c1842251a1cc34f016a85391664ae2121f7d4f
parentb94fbeaf137c3981976699ef5dcc8cf95088413a (diff)
downloadtalos-skiboot-55c13bd1231a51e0109eeadc17cbbf46fa649f02.tar.gz
talos-skiboot-55c13bd1231a51e0109eeadc17cbbf46fa649f02.zip
core/device.c: Fix dt_find_compatible_node
dt_find_compatible_node() and dt_find_compatible_node_on_chip() are used to find device nodes under a parent/root node with a given compatible property. dt_next(root, prev) is used to walk the child nodes of the given parent and takes two arguments - root contains the parent node to walk whilst prev contains the previous child to search from so that it can be used as an iterator over all children nodes. The first iteration of dt_find_compatible_node(root, prev) calls dt_next(root, root) which is not a well defined operation as prev is assumed to be child of the root node. The result is that when a node contains no children it will start returning the parent nodes siblings until it hits the top of the tree at which point a NULL derefence is attempted when looking for the root nodes parent. Dereferencing NULL can result in undesirable data exceptions during system boot and untimely non-hilarious system crashes. dt_next() should not be called with prev == root. Instead we add a check to dt_next() such that passing prev = NULL will cause it to start iterating from the first child node (if any). Also add a unit test for this case to run-device.c. Signed-off-by: Alistair Popple <alistair@popple.id.au> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r--core/device.c19
-rw-r--r--core/test/run-device.c31
2 files changed, 39 insertions, 11 deletions
diff --git a/core/device.c b/core/device.c
index fc1db568..50fbb186 100644
--- a/core/device.c
+++ b/core/device.c
@@ -608,6 +608,15 @@ struct dt_node *dt_first(const struct dt_node *root)
struct dt_node *dt_next(const struct dt_node *root,
const struct dt_node *prev)
{
+ if (!prev) {
+ struct dt_node *first = dt_first(root);
+
+ if (!first)
+ return NULL;
+ else
+ return first;
+ }
+
/* Children? */
if (!list_empty(&prev->children))
return dt_first(prev);
@@ -719,10 +728,9 @@ struct dt_node *dt_find_compatible_node(struct dt_node *root,
struct dt_node *prev,
const char *compat)
{
- struct dt_node *node;
+ struct dt_node *node = prev;
- node = prev ? dt_next(root, prev) : root;
- for (; node; node = dt_next(root, node))
+ while ((node = dt_next(root, node)))
if (dt_node_is_compatible(node, compat))
return node;
return NULL;
@@ -964,10 +972,9 @@ struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
const char *compat,
uint32_t chip_id)
{
- struct dt_node *node;
+ struct dt_node *node = prev;
- node = prev ? dt_next(root, prev) : root;
- for (; node; node = dt_next(root, node)) {
+ while ((node = dt_next(root, node))) {
u32 cid = __dt_get_chip_id(node);
if (cid == chip_id &&
dt_node_is_compatible(node, compat))
diff --git a/core/test/run-device.c b/core/test/run-device.c
index 326be3ff..c3e46793 100644
--- a/core/test/run-device.c
+++ b/core/test/run-device.c
@@ -323,14 +323,27 @@ int main(void)
gc1 = dt_new(c1, "coprocessor1");
dt_add_property_strings(gc1, "compatible",
"specific-fake-coprocessor");
+ gc2 = dt_new(gc1, "coprocessor2");
+ dt_add_property_strings(gc2, "compatible",
+ "specific-fake-coprocessor");
+ gc3 = dt_new(c1, "coprocessor3");
+ dt_add_property_strings(gc3, "compatible",
+ "specific-fake-coprocessor");
- gc2 = dt_new(c1, "node-without-compatible");
- assert(__dt_find_property(gc2, "compatible") == NULL);
- assert(!dt_node_is_compatible(gc2, "any-property"));
assert(dt_find_compatible_node(root, NULL, "generic-fake-bus") == c2);
assert(dt_find_compatible_node(root, c2, "generic-fake-bus") == NULL);
+ /* we can find all compatible nodes */
+ assert(dt_find_compatible_node(c1, NULL, "specific-fake-coprocessor") == gc1);
+ assert(dt_find_compatible_node(c1, gc1, "specific-fake-coprocessor") == gc2);
+ assert(dt_find_compatible_node(c1, gc2, "specific-fake-coprocessor") == gc3);
+ assert(dt_find_compatible_node(c1, gc3, "specific-fake-coprocessor") == NULL);
+ assert(dt_find_compatible_node(root, NULL, "specific-fake-coprocessor") == gc1);
+ assert(dt_find_compatible_node(root, gc1, "specific-fake-coprocessor") == gc2);
+ assert(dt_find_compatible_node(root, gc2, "specific-fake-coprocessor") == gc3);
+ assert(dt_find_compatible_node(root, gc3, "specific-fake-coprocessor") == NULL);
+
/* we can find the coprocessor once on the cpu */
assert(dt_find_compatible_node_on_chip(root,
NULL,
@@ -339,6 +352,14 @@ int main(void)
assert(dt_find_compatible_node_on_chip(root,
gc1,
"specific-fake-coprocessor",
+ 0xcafe) == gc2);
+ assert(dt_find_compatible_node_on_chip(root,
+ gc2,
+ "specific-fake-coprocessor",
+ 0xcafe) == gc3);
+ assert(dt_find_compatible_node_on_chip(root,
+ gc3,
+ "specific-fake-coprocessor",
0xcafe) == NULL);
/* we can't find the coprocessor on the bus */
@@ -349,9 +370,9 @@ int main(void)
/* Test phandles. We override the automatically generated one. */
phandle = 0xf00;
- dt_add_property(gc2, "phandle", (const void *)&phandle, 4);
+ dt_add_property(gc3, "phandle", (const void *)&phandle, 4);
assert(last_phandle == 0xf00);
- assert(dt_find_by_phandle(root, 0xf00) == gc2);
+ assert(dt_find_by_phandle(root, 0xf00) == gc3);
assert(dt_find_by_phandle(root, 0xf0f) == NULL);
dt_free(root);
OpenPOWER on IntegriCloud