From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Subject: Re: [PATCH 2/7] of: add support for devicetree overlays
Date: Thu, 5 Sep 2019 17:24:08 +0200 [thread overview]
Message-ID: <0621ffd7-a66a-6aed-ecdd-48e4e7fff197@pengutronix.de> (raw)
In-Reply-To: <20190905105142.13681-3-m.tretter@pengutronix.de>
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
next prev parent reply other threads:[~2019-09-05 15:24 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=0621ffd7-a66a-6aed-ecdd-48e4e7fff197@pengutronix.de \
--to=a.fatoum@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox