[PATCH v2 4/6] of: overlay: Introduce target root capability.
From: Pantelis Antoniou
Date: Mon May 16 2016 - 16:18:40 EST
The target facility of an overlay allows the target to be any point
in the live kernel tree, since it usually that's required when
creating overlays for internal SoC devices. The target ends up
to be a single node in the tree.
However when we're dealing with probeable busses this is a problem
since the target node differs according to the bus the plugged
device lies.
Using an overlay creating method using a target root node allows
us to use a single overlay for those cases.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@xxxxxxxxxxxx>
---
drivers/of/overlay.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++----
include/linux/of.h | 8 ++++
2 files changed, 102 insertions(+), 7 deletions(-)
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 2efd4b7..49bdcba 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -76,6 +76,7 @@ struct of_overlay {
struct of_changeset cset;
struct kobject kobj;
int target_index;
+ struct device_node *target_root;
};
/* master enable switch; once set to 0 can't be re-enabled */
@@ -231,22 +232,85 @@ static int of_overlay_apply(struct of_overlay *ov)
static struct device_node *find_target_node(struct of_overlay *ov,
struct device_node *info_node, int index)
{
+ struct device_node *target = NULL, *np;
const char *path;
+ char *newpath;
u32 val;
int ret;
/* first try to go by using the target as a phandle */
ret = of_property_read_u32_index(info_node, "target", index, &val);
- if (ret == 0)
- return of_find_node_by_phandle(val);
+ if (ret == 0) {
+ target = of_find_node_by_phandle(val);
+ if (!target) {
+ pr_err("%s: Could not find target phandle 0x%x\n",
+ __func__, val);
+ return NULL;
+ }
+ goto check_root;
+ }
/* failed, try to locate by path */
ret = of_property_read_string_index(info_node, "target-path", index,
&path);
- if (ret == 0)
- return of_find_node_by_path(path);
+ if (ret == 0) {
+
+ if (!ov->target_root) {
+ target = of_find_node_by_path(path);
+ if (!target)
+ pr_err("%s: Could not find target path \"%s\"\n",
+ __func__, path);
+ return target;
+ }
+
+ /* remove preceding '/' from path; relative path */
+ if (*path == '/') {
+ while (*path == '/')
+ path++;
+
+ newpath = kasprintf(GFP_KERNEL, "%s%s%s",
+ of_node_full_name(ov->target_root),
+ *path ? "/" : "", path);
+ if (!newpath) {
+ pr_err("%s: Could not allocate \"%s%s%s\"\n",
+ __func__,
+ of_node_full_name(ov->target_root),
+ *path ? "/" : "", path);
+ return NULL;
+ }
+ target = of_find_node_by_path(newpath);
+ kfree(newpath);
+
+ return target;
+
+ }
+ /* target is an alias, need to check */
+ target = of_find_node_by_path(path);
+ if (!target) {
+ pr_err("%s: Could not find alias \"%s\"\n",
+ __func__, path);
+ return NULL;
+ }
+ goto check_root;
+ }
return NULL;
+
+check_root:
+ if (!ov->target_root)
+ return target;
+
+ /* got a target, but we have to check it's under target root */
+ for (np = target; np; np = np->parent) {
+ if (np == ov->target_root)
+ return target;
+ }
+ pr_err("%s: target \"%s\" not under target_root \"%s\"\n",
+ __func__, of_node_full_name(target),
+ of_node_full_name(ov->target_root));
+ /* target is not under target_root */
+ of_node_put(target);
+ return NULL;
}
/**
@@ -418,6 +482,7 @@ void of_overlay_release(struct kobject *kobj)
{
struct of_overlay *ov = kobj_to_overlay(kobj);
+ of_node_put(ov->target_root);
kfree(ov);
}
@@ -474,7 +539,7 @@ static struct kobj_type of_overlay_ktype = {
static struct kset *ov_kset;
static int __of_overlay_create(struct device_node *tree,
- int target_index)
+ int target_index, struct device_node *target_root)
{
struct of_overlay *ov;
int err, id;
@@ -490,6 +555,7 @@ static int __of_overlay_create(struct device_node *tree,
ov->id = -1;
ov->target_index = target_index;
+ ov->target_root = of_node_get(target_root);
INIT_LIST_HEAD(&ov->node);
@@ -565,6 +631,7 @@ err_free_idr:
idr_remove(&ov_idr, ov->id);
err_destroy_trans:
of_changeset_destroy(&ov->cset);
+ of_node_put(ov->target_root);
kfree(ov);
mutex_unlock(&of_mutex);
@@ -583,7 +650,7 @@ err_destroy_trans:
*/
int of_overlay_create(struct device_node *tree)
{
- return __of_overlay_create(tree, 0);
+ return __of_overlay_create(tree, 0, NULL);
}
EXPORT_SYMBOL_GPL(of_overlay_create);
@@ -600,10 +667,30 @@ EXPORT_SYMBOL_GPL(of_overlay_create);
*/
int of_overlay_create_target_index(struct device_node *tree, int index)
{
- return __of_overlay_create(tree, index);
+ return __of_overlay_create(tree, index, NULL);
}
EXPORT_SYMBOL_GPL(of_overlay_create_target_index);
+/**
+ * of_overlay_create_target_root() - Create and apply an overlay
+ * under which will be limited to target_root
+ * @tree: Device node containing all the overlays
+ * @target_root: Target root for the overlay.
+ *
+ * Creates and applies an overlay while also keeping track
+ * of the overlay in a list. This list can be used to prevent
+ * illegal overlay removals. The overlay is only allowed to
+ * target nodes under the target_root node.
+ *
+ * Returns the id of the created overlay, or an negative error number
+ */
+int of_overlay_create_target_root(struct device_node *tree,
+ struct device_node *target_root)
+{
+ return __of_overlay_create(tree, 0, target_root);
+}
+EXPORT_SYMBOL_GPL(of_overlay_create_target_root);
+
/* check whether the given node, lies under the given tree */
static int overlay_subtree_check(struct device_node *tree,
struct device_node *dn)
diff --git a/include/linux/of.h b/include/linux/of.h
index 4548773..23d334c 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -1421,6 +1421,8 @@ int of_overlay_destroy(int id);
int of_overlay_destroy_all(void);
int of_overlay_create_target_index(struct device_node *tree, int index);
+int of_overlay_create_target_root(struct device_node *tree,
+ struct device_node *target_root);
#else
@@ -1445,6 +1447,12 @@ static inline int of_overlay_create_target_index(struct device_node *tree,
return -ENOTSUPP;
}
+static inline int of_overlay_create_target_root(struct device_node *tree,
+ struct device_node *target_root)
+{
+ return -ENOTSUPP;
+}
+
#endif
#endif /* _LINUX_OF_H */
--
1.7.12