mirror of
https://github.com/home-assistant/operating-system.git
synced 2025-07-24 13:36:31 +00:00
Bugfix missing files
This commit is contained in:
parent
d784ba8c36
commit
073fefb5d5
@ -1,19 +1,23 @@
|
||||
From 059762285f200a6e2527a4447d35f34d66b07bc2 Mon Sep 17 00:00:00 2001
|
||||
From 0ea63818330f3ebe989a56df89c1f49c63e7d761 Mon Sep 17 00:00:00 2001
|
||||
From: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
Date: Sun, 27 May 2018 09:33:20 +0000
|
||||
Date: Sun, 27 May 2018 14:40:45 +0000
|
||||
Subject: [PATCH 1/2] drivers: of: implement overlay support
|
||||
|
||||
Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
---
|
||||
Documentation/user/devicetree.rst | 101 +++++++++++++++++++++++++++++-
|
||||
Documentation/user/devicetree.rst | 101 ++++++++-
|
||||
arch/sandbox/dts/Makefile | 5 +-
|
||||
arch/sandbox/dts/sandbox.dts | 6 ++
|
||||
commands/oftree.c | 65 +++++++++++++++++--
|
||||
drivers/of/Kconfig | 6 ++
|
||||
arch/sandbox/dts/sandbox.dts | 6 +
|
||||
commands/oftree.c | 65 +++++-
|
||||
drivers/of/Kconfig | 6 +
|
||||
drivers/of/Makefile | 1 +
|
||||
include/of.h | 71 +++++++++++++++++++++
|
||||
drivers/of/overlay.c | 234 ++++++++++++++++++++
|
||||
drivers/of/resolver.c | 346 ++++++++++++++++++++++++++++++
|
||||
include/of.h | 71 ++++++
|
||||
scripts/Makefile.lib | 5 +-
|
||||
8 files changed, 251 insertions(+), 9 deletions(-)
|
||||
10 files changed, 831 insertions(+), 9 deletions(-)
|
||||
create mode 100644 drivers/of/overlay.c
|
||||
create mode 100644 drivers/of/resolver.c
|
||||
|
||||
diff --git a/Documentation/user/devicetree.rst b/Documentation/user/devicetree.rst
|
||||
index 17934d86e..b2220997d 100644
|
||||
@ -314,6 +318,598 @@ index ec4387006..323543461 100644
|
||||
obj-$(CONFIG_MTD) += of_mtd.o
|
||||
obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
|
||||
+obj-$(CONFIG_OFTREE_OVERLAY) += resolver.o overlay.o
|
||||
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
|
||||
new file mode 100644
|
||||
index 000000000..a711a353f
|
||||
--- /dev/null
|
||||
+++ b/drivers/of/overlay.c
|
||||
@@ -0,0 +1,234 @@
|
||||
+/*
|
||||
+ * Functions for working with device tree overlays
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
|
||||
+ * Copyright (C) 2012 Texas Instruments Inc.
|
||||
+ * Copyright (C) 2015 Pengutronix, Jan Luebbe <jlu@pengutronix.de>
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#include <common.h>
|
||||
+#include <of.h>
|
||||
+#include <errno.h>
|
||||
+#include <malloc.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/ctype.h>
|
||||
+#include <linux/err.h>
|
||||
+
|
||||
+/**
|
||||
+ * Apply a single overlay node recursively.
|
||||
+ *
|
||||
+ * All the property notifiers are appropriately called.
|
||||
+ * Note that the in case of an error the target node is left
|
||||
+ * in a inconsistent state. Error recovery should be performed
|
||||
+ * by recording the modification using the of notifiers.
|
||||
+ */
|
||||
+static int of_overlay_apply_one(struct device_node *target,
|
||||
+ const struct device_node *overlay)
|
||||
+{
|
||||
+ struct device_node *child, *tchild;
|
||||
+ struct property *prop;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* sanity checks */
|
||||
+ if (target == NULL || overlay == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for_each_property_of_node(overlay, prop) {
|
||||
+ /* don't touch, 'name' */
|
||||
+ if (of_prop_cmp(prop->name, "name") == 0)
|
||||
+ continue;
|
||||
+
|
||||
+ of_delete_property(of_find_property(target, prop->name, NULL));
|
||||
+
|
||||
+ if (of_new_property(target, prop->name, prop->value, prop->length) == NULL)
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ for_each_child_of_node(overlay, child) {
|
||||
+ tchild = of_get_child_by_name(target, child->name);
|
||||
+ if (tchild != NULL) {
|
||||
+ /* apply overlay recursively */
|
||||
+ ret = of_overlay_apply_one(tchild, child);
|
||||
+
|
||||
+ if (ret != 0)
|
||||
+ return ret;
|
||||
+ } else {
|
||||
+ /* create new child */
|
||||
+ tchild = of_new_node(target, child->name);
|
||||
+ if (tchild == NULL)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ /* apply the overlay */
|
||||
+ ret = of_overlay_apply_one(tchild, child);
|
||||
+ if (ret != 0) {
|
||||
+ of_delete_node(tchild);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_overlay - Apply @count overlays pointed at by @ovinfo_tab
|
||||
+ * @count: Number of of_overlay_info's
|
||||
+ * @ovinfo_tab: Array of overlay_info's to apply
|
||||
+ *
|
||||
+ * Applies the overlays given, while handling all error conditions
|
||||
+ * appropriately. Either the operation succeeds, or if it fails the
|
||||
+ * live tree is reverted to the state before the attempt.
|
||||
+ * Returns 0, or an error if the overlay attempt failed.
|
||||
+ */
|
||||
+int of_overlay(int count, struct of_overlay_info *ovinfo_tab)
|
||||
+{
|
||||
+ struct of_overlay_info *ovinfo;
|
||||
+ int i, err;
|
||||
+
|
||||
+ if (!ovinfo_tab)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ ovinfo = &ovinfo_tab[i];
|
||||
+ err = of_overlay_apply_one(ovinfo->target, ovinfo->overlay);
|
||||
+ if (err != 0) {
|
||||
+ pr_err("%s: overlay failed '%s'\n",
|
||||
+ __func__, ovinfo->target->full_name);
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_fail:
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_fill_overlay_info - Fill an overlay info structure
|
||||
+ * @info_node: Device node containing the overlay
|
||||
+ * @ovinfo: Pointer to the overlay info structure to fill
|
||||
+ *
|
||||
+ * Fills an overlay info structure with the overlay information
|
||||
+ * from a device node. This device node must have a target property
|
||||
+ * which contains a phandle of the overlay target node, and an
|
||||
+ * __overlay__ child node which has the overlay contents.
|
||||
+ * Both ovinfo->target & ovinfo->overlay have their references taken.
|
||||
+ *
|
||||
+ * Returns 0 on success, or a negative error value.
|
||||
+ */
|
||||
+int of_fill_overlay_info(struct device_node *info_node,
|
||||
+ struct of_overlay_info *ovinfo)
|
||||
+{
|
||||
+ const char *target_path;
|
||||
+ u32 target_handle;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!info_node || !ovinfo)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = of_property_read_u32(info_node, "target", &target_handle);
|
||||
+ if (ret == 0) {
|
||||
+ ovinfo->target = of_find_node_by_phandle(target_handle);
|
||||
+ } else {
|
||||
+ ret = of_property_read_string(info_node, "target-path", &target_path);
|
||||
+ if (ret == 0) {
|
||||
+ ovinfo->target = of_find_node_by_path(target_path);
|
||||
+ }
|
||||
+ }
|
||||
+ if (ovinfo->target == NULL)
|
||||
+ goto err_fail;
|
||||
+
|
||||
+ ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__");
|
||||
+ if (ovinfo->overlay == NULL)
|
||||
+ goto err_fail;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_fail:
|
||||
+ memset(ovinfo, 0, sizeof(*ovinfo));
|
||||
+ return -EINVAL;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_build_overlay_info - Build an overlay info array
|
||||
+ * @tree: Device node containing all the overlays
|
||||
+ * @cntp: Pointer to where the overlay info count will be help
|
||||
+ * @ovinfop: Pointer to the pointer of an overlay info structure.
|
||||
+ *
|
||||
+ * Helper function that given a tree containing overlay information,
|
||||
+ * allocates and builds an overlay info array containing it, ready
|
||||
+ * for use using of_overlay.
|
||||
+ *
|
||||
+ * Returns 0 on success with the @cntp @ovinfop pointers valid,
|
||||
+ * while on error a negative error value is returned.
|
||||
+ */
|
||||
+int of_build_overlay_info(struct device_node *tree,
|
||||
+ int *cntp, struct of_overlay_info **ovinfop)
|
||||
+{
|
||||
+ struct device_node *node;
|
||||
+ struct of_overlay_info *ovinfo;
|
||||
+ int cnt, err;
|
||||
+
|
||||
+ if (tree == NULL || cntp == NULL || ovinfop == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* worst case; every child is a node */
|
||||
+ cnt = 0;
|
||||
+ for_each_child_of_node(tree, node)
|
||||
+ cnt++;
|
||||
+
|
||||
+ ovinfo = kzalloc(cnt * sizeof(*ovinfo), GFP_KERNEL);
|
||||
+ if (ovinfo == NULL)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ cnt = 0;
|
||||
+ for_each_child_of_node(tree, node) {
|
||||
+ memset(&ovinfo[cnt], 0, sizeof(*ovinfo));
|
||||
+ err = of_fill_overlay_info(node, &ovinfo[cnt]);
|
||||
+ if (err == 0)
|
||||
+ cnt++;
|
||||
+ }
|
||||
+
|
||||
+ /* if nothing filled, return error */
|
||||
+ if (cnt == 0) {
|
||||
+ kfree(ovinfo);
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ *cntp = cnt;
|
||||
+ *ovinfop = ovinfo;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_free_overlay_info - Free an overlay info array
|
||||
+ * @count: Number of of_overlay_info's
|
||||
+ * @ovinfo_tab: Array of overlay_info's to free
|
||||
+ *
|
||||
+ * Releases the memory of a previously allocate ovinfo array
|
||||
+ * by of_build_overlay_info.
|
||||
+ * Returns 0, or an error if the arguments are bogus.
|
||||
+ */
|
||||
+int of_free_overlay_info(int count, struct of_overlay_info *ovinfo_tab)
|
||||
+{
|
||||
+ struct of_overlay_info *ovinfo;
|
||||
+ int i;
|
||||
+
|
||||
+ if (!ovinfo_tab || count < 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* do it in reverse */
|
||||
+ for (i = count - 1; i >= 0; i--)
|
||||
+ ovinfo = &ovinfo_tab[i];
|
||||
+
|
||||
+ kfree(ovinfo_tab);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
|
||||
new file mode 100644
|
||||
index 000000000..62476093c
|
||||
--- /dev/null
|
||||
+++ b/drivers/of/resolver.c
|
||||
@@ -0,0 +1,346 @@
|
||||
+/*
|
||||
+ * Functions for dealing with DT resolution
|
||||
+ *
|
||||
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
|
||||
+ * Copyright (C) 2012 Texas Instruments Inc.
|
||||
+ * Copyright (C) 2015 Pengutronix, Jan Luebbe <jlu@pengutronix.de>
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#include <common.h>
|
||||
+#include <of.h>
|
||||
+#include <errno.h>
|
||||
+#include <malloc.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/ctype.h>
|
||||
+#include <linux/string.h>
|
||||
+
|
||||
+/**
|
||||
+ * Adjust a subtree's phandle values by a given delta.
|
||||
+ * Makes sure not to just adjust the device node's phandle value,
|
||||
+ * but modify the phandle properties values as well.
|
||||
+ */
|
||||
+static void of_adjust_tree_phandles(struct device_node *node,
|
||||
+ int phandle_delta)
|
||||
+{
|
||||
+ struct device_node *child;
|
||||
+ struct property *prop;
|
||||
+ phandle phandle;
|
||||
+
|
||||
+ /* first adjust the node's phandle direct value */
|
||||
+ if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
|
||||
+ node->phandle += phandle_delta;
|
||||
+
|
||||
+ /* now adjust phandle & linux,phandle values */
|
||||
+ for_each_property_of_node(node, prop) {
|
||||
+ /* only look for these two */
|
||||
+ if (of_prop_cmp(prop->name, "phandle") != 0 &&
|
||||
+ of_prop_cmp(prop->name, "linux,phandle") != 0)
|
||||
+ continue;
|
||||
+
|
||||
+ /* must be big enough */
|
||||
+ if (prop->length < 4)
|
||||
+ continue;
|
||||
+
|
||||
+ /* read phandle value */
|
||||
+ phandle = be32_to_cpu(*(uint32_t *)prop->value);
|
||||
+ if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
|
||||
+ continue;
|
||||
+
|
||||
+ /* adjust */
|
||||
+ *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
|
||||
+ }
|
||||
+
|
||||
+ /* now do the children recursively */
|
||||
+ for_each_child_of_node(node, child)
|
||||
+ of_adjust_tree_phandles(child, phandle_delta);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Adjust the local phandle references by the given phandle delta.
|
||||
+ * Assumes the existances of a __local_fixups__ node at the root
|
||||
+ * of the tree. Does not take any devtree locks so make sure you
|
||||
+ * call this on a tree which is at the detached state.
|
||||
+ */
|
||||
+static int of_adjust_tree_phandle_references(struct device_node *node,
|
||||
+ int phandle_delta)
|
||||
+{
|
||||
+ phandle phandle;
|
||||
+ struct device_node *refnode, *child;
|
||||
+ struct property *rprop, *sprop;
|
||||
+ char *propval, *propcur, *propend, *nodestr, *propstr, *s;
|
||||
+ int offset, propcurlen;
|
||||
+ int err;
|
||||
+ bool found = false;
|
||||
+
|
||||
+ /* locate the symbols & fixups nodes on resolve */
|
||||
+ for_each_child_of_node(node, child)
|
||||
+ if (of_node_cmp(child->name, "__local_fixups__") == 0) {
|
||||
+ found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* no local fixups */
|
||||
+ if (!found)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* find the local fixups property */
|
||||
+ for_each_property_of_node(child, rprop) {
|
||||
+ /* skip properties added automatically */
|
||||
+ if (of_prop_cmp(rprop->name, "name") == 0)
|
||||
+ continue;
|
||||
+
|
||||
+ /* make a copy */
|
||||
+ propval = kmalloc(rprop->length, GFP_KERNEL);
|
||||
+ if (propval == NULL) {
|
||||
+ pr_err("%s: Could not copy value of '%s'\n",
|
||||
+ __func__, rprop->name);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ memcpy(propval, rprop->value, rprop->length);
|
||||
+
|
||||
+ propend = propval + rprop->length;
|
||||
+ for (propcur = propval; propcur < propend;
|
||||
+ propcur += propcurlen + 1) {
|
||||
+
|
||||
+ propcurlen = strlen(propcur);
|
||||
+
|
||||
+ nodestr = propcur;
|
||||
+ s = strchr(propcur, ':');
|
||||
+ if (s == NULL) {
|
||||
+ pr_err("%s: Illegal symbol entry '%s' (1)\n",
|
||||
+ __func__, propcur);
|
||||
+ err = -EINVAL;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+ *s++ = '\0';
|
||||
+
|
||||
+ propstr = s;
|
||||
+ s = strchr(s, ':');
|
||||
+ if (s == NULL) {
|
||||
+ pr_err("%s: Illegal symbol entry '%s' (2)\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ err = -EINVAL;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ *s++ = '\0';
|
||||
+ offset = simple_strtoul(s, NULL, 10);
|
||||
+
|
||||
+ /* look into the resolve node for the full path */
|
||||
+ refnode = of_find_node_by_path_from(node, nodestr);
|
||||
+ if (refnode == NULL) {
|
||||
+ pr_warn("%s: Could not find refnode '%s'\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* now find the property */
|
||||
+ found = false;
|
||||
+ for_each_property_of_node(refnode, sprop)
|
||||
+ if (of_prop_cmp(sprop->name, propstr) == 0) {
|
||||
+ found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!found) {
|
||||
+ pr_err("%s: Could not find property '%s'\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ err = -ENOENT;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ phandle = be32_to_cpu(*(uint32_t *)
|
||||
+ (sprop->value + offset));
|
||||
+ *(uint32_t *)(sprop->value + offset) =
|
||||
+ cpu_to_be32(phandle + phandle_delta);
|
||||
+ }
|
||||
+
|
||||
+ kfree(propval);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_fail:
|
||||
+ kfree(propval);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * of_resolve - Resolve the given node against the live tree.
|
||||
+ *
|
||||
+ * @resolve: Node to resolve
|
||||
+ *
|
||||
+ * Perform dynamic Device Tree resolution against the live tree
|
||||
+ * to the given node to resolve. This depends on the live tree
|
||||
+ * having a __symbols__ node, and the resolve node the __fixups__ &
|
||||
+ * __local_fixups__ nodes (if needed).
|
||||
+ * The result of the operation is a resolve node that it's contents
|
||||
+ * are fit to be inserted or operate upon the live tree.
|
||||
+ * Returns 0 on success or a negative error value on error.
|
||||
+ */
|
||||
+int of_resolve(struct device_node *resolve)
|
||||
+{
|
||||
+ struct device_node *child, *refnode;
|
||||
+ struct device_node *root_sym, *resolve_sym, *resolve_fix;
|
||||
+ struct property *rprop, *sprop;
|
||||
+ const char *refpath;
|
||||
+ char *propval, *propcur, *propend, *nodestr, *propstr, *s;
|
||||
+ int offset, propcurlen;
|
||||
+ phandle phandle, phandle_delta;
|
||||
+ int err;
|
||||
+ bool found = false;
|
||||
+
|
||||
+ /* the resolve node must exist */
|
||||
+ if (resolve == NULL) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* first we need to adjust the phandles */
|
||||
+ phandle_delta = of_get_tree_max_phandle(NULL) + 1;
|
||||
+ of_adjust_tree_phandles(resolve, phandle_delta);
|
||||
+ err = of_adjust_tree_phandle_references(resolve, phandle_delta);
|
||||
+ if (err != 0)
|
||||
+ return err;
|
||||
+
|
||||
+ root_sym = NULL;
|
||||
+ resolve_sym = NULL;
|
||||
+ resolve_fix = NULL;
|
||||
+
|
||||
+ /* this may fail (if no fixups are required) */
|
||||
+ root_sym = of_find_node_by_path("/__symbols__");
|
||||
+
|
||||
+ /* locate the symbols & fixups nodes on resolve */
|
||||
+ for_each_child_of_node(resolve, child) {
|
||||
+ if (resolve_sym == NULL &&
|
||||
+ of_node_cmp(child->name, "__symbols__") == 0)
|
||||
+ resolve_sym = child;
|
||||
+
|
||||
+ if (resolve_fix == NULL &&
|
||||
+ of_node_cmp(child->name, "__fixups__") == 0)
|
||||
+ resolve_fix = child;
|
||||
+
|
||||
+ /* both found, don't bother anymore */
|
||||
+ if (resolve_sym != NULL && resolve_fix != NULL)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* we do allow for the case where no fixups are needed */
|
||||
+ if (resolve_fix == NULL)
|
||||
+ goto merge_sym;
|
||||
+
|
||||
+ /* we need to fixup, but no root symbols... */
|
||||
+ if (root_sym == NULL)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for_each_property_of_node(resolve_fix, rprop) {
|
||||
+ /* skip properties added automatically */
|
||||
+ if (of_prop_cmp(rprop->name, "name") == 0)
|
||||
+ continue;
|
||||
+
|
||||
+ err = of_property_read_string(root_sym,
|
||||
+ rprop->name, &refpath);
|
||||
+ if (err != 0) {
|
||||
+ pr_err("%s: Could not find symbol '%s'\n",
|
||||
+ __func__, rprop->name);
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ refnode = of_find_node_by_path(refpath);
|
||||
+ if (refnode == NULL) {
|
||||
+ pr_err("%s: Could not find node by path '%s'\n",
|
||||
+ __func__, refpath);
|
||||
+ err = -ENOENT;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ phandle = refnode->phandle;
|
||||
+
|
||||
+ pr_debug("%s: %s phandle is 0x%08x\n",
|
||||
+ __func__, rprop->name, phandle);
|
||||
+
|
||||
+ /* make a copy */
|
||||
+ propval = kmalloc(rprop->length, GFP_KERNEL);
|
||||
+ if (propval == NULL) {
|
||||
+ pr_err("%s: Could not copy value of '%s'\n",
|
||||
+ __func__, rprop->name);
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ memcpy(propval, rprop->value, rprop->length);
|
||||
+
|
||||
+ propend = propval + rprop->length;
|
||||
+ for (propcur = propval; propcur < propend;
|
||||
+ propcur += propcurlen + 1) {
|
||||
+ propcurlen = strlen(propcur);
|
||||
+
|
||||
+ nodestr = propcur;
|
||||
+ s = strchr(propcur, ':');
|
||||
+ if (s == NULL) {
|
||||
+ pr_err("%s: Illegal symbol "
|
||||
+ "entry '%s' (1)\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ kfree(propval);
|
||||
+ err = -EINVAL;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+ *s++ = '\0';
|
||||
+
|
||||
+ propstr = s;
|
||||
+ s = strchr(s, ':');
|
||||
+ if (s == NULL) {
|
||||
+ pr_err("%s: Illegal symbol "
|
||||
+ "entry '%s' (2)\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ kfree(propval);
|
||||
+ err = -EINVAL;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ *s++ = '\0';
|
||||
+ offset = simple_strtoul(s, NULL, 10);
|
||||
+
|
||||
+ /* look into the resolve node for the full path */
|
||||
+ refnode = of_find_node_by_path_from(resolve, nodestr);
|
||||
+ if (refnode == NULL) {
|
||||
+ pr_err("%s: Could not find refnode '%s'\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ kfree(propval);
|
||||
+ err = -ENOENT;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ /* now find the property */
|
||||
+ found = false;
|
||||
+ for_each_property_of_node(refnode, sprop)
|
||||
+ if (of_prop_cmp(sprop->name, propstr) == 0) {
|
||||
+ found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!found) {
|
||||
+ pr_err("%s: Could not find property '%s'\n",
|
||||
+ __func__, (char *)rprop->value);
|
||||
+ kfree(propval);
|
||||
+ err = -ENOENT;
|
||||
+ goto err_fail;
|
||||
+ }
|
||||
+
|
||||
+ *(uint32_t *)(sprop->value + offset) =
|
||||
+ cpu_to_be32(phandle);
|
||||
+ }
|
||||
+
|
||||
+ kfree(propval);
|
||||
+ }
|
||||
+
|
||||
+merge_sym:
|
||||
+ return 0;
|
||||
+
|
||||
+err_fail:
|
||||
+ return err;
|
||||
+}
|
||||
diff --git a/include/of.h b/include/of.h
|
||||
index fec51bb94..91476ae47 100644
|
||||
--- a/include/of.h
|
||||
|
@ -1,6 +1,6 @@
|
||||
From b283776d26027ce237276f57e4522f5d0b6aab3f Mon Sep 17 00:00:00 2001
|
||||
From d898e0f7e552b781b767bb9edabb66d0a6cbb019 Mon Sep 17 00:00:00 2001
|
||||
From: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
Date: Sun, 27 May 2018 09:34:21 +0000
|
||||
Date: Sun, 27 May 2018 14:41:33 +0000
|
||||
Subject: [PATCH 2/2] scripts/dtc: Update to upstream version 1.4.6
|
||||
|
||||
Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
@ -19,6 +19,7 @@ Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
scripts/dtc/fdt.c | 24 +-
|
||||
scripts/dtc/fdt.h | 6 +-
|
||||
scripts/dtc/fdt_empty_tree.c | 1 -
|
||||
scripts/dtc/fdt_overlay.c | 912 ++++++++++++++++++++
|
||||
scripts/dtc/fdt_ro.c | 288 ++++++-
|
||||
scripts/dtc/fdt_rw.c | 121 +--
|
||||
scripts/dtc/fdt_strerror.c | 6 +
|
||||
@ -40,7 +41,8 @@ Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
|
||||
scripts/dtc/util.c | 44 +-
|
||||
scripts/dtc/util.h | 33 +-
|
||||
scripts/dtc/version_gen.h | 2 +-
|
||||
35 files changed, 3666 insertions(+), 1728 deletions(-)
|
||||
36 files changed, 4578 insertions(+), 1728 deletions(-)
|
||||
create mode 100644 scripts/dtc/fdt_overlay.c
|
||||
|
||||
diff --git a/scripts/dtc/.gitignore b/scripts/dtc/.gitignore
|
||||
index 80f6b50fd..cdabdc95a 100644
|
||||
@ -5679,6 +5681,924 @@ index f72d13b1d..f2ae9b77c 100644
|
||||
return fdt_open_into(buf, buf, bufsize);
|
||||
}
|
||||
-
|
||||
diff --git a/scripts/dtc/fdt_overlay.c b/scripts/dtc/fdt_overlay.c
|
||||
new file mode 100644
|
||||
index 000000000..bf75388ec
|
||||
--- /dev/null
|
||||
+++ b/scripts/dtc/fdt_overlay.c
|
||||
@@ -0,0 +1,912 @@
|
||||
+/*
|
||||
+ * libfdt - Flat Device Tree manipulation
|
||||
+ * Copyright (C) 2016 Free Electrons
|
||||
+ * Copyright (C) 2016 NextThing Co.
|
||||
+ *
|
||||
+ * libfdt is dual licensed: you can use it either under the terms of
|
||||
+ * the GPL, or the BSD license, at your option.
|
||||
+ *
|
||||
+ * a) This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License as
|
||||
+ * published by the Free Software Foundation; either version 2 of the
|
||||
+ * License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library 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.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public
|
||||
+ * License along with this library; if not, write to the Free
|
||||
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
||||
+ * MA 02110-1301 USA
|
||||
+ *
|
||||
+ * Alternatively,
|
||||
+ *
|
||||
+ * b) Redistribution and use in source and binary forms, with or
|
||||
+ * without modification, are permitted provided that the following
|
||||
+ * conditions are met:
|
||||
+ *
|
||||
+ * 1. Redistributions of source code must retain the above
|
||||
+ * copyright notice, this list of conditions and the following
|
||||
+ * disclaimer.
|
||||
+ * 2. Redistributions in binary form must reproduce the above
|
||||
+ * copyright notice, this list of conditions and the following
|
||||
+ * disclaimer in the documentation and/or other materials
|
||||
+ * provided with the distribution.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ */
|
||||
+#include "libfdt_env.h"
|
||||
+
|
||||
+#include <fdt.h>
|
||||
+#include <libfdt.h>
|
||||
+
|
||||
+#include "libfdt_internal.h"
|
||||
+
|
||||
+/**
|
||||
+ * overlay_get_target_phandle - retrieves the target phandle of a fragment
|
||||
+ * @fdto: pointer to the device tree overlay blob
|
||||
+ * @fragment: node offset of the fragment in the overlay
|
||||
+ *
|
||||
+ * overlay_get_target_phandle() retrieves the target phandle of an
|
||||
+ * overlay fragment when that fragment uses a phandle (target
|
||||
+ * property) instead of a path (target-path property).
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * the phandle pointed by the target property
|
||||
+ * 0, if the phandle was not found
|
||||
+ * -1, if the phandle was malformed
|
||||
+ */
|
||||
+static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
|
||||
+{
|
||||
+ const fdt32_t *val;
|
||||
+ int len;
|
||||
+
|
||||
+ val = fdt_getprop(fdto, fragment, "target", &len);
|
||||
+ if (!val)
|
||||
+ return 0;
|
||||
+
|
||||
+ if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
|
||||
+ return (uint32_t)-1;
|
||||
+
|
||||
+ return fdt32_to_cpu(*val);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_get_target - retrieves the offset of a fragment's target
|
||||
+ * @fdt: Base device tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @fragment: node offset of the fragment in the overlay
|
||||
+ * @pathp: pointer which receives the path of the target (or NULL)
|
||||
+ *
|
||||
+ * overlay_get_target() retrieves the target offset in the base
|
||||
+ * device tree of a fragment, no matter how the actual targetting is
|
||||
+ * done (through a phandle or a path)
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * the targetted node offset in the base device tree
|
||||
+ * Negative error code on error
|
||||
+ */
|
||||
+static int overlay_get_target(const void *fdt, const void *fdto,
|
||||
+ int fragment, char const **pathp)
|
||||
+{
|
||||
+ uint32_t phandle;
|
||||
+ const char *path = NULL;
|
||||
+ int path_len = 0, ret;
|
||||
+
|
||||
+ /* Try first to do a phandle based lookup */
|
||||
+ phandle = overlay_get_target_phandle(fdto, fragment);
|
||||
+ if (phandle == (uint32_t)-1)
|
||||
+ return -FDT_ERR_BADPHANDLE;
|
||||
+
|
||||
+ /* no phandle, try path */
|
||||
+ if (!phandle) {
|
||||
+ /* And then a path based lookup */
|
||||
+ path = fdt_getprop(fdto, fragment, "target-path", &path_len);
|
||||
+ if (path)
|
||||
+ ret = fdt_path_offset(fdt, path);
|
||||
+ else
|
||||
+ ret = path_len;
|
||||
+ } else
|
||||
+ ret = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
+
|
||||
+ /*
|
||||
+ * If we haven't found either a target or a
|
||||
+ * target-path property in a node that contains a
|
||||
+ * __overlay__ subnode (we wouldn't be called
|
||||
+ * otherwise), consider it a improperly written
|
||||
+ * overlay
|
||||
+ */
|
||||
+ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
|
||||
+ ret = -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ /* return on error */
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* return pointer to path (if available) */
|
||||
+ if (pathp)
|
||||
+ *pathp = path ? path : NULL;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_phandle_add_offset - Increases a phandle by an offset
|
||||
+ * @fdt: Base device tree blob
|
||||
+ * @node: Device tree overlay blob
|
||||
+ * @name: Name of the property to modify (phandle or linux,phandle)
|
||||
+ * @delta: offset to apply
|
||||
+ *
|
||||
+ * overlay_phandle_add_offset() increments a node phandle by a given
|
||||
+ * offset.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success.
|
||||
+ * Negative error code on error
|
||||
+ */
|
||||
+static int overlay_phandle_add_offset(void *fdt, int node,
|
||||
+ const char *name, uint32_t delta)
|
||||
+{
|
||||
+ const fdt32_t *val;
|
||||
+ uint32_t adj_val;
|
||||
+ int len;
|
||||
+
|
||||
+ val = fdt_getprop(fdt, node, name, &len);
|
||||
+ if (!val)
|
||||
+ return len;
|
||||
+
|
||||
+ if (len != sizeof(*val))
|
||||
+ return -FDT_ERR_BADPHANDLE;
|
||||
+
|
||||
+ adj_val = fdt32_to_cpu(*val);
|
||||
+ if ((adj_val + delta) < adj_val)
|
||||
+ return -FDT_ERR_NOPHANDLES;
|
||||
+
|
||||
+ adj_val += delta;
|
||||
+ if (adj_val == (uint32_t)-1)
|
||||
+ return -FDT_ERR_NOPHANDLES;
|
||||
+
|
||||
+ return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_adjust_node_phandles - Offsets the phandles of a node
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @node: Offset of the node we want to adjust
|
||||
+ * @delta: Offset to shift the phandles of
|
||||
+ *
|
||||
+ * overlay_adjust_node_phandles() adds a constant to all the phandles
|
||||
+ * of a given node. This is mainly use as part of the overlay
|
||||
+ * application process, when we want to update all the overlay
|
||||
+ * phandles to not conflict with the overlays of the base device tree.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_adjust_node_phandles(void *fdto, int node,
|
||||
+ uint32_t delta)
|
||||
+{
|
||||
+ int child;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
|
||||
+ if (ret && ret != -FDT_ERR_NOTFOUND)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
|
||||
+ if (ret && ret != -FDT_ERR_NOTFOUND)
|
||||
+ return ret;
|
||||
+
|
||||
+ fdt_for_each_subnode(child, fdto, node) {
|
||||
+ ret = overlay_adjust_node_phandles(fdto, child, delta);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @delta: Offset to shift the phandles of
|
||||
+ *
|
||||
+ * overlay_adjust_local_phandles() adds a constant to all the
|
||||
+ * phandles of an overlay. This is mainly use as part of the overlay
|
||||
+ * application process, when we want to update all the overlay
|
||||
+ * phandles to not conflict with the overlays of the base device tree.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
|
||||
+{
|
||||
+ /*
|
||||
+ * Start adjusting the phandles from the overlay root
|
||||
+ */
|
||||
+ return overlay_adjust_node_phandles(fdto, 0, delta);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_update_local_node_references - Adjust the overlay references
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @tree_node: Node offset of the node to operate on
|
||||
+ * @fixup_node: Node offset of the matching local fixups node
|
||||
+ * @delta: Offset to shift the phandles of
|
||||
+ *
|
||||
+ * overlay_update_local_nodes_references() update the phandles
|
||||
+ * pointing to a node within the device tree overlay by adding a
|
||||
+ * constant delta.
|
||||
+ *
|
||||
+ * This is mainly used as part of a device tree application process,
|
||||
+ * where you want the device tree overlays phandles to not conflict
|
||||
+ * with the ones from the base device tree before merging them.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_update_local_node_references(void *fdto,
|
||||
+ int tree_node,
|
||||
+ int fixup_node,
|
||||
+ uint32_t delta)
|
||||
+{
|
||||
+ int fixup_prop;
|
||||
+ int fixup_child;
|
||||
+ int ret;
|
||||
+
|
||||
+ fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
|
||||
+ const fdt32_t *fixup_val;
|
||||
+ const char *tree_val;
|
||||
+ const char *name;
|
||||
+ int fixup_len;
|
||||
+ int tree_len;
|
||||
+ int i;
|
||||
+
|
||||
+ fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
|
||||
+ &name, &fixup_len);
|
||||
+ if (!fixup_val)
|
||||
+ return fixup_len;
|
||||
+
|
||||
+ if (fixup_len % sizeof(uint32_t))
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
|
||||
+ if (!tree_val) {
|
||||
+ if (tree_len == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ return tree_len;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
|
||||
+ fdt32_t adj_val;
|
||||
+ uint32_t poffset;
|
||||
+
|
||||
+ poffset = fdt32_to_cpu(fixup_val[i]);
|
||||
+
|
||||
+ /*
|
||||
+ * phandles to fixup can be unaligned.
|
||||
+ *
|
||||
+ * Use a memcpy for the architectures that do
|
||||
+ * not support unaligned accesses.
|
||||
+ */
|
||||
+ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
|
||||
+
|
||||
+ adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
|
||||
+
|
||||
+ ret = fdt_setprop_inplace_namelen_partial(fdto,
|
||||
+ tree_node,
|
||||
+ name,
|
||||
+ strlen(name),
|
||||
+ poffset,
|
||||
+ &adj_val,
|
||||
+ sizeof(adj_val));
|
||||
+ if (ret == -FDT_ERR_NOSPACE)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
|
||||
+ const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
|
||||
+ NULL);
|
||||
+ int tree_child;
|
||||
+
|
||||
+ tree_child = fdt_subnode_offset(fdto, tree_node,
|
||||
+ fixup_child_name);
|
||||
+ if (tree_child == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+ if (tree_child < 0)
|
||||
+ return tree_child;
|
||||
+
|
||||
+ ret = overlay_update_local_node_references(fdto,
|
||||
+ tree_child,
|
||||
+ fixup_child,
|
||||
+ delta);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_update_local_references - Adjust the overlay references
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @delta: Offset to shift the phandles of
|
||||
+ *
|
||||
+ * overlay_update_local_references() update all the phandles pointing
|
||||
+ * to a node within the device tree overlay by adding a constant
|
||||
+ * delta to not conflict with the base overlay.
|
||||
+ *
|
||||
+ * This is mainly used as part of a device tree application process,
|
||||
+ * where you want the device tree overlays phandles to not conflict
|
||||
+ * with the ones from the base device tree before merging them.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_update_local_references(void *fdto, uint32_t delta)
|
||||
+{
|
||||
+ int fixups;
|
||||
+
|
||||
+ fixups = fdt_path_offset(fdto, "/__local_fixups__");
|
||||
+ if (fixups < 0) {
|
||||
+ /* There's no local phandles to adjust, bail out */
|
||||
+ if (fixups == -FDT_ERR_NOTFOUND)
|
||||
+ return 0;
|
||||
+
|
||||
+ return fixups;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Update our local references from the root of the tree
|
||||
+ */
|
||||
+ return overlay_update_local_node_references(fdto, 0, fixups,
|
||||
+ delta);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_fixup_one_phandle - Set an overlay phandle to the base one
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @symbols_off: Node offset of the symbols node in the base device tree
|
||||
+ * @path: Path to a node holding a phandle in the overlay
|
||||
+ * @path_len: number of path characters to consider
|
||||
+ * @name: Name of the property holding the phandle reference in the overlay
|
||||
+ * @name_len: number of name characters to consider
|
||||
+ * @poffset: Offset within the overlay property where the phandle is stored
|
||||
+ * @label: Label of the node referenced by the phandle
|
||||
+ *
|
||||
+ * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
|
||||
+ * a node in the base device tree.
|
||||
+ *
|
||||
+ * This is part of the device tree overlay application process, when
|
||||
+ * you want all the phandles in the overlay to point to the actual
|
||||
+ * base dt nodes.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_fixup_one_phandle(void *fdt, void *fdto,
|
||||
+ int symbols_off,
|
||||
+ const char *path, uint32_t path_len,
|
||||
+ const char *name, uint32_t name_len,
|
||||
+ int poffset, const char *label)
|
||||
+{
|
||||
+ const char *symbol_path;
|
||||
+ uint32_t phandle;
|
||||
+ fdt32_t phandle_prop;
|
||||
+ int symbol_off, fixup_off;
|
||||
+ int prop_len;
|
||||
+
|
||||
+ if (symbols_off < 0)
|
||||
+ return symbols_off;
|
||||
+
|
||||
+ symbol_path = fdt_getprop(fdt, symbols_off, label,
|
||||
+ &prop_len);
|
||||
+ if (!symbol_path)
|
||||
+ return prop_len;
|
||||
+
|
||||
+ symbol_off = fdt_path_offset(fdt, symbol_path);
|
||||
+ if (symbol_off < 0)
|
||||
+ return symbol_off;
|
||||
+
|
||||
+ phandle = fdt_get_phandle(fdt, symbol_off);
|
||||
+ if (!phandle)
|
||||
+ return -FDT_ERR_NOTFOUND;
|
||||
+
|
||||
+ fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
|
||||
+ if (fixup_off == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+ if (fixup_off < 0)
|
||||
+ return fixup_off;
|
||||
+
|
||||
+ phandle_prop = cpu_to_fdt32(phandle);
|
||||
+ return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
|
||||
+ name, name_len, poffset,
|
||||
+ &phandle_prop,
|
||||
+ sizeof(phandle_prop));
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * overlay_fixup_phandle - Set an overlay phandle to the base one
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @symbols_off: Node offset of the symbols node in the base device tree
|
||||
+ * @property: Property offset in the overlay holding the list of fixups
|
||||
+ *
|
||||
+ * overlay_fixup_phandle() resolves all the overlay phandles pointed
|
||||
+ * to in a __fixups__ property, and updates them to match the phandles
|
||||
+ * in use in the base device tree.
|
||||
+ *
|
||||
+ * This is part of the device tree overlay application process, when
|
||||
+ * you want all the phandles in the overlay to point to the actual
|
||||
+ * base dt nodes.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
|
||||
+ int property)
|
||||
+{
|
||||
+ const char *value;
|
||||
+ const char *label;
|
||||
+ int len;
|
||||
+
|
||||
+ value = fdt_getprop_by_offset(fdto, property,
|
||||
+ &label, &len);
|
||||
+ if (!value) {
|
||||
+ if (len == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_INTERNAL;
|
||||
+
|
||||
+ return len;
|
||||
+ }
|
||||
+
|
||||
+ do {
|
||||
+ const char *path, *name, *fixup_end;
|
||||
+ const char *fixup_str = value;
|
||||
+ uint32_t path_len, name_len;
|
||||
+ uint32_t fixup_len;
|
||||
+ char *sep, *endptr;
|
||||
+ int poffset, ret;
|
||||
+
|
||||
+ fixup_end = memchr(value, '\0', len);
|
||||
+ if (!fixup_end)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+ fixup_len = fixup_end - fixup_str;
|
||||
+
|
||||
+ len -= fixup_len + 1;
|
||||
+ value += fixup_len + 1;
|
||||
+
|
||||
+ path = fixup_str;
|
||||
+ sep = memchr(fixup_str, ':', fixup_len);
|
||||
+ if (!sep || *sep != ':')
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ path_len = sep - path;
|
||||
+ if (path_len == (fixup_len - 1))
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ fixup_len -= path_len + 1;
|
||||
+ name = sep + 1;
|
||||
+ sep = memchr(name, ':', fixup_len);
|
||||
+ if (!sep || *sep != ':')
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ name_len = sep - name;
|
||||
+ if (!name_len)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ poffset = strtoul(sep + 1, &endptr, 10);
|
||||
+ if ((*endptr != '\0') || (endptr <= (sep + 1)))
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
|
||||
+ path, path_len, name, name_len,
|
||||
+ poffset, label);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ } while (len > 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_fixup_phandles - Resolve the overlay phandles to the base
|
||||
+ * device tree
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ *
|
||||
+ * overlay_fixup_phandles() resolves all the overlay phandles pointing
|
||||
+ * to nodes in the base device tree.
|
||||
+ *
|
||||
+ * This is one of the steps of the device tree overlay application
|
||||
+ * process, when you want all the phandles in the overlay to point to
|
||||
+ * the actual base dt nodes.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_fixup_phandles(void *fdt, void *fdto)
|
||||
+{
|
||||
+ int fixups_off, symbols_off;
|
||||
+ int property;
|
||||
+
|
||||
+ /* We can have overlays without any fixups */
|
||||
+ fixups_off = fdt_path_offset(fdto, "/__fixups__");
|
||||
+ if (fixups_off == -FDT_ERR_NOTFOUND)
|
||||
+ return 0; /* nothing to do */
|
||||
+ if (fixups_off < 0)
|
||||
+ return fixups_off;
|
||||
+
|
||||
+ /* And base DTs without symbols */
|
||||
+ symbols_off = fdt_path_offset(fdt, "/__symbols__");
|
||||
+ if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
|
||||
+ return symbols_off;
|
||||
+
|
||||
+ fdt_for_each_property_offset(property, fdto, fixups_off) {
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_apply_node - Merges a node into the base device tree
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @target: Node offset in the base device tree to apply the fragment to
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ * @node: Node offset in the overlay holding the changes to merge
|
||||
+ *
|
||||
+ * overlay_apply_node() merges a node into a target base device tree
|
||||
+ * node pointed.
|
||||
+ *
|
||||
+ * This is part of the final step in the device tree overlay
|
||||
+ * application process, when all the phandles have been adjusted and
|
||||
+ * resolved and you just have to merge overlay into the base device
|
||||
+ * tree.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_apply_node(void *fdt, int target,
|
||||
+ void *fdto, int node)
|
||||
+{
|
||||
+ int property;
|
||||
+ int subnode;
|
||||
+
|
||||
+ fdt_for_each_property_offset(property, fdto, node) {
|
||||
+ const char *name;
|
||||
+ const void *prop;
|
||||
+ int prop_len;
|
||||
+ int ret;
|
||||
+
|
||||
+ prop = fdt_getprop_by_offset(fdto, property, &name,
|
||||
+ &prop_len);
|
||||
+ if (prop_len == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_INTERNAL;
|
||||
+ if (prop_len < 0)
|
||||
+ return prop_len;
|
||||
+
|
||||
+ ret = fdt_setprop(fdt, target, name, prop, prop_len);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ fdt_for_each_subnode(subnode, fdto, node) {
|
||||
+ const char *name = fdt_get_name(fdto, subnode, NULL);
|
||||
+ int nnode;
|
||||
+ int ret;
|
||||
+
|
||||
+ nnode = fdt_add_subnode(fdt, target, name);
|
||||
+ if (nnode == -FDT_ERR_EXISTS) {
|
||||
+ nnode = fdt_subnode_offset(fdt, target, name);
|
||||
+ if (nnode == -FDT_ERR_NOTFOUND)
|
||||
+ return -FDT_ERR_INTERNAL;
|
||||
+ }
|
||||
+
|
||||
+ if (nnode < 0)
|
||||
+ return nnode;
|
||||
+
|
||||
+ ret = overlay_apply_node(fdt, nnode, fdto, subnode);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_merge - Merge an overlay into its base device tree
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ *
|
||||
+ * overlay_merge() merges an overlay into its base device tree.
|
||||
+ *
|
||||
+ * This is the next to last step in the device tree overlay application
|
||||
+ * process, when all the phandles have been adjusted and resolved and
|
||||
+ * you just have to merge overlay into the base device tree.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_merge(void *fdt, void *fdto)
|
||||
+{
|
||||
+ int fragment;
|
||||
+
|
||||
+ fdt_for_each_subnode(fragment, fdto, 0) {
|
||||
+ int overlay;
|
||||
+ int target;
|
||||
+ int ret;
|
||||
+
|
||||
+ /*
|
||||
+ * Each fragments will have an __overlay__ node. If
|
||||
+ * they don't, it's not supposed to be merged
|
||||
+ */
|
||||
+ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
|
||||
+ if (overlay == -FDT_ERR_NOTFOUND)
|
||||
+ continue;
|
||||
+
|
||||
+ if (overlay < 0)
|
||||
+ return overlay;
|
||||
+
|
||||
+ target = overlay_get_target(fdt, fdto, fragment, NULL);
|
||||
+ if (target < 0)
|
||||
+ return target;
|
||||
+
|
||||
+ ret = overlay_apply_node(fdt, target, fdto, overlay);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int get_path_len(const void *fdt, int nodeoffset)
|
||||
+{
|
||||
+ int len = 0, namelen;
|
||||
+ const char *name;
|
||||
+
|
||||
+ FDT_CHECK_HEADER(fdt);
|
||||
+
|
||||
+ for (;;) {
|
||||
+ name = fdt_get_name(fdt, nodeoffset, &namelen);
|
||||
+ if (!name)
|
||||
+ return namelen;
|
||||
+
|
||||
+ /* root? we're done */
|
||||
+ if (namelen == 0)
|
||||
+ break;
|
||||
+
|
||||
+ nodeoffset = fdt_parent_offset(fdt, nodeoffset);
|
||||
+ if (nodeoffset < 0)
|
||||
+ return nodeoffset;
|
||||
+ len += namelen + 1;
|
||||
+ }
|
||||
+
|
||||
+ /* in case of root pretend it's "/" */
|
||||
+ if (len == 0)
|
||||
+ len++;
|
||||
+ return len;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * overlay_symbol_update - Update the symbols of base tree after a merge
|
||||
+ * @fdt: Base Device Tree blob
|
||||
+ * @fdto: Device tree overlay blob
|
||||
+ *
|
||||
+ * overlay_symbol_update() updates the symbols of the base tree with the
|
||||
+ * symbols of the applied overlay
|
||||
+ *
|
||||
+ * This is the last step in the device tree overlay application
|
||||
+ * process, allowing the reference of overlay symbols by subsequent
|
||||
+ * overlay operations.
|
||||
+ *
|
||||
+ * returns:
|
||||
+ * 0 on success
|
||||
+ * Negative error code on failure
|
||||
+ */
|
||||
+static int overlay_symbol_update(void *fdt, void *fdto)
|
||||
+{
|
||||
+ int root_sym, ov_sym, prop, path_len, fragment, target;
|
||||
+ int len, frag_name_len, ret, rel_path_len;
|
||||
+ const char *s, *e;
|
||||
+ const char *path;
|
||||
+ const char *name;
|
||||
+ const char *frag_name;
|
||||
+ const char *rel_path;
|
||||
+ const char *target_path;
|
||||
+ char *buf;
|
||||
+ void *p;
|
||||
+
|
||||
+ ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
|
||||
+
|
||||
+ /* if no overlay symbols exist no problem */
|
||||
+ if (ov_sym < 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
|
||||
+
|
||||
+ /* it no root symbols exist we should create them */
|
||||
+ if (root_sym == -FDT_ERR_NOTFOUND)
|
||||
+ root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
|
||||
+
|
||||
+ /* any error is fatal now */
|
||||
+ if (root_sym < 0)
|
||||
+ return root_sym;
|
||||
+
|
||||
+ /* iterate over each overlay symbol */
|
||||
+ fdt_for_each_property_offset(prop, fdto, ov_sym) {
|
||||
+ path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
|
||||
+ if (!path)
|
||||
+ return path_len;
|
||||
+
|
||||
+ /* verify it's a string property (terminated by a single \0) */
|
||||
+ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
|
||||
+ return -FDT_ERR_BADVALUE;
|
||||
+
|
||||
+ /* keep end marker to avoid strlen() */
|
||||
+ e = path + path_len;
|
||||
+
|
||||
+ /* format: /<fragment-name>/__overlay__/<relative-subnode-path> */
|
||||
+
|
||||
+ if (*path != '/')
|
||||
+ return -FDT_ERR_BADVALUE;
|
||||
+
|
||||
+ /* get fragment name first */
|
||||
+ s = strchr(path + 1, '/');
|
||||
+ if (!s)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ frag_name = path + 1;
|
||||
+ frag_name_len = s - path - 1;
|
||||
+
|
||||
+ /* verify format; safe since "s" lies in \0 terminated prop */
|
||||
+ len = sizeof("/__overlay__/") - 1;
|
||||
+ if ((e - s) < len || memcmp(s, "/__overlay__/", len))
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ rel_path = s + len;
|
||||
+ rel_path_len = e - rel_path;
|
||||
+
|
||||
+ /* find the fragment index in which the symbol lies */
|
||||
+ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
|
||||
+ frag_name_len);
|
||||
+ /* not found? */
|
||||
+ if (ret < 0)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+ fragment = ret;
|
||||
+
|
||||
+ /* an __overlay__ subnode must exist */
|
||||
+ ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
|
||||
+ if (ret < 0)
|
||||
+ return -FDT_ERR_BADOVERLAY;
|
||||
+
|
||||
+ /* get the target of the fragment */
|
||||
+ ret = overlay_get_target(fdt, fdto, fragment, &target_path);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ target = ret;
|
||||
+
|
||||
+ /* if we have a target path use */
|
||||
+ if (!target_path) {
|
||||
+ ret = get_path_len(fdt, target);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ len = ret;
|
||||
+ } else {
|
||||
+ len = strlen(target_path);
|
||||
+ }
|
||||
+
|
||||
+ ret = fdt_setprop_placeholder(fdt, root_sym, name,
|
||||
+ len + (len > 1) + rel_path_len + 1, &p);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (!target_path) {
|
||||
+ /* again in case setprop_placeholder changed it */
|
||||
+ ret = overlay_get_target(fdt, fdto, fragment, &target_path);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ target = ret;
|
||||
+ }
|
||||
+
|
||||
+ buf = p;
|
||||
+ if (len > 1) { /* target is not root */
|
||||
+ if (!target_path) {
|
||||
+ ret = fdt_get_path(fdt, target, buf, len + 1);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ } else
|
||||
+ memcpy(buf, target_path, len + 1);
|
||||
+
|
||||
+ } else
|
||||
+ len--;
|
||||
+
|
||||
+ buf[len] = '/';
|
||||
+ memcpy(buf + len + 1, rel_path, rel_path_len);
|
||||
+ buf[len + 1 + rel_path_len] = '\0';
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int fdt_overlay_apply(void *fdt, void *fdto)
|
||||
+{
|
||||
+ uint32_t delta = fdt_get_max_phandle(fdt);
|
||||
+ int ret;
|
||||
+
|
||||
+ FDT_CHECK_HEADER(fdt);
|
||||
+ FDT_CHECK_HEADER(fdto);
|
||||
+
|
||||
+ ret = overlay_adjust_local_phandles(fdto, delta);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = overlay_update_local_references(fdto, delta);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = overlay_fixup_phandles(fdt, fdto);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = overlay_merge(fdt, fdto);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ ret = overlay_symbol_update(fdt, fdto);
|
||||
+ if (ret)
|
||||
+ goto err;
|
||||
+
|
||||
+ /*
|
||||
+ * The overlay has been damaged, erase its magic.
|
||||
+ */
|
||||
+ fdt_set_magic(fdto, ~0);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err:
|
||||
+ /*
|
||||
+ * The overlay might have been damaged, erase its magic.
|
||||
+ */
|
||||
+ fdt_set_magic(fdto, ~0);
|
||||
+
|
||||
+ /*
|
||||
+ * The base device tree might have been damaged, erase its
|
||||
+ * magic.
|
||||
+ */
|
||||
+ fdt_set_magic(fdt, ~0);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
diff --git a/scripts/dtc/fdt_ro.c b/scripts/dtc/fdt_ro.c
|
||||
index 50007f61c..dfb3236da 100644
|
||||
--- a/scripts/dtc/fdt_ro.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user