mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/7] Device Tree Overlay Support
@ 2019-09-05 10:51 Michael Tretter
  2019-09-05 10:51 ` [PATCH 1/7] dtc: add -@ option to enable __symbols__ Michael Tretter
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

Hello,

This series adds device tree overlay support loosely based on the Linux
driver.

In contrast to Linux, the overlay is not applied to the live tree, but
registered as a device tree fixup. This allows to apply the overlay to the
build-in device tree and external device trees, which is necessary for booting
Linux with an overlaid device tree.

I further added the "devicetree-overlay" key to the blspec. This allows to
specify device tree overlays that should be applied before booting the OS from
that blspec entry. Note that if an overlay fails to apply, Linux still would
boot normally with the base device tree.

The RFC extended the oftree command, but I now defined a new "of_overlay"
command that accepts the file name of the overlay as an argument. Having a
separate command with separate options for overlays looks much cleaner to me.

Compared to the RFC, I dropped the notifiers, because they are too limited to
get information from the module that registers the overlay to the module that
is notified and reacts on an overlay. Now, any other module that should react
on an overlay must be called explicitly.

Michael

Changelog:

rfc -> v1:
 - new of_overlay command
 - no more notifiers for device tree overlays

Michael Tretter (7):
  dtc: add -@ option to enable __symbols__
  of: add support for devicetree overlays
  blspec: add support for devicetree overlays
  of: add iterator for overlays
  firmware: add support to load firmware from dt overlay
  blspec: load firmware if specified in dt overlay
  commands: add of_overlay command for device tree overlays

 Documentation/user/booting-linux.rst |   4 +
 commands/Kconfig                     |  12 ++
 commands/Makefile                    |   1 +
 commands/of_overlay.c                |  89 +++++++++
 common/blspec.c                      |  75 ++++++++
 common/firmware.c                    |  77 ++++++++
 drivers/of/Kconfig                   |   9 +
 drivers/of/Makefile                  |   1 +
 drivers/of/overlay.c                 | 245 +++++++++++++++++++++++
 drivers/of/resolver.c                | 278 +++++++++++++++++++++++++++
 include/firmware.h                   |   3 +
 include/of.h                         |  33 ++++
 scripts/Makefile.lib                 |   2 +-
 13 files changed, 828 insertions(+), 1 deletion(-)
 create mode 100644 commands/of_overlay.c
 create mode 100644 drivers/of/overlay.c
 create mode 100644 drivers/of/resolver.c

-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/7] dtc: add -@ option to enable __symbols__
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-05 10:51 ` [PATCH 2/7] of: add support for devicetree overlays Michael Tretter
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

The devicetree overlay driver requires the __symbols__ node to resolve
phandles to the base devicetree. As Barebox applies the overlay to the
live devicetree, the build-in devicetree must be built with the
__symbols__ node.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 scripts/Makefile.lib | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index fc5fe3d7e8..292dc938d9 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -292,7 +292,7 @@ $(obj)/%.dtb.S: $(obj)/%.dtb $(srctree)/scripts/gen-dtb-s FORCE
 
 quiet_cmd_dtc = DTC     $@
 cmd_dtc = $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
-	$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 \
+	$(objtree)/scripts/dtc/dtc -@ -O dtb -o $@ -b 0 \
 		-i $(srctree)/arch/$(SRCARCH)/dts $(DTC_FLAGS) \
 		-i $(srctree)/dts/src/$(SRCARCH) \
 		-d $(depfile).dtc $(dtc-tmp) ; \
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/7] of: add support for devicetree overlays
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
  2019-09-05 10:51 ` [PATCH 1/7] dtc: add -@ option to enable __symbols__ Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-05 15:24   ` Ahmad Fatoum
  2019-09-06  7:29   ` Sascha Hauer
  2019-09-05 10:51 ` [PATCH 3/7] blspec: " Michael Tretter
                   ` (4 subsequent siblings)
  6 siblings, 2 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

The devicetree overlay support is based on the Linux driver for device
tree overlays, but many features that are not required in Barebox are
left out.

Unlike Linux, which applies the overlay to the live devicetree, Barebox
registers a fixup for the overlay which is applied with other fixups to
whatever tree is fixed. This is necessary to apply the overlay to
devicetrees that are passed to Linux, which might differ from the
devicetree that is currently live in Barebox.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 drivers/of/Kconfig    |   9 ++
 drivers/of/Makefile   |   1 +
 drivers/of/overlay.c  | 200 ++++++++++++++++++++++++++++++
 drivers/of/resolver.c | 278 ++++++++++++++++++++++++++++++++++++++++++
 include/of.h          |  20 +++
 5 files changed, 508 insertions(+)
 create mode 100644 drivers/of/overlay.c
 create mode 100644 drivers/of/resolver.c

diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 24cf4465a8..1bb6639c5a 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -50,3 +50,12 @@ config OF_BAREBOX_ENV_IN_FS
 	help
 	  Allow the devie tree configuration of the barebox environment path
 	  to specify a file in filesystem, which will be mounted.
+
+config OF_OVERLAY
+	select OFTREE
+	bool "Devicetree overlays"
+	help
+	  Overlays allow to patch the devicetree. Unlike Linux, Barebox does
+	  not patch the live devicetree, but applies the overlays as fixup to
+	  the devicetree. Furthermore, overlays cannot be removed after they
+	  have been applied.
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ec43870061..9c6f8de814 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -6,3 +6,4 @@ obj-y += partition.o
 obj-y += of_net.o
 obj-$(CONFIG_MTD) += of_mtd.o
 obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
+obj-$(CONFIG_OF_OVERLAY) += overlay.o resolver.o
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
new file mode 100644
index 0000000000..b9283e6a90
--- /dev/null
+++ b/drivers/of/overlay.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions for working with device tree overlays
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ */
+
+#include <common.h>
+#include <notifier.h>
+#include <of.h>
+#include <errno.h>
+
+static struct device_node *find_target(struct device_node *root,
+				       struct device_node *fragment)
+{
+	struct device_node *node;
+	const char *path;
+	u32 phandle;
+	int ret;
+
+	ret = of_property_read_u32(fragment, "target", &phandle);
+	if (!ret) {
+		node = of_find_node_by_phandle_from(phandle, root);
+		if (!node)
+			pr_err("fragment %pOF: phandle 0x%x not found\n",
+			       fragment, phandle);
+		return node;
+	}
+
+	ret = of_property_read_string(fragment, "target-path", &path);
+	if (!ret) {
+		node = of_find_node_by_path_from(root, path);
+		if (!node)
+			pr_err("fragment %pOF: path '%s' not found\n",
+			       fragment, path);
+		return node;
+	}
+
+	pr_err("fragment %pOF: no target property\n", fragment);
+
+	return NULL;
+}
+
+static int of_overlay_apply(struct device_node *target,
+			    const struct device_node *overlay)
+{
+	struct device_node *child;
+	struct device_node *target_child;
+	struct property *prop;
+	int err;
+
+	if (target == NULL || overlay == NULL)
+		return -EINVAL;
+
+	list_for_each_entry(prop, &overlay->properties, list) {
+		if (of_prop_cmp(prop->name, "name") == 0)
+			continue;
+
+		err = of_set_property(target, prop->name, prop->value,
+				      prop->length, true);
+		if (err)
+			return err;
+	}
+
+	for_each_child_of_node(overlay, child) {
+		target_child = of_get_child_by_name(target, child->name);
+		if (!target_child)
+			target_child = of_new_node(target, child->name);
+		if (!target_child)
+			return -ENOMEM;
+
+		err = of_overlay_apply(target_child, child);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static char *of_overlay_fix_path(struct device_node *root,
+				 struct device_node *overlay, const char *path)
+{
+	struct device_node *fragment;
+	struct device_node *target;
+	const char *path_tail;
+	const char *prefix;
+
+	fragment = of_find_node_by_path_from(overlay, path);
+	while ((fragment = of_get_parent(fragment)) != NULL) {
+		if (of_get_child_by_name(fragment, "__overlay__"))
+			break;
+	}
+	if (!fragment)
+		return NULL;
+
+	target = find_target(root, fragment);
+	if (!target)
+		return NULL;
+
+	prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
+	path_tail = path + strlen(prefix);
+
+	return basprintf("%s%s", target->full_name, path_tail);
+}
+
+static int of_overlay_apply_symbols(struct device_node *root,
+				    struct device_node *overlay)
+{
+	const char *old_path;
+	char *new_path;
+	struct property *prop;
+	struct device_node *root_symbols;
+	struct device_node *overlay_symbols;
+
+	root_symbols = of_get_child_by_name(root, "__symbols__");
+	if (!root_symbols)
+		return -EINVAL;
+
+	overlay_symbols = of_get_child_by_name(overlay, "__symbols__");
+	if (!overlay_symbols)
+		return -EINVAL;
+
+	list_for_each_entry(prop, &overlay_symbols->properties, list) {
+		if (of_prop_cmp(prop->name, "name") == 0)
+			continue;
+
+		old_path = of_property_get_value(prop);
+		new_path = of_overlay_fix_path(root, overlay, old_path);
+
+		pr_debug("add symbol %s with new path %s\n",
+			 prop->name, new_path);
+		of_property_write_string(root_symbols, prop->name, new_path);
+	}
+
+	return 0;
+}
+
+static int of_overlay_apply_fragment(struct device_node *root,
+				     struct device_node *fragment)
+{
+	struct device_node *target;
+	struct device_node *overlay;
+
+	overlay = of_get_child_by_name(fragment, "__overlay__");
+	if (!overlay)
+		return 0;
+
+	target = find_target(root, fragment);
+	if (!target)
+		return -EINVAL;
+
+	return of_overlay_apply(target, overlay);
+}
+
+/**
+ * Fix the passed root node using the device tree overlay in data
+ */
+static int of_overlay_fixup(struct device_node *root, void *data)
+{
+	struct device_node *overlay = data;
+	struct device_node *resolved;
+	struct device_node *fragment;
+	int err;
+
+	resolved = of_resolve_phandles(root, overlay);
+	if (!resolved)
+		return -EINVAL;
+
+	/* Copy symbols from resolved overlay to base device tree */
+	err = of_overlay_apply_symbols(root, resolved);
+	if (err)
+		pr_warn("failed to copy symbols from overlay");
+
+	/* Copy nodes and properties from resolved overlay to root */
+	for_each_child_of_node(resolved, fragment) {
+		err = of_overlay_apply_fragment(root, fragment);
+		if (err)
+			pr_warn("failed to apply %s", fragment->name);
+	}
+
+	of_delete_node(resolved);
+
+	return 0;
+}
+
+/**
+ * Register a devicetree overlay
+ *
+ * The overlay is not applied to the live device tree, but registered as fixup
+ * for the fixed up device tree. Therefore, drivers relying on the overlay
+ * must use the fixed device tree.
+ */
+int of_register_overlay(struct device_node *overlay)
+{
+	of_register_fixup(of_overlay_fixup, overlay);
+
+	return 0;
+}
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644
index 0000000000..574c75cb4b
--- /dev/null
+++ b/drivers/of/resolver.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions for dealing with DT resolution
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ */
+
+#include <common.h>
+#include <of.h>
+#include <errno.h>
+
+/**
+ * Recursively update phandles in overlay by adding delta
+ */
+static void adjust_overlay_phandles(struct device_node *overlay, int delta)
+{
+	struct device_node *child;
+	struct property *prop;
+
+	if (overlay->phandle != 0)
+		overlay->phandle += delta;
+
+	list_for_each_entry(prop, &overlay->properties, list) {
+		if (of_prop_cmp(prop->name, "phandle") != 0 &&
+		    of_prop_cmp(prop->name, "linux,phandle") != 0)
+			continue;
+		if (prop->length < 4)
+			continue;
+
+		be32_add_cpu(prop->value, delta);
+	}
+
+	for_each_child_of_node(overlay, child)
+		adjust_overlay_phandles(child, delta);
+}
+
+/**
+ * Update all unresolved phandles in the overlay using prop_fixup
+ *
+ * prop_fixup contains a list of tuples of path:property_name:offset, each of
+ * which refers to a property that is phandle to a node in the base
+ * devicetree.
+ */
+static int update_usages_of_a_phandle_reference(struct device_node *overlay,
+						struct property *prop_fixup,
+						phandle phandle)
+{
+	struct device_node *refnode;
+	struct property *prop;
+	char *value, *cur, *end, *node_path, *prop_name, *s;
+	int offset, len;
+	int err = 0;
+
+	pr_debug("resolve references to %s to phandle 0x%x\n",
+		 prop_fixup->name, phandle);
+
+	value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
+	if (!value)
+		return -ENOMEM;
+
+	end = value + prop_fixup->length;
+	for (cur = value; cur < end; cur += len + 1) {
+		len = strlen(cur);
+
+		node_path = cur;
+		s = strchr(cur, ':');
+		if (!s) {
+			err = -EINVAL;
+			goto err_fail;
+		}
+		*s++ = '\0';
+
+		prop_name = s;
+		s = strchr(s, ':');
+		if (!s) {
+			err = -EINVAL;
+			goto err_fail;
+		}
+		*s++ = '\0';
+
+		err = kstrtoint(s, 10, &offset);
+		if (err)
+			goto err_fail;
+
+		refnode = of_find_node_by_path_from(overlay, node_path);
+		if (!refnode)
+			continue;
+
+		prop = of_find_property(refnode, prop_name, NULL);
+		if (!prop) {
+			err = -ENOENT;
+			goto err_fail;
+		}
+
+		if (offset < 0 || offset + sizeof(__be32) > prop->length) {
+			err = -EINVAL;
+			goto err_fail;
+		}
+
+		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
+	}
+
+err_fail:
+	kfree(value);
+
+	if (err)
+		pr_debug("failed to resolve references to %s\n",
+			 prop_fixup->name);
+
+	return err;
+}
+
+/*
+ * Adjust the local phandle references by the given phandle delta.
+ *
+ * Subtree @local_fixups, which is overlay node __local_fixups__,
+ * mirrors the fragment node structure at the root of the overlay.
+ *
+ * For each property in the fragments that contains a phandle reference,
+ * @local_fixups has a property of the same name that contains a list
+ * of offsets of the phandle reference(s) within the respective property
+ * value(s).  The values at these offsets will be fixed up.
+ */
+static int adjust_local_phandle_references(struct device_node *local_fixups,
+		struct device_node *overlay, int phandle_delta)
+{
+	struct device_node *child, *overlay_child;
+	struct property *prop_fix, *prop;
+	int err, i, count;
+	unsigned int off;
+
+	if (!local_fixups)
+		return 0;
+
+	list_for_each_entry(prop_fix, &local_fixups->properties, list) {
+		if (!of_prop_cmp(prop_fix->name, "name") ||
+		    !of_prop_cmp(prop_fix->name, "phandle") ||
+		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
+			continue;
+
+		if ((prop_fix->length % sizeof(__be32)) != 0 ||
+		    prop_fix->length == 0)
+			return -EINVAL;
+		count = prop_fix->length / sizeof(__be32);
+
+		prop = of_find_property(overlay, prop_fix->name, NULL);
+		if (!prop)
+			return -EINVAL;
+
+		for (i = 0; i < count; i++) {
+			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
+			if ((off + sizeof(__be32)) > prop->length)
+				return -EINVAL;
+
+			be32_add_cpu(prop->value + off, phandle_delta);
+		}
+	}
+
+	for_each_child_of_node(local_fixups, child) {
+		for_each_child_of_node(overlay, overlay_child)
+			if (!of_node_cmp(child->name, overlay_child->name))
+				break;
+		if (!overlay_child)
+			return -EINVAL;
+
+		err = adjust_local_phandle_references(child, overlay_child,
+				phandle_delta);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * of_resolve_phandles - Resolve phandles in overlay based on root
+ *
+ * Rename phandles in overlay to avoid conflicts with the base devicetree and
+ * replace all phandles in the overlay with their renamed versions. Resolve
+ * phandles referring to nodes in the base devicetree with the phandle from
+ * the base devicetree.
+ *
+ * Returns a new device_node with resolved phandles which must be deleted by
+ * the caller of this function.
+ */
+struct device_node *of_resolve_phandles(struct device_node *root,
+					const struct device_node *overlay)
+{
+	struct device_node *result;
+	struct device_node *local_fixups;
+	struct device_node *refnode;
+	struct device_node *symbols;
+	struct device_node *overlay_fixups;
+	struct property *prop;
+	const char *refpath;
+	phandle delta;
+	int err;
+
+	result = of_copy_node(NULL, overlay);
+	if (!result)
+		return NULL;
+
+	delta = of_get_tree_max_phandle(root) + 1;
+
+	/*
+	 * Rename the phandles in the devicetree overlay to prevent conflicts
+	 * with the phandles in the base devicetree.
+	 */
+	adjust_overlay_phandles(result, delta);
+
+	/*
+	 * __local_fixups__ contains all locations in the overlay that refer
+	 * to a phandle defined in the overlay. We must update the references,
+	 * because we just adjusted the definitions.
+	 */
+	local_fixups = of_find_node_by_name(result, "__local_fixups__");
+	err = adjust_local_phandle_references(local_fixups, result, delta);
+	if (err) {
+		pr_err("failed to fix phandles in overlay\n");
+		goto err;
+	}
+
+	/*
+	 * __fixups__ contains all locations in the overlay that refer to a
+	 * phandle that is not defined in the overlay and should be defined in
+	 * the base device tree. We must update the references, because they
+	 * are otherwise undefined.
+	 */
+	overlay_fixups = of_find_node_by_name(result, "__fixups__");
+	if (!overlay_fixups) {
+		pr_debug("overlay does not contain phandles to base devicetree\n");
+		goto out;
+	}
+
+	symbols = of_find_node_by_path_from(root, "/__symbols__");
+	if (!symbols) {
+		pr_err("__symbols__ missing from base devicetree\n");
+		goto err;
+	}
+
+	list_for_each_entry(prop, &overlay_fixups->properties, list) {
+		if (!of_prop_cmp(prop->name, "name"))
+			continue;
+
+		err = of_property_read_string(symbols, prop->name, &refpath);
+		if (err) {
+			pr_err("cannot find node %s in base devicetree\n",
+			       prop->name);
+			goto err;
+		}
+
+		refnode = of_find_node_by_path_from(root, refpath);
+		if (!refnode) {
+			pr_err("cannot find path %s in base devicetree\n",
+			       refpath);
+			err = -EINVAL;
+			goto err;
+		}
+
+		err = update_usages_of_a_phandle_reference(result, prop,
+							   refnode->phandle);
+		if (err) {
+			pr_err("failed to update phandles for %s in overlay",
+			       prop->name);
+			goto err;
+		}
+	}
+
+out:
+	return result;
+err:
+	of_delete_node(result);
+
+	return NULL;
+
+}
diff --git a/include/of.h b/include/of.h
index b5f54dd4e5..ac80dc3247 100644
--- a/include/of.h
+++ b/include/of.h
@@ -3,6 +3,7 @@
 
 #include <fdt.h>
 #include <errno.h>
+#include <notifier.h>
 #include <linux/types.h>
 #include <linux/list.h>
 #include <asm/byteorder.h>
@@ -870,4 +871,23 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
 
 	return node;
 }
+
+#ifdef CONFIG_OF_OVERLAY
+struct device_node *of_resolve_phandles(struct device_node *root,
+					const struct device_node *overlay);
+int of_register_overlay(struct device_node *overlay);
+#else
+static inline struct device_node *of_resolve_phandles(struct device_node *root,
+					const struct device_node *overlay)
+{
+	return NULL;
+}
+
+static inline int of_register_overlay(struct device_node *overlay)
+{
+	return -ENOSYS;
+}
+
+#endif
+
 #endif /* __OF_H */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 3/7] blspec: add support for devicetree overlays
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
  2019-09-05 10:51 ` [PATCH 1/7] dtc: add -@ option to enable __symbols__ Michael Tretter
  2019-09-05 10:51 ` [PATCH 2/7] of: add support for devicetree overlays Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-06  7:33   ` Sascha Hauer
  2019-09-05 10:51 ` [PATCH 4/7] of: add iterator for overlays Michael Tretter
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

Read the devicetree-overlay property from the blspec entry and register
the overlays when booting the blspec entry.

Do not fail the boot if an overlay cannot be loaded, because if Linux
fails to boot without an overlay, the base device tree is broken.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 Documentation/user/booting-linux.rst |  4 ++
 common/blspec.c                      | 59 ++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst
index 437f4e80ca..12cd505e71 100644
--- a/Documentation/user/booting-linux.rst
+++ b/Documentation/user/booting-linux.rst
@@ -232,6 +232,10 @@ device where the entry is found on. This makes it possible to use the same rootf
 image on different devices without having to specify a different root= option each
 time.
 
+Additionally to the options defined in the original spec, Barebox has the
+``devicetree-overlay`` option. This is a string value that refer to overlays
+that will be applied to the device tree before passing it to Linux.
+
 Network boot
 ------------
 
diff --git a/common/blspec.c b/common/blspec.c
index 66e5033e35..6b15d36d67 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -42,6 +42,60 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
 			val ? strlen(val) + 1 : 0, 1);
 }
 
+static int blspec_apply_oftree_overlay(char *file, const char *abspath,
+				       int dryrun)
+{
+	int ret;
+	struct fdt_header *fdt;
+	struct device_node *overlay;
+	char *path;
+
+	path = basprintf("%s/%s", abspath, file);
+
+	fdt = read_file(path, NULL);
+	if (!fdt) {
+		pr_warn("unable to read \"%s\"\n", path);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	overlay = of_unflatten_dtb(fdt);
+	if (IS_ERR(overlay)) {
+		printf("\"%s\" is not a valid devicetree\n", path);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (dryrun) {
+		pr_info("dry run: skip overlay %s\n", path);
+		of_delete_node(overlay);
+		goto out;
+	}
+
+	ret = of_register_overlay(overlay);
+	if (ret)
+		pr_warn("cannot register devicetree overlay \"%s\"\n", path);
+
+out:
+	free(path);
+
+	return ret;
+}
+
+static void blspec_apply_oftree_overlays(const char *overlays,
+					 const char *abspath, int dryrun)
+{
+	char *overlay;
+	char *sep, *freep;
+
+	sep = freep = xstrdup(overlays);
+
+	while ((overlay = strsep(&sep, " ")))
+		blspec_apply_oftree_overlay(overlay, abspath, dryrun);
+
+	free(freep);
+}
+
 /*
  * blspec_boot - boot an entry
  *
@@ -54,6 +108,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
 	struct blspec_entry *entry = container_of(be, struct blspec_entry, entry);
 	int ret;
 	const char *abspath, *devicetree, *options, *initrd, *linuximage;
+	const char *overlays;
 	const char *appendroot;
 	struct bootm_data data = {
 		.initrd_address = UIMAGE_INVALID_ADDRESS,
@@ -73,6 +128,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
 	initrd = blspec_entry_var_get(entry, "initrd");
 	options = blspec_entry_var_get(entry, "options");
 	linuximage = blspec_entry_var_get(entry, "linux");
+	overlays = blspec_entry_var_get(entry, "devicetree-overlay");
 
 	if (entry->rootpath)
 		abspath = entry->rootpath;
@@ -92,6 +148,9 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
 		}
 	}
 
+	if (overlays)
+		blspec_apply_oftree_overlays(overlays, abspath, dryrun);
+
 	if (initrd)
 		data.initrd_file = basprintf("%s/%s", abspath, initrd);
 
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 4/7] of: add iterator for overlays
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
                   ` (2 preceding siblings ...)
  2019-09-05 10:51 ` [PATCH 3/7] blspec: " Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-05 10:51 ` [PATCH 5/7] firmware: add support to load firmware from dt overlay Michael Tretter
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

Device tree overlays (the dto files) may contain multiple fragments for
different target nodes. Each fragment contains a __overlay__ node that
is applied to target node specified in the fragment.

Add a helper to call a function for each fragment in a device tree
overlay to avoid having device tree overlay internal information in
other modules.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 drivers/of/overlay.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
 include/of.h         | 13 +++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index b9283e6a90..c413ad9849 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -185,6 +185,51 @@ static int of_overlay_fixup(struct device_node *root, void *data)
 	return 0;
 }
 
+/**
+ * Iterate the overlay and call process for each fragment
+ *
+ * If process() fails for any fragment, the function will stop to process
+ * other fragments and return the error of the failed process() call.
+ */
+int of_process_overlay(struct device_node *root,
+		       struct device_node *overlay,
+		       int (*process)(struct device_node *target,
+				      struct device_node *overlay, void *data),
+		       void *data)
+{
+	struct device_node *resolved;
+	struct device_node *fragment;
+	int err = 0;
+
+	resolved = of_resolve_phandles(root, overlay);
+	if (!resolved)
+		return -EINVAL;
+
+	for_each_child_of_node(resolved, fragment) {
+		struct device_node *ovl;
+		struct device_node *target;
+
+		ovl = of_get_child_by_name(fragment, "__overlay__");
+		if (!ovl)
+			continue;
+
+		target = find_target(root, fragment);
+		if (!target)
+			continue;
+
+		err = process(target, ovl, data);
+		if (err) {
+			pr_warn("failed to process %s\n", fragment->name);
+			goto out;
+		}
+	}
+
+out:
+	of_delete_node(resolved);
+
+	return err;
+}
+
 /**
  * Register a devicetree overlay
  *
diff --git a/include/of.h b/include/of.h
index ac80dc3247..48f85c589d 100644
--- a/include/of.h
+++ b/include/of.h
@@ -876,6 +876,11 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
 struct device_node *of_resolve_phandles(struct device_node *root,
 					const struct device_node *overlay);
 int of_register_overlay(struct device_node *overlay);
+int of_process_overlay(struct device_node *root,
+		    struct device_node *overlay,
+		    int (*process)(struct device_node *target,
+				   struct device_node *overlay, void *data),
+		    void *data);
 #else
 static inline struct device_node *of_resolve_phandles(struct device_node *root,
 					const struct device_node *overlay)
@@ -888,6 +893,14 @@ static inline int of_register_overlay(struct device_node *overlay)
 	return -ENOSYS;
 }
 
+int of_process_overlay(struct device_node *root,
+		    struct device_node *overlay,
+		    int (*process)(struct device_node *target,
+				   struct device_node *overlay, void *data),
+		    void *data)
+{
+	return -ENOSYS;
+}
 #endif
 
 #endif /* __OF_H */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 5/7] firmware: add support to load firmware from dt overlay
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
                   ` (3 preceding siblings ...)
  2019-09-05 10:51 ` [PATCH 4/7] of: add iterator for overlays Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-05 10:51 ` [PATCH 6/7] blspec: load firmware if specified in " Michael Tretter
  2019-09-05 10:51 ` [PATCH 7/7] commands: add of_overlay command for device tree overlays Michael Tretter
  6 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

fpga-region device tree nodes have the firmware-name property that
contains the file name of firmware in the firmware search path (but not
the path) that shall be loaded before the overlay is applied.

Add the firmware_load_overlay() function that accepts an overlay and a
firmware search path, finds the responsible firmware_mgr and loads the
firmware.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 common/firmware.c  | 77 ++++++++++++++++++++++++++++++++++++++++++++++
 include/firmware.h |  3 ++
 2 files changed, 80 insertions(+)

diff --git a/common/firmware.c b/common/firmware.c
index 9d55d73e7a..d15026cf07 100644
--- a/common/firmware.c
+++ b/common/firmware.c
@@ -62,6 +62,39 @@ struct firmware_mgr *firmwaremgr_find(const char *id)
 	return NULL;
 }
 
+/*
+ * firmwaremgr_find_by_node - find a firmware device handler
+ *
+ * Find a firmware device handler using the device node of the firmware
+ * handler. This allows to retrieve the firmware handler with a phandle from
+ * the device tree.
+ */
+struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+{
+	struct firmware_mgr *mgr;
+
+	list_for_each_entry(mgr, &firmwaremgr_list, list)
+		if (mgr->handler->dev->parent->device_node == np)
+			return mgr;
+
+	return NULL;
+}
+
+static struct firmware_mgr *of_node_get_mgr(const struct device_node *np)
+{
+	struct device_node *mgr_node;
+
+	do {
+		if (of_device_is_compatible(np, "fpga-region")) {
+			mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
+			if (mgr_node)
+				return firmwaremgr_find_by_node(mgr_node);
+		}
+	} while ((np = of_get_parent(np)) != NULL);
+
+	return NULL;
+}
+
 /*
  * firmwaremgr_list_handlers - list registered firmware device handlers
  *                             in pretty format
@@ -153,6 +186,40 @@ static int firmware_close(struct cdev *cdev)
 	return 0;
 }
 
+struct overlay_info {
+	const char *firmware_path;
+};
+
+static int load_firmware(struct device_node *target,
+			 struct device_node *fragment, void *data)
+{
+	struct overlay_info *info = data;
+	const char *firmware_name;
+	const char *firmware_path = info->firmware_path;
+	char *firmware;
+	int err;
+	struct firmware_mgr *mgr;
+
+	err = of_property_read_string(fragment,
+				      "firmware-name", &firmware_name);
+	if (err)
+		return 0;
+
+	mgr = of_node_get_mgr(target);
+	if (!mgr)
+		return -EINVAL;
+
+	firmware = basprintf("%s/%s", firmware_path, firmware_name);
+	if (!firmware)
+		return -ENOMEM;
+
+	err = firmwaremgr_load_file(mgr, firmware);
+
+	free(firmware);
+
+	return err;
+}
+
 static struct cdev_operations firmware_ops = {
 	.open = firmware_open,
 	.write = firmware_write,
@@ -210,3 +277,13 @@ int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *firmware)
 
 	return ret;
 }
+
+int firmware_load_overlay(struct device_node *overlay, const char *path)
+{
+	struct overlay_info info = {
+		.firmware_path = path,
+	};
+
+	return of_process_overlay(of_get_root_node(), overlay,
+				  load_firmware, &info);
+}
diff --git a/include/firmware.h b/include/firmware.h
index 284e0f9705..a3c2f9fe4a 100644
--- a/include/firmware.h
+++ b/include/firmware.h
@@ -34,11 +34,14 @@ struct firmware_mgr;
 int firmwaremgr_register(struct firmware_handler *);
 
 struct firmware_mgr *firmwaremgr_find(const char *);
+struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np);
 
 void firmwaremgr_list_handlers(void);
 
 int firmwaremgr_load_file(struct firmware_mgr *, const char *path);
 
+int firmware_load_overlay(struct device_node *overlay, const char *path);
+
 #define get_builtin_firmware(name, start, size) \
 	{							\
 		extern char _fw_##name##_start[];		\
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 6/7] blspec: load firmware if specified in dt overlay
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
                   ` (4 preceding siblings ...)
  2019-09-05 10:51 ` [PATCH 5/7] firmware: add support to load firmware from dt overlay Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  2019-09-05 10:51 ` [PATCH 7/7] commands: add of_overlay command for device tree overlays Michael Tretter
  6 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

If a device tree overlay referenced by the blspec depends on firmware,
try to load the firmware from the default Linux firmware search path
/lib/firmware in the about to be started rootfs.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 common/blspec.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/common/blspec.c b/common/blspec.c
index 6b15d36d67..d6f9337d9c 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -14,6 +14,7 @@
 
 #include <environment.h>
 #include <globalvar.h>
+#include <firmware.h>
 #include <readkey.h>
 #include <common.h>
 #include <driver.h>
@@ -49,6 +50,7 @@ static int blspec_apply_oftree_overlay(char *file, const char *abspath,
 	struct fdt_header *fdt;
 	struct device_node *overlay;
 	char *path;
+	char *firmware_path;
 
 	path = basprintf("%s/%s", abspath, file);
 
@@ -72,6 +74,20 @@ static int blspec_apply_oftree_overlay(char *file, const char *abspath,
 		goto out;
 	}
 
+	/*
+	 * Unfortunately the device tree overlay contains only the filename of
+	 * the firmware and relies on the firmware search paths to find the
+	 * actual file. Use /lib/firmware in the Linux root directory and hope
+	 * for the best.
+	 */
+	firmware_path = basprintf("%s/%s", abspath, "/lib/firmware");
+	ret = firmware_load_overlay(overlay, firmware_path);
+	free(firmware_path);
+	if (ret) {
+		of_delete_node(overlay);
+		goto out;
+	}
+
 	ret = of_register_overlay(overlay);
 	if (ret)
 		pr_warn("cannot register devicetree overlay \"%s\"\n", path);
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 7/7] commands: add of_overlay command for device tree overlays
  2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
                   ` (5 preceding siblings ...)
  2019-09-05 10:51 ` [PATCH 6/7] blspec: load firmware if specified in " Michael Tretter
@ 2019-09-05 10:51 ` Michael Tretter
  6 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-05 10:51 UTC (permalink / raw)
  To: barebox; +Cc: Michael Tretter

Add a new command "of_overlay" for applying device tree overlays,
because it does not really fit into any other device tree related
command and would create a mess with the command options.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
 commands/Kconfig      | 12 ++++++
 commands/Makefile     |  1 +
 commands/of_overlay.c | 89 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+)
 create mode 100644 commands/of_overlay.c

diff --git a/commands/Kconfig b/commands/Kconfig
index 039fd7d1ac..01d82031ad 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2088,6 +2088,18 @@ config CMD_OF_FIXUP_STATUS
 	  Register a fixup to enable or disable a device tree node.
 	  Nodes are enabled on default. Disabled with -d.
 
+config CMD_OF_OVERLAY
+	tristate
+	select OFTREE
+	prompt "of_overlay"
+	help
+	  of_overlay - register device tree overlay as fixup
+
+	  Usage: of_overlay [-S path] FILE
+
+	  Options:
+		  -S path	load firmware using this search path
+
 config CMD_OFTREE
 	tristate
 	select OFTREE
diff --git a/commands/Makefile b/commands/Makefile
index e69fb5046f..62ad9ce2b2 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_CMD_OF_NODE)	+= of_node.o
 obj-$(CONFIG_CMD_OF_DUMP)	+= of_dump.o
 obj-$(CONFIG_CMD_OF_DISPLAY_TIMINGS)	+= of_display_timings.o
 obj-$(CONFIG_CMD_OF_FIXUP_STATUS)	+= of_fixup_status.o
+obj-$(CONFIG_CMD_OF_OVERLAY)	+= of_overlay.o
 obj-$(CONFIG_CMD_MAGICVAR)	+= magicvar.o
 obj-$(CONFIG_CMD_IOMEM)		+= iomemport.o
 obj-$(CONFIG_CMD_LINUX_EXEC)	+= linux_exec.o
diff --git a/commands/of_overlay.c b/commands/of_overlay.c
new file mode 100644
index 0000000000..f929443bfe
--- /dev/null
+++ b/commands/of_overlay.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Michael Tretter <m.tretter@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <command.h>
+#include <common.h>
+#include <environment.h>
+#include <fdt.h>
+#include <firmware.h>
+#include <fs.h>
+#include <getopt.h>
+#include <libfile.h>
+#include <of.h>
+
+static int do_of_overlay(int argc, char *argv[])
+{
+	int opt, ret;
+	struct fdt_header *fdt;
+	struct device_node *overlay;
+	const char *search_path = NULL;
+
+	while ((opt = getopt(argc, argv, "S:")) > 0) {
+		switch (opt) {
+		case 'S':
+			search_path = optarg;
+			break;
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	if (argc != optind + 1)
+		return COMMAND_ERROR_USAGE;
+
+	fdt = read_file(argv[optind], NULL);
+	if (!fdt) {
+		printf("cannot read %s\n", argv[optind]);
+		return 1;
+	}
+
+	overlay = of_unflatten_dtb(fdt);
+	free(fdt);
+	if (IS_ERR(overlay))
+		return PTR_ERR(overlay);
+
+	if (search_path) {
+		ret = firmware_load_overlay(overlay, search_path);
+		if (ret)
+			goto err;
+	}
+
+	ret = of_register_overlay(overlay);
+	if (ret) {
+		printf("cannot apply oftree overlay: %s\n", strerror(-ret));
+		goto err;
+	}
+
+	return 0;
+
+err:
+	of_delete_node(overlay);
+	return ret;
+}
+
+BAREBOX_CMD_HELP_START(of_overlay)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-S path", "load firmware using this search path")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(of_overlay)
+	.cmd = do_of_overlay,
+	BAREBOX_CMD_DESC("register device tree overlay as fixup")
+	BAREBOX_CMD_OPTS("[-S path] FILE")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_of_overlay_help)
+BAREBOX_CMD_END
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7] of: add support for devicetree overlays
  2019-09-05 10:51 ` [PATCH 2/7] of: add support for devicetree overlays Michael Tretter
@ 2019-09-05 15:24   ` Ahmad Fatoum
  2019-09-06  7:15     ` Michael Tretter
  2019-09-06  7:29   ` Sascha Hauer
  1 sibling, 1 reply; 12+ messages in thread
From: Ahmad Fatoum @ 2019-09-05 15:24 UTC (permalink / raw)
  To: barebox



On 9/5/19 12:51 PM, Michael Tretter wrote:
> The devicetree overlay support is based on the Linux driver for device
> tree overlays, but many features that are not required in Barebox are
> left out.
> 
> Unlike Linux, which applies the overlay to the live devicetree, Barebox
> registers a fixup for the overlay which is applied with other fixups to
> whatever tree is fixed. This is necessary to apply the overlay to
> devicetrees that are passed to Linux, which might differ from the
> devicetree that is currently live in Barebox.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> ---
>  drivers/of/Kconfig    |   9 ++
>  drivers/of/Makefile   |   1 +
>  drivers/of/overlay.c  | 200 ++++++++++++++++++++++++++++++
>  drivers/of/resolver.c | 278 ++++++++++++++++++++++++++++++++++++++++++
>  include/of.h          |  20 +++
>  5 files changed, 508 insertions(+)
>  create mode 100644 drivers/of/overlay.c
>  create mode 100644 drivers/of/resolver.c
> 
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index 24cf4465a8..1bb6639c5a 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -50,3 +50,12 @@ config OF_BAREBOX_ENV_IN_FS
>  	help
>  	  Allow the devie tree configuration of the barebox environment path
>  	  to specify a file in filesystem, which will be mounted.
> +
> +config OF_OVERLAY
> +	select OFTREE
> +	bool "Devicetree overlays"
> +	help
> +	  Overlays allow to patch the devicetree. Unlike Linux, Barebox does
> +	  not patch the live devicetree, but applies the overlays as fixup to
> +	  the devicetree. Furthermore, overlays cannot be removed after they
> +	  have been applied.
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index ec43870061..9c6f8de814 100644
> --- a/drivers/of/Makefile
> +++ b/drivers/of/Makefile
> @@ -6,3 +6,4 @@ obj-y += partition.o
>  obj-y += of_net.o
>  obj-$(CONFIG_MTD) += of_mtd.o
>  obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
> +obj-$(CONFIG_OF_OVERLAY) += overlay.o resolver.o
> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
> new file mode 100644
> index 0000000000..b9283e6a90
> --- /dev/null
> +++ b/drivers/of/overlay.c
> @@ -0,0 +1,200 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Functions for working with device tree overlays
> + *
> + * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
> + * Copyright (C) 2012 Texas Instruments Inc.
> + * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
> + */
> +
> +#include <common.h>
> +#include <notifier.h>

Still needed?

> +#include <of.h>
> +#include <errno.h>
> +
> +static struct device_node *find_target(struct device_node *root,
> +				       struct device_node *fragment)
> +{
> +	struct device_node *node;
> +	const char *path;
> +	u32 phandle;
> +	int ret;
> +
> +	ret = of_property_read_u32(fragment, "target", &phandle);
> +	if (!ret) {
> +		node = of_find_node_by_phandle_from(phandle, root);
> +		if (!node)
> +			pr_err("fragment %pOF: phandle 0x%x not found\n",
> +			       fragment, phandle);
> +		return node;
> +	}
> +
> +	ret = of_property_read_string(fragment, "target-path", &path);
> +	if (!ret) {
> +		node = of_find_node_by_path_from(root, path);
> +		if (!node)
> +			pr_err("fragment %pOF: path '%s' not found\n",
> +			       fragment, path);
> +		return node;
> +	}
> +
> +	pr_err("fragment %pOF: no target property\n", fragment);
> +
> +	return NULL;
> +}
> +
> +static int of_overlay_apply(struct device_node *target,
> +			    const struct device_node *overlay)
> +{
> +	struct device_node *child;
> +	struct device_node *target_child;
> +	struct property *prop;
> +	int err;
> +
> +	if (target == NULL || overlay == NULL)
> +		return -EINVAL;
> +
> +	list_for_each_entry(prop, &overlay->properties, list) {
> +		if (of_prop_cmp(prop->name, "name") == 0)
> +			continue;
> +
> +		err = of_set_property(target, prop->name, prop->value,
> +				      prop->length, true);
> +		if (err)
> +			return err;
> +	}
> +
> +	for_each_child_of_node(overlay, child) {
> +		target_child = of_get_child_by_name(target, child->name);
> +		if (!target_child)
> +			target_child = of_new_node(target, child->name);
> +		if (!target_child)
> +			return -ENOMEM;
> +
> +		err = of_overlay_apply(target_child, child);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static char *of_overlay_fix_path(struct device_node *root,
> +				 struct device_node *overlay, const char *path)
> +{
> +	struct device_node *fragment;
> +	struct device_node *target;
> +	const char *path_tail;
> +	const char *prefix;
> +
> +	fragment = of_find_node_by_path_from(overlay, path);
> +	while ((fragment = of_get_parent(fragment)) != NULL) {
> +		if (of_get_child_by_name(fragment, "__overlay__"))
> +			break;
> +	}
> +	if (!fragment)
> +		return NULL;
> +
> +	target = find_target(root, fragment);
> +	if (!target)
> +		return NULL;
> +
> +	prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
> +	path_tail = path + strlen(prefix);
> +
> +	return basprintf("%s%s", target->full_name, path_tail);
> +}
> +
> +static int of_overlay_apply_symbols(struct device_node *root,
> +				    struct device_node *overlay)
> +{
> +	const char *old_path;
> +	char *new_path;
> +	struct property *prop;
> +	struct device_node *root_symbols;
> +	struct device_node *overlay_symbols;
> +
> +	root_symbols = of_get_child_by_name(root, "__symbols__");
> +	if (!root_symbols)
> +		return -EINVAL;
> +
> +	overlay_symbols = of_get_child_by_name(overlay, "__symbols__");
> +	if (!overlay_symbols)
> +		return -EINVAL;
> +
> +	list_for_each_entry(prop, &overlay_symbols->properties, list) {
> +		if (of_prop_cmp(prop->name, "name") == 0)
> +			continue;
> +
> +		old_path = of_property_get_value(prop);
> +		new_path = of_overlay_fix_path(root, overlay, old_path);
> +
> +		pr_debug("add symbol %s with new path %s\n",
> +			 prop->name, new_path);
> +		of_property_write_string(root_symbols, prop->name, new_path);
> +	}
> +
> +	return 0;
> +}
> +
> +static int of_overlay_apply_fragment(struct device_node *root,
> +				     struct device_node *fragment)
> +{
> +	struct device_node *target;
> +	struct device_node *overlay;
> +
> +	overlay = of_get_child_by_name(fragment, "__overlay__");
> +	if (!overlay)
> +		return 0;
> +
> +	target = find_target(root, fragment);
> +	if (!target)
> +		return -EINVAL;
> +
> +	return of_overlay_apply(target, overlay);
> +}
> +
> +/**
> + * Fix the passed root node using the device tree overlay in data
> + */
> +static int of_overlay_fixup(struct device_node *root, void *data)
> +{
> +	struct device_node *overlay = data;
> +	struct device_node *resolved;
> +	struct device_node *fragment;
> +	int err;
> +
> +	resolved = of_resolve_phandles(root, overlay);
> +	if (!resolved)
> +		return -EINVAL;
> +
> +	/* Copy symbols from resolved overlay to base device tree */
> +	err = of_overlay_apply_symbols(root, resolved);
> +	if (err)
> +		pr_warn("failed to copy symbols from overlay");
> +
> +	/* Copy nodes and properties from resolved overlay to root */
> +	for_each_child_of_node(resolved, fragment) {
> +		err = of_overlay_apply_fragment(root, fragment);
> +		if (err)
> +			pr_warn("failed to apply %s", fragment->name);
> +	}
> +
> +	of_delete_node(resolved);
> +
> +	return 0;
> +}
> +
> +/**
> + * Register a devicetree overlay
> + *
> + * The overlay is not applied to the live device tree, but registered as fixup
> + * for the fixed up device tree. Therefore, drivers relying on the overlay
> + * must use the fixed device tree.
> + */
> +int of_register_overlay(struct device_node *overlay)
> +{
> +	of_register_fixup(of_overlay_fixup, overlay);
> +
> +	return 0;
> +}
> diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
> new file mode 100644
> index 0000000000..574c75cb4b
> --- /dev/null
> +++ b/drivers/of/resolver.c
> @@ -0,0 +1,278 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Functions for dealing with DT resolution
> + *
> + * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
> + * Copyright (C) 2012 Texas Instruments Inc.
> + * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
> + */
> +
> +#include <common.h>
> +#include <of.h>
> +#include <errno.h>
> +
> +/**
> + * Recursively update phandles in overlay by adding delta
> + */
> +static void adjust_overlay_phandles(struct device_node *overlay, int delta)
> +{
> +	struct device_node *child;
> +	struct property *prop;
> +
> +	if (overlay->phandle != 0)
> +		overlay->phandle += delta;
> +
> +	list_for_each_entry(prop, &overlay->properties, list) {
> +		if (of_prop_cmp(prop->name, "phandle") != 0 &&
> +		    of_prop_cmp(prop->name, "linux,phandle") != 0)
> +			continue;
> +		if (prop->length < 4)
> +			continue;
> +
> +		be32_add_cpu(prop->value, delta);
> +	}
> +
> +	for_each_child_of_node(overlay, child)
> +		adjust_overlay_phandles(child, delta);
> +}
> +
> +/**
> + * Update all unresolved phandles in the overlay using prop_fixup
> + *
> + * prop_fixup contains a list of tuples of path:property_name:offset, each of
> + * which refers to a property that is phandle to a node in the base
> + * devicetree.
> + */
> +static int update_usages_of_a_phandle_reference(struct device_node *overlay,
> +						struct property *prop_fixup,
> +						phandle phandle)
> +{
> +	struct device_node *refnode;
> +	struct property *prop;
> +	char *value, *cur, *end, *node_path, *prop_name, *s;
> +	int offset, len;
> +	int err = 0;
> +
> +	pr_debug("resolve references to %s to phandle 0x%x\n",
> +		 prop_fixup->name, phandle);
> +
> +	value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
> +	if (!value)
> +		return -ENOMEM;
> +
> +	end = value + prop_fixup->length;
> +	for (cur = value; cur < end; cur += len + 1) {
> +		len = strlen(cur);
> +
> +		node_path = cur;
> +		s = strchr(cur, ':');
> +		if (!s) {
> +			err = -EINVAL;
> +			goto err_fail;
> +		}
> +		*s++ = '\0';
> +
> +		prop_name = s;
> +		s = strchr(s, ':');
> +		if (!s) {
> +			err = -EINVAL;
> +			goto err_fail;
> +		}
> +		*s++ = '\0';
> +
> +		err = kstrtoint(s, 10, &offset);
> +		if (err)
> +			goto err_fail;
> +
> +		refnode = of_find_node_by_path_from(overlay, node_path);
> +		if (!refnode)
> +			continue;
> +
> +		prop = of_find_property(refnode, prop_name, NULL);
> +		if (!prop) {
> +			err = -ENOENT;
> +			goto err_fail;
> +		}
> +
> +		if (offset < 0 || offset + sizeof(__be32) > prop->length) {
> +			err = -EINVAL;
> +			goto err_fail;
> +		}
> +
> +		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
> +	}
> +
> +err_fail:
> +	kfree(value);
> +
> +	if (err)
> +		pr_debug("failed to resolve references to %s\n",
> +			 prop_fixup->name);
> +
> +	return err;
> +}
> +
> +/*
> + * Adjust the local phandle references by the given phandle delta.
> + *
> + * Subtree @local_fixups, which is overlay node __local_fixups__,
> + * mirrors the fragment node structure at the root of the overlay.
> + *
> + * For each property in the fragments that contains a phandle reference,
> + * @local_fixups has a property of the same name that contains a list
> + * of offsets of the phandle reference(s) within the respective property
> + * value(s).  The values at these offsets will be fixed up.
> + */
> +static int adjust_local_phandle_references(struct device_node *local_fixups,
> +		struct device_node *overlay, int phandle_delta)
> +{
> +	struct device_node *child, *overlay_child;
> +	struct property *prop_fix, *prop;
> +	int err, i, count;
> +	unsigned int off;
> +
> +	if (!local_fixups)
> +		return 0;
> +
> +	list_for_each_entry(prop_fix, &local_fixups->properties, list) {
> +		if (!of_prop_cmp(prop_fix->name, "name") ||
> +		    !of_prop_cmp(prop_fix->name, "phandle") ||
> +		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
> +			continue;
> +
> +		if ((prop_fix->length % sizeof(__be32)) != 0 ||
> +		    prop_fix->length == 0)
> +			return -EINVAL;
> +		count = prop_fix->length / sizeof(__be32);
> +
> +		prop = of_find_property(overlay, prop_fix->name, NULL);
> +		if (!prop)
> +			return -EINVAL;
> +
> +		for (i = 0; i < count; i++) {
> +			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
> +			if ((off + sizeof(__be32)) > prop->length)
> +				return -EINVAL;
> +
> +			be32_add_cpu(prop->value + off, phandle_delta);
> +		}
> +	}
> +
> +	for_each_child_of_node(local_fixups, child) {
> +		for_each_child_of_node(overlay, overlay_child)
> +			if (!of_node_cmp(child->name, overlay_child->name))
> +				break;
> +		if (!overlay_child)
> +			return -EINVAL;
> +
> +		err = adjust_local_phandle_references(child, overlay_child,
> +				phandle_delta);
> +		if (err)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * of_resolve_phandles - Resolve phandles in overlay based on root
> + *
> + * Rename phandles in overlay to avoid conflicts with the base devicetree and
> + * replace all phandles in the overlay with their renamed versions. Resolve
> + * phandles referring to nodes in the base devicetree with the phandle from
> + * the base devicetree.
> + *
> + * Returns a new device_node with resolved phandles which must be deleted by
> + * the caller of this function.
> + */
> +struct device_node *of_resolve_phandles(struct device_node *root,
> +					const struct device_node *overlay)
> +{
> +	struct device_node *result;
> +	struct device_node *local_fixups;
> +	struct device_node *refnode;
> +	struct device_node *symbols;
> +	struct device_node *overlay_fixups;
> +	struct property *prop;
> +	const char *refpath;
> +	phandle delta;
> +	int err;
> +
> +	result = of_copy_node(NULL, overlay);
> +	if (!result)
> +		return NULL;
> +
> +	delta = of_get_tree_max_phandle(root) + 1;
> +
> +	/*
> +	 * Rename the phandles in the devicetree overlay to prevent conflicts
> +	 * with the phandles in the base devicetree.
> +	 */
> +	adjust_overlay_phandles(result, delta);
> +
> +	/*
> +	 * __local_fixups__ contains all locations in the overlay that refer
> +	 * to a phandle defined in the overlay. We must update the references,
> +	 * because we just adjusted the definitions.
> +	 */
> +	local_fixups = of_find_node_by_name(result, "__local_fixups__");
> +	err = adjust_local_phandle_references(local_fixups, result, delta);
> +	if (err) {
> +		pr_err("failed to fix phandles in overlay\n");
> +		goto err;
> +	}
> +
> +	/*
> +	 * __fixups__ contains all locations in the overlay that refer to a
> +	 * phandle that is not defined in the overlay and should be defined in
> +	 * the base device tree. We must update the references, because they
> +	 * are otherwise undefined.
> +	 */
> +	overlay_fixups = of_find_node_by_name(result, "__fixups__");
> +	if (!overlay_fixups) {
> +		pr_debug("overlay does not contain phandles to base devicetree\n");
> +		goto out;
> +	}
> +
> +	symbols = of_find_node_by_path_from(root, "/__symbols__");
> +	if (!symbols) {
> +		pr_err("__symbols__ missing from base devicetree\n");
> +		goto err;
> +	}
> +
> +	list_for_each_entry(prop, &overlay_fixups->properties, list) {
> +		if (!of_prop_cmp(prop->name, "name"))
> +			continue;
> +
> +		err = of_property_read_string(symbols, prop->name, &refpath);
> +		if (err) {
> +			pr_err("cannot find node %s in base devicetree\n",
> +			       prop->name);
> +			goto err;
> +		}
> +
> +		refnode = of_find_node_by_path_from(root, refpath);
> +		if (!refnode) {
> +			pr_err("cannot find path %s in base devicetree\n",
> +			       refpath);
> +			err = -EINVAL;
> +			goto err;
> +		}
> +
> +		err = update_usages_of_a_phandle_reference(result, prop,
> +							   refnode->phandle);
> +		if (err) {
> +			pr_err("failed to update phandles for %s in overlay",
> +			       prop->name);
> +			goto err;
> +		}
> +	}
> +
> +out:
> +	return result;
> +err:
> +	of_delete_node(result);
> +
> +	return NULL;
> +
> +}
> diff --git a/include/of.h b/include/of.h
> index b5f54dd4e5..ac80dc3247 100644
> --- a/include/of.h
> +++ b/include/of.h
> @@ -3,6 +3,7 @@
>  
>  #include <fdt.h>
>  #include <errno.h>
> +#include <notifier.h>

Ditto.

>  #include <linux/types.h>
>  #include <linux/list.h>
>  #include <asm/byteorder.h>
> @@ -870,4 +871,23 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
>  
>  	return node;
>  }
> +
> +#ifdef CONFIG_OF_OVERLAY
> +struct device_node *of_resolve_phandles(struct device_node *root,
> +					const struct device_node *overlay);
> +int of_register_overlay(struct device_node *overlay);
> +#else
> +static inline struct device_node *of_resolve_phandles(struct device_node *root,
> +					const struct device_node *overlay)
> +{
> +	return NULL;
> +}
> +
> +static inline int of_register_overlay(struct device_node *overlay)
> +{
> +	return -ENOSYS;
> +}
> +
> +#endif
> +
>  #endif /* __OF_H */
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7] of: add support for devicetree overlays
  2019-09-05 15:24   ` Ahmad Fatoum
@ 2019-09-06  7:15     ` Michael Tretter
  0 siblings, 0 replies; 12+ messages in thread
From: Michael Tretter @ 2019-09-06  7:15 UTC (permalink / raw)
  To: Ahmad Fatoum, barebox

On Thu, 05 Sep 2019 17:24:08 +0200, Ahmad Fatoum wrote:
> On 9/5/19 12:51 PM, Michael Tretter wrote:
> > The devicetree overlay support is based on the Linux driver for device
> > tree overlays, but many features that are not required in Barebox are
> > left out.
> > 
> > Unlike Linux, which applies the overlay to the live devicetree, Barebox
> > registers a fixup for the overlay which is applied with other fixups to
> > whatever tree is fixed. This is necessary to apply the overlay to
> > devicetrees that are passed to Linux, which might differ from the
> > devicetree that is currently live in Barebox.
> > 
> > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> > ---
> >  drivers/of/Kconfig    |   9 ++
> >  drivers/of/Makefile   |   1 +
> >  drivers/of/overlay.c  | 200 ++++++++++++++++++++++++++++++
> >  drivers/of/resolver.c | 278 ++++++++++++++++++++++++++++++++++++++++++
> >  include/of.h          |  20 +++
> >  5 files changed, 508 insertions(+)
> >  create mode 100644 drivers/of/overlay.c
> >  create mode 100644 drivers/of/resolver.c
> > 
> > diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> > index 24cf4465a8..1bb6639c5a 100644
> > --- a/drivers/of/Kconfig
> > +++ b/drivers/of/Kconfig
> > @@ -50,3 +50,12 @@ config OF_BAREBOX_ENV_IN_FS
> >  	help
> >  	  Allow the devie tree configuration of the barebox environment path
> >  	  to specify a file in filesystem, which will be mounted.
> > +
> > +config OF_OVERLAY
> > +	select OFTREE
> > +	bool "Devicetree overlays"
> > +	help
> > +	  Overlays allow to patch the devicetree. Unlike Linux, Barebox does
> > +	  not patch the live devicetree, but applies the overlays as fixup to
> > +	  the devicetree. Furthermore, overlays cannot be removed after they
> > +	  have been applied.
> > diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> > index ec43870061..9c6f8de814 100644
> > --- a/drivers/of/Makefile
> > +++ b/drivers/of/Makefile
> > @@ -6,3 +6,4 @@ obj-y += partition.o
> >  obj-y += of_net.o
> >  obj-$(CONFIG_MTD) += of_mtd.o
> >  obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
> > +obj-$(CONFIG_OF_OVERLAY) += overlay.o resolver.o
> > diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
> > new file mode 100644
> > index 0000000000..b9283e6a90
> > --- /dev/null
> > +++ b/drivers/of/overlay.c
> > @@ -0,0 +1,200 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Functions for working with device tree overlays
> > + *
> > + * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
> > + * Copyright (C) 2012 Texas Instruments Inc.
> > + * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
> > + */
> > +
> > +#include <common.h>
> > +#include <notifier.h>  
> 
> Still needed?

Oops. I missed this. Thanks for the catch. Will fix in a v2.

> 
> > +#include <of.h>
> > +#include <errno.h>
> > +
> > +static struct device_node *find_target(struct device_node *root,
> > +				       struct device_node *fragment)
> > +{
> > +	struct device_node *node;
> > +	const char *path;
> > +	u32 phandle;
> > +	int ret;
> > +
> > +	ret = of_property_read_u32(fragment, "target", &phandle);
> > +	if (!ret) {
> > +		node = of_find_node_by_phandle_from(phandle, root);
> > +		if (!node)
> > +			pr_err("fragment %pOF: phandle 0x%x not found\n",
> > +			       fragment, phandle);
> > +		return node;
> > +	}
> > +
> > +	ret = of_property_read_string(fragment, "target-path", &path);
> > +	if (!ret) {
> > +		node = of_find_node_by_path_from(root, path);
> > +		if (!node)
> > +			pr_err("fragment %pOF: path '%s' not found\n",
> > +			       fragment, path);
> > +		return node;
> > +	}
> > +
> > +	pr_err("fragment %pOF: no target property\n", fragment);
> > +
> > +	return NULL;
> > +}
> > +
> > +static int of_overlay_apply(struct device_node *target,
> > +			    const struct device_node *overlay)
> > +{
> > +	struct device_node *child;
> > +	struct device_node *target_child;
> > +	struct property *prop;
> > +	int err;
> > +
> > +	if (target == NULL || overlay == NULL)
> > +		return -EINVAL;
> > +
> > +	list_for_each_entry(prop, &overlay->properties, list) {
> > +		if (of_prop_cmp(prop->name, "name") == 0)
> > +			continue;
> > +
> > +		err = of_set_property(target, prop->name, prop->value,
> > +				      prop->length, true);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	for_each_child_of_node(overlay, child) {
> > +		target_child = of_get_child_by_name(target, child->name);
> > +		if (!target_child)
> > +			target_child = of_new_node(target, child->name);
> > +		if (!target_child)
> > +			return -ENOMEM;
> > +
> > +		err = of_overlay_apply(target_child, child);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static char *of_overlay_fix_path(struct device_node *root,
> > +				 struct device_node *overlay, const char *path)
> > +{
> > +	struct device_node *fragment;
> > +	struct device_node *target;
> > +	const char *path_tail;
> > +	const char *prefix;
> > +
> > +	fragment = of_find_node_by_path_from(overlay, path);
> > +	while ((fragment = of_get_parent(fragment)) != NULL) {
> > +		if (of_get_child_by_name(fragment, "__overlay__"))
> > +			break;
> > +	}
> > +	if (!fragment)
> > +		return NULL;
> > +
> > +	target = find_target(root, fragment);
> > +	if (!target)
> > +		return NULL;
> > +
> > +	prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
> > +	path_tail = path + strlen(prefix);
> > +
> > +	return basprintf("%s%s", target->full_name, path_tail);
> > +}
> > +
> > +static int of_overlay_apply_symbols(struct device_node *root,
> > +				    struct device_node *overlay)
> > +{
> > +	const char *old_path;
> > +	char *new_path;
> > +	struct property *prop;
> > +	struct device_node *root_symbols;
> > +	struct device_node *overlay_symbols;
> > +
> > +	root_symbols = of_get_child_by_name(root, "__symbols__");
> > +	if (!root_symbols)
> > +		return -EINVAL;
> > +
> > +	overlay_symbols = of_get_child_by_name(overlay, "__symbols__");
> > +	if (!overlay_symbols)
> > +		return -EINVAL;
> > +
> > +	list_for_each_entry(prop, &overlay_symbols->properties, list) {
> > +		if (of_prop_cmp(prop->name, "name") == 0)
> > +			continue;
> > +
> > +		old_path = of_property_get_value(prop);
> > +		new_path = of_overlay_fix_path(root, overlay, old_path);
> > +
> > +		pr_debug("add symbol %s with new path %s\n",
> > +			 prop->name, new_path);
> > +		of_property_write_string(root_symbols, prop->name, new_path);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int of_overlay_apply_fragment(struct device_node *root,
> > +				     struct device_node *fragment)
> > +{
> > +	struct device_node *target;
> > +	struct device_node *overlay;
> > +
> > +	overlay = of_get_child_by_name(fragment, "__overlay__");
> > +	if (!overlay)
> > +		return 0;
> > +
> > +	target = find_target(root, fragment);
> > +	if (!target)
> > +		return -EINVAL;
> > +
> > +	return of_overlay_apply(target, overlay);
> > +}
> > +
> > +/**
> > + * Fix the passed root node using the device tree overlay in data
> > + */
> > +static int of_overlay_fixup(struct device_node *root, void *data)
> > +{
> > +	struct device_node *overlay = data;
> > +	struct device_node *resolved;
> > +	struct device_node *fragment;
> > +	int err;
> > +
> > +	resolved = of_resolve_phandles(root, overlay);
> > +	if (!resolved)
> > +		return -EINVAL;
> > +
> > +	/* Copy symbols from resolved overlay to base device tree */
> > +	err = of_overlay_apply_symbols(root, resolved);
> > +	if (err)
> > +		pr_warn("failed to copy symbols from overlay");
> > +
> > +	/* Copy nodes and properties from resolved overlay to root */
> > +	for_each_child_of_node(resolved, fragment) {
> > +		err = of_overlay_apply_fragment(root, fragment);
> > +		if (err)
> > +			pr_warn("failed to apply %s", fragment->name);
> > +	}
> > +
> > +	of_delete_node(resolved);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * Register a devicetree overlay
> > + *
> > + * The overlay is not applied to the live device tree, but registered as fixup
> > + * for the fixed up device tree. Therefore, drivers relying on the overlay
> > + * must use the fixed device tree.
> > + */
> > +int of_register_overlay(struct device_node *overlay)
> > +{
> > +	of_register_fixup(of_overlay_fixup, overlay);
> > +
> > +	return 0;
> > +}
> > diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
> > new file mode 100644
> > index 0000000000..574c75cb4b
> > --- /dev/null
> > +++ b/drivers/of/resolver.c
> > @@ -0,0 +1,278 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Functions for dealing with DT resolution
> > + *
> > + * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
> > + * Copyright (C) 2012 Texas Instruments Inc.
> > + * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
> > + */
> > +
> > +#include <common.h>
> > +#include <of.h>
> > +#include <errno.h>
> > +
> > +/**
> > + * Recursively update phandles in overlay by adding delta
> > + */
> > +static void adjust_overlay_phandles(struct device_node *overlay, int delta)
> > +{
> > +	struct device_node *child;
> > +	struct property *prop;
> > +
> > +	if (overlay->phandle != 0)
> > +		overlay->phandle += delta;
> > +
> > +	list_for_each_entry(prop, &overlay->properties, list) {
> > +		if (of_prop_cmp(prop->name, "phandle") != 0 &&
> > +		    of_prop_cmp(prop->name, "linux,phandle") != 0)
> > +			continue;
> > +		if (prop->length < 4)
> > +			continue;
> > +
> > +		be32_add_cpu(prop->value, delta);
> > +	}
> > +
> > +	for_each_child_of_node(overlay, child)
> > +		adjust_overlay_phandles(child, delta);
> > +}
> > +
> > +/**
> > + * Update all unresolved phandles in the overlay using prop_fixup
> > + *
> > + * prop_fixup contains a list of tuples of path:property_name:offset, each of
> > + * which refers to a property that is phandle to a node in the base
> > + * devicetree.
> > + */
> > +static int update_usages_of_a_phandle_reference(struct device_node *overlay,
> > +						struct property *prop_fixup,
> > +						phandle phandle)
> > +{
> > +	struct device_node *refnode;
> > +	struct property *prop;
> > +	char *value, *cur, *end, *node_path, *prop_name, *s;
> > +	int offset, len;
> > +	int err = 0;
> > +
> > +	pr_debug("resolve references to %s to phandle 0x%x\n",
> > +		 prop_fixup->name, phandle);
> > +
> > +	value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
> > +	if (!value)
> > +		return -ENOMEM;
> > +
> > +	end = value + prop_fixup->length;
> > +	for (cur = value; cur < end; cur += len + 1) {
> > +		len = strlen(cur);
> > +
> > +		node_path = cur;
> > +		s = strchr(cur, ':');
> > +		if (!s) {
> > +			err = -EINVAL;
> > +			goto err_fail;
> > +		}
> > +		*s++ = '\0';
> > +
> > +		prop_name = s;
> > +		s = strchr(s, ':');
> > +		if (!s) {
> > +			err = -EINVAL;
> > +			goto err_fail;
> > +		}
> > +		*s++ = '\0';
> > +
> > +		err = kstrtoint(s, 10, &offset);
> > +		if (err)
> > +			goto err_fail;
> > +
> > +		refnode = of_find_node_by_path_from(overlay, node_path);
> > +		if (!refnode)
> > +			continue;
> > +
> > +		prop = of_find_property(refnode, prop_name, NULL);
> > +		if (!prop) {
> > +			err = -ENOENT;
> > +			goto err_fail;
> > +		}
> > +
> > +		if (offset < 0 || offset + sizeof(__be32) > prop->length) {
> > +			err = -EINVAL;
> > +			goto err_fail;
> > +		}
> > +
> > +		*(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
> > +	}
> > +
> > +err_fail:
> > +	kfree(value);
> > +
> > +	if (err)
> > +		pr_debug("failed to resolve references to %s\n",
> > +			 prop_fixup->name);
> > +
> > +	return err;
> > +}
> > +
> > +/*
> > + * Adjust the local phandle references by the given phandle delta.
> > + *
> > + * Subtree @local_fixups, which is overlay node __local_fixups__,
> > + * mirrors the fragment node structure at the root of the overlay.
> > + *
> > + * For each property in the fragments that contains a phandle reference,
> > + * @local_fixups has a property of the same name that contains a list
> > + * of offsets of the phandle reference(s) within the respective property
> > + * value(s).  The values at these offsets will be fixed up.
> > + */
> > +static int adjust_local_phandle_references(struct device_node *local_fixups,
> > +		struct device_node *overlay, int phandle_delta)
> > +{
> > +	struct device_node *child, *overlay_child;
> > +	struct property *prop_fix, *prop;
> > +	int err, i, count;
> > +	unsigned int off;
> > +
> > +	if (!local_fixups)
> > +		return 0;
> > +
> > +	list_for_each_entry(prop_fix, &local_fixups->properties, list) {
> > +		if (!of_prop_cmp(prop_fix->name, "name") ||
> > +		    !of_prop_cmp(prop_fix->name, "phandle") ||
> > +		    !of_prop_cmp(prop_fix->name, "linux,phandle"))
> > +			continue;
> > +
> > +		if ((prop_fix->length % sizeof(__be32)) != 0 ||
> > +		    prop_fix->length == 0)
> > +			return -EINVAL;
> > +		count = prop_fix->length / sizeof(__be32);
> > +
> > +		prop = of_find_property(overlay, prop_fix->name, NULL);
> > +		if (!prop)
> > +			return -EINVAL;
> > +
> > +		for (i = 0; i < count; i++) {
> > +			off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
> > +			if ((off + sizeof(__be32)) > prop->length)
> > +				return -EINVAL;
> > +
> > +			be32_add_cpu(prop->value + off, phandle_delta);
> > +		}
> > +	}
> > +
> > +	for_each_child_of_node(local_fixups, child) {
> > +		for_each_child_of_node(overlay, overlay_child)
> > +			if (!of_node_cmp(child->name, overlay_child->name))
> > +				break;
> > +		if (!overlay_child)
> > +			return -EINVAL;
> > +
> > +		err = adjust_local_phandle_references(child, overlay_child,
> > +				phandle_delta);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * of_resolve_phandles - Resolve phandles in overlay based on root
> > + *
> > + * Rename phandles in overlay to avoid conflicts with the base devicetree and
> > + * replace all phandles in the overlay with their renamed versions. Resolve
> > + * phandles referring to nodes in the base devicetree with the phandle from
> > + * the base devicetree.
> > + *
> > + * Returns a new device_node with resolved phandles which must be deleted by
> > + * the caller of this function.
> > + */
> > +struct device_node *of_resolve_phandles(struct device_node *root,
> > +					const struct device_node *overlay)
> > +{
> > +	struct device_node *result;
> > +	struct device_node *local_fixups;
> > +	struct device_node *refnode;
> > +	struct device_node *symbols;
> > +	struct device_node *overlay_fixups;
> > +	struct property *prop;
> > +	const char *refpath;
> > +	phandle delta;
> > +	int err;
> > +
> > +	result = of_copy_node(NULL, overlay);
> > +	if (!result)
> > +		return NULL;
> > +
> > +	delta = of_get_tree_max_phandle(root) + 1;
> > +
> > +	/*
> > +	 * Rename the phandles in the devicetree overlay to prevent conflicts
> > +	 * with the phandles in the base devicetree.
> > +	 */
> > +	adjust_overlay_phandles(result, delta);
> > +
> > +	/*
> > +	 * __local_fixups__ contains all locations in the overlay that refer
> > +	 * to a phandle defined in the overlay. We must update the references,
> > +	 * because we just adjusted the definitions.
> > +	 */
> > +	local_fixups = of_find_node_by_name(result, "__local_fixups__");
> > +	err = adjust_local_phandle_references(local_fixups, result, delta);
> > +	if (err) {
> > +		pr_err("failed to fix phandles in overlay\n");
> > +		goto err;
> > +	}
> > +
> > +	/*
> > +	 * __fixups__ contains all locations in the overlay that refer to a
> > +	 * phandle that is not defined in the overlay and should be defined in
> > +	 * the base device tree. We must update the references, because they
> > +	 * are otherwise undefined.
> > +	 */
> > +	overlay_fixups = of_find_node_by_name(result, "__fixups__");
> > +	if (!overlay_fixups) {
> > +		pr_debug("overlay does not contain phandles to base devicetree\n");
> > +		goto out;
> > +	}
> > +
> > +	symbols = of_find_node_by_path_from(root, "/__symbols__");
> > +	if (!symbols) {
> > +		pr_err("__symbols__ missing from base devicetree\n");
> > +		goto err;
> > +	}
> > +
> > +	list_for_each_entry(prop, &overlay_fixups->properties, list) {
> > +		if (!of_prop_cmp(prop->name, "name"))
> > +			continue;
> > +
> > +		err = of_property_read_string(symbols, prop->name, &refpath);
> > +		if (err) {
> > +			pr_err("cannot find node %s in base devicetree\n",
> > +			       prop->name);
> > +			goto err;
> > +		}
> > +
> > +		refnode = of_find_node_by_path_from(root, refpath);
> > +		if (!refnode) {
> > +			pr_err("cannot find path %s in base devicetree\n",
> > +			       refpath);
> > +			err = -EINVAL;
> > +			goto err;
> > +		}
> > +
> > +		err = update_usages_of_a_phandle_reference(result, prop,
> > +							   refnode->phandle);
> > +		if (err) {
> > +			pr_err("failed to update phandles for %s in overlay",
> > +			       prop->name);
> > +			goto err;
> > +		}
> > +	}
> > +
> > +out:
> > +	return result;
> > +err:
> > +	of_delete_node(result);
> > +
> > +	return NULL;
> > +
> > +}
> > diff --git a/include/of.h b/include/of.h
> > index b5f54dd4e5..ac80dc3247 100644
> > --- a/include/of.h
> > +++ b/include/of.h
> > @@ -3,6 +3,7 @@
> >  
> >  #include <fdt.h>
> >  #include <errno.h>
> > +#include <notifier.h>  
> 
> Ditto.

This one as well.

Michael

> 
> >  #include <linux/types.h>
> >  #include <linux/list.h>
> >  #include <asm/byteorder.h>
> > @@ -870,4 +871,23 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
> >  
> >  	return node;
> >  }
> > +
> > +#ifdef CONFIG_OF_OVERLAY
> > +struct device_node *of_resolve_phandles(struct device_node *root,
> > +					const struct device_node *overlay);
> > +int of_register_overlay(struct device_node *overlay);
> > +#else
> > +static inline struct device_node *of_resolve_phandles(struct device_node *root,
> > +					const struct device_node *overlay)
> > +{
> > +	return NULL;
> > +}
> > +
> > +static inline int of_register_overlay(struct device_node *overlay)
> > +{
> > +	return -ENOSYS;
> > +}
> > +
> > +#endif
> > +
> >  #endif /* __OF_H */
> >   
> 

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/7] of: add support for devicetree overlays
  2019-09-05 10:51 ` [PATCH 2/7] of: add support for devicetree overlays Michael Tretter
  2019-09-05 15:24   ` Ahmad Fatoum
@ 2019-09-06  7:29   ` Sascha Hauer
  1 sibling, 0 replies; 12+ messages in thread
From: Sascha Hauer @ 2019-09-06  7:29 UTC (permalink / raw)
  To: Michael Tretter; +Cc: barebox

On Thu, Sep 05, 2019 at 12:51:37PM +0200, Michael Tretter wrote:
> The devicetree overlay support is based on the Linux driver for device
> tree overlays, but many features that are not required in Barebox are
> left out.
> 
> Unlike Linux, which applies the overlay to the live devicetree, Barebox
> registers a fixup for the overlay which is applied with other fixups to
> whatever tree is fixed. This is necessary to apply the overlay to
> devicetrees that are passed to Linux, which might differ from the
> devicetree that is currently live in Barebox.

Even though we usually apply the overlay as a fixup I would expect some
(exported) function to apply this overlay to that devicetree *now*.

> +	ret = of_property_read_string(fragment, "target-path", &path);
> +	if (!ret) {
> +		node = of_find_node_by_path_from(root, path);
> +		if (!node)
> +			pr_err("fragment %pOF: path '%s' not found\n",
> +			       fragment, path);
> +		return node;
> +	}
> +
> +	pr_err("fragment %pOF: no target property\n", fragment);

You use pr_* functions, so please also add a pr_fmt to give the messages
some more context.

Sascha


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/7] blspec: add support for devicetree overlays
  2019-09-05 10:51 ` [PATCH 3/7] blspec: " Michael Tretter
@ 2019-09-06  7:33   ` Sascha Hauer
  0 siblings, 0 replies; 12+ messages in thread
From: Sascha Hauer @ 2019-09-06  7:33 UTC (permalink / raw)
  To: Michael Tretter; +Cc: barebox

On Thu, Sep 05, 2019 at 12:51:38PM +0200, Michael Tretter wrote:
> Read the devicetree-overlay property from the blspec entry and register
> the overlays when booting the blspec entry.
> 
> Do not fail the boot if an overlay cannot be loaded, because if Linux
> fails to boot without an overlay, the base device tree is broken.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> ---
>  Documentation/user/booting-linux.rst |  4 ++
>  common/blspec.c                      | 59 ++++++++++++++++++++++++++++
>  2 files changed, 63 insertions(+)
> 
> diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst
> index 437f4e80ca..12cd505e71 100644
> --- a/Documentation/user/booting-linux.rst
> +++ b/Documentation/user/booting-linux.rst
> @@ -232,6 +232,10 @@ device where the entry is found on. This makes it possible to use the same rootf
>  image on different devices without having to specify a different root= option each
>  time.
>  
> +Additionally to the options defined in the original spec, Barebox has the
> +``devicetree-overlay`` option. This is a string value that refer to overlays
> +that will be applied to the device tree before passing it to Linux.
> +
>  Network boot
>  ------------
>  
> diff --git a/common/blspec.c b/common/blspec.c
> index 66e5033e35..6b15d36d67 100644
> --- a/common/blspec.c
> +++ b/common/blspec.c
> @@ -42,6 +42,60 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
>  			val ? strlen(val) + 1 : 0, 1);
>  }
>  
> +static int blspec_apply_oftree_overlay(char *file, const char *abspath,
> +				       int dryrun)
> +{
> +	int ret;
> +	struct fdt_header *fdt;
> +	struct device_node *overlay;
> +	char *path;
> +
> +	path = basprintf("%s/%s", abspath, file);
> +
> +	fdt = read_file(path, NULL);

This memory is never freed.

> +	if (!fdt) {
> +		pr_warn("unable to read \"%s\"\n", path);
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	overlay = of_unflatten_dtb(fdt);
> +	if (IS_ERR(overlay)) {
> +		printf("\"%s\" is not a valid devicetree\n", path);
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (dryrun) {
> +		pr_info("dry run: skip overlay %s\n", path);
> +		of_delete_node(overlay);
> +		goto out;
> +	}
> +
> +	ret = of_register_overlay(overlay);
> +	if (ret)
> +		pr_warn("cannot register devicetree overlay \"%s\"\n", path);

You do not free the overlay in the error path. What happens to this
memory when succesful? Should of_register_overlay() make a copy or free
it when succesfully fixed up?

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-09-06  7:33 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-05 10:51 [PATCH 0/7] Device Tree Overlay Support Michael Tretter
2019-09-05 10:51 ` [PATCH 1/7] dtc: add -@ option to enable __symbols__ Michael Tretter
2019-09-05 10:51 ` [PATCH 2/7] of: add support for devicetree overlays Michael Tretter
2019-09-05 15:24   ` Ahmad Fatoum
2019-09-06  7:15     ` Michael Tretter
2019-09-06  7:29   ` Sascha Hauer
2019-09-05 10:51 ` [PATCH 3/7] blspec: " Michael Tretter
2019-09-06  7:33   ` Sascha Hauer
2019-09-05 10:51 ` [PATCH 4/7] of: add iterator for overlays Michael Tretter
2019-09-05 10:51 ` [PATCH 5/7] firmware: add support to load firmware from dt overlay Michael Tretter
2019-09-05 10:51 ` [PATCH 6/7] blspec: load firmware if specified in " Michael Tretter
2019-09-05 10:51 ` [PATCH 7/7] commands: add of_overlay command for device tree overlays Michael Tretter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox