of: Add unit tests for applying overlays
Existing overlay unit tests examine individual pieces of the overlay code. The new tests target the entire process of applying an overlay. Signed-off-by: Frank Rowand <frank.rowand@sony.com> Signed-off-by: Rob Herring <robh@kernel.org>
This commit is contained in:
Родитель
331f741679
Коммит
81d0848fc8
|
@ -31,6 +31,8 @@
|
|||
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
|
||||
#include <asm/page.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
/*
|
||||
* of_fdt_limit_memory - limit the number of regions in the /memory node
|
||||
* @limit: maximum entries
|
||||
|
@ -469,11 +471,11 @@ static int unflatten_dt_nodes(const void *blob,
|
|||
* Returns NULL on failure or the memory chunk containing the unflattened
|
||||
* device tree on success.
|
||||
*/
|
||||
static void *__unflatten_device_tree(const void *blob,
|
||||
struct device_node *dad,
|
||||
struct device_node **mynodes,
|
||||
void *(*dt_alloc)(u64 size, u64 align),
|
||||
bool detached)
|
||||
void *__unflatten_device_tree(const void *blob,
|
||||
struct device_node *dad,
|
||||
struct device_node **mynodes,
|
||||
void *(*dt_alloc)(u64 size, u64 align),
|
||||
bool detached)
|
||||
{
|
||||
int size;
|
||||
void *mem;
|
||||
|
@ -1261,6 +1263,8 @@ void __init unflatten_device_tree(void)
|
|||
|
||||
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
|
||||
of_alias_scan(early_init_dt_alloc_memory_arch);
|
||||
|
||||
unittest_unflatten_overlay_base();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,6 +55,18 @@ static inline int of_property_notify(int action, struct device_node *np,
|
|||
}
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
#ifdef CONFIG_OF_UNITTEST
|
||||
extern void __init unittest_unflatten_overlay_base(void);
|
||||
#else
|
||||
static inline void unittest_unflatten_overlay_base(void) {};
|
||||
#endif
|
||||
|
||||
extern void *__unflatten_device_tree(const void *blob,
|
||||
struct device_node *dad,
|
||||
struct device_node **mynodes,
|
||||
void *(*dt_alloc)(u64 size, u64 align),
|
||||
bool detached);
|
||||
|
||||
/**
|
||||
* General utilities for working with live trees.
|
||||
*
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
obj-y += testcases.dtb.o
|
||||
obj-y += overlay.dtb.o
|
||||
obj-y += overlay_bad_phandle.dtb.o
|
||||
obj-y += overlay_base.dtb.o
|
||||
|
||||
targets += testcases.dtb testcases.dtb.S
|
||||
targets += overlay.dtb overlay.dtb.S
|
||||
targets += overlay_bad_phandle.dtb overlay_bad_phandle.dtb.S
|
||||
targets += overlay_base.dtb overlay_base.dtb.S
|
||||
|
||||
.SECONDARY: \
|
||||
$(obj)/testcases.dtb.S \
|
||||
$(obj)/testcases.dtb
|
||||
.PRECIOUS: \
|
||||
$(obj)/%.dtb.S \
|
||||
$(obj)/%.dtb
|
||||
|
||||
# enable creation of __symbols__ node
|
||||
DTC_FLAGS_overlay := -@
|
||||
DTC_FLAGS_overlay_bad_phandle := -@
|
||||
DTC_FLAGS_overlay_base := -@
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
/ {
|
||||
|
||||
fragment@0 {
|
||||
target = <&electric_1>;
|
||||
|
||||
__overlay__ {
|
||||
status = "ok";
|
||||
|
||||
hvac_2: hvac-large-1 {
|
||||
compatible = "ot,hvac-large";
|
||||
heat-range = < 40 75 >;
|
||||
cool-range = < 65 80 >;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fragment@1 {
|
||||
target = <&rides_1>;
|
||||
|
||||
__overlay__ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
status = "ok";
|
||||
|
||||
ride@200 {
|
||||
compatible = "ot,ferris-wheel";
|
||||
reg = < 0x00000200 0x100 >;
|
||||
hvac-provider = < &hvac_2 >;
|
||||
hvac-thermostat = < 27 32 > ;
|
||||
hvac-zones = < 12 5 >;
|
||||
hvac-zone-names = "operator", "snack-bar";
|
||||
spin-controller = < &spin_ctrl_1 3 >;
|
||||
spin-rph = < 30 >;
|
||||
gondolas = < 16 >;
|
||||
gondola-capacity = < 6 >;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fragment@2 {
|
||||
target = <&lights_2>;
|
||||
|
||||
__overlay__ {
|
||||
status = "ok";
|
||||
color = "purple", "white", "red", "green";
|
||||
rate = < 3 256 >;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
/ {
|
||||
|
||||
fragment@0 {
|
||||
target = <&electric_1>;
|
||||
|
||||
__overlay__ {
|
||||
|
||||
// This label should cause an error when the overlay
|
||||
// is applied. There is already a phandle value
|
||||
// in the base tree for motor-1.
|
||||
spin_ctrl_1_conflict: motor-1 {
|
||||
accelerate = < 3 >;
|
||||
decelerate = < 5 >;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
/*
|
||||
* Base device tree that overlays will be applied against.
|
||||
*
|
||||
* Do not add any properties in node "/".
|
||||
* Do not add any nodes other than "/testcase-data-2" in node "/".
|
||||
* Do not add anything that would result in dtc creating node "/__fixups__".
|
||||
* dtc will create nodes "/__symbols__" and "/__local_fixups__".
|
||||
*/
|
||||
|
||||
/ {
|
||||
testcase-data-2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
electric_1: substation@100 {
|
||||
compatible = "ot,big-volts-control";
|
||||
reg = < 0x00000100 0x100 >;
|
||||
status = "disabled";
|
||||
|
||||
hvac_1: hvac-medium-1 {
|
||||
compatible = "ot,hvac-medium";
|
||||
heat-range = < 50 75 >;
|
||||
cool-range = < 60 80 >;
|
||||
};
|
||||
|
||||
spin_ctrl_1: motor-1 {
|
||||
compatible = "ot,ferris-wheel-motor";
|
||||
spin = "clockwise";
|
||||
};
|
||||
|
||||
spin_ctrl_2: motor-8 {
|
||||
compatible = "ot,roller-coaster-motor";
|
||||
};
|
||||
};
|
||||
|
||||
rides_1: fairway-1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "ot,rides";
|
||||
status = "disabled";
|
||||
orientation = < 127 >;
|
||||
|
||||
ride@100 {
|
||||
compatible = "ot,roller-coaster";
|
||||
reg = < 0x00000100 0x100 >;
|
||||
hvac-provider = < &hvac_1 >;
|
||||
hvac-thermostat = < 29 > ;
|
||||
hvac-zones = < 14 >;
|
||||
hvac-zone-names = "operator";
|
||||
spin-controller = < &spin_ctrl_2 5 &spin_ctrl_2 7 >;
|
||||
spin-controller-names = "track_1", "track_2";
|
||||
queues = < 2 >;
|
||||
};
|
||||
};
|
||||
|
||||
lights_1: lights@30000 {
|
||||
compatible = "ot,work-lights";
|
||||
reg = < 0x00030000 0x1000 >;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
lights_2: lights@40000 {
|
||||
compatible = "ot,show-lights";
|
||||
reg = < 0x00040000 0x1000 >;
|
||||
status = "disabled";
|
||||
rate = < 13 138 >;
|
||||
};
|
||||
|
||||
retail_1: vending@50000 {
|
||||
reg = < 0x00050000 0x1000 >;
|
||||
compatible = "ot,tickets";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
@ -1925,6 +1926,320 @@ out:
|
|||
static inline void __init of_unittest_overlay(void) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* __dtb_ot_begin[] and __dtb_ot_end[] are created by cmd_dt_S_dtb
|
||||
* in scripts/Makefile.lib
|
||||
*/
|
||||
|
||||
#define OVERLAY_INFO_EXTERN(name) \
|
||||
extern uint8_t __dtb_##name##_begin[]; \
|
||||
extern uint8_t __dtb_##name##_end[]
|
||||
|
||||
#define OVERLAY_INFO(name, expected) \
|
||||
{ .dtb_begin = __dtb_##name##_begin, \
|
||||
.dtb_end = __dtb_##name##_end, \
|
||||
.expected_result = expected, \
|
||||
}
|
||||
|
||||
struct overlay_info {
|
||||
uint8_t *dtb_begin;
|
||||
uint8_t *dtb_end;
|
||||
void *data;
|
||||
struct device_node *np_overlay;
|
||||
int expected_result;
|
||||
int overlay_id;
|
||||
};
|
||||
|
||||
OVERLAY_INFO_EXTERN(overlay_base);
|
||||
OVERLAY_INFO_EXTERN(overlay);
|
||||
OVERLAY_INFO_EXTERN(overlay_bad_phandle);
|
||||
|
||||
#ifdef CONFIG_OF_OVERLAY
|
||||
|
||||
/* order of entries is hard-coded into users of overlays[] */
|
||||
static struct overlay_info overlays[] = {
|
||||
OVERLAY_INFO(overlay_base, -9999),
|
||||
OVERLAY_INFO(overlay, 0),
|
||||
OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct device_node *overlay_base_root;
|
||||
|
||||
/*
|
||||
* Create base device tree for the overlay unittest.
|
||||
*
|
||||
* This is called from very early boot code.
|
||||
*
|
||||
* Do as much as possible the same way as done in __unflatten_device_tree
|
||||
* and other early boot steps for the normal FDT so that the overlay base
|
||||
* unflattened tree will have the same characteristics as the real tree
|
||||
* (such as having memory allocated by the early allocator). The goal
|
||||
* is to test "the real thing" as much as possible, and test "test setup
|
||||
* code" as little as possible.
|
||||
*
|
||||
* Have to stop before resolving phandles, because that uses kmalloc.
|
||||
*/
|
||||
void __init unittest_unflatten_overlay_base(void)
|
||||
{
|
||||
struct overlay_info *info;
|
||||
u32 data_size;
|
||||
u32 size;
|
||||
|
||||
info = &overlays[0];
|
||||
|
||||
if (info->expected_result != -9999) {
|
||||
pr_err("No dtb 'overlay_base' to attach\n");
|
||||
return;
|
||||
}
|
||||
|
||||
data_size = info->dtb_end - info->dtb_begin;
|
||||
if (!data_size) {
|
||||
pr_err("No dtb 'overlay_base' to attach\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size = fdt_totalsize(info->dtb_begin);
|
||||
if (size != data_size) {
|
||||
pr_err("dtb 'overlay_base' header totalsize != actual size");
|
||||
return;
|
||||
}
|
||||
|
||||
info->data = early_init_dt_alloc_memory_arch(size,
|
||||
roundup_pow_of_two(FDT_V17_SIZE));
|
||||
if (!info->data) {
|
||||
pr_err("alloc for dtb 'overlay_base' failed");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(info->data, info->dtb_begin, size);
|
||||
|
||||
__unflatten_device_tree(info->data, NULL, &info->np_overlay,
|
||||
early_init_dt_alloc_memory_arch, true);
|
||||
overlay_base_root = info->np_overlay;
|
||||
}
|
||||
|
||||
/*
|
||||
* The purpose of of_unittest_overlay_data_add is to add an
|
||||
* overlay in the normal fashion. This is a test of the whole
|
||||
* picture, instead of testing individual elements.
|
||||
*
|
||||
* A secondary purpose is to be able to verify that the contents of
|
||||
* /proc/device-tree/ contains the updated structure and values from
|
||||
* the overlay. That must be verified separately in user space.
|
||||
*
|
||||
* Return 0 on unexpected error.
|
||||
*/
|
||||
static int __init overlay_data_add(int onum)
|
||||
{
|
||||
struct overlay_info *info;
|
||||
int k;
|
||||
int ret;
|
||||
u32 size;
|
||||
u32 size_from_header;
|
||||
|
||||
for (k = 0, info = overlays; info; info++, k++) {
|
||||
if (k == onum)
|
||||
break;
|
||||
}
|
||||
if (onum > k)
|
||||
return 0;
|
||||
|
||||
size = info->dtb_end - info->dtb_begin;
|
||||
if (!size) {
|
||||
pr_err("no overlay to attach, %d\n", onum);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
size_from_header = fdt_totalsize(info->dtb_begin);
|
||||
if (size_from_header != size) {
|
||||
pr_err("overlay header totalsize != actual size, %d", onum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must create permanent copy of FDT because of_fdt_unflatten_tree()
|
||||
* will create pointers to the passed in FDT in the EDT.
|
||||
*/
|
||||
info->data = kmemdup(info->dtb_begin, size, GFP_KERNEL);
|
||||
if (!info->data) {
|
||||
pr_err("unable to allocate memory for data, %d\n", onum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
of_fdt_unflatten_tree(info->data, NULL, &info->np_overlay);
|
||||
if (!info->np_overlay) {
|
||||
pr_err("unable to unflatten overlay, %d\n", onum);
|
||||
ret = 0;
|
||||
goto out_free_data;
|
||||
}
|
||||
of_node_set_flag(info->np_overlay, OF_DETACHED);
|
||||
|
||||
ret = of_resolve_phandles(info->np_overlay);
|
||||
if (ret) {
|
||||
pr_err("resolve ot phandles (ret=%d), %d\n", ret, onum);
|
||||
goto out_free_np_overlay;
|
||||
}
|
||||
|
||||
ret = of_overlay_create(info->np_overlay);
|
||||
if (ret < 0) {
|
||||
pr_err("of_overlay_create() (ret=%d), %d\n", ret, onum);
|
||||
goto out_free_np_overlay;
|
||||
} else {
|
||||
info->overlay_id = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
pr_debug("__dtb_overlay_begin applied, overlay id %d\n", ret);
|
||||
|
||||
goto out;
|
||||
|
||||
out_free_np_overlay:
|
||||
/*
|
||||
* info->np_overlay is the unflattened device tree
|
||||
* It has not been spliced into the live tree.
|
||||
*/
|
||||
|
||||
/* todo: function to free unflattened device tree */
|
||||
|
||||
out_free_data:
|
||||
kfree(info->data);
|
||||
|
||||
out:
|
||||
return (ret == info->expected_result);
|
||||
}
|
||||
|
||||
/*
|
||||
* The purpose of of_unittest_overlay_high_level is to add an overlay
|
||||
* in the normal fashion. This is a test of the whole picture,
|
||||
* instead of individual elements.
|
||||
*
|
||||
* The first part of the function is _not_ normal overlay usage; it is
|
||||
* finishing splicing the base overlay device tree into the live tree.
|
||||
*/
|
||||
static __init void of_unittest_overlay_high_level(void)
|
||||
{
|
||||
struct device_node *last_sibling;
|
||||
struct device_node *np;
|
||||
struct device_node *of_symbols;
|
||||
struct device_node *overlay_base_symbols;
|
||||
struct device_node **pprev;
|
||||
struct property *prop;
|
||||
int ret;
|
||||
|
||||
if (!overlay_base_root) {
|
||||
unittest(0, "overlay_base_root not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Could not fixup phandles in unittest_unflatten_overlay_base()
|
||||
* because kmalloc() was not yet available.
|
||||
*/
|
||||
of_resolve_phandles(overlay_base_root);
|
||||
|
||||
/*
|
||||
* do not allow overlay_base to duplicate any node already in
|
||||
* tree, this greatly simplifies the code
|
||||
*/
|
||||
|
||||
/*
|
||||
* remove overlay_base_root node "__local_fixups", after
|
||||
* being used by of_resolve_phandles()
|
||||
*/
|
||||
pprev = &overlay_base_root->child;
|
||||
for (np = overlay_base_root->child; np; np = np->sibling) {
|
||||
if (!of_node_cmp(np->name, "__local_fixups__")) {
|
||||
*pprev = np->sibling;
|
||||
break;
|
||||
}
|
||||
pprev = &np->sibling;
|
||||
}
|
||||
|
||||
/* remove overlay_base_root node "__symbols__" if in live tree */
|
||||
of_symbols = of_get_child_by_name(of_root, "__symbols__");
|
||||
if (of_symbols) {
|
||||
/* will have to graft properties from node into live tree */
|
||||
pprev = &overlay_base_root->child;
|
||||
for (np = overlay_base_root->child; np; np = np->sibling) {
|
||||
if (!of_node_cmp(np->name, "__symbols__")) {
|
||||
overlay_base_symbols = np;
|
||||
*pprev = np->sibling;
|
||||
break;
|
||||
}
|
||||
pprev = &np->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
for (np = overlay_base_root->child; np; np = np->sibling) {
|
||||
if (of_get_child_by_name(of_root, np->name)) {
|
||||
unittest(0, "illegal node name in overlay_base %s",
|
||||
np->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* overlay 'overlay_base' is not allowed to have root
|
||||
* properties, so only need to splice nodes into main device tree.
|
||||
*
|
||||
* root node of *overlay_base_root will not be freed, it is lost
|
||||
* memory.
|
||||
*/
|
||||
|
||||
for (np = overlay_base_root->child; np; np = np->sibling)
|
||||
np->parent = of_root;
|
||||
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
for (np = of_root->child; np; np = np->sibling)
|
||||
last_sibling = np;
|
||||
|
||||
if (last_sibling)
|
||||
last_sibling->sibling = overlay_base_root->child;
|
||||
else
|
||||
of_root->child = overlay_base_root->child;
|
||||
|
||||
for_each_of_allnodes_from(overlay_base_root, np)
|
||||
__of_attach_node_sysfs(np);
|
||||
|
||||
if (of_symbols) {
|
||||
for_each_property_of_node(overlay_base_symbols, prop) {
|
||||
ret = __of_add_property(of_symbols, prop);
|
||||
if (ret) {
|
||||
unittest(0,
|
||||
"duplicate property '%s' in overlay_base node __symbols__",
|
||||
prop->name);
|
||||
return;
|
||||
}
|
||||
ret = __of_add_property_sysfs(of_symbols, prop);
|
||||
if (ret) {
|
||||
unittest(0,
|
||||
"unable to add property '%s' in overlay_base node __symbols__ to sysfs",
|
||||
prop->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
|
||||
/* now do the normal overlay usage test */
|
||||
|
||||
unittest(overlay_data_add(1),
|
||||
"Adding overlay 'overlay' failed\n");
|
||||
|
||||
unittest(overlay_data_add(2),
|
||||
"Adding overlay 'overlay_bad_phandle' failed\n");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline __init void of_unittest_overlay_high_level(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init of_unittest(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -1962,6 +2277,8 @@ static int __init of_unittest(void)
|
|||
/* Double check linkage after removing testcase data */
|
||||
of_unittest_check_tree_linkage();
|
||||
|
||||
of_unittest_overlay_high_level();
|
||||
|
||||
pr_info("end of unittest - %i passed, %i failed\n",
|
||||
unittest_results.passed, unittest_results.failed);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче