[U-Boot] [PATCH RESEND 2/2] cmd: fdt: add fdt overlay application subcommand

Maxime Ripard maxime.ripard at free-electrons.com
Mon Apr 4 20:25:07 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 or the raspberry pi).

However, so far, the usual mechanism to deal with it was to have in Linux
some driver detecting the expansion boards plugged in and then request
these overlays using the firmware interface.

That works in most cases, but in some cases, you might want to have the
overlays applied before the userspace comes in. Either because the new
board requires some kind of an early initialization, or because your root
filesystem is accessed through that expansion board.

The easiest solution in such a case is to simply have the component before
Linux applying that overlay, removing all these drawbacks.

Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>
---
 cmd/Makefile          |   2 +-
 cmd/fdt.c             |  19 +++
 cmd/fdt_overlay.c     | 464 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/fdt_support.h |   2 +-
 4 files changed, 485 insertions(+), 2 deletions(-)
 create mode 100644 cmd/fdt_overlay.c

diff --git a/cmd/Makefile b/cmd/Makefile
index 03f7e0a21d2f..09f993971d93 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -52,7 +52,7 @@ obj-$(CONFIG_CMD_EXT4) += ext4.o
 obj-$(CONFIG_CMD_EXT2) += ext2.o
 obj-$(CONFIG_CMD_FAT) += fat.o
 obj-$(CONFIG_CMD_FDC) += fdc.o
-obj-$(CONFIG_OF_LIBFDT) += fdt.o
+obj-$(CONFIG_OF_LIBFDT) += fdt.o fdt_overlay.o
 obj-$(CONFIG_CMD_FITUPD) += fitupd.o
 obj-$(CONFIG_CMD_FLASH) += flash.o
 ifdef CONFIG_FPGA
diff --git a/cmd/fdt.c b/cmd/fdt.c
index ca6c707dfb48..c875ba688d3b 100644
--- a/cmd/fdt.c
+++ b/cmd/fdt.c
@@ -639,6 +639,24 @@ static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 #endif
 
 	}
+	/* apply an overlay */
+	else if (strncmp(argv[1], "ap", 2) == 0) {
+		unsigned long addr;
+		struct fdt_header *blob;
+
+		if (argc != 3)
+			return CMD_RET_USAGE;
+
+		if (!working_fdt)
+			return CMD_RET_FAILURE;
+
+		addr = simple_strtoul(argv[2], NULL, 16);
+		blob = map_sysmem(addr, 0);
+		if (!fdt_valid(&blob))
+			return CMD_RET_FAILURE;
+
+		fdt_overlay_apply(working_fdt, blob);
+	}
 	/* resize the fdt */
 	else if (strncmp(argv[1], "re", 2) == 0) {
 		fdt_shrink_to_minimum(working_fdt);
@@ -1025,6 +1043,7 @@ static int fdt_print(const char *pathp, char *prop, int depth)
 #ifdef CONFIG_SYS_LONGHELP
 static char fdt_help_text[] =
 	"addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
+	"fdt apply <addr>                    - Apply overlay to the DT\n"
 #ifdef CONFIG_OF_BOARD_SETUP
 	"fdt boardsetup                      - Do board-specific set up\n"
 #endif
diff --git a/cmd/fdt_overlay.c b/cmd/fdt_overlay.c
new file mode 100644
index 000000000000..d2784d791a09
--- /dev/null
+++ b/cmd/fdt_overlay.c
@@ -0,0 +1,464 @@
+#include <common.h>
+#include <malloc.h>
+#include <libfdt.h>
+
+#define fdt_for_each_property(fdt, node, property)		\
+	for (property = fdt_first_property_offset(fdt, node);	\
+	     property >= 0;					\
+	     property = fdt_next_property_offset(fdt, property))
+
+struct fdt_overlay_fixup {
+	char	label[64];
+	char	name[64];
+	char	path[64];
+	int	index;
+};
+
+static int fdt_get_max_phandle(const void *dt)
+{
+	int offset, phandle = 0, new_phandle;
+
+	for (offset = fdt_next_node(dt, -1, NULL); offset >= 0;
+	     offset = fdt_next_node(dt, offset, NULL)) {
+		new_phandle = fdt_get_phandle(dt, offset);
+		if (new_phandle > phandle)
+			phandle = new_phandle;
+	}
+
+	return phandle;
+}
+
+static uint32_t fdt_overlay_get_target_phandle(const void *dt, int node)
+{
+	const uint32_t *val;
+	int len;
+
+	val = fdt_getprop(dt, node, "target", &len);
+	if (!val || (len != sizeof(*val)))
+		return 0;
+
+	return fdt32_to_cpu(*val);
+}
+
+static int fdt_overlay_get_target(const void *dt, const void *dto, int fragment)
+{
+	uint32_t phandle;
+	const char *path;
+
+	/* Try first to do a phandle based lookup */
+	phandle = fdt_overlay_get_target_phandle(dto, fragment);
+	if (phandle)
+		return fdt_node_offset_by_phandle(dt, phandle);
+
+	/* And then a path based lookup */
+	path = fdt_getprop(dto, fragment, "target-path", NULL);
+	if (!path)
+		return -FDT_ERR_NOTFOUND;
+
+	return fdt_path_offset(dt, path);
+}
+
+static int fdt_overlay_adjust_node_phandles(void *dto, int node,
+					    uint32_t delta)
+{
+	int property;
+	int child;
+
+	fdt_for_each_property(dto, node, property) {
+		const uint32_t *val;
+		const char *name;
+		uint32_t adj_val;
+		int len;
+		int ret;
+
+		val = fdt_getprop_by_offset(dto, property,
+					    &name, &len);
+		if (!val || (len != 4))
+			continue;
+
+		if (strcmp(name, "phandle") && strcmp(name, "linux,phandle"))
+			continue;
+
+		adj_val = fdt32_to_cpu(*val);
+		adj_val += delta;
+		ret = fdt_setprop_inplace_u32(dto, node, name, adj_val);
+		if (ret) {
+			printf("Failed to ajust property %s phandle\n", name);
+			return ret;
+		}
+	}
+
+	fdt_for_each_subnode(dto, child, node)
+		fdt_overlay_adjust_node_phandles(dto, child, delta);
+
+	return 0;
+}
+
+static int fdt_overlay_adjust_local_phandles(void *overlay, uint32_t delta)
+{
+	int root;
+
+	root = fdt_path_offset(overlay, "/");
+	if (root < 0) {
+		printf("Couldn't locate the root of the overlay\n");
+		return root;
+	}
+
+	return fdt_overlay_adjust_node_phandles(overlay, root, delta);
+}
+
+static int _fdt_overlay_update_local_references(void *dto,
+						int tree_node,
+						int fixup_node,
+						uint32_t delta)
+{
+	int fixup_prop;
+	int fixup_child;
+
+	fdt_for_each_property(dto, fixup_node, fixup_prop) {
+		const char *fixup_name, *tree_name;
+		const uint32_t *val;
+		uint32_t adj_val;
+		int fixup_len;
+		int tree_prop;
+		int ret;
+
+		fdt_getprop_by_offset(dto, fixup_prop,
+				      &fixup_name, &fixup_len);
+
+		if (!strcmp(fixup_name, "phandle") ||
+		    !strcmp(fixup_name, "linux,phandle"))
+			continue;
+
+		if (fixup_len != 4) {
+			printf("Illegal property size (%d) %s\n",
+			       fixup_len, fixup_name);
+			return -FDT_ERR_NOTFOUND;
+		}
+
+		fdt_for_each_property(dto, tree_node, tree_prop) {
+			int tree_len;
+
+			val = fdt_getprop_by_offset(dto, tree_prop,
+						    &tree_name, &tree_len);
+
+			if (!strcmp(tree_name, fixup_name))
+				break;
+		}
+
+		if (!tree_name) {
+			printf("Couldn't find target property %s\n",
+			       fixup_name);
+			return -FDT_ERR_NOTFOUND;
+		}
+
+		adj_val = fdt32_to_cpu(*val);
+		adj_val += delta;
+		ret = fdt_setprop_inplace_u32(dto, tree_node, fixup_name,
+					      adj_val);
+		if (ret) {
+			printf("Failed to ajust property %s phandle\n",
+			       fixup_name);
+			return ret;
+		}
+	}
+
+	fdt_for_each_subnode(dto, fixup_child, fixup_node) {
+		const char *fixup_child_name = fdt_get_name(dto,
+							    fixup_child, NULL);
+		int tree_child;
+
+		fdt_for_each_subnode(dto, tree_child, tree_node) {
+			const char *tree_child_name = fdt_get_name(dto,
+								   tree_child,
+								   NULL);
+
+			if (!strcmp(fixup_child_name, tree_child_name))
+				break;
+		}
+
+		_fdt_overlay_update_local_references(dto,
+						     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) {
+		printf("Couldn't locate the root of the overlay\n");
+		return root;
+	}
+
+	fixups = fdt_path_offset(dto, "/__local_fixups__");
+	if (root < 0) {
+		printf("Couldn't locate the local fixups in the overlay\n");
+		return root;
+	}
+
+	return _fdt_overlay_update_local_references(dto, root, fixups,
+						    delta);
+}
+
+static struct fdt_overlay_fixup *fdt_fixups_parse_property(void *dto,
+							   int property,
+							   int *number)
+{
+	struct fdt_overlay_fixup *fixups;
+	const char *value, *tmp_value;
+	const char *name;
+	int tmp_len, len, next;
+	int table = 0;
+	int i;
+
+	value = fdt_getprop_by_offset(dto, property,
+				      &name, &len);
+	tmp_value = value;
+	tmp_len = len;
+
+	do {
+		next = strlen(tmp_value) + 1;
+		tmp_len -= next;
+		tmp_value += next;
+		table++;
+	} while (tmp_len > 0);
+
+	fixups = malloc(table * sizeof(*fixups));
+	if (!fixups)
+		return NULL;
+
+	for (i = 0; i < table; i++) {
+		struct fdt_overlay_fixup *fixup = fixups + i;
+		const char *prop_string = value;
+		char *sep;
+		int stlen;
+
+		stlen = strlen(prop_string);
+
+		sep = strchr(prop_string, ':');
+		strncpy(fixup->path, prop_string, sep - prop_string);
+		fixup->path[sep - prop_string] = '\0';
+		prop_string = sep + 1;
+
+		sep = strchr(prop_string, ':');
+		strncpy(fixup->name, prop_string, sep - prop_string);
+		fixup->name[sep - prop_string] = '\0';
+		prop_string = sep + 1;
+
+		fixup->index = simple_strtol(prop_string, NULL, 10);
+		strncpy(fixup->label, name, 64);
+
+		value += stlen + 1;
+		len -= stlen + 1;
+	}
+
+	*number = table;
+	return fixups;
+}
+
+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) {
+		struct fdt_overlay_fixup *fixups;
+		int n_fixups;
+		int i;
+
+		fixups = fdt_fixups_parse_property(dto, property, &n_fixups);
+		if (!fixups || n_fixups == 0)
+			continue;
+
+		for (i = 0; i < n_fixups; i++) {
+			struct fdt_overlay_fixup *fixup = fixups + i;
+			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(dt, symbols_off, fixup->label,
+						  &prop_len);
+			if (!symbol_path) {
+				printf("Couldn't lookup symbol %s in the main DT... Skipping\n",
+				       fixup->label);
+				continue;
+			}
+
+			symbol_off = fdt_path_offset(dt, symbol_path);
+			if (symbol_off < 0) {
+				printf("Couldn't match the symbol %s to node %s... Skipping\n",
+				       fixup->label, symbol_path);
+				continue;
+			}
+
+			phandle = fdt_get_phandle(dt, symbol_off);
+			if (!phandle) {
+				printf("Symbol node %s has no phandle... Skipping\n",
+				       symbol_path);
+				continue;
+			}
+
+			fixup_off = fdt_path_offset(dto, fixup->path);
+			if (fixup_off < 0) {
+				printf("Invalid overlay node %s to fixup... Skipping\n",
+				       fixup->path);
+				continue;
+			}
+
+			prop_val = fdt_getprop(dto, fixup_off, fixup->name,
+					       &prop_len);
+			if (!prop_val) {
+				printf("Couldn't retrieve property %s/%s value... Skipping\n",
+				       fixup->path, fixup->name);
+				continue;
+			}
+
+			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[fixup->index]) != 0xdeadbeef) {
+				printf("Value pointed (0x%x) is not supposed to be fixed up... Skipping\n",
+				       fdt32_to_cpu(fixup_val[fixup->index]));
+				continue;
+			}
+
+			fixup_val[fixup->index] = cpu_to_fdt32(phandle);
+			ret = fdt_setprop_inplace(dto, fixup_off,
+						  fixup->name, fixup_val,
+						  prop_len);
+			if (ret) {
+				printf("Couldn't fixup phandle in overlay property %s/%s (%d)... Skipping\n",
+				       fixup->path, fixup->name, ret);
+			}
+		}
+
+		free(fixups);
+	}
+
+	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) {
+			printf("Couldn't set property %s\n", name);
+			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) {
+			printf("Couldn't add subnode %s (%d)\n", name, nnode);
+			return nnode;
+		}
+
+		ret = fdt_apply_overlay_node(dt, dto, nnode, node);
+		if (ret) {
+			printf("Couldn't apply sub-overlay (%d)\n", 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) {
+		printf("Couldn't locate the root of our overlay\n");
+		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) {
+			printf("Couldn't locate %s target\n", name);
+			return target;
+		}
+
+		overlay = fdt_subnode_offset(dto, fragment, "__overlay__");
+		if (overlay < 0) {
+			printf("Couldn't locate %s overlay\n", name);
+			return overlay;
+		}
+
+		ret = fdt_apply_overlay_node(dt, dto, target, overlay);
+		if (ret) {
+			printf("Couldn't apply %s overlay\n", name);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+int fdt_overlay_apply(void *dt, void *dto)
+{
+	uint32_t delta = fdt_get_max_phandle(dt) + 1;
+	int ret;
+
+	ret = fdt_overlay_adjust_local_phandles(dto, delta);
+	if (ret) {
+		printf("Couldn't adjust local phandles\n");
+		return ret;
+	}
+
+	ret = fdt_overlay_update_local_references(dto, delta);
+	if (ret) {
+		printf("Couldn't update our local references\n");
+		return ret;
+	}
+
+	ret = fdt_overlay_fixup_phandles(dt, dto);
+	if (ret) {
+		printf("Couldn't resolve the global phandles\n");
+		return ret;
+	}
+
+	return fdt_overlay_merge(dt, dto);
+}
diff --git a/include/fdt_support.h b/include/fdt_support.h
index 296add01f34f..b4184a767e28 100644
--- a/include/fdt_support.h
+++ b/include/fdt_support.h
@@ -240,7 +240,7 @@ int arch_fixup_memory_node(void *blob);
 
 int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
 			    u32 height, u32 stride, const char *format);
-
+int fdt_overlay_apply(void *fdt, void *overlay);
 #endif /* ifdef CONFIG_OF_LIBFDT */
 
 #ifdef USE_HOSTCC
-- 
2.8.0



More information about the U-Boot mailing list