1af6074fcSRob Herring // SPDX-License-Identifier: GPL-2.0
27518b589SPantelis Antoniou /*
37518b589SPantelis Antoniou * Functions for working with device tree overlays
47518b589SPantelis Antoniou *
57518b589SPantelis Antoniou * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
67518b589SPantelis Antoniou * Copyright (C) 2012 Texas Instruments Inc.
77518b589SPantelis Antoniou */
8606ad42aSRob Herring
9606ad42aSRob Herring #define pr_fmt(fmt) "OF: overlay: " fmt
10606ad42aSRob Herring
117518b589SPantelis Antoniou #include <linux/kernel.h>
127518b589SPantelis Antoniou #include <linux/module.h>
137518b589SPantelis Antoniou #include <linux/of.h>
147518b589SPantelis Antoniou #include <linux/of_device.h>
1539a751a4SFrank Rowand #include <linux/of_fdt.h>
167518b589SPantelis Antoniou #include <linux/string.h>
177518b589SPantelis Antoniou #include <linux/ctype.h>
187518b589SPantelis Antoniou #include <linux/errno.h>
197518b589SPantelis Antoniou #include <linux/slab.h>
2039a751a4SFrank Rowand #include <linux/libfdt.h>
217518b589SPantelis Antoniou #include <linux/err.h>
220d1886dfSMark Brown #include <linux/idr.h>
237518b589SPantelis Antoniou
247518b589SPantelis Antoniou #include "of_private.h"
257518b589SPantelis Antoniou
267518b589SPantelis Antoniou /**
276b4955baSFrank Rowand * struct target - info about current target node as recursing through overlay
286b4955baSFrank Rowand * @np: node where current level of overlay will be applied
296b4955baSFrank Rowand * @in_livetree: @np is a node in the live devicetree
306b4955baSFrank Rowand *
316b4955baSFrank Rowand * Used in the algorithm to create the portion of a changeset that describes
326b4955baSFrank Rowand * an overlay fragment, which is a devicetree subtree. Initially @np is a node
336b4955baSFrank Rowand * in the live devicetree where the overlay subtree is targeted to be grafted
346b4955baSFrank Rowand * into. When recursing to the next level of the overlay subtree, the target
356b4955baSFrank Rowand * also recurses to the next level of the live devicetree, as long as overlay
366b4955baSFrank Rowand * subtree node also exists in the live devicetree. When a node in the overlay
376b4955baSFrank Rowand * subtree does not exist at the same level in the live devicetree, target->np
386b4955baSFrank Rowand * points to a newly allocated node, and all subsequent targets in the subtree
396b4955baSFrank Rowand * will be newly allocated nodes.
406b4955baSFrank Rowand */
416b4955baSFrank Rowand struct target {
426b4955baSFrank Rowand struct device_node *np;
436b4955baSFrank Rowand bool in_livetree;
446b4955baSFrank Rowand };
456b4955baSFrank Rowand
466b4955baSFrank Rowand /**
470290c4caSFrank Rowand * struct fragment - info about fragment nodes in overlay expanded device tree
480290c4caSFrank Rowand * @overlay: pointer to the __overlay__ node
49*5d007ffdSGeert Uytterhoeven * @target: target of the overlay operation
507518b589SPantelis Antoniou */
510290c4caSFrank Rowand struct fragment {
527518b589SPantelis Antoniou struct device_node *overlay;
5381225ea6SFrank Rowand struct device_node *target;
547518b589SPantelis Antoniou };
557518b589SPantelis Antoniou
567518b589SPantelis Antoniou /**
570290c4caSFrank Rowand * struct overlay_changeset
5839a751a4SFrank Rowand * @id: changeset identifier
590290c4caSFrank Rowand * @ovcs_list: list on which we are located
601e408966SFrank Rowand * @new_fdt: Memory allocated to hold unflattened aligned FDT
61067c0987SFrank Rowand * @overlay_mem: the memory chunk that contains @overlay_root
621e408966SFrank Rowand * @overlay_root: expanded device tree that contains the fragment nodes
63067c0987SFrank Rowand * @notify_state: most recent notify action used on overlay
643912b791SFrank Rowand * @count: count of fragment structures
653912b791SFrank Rowand * @fragments: fragment nodes in the overlay expanded device tree
663912b791SFrank Rowand * @symbols_fragment: last element of @fragments[] is the __symbols__ node
670290c4caSFrank Rowand * @cset: changeset to apply fragments to live device tree
687518b589SPantelis Antoniou */
690290c4caSFrank Rowand struct overlay_changeset {
707518b589SPantelis Antoniou int id;
710290c4caSFrank Rowand struct list_head ovcs_list;
721e408966SFrank Rowand const void *new_fdt;
73067c0987SFrank Rowand const void *overlay_mem;
741e408966SFrank Rowand struct device_node *overlay_root;
75067c0987SFrank Rowand enum of_overlay_notify_action notify_state;
767518b589SPantelis Antoniou int count;
770290c4caSFrank Rowand struct fragment *fragments;
783912b791SFrank Rowand bool symbols_fragment;
797518b589SPantelis Antoniou struct of_changeset cset;
807518b589SPantelis Antoniou };
817518b589SPantelis Antoniou
8224789c5cSFrank Rowand /* flags are sticky - once set, do not reset */
8324789c5cSFrank Rowand static int devicetree_state_flags;
8424789c5cSFrank Rowand #define DTSF_APPLY_FAIL 0x01
8524789c5cSFrank Rowand #define DTSF_REVERT_FAIL 0x02
8624789c5cSFrank Rowand
8724789c5cSFrank Rowand /*
8824789c5cSFrank Rowand * If a changeset apply or revert encounters an error, an attempt will
8924789c5cSFrank Rowand * be made to undo partial changes, but may fail. If the undo fails
9024789c5cSFrank Rowand * we do not know the state of the devicetree.
9124789c5cSFrank Rowand */
devicetree_corrupt(void)9224789c5cSFrank Rowand static int devicetree_corrupt(void)
9324789c5cSFrank Rowand {
9424789c5cSFrank Rowand return devicetree_state_flags &
9524789c5cSFrank Rowand (DTSF_APPLY_FAIL | DTSF_REVERT_FAIL);
9624789c5cSFrank Rowand }
9724789c5cSFrank Rowand
980290c4caSFrank Rowand static int build_changeset_next_level(struct overlay_changeset *ovcs,
996b4955baSFrank Rowand struct target *target, const struct device_node *overlay_node);
1007518b589SPantelis Antoniou
101f948d6d8SFrank Rowand /*
102f948d6d8SFrank Rowand * of_resolve_phandles() finds the largest phandle in the live tree.
103f948d6d8SFrank Rowand * of_overlay_apply() may add a larger phandle to the live tree.
104f948d6d8SFrank Rowand * Do not allow race between two overlays being applied simultaneously:
105f948d6d8SFrank Rowand * mutex_lock(&of_overlay_phandle_mutex)
106f948d6d8SFrank Rowand * of_resolve_phandles()
107f948d6d8SFrank Rowand * of_overlay_apply()
108f948d6d8SFrank Rowand * mutex_unlock(&of_overlay_phandle_mutex)
109f948d6d8SFrank Rowand */
110f948d6d8SFrank Rowand static DEFINE_MUTEX(of_overlay_phandle_mutex);
111f948d6d8SFrank Rowand
of_overlay_mutex_lock(void)112f948d6d8SFrank Rowand void of_overlay_mutex_lock(void)
113f948d6d8SFrank Rowand {
114f948d6d8SFrank Rowand mutex_lock(&of_overlay_phandle_mutex);
115f948d6d8SFrank Rowand }
116f948d6d8SFrank Rowand
of_overlay_mutex_unlock(void)117f948d6d8SFrank Rowand void of_overlay_mutex_unlock(void)
118f948d6d8SFrank Rowand {
119f948d6d8SFrank Rowand mutex_unlock(&of_overlay_phandle_mutex);
120f948d6d8SFrank Rowand }
121f948d6d8SFrank Rowand
12261b4de4eSFrank Rowand static LIST_HEAD(ovcs_list);
12361b4de4eSFrank Rowand static DEFINE_IDR(ovcs_idr);
12461b4de4eSFrank Rowand
1250290c4caSFrank Rowand static BLOCKING_NOTIFIER_HEAD(overlay_notify_chain);
12639a842e2SAlan Tull
12783ef4777SJan Kiszka /**
12883ef4777SJan Kiszka * of_overlay_notifier_register() - Register notifier for overlay operations
12983ef4777SJan Kiszka * @nb: Notifier block to register
13083ef4777SJan Kiszka *
13183ef4777SJan Kiszka * Register for notification on overlay operations on device tree nodes. The
13283ef4777SJan Kiszka * reported actions definied by @of_reconfig_change. The notifier callback
13383ef4777SJan Kiszka * furthermore receives a pointer to the affected device tree node.
13483ef4777SJan Kiszka *
13583ef4777SJan Kiszka * Note that a notifier callback is not supposed to store pointers to a device
13683ef4777SJan Kiszka * tree node or its content beyond @OF_OVERLAY_POST_REMOVE corresponding to the
13783ef4777SJan Kiszka * respective node it received.
13883ef4777SJan Kiszka */
of_overlay_notifier_register(struct notifier_block * nb)13939a842e2SAlan Tull int of_overlay_notifier_register(struct notifier_block *nb)
14039a842e2SAlan Tull {
1410290c4caSFrank Rowand return blocking_notifier_chain_register(&overlay_notify_chain, nb);
14239a842e2SAlan Tull }
14339a842e2SAlan Tull EXPORT_SYMBOL_GPL(of_overlay_notifier_register);
14439a842e2SAlan Tull
14583ef4777SJan Kiszka /**
146f957d5b7SLee Jones * of_overlay_notifier_unregister() - Unregister notifier for overlay operations
14783ef4777SJan Kiszka * @nb: Notifier block to unregister
14883ef4777SJan Kiszka */
of_overlay_notifier_unregister(struct notifier_block * nb)14939a842e2SAlan Tull int of_overlay_notifier_unregister(struct notifier_block *nb)
15039a842e2SAlan Tull {
1510290c4caSFrank Rowand return blocking_notifier_chain_unregister(&overlay_notify_chain, nb);
15239a842e2SAlan Tull }
15339a842e2SAlan Tull EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister);
15439a842e2SAlan Tull
overlay_notify(struct overlay_changeset * ovcs,enum of_overlay_notify_action action)1550290c4caSFrank Rowand static int overlay_notify(struct overlay_changeset *ovcs,
15639a842e2SAlan Tull enum of_overlay_notify_action action)
15739a842e2SAlan Tull {
15839a842e2SAlan Tull struct of_overlay_notify_data nd;
15939a842e2SAlan Tull int i, ret;
16039a842e2SAlan Tull
161067c0987SFrank Rowand ovcs->notify_state = action;
162067c0987SFrank Rowand
1630290c4caSFrank Rowand for (i = 0; i < ovcs->count; i++) {
1640290c4caSFrank Rowand struct fragment *fragment = &ovcs->fragments[i];
16539a842e2SAlan Tull
1660290c4caSFrank Rowand nd.target = fragment->target;
1670290c4caSFrank Rowand nd.overlay = fragment->overlay;
16839a842e2SAlan Tull
1690290c4caSFrank Rowand ret = blocking_notifier_call_chain(&overlay_notify_chain,
17039a842e2SAlan Tull action, &nd);
1715f756a2eSNuno Sá if (notifier_to_errno(ret)) {
17224789c5cSFrank Rowand ret = notifier_to_errno(ret);
17324789c5cSFrank Rowand pr_err("overlay changeset %s notifier error %d, target: %pOF\n",
1741ac17586SFrank Rowand of_overlay_action_name(action), ret, nd.target);
17524789c5cSFrank Rowand return ret;
17624789c5cSFrank Rowand }
17739a842e2SAlan Tull }
17839a842e2SAlan Tull
17939a842e2SAlan Tull return 0;
18039a842e2SAlan Tull }
18139a842e2SAlan Tull
18242b2e94fSFrank Rowand /*
183e0a58f3eSFrank Rowand * The values of properties in the "/__symbols__" node are paths in
1841e408966SFrank Rowand * the ovcs->overlay_root. When duplicating the properties, the paths
185e0a58f3eSFrank Rowand * need to be adjusted to be the correct path for the live device tree.
18642b2e94fSFrank Rowand *
187e0a58f3eSFrank Rowand * The paths refer to a node in the subtree of a fragment node's "__overlay__"
188e0a58f3eSFrank Rowand * node, for example "/fragment@0/__overlay__/symbol_path_tail",
189e0a58f3eSFrank Rowand * where symbol_path_tail can be a single node or it may be a multi-node path.
19042b2e94fSFrank Rowand *
19142b2e94fSFrank Rowand * The duplicated property value will be modified by replacing the
19242b2e94fSFrank Rowand * "/fragment_name/__overlay/" portion of the value with the target
19342b2e94fSFrank Rowand * path from the fragment node.
19442b2e94fSFrank Rowand */
dup_and_fixup_symbol_prop(struct overlay_changeset * ovcs,const struct property * prop)1950290c4caSFrank Rowand static struct property *dup_and_fixup_symbol_prop(
1960290c4caSFrank Rowand struct overlay_changeset *ovcs, const struct property *prop)
1977518b589SPantelis Antoniou {
1980290c4caSFrank Rowand struct fragment *fragment;
199e0a58f3eSFrank Rowand struct property *new_prop;
200e0a58f3eSFrank Rowand struct device_node *fragment_node;
201e0a58f3eSFrank Rowand struct device_node *overlay_node;
202e0a58f3eSFrank Rowand const char *path;
203e0a58f3eSFrank Rowand const char *path_tail;
204d1651b03SFrank Rowand const char *target_path;
205d1651b03SFrank Rowand int k;
206d1651b03SFrank Rowand int overlay_name_len;
207e0a58f3eSFrank Rowand int path_len;
208e0a58f3eSFrank Rowand int path_tail_len;
209d1651b03SFrank Rowand int target_path_len;
210d1651b03SFrank Rowand
211d1651b03SFrank Rowand if (!prop->value)
212d1651b03SFrank Rowand return NULL;
213e0a58f3eSFrank Rowand if (strnlen(prop->value, prop->length) >= prop->length)
214d1651b03SFrank Rowand return NULL;
215e0a58f3eSFrank Rowand path = prop->value;
216e0a58f3eSFrank Rowand path_len = strlen(path);
217e0a58f3eSFrank Rowand
218e0a58f3eSFrank Rowand if (path_len < 1)
219e0a58f3eSFrank Rowand return NULL;
2201e408966SFrank Rowand fragment_node = __of_find_node_by_path(ovcs->overlay_root, path + 1);
221e0a58f3eSFrank Rowand overlay_node = __of_find_node_by_path(fragment_node, "__overlay__/");
222e0a58f3eSFrank Rowand of_node_put(fragment_node);
223e0a58f3eSFrank Rowand of_node_put(overlay_node);
224d1651b03SFrank Rowand
2250290c4caSFrank Rowand for (k = 0; k < ovcs->count; k++) {
2260290c4caSFrank Rowand fragment = &ovcs->fragments[k];
227e0a58f3eSFrank Rowand if (fragment->overlay == overlay_node)
228d1651b03SFrank Rowand break;
229d1651b03SFrank Rowand }
2300290c4caSFrank Rowand if (k >= ovcs->count)
231e0a58f3eSFrank Rowand return NULL;
232d1651b03SFrank Rowand
233e0a58f3eSFrank Rowand overlay_name_len = snprintf(NULL, 0, "%pOF", fragment->overlay);
234e0a58f3eSFrank Rowand
235e0a58f3eSFrank Rowand if (overlay_name_len > path_len)
236e0a58f3eSFrank Rowand return NULL;
237e0a58f3eSFrank Rowand path_tail = path + overlay_name_len;
238e0a58f3eSFrank Rowand path_tail_len = strlen(path_tail);
239e0a58f3eSFrank Rowand
240e0a58f3eSFrank Rowand target_path = kasprintf(GFP_KERNEL, "%pOF", fragment->target);
241e0a58f3eSFrank Rowand if (!target_path)
242e0a58f3eSFrank Rowand return NULL;
243d1651b03SFrank Rowand target_path_len = strlen(target_path);
244d1651b03SFrank Rowand
245e0a58f3eSFrank Rowand new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
246e0a58f3eSFrank Rowand if (!new_prop)
247e0a58f3eSFrank Rowand goto err_free_target_path;
248d1651b03SFrank Rowand
249e0a58f3eSFrank Rowand new_prop->name = kstrdup(prop->name, GFP_KERNEL);
250e0a58f3eSFrank Rowand new_prop->length = target_path_len + path_tail_len + 1;
251e0a58f3eSFrank Rowand new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
252e0a58f3eSFrank Rowand if (!new_prop->name || !new_prop->value)
253e0a58f3eSFrank Rowand goto err_free_new_prop;
254d1651b03SFrank Rowand
255e0a58f3eSFrank Rowand strcpy(new_prop->value, target_path);
256e0a58f3eSFrank Rowand strcpy(new_prop->value + target_path_len, path_tail);
257d1651b03SFrank Rowand
258e0a58f3eSFrank Rowand of_property_set_flag(new_prop, OF_DYNAMIC);
259d1651b03SFrank Rowand
260478ff649SFrank Rowand kfree(target_path);
261478ff649SFrank Rowand
262e0a58f3eSFrank Rowand return new_prop;
263d1651b03SFrank Rowand
264e0a58f3eSFrank Rowand err_free_new_prop:
265e0a58f3eSFrank Rowand kfree(new_prop->name);
266e0a58f3eSFrank Rowand kfree(new_prop->value);
267e0a58f3eSFrank Rowand kfree(new_prop);
268e0a58f3eSFrank Rowand err_free_target_path:
269e0a58f3eSFrank Rowand kfree(target_path);
270d1651b03SFrank Rowand
271d1651b03SFrank Rowand return NULL;
272d1651b03SFrank Rowand }
273d1651b03SFrank Rowand
2740290c4caSFrank Rowand /**
2750290c4caSFrank Rowand * add_changeset_property() - add @overlay_prop to overlay changeset
2760290c4caSFrank Rowand * @ovcs: overlay changeset
2776b4955baSFrank Rowand * @target: where @overlay_prop will be placed
2780290c4caSFrank Rowand * @overlay_prop: property to add or update, from overlay tree
2793912b791SFrank Rowand * @is_symbols_prop: 1 if @overlay_prop is from node "/__symbols__"
2800290c4caSFrank Rowand *
2816b4955baSFrank Rowand * If @overlay_prop does not already exist in live devicetree, add changeset
2826b4955baSFrank Rowand * entry to add @overlay_prop in @target, else add changeset entry to update
2830290c4caSFrank Rowand * value of @overlay_prop.
2840290c4caSFrank Rowand *
2856b4955baSFrank Rowand * @target may be either in the live devicetree or in a new subtree that
2866b4955baSFrank Rowand * is contained in the changeset.
2876b4955baSFrank Rowand *
2886f751188SFrank Rowand * Some special properties are not added or updated (no error returned):
2896f751188SFrank Rowand * "name", "phandle", "linux,phandle".
2906f751188SFrank Rowand *
2916f751188SFrank Rowand * Properties "#address-cells" and "#size-cells" are not updated if they
2926f751188SFrank Rowand * are already in the live tree, but if present in the live tree, the values
2936f751188SFrank Rowand * in the overlay must match the values in the live tree.
2940290c4caSFrank Rowand *
295646afc4aSFrank Rowand * Update of property in symbols node is not allowed.
2960290c4caSFrank Rowand *
2978c8239c2SRob Herring * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
2980290c4caSFrank Rowand * invalid @overlay.
299646afc4aSFrank Rowand */
add_changeset_property(struct overlay_changeset * ovcs,struct target * target,struct property * overlay_prop,bool is_symbols_prop)3000290c4caSFrank Rowand static int add_changeset_property(struct overlay_changeset *ovcs,
3016b4955baSFrank Rowand struct target *target, struct property *overlay_prop,
3023912b791SFrank Rowand bool is_symbols_prop)
303d1651b03SFrank Rowand {
3040290c4caSFrank Rowand struct property *new_prop = NULL, *prop;
305ac0f3e30SLixin Wang int ret = 0;
3067518b589SPantelis Antoniou
307f9627881SFrank Rowand if (target->in_livetree)
3080290c4caSFrank Rowand if (!of_prop_cmp(overlay_prop->name, "name") ||
3090290c4caSFrank Rowand !of_prop_cmp(overlay_prop->name, "phandle") ||
3100290c4caSFrank Rowand !of_prop_cmp(overlay_prop->name, "linux,phandle"))
3117518b589SPantelis Antoniou return 0;
3127518b589SPantelis Antoniou
3136b4955baSFrank Rowand if (target->in_livetree)
3146b4955baSFrank Rowand prop = of_find_property(target->np, overlay_prop->name, NULL);
3156b4955baSFrank Rowand else
3166b4955baSFrank Rowand prop = NULL;
3176b4955baSFrank Rowand
318637392a8SFrank Rowand if (prop) {
319637392a8SFrank Rowand if (!of_prop_cmp(prop->name, "#address-cells")) {
320637392a8SFrank Rowand if (!of_prop_val_eq(prop, overlay_prop)) {
321637392a8SFrank Rowand pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n",
322637392a8SFrank Rowand target->np);
323637392a8SFrank Rowand ret = -EINVAL;
324637392a8SFrank Rowand }
325637392a8SFrank Rowand return ret;
326637392a8SFrank Rowand
327637392a8SFrank Rowand } else if (!of_prop_cmp(prop->name, "#size-cells")) {
328637392a8SFrank Rowand if (!of_prop_val_eq(prop, overlay_prop)) {
329637392a8SFrank Rowand pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n",
330637392a8SFrank Rowand target->np);
331637392a8SFrank Rowand ret = -EINVAL;
332637392a8SFrank Rowand }
333637392a8SFrank Rowand return ret;
334637392a8SFrank Rowand }
335637392a8SFrank Rowand }
336637392a8SFrank Rowand
3373912b791SFrank Rowand if (is_symbols_prop) {
3380290c4caSFrank Rowand if (prop)
339d1651b03SFrank Rowand return -EINVAL;
3400290c4caSFrank Rowand new_prop = dup_and_fixup_symbol_prop(ovcs, overlay_prop);
341d1651b03SFrank Rowand } else {
3420290c4caSFrank Rowand new_prop = __of_prop_dup(overlay_prop, GFP_KERNEL);
343d1651b03SFrank Rowand }
344d1651b03SFrank Rowand
3450290c4caSFrank Rowand if (!new_prop)
3467518b589SPantelis Antoniou return -ENOMEM;
3477518b589SPantelis Antoniou
3486f751188SFrank Rowand if (!prop) {
349f9627881SFrank Rowand if (!target->in_livetree) {
350f9627881SFrank Rowand new_prop->next = target->np->deadprops;
351f9627881SFrank Rowand target->np->deadprops = new_prop;
352f9627881SFrank Rowand }
3536b4955baSFrank Rowand ret = of_changeset_add_property(&ovcs->cset, target->np,
3540290c4caSFrank Rowand new_prop);
3556f751188SFrank Rowand } else {
3566b4955baSFrank Rowand ret = of_changeset_update_property(&ovcs->cset, target->np,
3570290c4caSFrank Rowand new_prop);
3586f751188SFrank Rowand }
3596f751188SFrank Rowand
360637392a8SFrank Rowand if (!of_node_check_flag(target->np, OF_OVERLAY))
3616f751188SFrank Rowand pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n",
3626f751188SFrank Rowand target->np, new_prop->name);
3637518b589SPantelis Antoniou
364ac0f3e30SLixin Wang if (ret) {
3650290c4caSFrank Rowand kfree(new_prop->name);
3660290c4caSFrank Rowand kfree(new_prop->value);
3670290c4caSFrank Rowand kfree(new_prop);
368ac0f3e30SLixin Wang }
369ac0f3e30SLixin Wang return ret;
3707518b589SPantelis Antoniou }
3717518b589SPantelis Antoniou
3720290c4caSFrank Rowand /**
3730290c4caSFrank Rowand * add_changeset_node() - add @node (and children) to overlay changeset
3740290c4caSFrank Rowand * @ovcs: overlay changeset
3756b4955baSFrank Rowand * @target: where @node will be placed in live tree or changeset
3760290c4caSFrank Rowand * @node: node from within overlay device tree fragment
3770290c4caSFrank Rowand *
3786b4955baSFrank Rowand * If @node does not already exist in @target, add changeset entry
3796b4955baSFrank Rowand * to add @node in @target.
3800290c4caSFrank Rowand *
3816b4955baSFrank Rowand * If @node already exists in @target, and the existing node has
3820290c4caSFrank Rowand * a phandle, the overlay node is not allowed to have a phandle.
3830290c4caSFrank Rowand *
3840290c4caSFrank Rowand * If @node has child nodes, add the children recursively via
3850290c4caSFrank Rowand * build_changeset_next_level().
3860290c4caSFrank Rowand *
387b89dae18SFrank Rowand * NOTE_1: A live devicetree created from a flattened device tree (FDT) will
388b89dae18SFrank Rowand * not contain the full path in node->full_name. Thus an overlay
389b89dae18SFrank Rowand * created from an FDT also will not contain the full path in
390b89dae18SFrank Rowand * node->full_name. However, a live devicetree created from Open
391b89dae18SFrank Rowand * Firmware may have the full path in node->full_name.
392b89dae18SFrank Rowand *
393b89dae18SFrank Rowand * add_changeset_node() follows the FDT convention and does not include
394b89dae18SFrank Rowand * the full path in node->full_name. Even though it expects the overlay
395b89dae18SFrank Rowand * to not contain the full path, it uses kbasename() to remove the
396b89dae18SFrank Rowand * full path should it exist. It also uses kbasename() in comparisons
397b89dae18SFrank Rowand * to nodes in the live devicetree so that it can apply an overlay to
398b89dae18SFrank Rowand * a live devicetree created from Open Firmware.
399b89dae18SFrank Rowand *
400b89dae18SFrank Rowand * NOTE_2: Multiple mods of created nodes not supported.
4010290c4caSFrank Rowand *
4028c8239c2SRob Herring * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
4030290c4caSFrank Rowand * invalid @overlay.
4040290c4caSFrank Rowand */
add_changeset_node(struct overlay_changeset * ovcs,struct target * target,struct device_node * node)4050290c4caSFrank Rowand static int add_changeset_node(struct overlay_changeset *ovcs,
4066b4955baSFrank Rowand struct target *target, struct device_node *node)
4077518b589SPantelis Antoniou {
4080290c4caSFrank Rowand const char *node_kbasename;
409f9627881SFrank Rowand const __be32 *phandle;
410d3a89165SFabio Estevam struct device_node *tchild;
4116b4955baSFrank Rowand struct target target_child;
412f9627881SFrank Rowand int ret = 0, size;
4137518b589SPantelis Antoniou
4140290c4caSFrank Rowand node_kbasename = kbasename(node->full_name);
4157518b589SPantelis Antoniou
4166b4955baSFrank Rowand for_each_child_of_node(target->np, tchild)
4170290c4caSFrank Rowand if (!of_node_cmp(node_kbasename, kbasename(tchild->full_name)))
418c1cd1e01SFrank Rowand break;
419c1cd1e01SFrank Rowand
42061b4de4eSFrank Rowand if (!tchild) {
4218814dc46SFrank Rowand tchild = __of_node_dup(NULL, node_kbasename);
4227518b589SPantelis Antoniou if (!tchild)
4237518b589SPantelis Antoniou return -ENOMEM;
4247518b589SPantelis Antoniou
4256b4955baSFrank Rowand tchild->parent = target->np;
426f9627881SFrank Rowand tchild->name = __of_get_property(node, "name", NULL);
427f9627881SFrank Rowand
428f9627881SFrank Rowand if (!tchild->name)
429f9627881SFrank Rowand tchild->name = "<NULL>";
430f9627881SFrank Rowand
431f9627881SFrank Rowand /* ignore obsolete "linux,phandle" */
432f9627881SFrank Rowand phandle = __of_get_property(node, "phandle", &size);
433f9627881SFrank Rowand if (phandle && (size == 4))
434f9627881SFrank Rowand tchild->phandle = be32_to_cpup(phandle);
435f9627881SFrank Rowand
436144552c7SFrank Rowand of_node_set_flag(tchild, OF_OVERLAY);
4377518b589SPantelis Antoniou
4380290c4caSFrank Rowand ret = of_changeset_attach_node(&ovcs->cset, tchild);
4397518b589SPantelis Antoniou if (ret)
4407518b589SPantelis Antoniou return ret;
4417518b589SPantelis Antoniou
4426b4955baSFrank Rowand target_child.np = tchild;
4436b4955baSFrank Rowand target_child.in_livetree = false;
4446b4955baSFrank Rowand
4456b4955baSFrank Rowand ret = build_changeset_next_level(ovcs, &target_child, node);
4467c528e45SFrank Rowand of_node_put(tchild);
4477c528e45SFrank Rowand return ret;
4487518b589SPantelis Antoniou }
4497518b589SPantelis Antoniou
4506b4955baSFrank Rowand if (node->phandle && tchild->phandle) {
4516d0f5470SFrank Rowand ret = -EINVAL;
4526b4955baSFrank Rowand } else {
4536b4955baSFrank Rowand target_child.np = tchild;
4546b4955baSFrank Rowand target_child.in_livetree = target->in_livetree;
4556b4955baSFrank Rowand ret = build_changeset_next_level(ovcs, &target_child, node);
4566b4955baSFrank Rowand }
45761b4de4eSFrank Rowand of_node_put(tchild);
45861b4de4eSFrank Rowand
4597518b589SPantelis Antoniou return ret;
4607518b589SPantelis Antoniou }
4617518b589SPantelis Antoniou
4620290c4caSFrank Rowand /**
4630290c4caSFrank Rowand * build_changeset_next_level() - add level of overlay changeset
4640290c4caSFrank Rowand * @ovcs: overlay changeset
4656b4955baSFrank Rowand * @target: where to place @overlay_node in live tree
4660290c4caSFrank Rowand * @overlay_node: node from within an overlay device tree fragment
4677518b589SPantelis Antoniou *
4680290c4caSFrank Rowand * Add the properties (if any) and nodes (if any) from @overlay_node to the
4690290c4caSFrank Rowand * @ovcs->cset changeset. If an added node has child nodes, they will
4700290c4caSFrank Rowand * be added recursively.
471646afc4aSFrank Rowand *
472646afc4aSFrank Rowand * Do not allow symbols node to have any children.
4730290c4caSFrank Rowand *
4748c8239c2SRob Herring * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
4750290c4caSFrank Rowand * invalid @overlay_node.
4767518b589SPantelis Antoniou */
build_changeset_next_level(struct overlay_changeset * ovcs,struct target * target,const struct device_node * overlay_node)4770290c4caSFrank Rowand static int build_changeset_next_level(struct overlay_changeset *ovcs,
4786b4955baSFrank Rowand struct target *target, const struct device_node *overlay_node)
4797518b589SPantelis Antoniou {
4807518b589SPantelis Antoniou struct device_node *child;
4817518b589SPantelis Antoniou struct property *prop;
4827518b589SPantelis Antoniou int ret;
4837518b589SPantelis Antoniou
4840290c4caSFrank Rowand for_each_property_of_node(overlay_node, prop) {
4856b4955baSFrank Rowand ret = add_changeset_property(ovcs, target, prop, 0);
4867518b589SPantelis Antoniou if (ret) {
48724789c5cSFrank Rowand pr_debug("Failed to apply prop @%pOF/%s, err=%d\n",
4886b4955baSFrank Rowand target->np, prop->name, ret);
4897518b589SPantelis Antoniou return ret;
4907518b589SPantelis Antoniou }
4917518b589SPantelis Antoniou }
4927518b589SPantelis Antoniou
4930290c4caSFrank Rowand for_each_child_of_node(overlay_node, child) {
4946b4955baSFrank Rowand ret = add_changeset_node(ovcs, target, child);
495bbed8794SFrank Rowand if (ret) {
496a613b26aSRob Herring pr_debug("Failed to apply node @%pOF/%pOFn, err=%d\n",
4976b4955baSFrank Rowand target->np, child, ret);
498001cf504SJulia Lawall of_node_put(child);
4997518b589SPantelis Antoniou return ret;
5007518b589SPantelis Antoniou }
5017518b589SPantelis Antoniou }
5027518b589SPantelis Antoniou
5037518b589SPantelis Antoniou return 0;
5047518b589SPantelis Antoniou }
5057518b589SPantelis Antoniou
5063912b791SFrank Rowand /*
5073912b791SFrank Rowand * Add the properties from __overlay__ node to the @ovcs->cset changeset.
5083912b791SFrank Rowand */
build_changeset_symbols_node(struct overlay_changeset * ovcs,struct target * target,const struct device_node * overlay_symbols_node)5093912b791SFrank Rowand static int build_changeset_symbols_node(struct overlay_changeset *ovcs,
5106b4955baSFrank Rowand struct target *target,
5113912b791SFrank Rowand const struct device_node *overlay_symbols_node)
5123912b791SFrank Rowand {
5133912b791SFrank Rowand struct property *prop;
5143912b791SFrank Rowand int ret;
5153912b791SFrank Rowand
5163912b791SFrank Rowand for_each_property_of_node(overlay_symbols_node, prop) {
5176b4955baSFrank Rowand ret = add_changeset_property(ovcs, target, prop, 1);
5183912b791SFrank Rowand if (ret) {
519a15e824fSFrank Rowand pr_debug("Failed to apply symbols prop @%pOF/%s, err=%d\n",
5206b4955baSFrank Rowand target->np, prop->name, ret);
5213912b791SFrank Rowand return ret;
5223912b791SFrank Rowand }
5233912b791SFrank Rowand }
5243912b791SFrank Rowand
5253912b791SFrank Rowand return 0;
5263912b791SFrank Rowand }
5273912b791SFrank Rowand
find_dup_cset_node_entry(struct overlay_changeset * ovcs,struct of_changeset_entry * ce_1)5282fe0e876SFrank Rowand static int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
5292fe0e876SFrank Rowand struct of_changeset_entry *ce_1)
530c168263bSFrank Rowand {
5312fe0e876SFrank Rowand struct of_changeset_entry *ce_2;
532c168263bSFrank Rowand char *fn_1, *fn_2;
5332fe0e876SFrank Rowand int node_path_match;
534c168263bSFrank Rowand
5352fe0e876SFrank Rowand if (ce_1->action != OF_RECONFIG_ATTACH_NODE &&
5362fe0e876SFrank Rowand ce_1->action != OF_RECONFIG_DETACH_NODE)
5372fe0e876SFrank Rowand return 0;
538c168263bSFrank Rowand
539c168263bSFrank Rowand ce_2 = ce_1;
540c168263bSFrank Rowand list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
5412fe0e876SFrank Rowand if ((ce_2->action != OF_RECONFIG_ATTACH_NODE &&
5422fe0e876SFrank Rowand ce_2->action != OF_RECONFIG_DETACH_NODE) ||
5432fe0e876SFrank Rowand of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
5442fe0e876SFrank Rowand continue;
5452fe0e876SFrank Rowand
546c168263bSFrank Rowand fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
547c168263bSFrank Rowand fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
548ee9d7a0eSruanjinjie node_path_match = !fn_1 || !fn_2 || !strcmp(fn_1, fn_2);
549c168263bSFrank Rowand kfree(fn_1);
550c168263bSFrank Rowand kfree(fn_2);
5512fe0e876SFrank Rowand if (node_path_match) {
5522fe0e876SFrank Rowand pr_err("ERROR: multiple fragments add and/or delete node %pOF\n",
553c168263bSFrank Rowand ce_1->np);
554c168263bSFrank Rowand return -EINVAL;
555c168263bSFrank Rowand }
556c168263bSFrank Rowand }
5572fe0e876SFrank Rowand
5582fe0e876SFrank Rowand return 0;
559c168263bSFrank Rowand }
5602fe0e876SFrank Rowand
find_dup_cset_prop(struct overlay_changeset * ovcs,struct of_changeset_entry * ce_1)5612fe0e876SFrank Rowand static int find_dup_cset_prop(struct overlay_changeset *ovcs,
5622fe0e876SFrank Rowand struct of_changeset_entry *ce_1)
5632fe0e876SFrank Rowand {
5642fe0e876SFrank Rowand struct of_changeset_entry *ce_2;
5652fe0e876SFrank Rowand char *fn_1, *fn_2;
5662fe0e876SFrank Rowand int node_path_match;
5672fe0e876SFrank Rowand
5682fe0e876SFrank Rowand if (ce_1->action != OF_RECONFIG_ADD_PROPERTY &&
5692fe0e876SFrank Rowand ce_1->action != OF_RECONFIG_REMOVE_PROPERTY &&
5702fe0e876SFrank Rowand ce_1->action != OF_RECONFIG_UPDATE_PROPERTY)
5712fe0e876SFrank Rowand return 0;
5722fe0e876SFrank Rowand
5732fe0e876SFrank Rowand ce_2 = ce_1;
5742fe0e876SFrank Rowand list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
5752fe0e876SFrank Rowand if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY &&
5762fe0e876SFrank Rowand ce_2->action != OF_RECONFIG_REMOVE_PROPERTY &&
5772fe0e876SFrank Rowand ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) ||
5782fe0e876SFrank Rowand of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
5792fe0e876SFrank Rowand continue;
5802fe0e876SFrank Rowand
5812fe0e876SFrank Rowand fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
5822fe0e876SFrank Rowand fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
583ee9d7a0eSruanjinjie node_path_match = !fn_1 || !fn_2 || !strcmp(fn_1, fn_2);
5842fe0e876SFrank Rowand kfree(fn_1);
5852fe0e876SFrank Rowand kfree(fn_2);
5862fe0e876SFrank Rowand if (node_path_match &&
5872fe0e876SFrank Rowand !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) {
5882fe0e876SFrank Rowand pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n",
5892fe0e876SFrank Rowand ce_1->np, ce_1->prop->name);
5902fe0e876SFrank Rowand return -EINVAL;
591c168263bSFrank Rowand }
592c168263bSFrank Rowand }
593c168263bSFrank Rowand
594c168263bSFrank Rowand return 0;
595c168263bSFrank Rowand }
596c168263bSFrank Rowand
597c168263bSFrank Rowand /**
5982fe0e876SFrank Rowand * changeset_dup_entry_check() - check for duplicate entries
5992fe0e876SFrank Rowand * @ovcs: Overlay changeset
6002fe0e876SFrank Rowand *
6012fe0e876SFrank Rowand * Check changeset @ovcs->cset for multiple {add or delete} node entries for
6022fe0e876SFrank Rowand * the same node or duplicate {add, delete, or update} properties entries
6032fe0e876SFrank Rowand * for the same property.
6042fe0e876SFrank Rowand *
6058c8239c2SRob Herring * Return: 0 on success, or -EINVAL if duplicate changeset entry found.
6062fe0e876SFrank Rowand */
changeset_dup_entry_check(struct overlay_changeset * ovcs)6072fe0e876SFrank Rowand static int changeset_dup_entry_check(struct overlay_changeset *ovcs)
6082fe0e876SFrank Rowand {
6092fe0e876SFrank Rowand struct of_changeset_entry *ce_1;
6102fe0e876SFrank Rowand int dup_entry = 0;
6112fe0e876SFrank Rowand
6122fe0e876SFrank Rowand list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
6132fe0e876SFrank Rowand dup_entry |= find_dup_cset_node_entry(ovcs, ce_1);
6142fe0e876SFrank Rowand dup_entry |= find_dup_cset_prop(ovcs, ce_1);
6152fe0e876SFrank Rowand }
6162fe0e876SFrank Rowand
6172fe0e876SFrank Rowand return dup_entry ? -EINVAL : 0;
6182fe0e876SFrank Rowand }
6192fe0e876SFrank Rowand
6202fe0e876SFrank Rowand /**
6210290c4caSFrank Rowand * build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments
6220290c4caSFrank Rowand * @ovcs: Overlay changeset
6237518b589SPantelis Antoniou *
6240290c4caSFrank Rowand * Create changeset @ovcs->cset to contain the nodes and properties of the
6250290c4caSFrank Rowand * overlay device tree fragments in @ovcs->fragments[]. If an error occurs,
6260290c4caSFrank Rowand * any portions of the changeset that were successfully created will remain
6270290c4caSFrank Rowand * in @ovcs->cset.
6280290c4caSFrank Rowand *
6298c8239c2SRob Herring * Return: 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
6300290c4caSFrank Rowand * invalid overlay in @ovcs->fragments[].
6317518b589SPantelis Antoniou */
build_changeset(struct overlay_changeset * ovcs)6320290c4caSFrank Rowand static int build_changeset(struct overlay_changeset *ovcs)
6337518b589SPantelis Antoniou {
6343912b791SFrank Rowand struct fragment *fragment;
6356b4955baSFrank Rowand struct target target;
6363912b791SFrank Rowand int fragments_count, i, ret;
6377518b589SPantelis Antoniou
6383912b791SFrank Rowand /*
6393912b791SFrank Rowand * if there is a symbols fragment in ovcs->fragments[i] it is
6403912b791SFrank Rowand * the final element in the array
6413912b791SFrank Rowand */
6423912b791SFrank Rowand if (ovcs->symbols_fragment)
6433912b791SFrank Rowand fragments_count = ovcs->count - 1;
6443912b791SFrank Rowand else
6453912b791SFrank Rowand fragments_count = ovcs->count;
6463912b791SFrank Rowand
6473912b791SFrank Rowand for (i = 0; i < fragments_count; i++) {
6483912b791SFrank Rowand fragment = &ovcs->fragments[i];
6497518b589SPantelis Antoniou
6506b4955baSFrank Rowand target.np = fragment->target;
6516b4955baSFrank Rowand target.in_livetree = true;
6526b4955baSFrank Rowand ret = build_changeset_next_level(ovcs, &target,
6533912b791SFrank Rowand fragment->overlay);
6543912b791SFrank Rowand if (ret) {
655a15e824fSFrank Rowand pr_debug("fragment apply failed '%pOF'\n",
656a15e824fSFrank Rowand fragment->target);
6573912b791SFrank Rowand return ret;
6583912b791SFrank Rowand }
6593912b791SFrank Rowand }
6603912b791SFrank Rowand
6613912b791SFrank Rowand if (ovcs->symbols_fragment) {
6623912b791SFrank Rowand fragment = &ovcs->fragments[ovcs->count - 1];
6636b4955baSFrank Rowand
6646b4955baSFrank Rowand target.np = fragment->target;
6656b4955baSFrank Rowand target.in_livetree = true;
6666b4955baSFrank Rowand ret = build_changeset_symbols_node(ovcs, &target,
6673912b791SFrank Rowand fragment->overlay);
6680290c4caSFrank Rowand if (ret) {
669a15e824fSFrank Rowand pr_debug("symbols fragment apply failed '%pOF'\n",
670a15e824fSFrank Rowand fragment->target);
6710290c4caSFrank Rowand return ret;
6727518b589SPantelis Antoniou }
6737518b589SPantelis Antoniou }
6747518b589SPantelis Antoniou
6752fe0e876SFrank Rowand return changeset_dup_entry_check(ovcs);
6767518b589SPantelis Antoniou }
6777518b589SPantelis Antoniou
6787518b589SPantelis Antoniou /*
6797518b589SPantelis Antoniou * Find the target node using a number of different strategies
680646afc4aSFrank Rowand * in order of preference:
6817518b589SPantelis Antoniou *
682646afc4aSFrank Rowand * 1) "target" property containing the phandle of the target
683646afc4aSFrank Rowand * 2) "target-path" property containing the path of the target
6847518b589SPantelis Antoniou */
find_target(struct device_node * info_node,struct device_node * target_base)68547284862SLizhi Hou static struct device_node *find_target(struct device_node *info_node,
68647284862SLizhi Hou struct device_node *target_base)
6877518b589SPantelis Antoniou {
688e547c003SFrank Rowand struct device_node *node;
68947284862SLizhi Hou char *target_path;
6907518b589SPantelis Antoniou const char *path;
6917518b589SPantelis Antoniou u32 val;
6927518b589SPantelis Antoniou int ret;
6937518b589SPantelis Antoniou
6947518b589SPantelis Antoniou ret = of_property_read_u32(info_node, "target", &val);
695e547c003SFrank Rowand if (!ret) {
696e547c003SFrank Rowand node = of_find_node_by_phandle(val);
697e547c003SFrank Rowand if (!node)
698e547c003SFrank Rowand pr_err("find target, node: %pOF, phandle 0x%x not found\n",
699e547c003SFrank Rowand info_node, val);
700e547c003SFrank Rowand return node;
701e547c003SFrank Rowand }
7027518b589SPantelis Antoniou
7037518b589SPantelis Antoniou ret = of_property_read_string(info_node, "target-path", &path);
704e547c003SFrank Rowand if (!ret) {
70547284862SLizhi Hou if (target_base) {
70647284862SLizhi Hou target_path = kasprintf(GFP_KERNEL, "%pOF%s", target_base, path);
70747284862SLizhi Hou if (!target_path)
70847284862SLizhi Hou return NULL;
70947284862SLizhi Hou node = of_find_node_by_path(target_path);
71047284862SLizhi Hou if (!node) {
71147284862SLizhi Hou pr_err("find target, node: %pOF, path '%s' not found\n",
71247284862SLizhi Hou info_node, target_path);
71347284862SLizhi Hou }
71447284862SLizhi Hou kfree(target_path);
71547284862SLizhi Hou } else {
716e547c003SFrank Rowand node = of_find_node_by_path(path);
71747284862SLizhi Hou if (!node) {
718e547c003SFrank Rowand pr_err("find target, node: %pOF, path '%s' not found\n",
719e547c003SFrank Rowand info_node, path);
72047284862SLizhi Hou }
72147284862SLizhi Hou }
722e547c003SFrank Rowand return node;
723e547c003SFrank Rowand }
7247518b589SPantelis Antoniou
725e547c003SFrank Rowand pr_err("find target, node: %pOF, no target property\n", info_node);
7267518b589SPantelis Antoniou
7277518b589SPantelis Antoniou return NULL;
7287518b589SPantelis Antoniou }
7297518b589SPantelis Antoniou
7307518b589SPantelis Antoniou /**
7310290c4caSFrank Rowand * init_overlay_changeset() - initialize overlay changeset from overlay tree
73239a751a4SFrank Rowand * @ovcs: Overlay changeset to build
73347284862SLizhi Hou * @target_base: Point to the target node to apply overlay
7347518b589SPantelis Antoniou *
7350290c4caSFrank Rowand * Initialize @ovcs. Populate @ovcs->fragments with node information from
7361e408966SFrank Rowand * the top level of @overlay_root. The relevant top level nodes are the
7371e408966SFrank Rowand * fragment nodes and the __symbols__ node. Any other top level node will
738067c0987SFrank Rowand * be ignored. Populate other @ovcs fields.
7397518b589SPantelis Antoniou *
7408c8239c2SRob Herring * Return: 0 on success, -ENOMEM if memory allocation failure, -EINVAL if error
741067c0987SFrank Rowand * detected in @overlay_root. On error return, the caller of
742067c0987SFrank Rowand * init_overlay_changeset() must call free_overlay_changeset().
7437518b589SPantelis Antoniou */
init_overlay_changeset(struct overlay_changeset * ovcs,struct device_node * target_base)74447284862SLizhi Hou static int init_overlay_changeset(struct overlay_changeset *ovcs,
74547284862SLizhi Hou struct device_node *target_base)
7467518b589SPantelis Antoniou {
74761b4de4eSFrank Rowand struct device_node *node, *overlay_node;
7480290c4caSFrank Rowand struct fragment *fragment;
7490290c4caSFrank Rowand struct fragment *fragments;
750067c0987SFrank Rowand int cnt, ret;
751067c0987SFrank Rowand
752067c0987SFrank Rowand /*
753067c0987SFrank Rowand * None of the resources allocated by this function will be freed in
754067c0987SFrank Rowand * the error paths. Instead the caller of this function is required
755067c0987SFrank Rowand * to call free_overlay_changeset() (which will free the resources)
756067c0987SFrank Rowand * if error return.
757067c0987SFrank Rowand */
7587518b589SPantelis Antoniou
75924789c5cSFrank Rowand /*
76024789c5cSFrank Rowand * Warn for some issues. Can not return -EINVAL for these until
76124789c5cSFrank Rowand * of_unittest_apply_overlay() is fixed to pass these checks.
76224789c5cSFrank Rowand */
763067c0987SFrank Rowand if (!of_node_check_flag(ovcs->overlay_root, OF_DYNAMIC))
764067c0987SFrank Rowand pr_debug("%s() ovcs->overlay_root is not dynamic\n", __func__);
76524789c5cSFrank Rowand
766067c0987SFrank Rowand if (!of_node_check_flag(ovcs->overlay_root, OF_DETACHED))
767067c0987SFrank Rowand pr_debug("%s() ovcs->overlay_root is not detached\n", __func__);
76824789c5cSFrank Rowand
769067c0987SFrank Rowand if (!of_node_is_root(ovcs->overlay_root))
770067c0987SFrank Rowand pr_debug("%s() ovcs->overlay_root is not root\n", __func__);
7717518b589SPantelis Antoniou
77261b4de4eSFrank Rowand cnt = 0;
77361b4de4eSFrank Rowand
77461b4de4eSFrank Rowand /* fragment nodes */
775067c0987SFrank Rowand for_each_child_of_node(ovcs->overlay_root, node) {
77661b4de4eSFrank Rowand overlay_node = of_get_child_by_name(node, "__overlay__");
77761b4de4eSFrank Rowand if (overlay_node) {
778d1651b03SFrank Rowand cnt++;
77961b4de4eSFrank Rowand of_node_put(overlay_node);
78061b4de4eSFrank Rowand }
78161b4de4eSFrank Rowand }
78261b4de4eSFrank Rowand
783067c0987SFrank Rowand node = of_get_child_by_name(ovcs->overlay_root, "__symbols__");
78461b4de4eSFrank Rowand if (node) {
78561b4de4eSFrank Rowand cnt++;
78661b4de4eSFrank Rowand of_node_put(node);
78761b4de4eSFrank Rowand }
788d1651b03SFrank Rowand
7890290c4caSFrank Rowand fragments = kcalloc(cnt, sizeof(*fragments), GFP_KERNEL);
79061b4de4eSFrank Rowand if (!fragments) {
79161b4de4eSFrank Rowand ret = -ENOMEM;
792067c0987SFrank Rowand goto err_out;
79361b4de4eSFrank Rowand }
794067c0987SFrank Rowand ovcs->fragments = fragments;
7957518b589SPantelis Antoniou
7967518b589SPantelis Antoniou cnt = 0;
797067c0987SFrank Rowand for_each_child_of_node(ovcs->overlay_root, node) {
79835e691edSGeert Uytterhoeven overlay_node = of_get_child_by_name(node, "__overlay__");
799589b754dSGeert Uytterhoeven if (!overlay_node)
800589b754dSGeert Uytterhoeven continue;
801589b754dSGeert Uytterhoeven
80261b4de4eSFrank Rowand fragment = &fragments[cnt];
80335e691edSGeert Uytterhoeven fragment->overlay = overlay_node;
80447284862SLizhi Hou fragment->target = find_target(node, target_base);
80561b4de4eSFrank Rowand if (!fragment->target) {
80661b4de4eSFrank Rowand of_node_put(fragment->overlay);
80761b4de4eSFrank Rowand ret = -EINVAL;
808c4d74f0fSkernel test robot of_node_put(node);
809067c0987SFrank Rowand goto err_out;
8107518b589SPantelis Antoniou }
8116de67de3SGeert Uytterhoeven
8126de67de3SGeert Uytterhoeven cnt++;
81361b4de4eSFrank Rowand }
8147518b589SPantelis Antoniou
8153912b791SFrank Rowand /*
8163912b791SFrank Rowand * if there is a symbols fragment in ovcs->fragments[i] it is
8173912b791SFrank Rowand * the final element in the array
8183912b791SFrank Rowand */
819067c0987SFrank Rowand node = of_get_child_by_name(ovcs->overlay_root, "__symbols__");
820d1651b03SFrank Rowand if (node) {
8213912b791SFrank Rowand ovcs->symbols_fragment = 1;
8220290c4caSFrank Rowand fragment = &fragments[cnt];
8230290c4caSFrank Rowand fragment->overlay = node;
8240290c4caSFrank Rowand fragment->target = of_find_node_by_path("/__symbols__");
825d1651b03SFrank Rowand
8260290c4caSFrank Rowand if (!fragment->target) {
8274ee7c0d9SFrank Rowand pr_err("symbols in overlay, but not in live tree\n");
82861b4de4eSFrank Rowand ret = -EINVAL;
82939affd1fSKunihiko Hayashi of_node_put(node);
830067c0987SFrank Rowand goto err_out;
831d1651b03SFrank Rowand }
832d1651b03SFrank Rowand
833d1651b03SFrank Rowand cnt++;
834d1651b03SFrank Rowand }
835d1651b03SFrank Rowand
836bbed8794SFrank Rowand if (!cnt) {
83739a751a4SFrank Rowand pr_err("no fragments or symbols in overlay\n");
83861b4de4eSFrank Rowand ret = -EINVAL;
839067c0987SFrank Rowand goto err_out;
8407518b589SPantelis Antoniou }
8417518b589SPantelis Antoniou
8420290c4caSFrank Rowand ovcs->count = cnt;
8437518b589SPantelis Antoniou
8447518b589SPantelis Antoniou return 0;
84561b4de4eSFrank Rowand
846067c0987SFrank Rowand err_out:
84724789c5cSFrank Rowand pr_err("%s() failed, ret = %d\n", __func__, ret);
84824789c5cSFrank Rowand
84961b4de4eSFrank Rowand return ret;
8507518b589SPantelis Antoniou }
8517518b589SPantelis Antoniou
free_overlay_changeset(struct overlay_changeset * ovcs)85261b4de4eSFrank Rowand static void free_overlay_changeset(struct overlay_changeset *ovcs)
8537518b589SPantelis Antoniou {
8547518b589SPantelis Antoniou int i;
8557518b589SPantelis Antoniou
8561352f09bSGeert Uytterhoeven if (ovcs->cset.entries.next)
85761b4de4eSFrank Rowand of_changeset_destroy(&ovcs->cset);
85861b4de4eSFrank Rowand
859067c0987SFrank Rowand if (ovcs->id) {
86061b4de4eSFrank Rowand idr_remove(&ovcs_idr, ovcs->id);
861067c0987SFrank Rowand list_del(&ovcs->ovcs_list);
862067c0987SFrank Rowand ovcs->id = 0;
863067c0987SFrank Rowand }
864067c0987SFrank Rowand
86561b4de4eSFrank Rowand
86661b4de4eSFrank Rowand for (i = 0; i < ovcs->count; i++) {
8670290c4caSFrank Rowand of_node_put(ovcs->fragments[i].target);
8680290c4caSFrank Rowand of_node_put(ovcs->fragments[i].overlay);
8697518b589SPantelis Antoniou }
8700290c4caSFrank Rowand kfree(ovcs->fragments);
871067c0987SFrank Rowand
87239a751a4SFrank Rowand /*
873067c0987SFrank Rowand * There should be no live pointers into ovcs->overlay_mem and
8741e408966SFrank Rowand * ovcs->new_fdt due to the policy that overlay notifiers are not
875067c0987SFrank Rowand * allowed to retain pointers into the overlay devicetree other
876067c0987SFrank Rowand * than during the window from OF_OVERLAY_PRE_APPLY overlay
877067c0987SFrank Rowand * notifiers until the OF_OVERLAY_POST_REMOVE overlay notifiers.
878067c0987SFrank Rowand *
879067c0987SFrank Rowand * A memory leak will occur here if within the window.
88039a751a4SFrank Rowand */
881067c0987SFrank Rowand
882067c0987SFrank Rowand if (ovcs->notify_state == OF_OVERLAY_INIT ||
883067c0987SFrank Rowand ovcs->notify_state == OF_OVERLAY_POST_REMOVE) {
884067c0987SFrank Rowand kfree(ovcs->overlay_mem);
8851e408966SFrank Rowand kfree(ovcs->new_fdt);
886067c0987SFrank Rowand }
88761b4de4eSFrank Rowand kfree(ovcs);
88861b4de4eSFrank Rowand }
8897518b589SPantelis Antoniou
89039a751a4SFrank Rowand /*
89139a751a4SFrank Rowand * internal documentation
89239a751a4SFrank Rowand *
8930290c4caSFrank Rowand * of_overlay_apply() - Create and apply an overlay changeset
894067c0987SFrank Rowand * @ovcs: overlay changeset
89547284862SLizhi Hou * @base: point to the target node to apply overlay
8967518b589SPantelis Antoniou *
89724789c5cSFrank Rowand * Creates and applies an overlay changeset.
8987518b589SPantelis Antoniou *
89924789c5cSFrank Rowand * If an error is returned by an overlay changeset pre-apply notifier
90024789c5cSFrank Rowand * then no further overlay changeset pre-apply notifier will be called.
90124789c5cSFrank Rowand *
90224789c5cSFrank Rowand * If an error is returned by an overlay changeset post-apply notifier
90324789c5cSFrank Rowand * then no further overlay changeset post-apply notifier will be called.
90424789c5cSFrank Rowand *
90524789c5cSFrank Rowand * If more than one notifier returns an error, then the last notifier
90624789c5cSFrank Rowand * error to occur is returned.
90724789c5cSFrank Rowand *
90824789c5cSFrank Rowand * If an error occurred while applying the overlay changeset, then an
90924789c5cSFrank Rowand * attempt is made to revert any changes that were made to the
91024789c5cSFrank Rowand * device tree. If there were any errors during the revert attempt
91124789c5cSFrank Rowand * then the state of the device tree can not be determined, and any
91224789c5cSFrank Rowand * following attempt to apply or remove an overlay changeset will be
91324789c5cSFrank Rowand * refused.
91424789c5cSFrank Rowand *
915067c0987SFrank Rowand * Returns 0 on success, or a negative error number. On error return,
916067c0987SFrank Rowand * the caller of of_overlay_apply() must call free_overlay_changeset().
9177518b589SPantelis Antoniou */
91824789c5cSFrank Rowand
of_overlay_apply(struct overlay_changeset * ovcs,struct device_node * base)91947284862SLizhi Hou static int of_overlay_apply(struct overlay_changeset *ovcs,
92047284862SLizhi Hou struct device_node *base)
9217518b589SPantelis Antoniou {
92224789c5cSFrank Rowand int ret = 0, ret_revert, ret_tmp;
92324789c5cSFrank Rowand
924067c0987SFrank Rowand ret = of_resolve_phandles(ovcs->overlay_root);
925067c0987SFrank Rowand if (ret)
92624789c5cSFrank Rowand goto out;
9277518b589SPantelis Antoniou
92847284862SLizhi Hou ret = init_overlay_changeset(ovcs, base);
929f948d6d8SFrank Rowand if (ret)
930067c0987SFrank Rowand goto out;
9317518b589SPantelis Antoniou
9320290c4caSFrank Rowand ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
9331ac17586SFrank Rowand if (ret)
934067c0987SFrank Rowand goto out;
93539a842e2SAlan Tull
9360290c4caSFrank Rowand ret = build_changeset(ovcs);
9370290c4caSFrank Rowand if (ret)
938067c0987SFrank Rowand goto out;
9397518b589SPantelis Antoniou
94024789c5cSFrank Rowand ret_revert = 0;
94124789c5cSFrank Rowand ret = __of_changeset_apply_entries(&ovcs->cset, &ret_revert);
94224789c5cSFrank Rowand if (ret) {
94324789c5cSFrank Rowand if (ret_revert) {
94424789c5cSFrank Rowand pr_debug("overlay changeset revert error %d\n",
94524789c5cSFrank Rowand ret_revert);
94624789c5cSFrank Rowand devicetree_state_flags |= DTSF_APPLY_FAIL;
94724789c5cSFrank Rowand }
948067c0987SFrank Rowand goto out;
9496de67de3SGeert Uytterhoeven }
9506de67de3SGeert Uytterhoeven
95124789c5cSFrank Rowand ret = __of_changeset_apply_notify(&ovcs->cset);
95224789c5cSFrank Rowand if (ret)
953a15e824fSFrank Rowand pr_err("overlay apply changeset entry notify error %d\n", ret);
9546de67de3SGeert Uytterhoeven /* notify failure is not fatal, continue */
955606ad42aSRob Herring
95624789c5cSFrank Rowand ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY);
9571ac17586SFrank Rowand if (ret_tmp)
95824789c5cSFrank Rowand if (!ret)
95924789c5cSFrank Rowand ret = ret_tmp;
96039a842e2SAlan Tull
96124789c5cSFrank Rowand out:
96224789c5cSFrank Rowand pr_debug("%s() err=%d\n", __func__, ret);
96324789c5cSFrank Rowand
9640290c4caSFrank Rowand return ret;
9657518b589SPantelis Antoniou }
96639a751a4SFrank Rowand
967421f4d14SFrank Rowand /*
968421f4d14SFrank Rowand * of_overlay_fdt_apply() - Create and apply an overlay changeset
969421f4d14SFrank Rowand * @overlay_fdt: pointer to overlay FDT
970421f4d14SFrank Rowand * @overlay_fdt_size: number of bytes in @overlay_fdt
971421f4d14SFrank Rowand * @ret_ovcs_id: pointer for returning created changeset id
97247284862SLizhi Hou * @base: pointer for the target node to apply overlay
973421f4d14SFrank Rowand *
974421f4d14SFrank Rowand * Creates and applies an overlay changeset.
975421f4d14SFrank Rowand *
976421f4d14SFrank Rowand * See of_overlay_apply() for important behavior information.
977421f4d14SFrank Rowand *
978421f4d14SFrank Rowand * Return: 0 on success, or a negative error number. *@ret_ovcs_id is set to
979421f4d14SFrank Rowand * the value of overlay changeset id, which can be passed to of_overlay_remove()
980421f4d14SFrank Rowand * to remove the overlay.
981421f4d14SFrank Rowand *
982421f4d14SFrank Rowand * On error return, the changeset may be partially applied. This is especially
983421f4d14SFrank Rowand * likely if an OF_OVERLAY_POST_APPLY notifier returns an error. In this case
984421f4d14SFrank Rowand * the caller should call of_overlay_remove() with the value in *@ret_ovcs_id.
985421f4d14SFrank Rowand */
986421f4d14SFrank Rowand
of_overlay_fdt_apply(const void * overlay_fdt,u32 overlay_fdt_size,int * ret_ovcs_id,struct device_node * base)98739a751a4SFrank Rowand int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
98847284862SLizhi Hou int *ret_ovcs_id, struct device_node *base)
98939a751a4SFrank Rowand {
99048d499bdSFrank Rowand void *new_fdt;
99148d499bdSFrank Rowand void *new_fdt_align;
992067c0987SFrank Rowand void *overlay_mem;
99339a751a4SFrank Rowand int ret;
99439a751a4SFrank Rowand u32 size;
995067c0987SFrank Rowand struct overlay_changeset *ovcs;
99639a751a4SFrank Rowand
997067c0987SFrank Rowand *ret_ovcs_id = 0;
99839a751a4SFrank Rowand
999e385b0baSGeert Uytterhoeven if (devicetree_corrupt()) {
1000e385b0baSGeert Uytterhoeven pr_err("devicetree state suspect, refuse to apply overlay\n");
1001e385b0baSGeert Uytterhoeven return -EBUSY;
1002e385b0baSGeert Uytterhoeven }
1003e385b0baSGeert Uytterhoeven
100439a751a4SFrank Rowand if (overlay_fdt_size < sizeof(struct fdt_header) ||
100539a751a4SFrank Rowand fdt_check_header(overlay_fdt)) {
100639a751a4SFrank Rowand pr_err("Invalid overlay_fdt header\n");
100739a751a4SFrank Rowand return -EINVAL;
100839a751a4SFrank Rowand }
100939a751a4SFrank Rowand
101039a751a4SFrank Rowand size = fdt_totalsize(overlay_fdt);
101139a751a4SFrank Rowand if (overlay_fdt_size < size)
101239a751a4SFrank Rowand return -EINVAL;
101339a751a4SFrank Rowand
1014067c0987SFrank Rowand ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
1015067c0987SFrank Rowand if (!ovcs)
1016067c0987SFrank Rowand return -ENOMEM;
1017067c0987SFrank Rowand
1018067c0987SFrank Rowand of_overlay_mutex_lock();
1019067c0987SFrank Rowand mutex_lock(&of_mutex);
1020067c0987SFrank Rowand
1021067c0987SFrank Rowand /*
1022067c0987SFrank Rowand * ovcs->notify_state must be set to OF_OVERLAY_INIT before allocating
1023067c0987SFrank Rowand * ovcs resources, implicitly set by kzalloc() of ovcs
1024067c0987SFrank Rowand */
1025067c0987SFrank Rowand
1026067c0987SFrank Rowand ovcs->id = idr_alloc(&ovcs_idr, ovcs, 1, 0, GFP_KERNEL);
1027067c0987SFrank Rowand if (ovcs->id <= 0) {
1028067c0987SFrank Rowand ret = ovcs->id;
1029067c0987SFrank Rowand goto err_free_ovcs;
1030067c0987SFrank Rowand }
1031067c0987SFrank Rowand
1032067c0987SFrank Rowand INIT_LIST_HEAD(&ovcs->ovcs_list);
1033067c0987SFrank Rowand list_add_tail(&ovcs->ovcs_list, &ovcs_list);
1034a9515ff4SGeert Uytterhoeven of_changeset_init(&ovcs->cset);
1035067c0987SFrank Rowand
103639a751a4SFrank Rowand /*
103739a751a4SFrank Rowand * Must create permanent copy of FDT because of_fdt_unflatten_tree()
103839a751a4SFrank Rowand * will create pointers to the passed in FDT in the unflattened tree.
103939a751a4SFrank Rowand */
104048d499bdSFrank Rowand new_fdt = kmalloc(size + FDT_ALIGN_SIZE, GFP_KERNEL);
1041067c0987SFrank Rowand if (!new_fdt) {
1042067c0987SFrank Rowand ret = -ENOMEM;
1043067c0987SFrank Rowand goto err_free_ovcs;
1044067c0987SFrank Rowand }
1045067c0987SFrank Rowand ovcs->new_fdt = new_fdt;
104639a751a4SFrank Rowand
104748d499bdSFrank Rowand new_fdt_align = PTR_ALIGN(new_fdt, FDT_ALIGN_SIZE);
104848d499bdSFrank Rowand memcpy(new_fdt_align, overlay_fdt, size);
104948d499bdSFrank Rowand
1050067c0987SFrank Rowand overlay_mem = of_fdt_unflatten_tree(new_fdt_align, NULL,
1051067c0987SFrank Rowand &ovcs->overlay_root);
1052067c0987SFrank Rowand if (!overlay_mem) {
105339a751a4SFrank Rowand pr_err("unable to unflatten overlay_fdt\n");
105439a751a4SFrank Rowand ret = -EINVAL;
1055067c0987SFrank Rowand goto err_free_ovcs;
105639a751a4SFrank Rowand }
1057067c0987SFrank Rowand ovcs->overlay_mem = overlay_mem;
105839a751a4SFrank Rowand
105947284862SLizhi Hou ret = of_overlay_apply(ovcs, base);
1060421f4d14SFrank Rowand /*
1061421f4d14SFrank Rowand * If of_overlay_apply() error, calling free_overlay_changeset() may
1062421f4d14SFrank Rowand * result in a memory leak if the apply partly succeeded, so do NOT
1063421f4d14SFrank Rowand * goto err_free_ovcs. Instead, the caller of of_overlay_fdt_apply()
1064421f4d14SFrank Rowand * can call of_overlay_remove();
1065421f4d14SFrank Rowand */
1066067c0987SFrank Rowand *ret_ovcs_id = ovcs->id;
1067e76f4a61SGeert Uytterhoeven goto out_unlock;
106839a751a4SFrank Rowand
1069067c0987SFrank Rowand err_free_ovcs:
1070067c0987SFrank Rowand free_overlay_changeset(ovcs);
107139a751a4SFrank Rowand
1072e76f4a61SGeert Uytterhoeven out_unlock:
1073067c0987SFrank Rowand mutex_unlock(&of_mutex);
1074067c0987SFrank Rowand of_overlay_mutex_unlock();
107539a751a4SFrank Rowand return ret;
107639a751a4SFrank Rowand }
107739a751a4SFrank Rowand EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
10787518b589SPantelis Antoniou
1079646afc4aSFrank Rowand /*
10800290c4caSFrank Rowand * Find @np in @tree.
10810290c4caSFrank Rowand *
10820290c4caSFrank Rowand * Returns 1 if @np is @tree or is contained in @tree, else 0
1083646afc4aSFrank Rowand */
find_node(struct device_node * tree,struct device_node * np)10840290c4caSFrank Rowand static int find_node(struct device_node *tree, struct device_node *np)
10857518b589SPantelis Antoniou {
10867518b589SPantelis Antoniou struct device_node *child;
10877518b589SPantelis Antoniou
10880290c4caSFrank Rowand if (tree == np)
10897518b589SPantelis Antoniou return 1;
10907518b589SPantelis Antoniou
10917518b589SPantelis Antoniou for_each_child_of_node(tree, child) {
10920290c4caSFrank Rowand if (find_node(child, np)) {
1093001cf504SJulia Lawall of_node_put(child);
10947518b589SPantelis Antoniou return 1;
10957518b589SPantelis Antoniou }
1096001cf504SJulia Lawall }
10977518b589SPantelis Antoniou
10987518b589SPantelis Antoniou return 0;
10997518b589SPantelis Antoniou }
11007518b589SPantelis Antoniou
1101646afc4aSFrank Rowand /*
110287f242c1SFrank Rowand * Is @remove_ce_node a child of, a parent of, or the same as any
11030290c4caSFrank Rowand * node in an overlay changeset more topmost than @remove_ovcs?
11040290c4caSFrank Rowand *
11050290c4caSFrank Rowand * Returns 1 if found, else 0
1106646afc4aSFrank Rowand */
node_overlaps_later_cs(struct overlay_changeset * remove_ovcs,struct device_node * remove_ce_node)110787f242c1SFrank Rowand static int node_overlaps_later_cs(struct overlay_changeset *remove_ovcs,
110887f242c1SFrank Rowand struct device_node *remove_ce_node)
11097518b589SPantelis Antoniou {
11100290c4caSFrank Rowand struct overlay_changeset *ovcs;
11117518b589SPantelis Antoniou struct of_changeset_entry *ce;
11127518b589SPantelis Antoniou
11130290c4caSFrank Rowand list_for_each_entry_reverse(ovcs, &ovcs_list, ovcs_list) {
11140290c4caSFrank Rowand if (ovcs == remove_ovcs)
11157518b589SPantelis Antoniou break;
11167518b589SPantelis Antoniou
11170290c4caSFrank Rowand list_for_each_entry(ce, &ovcs->cset.entries, node) {
111887f242c1SFrank Rowand if (find_node(ce->np, remove_ce_node)) {
111987f242c1SFrank Rowand pr_err("%s: #%d overlaps with #%d @%pOF\n",
11200290c4caSFrank Rowand __func__, remove_ovcs->id, ovcs->id,
112187f242c1SFrank Rowand remove_ce_node);
112287f242c1SFrank Rowand return 1;
112387f242c1SFrank Rowand }
112487f242c1SFrank Rowand if (find_node(remove_ce_node, ce->np)) {
112587f242c1SFrank Rowand pr_err("%s: #%d overlaps with #%d @%pOF\n",
112687f242c1SFrank Rowand __func__, remove_ovcs->id, ovcs->id,
112787f242c1SFrank Rowand remove_ce_node);
11280290c4caSFrank Rowand return 1;
11297518b589SPantelis Antoniou }
11307518b589SPantelis Antoniou }
11317518b589SPantelis Antoniou }
11327518b589SPantelis Antoniou
11330290c4caSFrank Rowand return 0;
11347518b589SPantelis Antoniou }
11357518b589SPantelis Antoniou
11367518b589SPantelis Antoniou /*
11377518b589SPantelis Antoniou * We can safely remove the overlay only if it's the top-most one.
11387518b589SPantelis Antoniou * Newly applied overlays are inserted at the tail of the overlay list,
11397518b589SPantelis Antoniou * so a top most overlay is the one that is closest to the tail.
11407518b589SPantelis Antoniou *
11417518b589SPantelis Antoniou * The topmost check is done by exploiting this property. For each
11427518b589SPantelis Antoniou * affected device node in the log list we check if this overlay is
11437518b589SPantelis Antoniou * the one closest to the tail. If another overlay has affected this
11448e5d0c68SRicardo Ribalda * device node and is closest to the tail, then removal is not permitted.
11457518b589SPantelis Antoniou */
overlay_removal_is_ok(struct overlay_changeset * remove_ovcs)11460290c4caSFrank Rowand static int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs)
11477518b589SPantelis Antoniou {
11480290c4caSFrank Rowand struct of_changeset_entry *remove_ce;
11497518b589SPantelis Antoniou
11500290c4caSFrank Rowand list_for_each_entry(remove_ce, &remove_ovcs->cset.entries, node) {
115187f242c1SFrank Rowand if (node_overlaps_later_cs(remove_ovcs, remove_ce->np)) {
11520290c4caSFrank Rowand pr_err("overlay #%d is not topmost\n", remove_ovcs->id);
11537518b589SPantelis Antoniou return 0;
11547518b589SPantelis Antoniou }
11557518b589SPantelis Antoniou }
11567518b589SPantelis Antoniou
11577518b589SPantelis Antoniou return 1;
11587518b589SPantelis Antoniou }
11597518b589SPantelis Antoniou
11607518b589SPantelis Antoniou /**
11610290c4caSFrank Rowand * of_overlay_remove() - Revert and free an overlay changeset
116224789c5cSFrank Rowand * @ovcs_id: Pointer to overlay changeset id
11637518b589SPantelis Antoniou *
116424789c5cSFrank Rowand * Removes an overlay if it is permissible. @ovcs_id was previously returned
1165a514266bSGeert Uytterhoeven * by of_overlay_fdt_apply().
11667518b589SPantelis Antoniou *
116724789c5cSFrank Rowand * If an error occurred while attempting to revert the overlay changeset,
116824789c5cSFrank Rowand * then an attempt is made to re-apply any changeset entry that was
116924789c5cSFrank Rowand * reverted. If an error occurs on re-apply then the state of the device
117024789c5cSFrank Rowand * tree can not be determined, and any following attempt to apply or remove
117124789c5cSFrank Rowand * an overlay changeset will be refused.
117224789c5cSFrank Rowand *
117324789c5cSFrank Rowand * A non-zero return value will not revert the changeset if error is from:
117424789c5cSFrank Rowand * - parameter checks
1175e9d92e40SGeert Uytterhoeven * - overlay changeset pre-remove notifier
117624789c5cSFrank Rowand * - overlay changeset entry revert
117724789c5cSFrank Rowand *
117824789c5cSFrank Rowand * If an error is returned by an overlay changeset pre-remove notifier
117924789c5cSFrank Rowand * then no further overlay changeset pre-remove notifier will be called.
118024789c5cSFrank Rowand *
118124789c5cSFrank Rowand * If more than one notifier returns an error, then the last notifier
118224789c5cSFrank Rowand * error to occur is returned.
118324789c5cSFrank Rowand *
118424789c5cSFrank Rowand * A non-zero return value will revert the changeset if error is from:
118524789c5cSFrank Rowand * - overlay changeset entry notifier
1186e9d92e40SGeert Uytterhoeven * - overlay changeset post-remove notifier
118724789c5cSFrank Rowand *
118824789c5cSFrank Rowand * If an error is returned by an overlay changeset post-remove notifier
118924789c5cSFrank Rowand * then no further overlay changeset post-remove notifier will be called.
119024789c5cSFrank Rowand *
1191aed4349cSRob Herring * Return: 0 on success, or a negative error number. *@ovcs_id is set to
119224789c5cSFrank Rowand * zero after reverting the changeset, even if a subsequent error occurs.
11937518b589SPantelis Antoniou */
of_overlay_remove(int * ovcs_id)119424789c5cSFrank Rowand int of_overlay_remove(int *ovcs_id)
11957518b589SPantelis Antoniou {
11960290c4caSFrank Rowand struct overlay_changeset *ovcs;
119724789c5cSFrank Rowand int ret, ret_apply, ret_tmp;
11987518b589SPantelis Antoniou
119924789c5cSFrank Rowand if (devicetree_corrupt()) {
120024789c5cSFrank Rowand pr_err("suspect devicetree state, refuse to remove overlay\n");
12010290c4caSFrank Rowand ret = -EBUSY;
12027518b589SPantelis Antoniou goto out;
12037518b589SPantelis Antoniou }
12047518b589SPantelis Antoniou
120524789c5cSFrank Rowand mutex_lock(&of_mutex);
120624789c5cSFrank Rowand
120724789c5cSFrank Rowand ovcs = idr_find(&ovcs_idr, *ovcs_id);
120824789c5cSFrank Rowand if (!ovcs) {
120924789c5cSFrank Rowand ret = -ENODEV;
121024789c5cSFrank Rowand pr_err("remove: Could not find overlay #%d\n", *ovcs_id);
1211067c0987SFrank Rowand goto err_unlock;
121224789c5cSFrank Rowand }
121324789c5cSFrank Rowand
121424789c5cSFrank Rowand if (!overlay_removal_is_ok(ovcs)) {
121524789c5cSFrank Rowand ret = -EBUSY;
1216067c0987SFrank Rowand goto err_unlock;
121724789c5cSFrank Rowand }
121824789c5cSFrank Rowand
121924789c5cSFrank Rowand ret = overlay_notify(ovcs, OF_OVERLAY_PRE_REMOVE);
12201ac17586SFrank Rowand if (ret)
1221067c0987SFrank Rowand goto err_unlock;
122261b4de4eSFrank Rowand
122324789c5cSFrank Rowand ret_apply = 0;
122424789c5cSFrank Rowand ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply);
122524789c5cSFrank Rowand if (ret) {
122624789c5cSFrank Rowand if (ret_apply)
122724789c5cSFrank Rowand devicetree_state_flags |= DTSF_REVERT_FAIL;
1228067c0987SFrank Rowand goto err_unlock;
12296de67de3SGeert Uytterhoeven }
12306de67de3SGeert Uytterhoeven
123124789c5cSFrank Rowand ret = __of_changeset_revert_notify(&ovcs->cset);
12326de67de3SGeert Uytterhoeven if (ret)
1233a15e824fSFrank Rowand pr_err("overlay remove changeset entry notify error %d\n", ret);
12346de67de3SGeert Uytterhoeven /* notify failure is not fatal, continue */
123561b4de4eSFrank Rowand
123624789c5cSFrank Rowand *ovcs_id = 0;
123724789c5cSFrank Rowand
1238067c0987SFrank Rowand /*
1239067c0987SFrank Rowand * Note that the overlay memory will be kfree()ed by
1240067c0987SFrank Rowand * free_overlay_changeset() even if the notifier for
1241067c0987SFrank Rowand * OF_OVERLAY_POST_REMOVE returns an error.
1242067c0987SFrank Rowand */
124324789c5cSFrank Rowand ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_REMOVE);
12441ac17586SFrank Rowand if (ret_tmp)
124524789c5cSFrank Rowand if (!ret)
124624789c5cSFrank Rowand ret = ret_tmp;
124761b4de4eSFrank Rowand
124861b4de4eSFrank Rowand free_overlay_changeset(ovcs);
12497518b589SPantelis Antoniou
1250067c0987SFrank Rowand err_unlock:
1251067c0987SFrank Rowand /*
1252067c0987SFrank Rowand * If jumped over free_overlay_changeset(), then did not kfree()
1253067c0987SFrank Rowand * overlay related memory. This is a memory leak unless a subsequent
1254067c0987SFrank Rowand * of_overlay_remove() of this overlay is successful.
1255067c0987SFrank Rowand */
12567518b589SPantelis Antoniou mutex_unlock(&of_mutex);
12577518b589SPantelis Antoniou
125824789c5cSFrank Rowand out:
125924789c5cSFrank Rowand pr_debug("%s() err=%d\n", __func__, ret);
126024789c5cSFrank Rowand
12610290c4caSFrank Rowand return ret;
12627518b589SPantelis Antoniou }
12630290c4caSFrank Rowand EXPORT_SYMBOL_GPL(of_overlay_remove);
12647518b589SPantelis Antoniou
12657518b589SPantelis Antoniou /**
12660290c4caSFrank Rowand * of_overlay_remove_all() - Reverts and frees all overlay changesets
12677518b589SPantelis Antoniou *
12687518b589SPantelis Antoniou * Removes all overlays from the system in the correct order.
12697518b589SPantelis Antoniou *
12708c8239c2SRob Herring * Return: 0 on success, or a negative error number
12717518b589SPantelis Antoniou */
of_overlay_remove_all(void)12720290c4caSFrank Rowand int of_overlay_remove_all(void)
12737518b589SPantelis Antoniou {
12740290c4caSFrank Rowand struct overlay_changeset *ovcs, *ovcs_n;
127561b4de4eSFrank Rowand int ret;
12767518b589SPantelis Antoniou
12777518b589SPantelis Antoniou /* the tail of list is guaranteed to be safe to remove */
12780290c4caSFrank Rowand list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) {
127924789c5cSFrank Rowand ret = of_overlay_remove(&ovcs->id);
128061b4de4eSFrank Rowand if (ret)
128161b4de4eSFrank Rowand return ret;
12827518b589SPantelis Antoniou }
12837518b589SPantelis Antoniou
12847518b589SPantelis Antoniou return 0;
12857518b589SPantelis Antoniou }
12860290c4caSFrank Rowand EXPORT_SYMBOL_GPL(of_overlay_remove_all);
1287