Skip to content

Commit 94aae1d

Browse files
author
Laurent Pinchart
committed
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().
2 parents f073d78 + e547c00 commit 94aae1d

27 files changed

+586
-487
lines changed

Documentation/devicetree/overlay-notes.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ Overlay in-kernel API
8787

8888
The API is quite easy to use.
8989

90-
1. Call of_overlay_apply() to create and apply an overlay changeset. The return
91-
value is an error or a cookie identifying this overlay.
90+
1. Call of_overlay_fdt_apply() to create and apply an overlay changeset. The
91+
return value is an error or a cookie identifying this overlay.
9292

9393
2. Call of_overlay_remove() to remove and cleanup the overlay changeset
9494
previously created via the call to of_overlay_apply(). Removal of an overlay

arch/x86/kernel/devicetree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ static void __init dtb_apic_setup(void)
259259
dtb_ioapic_setup();
260260
}
261261

262-
#ifdef CONFIG_OF_FLATTREE
262+
#ifdef CONFIG_OF_EARLY_FLATTREE
263263
static void __init x86_flattree_get_config(void)
264264
{
265265
u32 size, map_len;

drivers/of/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ config OF_RESOLVE
9292
config OF_OVERLAY
9393
bool "Device Tree overlays"
9494
select OF_DYNAMIC
95+
select OF_FLATTREE
9596
select OF_RESOLVE
9697
help
9798
Overlays are a method to dynamically modify part of the kernel's

drivers/of/overlay.c

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
#include <linux/module.h>
1313
#include <linux/of.h>
1414
#include <linux/of_device.h>
15+
#include <linux/of_fdt.h>
1516
#include <linux/string.h>
1617
#include <linux/ctype.h>
1718
#include <linux/errno.h>
1819
#include <linux/slab.h>
20+
#include <linux/libfdt.h>
1921
#include <linux/err.h>
2022
#include <linux/idr.h>
2123

@@ -33,7 +35,9 @@ struct fragment {
3335

3436
/**
3537
* struct overlay_changeset
38+
* @id: changeset identifier
3639
* @ovcs_list: list on which we are located
40+
* @fdt: FDT that was unflattened to create @overlay_tree
3741
* @overlay_tree: expanded device tree that contains the fragment nodes
3842
* @count: count of fragment structures
3943
* @fragments: fragment nodes in the overlay expanded device tree
@@ -43,6 +47,7 @@ struct fragment {
4347
struct overlay_changeset {
4448
int id;
4549
struct list_head ovcs_list;
50+
const void *fdt;
4651
struct device_node *overlay_tree;
4752
int count;
4853
struct fragment *fragments;
@@ -483,27 +488,38 @@ static int build_changeset(struct overlay_changeset *ovcs)
483488
*/
484489
static struct device_node *find_target_node(struct device_node *info_node)
485490
{
491+
struct device_node *node;
486492
const char *path;
487493
u32 val;
488494
int ret;
489495

490496
ret = of_property_read_u32(info_node, "target", &val);
491-
if (!ret)
492-
return of_find_node_by_phandle(val);
497+
if (!ret) {
498+
node = of_find_node_by_phandle(val);
499+
if (!node)
500+
pr_err("find target, node: %pOF, phandle 0x%x not found\n",
501+
info_node, val);
502+
return node;
503+
}
493504

494505
ret = of_property_read_string(info_node, "target-path", &path);
495-
if (!ret)
496-
return of_find_node_by_path(path);
506+
if (!ret) {
507+
node = of_find_node_by_path(path);
508+
if (!node)
509+
pr_err("find target, node: %pOF, path '%s' not found\n",
510+
info_node, path);
511+
return node;
512+
}
497513

498-
pr_err("Failed to find target for node %p (%s)\n",
499-
info_node, info_node->name);
514+
pr_err("find target, node: %pOF, no target property\n", info_node);
500515

501516
return NULL;
502517
}
503518

504519
/**
505520
* init_overlay_changeset() - initialize overlay changeset from overlay tree
506-
* @ovcs Overlay changeset to build
521+
* @ovcs: Overlay changeset to build
522+
* @fdt: the FDT that was unflattened to create @tree
507523
* @tree: Contains all the overlay fragments and overlay fixup nodes
508524
*
509525
* 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)
514530
* detected in @tree, or -ENOSPC if idr_alloc() error.
515531
*/
516532
static int init_overlay_changeset(struct overlay_changeset *ovcs,
517-
struct device_node *tree)
533+
const void *fdt, struct device_node *tree)
518534
{
519535
struct device_node *node, *overlay_node;
520536
struct fragment *fragment;
@@ -535,6 +551,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
535551
pr_debug("%s() tree is not root\n", __func__);
536552

537553
ovcs->overlay_tree = tree;
554+
ovcs->fdt = fdt;
538555

539556
INIT_LIST_HEAD(&ovcs->ovcs_list);
540557

@@ -606,6 +623,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
606623
}
607624

608625
if (!cnt) {
626+
pr_err("no fragments or symbols in overlay\n");
609627
ret = -EINVAL;
610628
goto err_free_fragments;
611629
}
@@ -642,11 +660,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
642660
}
643661
kfree(ovcs->fragments);
644662

663+
/*
664+
* TODO
665+
*
666+
* would like to: kfree(ovcs->overlay_tree);
667+
* but can not since drivers may have pointers into this data
668+
*
669+
* would like to: kfree(ovcs->fdt);
670+
* but can not since drivers may have pointers into this data
671+
*/
672+
645673
kfree(ovcs);
646674
}
647675

648-
/**
676+
/*
677+
* internal documentation
678+
*
649679
* of_overlay_apply() - Create and apply an overlay changeset
680+
* @fdt: the FDT that was unflattened to create @tree
650681
* @tree: Expanded overlay device tree
651682
* @ovcs_id: Pointer to overlay changeset id
652683
*
@@ -685,21 +716,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
685716
* id is returned to *ovcs_id.
686717
*/
687718

688-
int of_overlay_apply(struct device_node *tree, int *ovcs_id)
719+
static int of_overlay_apply(const void *fdt, struct device_node *tree,
720+
int *ovcs_id)
689721
{
690722
struct overlay_changeset *ovcs;
691723
int ret = 0, ret_revert, ret_tmp;
692724

693-
*ovcs_id = 0;
725+
/*
726+
* As of this point, fdt and tree belong to the overlay changeset.
727+
* overlay changeset code is responsible for freeing them.
728+
*/
694729

695730
if (devicetree_corrupt()) {
696731
pr_err("devicetree state suspect, refuse to apply overlay\n");
732+
kfree(fdt);
733+
kfree(tree);
697734
ret = -EBUSY;
698735
goto out;
699736
}
700737

701738
ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
702739
if (!ovcs) {
740+
kfree(fdt);
741+
kfree(tree);
703742
ret = -ENOMEM;
704743
goto out;
705744
}
@@ -709,12 +748,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
709748

710749
ret = of_resolve_phandles(tree);
711750
if (ret)
712-
goto err_free_overlay_changeset;
751+
goto err_free_tree;
713752

714-
ret = init_overlay_changeset(ovcs, tree);
753+
ret = init_overlay_changeset(ovcs, fdt, tree);
715754
if (ret)
716-
goto err_free_overlay_changeset;
755+
goto err_free_tree;
717756

757+
/*
758+
* after overlay_notify(), ovcs->overlay_tree related pointers may have
759+
* leaked to drivers, so can not kfree() tree, aka ovcs->overlay_tree;
760+
* and can not free fdt, aka ovcs->fdt
761+
*/
718762
ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
719763
if (ret) {
720764
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)
754798

755799
goto out_unlock;
756800

801+
err_free_tree:
802+
kfree(fdt);
803+
kfree(tree);
804+
757805
err_free_overlay_changeset:
758806
free_overlay_changeset(ovcs);
759807

@@ -766,7 +814,63 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
766814

767815
return ret;
768816
}
769-
EXPORT_SYMBOL_GPL(of_overlay_apply);
817+
818+
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
819+
int *ovcs_id)
820+
{
821+
const void *new_fdt;
822+
int ret;
823+
u32 size;
824+
struct device_node *overlay_root;
825+
826+
*ovcs_id = 0;
827+
ret = 0;
828+
829+
if (overlay_fdt_size < sizeof(struct fdt_header) ||
830+
fdt_check_header(overlay_fdt)) {
831+
pr_err("Invalid overlay_fdt header\n");
832+
return -EINVAL;
833+
}
834+
835+
size = fdt_totalsize(overlay_fdt);
836+
if (overlay_fdt_size < size)
837+
return -EINVAL;
838+
839+
/*
840+
* Must create permanent copy of FDT because of_fdt_unflatten_tree()
841+
* will create pointers to the passed in FDT in the unflattened tree.
842+
*/
843+
new_fdt = kmemdup(overlay_fdt, size, GFP_KERNEL);
844+
if (!new_fdt)
845+
return -ENOMEM;
846+
847+
of_fdt_unflatten_tree(new_fdt, NULL, &overlay_root);
848+
if (!overlay_root) {
849+
pr_err("unable to unflatten overlay_fdt\n");
850+
ret = -EINVAL;
851+
goto out_free_new_fdt;
852+
}
853+
854+
ret = of_overlay_apply(new_fdt, overlay_root, ovcs_id);
855+
if (ret < 0) {
856+
/*
857+
* new_fdt and overlay_root now belong to the overlay
858+
* changeset.
859+
* overlay changeset code is responsible for freeing them.
860+
*/
861+
goto out;
862+
}
863+
864+
return 0;
865+
866+
867+
out_free_new_fdt:
868+
kfree(new_fdt);
869+
870+
out:
871+
return ret;
872+
}
873+
EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
770874

771875
/*
772876
* Find @np in @tree.

drivers/of/resolver.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,17 +269,11 @@ int of_resolve_phandles(struct device_node *overlay)
269269
goto out;
270270
}
271271

272-
#if 0
273-
Temporarily disable check so that old style overlay unittests
274-
do not fail when of_resolve_phandles() is moved into
275-
of_overlay_apply().
276-
277272
if (!of_node_check_flag(overlay, OF_DETACHED)) {
278273
pr_err("overlay not detached\n");
279274
err = -EINVAL;
280275
goto out;
281276
}
282-
#endif
283277

284278
phandle_delta = live_tree_max_phandle() + 1;
285279
adjust_overlay_phandles(overlay, phandle_delta);

drivers/of/unittest-data/Makefile

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
DTC_FLAGS_testcases := -Wno-interrupts_property
32
obj-y += testcases.dtb.o
43

54
obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
5+
overlay_0.dtb.o \
6+
overlay_1.dtb.o \
7+
overlay_2.dtb.o \
8+
overlay_3.dtb.o \
9+
overlay_4.dtb.o \
10+
overlay_5.dtb.o \
11+
overlay_6.dtb.o \
12+
overlay_7.dtb.o \
13+
overlay_8.dtb.o \
14+
overlay_9.dtb.o \
15+
overlay_10.dtb.o \
16+
overlay_11.dtb.o \
17+
overlay_12.dtb.o \
18+
overlay_13.dtb.o \
19+
overlay_15.dtb.o \
620
overlay_bad_phandle.dtb.o \
721
overlay_bad_symbol.dtb.o \
822
overlay_base.dtb.o
923

1024
targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y)))
1125

1226
# enable creation of __symbols__ node
13-
DTC_FLAGS_overlay := -@
14-
DTC_FLAGS_overlay_bad_phandle := -@
15-
DTC_FLAGS_overlay_bad_symbol := -@
16-
DTC_FLAGS_overlay_base := -@
27+
DTC_FLAGS_overlay += -@
28+
DTC_FLAGS_overlay_bad_phandle += -@
29+
DTC_FLAGS_overlay_bad_symbol += -@
30+
DTC_FLAGS_overlay_base += -@
31+
DTC_FLAGS_testcases += -@
32+
33+
# suppress warnings about intentional errors
34+
DTC_FLAGS_testcases += -Wno-interrupts_property
1735

1836
.PRECIOUS: \
1937
$(obj)/%.dtb.S \

0 commit comments

Comments
 (0)