Adding selftest testdata dynamically into live tree
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. Tested successfully with current selftest's testcases. Signed-off-by: Gaurav Minocha <gaurav.minocha.os@gmail.com> [glikely: Removed ability to build as a module and fixed no-devicetree bug] Signed-off-by: Grant Likely <grant.likely@linaro.org>
This commit is contained in:
Родитель
848007ba9e
Коммит
ae9304c9d3
|
@ -46,5 +46,3 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <testcases.dtsi>
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ menu "Device Tree and Open Firmware support"
|
||||||
config OF_SELFTEST
|
config OF_SELFTEST
|
||||||
bool "Device Tree Runtime self tests"
|
bool "Device Tree Runtime self tests"
|
||||||
depends on OF_IRQ
|
depends on OF_IRQ
|
||||||
|
select OF_DYNAMIC
|
||||||
help
|
help
|
||||||
This option builds in test cases for the device tree infrastructure
|
This option builds in test cases for the device tree infrastructure
|
||||||
that are executed once at boot time, and the results dumped to the
|
that are executed once at boot time, and the results dumped to the
|
||||||
|
|
|
@ -5,7 +5,8 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
|
||||||
obj-$(CONFIG_OF_ADDRESS) += address.o
|
obj-$(CONFIG_OF_ADDRESS) += address.o
|
||||||
obj-$(CONFIG_OF_IRQ) += irq.o
|
obj-$(CONFIG_OF_IRQ) += irq.o
|
||||||
obj-$(CONFIG_OF_NET) += of_net.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_MDIO) += of_mdio.o
|
||||||
obj-$(CONFIG_OF_PCI) += of_pci.o
|
obj-$(CONFIG_OF_PCI) += of_pci.o
|
||||||
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
|
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_fdt.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
@ -21,6 +22,10 @@ static struct selftest_results {
|
||||||
int failed;
|
int failed;
|
||||||
} selftest_results;
|
} selftest_results;
|
||||||
|
|
||||||
|
#define NO_OF_NODES 2
|
||||||
|
static struct device_node *nodes[NO_OF_NODES];
|
||||||
|
static int last_node_index;
|
||||||
|
|
||||||
#define selftest(result, fmt, ...) { \
|
#define selftest(result, fmt, ...) { \
|
||||||
if (!(result)) { \
|
if (!(result)) { \
|
||||||
selftest_results.failed++; \
|
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)
|
static int __init of_selftest(void)
|
||||||
{
|
{
|
||||||
struct device_node *np;
|
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");
|
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
|
||||||
if (!np) {
|
if (!np) {
|
||||||
|
@ -539,6 +691,10 @@ static int __init of_selftest(void)
|
||||||
of_selftest_platform_populate();
|
of_selftest_platform_populate();
|
||||||
pr_info("end of selftest - %i passed, %i failed\n",
|
pr_info("end of selftest - %i passed, %i failed\n",
|
||||||
selftest_results.passed, selftest_results.failed);
|
selftest_results.passed, selftest_results.failed);
|
||||||
|
|
||||||
|
/* removing selftest data from live tree */
|
||||||
|
selftest_data_remove();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
late_initcall(of_selftest);
|
late_initcall(of_selftest);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/dts-v1/;
|
||||||
#include "tests-phandle.dtsi"
|
#include "tests-phandle.dtsi"
|
||||||
#include "tests-interrupts.dtsi"
|
#include "tests-interrupts.dtsi"
|
||||||
#include "tests-match.dtsi"
|
#include "tests-match.dtsi"
|
Загрузка…
Ссылка в новой задаче