Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b46c7866 authored by Rob Herring's avatar Rob Herring
Browse files

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 dt/next

DT overlay applying rework from 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 c11e6f0f e547c003
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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
+119 −15
Original line number Diff line number Diff line
@@ -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.
+0 −6
Original line number Diff line number Diff line
@@ -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);
Loading