[U-Boot] [PATCH v2 7/9] libfdt: Add overlay application function

Maxime Ripard maxime.ripard at free-electrons.com
Fri May 27 11:13:20 CEST 2016


The device tree overlays are a good way to deal with user-modifyable
boards or boards with some kind of an expansion mechanism where we can
easily plug new board in (like the BBB, the Raspberry Pi or the CHIP).

Add a new function to merge overlays with a base device tree.

Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>
---
 include/libfdt.h         |  30 ++++
 lib/libfdt/Makefile      |   2 +-
 lib/libfdt/fdt_overlay.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 445 insertions(+), 1 deletion(-)
 create mode 100644 lib/libfdt/fdt_overlay.c

diff --git a/include/libfdt.h b/include/libfdt.h
index 1e01b2c7ebdf..783067e841a1 100644
--- a/include/libfdt.h
+++ b/include/libfdt.h
@@ -1698,6 +1698,36 @@ int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
  */
 int fdt_del_node(void *fdt, int nodeoffset);
 
+/**
+ * fdt_overlay_apply - Applies a DT overlay on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdto: pointer to the device tree overlay blob
+ *
+ * fdt_overlay_apply() will apply the given device tree overlay on the
+ * given base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ *	0, on success
+ *	-FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ *	-FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ *		properties in the base DT
+ *	-FDT_ERR_BADPHANDLE, the phandles in the overlay do not have the right
+ *		magic
+ *	-FDT_ERR_INTERNAL,
+ *	-FDT_ERR_BADLAYOUT,
+ *	-FDT_ERR_BADMAGIC,
+ *	-FDT_ERR_BADOFFSET,
+ *	-FDT_ERR_BADPATH,
+ *	-FDT_ERR_BADVERSION,
+ *	-FDT_ERR_BADSTRUCTURE,
+ *	-FDT_ERR_BADSTATE,
+ *	-FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_overlay_apply(void *fdt, void *fdto);
+
 /**********************************************************************/
 /* Debugging / informational functions                                */
 /**********************************************************************/
diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile
index 934d6142c3e9..4935bf012a75 100644
--- a/lib/libfdt/Makefile
+++ b/lib/libfdt/Makefile
@@ -6,4 +6,4 @@
 #
 
 obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \
-	fdt_empty_tree.o fdt_addresses.o fdt_region.o
+	fdt_empty_tree.o fdt_addresses.o fdt_region.o fdt_overlay.o
diff --git a/lib/libfdt/fdt_overlay.c b/lib/libfdt/fdt_overlay.c
new file mode 100644
index 000000000000..1e68e903c734
--- /dev/null
+++ b/lib/libfdt/fdt_overlay.c
@@ -0,0 +1,414 @@
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static uint32_t fdt_overlay_get_target_phandle(const void *fdto, int node)
+{
+	const uint32_t *val;
+	int len;
+
+	val = fdt_getprop(fdto, node, "target", &len);
+	if (!val || (len != sizeof(*val)))
+		return 0;
+
+	return fdt32_to_cpu(*val);
+}
+
+static int fdt_overlay_get_target(const void *fdt, const void *fdto,
+				  int fragment)
+{
+	uint32_t phandle;
+	const char *path;
+
+	/* Try first to do a phandle based lookup */
+	phandle = fdt_overlay_get_target_phandle(fdto, fragment);
+	if (phandle)
+		return fdt_node_offset_by_phandle(fdt, phandle);
+
+	/* And then a path based lookup */
+	path = fdt_getprop(fdto, fragment, "target-path", NULL);
+	if (!path)
+		return -FDT_ERR_NOTFOUND;
+
+	return fdt_path_offset(fdt, path);
+}
+
+static int fdt_overlay_adjust_node_phandles(void *fdto, int node,
+					    uint32_t delta)
+{
+	int property;
+	int child;
+
+	fdt_for_each_property(fdto, node, property) {
+		const uint32_t *val;
+		const char *name;
+		uint32_t adj_val;
+		int len;
+		int ret;
+
+		val = fdt_getprop_by_offset(fdto, property,
+					    &name, &len);
+		if (!val || (len != sizeof(*val)))
+			continue;
+
+		if (strcmp(name, "phandle") && strcmp(name, "linux,phandle"))
+			continue;
+
+		adj_val = fdt32_to_cpu(*val);
+		adj_val += delta;
+		ret = fdt_setprop_inplace_u32(fdto, node, name, adj_val);
+		if (ret)
+			return ret;
+	}
+
+	fdt_for_each_subnode(fdto, child, node)
+		fdt_overlay_adjust_node_phandles(fdto, child, delta);
+
+	return 0;
+}
+
+static int fdt_overlay_adjust_local_phandles(void *fdto, uint32_t delta)
+{
+	int root;
+
+	root = fdt_path_offset(fdto, "/");
+	if (root < 0)
+		return root;
+
+	return fdt_overlay_adjust_node_phandles(fdto, root, delta);
+}
+
+static int _fdt_overlay_update_local_references(void *fdto,
+						int tree_node,
+						int fixup_node,
+						uint32_t delta)
+{
+	int fixup_prop;
+	int fixup_child;
+
+	fdt_for_each_property(fdto, fixup_node, fixup_prop) {
+		const char *fixup_name, *tree_name;
+		const uint32_t *val = NULL;
+		uint32_t adj_val;
+		int fixup_len;
+		int tree_prop;
+		int tree_len;
+		int ret;
+
+		fdt_getprop_by_offset(fdto, fixup_prop,
+				      &fixup_name, &fixup_len);
+
+		if (!strcmp(fixup_name, "phandle") ||
+		    !strcmp(fixup_name, "linux,phandle"))
+			continue;
+
+		fdt_for_each_property(fdto, tree_node, tree_prop) {
+			val = fdt_getprop_by_offset(fdto, tree_prop,
+						    &tree_name, &tree_len);
+
+			if (!strcmp(tree_name, fixup_name))
+				break;
+		}
+
+		if (!val || !tree_len)
+			return -FDT_ERR_NOTFOUND;
+
+		if (!tree_name)
+			return -FDT_ERR_NOTFOUND;
+
+		if (tree_len != 4)
+			return -FDT_ERR_NOTFOUND;
+
+		adj_val = fdt32_to_cpu(*val);
+		adj_val += delta;
+		ret = fdt_setprop_inplace_u32(fdto, tree_node, fixup_name,
+					      adj_val);
+		if (ret)
+			return ret;
+	}
+
+	fdt_for_each_subnode(fdto, fixup_child, fixup_node) {
+		const char *fixup_child_name = fdt_get_name(fdto,
+							    fixup_child, NULL);
+		int tree_child;
+
+		fdt_for_each_subnode(fdto, tree_child, tree_node) {
+			const char *tree_child_name = fdt_get_name(fdto,
+								   tree_child,
+								   NULL);
+
+			if (!strcmp(fixup_child_name, tree_child_name))
+				break;
+		}
+
+		_fdt_overlay_update_local_references(fdto,
+						     tree_child,
+						     fixup_child,
+						     delta);
+	}
+
+	return 0;
+}
+
+static int fdt_overlay_update_local_references(void *dto, uint32_t delta)
+{
+	int root, fixups;
+
+	root = fdt_path_offset(dto, "/");
+	if (root < 0)
+		return root;
+
+	fixups = fdt_path_offset(dto, "/__local_fixups__");
+	if (root < 0)
+		return root;
+
+	return _fdt_overlay_update_local_references(dto, root, fixups,
+						    delta);
+}
+
+static int _fdt_overlay_fixup_phandle(void *fdt, void *fdto,
+				      int symbols_off,
+				      const char *path, const char *name,
+				      int index, const char *label)
+{
+	const uint32_t *prop_val;
+	const char *symbol_path;
+	uint32_t *fixup_val;
+	uint32_t phandle;
+	int symbol_off, fixup_off;
+	int prop_len;
+	int ret;
+
+	symbol_path = fdt_getprop(fdt, symbols_off, label,
+				  &prop_len);
+	if (!symbol_path)
+		return -FDT_ERR_NOTFOUND;
+
+	symbol_off = fdt_path_offset(fdt, symbol_path);
+	if (symbol_off < 0)
+		return symbol_off;
+
+	phandle = fdt_get_phandle(fdt, symbol_off);
+	if (!phandle)
+		return -FDT_ERR_NOTFOUND;
+
+	fixup_off = fdt_path_offset(fdto, path);
+	if (fixup_off < 0)
+		return fixup_off;
+
+	prop_val = fdt_getprop(fdto, fixup_off, name,
+			       &prop_len);
+	if (!prop_val)
+		return -FDT_ERR_NOTFOUND;
+
+	fixup_val = malloc(prop_len);
+	if (!fixup_val)
+		return -FDT_ERR_INTERNAL;
+	memcpy(fixup_val, prop_val, prop_len);
+
+	if (fdt32_to_cpu(fixup_val[index]) != 0xdeadbeef) {
+		ret = -FDT_ERR_BADPHANDLE;
+		goto out;
+	}
+
+	fixup_val[index] = cpu_to_fdt32(phandle);
+	ret = fdt_setprop_inplace(fdto, fixup_off,
+				  name, fixup_val,
+				  prop_len);
+
+out:
+	free(fixup_val);
+	return ret;
+};
+
+static int fdt_overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
+				     int property)
+{
+	const char *value, *tmp_value;
+	const char *label;
+	int tmp_len, len, next;
+	int table = 0;
+	int i;
+
+	value = fdt_getprop_by_offset(fdto, property,
+				      &label, &len);
+	tmp_value = value;
+	tmp_len = len;
+
+	do {
+		next = strlen(tmp_value) + 1;
+		tmp_len -= next;
+		tmp_value += next;
+		table++;
+	} while (tmp_len > 0);
+
+	for (i = 0; i < table; i++) {
+		const char *prop_string = value;
+		const char *path, *name;
+		char *prop_cpy, *prop_tmp;
+		int index, stlen;
+		char *sep;
+
+		stlen = strlen(prop_string);
+		prop_cpy = malloc(stlen + 1);
+		if (!prop_cpy)
+			return -FDT_ERR_INTERNAL;
+
+		strncpy(prop_cpy, prop_string, stlen);
+		prop_cpy[stlen] = '\0';
+
+		for (prop_tmp = prop_cpy; (sep = strchr(prop_tmp, ':'));
+		     prop_tmp += ((sep - prop_tmp) + 1))
+			*sep = '\0';
+
+		prop_tmp = prop_cpy;
+		path = prop_tmp;
+		prop_tmp += strlen(prop_tmp) + 1;
+
+		name = prop_tmp;
+		prop_tmp += strlen(prop_tmp) + 1;
+
+		index = strtol(prop_tmp, NULL, 10);
+		prop_tmp += strlen(prop_tmp) + 1;
+
+		value += stlen + 1;
+		len -= stlen + 1;
+
+		_fdt_overlay_fixup_phandle(fdt, fdto, symbols_off,
+					   path, name, index, label);
+
+		free(prop_cpy);
+	}
+
+	return 0;
+}
+
+static int fdt_overlay_fixup_phandles(void *dt, void *dto)
+{
+	int fixups_off, symbols_off;
+	int property;
+
+	symbols_off = fdt_path_offset(dt, "/__symbols__");
+	fixups_off = fdt_path_offset(dto, "/__fixups__");
+
+	fdt_for_each_property(dto, fixups_off, property)
+		fdt_overlay_fixup_phandle(dt, dto, symbols_off, property);
+
+	return 0;
+}
+
+static int fdt_apply_overlay_node(void *dt, void *dto,
+				  int target, int overlay)
+{
+	int property;
+	int node;
+
+	fdt_for_each_property(dto, overlay, property) {
+		const char *name;
+		const void *prop;
+		int prop_len;
+		int ret;
+
+		prop = fdt_getprop_by_offset(dto, property, &name,
+					     &prop_len);
+		if (!prop)
+			return -FDT_ERR_INTERNAL;
+
+		ret = fdt_setprop(dt, target, name, prop, prop_len);
+		if (ret)
+			return ret;
+	}
+
+	fdt_for_each_subnode(dto, node, overlay) {
+		const char *name = fdt_get_name(dto, node, NULL);
+		int nnode;
+		int ret;
+
+		nnode = fdt_add_subnode(dt, target, name);
+		if (nnode < 0)
+			return nnode;
+
+		ret = fdt_apply_overlay_node(dt, dto, nnode, node);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int fdt_overlay_merge(void *dt, void *dto)
+{
+	int root, fragment;
+
+	root = fdt_path_offset(dto, "/");
+	if (root < 0)
+		return root;
+
+	fdt_for_each_subnode(dto, fragment, root) {
+		const char *name = fdt_get_name(dto, fragment, NULL);
+		uint32_t target;
+		int overlay;
+		int ret;
+
+		if (strncmp(name, "fragment", 8))
+			continue;
+
+		target = fdt_overlay_get_target(dt, dto, fragment);
+		if (target < 0)
+			return target;
+
+		overlay = fdt_subnode_offset(dto, fragment, "__overlay__");
+		if (overlay < 0)
+			return overlay;
+
+		ret = fdt_apply_overlay_node(dt, dto, target, overlay);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int fdt_overlay_apply(void *fdt, void *fdto)
+{
+	uint32_t delta = fdt_get_max_phandle(fdt) + 1;
+	void *fdto_copy;
+	int ret;
+
+	FDT_CHECK_HEADER(fdt);
+	FDT_CHECK_HEADER(fdto);
+
+	/*
+	 * We're going to modify the overlay so that we can apply it.
+	 *
+	 * Make sure sure we don't destroy the original
+	 */
+	fdto_copy = malloc(fdt_totalsize(fdto));
+	if (!fdto_copy)
+		return -FDT_ERR_INTERNAL;
+
+	ret = fdt_move(fdto, fdto_copy, fdt_totalsize(fdto));
+	if (ret)
+		goto out;
+
+	ret = fdt_overlay_adjust_local_phandles(fdto_copy, delta);
+	if (ret)
+		goto out;
+
+	ret = fdt_overlay_update_local_references(fdto_copy, delta);
+	if (ret)
+		goto out;
+
+	ret = fdt_overlay_fixup_phandles(fdt, fdto_copy);
+	if (ret)
+		goto out;
+
+	ret = fdt_overlay_merge(fdt, fdto_copy);
+
+out:
+	free(fdto_copy);
+	return ret;
+}
-- 
2.8.2



More information about the U-Boot mailing list