Move duplicating and unflattening of an overlay flattened devicetree

(FDT) into the overlay application code.  To accomplish this,
 of_overlay_apply() is replaced by of_overlay_fdt_apply().
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJam68pAAoJEKFvmrgMstOVJEkQAIkYv+041F1RE8pezo8FvO7k
 GJnyiVVhiWEdddOfNofaR05im7QnrVLamKjmVgCZL/5ScF3pB0bkfxgbPn8oyjQ5
 PCDriQOZ+6DFjCWNtib8K2ZW150QMMI5V4YLSooIKRoH/Opm4VEP1k94sKWmrELt
 ja2WwI9+Wc6oiQe8n7okBgg6pKJ9dakeYlWdCAvDXPcgEdwrqgsQVj+gYRAqDuN7
 MKQLF9uKNP5XrIksKFBprIDadXHVa7AmxYN9nTWQvK2b1NGssNcr3jxG0JSwxPUN
 z+ov828nU/M68o1n6d6ADwRN2XFLWWFd1pZyMUErPWREIE8mqlvwux+Z8SpjLPd3
 T4fMs8vtURf/GlH3ENiulKOs3/KHmvEDBcmAQc7jI/JQQeCvvom20iLd042oUaKP
 3HKstI6nIPkKhEJ7MaCSzRjHN2HfXFAEcZGwSxYCsozOrWsf+lSkk4coK7777lJL
 6avpU6CX4t/Kd2jIM1zddFS+hnWtnmS8eiUHqsj1ps6p8NzkAehwq6JRQ/oZo3sJ
 lQvtmip53VQYO1DbsnbGHeYjfsqfnaTY635asB27gzux1baqxy0liY6I4TvqsEr6
 VW9HUvbtlFFzRqP08tkpOPOPeaH7fsygiBzKtk9Ezpoz/A5KhtAXifFbAe4yHkaF
 4Hm3vYwJ7xJ7OpjxGrD0
 =f/Se
 -----END PGP SIGNATURE-----

Merge tag 'overlay_apply_fdt_v7-for-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/frowand/linux into drm/next/du

- DT overlay applying rework (Frank Rowand)

Move duplicating and unflattening of an overlay flattened devicetree
(FDT) into the overlay application code.  To accomplish this,
of_overlay_apply() is replaced by of_overlay_fdt_apply().
This commit is contained in:
Laurent Pinchart 2018-03-04 13:33:50 +02:00
commit 94aae1d9ff
27 changed files with 598 additions and 499 deletions

View File

@ -87,8 +87,8 @@ Overlay in-kernel API
The API is quite easy to use. The API is quite easy to use.
1. Call of_overlay_apply() to create and apply an overlay changeset. The return 1. Call of_overlay_fdt_apply() to create and apply an overlay changeset. The
value is an error or a cookie identifying this overlay. return value is an error or a cookie identifying this overlay.
2. Call of_overlay_remove() to remove and cleanup the overlay changeset 2. Call of_overlay_remove() to remove and cleanup the overlay changeset
previously created via the call to of_overlay_apply(). Removal of an overlay previously created via the call to of_overlay_apply(). Removal of an overlay

View File

@ -259,7 +259,7 @@ static void __init dtb_apic_setup(void)
dtb_ioapic_setup(); dtb_ioapic_setup();
} }
#ifdef CONFIG_OF_FLATTREE #ifdef CONFIG_OF_EARLY_FLATTREE
static void __init x86_flattree_get_config(void) static void __init x86_flattree_get_config(void)
{ {
u32 size, map_len; u32 size, map_len;

View File

@ -92,6 +92,7 @@ config OF_RESOLVE
config OF_OVERLAY config OF_OVERLAY
bool "Device Tree overlays" bool "Device Tree overlays"
select OF_DYNAMIC select OF_DYNAMIC
select OF_FLATTREE
select OF_RESOLVE select OF_RESOLVE
help help
Overlays are a method to dynamically modify part of the kernel's Overlays are a method to dynamically modify part of the kernel's

View File

@ -12,10 +12,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_fdt.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/libfdt.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/idr.h> #include <linux/idr.h>
@ -33,7 +35,9 @@ struct fragment {
/** /**
* struct overlay_changeset * struct overlay_changeset
* @id: changeset identifier
* @ovcs_list: list on which we are located * @ovcs_list: list on which we are located
* @fdt: FDT that was unflattened to create @overlay_tree
* @overlay_tree: expanded device tree that contains the fragment nodes * @overlay_tree: expanded device tree that contains the fragment nodes
* @count: count of fragment structures * @count: count of fragment structures
* @fragments: fragment nodes in the overlay expanded device tree * @fragments: fragment nodes in the overlay expanded device tree
@ -43,6 +47,7 @@ struct fragment {
struct overlay_changeset { struct overlay_changeset {
int id; int id;
struct list_head ovcs_list; struct list_head ovcs_list;
const void *fdt;
struct device_node *overlay_tree; struct device_node *overlay_tree;
int count; int count;
struct fragment *fragments; struct fragment *fragments;
@ -483,27 +488,38 @@ static int build_changeset(struct overlay_changeset *ovcs)
*/ */
static struct device_node *find_target_node(struct device_node *info_node) static struct device_node *find_target_node(struct device_node *info_node)
{ {
struct device_node *node;
const char *path; const char *path;
u32 val; u32 val;
int ret; int ret;
ret = of_property_read_u32(info_node, "target", &val); ret = of_property_read_u32(info_node, "target", &val);
if (!ret) if (!ret) {
return of_find_node_by_phandle(val); node = of_find_node_by_phandle(val);
if (!node)
pr_err("find target, node: %pOF, phandle 0x%x not found\n",
info_node, val);
return node;
}
ret = of_property_read_string(info_node, "target-path", &path); ret = of_property_read_string(info_node, "target-path", &path);
if (!ret) if (!ret) {
return of_find_node_by_path(path); node = of_find_node_by_path(path);
if (!node)
pr_err("find target, node: %pOF, path '%s' not found\n",
info_node, path);
return node;
}
pr_err("Failed to find target for node %p (%s)\n", pr_err("find target, node: %pOF, no target property\n", info_node);
info_node, info_node->name);
return NULL; return NULL;
} }
/** /**
* init_overlay_changeset() - initialize overlay changeset from overlay tree * init_overlay_changeset() - initialize overlay changeset from overlay tree
* @ovcs Overlay changeset to build * @ovcs: Overlay changeset to build
* @fdt: the FDT that was unflattened to create @tree
* @tree: Contains all the overlay fragments and overlay fixup nodes * @tree: Contains all the overlay fragments and overlay fixup nodes
* *
* Initialize @ovcs. Populate @ovcs->fragments with node information from * Initialize @ovcs. Populate @ovcs->fragments with node information from
@ -514,7 +530,7 @@ static struct device_node *find_target_node(struct device_node *info_node)
* detected in @tree, or -ENOSPC if idr_alloc() error. * detected in @tree, or -ENOSPC if idr_alloc() error.
*/ */
static int init_overlay_changeset(struct overlay_changeset *ovcs, static int init_overlay_changeset(struct overlay_changeset *ovcs,
struct device_node *tree) const void *fdt, struct device_node *tree)
{ {
struct device_node *node, *overlay_node; struct device_node *node, *overlay_node;
struct fragment *fragment; struct fragment *fragment;
@ -535,6 +551,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
pr_debug("%s() tree is not root\n", __func__); pr_debug("%s() tree is not root\n", __func__);
ovcs->overlay_tree = tree; ovcs->overlay_tree = tree;
ovcs->fdt = fdt;
INIT_LIST_HEAD(&ovcs->ovcs_list); INIT_LIST_HEAD(&ovcs->ovcs_list);
@ -606,6 +623,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
} }
if (!cnt) { if (!cnt) {
pr_err("no fragments or symbols in overlay\n");
ret = -EINVAL; ret = -EINVAL;
goto err_free_fragments; goto err_free_fragments;
} }
@ -642,11 +660,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
} }
kfree(ovcs->fragments); kfree(ovcs->fragments);
/*
* TODO
*
* would like to: kfree(ovcs->overlay_tree);
* but can not since drivers may have pointers into this data
*
* would like to: kfree(ovcs->fdt);
* but can not since drivers may have pointers into this data
*/
kfree(ovcs); kfree(ovcs);
} }
/** /*
* internal documentation
*
* of_overlay_apply() - Create and apply an overlay changeset * of_overlay_apply() - Create and apply an overlay changeset
* @fdt: the FDT that was unflattened to create @tree
* @tree: Expanded overlay device tree * @tree: Expanded overlay device tree
* @ovcs_id: Pointer to overlay changeset id * @ovcs_id: Pointer to overlay changeset id
* *
@ -685,21 +716,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
* id is returned to *ovcs_id. * id is returned to *ovcs_id.
*/ */
int of_overlay_apply(struct device_node *tree, int *ovcs_id) static int of_overlay_apply(const void *fdt, struct device_node *tree,
int *ovcs_id)
{ {
struct overlay_changeset *ovcs; struct overlay_changeset *ovcs;
int ret = 0, ret_revert, ret_tmp; int ret = 0, ret_revert, ret_tmp;
*ovcs_id = 0; /*
* As of this point, fdt and tree belong to the overlay changeset.
* overlay changeset code is responsible for freeing them.
*/
if (devicetree_corrupt()) { if (devicetree_corrupt()) {
pr_err("devicetree state suspect, refuse to apply overlay\n"); pr_err("devicetree state suspect, refuse to apply overlay\n");
kfree(fdt);
kfree(tree);
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL); ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
if (!ovcs) { if (!ovcs) {
kfree(fdt);
kfree(tree);
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
@ -709,12 +748,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
ret = of_resolve_phandles(tree); ret = of_resolve_phandles(tree);
if (ret) if (ret)
goto err_free_overlay_changeset; goto err_free_tree;
ret = init_overlay_changeset(ovcs, tree); ret = init_overlay_changeset(ovcs, fdt, tree);
if (ret) if (ret)
goto err_free_overlay_changeset; goto err_free_tree;
/*
* after overlay_notify(), ovcs->overlay_tree related pointers may have
* leaked to drivers, so can not kfree() tree, aka ovcs->overlay_tree;
* and can not free fdt, aka ovcs->fdt
*/
ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY); ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
if (ret) { if (ret) {
pr_err("overlay changeset pre-apply notify error %d\n", ret); pr_err("overlay changeset pre-apply notify error %d\n", ret);
@ -754,6 +798,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
goto out_unlock; goto out_unlock;
err_free_tree:
kfree(fdt);
kfree(tree);
err_free_overlay_changeset: err_free_overlay_changeset:
free_overlay_changeset(ovcs); free_overlay_changeset(ovcs);
@ -766,7 +814,63 @@ out:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(of_overlay_apply);
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
int *ovcs_id)
{
const void *new_fdt;
int ret;
u32 size;
struct device_node *overlay_root;
*ovcs_id = 0;
ret = 0;
if (overlay_fdt_size < sizeof(struct fdt_header) ||
fdt_check_header(overlay_fdt)) {
pr_err("Invalid overlay_fdt header\n");
return -EINVAL;
}
size = fdt_totalsize(overlay_fdt);
if (overlay_fdt_size < size)
return -EINVAL;
/*
* Must create permanent copy of FDT because of_fdt_unflatten_tree()
* will create pointers to the passed in FDT in the unflattened tree.
*/
new_fdt = kmemdup(overlay_fdt, size, GFP_KERNEL);
if (!new_fdt)
return -ENOMEM;
of_fdt_unflatten_tree(new_fdt, NULL, &overlay_root);
if (!overlay_root) {
pr_err("unable to unflatten overlay_fdt\n");
ret = -EINVAL;
goto out_free_new_fdt;
}
ret = of_overlay_apply(new_fdt, overlay_root, ovcs_id);
if (ret < 0) {
/*
* new_fdt and overlay_root now belong to the overlay
* changeset.
* overlay changeset code is responsible for freeing them.
*/
goto out;
}
return 0;
out_free_new_fdt:
kfree(new_fdt);
out:
return ret;
}
EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
/* /*
* Find @np in @tree. * Find @np in @tree.

View File

@ -269,17 +269,11 @@ int of_resolve_phandles(struct device_node *overlay)
goto out; goto out;
} }
#if 0
Temporarily disable check so that old style overlay unittests
do not fail when of_resolve_phandles() is moved into
of_overlay_apply().
if (!of_node_check_flag(overlay, OF_DETACHED)) { if (!of_node_check_flag(overlay, OF_DETACHED)) {
pr_err("overlay not detached\n"); pr_err("overlay not detached\n");
err = -EINVAL; err = -EINVAL;
goto out; goto out;
} }
#endif
phandle_delta = live_tree_max_phandle() + 1; phandle_delta = live_tree_max_phandle() + 1;
adjust_overlay_phandles(overlay, phandle_delta); adjust_overlay_phandles(overlay, phandle_delta);

View File

@ -1,8 +1,22 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
DTC_FLAGS_testcases := -Wno-interrupts_property
obj-y += testcases.dtb.o obj-y += testcases.dtb.o
obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
overlay_0.dtb.o \
overlay_1.dtb.o \
overlay_2.dtb.o \
overlay_3.dtb.o \
overlay_4.dtb.o \
overlay_5.dtb.o \
overlay_6.dtb.o \
overlay_7.dtb.o \
overlay_8.dtb.o \
overlay_9.dtb.o \
overlay_10.dtb.o \
overlay_11.dtb.o \
overlay_12.dtb.o \
overlay_13.dtb.o \
overlay_15.dtb.o \
overlay_bad_phandle.dtb.o \ overlay_bad_phandle.dtb.o \
overlay_bad_symbol.dtb.o \ overlay_bad_symbol.dtb.o \
overlay_base.dtb.o overlay_base.dtb.o
@ -10,10 +24,14 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y))) targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y)))
# enable creation of __symbols__ node # enable creation of __symbols__ node
DTC_FLAGS_overlay := -@ DTC_FLAGS_overlay += -@
DTC_FLAGS_overlay_bad_phandle := -@ DTC_FLAGS_overlay_bad_phandle += -@
DTC_FLAGS_overlay_bad_symbol := -@ DTC_FLAGS_overlay_bad_symbol += -@
DTC_FLAGS_overlay_base := -@ DTC_FLAGS_overlay_base += -@
DTC_FLAGS_testcases += -@
# suppress warnings about intentional errors
DTC_FLAGS_testcases += -Wno-interrupts_property
.PRECIOUS: \ .PRECIOUS: \
$(obj)/%.dtb.S \ $(obj)/%.dtb.S \

View File

@ -2,76 +2,63 @@
/dts-v1/; /dts-v1/;
/plugin/; /plugin/;
/ { &electric_1 {
fragment@0 { status = "okay";
target = <&electric_1>;
__overlay__ { hvac_2: hvac-large-1 {
status = "okay"; compatible = "ot,hvac-large";
heat-range = < 40 75 >;
hvac_2: hvac-large-1 { cool-range = < 65 80 >;
compatible = "ot,hvac-large";
heat-range = < 40 75 >;
cool-range = < 65 80 >;
};
};
}; };
};
fragment@1 {
target = <&rides_1>; &rides_1 {
__overlay__ { #address-cells = <1>;
#address-cells = <1>; #size-cells = <1>;
#size-cells = <1>; status = "okay";
status = "okay";
ride@100 {
ride@100 { #address-cells = <1>;
#address-cells = <1>; #size-cells = <1>;
#size-cells = <1>;
track@30 {
track@30 { incline-up = < 48 32 16 >;
incline-up = < 48 32 16 >; };
};
track@40 {
track@40 { incline-up = < 47 31 15 >;
incline-up = < 47 31 15 >; };
}; };
};
ride_200: ride@200 {
ride_200: ride@200 { #address-cells = <1>;
#address-cells = <1>; #size-cells = <1>;
#size-cells = <1>; compatible = "ot,ferris-wheel";
compatible = "ot,ferris-wheel"; reg = < 0x00000200 0x100 >;
reg = < 0x00000200 0x100 >; hvac-provider = < &hvac_2 >;
hvac-provider = < &hvac_2 >; hvac-thermostat = < 27 32 > ;
hvac-thermostat = < 27 32 > ; hvac-zones = < 12 5 >;
hvac-zones = < 12 5 >; hvac-zone-names = "operator", "snack-bar";
hvac-zone-names = "operator", "snack-bar"; spin-controller = < &spin_ctrl_1 3 >;
spin-controller = < &spin_ctrl_1 3 >; spin-rph = < 30 >;
spin-rph = < 30 >; gondolas = < 16 >;
gondolas = < 16 >; gondola-capacity = < 6 >;
gondola-capacity = < 6 >;
ride_200_left: track@10 {
ride_200_left: track@10 { reg = < 0x00000010 0x10 >;
reg = < 0x00000010 0x10 >; };
};
ride_200_right: track@20 {
ride_200_right: track@20 { reg = < 0x00000020 0x10 >;
reg = < 0x00000020 0x10 >; };
}; };
}; };
};
}; &lights_2 {
fragment@2 { status = "okay";
target = <&lights_2>; color = "purple", "white", "red", "green";
rate = < 3 256 >;
__overlay__ {
status = "okay";
color = "purple", "white", "red", "green";
rate = < 3 256 >;
};
};
}; };

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_0 - enable using absolute target path */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
__overlay__ {
status = "okay";
};
};
};

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_1 - disable using absolute target path */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
__overlay__ {
status = "disabled";
};
};
};

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_10 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
&unittest_test_bus {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest10 {
compatible = "unittest";
status = "okay";
reg = <10>;
#address-cells = <1>;
#size-cells = <0>;
test-unittest101 {
compatible = "unittest";
status = "okay";
reg = <1>;
};
};
};

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_11 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
&unittest_test_bus {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest11 {
compatible = "unittest";
status = "okay";
reg = <11>;
#address-cells = <1>;
#size-cells = <0>;
test-unittest111 {
compatible = "unittest";
status = "okay";
reg = <1>;
};
};
};

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_12 - enable using absolute target path (i2c) */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
__overlay__ {
status = "okay";
};
};
};

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_13 - disable using absolute target path (i2c) */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
__overlay__ {
status = "disabled";
};
};
};

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_15 - mux overlay */
&unittest_i2c_test_bus {
#address-cells = <1>;
#size-cells = <0>;
test-unittest15 {
reg = <11>;
compatible = "unittest-i2c-mux";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
test-mux-dev {
reg = <32>;
compatible = "unittest-i2c-dev";
status = "okay";
};
};
};
};

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_2 - enable using label */
&unittest2 {
status = "okay";
};

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_3 - disable using label */
&unittest3 {
status = "disabled";
};

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_4 - test insertion of a full node */
&unittest_test_bus {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest4 {
compatible = "unittest";
status = "okay";
reg = <4>;
};
};

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_5 - test overlay apply revert */
&unittest5 {
status = "okay";
};

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_6 */
/* overlays 6, 7 application and removal in sequence */
&unittest6 {
status = "okay";
};

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_7 */
/* overlays 6, 7 application and removal in sequence */
&unittest7 {
status = "okay";
};

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_8 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
&unittest8 {
status = "okay";
};

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_9 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
&unittest8 {
property-foo = "bar";
};

View File

@ -2,20 +2,13 @@
/dts-v1/; /dts-v1/;
/plugin/; /plugin/;
/ { &electric_1 {
fragment@0 { // This label should cause an error when the overlay
target = <&electric_1>; // is applied. There is already a phandle value
// in the base tree for motor-1.
__overlay__ { spin_ctrl_1_conflict: motor-1 {
accelerate = < 3 >;
// This label should cause an error when the overlay decelerate = < 5 >;
// 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 >;
};
};
}; };
}; };

View File

@ -2,22 +2,15 @@
/dts-v1/; /dts-v1/;
/plugin/; /plugin/;
/ { &electric_1 {
fragment@0 { // This label should cause an error when the overlay
target = <&electric_1>; // is applied. There is already a symbol hvac_1
// in the base tree
__overlay__ { hvac_1: hvac-medium-2 {
compatible = "ot,hvac-medium";
// This label should cause an error when the overlay heat-range = < 50 75 >;
// is applied. There is already a symbol hvac_1 cool-range = < 60 80 >;
// in the base tree
hvac_1: hvac-medium-2 {
compatible = "ot,hvac-medium";
heat-range = < 50 75 >;
cool-range = < 60 80 >;
};
};
}; };
}; };

View File

@ -5,7 +5,7 @@
overlay-node { overlay-node {
/* test bus */ /* test bus */
unittestbus: test-bus { unittest_test_bus: test-bus {
compatible = "simple-bus"; compatible = "simple-bus";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -70,7 +70,7 @@
reg = <8>; reg = <8>;
}; };
i2c-test-bus { unittest_i2c_test_bus: i2c-test-bus {
compatible = "unittest-i2c-bus"; compatible = "unittest-i2c-bus";
status = "okay"; status = "okay";
reg = <50>; reg = <50>;
@ -113,218 +113,5 @@
}; };
}; };
}; };
/* test enable using absolute target path */
overlay0 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
__overlay__ {
status = "okay";
};
};
};
/* test disable using absolute target path */
overlay1 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
__overlay__ {
status = "disabled";
};
};
};
/* test enable using label */
overlay2 {
fragment@0 {
target = <&unittest2>;
__overlay__ {
status = "okay";
};
};
};
/* test disable using label */
overlay3 {
fragment@0 {
target = <&unittest3>;
__overlay__ {
status = "disabled";
};
};
};
/* test insertion of a full node */
overlay4 {
fragment@0 {
target = <&unittestbus>;
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest4 {
compatible = "unittest";
status = "okay";
reg = <4>;
};
};
};
};
/* test overlay apply revert */
overlay5 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest5";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in sequence */
overlay6 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest6";
__overlay__ {
status = "okay";
};
};
};
overlay7 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest7";
__overlay__ {
status = "okay";
};
};
};
/* test overlays application and removal in bad sequence */
overlay8 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
__overlay__ {
status = "okay";
};
};
};
overlay9 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
__overlay__ {
property-foo = "bar";
};
};
};
overlay10 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus";
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest10 {
compatible = "unittest";
status = "okay";
reg = <10>;
#address-cells = <1>;
#size-cells = <0>;
test-unittest101 {
compatible = "unittest";
status = "okay";
reg = <1>;
};
};
};
};
};
overlay11 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus";
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest11 {
compatible = "unittest";
status = "okay";
reg = <11>;
#address-cells = <1>;
#size-cells = <0>;
test-unittest111 {
compatible = "unittest";
status = "okay";
reg = <1>;
};
};
};
};
};
/* test enable using absolute target path (i2c) */
overlay12 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
__overlay__ {
status = "okay";
};
};
};
/* test disable using absolute target path (i2c) */
overlay13 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
__overlay__ {
status = "disabled";
};
};
};
/* test mux overlay */
overlay15 {
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
test-unittest15 {
reg = <11>;
compatible = "unittest-i2c-mux";
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
test-mux-dev {
reg = <32>;
compatible = "unittest-i2c-dev";
status = "okay";
};
};
};
};
};
};
}; };
}; };

View File

@ -45,6 +45,8 @@ static struct unittest_results {
failed; \ failed; \
}) })
static int __init overlay_data_apply(const char *overlay_name, int *overlay_id);
static void __init of_unittest_find_node_by_name(void) static void __init of_unittest_find_node_by_name(void)
{ {
struct device_node *np; struct device_node *np;
@ -997,8 +999,7 @@ static int __init unittest_data_add(void)
} }
/* /*
* This lock normally encloses of_overlay_apply() as well as * This lock normally encloses of_resolve_phandles()
* of_resolve_phandles().
*/ */
of_overlay_mutex_lock(); of_overlay_mutex_lock();
@ -1191,12 +1192,12 @@ static int of_unittest_device_exists(int unittest_nr, enum overlay_type ovtype)
return 0; return 0;
} }
static const char *overlay_path(int nr) static const char *overlay_name_from_nr(int nr)
{ {
static char buf[256]; static char buf[256];
snprintf(buf, sizeof(buf) - 1, snprintf(buf, sizeof(buf) - 1,
"/testcase-data/overlay%d", nr); "overlay_%d", nr);
buf[sizeof(buf) - 1] = '\0'; buf[sizeof(buf) - 1] = '\0';
return buf; return buf;
@ -1263,25 +1264,19 @@ static void of_unittest_destroy_tracked_overlays(void)
} while (defers > 0); } while (defers > 0);
} }
static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
int *overlay_id) int *overlay_id)
{ {
struct device_node *np = NULL; struct device_node *np = NULL;
const char *overlay_name;
int ret; int ret;
np = of_find_node_by_path(overlay_path(overlay_nr)); overlay_name = overlay_name_from_nr(overlay_nr);
if (np == NULL) {
unittest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr));
ret = -EINVAL;
goto out;
}
*overlay_id = 0; ret = overlay_data_apply(overlay_name, overlay_id);
ret = of_overlay_apply(np, overlay_id); if (!ret) {
if (ret < 0) { unittest(0, "could not apply overlay \"%s\"\n",
unittest(0, "could not create overlay from \"%s\"\n", overlay_name);
overlay_path(overlay_nr));
goto out; goto out;
} }
of_unittest_track_overlay(*overlay_id); of_unittest_track_overlay(*overlay_id);
@ -1295,15 +1290,16 @@ out:
} }
/* apply an overlay while checking before and after states */ /* apply an overlay while checking before and after states */
static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, static int __init of_unittest_apply_overlay_check(int overlay_nr,
int before, int after, enum overlay_type ovtype) int unittest_nr, int before, int after,
enum overlay_type ovtype)
{ {
int ret, ovcs_id; int ret, ovcs_id;
/* unittest device must not be in before state */ /* unittest device must not be in before state */
if (of_unittest_device_exists(unittest_nr, ovtype) != before) { if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", unittest(0, "%s with device @\"%s\" %s\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype), unittest_path(unittest_nr, ovtype),
!before ? "enabled" : "disabled"); !before ? "enabled" : "disabled");
return -EINVAL; return -EINVAL;
@ -1318,8 +1314,8 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
/* unittest device must be to set to after state */ /* unittest device must be to set to after state */
if (of_unittest_device_exists(unittest_nr, ovtype) != after) { if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", unittest(0, "%s failed to create @\"%s\" %s\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype), unittest_path(unittest_nr, ovtype),
!after ? "enabled" : "disabled"); !after ? "enabled" : "disabled");
return -EINVAL; return -EINVAL;
@ -1329,7 +1325,7 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
} }
/* apply an overlay and then revert it while checking before, after states */ /* apply an overlay and then revert it while checking before, after states */
static int of_unittest_apply_revert_overlay_check(int overlay_nr, static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
int unittest_nr, int before, int after, int unittest_nr, int before, int after,
enum overlay_type ovtype) enum overlay_type ovtype)
{ {
@ -1337,8 +1333,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
/* unittest device must be in before state */ /* unittest device must be in before state */
if (of_unittest_device_exists(unittest_nr, ovtype) != before) { if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", unittest(0, "%s with device @\"%s\" %s\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype), unittest_path(unittest_nr, ovtype),
!before ? "enabled" : "disabled"); !before ? "enabled" : "disabled");
return -EINVAL; return -EINVAL;
@ -1354,8 +1350,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
/* unittest device must be in after state */ /* unittest device must be in after state */
if (of_unittest_device_exists(unittest_nr, ovtype) != after) { if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", unittest(0, "%s failed to create @\"%s\" %s\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype), unittest_path(unittest_nr, ovtype),
!after ? "enabled" : "disabled"); !after ? "enabled" : "disabled");
return -EINVAL; return -EINVAL;
@ -1363,16 +1359,16 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
ret = of_overlay_remove(&ovcs_id); ret = of_overlay_remove(&ovcs_id);
if (ret != 0) { if (ret != 0) {
unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n", unittest(0, "%s failed to be destroyed @\"%s\"\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype)); unittest_path(unittest_nr, ovtype));
return ret; return ret;
} }
/* unittest device must be again in before state */ /* unittest device must be again in before state */
if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) { if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) {
unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", unittest(0, "%s with device @\"%s\" %s\n",
overlay_path(overlay_nr), overlay_name_from_nr(overlay_nr),
unittest_path(unittest_nr, ovtype), unittest_path(unittest_nr, ovtype),
!before ? "enabled" : "disabled"); !before ? "enabled" : "disabled");
return -EINVAL; return -EINVAL;
@ -1382,7 +1378,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
} }
/* test activation of device */ /* test activation of device */
static void of_unittest_overlay_0(void) static void __init of_unittest_overlay_0(void)
{ {
int ret; int ret;
@ -1395,7 +1391,7 @@ static void of_unittest_overlay_0(void)
} }
/* test deactivation of device */ /* test deactivation of device */
static void of_unittest_overlay_1(void) static void __init of_unittest_overlay_1(void)
{ {
int ret; int ret;
@ -1408,7 +1404,7 @@ static void of_unittest_overlay_1(void)
} }
/* test activation of device */ /* test activation of device */
static void of_unittest_overlay_2(void) static void __init of_unittest_overlay_2(void)
{ {
int ret; int ret;
@ -1421,7 +1417,7 @@ static void of_unittest_overlay_2(void)
} }
/* test deactivation of device */ /* test deactivation of device */
static void of_unittest_overlay_3(void) static void __init of_unittest_overlay_3(void)
{ {
int ret; int ret;
@ -1434,7 +1430,7 @@ static void of_unittest_overlay_3(void)
} }
/* test activation of a full device node */ /* test activation of a full device node */
static void of_unittest_overlay_4(void) static void __init of_unittest_overlay_4(void)
{ {
int ret; int ret;
@ -1447,7 +1443,7 @@ static void of_unittest_overlay_4(void)
} }
/* test overlay apply/revert sequence */ /* test overlay apply/revert sequence */
static void of_unittest_overlay_5(void) static void __init of_unittest_overlay_5(void)
{ {
int ret; int ret;
@ -1460,19 +1456,19 @@ static void of_unittest_overlay_5(void)
} }
/* test overlay application in sequence */ /* test overlay application in sequence */
static void of_unittest_overlay_6(void) static void __init of_unittest_overlay_6(void)
{ {
struct device_node *np;
int ret, i, ov_id[2], ovcs_id; int ret, i, ov_id[2], ovcs_id;
int overlay_nr = 6, unittest_nr = 6; int overlay_nr = 6, unittest_nr = 6;
int before = 0, after = 1; int before = 0, after = 1;
const char *overlay_name;
/* unittest device must be in before state */ /* unittest device must be in before state */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= before) { != before) {
unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", unittest(0, "%s with device @\"%s\" %s\n",
overlay_path(overlay_nr + i), overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i, unittest_path(unittest_nr + i,
PDEV_OVERLAY), PDEV_OVERLAY),
!before ? "enabled" : "disabled"); !before ? "enabled" : "disabled");
@ -1483,18 +1479,12 @@ static void of_unittest_overlay_6(void)
/* apply the overlays */ /* apply the overlays */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
np = of_find_node_by_path(overlay_path(overlay_nr + i)); overlay_name = overlay_name_from_nr(overlay_nr + i);
if (np == NULL) {
unittest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ovcs_id = 0; ret = overlay_data_apply(overlay_name, &ovcs_id);
ret = of_overlay_apply(np, &ovcs_id); if (!ret) {
if (ret < 0) { unittest(0, "could not apply overlay \"%s\"\n",
unittest(0, "could not create overlay from \"%s\"\n", overlay_name);
overlay_path(overlay_nr + i));
return; return;
} }
ov_id[i] = ovcs_id; ov_id[i] = ovcs_id;
@ -1506,7 +1496,7 @@ static void of_unittest_overlay_6(void)
if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= after) { != after) {
unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n", unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
overlay_path(overlay_nr + i), overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i, unittest_path(unittest_nr + i,
PDEV_OVERLAY), PDEV_OVERLAY),
!after ? "enabled" : "disabled"); !after ? "enabled" : "disabled");
@ -1518,8 +1508,8 @@ static void of_unittest_overlay_6(void)
ovcs_id = ov_id[i]; ovcs_id = ov_id[i];
ret = of_overlay_remove(&ovcs_id); ret = of_overlay_remove(&ovcs_id);
if (ret != 0) { if (ret != 0) {
unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n", unittest(0, "%s failed destroy @\"%s\"\n",
overlay_path(overlay_nr + i), overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i, unittest_path(unittest_nr + i,
PDEV_OVERLAY)); PDEV_OVERLAY));
return; return;
@ -1531,8 +1521,8 @@ static void of_unittest_overlay_6(void)
/* unittest device must be again in before state */ /* unittest device must be again in before state */
if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
!= before) { != before) {
unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", unittest(0, "%s with device @\"%s\" %s\n",
overlay_path(overlay_nr + i), overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr + i, unittest_path(unittest_nr + i,
PDEV_OVERLAY), PDEV_OVERLAY),
!before ? "enabled" : "disabled"); !before ? "enabled" : "disabled");
@ -1544,29 +1534,23 @@ static void of_unittest_overlay_6(void)
} }
/* test overlay application in sequence */ /* test overlay application in sequence */
static void of_unittest_overlay_8(void) static void __init of_unittest_overlay_8(void)
{ {
struct device_node *np;
int ret, i, ov_id[2], ovcs_id; int ret, i, ov_id[2], ovcs_id;
int overlay_nr = 8, unittest_nr = 8; int overlay_nr = 8, unittest_nr = 8;
const char *overlay_name;
/* we don't care about device state in this test */ /* we don't care about device state in this test */
/* apply the overlays */ /* apply the overlays */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
np = of_find_node_by_path(overlay_path(overlay_nr + i)); overlay_name = overlay_name_from_nr(overlay_nr + i);
if (np == NULL) {
unittest(0, "could not find overlay node @\"%s\"\n",
overlay_path(overlay_nr + i));
return;
}
ovcs_id = 0; ret = overlay_data_apply(overlay_name, &ovcs_id);
ret = of_overlay_apply(np, &ovcs_id);
if (ret < 0) { if (ret < 0) {
unittest(0, "could not create overlay from \"%s\"\n", unittest(0, "could not apply overlay \"%s\"\n",
overlay_path(overlay_nr + i)); overlay_name);
return; return;
} }
ov_id[i] = ovcs_id; ov_id[i] = ovcs_id;
@ -1577,8 +1561,8 @@ static void of_unittest_overlay_8(void)
ovcs_id = ov_id[0]; ovcs_id = ov_id[0];
ret = of_overlay_remove(&ovcs_id); ret = of_overlay_remove(&ovcs_id);
if (ret == 0) { if (ret == 0) {
unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n", unittest(0, "%s was destroyed @\"%s\"\n",
overlay_path(overlay_nr + 0), overlay_name_from_nr(overlay_nr + 0),
unittest_path(unittest_nr, unittest_path(unittest_nr,
PDEV_OVERLAY)); PDEV_OVERLAY));
return; return;
@ -1589,8 +1573,8 @@ static void of_unittest_overlay_8(void)
ovcs_id = ov_id[i]; ovcs_id = ov_id[i];
ret = of_overlay_remove(&ovcs_id); ret = of_overlay_remove(&ovcs_id);
if (ret != 0) { if (ret != 0) {
unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n", unittest(0, "%s not destroyed @\"%s\"\n",
overlay_path(overlay_nr + i), overlay_name_from_nr(overlay_nr + i),
unittest_path(unittest_nr, unittest_path(unittest_nr,
PDEV_OVERLAY)); PDEV_OVERLAY));
return; return;
@ -1602,7 +1586,7 @@ static void of_unittest_overlay_8(void)
} }
/* test insertion of a bus with parent devices */ /* test insertion of a bus with parent devices */
static void of_unittest_overlay_10(void) static void __init of_unittest_overlay_10(void)
{ {
int ret; int ret;
char *child_path; char *child_path;
@ -1625,7 +1609,7 @@ static void of_unittest_overlay_10(void)
} }
/* test insertion of a bus with parent devices (and revert) */ /* test insertion of a bus with parent devices (and revert) */
static void of_unittest_overlay_11(void) static void __init of_unittest_overlay_11(void)
{ {
int ret; int ret;
@ -1891,7 +1875,7 @@ static void of_unittest_overlay_i2c_cleanup(void)
i2c_del_driver(&unittest_i2c_dev_driver); i2c_del_driver(&unittest_i2c_dev_driver);
} }
static void of_unittest_overlay_i2c_12(void) static void __init of_unittest_overlay_i2c_12(void)
{ {
int ret; int ret;
@ -1904,7 +1888,7 @@ static void of_unittest_overlay_i2c_12(void)
} }
/* test deactivation of device */ /* test deactivation of device */
static void of_unittest_overlay_i2c_13(void) static void __init of_unittest_overlay_i2c_13(void)
{ {
int ret; int ret;
@ -1921,7 +1905,7 @@ static void of_unittest_overlay_i2c_14(void)
{ {
} }
static void of_unittest_overlay_i2c_15(void) static void __init of_unittest_overlay_i2c_15(void)
{ {
int ret; int ret;
@ -2023,23 +2007,38 @@ static inline void __init of_unittest_overlay(void) { }
extern uint8_t __dtb_##name##_begin[]; \ extern uint8_t __dtb_##name##_begin[]; \
extern uint8_t __dtb_##name##_end[] extern uint8_t __dtb_##name##_end[]
#define OVERLAY_INFO(name, expected) \ #define OVERLAY_INFO(overlay_name, expected) \
{ .dtb_begin = __dtb_##name##_begin, \ { .dtb_begin = __dtb_##overlay_name##_begin, \
.dtb_end = __dtb_##name##_end, \ .dtb_end = __dtb_##overlay_name##_end, \
.expected_result = expected, \ .expected_result = expected, \
.name = #overlay_name, \
} }
struct overlay_info { struct overlay_info {
uint8_t *dtb_begin; uint8_t *dtb_begin;
uint8_t *dtb_end; uint8_t *dtb_end;
void *data; int expected_result;
struct device_node *np_overlay; int overlay_id;
int expected_result; char *name;
int overlay_id;
}; };
OVERLAY_INFO_EXTERN(overlay_base); OVERLAY_INFO_EXTERN(overlay_base);
OVERLAY_INFO_EXTERN(overlay); OVERLAY_INFO_EXTERN(overlay);
OVERLAY_INFO_EXTERN(overlay_0);
OVERLAY_INFO_EXTERN(overlay_1);
OVERLAY_INFO_EXTERN(overlay_2);
OVERLAY_INFO_EXTERN(overlay_3);
OVERLAY_INFO_EXTERN(overlay_4);
OVERLAY_INFO_EXTERN(overlay_5);
OVERLAY_INFO_EXTERN(overlay_6);
OVERLAY_INFO_EXTERN(overlay_7);
OVERLAY_INFO_EXTERN(overlay_8);
OVERLAY_INFO_EXTERN(overlay_9);
OVERLAY_INFO_EXTERN(overlay_10);
OVERLAY_INFO_EXTERN(overlay_11);
OVERLAY_INFO_EXTERN(overlay_12);
OVERLAY_INFO_EXTERN(overlay_13);
OVERLAY_INFO_EXTERN(overlay_15);
OVERLAY_INFO_EXTERN(overlay_bad_phandle); OVERLAY_INFO_EXTERN(overlay_bad_phandle);
OVERLAY_INFO_EXTERN(overlay_bad_symbol); OVERLAY_INFO_EXTERN(overlay_bad_symbol);
@ -2047,6 +2046,21 @@ OVERLAY_INFO_EXTERN(overlay_bad_symbol);
static struct overlay_info overlays[] = { static struct overlay_info overlays[] = {
OVERLAY_INFO(overlay_base, -9999), OVERLAY_INFO(overlay_base, -9999),
OVERLAY_INFO(overlay, 0), OVERLAY_INFO(overlay, 0),
OVERLAY_INFO(overlay_0, 0),
OVERLAY_INFO(overlay_1, 0),
OVERLAY_INFO(overlay_2, 0),
OVERLAY_INFO(overlay_3, 0),
OVERLAY_INFO(overlay_4, 0),
OVERLAY_INFO(overlay_5, 0),
OVERLAY_INFO(overlay_6, 0),
OVERLAY_INFO(overlay_7, 0),
OVERLAY_INFO(overlay_8, 0),
OVERLAY_INFO(overlay_9, 0),
OVERLAY_INFO(overlay_10, 0),
OVERLAY_INFO(overlay_11, 0),
OVERLAY_INFO(overlay_12, 0),
OVERLAY_INFO(overlay_13, 0),
OVERLAY_INFO(overlay_15, 0),
OVERLAY_INFO(overlay_bad_phandle, -EINVAL), OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
OVERLAY_INFO(overlay_bad_symbol, -EINVAL), OVERLAY_INFO(overlay_bad_symbol, -EINVAL),
{} {}
@ -2077,6 +2091,7 @@ void __init unittest_unflatten_overlay_base(void)
{ {
struct overlay_info *info; struct overlay_info *info;
u32 data_size; u32 data_size;
void *new_fdt;
u32 size; u32 size;
info = &overlays[0]; info = &overlays[0];
@ -2098,17 +2113,16 @@ void __init unittest_unflatten_overlay_base(void)
return; return;
} }
info->data = dt_alloc_memory(size, roundup_pow_of_two(FDT_V17_SIZE)); new_fdt = dt_alloc_memory(size, roundup_pow_of_two(FDT_V17_SIZE));
if (!info->data) { if (!new_fdt) {
pr_err("alloc for dtb 'overlay_base' failed"); pr_err("alloc for dtb 'overlay_base' failed");
return; return;
} }
memcpy(info->data, info->dtb_begin, size); memcpy(new_fdt, info->dtb_begin, size);
__unflatten_device_tree(info->data, NULL, &info->np_overlay, __unflatten_device_tree(new_fdt, NULL, &overlay_base_root,
dt_alloc_memory, true); dt_alloc_memory, true);
overlay_base_root = info->np_overlay;
} }
/* /*
@ -2122,73 +2136,44 @@ void __init unittest_unflatten_overlay_base(void)
* *
* Return 0 on unexpected error. * Return 0 on unexpected error.
*/ */
static int __init overlay_data_add(int onum) static int __init overlay_data_apply(const char *overlay_name, int *overlay_id)
{ {
struct overlay_info *info; struct overlay_info *info;
int found = 0;
int k; int k;
int ret; int ret;
u32 size; u32 size;
u32 size_from_header;
for (k = 0, info = overlays; info; info++, k++) { for (k = 0, info = overlays; info && info->name; info++, k++) {
if (k == onum) if (!strcmp(overlay_name, info->name)) {
found = 1;
break; break;
}
} }
if (onum > k) if (!found) {
pr_err("no overlay data for %s\n", overlay_name);
return 0; return 0;
}
size = info->dtb_end - info->dtb_begin; size = info->dtb_end - info->dtb_begin;
if (!size) { if (!size) {
pr_err("no overlay to attach, %d\n", onum); pr_err("no overlay data for %s\n", overlay_name);
ret = 0; ret = 0;
} }
size_from_header = fdt_totalsize(info->dtb_begin); ret = of_overlay_fdt_apply(info->dtb_begin, size, &info->overlay_id);
if (size_from_header != size) { if (overlay_id)
pr_err("overlay header totalsize != actual size, %d", onum); *overlay_id = info->overlay_id;
return 0; if (ret < 0)
} goto out;
/* pr_debug("%s applied\n", overlay_name);
* 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;
}
info->overlay_id = 0;
ret = of_overlay_apply(info->np_overlay, &info->overlay_id);
if (ret < 0) {
pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum);
goto out_free_np_overlay;
}
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: out:
if (ret != info->expected_result)
pr_err("of_overlay_fdt_apply() expected %d, ret=%d, %s\n",
info->expected_result, ret, overlay_name);
return (ret == info->expected_result); return (ret == info->expected_result);
} }
@ -2290,18 +2275,29 @@ static __init void of_unittest_overlay_high_level(void)
__of_attach_node_sysfs(np); __of_attach_node_sysfs(np);
if (of_symbols) { if (of_symbols) {
struct property *new_prop;
for_each_property_of_node(overlay_base_symbols, prop) { for_each_property_of_node(overlay_base_symbols, prop) {
ret = __of_add_property(of_symbols, prop);
if (ret) { new_prop = __of_prop_dup(prop, GFP_KERNEL);
unittest(0, if (!new_prop) {
"duplicate property '%s' in overlay_base node __symbols__", unittest(0, "__of_prop_dup() of '%s' from overlay_base node __symbols__",
prop->name); prop->name);
goto err_unlock; goto err_unlock;
} }
ret = __of_add_property_sysfs(of_symbols, prop); ret = __of_add_property(of_symbols, new_prop);
if (ret) { if (ret) {
unittest(0, if (!strcmp(new_prop->name, "name")) {
"unable to add property '%s' in overlay_base node __symbols__ to sysfs", /* auto-generated by unflatten */
ret = 0;
continue;
}
unittest(0, "duplicate property '%s' in overlay_base node __symbols__",
prop->name);
goto err_unlock;
}
ret = __of_add_property_sysfs(of_symbols, new_prop);
if (ret) {
unittest(0, "unable to add property '%s' in overlay_base node __symbols__ to sysfs",
prop->name); prop->name);
goto err_unlock; goto err_unlock;
} }
@ -2313,13 +2309,13 @@ static __init void of_unittest_overlay_high_level(void)
/* now do the normal overlay usage test */ /* now do the normal overlay usage test */
unittest(overlay_data_add(1), unittest(overlay_data_apply("overlay", NULL),
"Adding overlay 'overlay' failed\n"); "Adding overlay 'overlay' failed\n");
unittest(overlay_data_add(2), unittest(overlay_data_apply("overlay_bad_phandle", NULL),
"Adding overlay 'overlay_bad_phandle' failed\n"); "Adding overlay 'overlay_bad_phandle' failed\n");
unittest(overlay_data_add(3), unittest(overlay_data_apply("overlay_bad_symbol", NULL),
"Adding overlay 'overlay_bad_symbol' failed\n"); "Adding overlay 'overlay_bad_symbol' failed\n");
return; return;

View File

@ -1359,8 +1359,8 @@ struct of_overlay_notify_data {
#ifdef CONFIG_OF_OVERLAY #ifdef CONFIG_OF_OVERLAY
/* ID based overlays; the API for external users */ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
int of_overlay_apply(struct device_node *tree, int *ovcs_id); int *ovcs_id);
int of_overlay_remove(int *ovcs_id); int of_overlay_remove(int *ovcs_id);
int of_overlay_remove_all(void); int of_overlay_remove_all(void);
@ -1369,7 +1369,7 @@ int of_overlay_notifier_unregister(struct notifier_block *nb);
#else #else
static inline int of_overlay_apply(struct device_node *tree, int *ovcs_id) static inline int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id)
{ {
return -ENOTSUPP; return -ENOTSUPP;
} }