162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Functions for working with device tree overlays
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
662306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt)	"OF: overlay: " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/of_device.h>
1562306a36Sopenharmony_ci#include <linux/of_fdt.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/ctype.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/libfdt.h>
2162306a36Sopenharmony_ci#include <linux/err.h>
2262306a36Sopenharmony_ci#include <linux/idr.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "of_private.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * struct target - info about current target node as recursing through overlay
2862306a36Sopenharmony_ci * @np:			node where current level of overlay will be applied
2962306a36Sopenharmony_ci * @in_livetree:	@np is a node in the live devicetree
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * Used in the algorithm to create the portion of a changeset that describes
3262306a36Sopenharmony_ci * an overlay fragment, which is a devicetree subtree.  Initially @np is a node
3362306a36Sopenharmony_ci * in the live devicetree where the overlay subtree is targeted to be grafted
3462306a36Sopenharmony_ci * into.  When recursing to the next level of the overlay subtree, the target
3562306a36Sopenharmony_ci * also recurses to the next level of the live devicetree, as long as overlay
3662306a36Sopenharmony_ci * subtree node also exists in the live devicetree.  When a node in the overlay
3762306a36Sopenharmony_ci * subtree does not exist at the same level in the live devicetree, target->np
3862306a36Sopenharmony_ci * points to a newly allocated node, and all subsequent targets in the subtree
3962306a36Sopenharmony_ci * will be newly allocated nodes.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_cistruct target {
4262306a36Sopenharmony_ci	struct device_node *np;
4362306a36Sopenharmony_ci	bool in_livetree;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/**
4762306a36Sopenharmony_ci * struct fragment - info about fragment nodes in overlay expanded device tree
4862306a36Sopenharmony_ci * @overlay:	pointer to the __overlay__ node
4962306a36Sopenharmony_ci * @target:	target of the overlay operation
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_cistruct fragment {
5262306a36Sopenharmony_ci	struct device_node *overlay;
5362306a36Sopenharmony_ci	struct device_node *target;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/**
5762306a36Sopenharmony_ci * struct overlay_changeset
5862306a36Sopenharmony_ci * @id:			changeset identifier
5962306a36Sopenharmony_ci * @ovcs_list:		list on which we are located
6062306a36Sopenharmony_ci * @new_fdt:		Memory allocated to hold unflattened aligned FDT
6162306a36Sopenharmony_ci * @overlay_mem:	the memory chunk that contains @overlay_root
6262306a36Sopenharmony_ci * @overlay_root:	expanded device tree that contains the fragment nodes
6362306a36Sopenharmony_ci * @notify_state:	most recent notify action used on overlay
6462306a36Sopenharmony_ci * @count:		count of fragment structures
6562306a36Sopenharmony_ci * @fragments:		fragment nodes in the overlay expanded device tree
6662306a36Sopenharmony_ci * @symbols_fragment:	last element of @fragments[] is the  __symbols__ node
6762306a36Sopenharmony_ci * @cset:		changeset to apply fragments to live device tree
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistruct overlay_changeset {
7062306a36Sopenharmony_ci	int id;
7162306a36Sopenharmony_ci	struct list_head ovcs_list;
7262306a36Sopenharmony_ci	const void *new_fdt;
7362306a36Sopenharmony_ci	const void *overlay_mem;
7462306a36Sopenharmony_ci	struct device_node *overlay_root;
7562306a36Sopenharmony_ci	enum of_overlay_notify_action notify_state;
7662306a36Sopenharmony_ci	int count;
7762306a36Sopenharmony_ci	struct fragment *fragments;
7862306a36Sopenharmony_ci	bool symbols_fragment;
7962306a36Sopenharmony_ci	struct of_changeset cset;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* flags are sticky - once set, do not reset */
8362306a36Sopenharmony_cistatic int devicetree_state_flags;
8462306a36Sopenharmony_ci#define DTSF_APPLY_FAIL		0x01
8562306a36Sopenharmony_ci#define DTSF_REVERT_FAIL	0x02
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * If a changeset apply or revert encounters an error, an attempt will
8962306a36Sopenharmony_ci * be made to undo partial changes, but may fail.  If the undo fails
9062306a36Sopenharmony_ci * we do not know the state of the devicetree.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic int devicetree_corrupt(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return devicetree_state_flags &
9562306a36Sopenharmony_ci		(DTSF_APPLY_FAIL | DTSF_REVERT_FAIL);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int build_changeset_next_level(struct overlay_changeset *ovcs,
9962306a36Sopenharmony_ci		struct target *target, const struct device_node *overlay_node);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/*
10262306a36Sopenharmony_ci * of_resolve_phandles() finds the largest phandle in the live tree.
10362306a36Sopenharmony_ci * of_overlay_apply() may add a larger phandle to the live tree.
10462306a36Sopenharmony_ci * Do not allow race between two overlays being applied simultaneously:
10562306a36Sopenharmony_ci *    mutex_lock(&of_overlay_phandle_mutex)
10662306a36Sopenharmony_ci *    of_resolve_phandles()
10762306a36Sopenharmony_ci *    of_overlay_apply()
10862306a36Sopenharmony_ci *    mutex_unlock(&of_overlay_phandle_mutex)
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic DEFINE_MUTEX(of_overlay_phandle_mutex);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid of_overlay_mutex_lock(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	mutex_lock(&of_overlay_phandle_mutex);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid of_overlay_mutex_unlock(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	mutex_unlock(&of_overlay_phandle_mutex);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic LIST_HEAD(ovcs_list);
12362306a36Sopenharmony_cistatic DEFINE_IDR(ovcs_idr);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(overlay_notify_chain);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/**
12862306a36Sopenharmony_ci * of_overlay_notifier_register() - Register notifier for overlay operations
12962306a36Sopenharmony_ci * @nb:		Notifier block to register
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * Register for notification on overlay operations on device tree nodes. The
13262306a36Sopenharmony_ci * reported actions definied by @of_reconfig_change. The notifier callback
13362306a36Sopenharmony_ci * furthermore receives a pointer to the affected device tree node.
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * Note that a notifier callback is not supposed to store pointers to a device
13662306a36Sopenharmony_ci * tree node or its content beyond @OF_OVERLAY_POST_REMOVE corresponding to the
13762306a36Sopenharmony_ci * respective node it received.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ciint of_overlay_notifier_register(struct notifier_block *nb)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	return blocking_notifier_chain_register(&overlay_notify_chain, nb);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_overlay_notifier_register);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * of_overlay_notifier_unregister() - Unregister notifier for overlay operations
14762306a36Sopenharmony_ci * @nb:		Notifier block to unregister
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_ciint of_overlay_notifier_unregister(struct notifier_block *nb)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(&overlay_notify_chain, nb);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_overlay_notifier_unregister);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int overlay_notify(struct overlay_changeset *ovcs,
15662306a36Sopenharmony_ci		enum of_overlay_notify_action action)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct of_overlay_notify_data nd;
15962306a36Sopenharmony_ci	int i, ret;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	ovcs->notify_state = action;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	for (i = 0; i < ovcs->count; i++) {
16462306a36Sopenharmony_ci		struct fragment *fragment = &ovcs->fragments[i];
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		nd.target = fragment->target;
16762306a36Sopenharmony_ci		nd.overlay = fragment->overlay;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		ret = blocking_notifier_call_chain(&overlay_notify_chain,
17062306a36Sopenharmony_ci						   action, &nd);
17162306a36Sopenharmony_ci		if (notifier_to_errno(ret)) {
17262306a36Sopenharmony_ci			ret = notifier_to_errno(ret);
17362306a36Sopenharmony_ci			pr_err("overlay changeset %s notifier error %d, target: %pOF\n",
17462306a36Sopenharmony_ci			       of_overlay_action_name(action), ret, nd.target);
17562306a36Sopenharmony_ci			return ret;
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * The values of properties in the "/__symbols__" node are paths in
18462306a36Sopenharmony_ci * the ovcs->overlay_root.  When duplicating the properties, the paths
18562306a36Sopenharmony_ci * need to be adjusted to be the correct path for the live device tree.
18662306a36Sopenharmony_ci *
18762306a36Sopenharmony_ci * The paths refer to a node in the subtree of a fragment node's "__overlay__"
18862306a36Sopenharmony_ci * node, for example "/fragment@0/__overlay__/symbol_path_tail",
18962306a36Sopenharmony_ci * where symbol_path_tail can be a single node or it may be a multi-node path.
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * The duplicated property value will be modified by replacing the
19262306a36Sopenharmony_ci * "/fragment_name/__overlay/" portion of the value  with the target
19362306a36Sopenharmony_ci * path from the fragment node.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic struct property *dup_and_fixup_symbol_prop(
19662306a36Sopenharmony_ci		struct overlay_changeset *ovcs, const struct property *prop)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct fragment *fragment;
19962306a36Sopenharmony_ci	struct property *new_prop;
20062306a36Sopenharmony_ci	struct device_node *fragment_node;
20162306a36Sopenharmony_ci	struct device_node *overlay_node;
20262306a36Sopenharmony_ci	const char *path;
20362306a36Sopenharmony_ci	const char *path_tail;
20462306a36Sopenharmony_ci	const char *target_path;
20562306a36Sopenharmony_ci	int k;
20662306a36Sopenharmony_ci	int overlay_name_len;
20762306a36Sopenharmony_ci	int path_len;
20862306a36Sopenharmony_ci	int path_tail_len;
20962306a36Sopenharmony_ci	int target_path_len;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (!prop->value)
21262306a36Sopenharmony_ci		return NULL;
21362306a36Sopenharmony_ci	if (strnlen(prop->value, prop->length) >= prop->length)
21462306a36Sopenharmony_ci		return NULL;
21562306a36Sopenharmony_ci	path = prop->value;
21662306a36Sopenharmony_ci	path_len = strlen(path);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (path_len < 1)
21962306a36Sopenharmony_ci		return NULL;
22062306a36Sopenharmony_ci	fragment_node = __of_find_node_by_path(ovcs->overlay_root, path + 1);
22162306a36Sopenharmony_ci	overlay_node = __of_find_node_by_path(fragment_node, "__overlay__/");
22262306a36Sopenharmony_ci	of_node_put(fragment_node);
22362306a36Sopenharmony_ci	of_node_put(overlay_node);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	for (k = 0; k < ovcs->count; k++) {
22662306a36Sopenharmony_ci		fragment = &ovcs->fragments[k];
22762306a36Sopenharmony_ci		if (fragment->overlay == overlay_node)
22862306a36Sopenharmony_ci			break;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	if (k >= ovcs->count)
23162306a36Sopenharmony_ci		return NULL;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	overlay_name_len = snprintf(NULL, 0, "%pOF", fragment->overlay);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (overlay_name_len > path_len)
23662306a36Sopenharmony_ci		return NULL;
23762306a36Sopenharmony_ci	path_tail = path + overlay_name_len;
23862306a36Sopenharmony_ci	path_tail_len = strlen(path_tail);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	target_path = kasprintf(GFP_KERNEL, "%pOF", fragment->target);
24162306a36Sopenharmony_ci	if (!target_path)
24262306a36Sopenharmony_ci		return NULL;
24362306a36Sopenharmony_ci	target_path_len = strlen(target_path);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
24662306a36Sopenharmony_ci	if (!new_prop)
24762306a36Sopenharmony_ci		goto err_free_target_path;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	new_prop->name = kstrdup(prop->name, GFP_KERNEL);
25062306a36Sopenharmony_ci	new_prop->length = target_path_len + path_tail_len + 1;
25162306a36Sopenharmony_ci	new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
25262306a36Sopenharmony_ci	if (!new_prop->name || !new_prop->value)
25362306a36Sopenharmony_ci		goto err_free_new_prop;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	strcpy(new_prop->value, target_path);
25662306a36Sopenharmony_ci	strcpy(new_prop->value + target_path_len, path_tail);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	of_property_set_flag(new_prop, OF_DYNAMIC);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	kfree(target_path);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return new_prop;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cierr_free_new_prop:
26562306a36Sopenharmony_ci	kfree(new_prop->name);
26662306a36Sopenharmony_ci	kfree(new_prop->value);
26762306a36Sopenharmony_ci	kfree(new_prop);
26862306a36Sopenharmony_cierr_free_target_path:
26962306a36Sopenharmony_ci	kfree(target_path);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return NULL;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * add_changeset_property() - add @overlay_prop to overlay changeset
27662306a36Sopenharmony_ci * @ovcs:		overlay changeset
27762306a36Sopenharmony_ci * @target:		where @overlay_prop will be placed
27862306a36Sopenharmony_ci * @overlay_prop:	property to add or update, from overlay tree
27962306a36Sopenharmony_ci * @is_symbols_prop:	1 if @overlay_prop is from node "/__symbols__"
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * If @overlay_prop does not already exist in live devicetree, add changeset
28262306a36Sopenharmony_ci * entry to add @overlay_prop in @target, else add changeset entry to update
28362306a36Sopenharmony_ci * value of @overlay_prop.
28462306a36Sopenharmony_ci *
28562306a36Sopenharmony_ci * @target may be either in the live devicetree or in a new subtree that
28662306a36Sopenharmony_ci * is contained in the changeset.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Some special properties are not added or updated (no error returned):
28962306a36Sopenharmony_ci * "name", "phandle", "linux,phandle".
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * Properties "#address-cells" and "#size-cells" are not updated if they
29262306a36Sopenharmony_ci * are already in the live tree, but if present in the live tree, the values
29362306a36Sopenharmony_ci * in the overlay must match the values in the live tree.
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci * Update of property in symbols node is not allowed.
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
29862306a36Sopenharmony_ci * invalid @overlay.
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cistatic int add_changeset_property(struct overlay_changeset *ovcs,
30162306a36Sopenharmony_ci		struct target *target, struct property *overlay_prop,
30262306a36Sopenharmony_ci		bool is_symbols_prop)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct property *new_prop = NULL, *prop;
30562306a36Sopenharmony_ci	int ret = 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (target->in_livetree)
30862306a36Sopenharmony_ci		if (!of_prop_cmp(overlay_prop->name, "name") ||
30962306a36Sopenharmony_ci		    !of_prop_cmp(overlay_prop->name, "phandle") ||
31062306a36Sopenharmony_ci		    !of_prop_cmp(overlay_prop->name, "linux,phandle"))
31162306a36Sopenharmony_ci			return 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (target->in_livetree)
31462306a36Sopenharmony_ci		prop = of_find_property(target->np, overlay_prop->name, NULL);
31562306a36Sopenharmony_ci	else
31662306a36Sopenharmony_ci		prop = NULL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (prop) {
31962306a36Sopenharmony_ci		if (!of_prop_cmp(prop->name, "#address-cells")) {
32062306a36Sopenharmony_ci			if (!of_prop_val_eq(prop, overlay_prop)) {
32162306a36Sopenharmony_ci				pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n",
32262306a36Sopenharmony_ci				       target->np);
32362306a36Sopenharmony_ci				ret = -EINVAL;
32462306a36Sopenharmony_ci			}
32562306a36Sopenharmony_ci			return ret;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		} else if (!of_prop_cmp(prop->name, "#size-cells")) {
32862306a36Sopenharmony_ci			if (!of_prop_val_eq(prop, overlay_prop)) {
32962306a36Sopenharmony_ci				pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n",
33062306a36Sopenharmony_ci				       target->np);
33162306a36Sopenharmony_ci				ret = -EINVAL;
33262306a36Sopenharmony_ci			}
33362306a36Sopenharmony_ci			return ret;
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (is_symbols_prop) {
33862306a36Sopenharmony_ci		if (prop)
33962306a36Sopenharmony_ci			return -EINVAL;
34062306a36Sopenharmony_ci		new_prop = dup_and_fixup_symbol_prop(ovcs, overlay_prop);
34162306a36Sopenharmony_ci	} else {
34262306a36Sopenharmony_ci		new_prop = __of_prop_dup(overlay_prop, GFP_KERNEL);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (!new_prop)
34662306a36Sopenharmony_ci		return -ENOMEM;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (!prop) {
34962306a36Sopenharmony_ci		if (!target->in_livetree) {
35062306a36Sopenharmony_ci			new_prop->next = target->np->deadprops;
35162306a36Sopenharmony_ci			target->np->deadprops = new_prop;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci		ret = of_changeset_add_property(&ovcs->cset, target->np,
35462306a36Sopenharmony_ci						new_prop);
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		ret = of_changeset_update_property(&ovcs->cset, target->np,
35762306a36Sopenharmony_ci						   new_prop);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (!of_node_check_flag(target->np, OF_OVERLAY))
36162306a36Sopenharmony_ci		pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n",
36262306a36Sopenharmony_ci		       target->np, new_prop->name);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (ret) {
36562306a36Sopenharmony_ci		kfree(new_prop->name);
36662306a36Sopenharmony_ci		kfree(new_prop->value);
36762306a36Sopenharmony_ci		kfree(new_prop);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	return ret;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * add_changeset_node() - add @node (and children) to overlay changeset
37462306a36Sopenharmony_ci * @ovcs:	overlay changeset
37562306a36Sopenharmony_ci * @target:	where @node will be placed in live tree or changeset
37662306a36Sopenharmony_ci * @node:	node from within overlay device tree fragment
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * If @node does not already exist in @target, add changeset entry
37962306a36Sopenharmony_ci * to add @node in @target.
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci * If @node already exists in @target, and the existing node has
38262306a36Sopenharmony_ci * a phandle, the overlay node is not allowed to have a phandle.
38362306a36Sopenharmony_ci *
38462306a36Sopenharmony_ci * If @node has child nodes, add the children recursively via
38562306a36Sopenharmony_ci * build_changeset_next_level().
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * NOTE_1: A live devicetree created from a flattened device tree (FDT) will
38862306a36Sopenharmony_ci *       not contain the full path in node->full_name.  Thus an overlay
38962306a36Sopenharmony_ci *       created from an FDT also will not contain the full path in
39062306a36Sopenharmony_ci *       node->full_name.  However, a live devicetree created from Open
39162306a36Sopenharmony_ci *       Firmware may have the full path in node->full_name.
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci *       add_changeset_node() follows the FDT convention and does not include
39462306a36Sopenharmony_ci *       the full path in node->full_name.  Even though it expects the overlay
39562306a36Sopenharmony_ci *       to not contain the full path, it uses kbasename() to remove the
39662306a36Sopenharmony_ci *       full path should it exist.  It also uses kbasename() in comparisons
39762306a36Sopenharmony_ci *       to nodes in the live devicetree so that it can apply an overlay to
39862306a36Sopenharmony_ci *       a live devicetree created from Open Firmware.
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * NOTE_2: Multiple mods of created nodes not supported.
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
40362306a36Sopenharmony_ci * invalid @overlay.
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_cistatic int add_changeset_node(struct overlay_changeset *ovcs,
40662306a36Sopenharmony_ci		struct target *target, struct device_node *node)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	const char *node_kbasename;
40962306a36Sopenharmony_ci	const __be32 *phandle;
41062306a36Sopenharmony_ci	struct device_node *tchild;
41162306a36Sopenharmony_ci	struct target target_child;
41262306a36Sopenharmony_ci	int ret = 0, size;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	node_kbasename = kbasename(node->full_name);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	for_each_child_of_node(target->np, tchild)
41762306a36Sopenharmony_ci		if (!of_node_cmp(node_kbasename, kbasename(tchild->full_name)))
41862306a36Sopenharmony_ci			break;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!tchild) {
42162306a36Sopenharmony_ci		tchild = __of_node_dup(NULL, node_kbasename);
42262306a36Sopenharmony_ci		if (!tchild)
42362306a36Sopenharmony_ci			return -ENOMEM;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		tchild->parent = target->np;
42662306a36Sopenharmony_ci		tchild->name = __of_get_property(node, "name", NULL);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (!tchild->name)
42962306a36Sopenharmony_ci			tchild->name = "<NULL>";
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		/* ignore obsolete "linux,phandle" */
43262306a36Sopenharmony_ci		phandle = __of_get_property(node, "phandle", &size);
43362306a36Sopenharmony_ci		if (phandle && (size == 4))
43462306a36Sopenharmony_ci			tchild->phandle = be32_to_cpup(phandle);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		of_node_set_flag(tchild, OF_OVERLAY);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		ret = of_changeset_attach_node(&ovcs->cset, tchild);
43962306a36Sopenharmony_ci		if (ret)
44062306a36Sopenharmony_ci			return ret;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		target_child.np = tchild;
44362306a36Sopenharmony_ci		target_child.in_livetree = false;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		ret = build_changeset_next_level(ovcs, &target_child, node);
44662306a36Sopenharmony_ci		of_node_put(tchild);
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (node->phandle && tchild->phandle) {
45162306a36Sopenharmony_ci		ret = -EINVAL;
45262306a36Sopenharmony_ci	} else {
45362306a36Sopenharmony_ci		target_child.np = tchild;
45462306a36Sopenharmony_ci		target_child.in_livetree = target->in_livetree;
45562306a36Sopenharmony_ci		ret = build_changeset_next_level(ovcs, &target_child, node);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci	of_node_put(tchild);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return ret;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/**
46362306a36Sopenharmony_ci * build_changeset_next_level() - add level of overlay changeset
46462306a36Sopenharmony_ci * @ovcs:		overlay changeset
46562306a36Sopenharmony_ci * @target:		where to place @overlay_node in live tree
46662306a36Sopenharmony_ci * @overlay_node:	node from within an overlay device tree fragment
46762306a36Sopenharmony_ci *
46862306a36Sopenharmony_ci * Add the properties (if any) and nodes (if any) from @overlay_node to the
46962306a36Sopenharmony_ci * @ovcs->cset changeset.  If an added node has child nodes, they will
47062306a36Sopenharmony_ci * be added recursively.
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * Do not allow symbols node to have any children.
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
47562306a36Sopenharmony_ci * invalid @overlay_node.
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_cistatic int build_changeset_next_level(struct overlay_changeset *ovcs,
47862306a36Sopenharmony_ci		struct target *target, const struct device_node *overlay_node)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct device_node *child;
48162306a36Sopenharmony_ci	struct property *prop;
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	for_each_property_of_node(overlay_node, prop) {
48562306a36Sopenharmony_ci		ret = add_changeset_property(ovcs, target, prop, 0);
48662306a36Sopenharmony_ci		if (ret) {
48762306a36Sopenharmony_ci			pr_debug("Failed to apply prop @%pOF/%s, err=%d\n",
48862306a36Sopenharmony_ci				 target->np, prop->name, ret);
48962306a36Sopenharmony_ci			return ret;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	for_each_child_of_node(overlay_node, child) {
49462306a36Sopenharmony_ci		ret = add_changeset_node(ovcs, target, child);
49562306a36Sopenharmony_ci		if (ret) {
49662306a36Sopenharmony_ci			pr_debug("Failed to apply node @%pOF/%pOFn, err=%d\n",
49762306a36Sopenharmony_ci				 target->np, child, ret);
49862306a36Sopenharmony_ci			of_node_put(child);
49962306a36Sopenharmony_ci			return ret;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/*
50762306a36Sopenharmony_ci * Add the properties from __overlay__ node to the @ovcs->cset changeset.
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic int build_changeset_symbols_node(struct overlay_changeset *ovcs,
51062306a36Sopenharmony_ci		struct target *target,
51162306a36Sopenharmony_ci		const struct device_node *overlay_symbols_node)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct property *prop;
51462306a36Sopenharmony_ci	int ret;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	for_each_property_of_node(overlay_symbols_node, prop) {
51762306a36Sopenharmony_ci		ret = add_changeset_property(ovcs, target, prop, 1);
51862306a36Sopenharmony_ci		if (ret) {
51962306a36Sopenharmony_ci			pr_debug("Failed to apply symbols prop @%pOF/%s, err=%d\n",
52062306a36Sopenharmony_ci				 target->np, prop->name, ret);
52162306a36Sopenharmony_ci			return ret;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
52962306a36Sopenharmony_ci		struct of_changeset_entry *ce_1)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct of_changeset_entry *ce_2;
53262306a36Sopenharmony_ci	char *fn_1, *fn_2;
53362306a36Sopenharmony_ci	int node_path_match;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (ce_1->action != OF_RECONFIG_ATTACH_NODE &&
53662306a36Sopenharmony_ci	    ce_1->action != OF_RECONFIG_DETACH_NODE)
53762306a36Sopenharmony_ci		return 0;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ce_2 = ce_1;
54062306a36Sopenharmony_ci	list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
54162306a36Sopenharmony_ci		if ((ce_2->action != OF_RECONFIG_ATTACH_NODE &&
54262306a36Sopenharmony_ci		     ce_2->action != OF_RECONFIG_DETACH_NODE) ||
54362306a36Sopenharmony_ci		    of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
54462306a36Sopenharmony_ci			continue;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
54762306a36Sopenharmony_ci		fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
54862306a36Sopenharmony_ci		node_path_match = !fn_1 || !fn_2 || !strcmp(fn_1, fn_2);
54962306a36Sopenharmony_ci		kfree(fn_1);
55062306a36Sopenharmony_ci		kfree(fn_2);
55162306a36Sopenharmony_ci		if (node_path_match) {
55262306a36Sopenharmony_ci			pr_err("ERROR: multiple fragments add and/or delete node %pOF\n",
55362306a36Sopenharmony_ci			       ce_1->np);
55462306a36Sopenharmony_ci			return -EINVAL;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return 0;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int find_dup_cset_prop(struct overlay_changeset *ovcs,
56262306a36Sopenharmony_ci		struct of_changeset_entry *ce_1)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct of_changeset_entry *ce_2;
56562306a36Sopenharmony_ci	char *fn_1, *fn_2;
56662306a36Sopenharmony_ci	int node_path_match;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (ce_1->action != OF_RECONFIG_ADD_PROPERTY &&
56962306a36Sopenharmony_ci	    ce_1->action != OF_RECONFIG_REMOVE_PROPERTY &&
57062306a36Sopenharmony_ci	    ce_1->action != OF_RECONFIG_UPDATE_PROPERTY)
57162306a36Sopenharmony_ci		return 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	ce_2 = ce_1;
57462306a36Sopenharmony_ci	list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
57562306a36Sopenharmony_ci		if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY &&
57662306a36Sopenharmony_ci		     ce_2->action != OF_RECONFIG_REMOVE_PROPERTY &&
57762306a36Sopenharmony_ci		     ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) ||
57862306a36Sopenharmony_ci		    of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
57962306a36Sopenharmony_ci			continue;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
58262306a36Sopenharmony_ci		fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
58362306a36Sopenharmony_ci		node_path_match = !fn_1 || !fn_2 || !strcmp(fn_1, fn_2);
58462306a36Sopenharmony_ci		kfree(fn_1);
58562306a36Sopenharmony_ci		kfree(fn_2);
58662306a36Sopenharmony_ci		if (node_path_match &&
58762306a36Sopenharmony_ci		    !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) {
58862306a36Sopenharmony_ci			pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n",
58962306a36Sopenharmony_ci			       ce_1->np, ce_1->prop->name);
59062306a36Sopenharmony_ci			return -EINVAL;
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/**
59862306a36Sopenharmony_ci * changeset_dup_entry_check() - check for duplicate entries
59962306a36Sopenharmony_ci * @ovcs:	Overlay changeset
60062306a36Sopenharmony_ci *
60162306a36Sopenharmony_ci * Check changeset @ovcs->cset for multiple {add or delete} node entries for
60262306a36Sopenharmony_ci * the same node or duplicate {add, delete, or update} properties entries
60362306a36Sopenharmony_ci * for the same property.
60462306a36Sopenharmony_ci *
60562306a36Sopenharmony_ci * Return: 0 on success, or -EINVAL if duplicate changeset entry found.
60662306a36Sopenharmony_ci */
60762306a36Sopenharmony_cistatic int changeset_dup_entry_check(struct overlay_changeset *ovcs)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct of_changeset_entry *ce_1;
61062306a36Sopenharmony_ci	int dup_entry = 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
61362306a36Sopenharmony_ci		dup_entry |= find_dup_cset_node_entry(ovcs, ce_1);
61462306a36Sopenharmony_ci		dup_entry |= find_dup_cset_prop(ovcs, ce_1);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return dup_entry ? -EINVAL : 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/**
62162306a36Sopenharmony_ci * build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments
62262306a36Sopenharmony_ci * @ovcs:	Overlay changeset
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Create changeset @ovcs->cset to contain the nodes and properties of the
62562306a36Sopenharmony_ci * overlay device tree fragments in @ovcs->fragments[].  If an error occurs,
62662306a36Sopenharmony_ci * any portions of the changeset that were successfully created will remain
62762306a36Sopenharmony_ci * in @ovcs->cset.
62862306a36Sopenharmony_ci *
62962306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
63062306a36Sopenharmony_ci * invalid overlay in @ovcs->fragments[].
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_cistatic int build_changeset(struct overlay_changeset *ovcs)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct fragment *fragment;
63562306a36Sopenharmony_ci	struct target target;
63662306a36Sopenharmony_ci	int fragments_count, i, ret;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/*
63962306a36Sopenharmony_ci	 * if there is a symbols fragment in ovcs->fragments[i] it is
64062306a36Sopenharmony_ci	 * the final element in the array
64162306a36Sopenharmony_ci	 */
64262306a36Sopenharmony_ci	if (ovcs->symbols_fragment)
64362306a36Sopenharmony_ci		fragments_count = ovcs->count - 1;
64462306a36Sopenharmony_ci	else
64562306a36Sopenharmony_ci		fragments_count = ovcs->count;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	for (i = 0; i < fragments_count; i++) {
64862306a36Sopenharmony_ci		fragment = &ovcs->fragments[i];
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		target.np = fragment->target;
65162306a36Sopenharmony_ci		target.in_livetree = true;
65262306a36Sopenharmony_ci		ret = build_changeset_next_level(ovcs, &target,
65362306a36Sopenharmony_ci						 fragment->overlay);
65462306a36Sopenharmony_ci		if (ret) {
65562306a36Sopenharmony_ci			pr_debug("fragment apply failed '%pOF'\n",
65662306a36Sopenharmony_ci				 fragment->target);
65762306a36Sopenharmony_ci			return ret;
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (ovcs->symbols_fragment) {
66262306a36Sopenharmony_ci		fragment = &ovcs->fragments[ovcs->count - 1];
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		target.np = fragment->target;
66562306a36Sopenharmony_ci		target.in_livetree = true;
66662306a36Sopenharmony_ci		ret = build_changeset_symbols_node(ovcs, &target,
66762306a36Sopenharmony_ci						   fragment->overlay);
66862306a36Sopenharmony_ci		if (ret) {
66962306a36Sopenharmony_ci			pr_debug("symbols fragment apply failed '%pOF'\n",
67062306a36Sopenharmony_ci				 fragment->target);
67162306a36Sopenharmony_ci			return ret;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	return changeset_dup_entry_check(ovcs);
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/*
67962306a36Sopenharmony_ci * Find the target node using a number of different strategies
68062306a36Sopenharmony_ci * in order of preference:
68162306a36Sopenharmony_ci *
68262306a36Sopenharmony_ci * 1) "target" property containing the phandle of the target
68362306a36Sopenharmony_ci * 2) "target-path" property containing the path of the target
68462306a36Sopenharmony_ci */
68562306a36Sopenharmony_cistatic struct device_node *find_target(struct device_node *info_node,
68662306a36Sopenharmony_ci				       struct device_node *target_base)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct device_node *node;
68962306a36Sopenharmony_ci	char *target_path;
69062306a36Sopenharmony_ci	const char *path;
69162306a36Sopenharmony_ci	u32 val;
69262306a36Sopenharmony_ci	int ret;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	ret = of_property_read_u32(info_node, "target", &val);
69562306a36Sopenharmony_ci	if (!ret) {
69662306a36Sopenharmony_ci		node = of_find_node_by_phandle(val);
69762306a36Sopenharmony_ci		if (!node)
69862306a36Sopenharmony_ci			pr_err("find target, node: %pOF, phandle 0x%x not found\n",
69962306a36Sopenharmony_ci			       info_node, val);
70062306a36Sopenharmony_ci		return node;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	ret = of_property_read_string(info_node, "target-path", &path);
70462306a36Sopenharmony_ci	if (!ret) {
70562306a36Sopenharmony_ci		if (target_base) {
70662306a36Sopenharmony_ci			target_path = kasprintf(GFP_KERNEL, "%pOF%s", target_base, path);
70762306a36Sopenharmony_ci			if (!target_path)
70862306a36Sopenharmony_ci				return NULL;
70962306a36Sopenharmony_ci			node = of_find_node_by_path(target_path);
71062306a36Sopenharmony_ci			if (!node) {
71162306a36Sopenharmony_ci				pr_err("find target, node: %pOF, path '%s' not found\n",
71262306a36Sopenharmony_ci				       info_node, target_path);
71362306a36Sopenharmony_ci			}
71462306a36Sopenharmony_ci			kfree(target_path);
71562306a36Sopenharmony_ci		} else {
71662306a36Sopenharmony_ci			node =  of_find_node_by_path(path);
71762306a36Sopenharmony_ci			if (!node) {
71862306a36Sopenharmony_ci				pr_err("find target, node: %pOF, path '%s' not found\n",
71962306a36Sopenharmony_ci				       info_node, path);
72062306a36Sopenharmony_ci			}
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci		return node;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	pr_err("find target, node: %pOF, no target property\n", info_node);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return NULL;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/**
73162306a36Sopenharmony_ci * init_overlay_changeset() - initialize overlay changeset from overlay tree
73262306a36Sopenharmony_ci * @ovcs:		Overlay changeset to build
73362306a36Sopenharmony_ci * @target_base:	Point to the target node to apply overlay
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * Initialize @ovcs.  Populate @ovcs->fragments with node information from
73662306a36Sopenharmony_ci * the top level of @overlay_root.  The relevant top level nodes are the
73762306a36Sopenharmony_ci * fragment nodes and the __symbols__ node.  Any other top level node will
73862306a36Sopenharmony_ci * be ignored.  Populate other @ovcs fields.
73962306a36Sopenharmony_ci *
74062306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM if memory allocation failure, -EINVAL if error
74162306a36Sopenharmony_ci * detected in @overlay_root.  On error return, the caller of
74262306a36Sopenharmony_ci * init_overlay_changeset() must call free_overlay_changeset().
74362306a36Sopenharmony_ci */
74462306a36Sopenharmony_cistatic int init_overlay_changeset(struct overlay_changeset *ovcs,
74562306a36Sopenharmony_ci				  struct device_node *target_base)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct device_node *node, *overlay_node;
74862306a36Sopenharmony_ci	struct fragment *fragment;
74962306a36Sopenharmony_ci	struct fragment *fragments;
75062306a36Sopenharmony_ci	int cnt, ret;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/*
75362306a36Sopenharmony_ci	 * None of the resources allocated by this function will be freed in
75462306a36Sopenharmony_ci	 * the error paths.  Instead the caller of this function is required
75562306a36Sopenharmony_ci	 * to call free_overlay_changeset() (which will free the resources)
75662306a36Sopenharmony_ci	 * if error return.
75762306a36Sopenharmony_ci	 */
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/*
76062306a36Sopenharmony_ci	 * Warn for some issues.  Can not return -EINVAL for these until
76162306a36Sopenharmony_ci	 * of_unittest_apply_overlay() is fixed to pass these checks.
76262306a36Sopenharmony_ci	 */
76362306a36Sopenharmony_ci	if (!of_node_check_flag(ovcs->overlay_root, OF_DYNAMIC))
76462306a36Sopenharmony_ci		pr_debug("%s() ovcs->overlay_root is not dynamic\n", __func__);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (!of_node_check_flag(ovcs->overlay_root, OF_DETACHED))
76762306a36Sopenharmony_ci		pr_debug("%s() ovcs->overlay_root is not detached\n", __func__);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (!of_node_is_root(ovcs->overlay_root))
77062306a36Sopenharmony_ci		pr_debug("%s() ovcs->overlay_root is not root\n", __func__);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	cnt = 0;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* fragment nodes */
77562306a36Sopenharmony_ci	for_each_child_of_node(ovcs->overlay_root, node) {
77662306a36Sopenharmony_ci		overlay_node = of_get_child_by_name(node, "__overlay__");
77762306a36Sopenharmony_ci		if (overlay_node) {
77862306a36Sopenharmony_ci			cnt++;
77962306a36Sopenharmony_ci			of_node_put(overlay_node);
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	node = of_get_child_by_name(ovcs->overlay_root, "__symbols__");
78462306a36Sopenharmony_ci	if (node) {
78562306a36Sopenharmony_ci		cnt++;
78662306a36Sopenharmony_ci		of_node_put(node);
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	fragments = kcalloc(cnt, sizeof(*fragments), GFP_KERNEL);
79062306a36Sopenharmony_ci	if (!fragments) {
79162306a36Sopenharmony_ci		ret = -ENOMEM;
79262306a36Sopenharmony_ci		goto err_out;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci	ovcs->fragments = fragments;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	cnt = 0;
79762306a36Sopenharmony_ci	for_each_child_of_node(ovcs->overlay_root, node) {
79862306a36Sopenharmony_ci		overlay_node = of_get_child_by_name(node, "__overlay__");
79962306a36Sopenharmony_ci		if (!overlay_node)
80062306a36Sopenharmony_ci			continue;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		fragment = &fragments[cnt];
80362306a36Sopenharmony_ci		fragment->overlay = overlay_node;
80462306a36Sopenharmony_ci		fragment->target = find_target(node, target_base);
80562306a36Sopenharmony_ci		if (!fragment->target) {
80662306a36Sopenharmony_ci			of_node_put(fragment->overlay);
80762306a36Sopenharmony_ci			ret = -EINVAL;
80862306a36Sopenharmony_ci			of_node_put(node);
80962306a36Sopenharmony_ci			goto err_out;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		cnt++;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/*
81662306a36Sopenharmony_ci	 * if there is a symbols fragment in ovcs->fragments[i] it is
81762306a36Sopenharmony_ci	 * the final element in the array
81862306a36Sopenharmony_ci	 */
81962306a36Sopenharmony_ci	node = of_get_child_by_name(ovcs->overlay_root, "__symbols__");
82062306a36Sopenharmony_ci	if (node) {
82162306a36Sopenharmony_ci		ovcs->symbols_fragment = 1;
82262306a36Sopenharmony_ci		fragment = &fragments[cnt];
82362306a36Sopenharmony_ci		fragment->overlay = node;
82462306a36Sopenharmony_ci		fragment->target = of_find_node_by_path("/__symbols__");
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci		if (!fragment->target) {
82762306a36Sopenharmony_ci			pr_err("symbols in overlay, but not in live tree\n");
82862306a36Sopenharmony_ci			ret = -EINVAL;
82962306a36Sopenharmony_ci			of_node_put(node);
83062306a36Sopenharmony_ci			goto err_out;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		cnt++;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (!cnt) {
83762306a36Sopenharmony_ci		pr_err("no fragments or symbols in overlay\n");
83862306a36Sopenharmony_ci		ret = -EINVAL;
83962306a36Sopenharmony_ci		goto err_out;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ovcs->count = cnt;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cierr_out:
84762306a36Sopenharmony_ci	pr_err("%s() failed, ret = %d\n", __func__, ret);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return ret;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic void free_overlay_changeset(struct overlay_changeset *ovcs)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	int i;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (ovcs->cset.entries.next)
85762306a36Sopenharmony_ci		of_changeset_destroy(&ovcs->cset);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (ovcs->id) {
86062306a36Sopenharmony_ci		idr_remove(&ovcs_idr, ovcs->id);
86162306a36Sopenharmony_ci		list_del(&ovcs->ovcs_list);
86262306a36Sopenharmony_ci		ovcs->id = 0;
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	for (i = 0; i < ovcs->count; i++) {
86762306a36Sopenharmony_ci		of_node_put(ovcs->fragments[i].target);
86862306a36Sopenharmony_ci		of_node_put(ovcs->fragments[i].overlay);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci	kfree(ovcs->fragments);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/*
87362306a36Sopenharmony_ci	 * There should be no live pointers into ovcs->overlay_mem and
87462306a36Sopenharmony_ci	 * ovcs->new_fdt due to the policy that overlay notifiers are not
87562306a36Sopenharmony_ci	 * allowed to retain pointers into the overlay devicetree other
87662306a36Sopenharmony_ci	 * than during the window from OF_OVERLAY_PRE_APPLY overlay
87762306a36Sopenharmony_ci	 * notifiers until the OF_OVERLAY_POST_REMOVE overlay notifiers.
87862306a36Sopenharmony_ci	 *
87962306a36Sopenharmony_ci	 * A memory leak will occur here if within the window.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (ovcs->notify_state == OF_OVERLAY_INIT ||
88362306a36Sopenharmony_ci	    ovcs->notify_state == OF_OVERLAY_POST_REMOVE) {
88462306a36Sopenharmony_ci		kfree(ovcs->overlay_mem);
88562306a36Sopenharmony_ci		kfree(ovcs->new_fdt);
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci	kfree(ovcs);
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci/*
89162306a36Sopenharmony_ci * internal documentation
89262306a36Sopenharmony_ci *
89362306a36Sopenharmony_ci * of_overlay_apply() - Create and apply an overlay changeset
89462306a36Sopenharmony_ci * @ovcs:	overlay changeset
89562306a36Sopenharmony_ci * @base:	point to the target node to apply overlay
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * Creates and applies an overlay changeset.
89862306a36Sopenharmony_ci *
89962306a36Sopenharmony_ci * If an error is returned by an overlay changeset pre-apply notifier
90062306a36Sopenharmony_ci * then no further overlay changeset pre-apply notifier will be called.
90162306a36Sopenharmony_ci *
90262306a36Sopenharmony_ci * If an error is returned by an overlay changeset post-apply notifier
90362306a36Sopenharmony_ci * then no further overlay changeset post-apply notifier will be called.
90462306a36Sopenharmony_ci *
90562306a36Sopenharmony_ci * If more than one notifier returns an error, then the last notifier
90662306a36Sopenharmony_ci * error to occur is returned.
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * If an error occurred while applying the overlay changeset, then an
90962306a36Sopenharmony_ci * attempt is made to revert any changes that were made to the
91062306a36Sopenharmony_ci * device tree.  If there were any errors during the revert attempt
91162306a36Sopenharmony_ci * then the state of the device tree can not be determined, and any
91262306a36Sopenharmony_ci * following attempt to apply or remove an overlay changeset will be
91362306a36Sopenharmony_ci * refused.
91462306a36Sopenharmony_ci *
91562306a36Sopenharmony_ci * Returns 0 on success, or a negative error number.  On error return,
91662306a36Sopenharmony_ci * the caller of of_overlay_apply() must call free_overlay_changeset().
91762306a36Sopenharmony_ci */
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int of_overlay_apply(struct overlay_changeset *ovcs,
92062306a36Sopenharmony_ci			    struct device_node *base)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	int ret = 0, ret_revert, ret_tmp;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	ret = of_resolve_phandles(ovcs->overlay_root);
92562306a36Sopenharmony_ci	if (ret)
92662306a36Sopenharmony_ci		goto out;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	ret = init_overlay_changeset(ovcs, base);
92962306a36Sopenharmony_ci	if (ret)
93062306a36Sopenharmony_ci		goto out;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
93362306a36Sopenharmony_ci	if (ret)
93462306a36Sopenharmony_ci		goto out;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	ret = build_changeset(ovcs);
93762306a36Sopenharmony_ci	if (ret)
93862306a36Sopenharmony_ci		goto out;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	ret_revert = 0;
94162306a36Sopenharmony_ci	ret = __of_changeset_apply_entries(&ovcs->cset, &ret_revert);
94262306a36Sopenharmony_ci	if (ret) {
94362306a36Sopenharmony_ci		if (ret_revert) {
94462306a36Sopenharmony_ci			pr_debug("overlay changeset revert error %d\n",
94562306a36Sopenharmony_ci				 ret_revert);
94662306a36Sopenharmony_ci			devicetree_state_flags |= DTSF_APPLY_FAIL;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci		goto out;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	ret = __of_changeset_apply_notify(&ovcs->cset);
95262306a36Sopenharmony_ci	if (ret)
95362306a36Sopenharmony_ci		pr_err("overlay apply changeset entry notify error %d\n", ret);
95462306a36Sopenharmony_ci	/* notify failure is not fatal, continue */
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY);
95762306a36Sopenharmony_ci	if (ret_tmp)
95862306a36Sopenharmony_ci		if (!ret)
95962306a36Sopenharmony_ci			ret = ret_tmp;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ciout:
96262306a36Sopenharmony_ci	pr_debug("%s() err=%d\n", __func__, ret);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	return ret;
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci/*
96862306a36Sopenharmony_ci * of_overlay_fdt_apply() - Create and apply an overlay changeset
96962306a36Sopenharmony_ci * @overlay_fdt:	pointer to overlay FDT
97062306a36Sopenharmony_ci * @overlay_fdt_size:	number of bytes in @overlay_fdt
97162306a36Sopenharmony_ci * @ret_ovcs_id:	pointer for returning created changeset id
97262306a36Sopenharmony_ci * @base:		pointer for the target node to apply overlay
97362306a36Sopenharmony_ci *
97462306a36Sopenharmony_ci * Creates and applies an overlay changeset.
97562306a36Sopenharmony_ci *
97662306a36Sopenharmony_ci * See of_overlay_apply() for important behavior information.
97762306a36Sopenharmony_ci *
97862306a36Sopenharmony_ci * Return: 0 on success, or a negative error number.  *@ret_ovcs_id is set to
97962306a36Sopenharmony_ci * the value of overlay changeset id, which can be passed to of_overlay_remove()
98062306a36Sopenharmony_ci * to remove the overlay.
98162306a36Sopenharmony_ci *
98262306a36Sopenharmony_ci * On error return, the changeset may be partially applied.  This is especially
98362306a36Sopenharmony_ci * likely if an OF_OVERLAY_POST_APPLY notifier returns an error.  In this case
98462306a36Sopenharmony_ci * the caller should call of_overlay_remove() with the value in *@ret_ovcs_id.
98562306a36Sopenharmony_ci */
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ciint of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
98862306a36Sopenharmony_ci			 int *ret_ovcs_id, struct device_node *base)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	void *new_fdt;
99162306a36Sopenharmony_ci	void *new_fdt_align;
99262306a36Sopenharmony_ci	void *overlay_mem;
99362306a36Sopenharmony_ci	int ret;
99462306a36Sopenharmony_ci	u32 size;
99562306a36Sopenharmony_ci	struct overlay_changeset *ovcs;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	*ret_ovcs_id = 0;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (devicetree_corrupt()) {
100062306a36Sopenharmony_ci		pr_err("devicetree state suspect, refuse to apply overlay\n");
100162306a36Sopenharmony_ci		return -EBUSY;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (overlay_fdt_size < sizeof(struct fdt_header) ||
100562306a36Sopenharmony_ci	    fdt_check_header(overlay_fdt)) {
100662306a36Sopenharmony_ci		pr_err("Invalid overlay_fdt header\n");
100762306a36Sopenharmony_ci		return -EINVAL;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	size = fdt_totalsize(overlay_fdt);
101162306a36Sopenharmony_ci	if (overlay_fdt_size < size)
101262306a36Sopenharmony_ci		return -EINVAL;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
101562306a36Sopenharmony_ci	if (!ovcs)
101662306a36Sopenharmony_ci		return -ENOMEM;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	of_overlay_mutex_lock();
101962306a36Sopenharmony_ci	mutex_lock(&of_mutex);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/*
102262306a36Sopenharmony_ci	 * ovcs->notify_state must be set to OF_OVERLAY_INIT before allocating
102362306a36Sopenharmony_ci	 * ovcs resources, implicitly set by kzalloc() of ovcs
102462306a36Sopenharmony_ci	 */
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	ovcs->id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL);
102762306a36Sopenharmony_ci	if (ovcs->id <= 0) {
102862306a36Sopenharmony_ci		ret = ovcs->id;
102962306a36Sopenharmony_ci		goto err_free_ovcs;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ovcs->ovcs_list);
103362306a36Sopenharmony_ci	list_add_tail(&ovcs->ovcs_list, &ovcs_list);
103462306a36Sopenharmony_ci	of_changeset_init(&ovcs->cset);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/*
103762306a36Sopenharmony_ci	 * Must create permanent copy of FDT because of_fdt_unflatten_tree()
103862306a36Sopenharmony_ci	 * will create pointers to the passed in FDT in the unflattened tree.
103962306a36Sopenharmony_ci	 */
104062306a36Sopenharmony_ci	new_fdt = kmalloc(size + FDT_ALIGN_SIZE, GFP_KERNEL);
104162306a36Sopenharmony_ci	if (!new_fdt) {
104262306a36Sopenharmony_ci		ret = -ENOMEM;
104362306a36Sopenharmony_ci		goto err_free_ovcs;
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci	ovcs->new_fdt = new_fdt;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	new_fdt_align = PTR_ALIGN(new_fdt, FDT_ALIGN_SIZE);
104862306a36Sopenharmony_ci	memcpy(new_fdt_align, overlay_fdt, size);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	overlay_mem = of_fdt_unflatten_tree(new_fdt_align, NULL,
105162306a36Sopenharmony_ci					    &ovcs->overlay_root);
105262306a36Sopenharmony_ci	if (!overlay_mem) {
105362306a36Sopenharmony_ci		pr_err("unable to unflatten overlay_fdt\n");
105462306a36Sopenharmony_ci		ret = -EINVAL;
105562306a36Sopenharmony_ci		goto err_free_ovcs;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci	ovcs->overlay_mem = overlay_mem;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	ret = of_overlay_apply(ovcs, base);
106062306a36Sopenharmony_ci	/*
106162306a36Sopenharmony_ci	 * If of_overlay_apply() error, calling free_overlay_changeset() may
106262306a36Sopenharmony_ci	 * result in a memory leak if the apply partly succeeded, so do NOT
106362306a36Sopenharmony_ci	 * goto err_free_ovcs.  Instead, the caller of of_overlay_fdt_apply()
106462306a36Sopenharmony_ci	 * can call of_overlay_remove();
106562306a36Sopenharmony_ci	 */
106662306a36Sopenharmony_ci	*ret_ovcs_id = ovcs->id;
106762306a36Sopenharmony_ci	goto out_unlock;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cierr_free_ovcs:
107062306a36Sopenharmony_ci	free_overlay_changeset(ovcs);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ciout_unlock:
107362306a36Sopenharmony_ci	mutex_unlock(&of_mutex);
107462306a36Sopenharmony_ci	of_overlay_mutex_unlock();
107562306a36Sopenharmony_ci	return ret;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci/*
108062306a36Sopenharmony_ci * Find @np in @tree.
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci * Returns 1 if @np is @tree or is contained in @tree, else 0
108362306a36Sopenharmony_ci */
108462306a36Sopenharmony_cistatic int find_node(struct device_node *tree, struct device_node *np)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct device_node *child;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (tree == np)
108962306a36Sopenharmony_ci		return 1;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	for_each_child_of_node(tree, child) {
109262306a36Sopenharmony_ci		if (find_node(child, np)) {
109362306a36Sopenharmony_ci			of_node_put(child);
109462306a36Sopenharmony_ci			return 1;
109562306a36Sopenharmony_ci		}
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci/*
110262306a36Sopenharmony_ci * Is @remove_ce_node a child of, a parent of, or the same as any
110362306a36Sopenharmony_ci * node in an overlay changeset more topmost than @remove_ovcs?
110462306a36Sopenharmony_ci *
110562306a36Sopenharmony_ci * Returns 1 if found, else 0
110662306a36Sopenharmony_ci */
110762306a36Sopenharmony_cistatic int node_overlaps_later_cs(struct overlay_changeset *remove_ovcs,
110862306a36Sopenharmony_ci		struct device_node *remove_ce_node)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	struct overlay_changeset *ovcs;
111162306a36Sopenharmony_ci	struct of_changeset_entry *ce;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	list_for_each_entry_reverse(ovcs, &ovcs_list, ovcs_list) {
111462306a36Sopenharmony_ci		if (ovcs == remove_ovcs)
111562306a36Sopenharmony_ci			break;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		list_for_each_entry(ce, &ovcs->cset.entries, node) {
111862306a36Sopenharmony_ci			if (find_node(ce->np, remove_ce_node)) {
111962306a36Sopenharmony_ci				pr_err("%s: #%d overlaps with #%d @%pOF\n",
112062306a36Sopenharmony_ci					__func__, remove_ovcs->id, ovcs->id,
112162306a36Sopenharmony_ci					remove_ce_node);
112262306a36Sopenharmony_ci				return 1;
112362306a36Sopenharmony_ci			}
112462306a36Sopenharmony_ci			if (find_node(remove_ce_node, ce->np)) {
112562306a36Sopenharmony_ci				pr_err("%s: #%d overlaps with #%d @%pOF\n",
112662306a36Sopenharmony_ci					__func__, remove_ovcs->id, ovcs->id,
112762306a36Sopenharmony_ci					remove_ce_node);
112862306a36Sopenharmony_ci				return 1;
112962306a36Sopenharmony_ci			}
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	return 0;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci/*
113762306a36Sopenharmony_ci * We can safely remove the overlay only if it's the top-most one.
113862306a36Sopenharmony_ci * Newly applied overlays are inserted at the tail of the overlay list,
113962306a36Sopenharmony_ci * so a top most overlay is the one that is closest to the tail.
114062306a36Sopenharmony_ci *
114162306a36Sopenharmony_ci * The topmost check is done by exploiting this property. For each
114262306a36Sopenharmony_ci * affected device node in the log list we check if this overlay is
114362306a36Sopenharmony_ci * the one closest to the tail. If another overlay has affected this
114462306a36Sopenharmony_ci * device node and is closest to the tail, then removal is not permitted.
114562306a36Sopenharmony_ci */
114662306a36Sopenharmony_cistatic int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct of_changeset_entry *remove_ce;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	list_for_each_entry(remove_ce, &remove_ovcs->cset.entries, node) {
115162306a36Sopenharmony_ci		if (node_overlaps_later_cs(remove_ovcs, remove_ce->np)) {
115262306a36Sopenharmony_ci			pr_err("overlay #%d is not topmost\n", remove_ovcs->id);
115362306a36Sopenharmony_ci			return 0;
115462306a36Sopenharmony_ci		}
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return 1;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/**
116162306a36Sopenharmony_ci * of_overlay_remove() - Revert and free an overlay changeset
116262306a36Sopenharmony_ci * @ovcs_id:	Pointer to overlay changeset id
116362306a36Sopenharmony_ci *
116462306a36Sopenharmony_ci * Removes an overlay if it is permissible.  @ovcs_id was previously returned
116562306a36Sopenharmony_ci * by of_overlay_fdt_apply().
116662306a36Sopenharmony_ci *
116762306a36Sopenharmony_ci * If an error occurred while attempting to revert the overlay changeset,
116862306a36Sopenharmony_ci * then an attempt is made to re-apply any changeset entry that was
116962306a36Sopenharmony_ci * reverted.  If an error occurs on re-apply then the state of the device
117062306a36Sopenharmony_ci * tree can not be determined, and any following attempt to apply or remove
117162306a36Sopenharmony_ci * an overlay changeset will be refused.
117262306a36Sopenharmony_ci *
117362306a36Sopenharmony_ci * A non-zero return value will not revert the changeset if error is from:
117462306a36Sopenharmony_ci *   - parameter checks
117562306a36Sopenharmony_ci *   - overlay changeset pre-remove notifier
117662306a36Sopenharmony_ci *   - overlay changeset entry revert
117762306a36Sopenharmony_ci *
117862306a36Sopenharmony_ci * If an error is returned by an overlay changeset pre-remove notifier
117962306a36Sopenharmony_ci * then no further overlay changeset pre-remove notifier will be called.
118062306a36Sopenharmony_ci *
118162306a36Sopenharmony_ci * If more than one notifier returns an error, then the last notifier
118262306a36Sopenharmony_ci * error to occur is returned.
118362306a36Sopenharmony_ci *
118462306a36Sopenharmony_ci * A non-zero return value will revert the changeset if error is from:
118562306a36Sopenharmony_ci *   - overlay changeset entry notifier
118662306a36Sopenharmony_ci *   - overlay changeset post-remove notifier
118762306a36Sopenharmony_ci *
118862306a36Sopenharmony_ci * If an error is returned by an overlay changeset post-remove notifier
118962306a36Sopenharmony_ci * then no further overlay changeset post-remove notifier will be called.
119062306a36Sopenharmony_ci *
119162306a36Sopenharmony_ci * Return: 0 on success, or a negative error number.  *@ovcs_id is set to
119262306a36Sopenharmony_ci * zero after reverting the changeset, even if a subsequent error occurs.
119362306a36Sopenharmony_ci */
119462306a36Sopenharmony_ciint of_overlay_remove(int *ovcs_id)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct overlay_changeset *ovcs;
119762306a36Sopenharmony_ci	int ret, ret_apply, ret_tmp;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (devicetree_corrupt()) {
120062306a36Sopenharmony_ci		pr_err("suspect devicetree state, refuse to remove overlay\n");
120162306a36Sopenharmony_ci		ret = -EBUSY;
120262306a36Sopenharmony_ci		goto out;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	mutex_lock(&of_mutex);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	ovcs = idr_find(&ovcs_idr, *ovcs_id);
120862306a36Sopenharmony_ci	if (!ovcs) {
120962306a36Sopenharmony_ci		ret = -ENODEV;
121062306a36Sopenharmony_ci		pr_err("remove: Could not find overlay #%d\n", *ovcs_id);
121162306a36Sopenharmony_ci		goto err_unlock;
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (!overlay_removal_is_ok(ovcs)) {
121562306a36Sopenharmony_ci		ret = -EBUSY;
121662306a36Sopenharmony_ci		goto err_unlock;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	ret = overlay_notify(ovcs, OF_OVERLAY_PRE_REMOVE);
122062306a36Sopenharmony_ci	if (ret)
122162306a36Sopenharmony_ci		goto err_unlock;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	ret_apply = 0;
122462306a36Sopenharmony_ci	ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply);
122562306a36Sopenharmony_ci	if (ret) {
122662306a36Sopenharmony_ci		if (ret_apply)
122762306a36Sopenharmony_ci			devicetree_state_flags |= DTSF_REVERT_FAIL;
122862306a36Sopenharmony_ci		goto err_unlock;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	ret = __of_changeset_revert_notify(&ovcs->cset);
123262306a36Sopenharmony_ci	if (ret)
123362306a36Sopenharmony_ci		pr_err("overlay remove changeset entry notify error %d\n", ret);
123462306a36Sopenharmony_ci	/* notify failure is not fatal, continue */
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	*ovcs_id = 0;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/*
123962306a36Sopenharmony_ci	 * Note that the overlay memory will be kfree()ed by
124062306a36Sopenharmony_ci	 * free_overlay_changeset() even if the notifier for
124162306a36Sopenharmony_ci	 * OF_OVERLAY_POST_REMOVE returns an error.
124262306a36Sopenharmony_ci	 */
124362306a36Sopenharmony_ci	ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_REMOVE);
124462306a36Sopenharmony_ci	if (ret_tmp)
124562306a36Sopenharmony_ci		if (!ret)
124662306a36Sopenharmony_ci			ret = ret_tmp;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	free_overlay_changeset(ovcs);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cierr_unlock:
125162306a36Sopenharmony_ci	/*
125262306a36Sopenharmony_ci	 * If jumped over free_overlay_changeset(), then did not kfree()
125362306a36Sopenharmony_ci	 * overlay related memory.  This is a memory leak unless a subsequent
125462306a36Sopenharmony_ci	 * of_overlay_remove() of this overlay is successful.
125562306a36Sopenharmony_ci	 */
125662306a36Sopenharmony_ci	mutex_unlock(&of_mutex);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ciout:
125962306a36Sopenharmony_ci	pr_debug("%s() err=%d\n", __func__, ret);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return ret;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_overlay_remove);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci/**
126662306a36Sopenharmony_ci * of_overlay_remove_all() - Reverts and frees all overlay changesets
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci * Removes all overlays from the system in the correct order.
126962306a36Sopenharmony_ci *
127062306a36Sopenharmony_ci * Return: 0 on success, or a negative error number
127162306a36Sopenharmony_ci */
127262306a36Sopenharmony_ciint of_overlay_remove_all(void)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	struct overlay_changeset *ovcs, *ovcs_n;
127562306a36Sopenharmony_ci	int ret;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/* the tail of list is guaranteed to be safe to remove */
127862306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) {
127962306a36Sopenharmony_ci		ret = of_overlay_remove(&ovcs->id);
128062306a36Sopenharmony_ci		if (ret)
128162306a36Sopenharmony_ci			return ret;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	return 0;
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_overlay_remove_all);
1287