summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Srivatsa Vaddagiri <vatsa@codeaurora.org> 2018-05-25 15:45:44 +0530
committer Michael Bestas <mkbestas@lineageos.org> 2025-06-15 18:57:38 +0300
commitc387b1d2d601a15beb4c40e7c7cbb497d81edee5 (patch)
tree73072b1f376ae4d217fec36e6dd8058a4cbdf6a1
parent5a4323bd4931d3ca51ece0c421a1de786ccd5ae4 (diff)
libfdt: overlay_merge: Rename fragments
When merging two overlay blobs, fragment nodes whose target node can't be found in base blob would need to be retained as-is (including the fragment names) in the combined blob. Such unresolved symbols will also need to be listed in __fixups__ section of combined blob. This could lead to name comflicts in combined blob (two nodes with same name/path such as /fragment@0). To avoid such name conflicts in combined blob, rename all fragment@xyz in overlay blob as fragment@xyz+delta, where delta is the maximum count of fragment nodes found in base blob Change-Id: I987f60ceed1c5e05279b0c4ff998affd069d70fd Signed-off-by: Srivatsa Vaddagiri <vatsa@codeaurora.org>
-rw-r--r--libfdt/fdt_overlay.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c
index c8d5b8c..096e4d0 100644
--- a/libfdt/fdt_overlay.c
+++ b/libfdt/fdt_overlay.c
@@ -8,6 +8,7 @@
#include <fdt.h>
#include <libfdt.h>
+#include <stdio.h>
#include "libfdt_internal.h"
@@ -866,6 +867,300 @@ err:
return ret;
}
+/*
+ * Property value could be in this format
+ * fragment@M ...fragment@N....fragment@O..
+ *
+ * This needs to be converted to
+ * fragment@M+delta...fragment@N+delta....fragment@O+delta
+ */
+static int rename_fragments_in_property(void *fdto, int offset,
+ int property, int delta)
+{
+ char *start, *sep, *end, *stop, *value;
+ int needed = 0, ret, len, found = 0, available, diff;
+ unsigned long index, new_index;
+ void *p = NULL;
+ const char *label;
+
+ value = (char *)(uintptr_t)fdt_getprop_by_offset(fdto, property,
+ &label, &len);
+ if (!value)
+ return len;
+
+ start = value;
+ end = value + len;
+
+ /* Find the required additional space */
+ while (start < end) {
+ sep = memchr(start, '@', (end - start));
+ if (!sep) {
+ needed += end - start;
+ break;
+ }
+
+ /* Check if string "fragment" exists */
+ sep -= 8;
+
+ if (sep < start || strncmp(sep, "fragment", 8)) {
+ /* Start scan again after '@' */
+ sep = sep + 9;
+ needed += (sep - start);
+ start = sep;
+ continue;
+ }
+
+ found = 1;
+ sep += 9;
+ needed += (sep - start);
+ index = strtoul(sep, &stop, 10);
+ if (ULONG_MAX - index < delta)
+ return -FDT_ERR_BADVALUE;
+
+ new_index = index + delta;
+ needed += snprintf(NULL, 0, "%lu", new_index);
+ start = stop;
+ }
+
+ if (!found)
+ return 0;
+
+ p = value;
+ if (needed > len) {
+ ret = fdt_setprop_placeholder(fdto, offset, label, needed, &p);
+ if (ret < 0)
+ return ret;
+ len = needed;
+ }
+
+ start = p;
+ end = start + len;
+ ret = 0;
+
+ while (start < end) {
+ sep = memchr(start, '@', (end - start));
+ if (!sep)
+ break;
+
+ /* Check if string "fragment" exists */
+ sep -= 8;
+ if (sep < start || strncmp(sep, "fragment", 8)) {
+ /* Start scan again after '@' */
+ start = sep + 9;
+ continue;
+ }
+
+ sep += 9;
+ index = strtoul(sep, &stop, 10);
+ new_index = index + delta;
+
+ needed = snprintf(NULL, 0, "%lu", new_index);
+ available = stop - sep;
+
+ if (available < needed) {
+ diff = needed - available;
+ memmove(stop + diff, stop, (end - stop));
+ }
+
+ {
+ /* +1 for NULL char */
+ char buf[needed + 1];
+
+ snprintf(buf, needed + 1, "%lu", new_index);
+ memcpy(sep, buf, needed);
+ }
+
+ start = sep + needed;
+ }
+
+ return 0;
+}
+
+/**
+ * rename_fragments_in_node - Rename fragment@xyz instances in a node's
+ * properties
+ *
+ * @fdto - pointer to a device-tree blob
+ * @nodename - Node in whose properties fragments need to be renamed
+ * @delta - Increment to be applied to fragment index
+ */
+static int rename_fragments_in_node(void *fdto, const char *nodename,
+ unsigned long delta)
+{
+ int offset, property;
+ int ret;
+
+ offset = fdt_path_offset(fdto, nodename);
+ if (offset < 0)
+ return offset;
+
+ fdt_for_each_property_offset(property, fdto, offset) {
+ ret = rename_fragments_in_property(fdto, offset,
+ property, delta);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * rename_nodes - Rename all fragement@xyz nodes
+ *
+ * @fdto - pointer to device-tree blob
+ * @parent_node - node offset of parent whose child fragment nodes need to be
+ * renamed
+ * @delta - increment to be added to fragment number
+ */
+static int rename_nodes(void *fdto, int parent_node, unsigned long delta)
+{
+ int offset = -1, ret, len, strsize;
+ int child_len, child_offset;
+ const char *name, *child_name, *idx;
+ char *stop = NULL;
+ unsigned long index, new_index;
+
+ offset = fdt_first_subnode(fdto, parent_node);
+ while (offset >= 0) {
+ name = fdt_get_name(fdto, offset, &len);
+ if (!name)
+ return len;
+
+ if (len < 9 || strncmp(name, "fragment@", 9))
+ goto next_node;
+
+ child_offset = fdt_first_subnode(fdto, offset);
+ if (child_offset < 0)
+ return child_offset;
+
+ child_name = fdt_get_name(fdto, child_offset, &child_len);
+ if (!child_name)
+ return child_len;
+
+ /* Extra FDT_TAGSIZE bytes for expanded node name */
+ strsize = FDT_TAGALIGN(len+1+FDT_TAGSIZE);
+
+ if (child_len >= 11 &&
+ !strncmp(child_name, "__overlay__", 11))
+ {
+ char new_name[strsize];
+
+ idx = name + 9;
+ stop = NULL;
+ index = strtoul(idx, &stop, 10);
+ if (ULONG_MAX - delta < index)
+ return -FDT_ERR_BADVALUE;
+
+ new_index = index + delta;
+ ret = snprintf(new_name, sizeof(new_name),
+ "fragment@%lu", new_index);
+ if (ret >= sizeof(new_name))
+ return -FDT_ERR_BADVALUE;
+
+ ret = fdt_set_name(fdto, offset, new_name);
+ if (ret < 0)
+ return ret;
+ }
+
+next_node:
+ offset = fdt_next_subnode(fdto, offset);
+ }
+
+ return 0;
+}
+
+/* Return maximum count of overlay fragments */
+static int count_fragments(void *fdt, unsigned long *max_base_fragments)
+{
+ int offset = -1, child_offset, child_len, len, found = 0;
+ const char *name, *child_name, *idx;
+ char *stop;
+ unsigned long index, max = 0;
+
+ offset = fdt_first_subnode(fdt, 0);
+ while (offset >= 0) {
+ name = fdt_get_name(fdt, offset, &len);
+ if (!name)
+ return len;
+
+ if (len < 9 || strncmp(name, "fragment@", 9))
+ goto next_node;
+
+ child_offset = fdt_first_subnode(fdt, offset);
+ if (child_offset < 0)
+ return child_offset;
+
+ child_name = fdt_get_name(fdt, child_offset, &child_len);
+ if (!child_name)
+ return child_len;
+
+ if (child_len < 11 || strncmp(child_name, "__overlay__", 11))
+ goto next_node;
+
+ found = 1;
+ idx = name + 9;
+ index = strtoul(idx, &stop, 10);
+ if (index > max)
+ max = index;
+next_node:
+ offset = fdt_next_subnode(fdt, offset);
+ }
+
+
+ if (!found)
+ return -FDT_ERR_NOTFOUND;
+
+ *max_base_fragments = max;
+
+ return 0;
+}
+
+/*
+ * Merging two overlay blobs involves copying some of the overlay fragment nodes
+ * (named as fragment@xyz) from second overlay blob into first, which can lead
+ * to naming conflicts (ex: two nodes of same name /fragment@0). To prevent such
+ * naming conflicts, rename all occurences of fragment@xyz in second overlay
+ * blob as fragment@xyz+delta, where delta is the maximum overlay fragments seen
+ * in first overlay blob
+ */
+static int overlay_rename_fragments(void *fdt, void *fdto)
+{
+ int ret, local_offset;
+ unsigned long max_base_fragments = 0;
+
+ ret = count_fragments(fdt, &max_base_fragments);
+ if (ret < 0)
+ return ret;
+
+ max_base_fragments += 1;
+ ret = rename_nodes(fdto, 0, max_base_fragments);
+ if (ret < 0)
+ return ret;
+
+ ret = rename_fragments_in_node(fdto, "/__fixups__", max_base_fragments);
+ if (ret < 0)
+ return ret;
+
+ ret = rename_fragments_in_node(fdto, "/__symbols__",
+ max_base_fragments);
+ if (ret < 0 && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ /*
+ * renaming fragments in __local_fixups__ node's properties should be
+ * covered by rename_nodes()
+ */
+ local_offset = fdt_path_offset(fdto, "/__local_fixups__");
+ if (local_offset >= 0)
+ ret = rename_nodes(fdto, local_offset, max_base_fragments);
+
+
+ if (ret == -FDT_ERR_NOTFOUND)
+ ret = 0;
+
+ return ret;
+}
+
int fdt_overlay_merge(void *fdt, void *fdto, int *fdto_nospace)
{
uint32_t delta = fdt_get_max_phandle(fdt);
@@ -876,6 +1171,13 @@ int fdt_overlay_merge(void *fdt, void *fdto, int *fdto_nospace)
*fdto_nospace = 0;
+ ret = overlay_rename_fragments(fdt, fdto);
+ if (ret) {
+ if (ret == -FDT_ERR_NOSPACE)
+ *fdto_nospace = 1;
+ goto err;
+ }
+
ret = overlay_adjust_local_phandles(fdto, delta);
if (ret)
goto err;