Commit 39a751a4 authored by Frank Rowand's avatar Frank Rowand Committed by Frank Rowand

of: change overlay apply input data from unflattened to FDT

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().

The copy of the FDT (aka "duplicate FDT") now belongs to devicetree
code, which is thus responsible for freeing the duplicate FDT.  The
caller of of_overlay_fdt_apply() remains responsible for freeing the
original FDT.

The unflattened devicetree now belongs to devicetree code, which is
thus responsible for freeing the unflattened devicetree.

These ownership changes prevent early freeing of the duplicated FDT
or the unflattened devicetree, which could result in use after free
errors.

of_overlay_fdt_apply() is a private function for the anticipated
overlay loader.

Update unittest.c to use of_overlay_fdt_apply() instead of
of_overlay_apply().

Move overlay fragments from artificial locations in
drivers/of/unittest-data/tests-overlay.dtsi into one devicetree
source file per overlay.  This led to changes in
drivers/of/unitest-data/Makefile and drivers/of/unitest.c.

  - Add overlay directives to the overlay devicetree source files so
    that dtc will compile them as true overlays into one FDT data
    chunk per overlay.

  - Set CFLAGS for drivers/of/unittest-data/testcases.dts so that
    symbols will be generated for overlay resolution of overlays
    that are no longer artificially contained in testcases.dts

  - Unflatten and apply each unittest overlay FDT using
    of_overlay_fdt_apply().

  - Enable the of_resolve_phandles() check for whether the unflattened
    overlay is detached.  This check was previously disabled because the
    overlays from tests-overlay.dtsi were not unflattened into detached
    trees.

  - Other changes to unittest.c infrastructure to manage multiple test
    FDTs built into the kernel image (access by name instead of
    arbitrary number).

  - of_unittest_overlay_high_level(): previously unused code to add
    properties from the overlay_base devicetree to the live tree
    was triggered by the restructuring of tests-overlay.dtsi and thus
    testcases.dts.  This exposed two bugs: (1) the need to dup a
    property before adding it, and (2) property 'name' is
    auto-generated in the unflatten code and thus will be a duplicate
    in the __symbols__ node - do not treat this duplicate as an error.
Signed-off-by: default avatarFrank Rowand <frank.rowand@sony.com>
parent 581e9290
......@@ -92,6 +92,7 @@ config OF_RESOLVE
config OF_OVERLAY
bool "Device Tree overlays"
select OF_DYNAMIC
select OF_FLATTREE
select OF_RESOLVE
help
Overlays are a method to dynamically modify part of the kernel's
......
......@@ -12,10 +12,12 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_fdt.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/libfdt.h>
#include <linux/err.h>
#include <linux/idr.h>
......@@ -33,7 +35,9 @@ struct fragment {
/**
* struct overlay_changeset
* @id: changeset identifier
* @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
* @count: count of fragment structures
* @fragments: fragment nodes in the overlay expanded device tree
......@@ -43,6 +47,7 @@ struct fragment {
struct overlay_changeset {
int id;
struct list_head ovcs_list;
const void *fdt;
struct device_node *overlay_tree;
int count;
struct fragment *fragments;
......@@ -503,7 +508,8 @@ static struct device_node *find_target_node(struct device_node *info_node)
/**
* 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
*
* Initialize @ovcs. Populate @ovcs->fragments with node information from
......@@ -514,7 +520,7 @@ static struct device_node *find_target_node(struct device_node *info_node)
* detected in @tree, or -ENOSPC if idr_alloc() error.
*/
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 fragment *fragment;
......@@ -535,6 +541,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
pr_debug("%s() tree is not root\n", __func__);
ovcs->overlay_tree = tree;
ovcs->fdt = fdt;
INIT_LIST_HEAD(&ovcs->ovcs_list);
......@@ -606,6 +613,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
}
if (!cnt) {
pr_err("no fragments or symbols in overlay\n");
ret = -EINVAL;
goto err_free_fragments;
}
......@@ -642,11 +650,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
}
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);
}
/**
/*
* internal documentation
*
* of_overlay_apply() - Create and apply an overlay changeset
* @fdt: the FDT that was unflattened to create @tree
* @tree: Expanded overlay device tree
* @ovcs_id: Pointer to overlay changeset id
*
......@@ -685,21 +706,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
* 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;
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()) {
pr_err("devicetree state suspect, refuse to apply overlay\n");
kfree(fdt);
kfree(tree);
ret = -EBUSY;
goto out;
}
ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
if (!ovcs) {
kfree(fdt);
kfree(tree);
ret = -ENOMEM;
goto out;
}
......@@ -709,12 +738,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
ret = of_resolve_phandles(tree);
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)
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);
if (ret) {
pr_err("overlay changeset pre-apply notify error %d\n", ret);
......@@ -754,6 +788,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
goto out_unlock;
err_free_tree:
kfree(fdt);
kfree(tree);
err_free_overlay_changeset:
free_overlay_changeset(ovcs);
......@@ -766,7 +804,63 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
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.
......
......@@ -269,17 +269,11 @@ int of_resolve_phandles(struct device_node *overlay)
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)) {
pr_err("overlay not detached\n");
err = -EINVAL;
goto out;
}
#endif
phandle_delta = live_tree_max_phandle() + 1;
adjust_overlay_phandles(overlay, phandle_delta);
......
# SPDX-License-Identifier: GPL-2.0
DTC_FLAGS_testcases := -Wno-interrupts_property
obj-y += testcases.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_symbol.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)))
# enable creation of __symbols__ node
DTC_FLAGS_overlay := -@
DTC_FLAGS_overlay_bad_phandle := -@
DTC_FLAGS_overlay_bad_symbol := -@
DTC_FLAGS_overlay_base := -@
DTC_FLAGS_overlay += -@
DTC_FLAGS_overlay_bad_phandle += -@
DTC_FLAGS_overlay_bad_symbol += -@
DTC_FLAGS_overlay_base += -@
DTC_FLAGS_testcases += -@
# suppress warnings about intentional errors
DTC_FLAGS_testcases += -Wno-interrupts_property
.PRECIOUS: \
$(obj)/%.dtb.S \
......
// 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";
};
};
};
// 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";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_10 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
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>;
};
};
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_11 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
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>;
};
};
};
};
};
// 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";
};
};
};
// 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";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_15 - mux overlay */
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";
};
};
};
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_2 - enable using label */
fragment@0 {
target = <&unittest2>;
__overlay__ {
status = "okay";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_3 - disable using label */
fragment@0 {
target = <&unittest3>;
__overlay__ {
status = "disabled";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_4 - test insertion of a full node */
fragment@0 {
target = <&unittestbus>;
__overlay__ {
/* suppress DTC warning */
#address-cells = <1>;
#size-cells = <0>;
test-unittest4 {
compatible = "unittest";
status = "okay";
reg = <4>;
};
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_5 - test overlay apply revert */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest5";
__overlay__ {
status = "okay";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_6 */
/* overlays 6, 7 application and removal in sequence */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest6";
__overlay__ {
status = "okay";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_7 */
/* overlays 6, 7 application and removal in sequence */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest7";
__overlay__ {
status = "okay";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_8 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
__overlay__ {
status = "okay";
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/ {
/* overlay_9 */
/* overlays 8, 9, 10, 11 application and removal in bad sequence */
fragment@0 {
target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
__overlay__ {
property-foo = "bar";
};
};
};
......@@ -113,218 +113,5 @@ test-mux-dev {
};
};
};
/* 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";
};
};
};
};
};
};
};
};
This diff is collapsed.
......@@ -1359,8 +1359,8 @@ struct of_overlay_notify_data {
#ifdef CONFIG_OF_OVERLAY
/* ID based overlays; the API for external users */
int of_overlay_apply(struct device_node *tree, int *ovcs_id);
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
int *ovcs_id);
int of_overlay_remove(int *ovcs_id);
int of_overlay_remove_all(void);
......@@ -1369,7 +1369,7 @@ int of_overlay_notifier_unregister(struct notifier_block *nb);
#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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment