aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/selftest.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/selftest.c')
-rw-r--r--drivers/of/selftest.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index 077314eebb95..d41002667833 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -9,6 +9,7 @@
9#include <linux/errno.h> 9#include <linux/errno.h>
10#include <linux/module.h> 10#include <linux/module.h>
11#include <linux/of.h> 11#include <linux/of.h>
12#include <linux/of_fdt.h>
12#include <linux/of_irq.h> 13#include <linux/of_irq.h>
13#include <linux/of_platform.h> 14#include <linux/of_platform.h>
14#include <linux/list.h> 15#include <linux/list.h>
@@ -16,11 +17,17 @@
16#include <linux/slab.h> 17#include <linux/slab.h>
17#include <linux/device.h> 18#include <linux/device.h>
18 19
20#include "of_private.h"
21
19static struct selftest_results { 22static struct selftest_results {
20 int passed; 23 int passed;
21 int failed; 24 int failed;
22} selftest_results; 25} selftest_results;
23 26
27#define NO_OF_NODES 2
28static struct device_node *nodes[NO_OF_NODES];
29static int last_node_index;
30
24#define selftest(result, fmt, ...) { \ 31#define selftest(result, fmt, ...) { \
25 if (!(result)) { \ 32 if (!(result)) { \
26 selftest_results.failed++; \ 33 selftest_results.failed++; \
@@ -266,6 +273,81 @@ static void __init of_selftest_property_match_string(void)
266 selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); 273 selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
267} 274}
268 275
276#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
277 (p1)->value && (p2)->value && \
278 !memcmp((p1)->value, (p2)->value, (p1)->length) && \
279 !strcmp((p1)->name, (p2)->name))
280static void __init of_selftest_property_copy(void)
281{
282#ifdef CONFIG_OF_DYNAMIC
283 struct property p1 = { .name = "p1", .length = 0, .value = "" };
284 struct property p2 = { .name = "p2", .length = 5, .value = "abcd" };
285 struct property *new;
286
287 new = __of_prop_dup(&p1, GFP_KERNEL);
288 selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
289 kfree(new->value);
290 kfree(new->name);
291 kfree(new);
292
293 new = __of_prop_dup(&p2, GFP_KERNEL);
294 selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
295 kfree(new->value);
296 kfree(new->name);
297 kfree(new);
298#endif
299}
300
301static void __init of_selftest_changeset(void)
302{
303#ifdef CONFIG_OF_DYNAMIC
304 struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
305 struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
306 struct property *ppremove;
307 struct device_node *n1, *n2, *n21, *nremove, *parent;
308 struct of_changeset chgset;
309
310 of_changeset_init(&chgset);
311 n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL);
312 selftest(n1, "testcase setup failure\n");
313 n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL);
314 selftest(n2, "testcase setup failure\n");
315 n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL);
316 selftest(n21, "testcase setup failure %p\n", n21);
317 nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
318 selftest(nremove, "testcase setup failure\n");
319 ppadd = __of_prop_dup(&padd, GFP_KERNEL);
320 selftest(ppadd, "testcase setup failure\n");
321 ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL);
322 selftest(ppupdate, "testcase setup failure\n");
323 parent = nremove->parent;
324 n1->parent = parent;
325 n2->parent = parent;
326 n21->parent = n2;
327 n2->child = n21;
328 ppremove = of_find_property(parent, "prop-remove", NULL);
329 selftest(ppremove, "failed to find removal prop");
330
331 of_changeset_init(&chgset);
332 selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
333 selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
334 selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
335 selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
336 selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
337 selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
338 selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
339 mutex_lock(&of_mutex);
340 selftest(!of_changeset_apply(&chgset), "apply failed\n");
341 mutex_unlock(&of_mutex);
342
343 mutex_lock(&of_mutex);
344 selftest(!of_changeset_revert(&chgset), "revert failed\n");
345 mutex_unlock(&of_mutex);
346
347 of_changeset_destroy(&chgset);
348#endif
349}
350
269static void __init of_selftest_parse_interrupts(void) 351static void __init of_selftest_parse_interrupts(void)
270{ 352{
271 struct device_node *np; 353 struct device_node *np;
@@ -517,9 +599,156 @@ static void __init of_selftest_platform_populate(void)
517 } 599 }
518} 600}
519 601
602/**
603 * update_node_properties - adds the properties
604 * of np into dup node (present in live tree) and
605 * updates parent of children of np to dup.
606 *
607 * @np: node already present in live tree
608 * @dup: node present in live tree to be updated
609 */
610static void update_node_properties(struct device_node *np,
611 struct device_node *dup)
612{
613 struct property *prop;
614 struct device_node *child;
615
616 for_each_property_of_node(np, prop)
617 of_add_property(dup, prop);
618
619 for_each_child_of_node(np, child)
620 child->parent = dup;
621}
622
623/**
624 * attach_node_and_children - attaches nodes
625 * and its children to live tree
626 *
627 * @np: Node to attach to live tree
628 */
629static int attach_node_and_children(struct device_node *np)
630{
631 struct device_node *next, *root = np, *dup;
632
633 if (!np) {
634 pr_warn("%s: No tree to attach; not running tests\n",
635 __func__);
636 return -ENODATA;
637 }
638
639
640 /* skip root node */
641 np = np->child;
642 /* storing a copy in temporary node */
643 dup = np;
644
645 while (dup) {
646 nodes[last_node_index++] = dup;
647 dup = dup->sibling;
648 }
649 dup = NULL;
650
651 while (np) {
652 next = np->allnext;
653 dup = of_find_node_by_path(np->full_name);
654 if (dup)
655 update_node_properties(np, dup);
656 else {
657 np->child = NULL;
658 if (np->parent == root)
659 np->parent = of_allnodes;
660 of_attach_node(np);
661 }
662 np = next;
663 }
664
665 return 0;
666}
667
668/**
669 * selftest_data_add - Reads, copies data from
670 * linked tree and attaches it to the live tree
671 */
672static int __init selftest_data_add(void)
673{
674 void *selftest_data;
675 struct device_node *selftest_data_node;
676 extern uint8_t __dtb_testcases_begin[];
677 extern uint8_t __dtb_testcases_end[];
678 const int size = __dtb_testcases_end - __dtb_testcases_begin;
679
680 if (!size || !of_allnodes) {
681 pr_warn("%s: No testcase data to attach; not running tests\n",
682 __func__);
683 return -ENODATA;
684 }
685
686 /* creating copy */
687 selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL);
688
689 if (!selftest_data) {
690 pr_warn("%s: Failed to allocate memory for selftest_data; "
691 "not running tests\n", __func__);
692 return -ENOMEM;
693 }
694 of_fdt_unflatten_tree(selftest_data, &selftest_data_node);
695
696 /* attach the sub-tree to live tree */
697 return attach_node_and_children(selftest_data_node);
698}
699
700/**
701 * detach_node_and_children - detaches node
702 * and its children from live tree
703 *
704 * @np: Node to detach from live tree
705 */
706static void detach_node_and_children(struct device_node *np)
707{
708 while (np->child)
709 detach_node_and_children(np->child);
710
711 while (np->sibling)
712 detach_node_and_children(np->sibling);
713
714 of_detach_node(np);
715}
716
717/**
718 * selftest_data_remove - removes the selftest data
719 * nodes from the live tree
720 */
721static void selftest_data_remove(void)
722{
723 struct device_node *np;
724 struct property *prop;
725
726 while (last_node_index >= 0) {
727 if (nodes[last_node_index]) {
728 np = of_find_node_by_path(nodes[last_node_index]->full_name);
729 if (strcmp(np->full_name, "/aliases") != 0) {
730 detach_node_and_children(np->child);
731 of_detach_node(np);
732 } else {
733 for_each_property_of_node(np, prop) {
734 if (strcmp(prop->name, "testcase-alias") == 0)
735 of_remove_property(np, prop);
736 }
737 }
738 }
739 last_node_index--;
740 }
741}
742
520static int __init of_selftest(void) 743static int __init of_selftest(void)
521{ 744{
522 struct device_node *np; 745 struct device_node *np;
746 int res;
747
748 /* adding data for selftest */
749 res = selftest_data_add();
750 if (res)
751 return res;
523 752
524 np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); 753 np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
525 if (!np) { 754 if (!np) {
@@ -533,12 +762,18 @@ static int __init of_selftest(void)
533 of_selftest_dynamic(); 762 of_selftest_dynamic();
534 of_selftest_parse_phandle_with_args(); 763 of_selftest_parse_phandle_with_args();
535 of_selftest_property_match_string(); 764 of_selftest_property_match_string();
765 of_selftest_property_copy();
766 of_selftest_changeset();
536 of_selftest_parse_interrupts(); 767 of_selftest_parse_interrupts();
537 of_selftest_parse_interrupts_extended(); 768 of_selftest_parse_interrupts_extended();
538 of_selftest_match_node(); 769 of_selftest_match_node();
539 of_selftest_platform_populate(); 770 of_selftest_platform_populate();
540 pr_info("end of selftest - %i passed, %i failed\n", 771 pr_info("end of selftest - %i passed, %i failed\n",
541 selftest_results.passed, selftest_results.failed); 772 selftest_results.passed, selftest_results.failed);
773
774 /* removing selftest data from live tree */
775 selftest_data_remove();
776
542 return 0; 777 return 0;
543} 778}
544late_initcall(of_selftest); 779late_initcall(of_selftest);