Commit 94aae1d9 authored by Laurent Pinchart's avatar Laurent Pinchart

Merge tag 'overlay_apply_fdt_v7-for-4.17' of...

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().
parents f073d78e e547c003
......@@ -87,8 +87,8 @@ Overlay in-kernel API
The API is quite easy to use.
1. Call of_overlay_apply() to create and apply an overlay changeset. The return
value is an error or a cookie identifying this overlay.
1. Call of_overlay_fdt_apply() to create and apply an overlay changeset. The
return value is an error or a cookie identifying this overlay.
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
......
......@@ -259,7 +259,7 @@ static void __init dtb_apic_setup(void)
dtb_ioapic_setup();
}
#ifdef CONFIG_OF_FLATTREE
#ifdef CONFIG_OF_EARLY_FLATTREE
static void __init x86_flattree_get_config(void)
{
u32 size, map_len;
......
......@@ -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;
......@@ -483,27 +488,38 @@ static int build_changeset(struct overlay_changeset *ovcs)
*/
static struct device_node *find_target_node(struct device_node *info_node)
{
struct device_node *node;
const char *path;
u32 val;
int ret;
ret = of_property_read_u32(info_node, "target", &val);
if (!ret)
return of_find_node_by_phandle(val);
if (!ret) {
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);
if (!ret)
return of_find_node_by_path(path);
if (!ret) {
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",
info_node, info_node->name);
pr_err("find target, node: %pOF, no target property\n", info_node);
return NULL;
}
/**
* 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 +530,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 +551,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 +623,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 +660,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 +716,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 +748,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 +798,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 +814,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 \
......
......@@ -2,12 +2,8 @@
/dts-v1/;
/plugin/;
/ {
&electric_1 {
fragment@0 {
target = <&electric_1>;
__overlay__ {
status = "okay";
hvac_2: hvac-large-1 {
......@@ -15,13 +11,10 @@ hvac_2: hvac-large-1 {
heat-range = < 40 75 >;
cool-range = < 65 80 >;
};
};
};
};
fragment@1 {
target = <&rides_1>;
&rides_1 {
__overlay__ {
#address-cells = <1>;
#size-cells = <1>;
status = "okay";
......@@ -61,17 +54,11 @@ ride_200_right: track@20 {
reg = < 0x00000020 0x10 >;
};
};
};
};
};
fragment@2 {
target = <&lights_2>;
&lights_2 {
__overlay__ {
status = "okay";
color = "purple", "white", "red", "green";
rate = < 3 256 >;
};
};
};
// 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 */
&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>;
};
};
};
// 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>;
};
};
};
// 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 */
&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";
};
};
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_2 - enable using label */
&unittest2 {
status = "okay";
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_3 - disable using label */
&unittest3 {
status = "disabled";
};
// 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>;
};
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_5 - test overlay apply revert */
&unittest5 {
status = "okay";
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_6 */
/* overlays 6, 7 application and removal in sequence */
&unittest6 {
status = "okay";
};
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
/plugin/;
/* overlay_7 */
/* overlays 6, 7 application and removal in sequence */
&unittest7 {
status = "okay";
};
// 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";
};
// 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";
};
......@@ -2,12 +2,7 @@
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&electric_1>;
__overlay__ {
&electric_1 {
// This label should cause an error when the overlay
// is applied. There is already a phandle value
......@@ -16,6 +11,4 @@ spin_ctrl_1_conflict: motor-1 {
accelerate = < 3 >;
decelerate = < 5 >;
};
};
};
};
......@@ -2,12 +2,7 @@
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&electric_1>;
__overlay__ {
&electric_1 {
// This label should cause an error when the overlay
// is applied. There is already a symbol hvac_1
......@@ -18,6 +13,4 @@ hvac_1: hvac-medium-2 {
cool-range = < 60 80 >;
};
};
};
};
......@@ -5,7 +5,7 @@ testcase-data {
overlay-node {
/* test bus */
unittestbus: test-bus {
unittest_test_bus: test-bus {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
......@@ -70,7 +70,7 @@ unittest8: test-unittest8 {
reg = <8>;
};
i2c-test-bus {
unittest_i2c_test_bus: i2c-test-bus {
compatible = "unittest-i2c-bus";
status = "okay";
reg = <50>;
......@@ -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