aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/overlay.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/overlay.c')
-rw-r--r--drivers/of/overlay.c118
1 files changed, 81 insertions, 37 deletions
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 8af8115bd36e..184cc2c4a931 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -508,52 +508,96 @@ static int build_changeset_symbols_node(struct overlay_changeset *ovcs,
return 0;
}
+static int find_dup_cset_node_entry(struct overlay_changeset *ovcs,
+ struct of_changeset_entry *ce_1)
+{
+ struct of_changeset_entry *ce_2;
+ char *fn_1, *fn_2;
+ int node_path_match;
+
+ if (ce_1->action != OF_RECONFIG_ATTACH_NODE &&
+ ce_1->action != OF_RECONFIG_DETACH_NODE)
+ return 0;
+
+ ce_2 = ce_1;
+ list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
+ if ((ce_2->action != OF_RECONFIG_ATTACH_NODE &&
+ ce_2->action != OF_RECONFIG_DETACH_NODE) ||
+ of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
+ continue;
+
+ fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
+ fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
+ node_path_match = !strcmp(fn_1, fn_2);
+ kfree(fn_1);
+ kfree(fn_2);
+ if (node_path_match) {
+ pr_err("ERROR: multiple fragments add and/or delete node %pOF\n",
+ ce_1->np);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int find_dup_cset_prop(struct overlay_changeset *ovcs,
+ struct of_changeset_entry *ce_1)
+{
+ struct of_changeset_entry *ce_2;
+ char *fn_1, *fn_2;
+ int node_path_match;
+
+ if (ce_1->action != OF_RECONFIG_ADD_PROPERTY &&
+ ce_1->action != OF_RECONFIG_REMOVE_PROPERTY &&
+ ce_1->action != OF_RECONFIG_UPDATE_PROPERTY)
+ return 0;
+
+ ce_2 = ce_1;
+ list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
+ if ((ce_2->action != OF_RECONFIG_ADD_PROPERTY &&
+ ce_2->action != OF_RECONFIG_REMOVE_PROPERTY &&
+ ce_2->action != OF_RECONFIG_UPDATE_PROPERTY) ||
+ of_node_cmp(ce_1->np->full_name, ce_2->np->full_name))
+ continue;
+
+ fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
+ fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
+ node_path_match = !strcmp(fn_1, fn_2);
+ kfree(fn_1);
+ kfree(fn_2);
+ if (node_path_match &&
+ !of_prop_cmp(ce_1->prop->name, ce_2->prop->name)) {
+ pr_err("ERROR: multiple fragments add, update, and/or delete property %pOF/%s\n",
+ ce_1->np, ce_1->prop->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
/**
- * check_changeset_dup_add_node() - changeset validation: duplicate add node
+ * changeset_dup_entry_check() - check for duplicate entries
* @ovcs: Overlay changeset
*
- * Check changeset @ovcs->cset for multiple add node entries for the same
- * node.
+ * Check changeset @ovcs->cset for multiple {add or delete} node entries for
+ * the same node or duplicate {add, delete, or update} properties entries
+ * for the same property.
*
- * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if
- * invalid overlay in @ovcs->fragments[].
+ * Returns 0 on success, or -EINVAL if duplicate changeset entry found.
*/
-static int check_changeset_dup_add_node(struct overlay_changeset *ovcs)
+static int changeset_dup_entry_check(struct overlay_changeset *ovcs)
{
- struct of_changeset_entry *ce_1, *ce_2;
- char *fn_1, *fn_2;
- int name_match;
+ struct of_changeset_entry *ce_1;
+ int dup_entry = 0;
list_for_each_entry(ce_1, &ovcs->cset.entries, node) {
-
- if (ce_1->action == OF_RECONFIG_ATTACH_NODE ||
- ce_1->action == OF_RECONFIG_DETACH_NODE) {
-
- ce_2 = ce_1;
- list_for_each_entry_continue(ce_2, &ovcs->cset.entries, node) {
- if (ce_2->action == OF_RECONFIG_ATTACH_NODE ||
- ce_2->action == OF_RECONFIG_DETACH_NODE) {
- /* inexpensive name compare */
- if (!of_node_cmp(ce_1->np->full_name,
- ce_2->np->full_name)) {
- /* expensive full path name compare */
- fn_1 = kasprintf(GFP_KERNEL, "%pOF", ce_1->np);
- fn_2 = kasprintf(GFP_KERNEL, "%pOF", ce_2->np);
- name_match = !strcmp(fn_1, fn_2);
- kfree(fn_1);
- kfree(fn_2);
- if (name_match) {
- pr_err("ERROR: multiple overlay fragments add and/or delete node %pOF\n",
- ce_1->np);
- return -EINVAL;
- }
- }
- }
- }
- }
+ dup_entry |= find_dup_cset_node_entry(ovcs, ce_1);
+ dup_entry |= find_dup_cset_prop(ovcs, ce_1);
}
- return 0;
+ return dup_entry ? -EINVAL : 0;
}
/**
@@ -611,7 +655,7 @@ static int build_changeset(struct overlay_changeset *ovcs)
}
}
- return check_changeset_dup_add_node(ovcs);
+ return changeset_dup_entry_check(ovcs);
}
/*