mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Sascha Hauer <s.hauer@pengutronix.de>
To: Barebox List <barebox@lists.infradead.org>
Subject: [PATCH 14/18] overlay: Add filters to choose which overlays to apply
Date: Thu, 24 Jun 2021 10:52:19 +0200	[thread overview]
Message-ID: <20210624085223.14616-15-s.hauer@pengutronix.de> (raw)
In-Reply-To: <20210624085223.14616-1-s.hauer@pengutronix.de>

This adds a filter mechanism to choose which overlays to apply.
Filters can either match on the filename or on the content of an
overlay. Two generic filters are registered, one matching filename
patterns given in global.of.overlay.filepattern, the other matching
device tree compatibles given in global.of.overlay.compatible. Other
board or SoC specific filters can be registered and activated using
the global.of.overlay.filter variable.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/of/overlay.c | 314 ++++++++++++++++++++++++++++++++++++++++++-
 include/of.h         |  18 ++-
 2 files changed, 329 insertions(+), 3 deletions(-)

diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 26b5fee775..a1e0c48fb4 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -16,6 +16,8 @@
 #include <string.h>
 #include <libfile.h>
 #include <fs.h>
+#include <libbb.h>
+#include <fnmatch.h>
 
 static struct device_node *find_target(struct device_node *root,
 				       struct device_node *fragment)
@@ -165,6 +167,8 @@ static int of_overlay_apply_fragment(struct device_node *root,
 	return of_overlay_apply(target, overlay);
 }
 
+static char *of_overlay_compatible;
+
 /**
  * Apply the overlay on the passed devicetree root
  * @root: the devicetree onto which the overlay will be applied
@@ -201,13 +205,105 @@ out_err:
 	return err;
 }
 
-int of_overlay_apply_file(struct device_node *root, const char *filename)
+static char *of_overlay_filter;
+
+static LIST_HEAD(of_overlay_filters);
+
+static struct of_overlay_filter *of_overlay_find_filter(const char *name)
+{
+	struct of_overlay_filter *f;
+
+	list_for_each_entry(f, &of_overlay_filters, list)
+		if (!strcmp(f->name, name))
+			return f;
+	return NULL;
+}
+
+static bool of_overlay_matches_filter(const char *filename, struct device_node *ovl)
+{
+	struct of_overlay_filter *filter;
+	char *p, *path, *n;
+	bool apply = false;
+	bool have_filename_filter = false;
+	bool have_content_filter = false;
+
+	p = path = strdup(of_overlay_filter);
+
+	while ((n = strsep_unescaped(&p, " "))) {
+		int score = 0;
+
+		if (!*n)
+			continue;
+
+		filter = of_overlay_find_filter(n);
+		if (!filter) {
+			pr_err("Ignoring unknown filter %s\n", n);
+			continue;
+		}
+
+		if (filter->filter_filename)
+			have_filename_filter = true;
+		if (filter->filter_content)
+			have_content_filter = true;
+
+		if (filename) {
+			if (filter->filter_filename &&
+			    filter->filter_filename(filter, kbasename(filename)))
+				score++;
+		} else {
+			score++;
+		}
+
+		if (ovl) {
+			if (filter->filter_content &&
+			    filter->filter_content(filter, ovl))
+				score++;
+		} else {
+			score++;
+		}
+
+		if (score == 2) {
+			apply = true;
+			break;
+		}
+	}
+
+	free(path);
+
+	/* No filter found at all, no match */
+	if (!have_filename_filter && !have_content_filter)
+		return false;
+
+	/* Want to match filename, but we do not have a filename_filter */
+	if (filename && !have_filename_filter)
+		return true;
+
+	/* Want to match content, but we do not have a content_filter */
+	if (ovl && !have_content_filter)
+		return true;
+
+	if (apply)
+		pr_debug("filename %s, overlay %p: match against filter %s\n",
+			 filename ?: "<NONE>",
+			 ovl, filter->name);
+	else
+		pr_debug("filename %s, overlay %p: no match\n",
+			 filename ?: "<NONE>", ovl);
+
+	return apply;
+}
+
+int of_overlay_apply_file(struct device_node *root, const char *filename,
+			  bool filter)
 {
 	void *fdt;
 	struct device_node *ovl;
 	size_t size;
 	int ret;
 
+	if (filter && !of_overlay_matches_filter(filename, NULL))
+		return 0;
+
 	ret = read_file_2(filename, &size, &fdt, FILESIZE_MAX);
 	if (ret)
 		return ret;
@@ -221,6 +317,9 @@ int of_overlay_apply_file(struct device_node *root, const char *filename)
 		return PTR_ERR(ovl);
 	}
 
+	if (filter && !of_overlay_matches_filter(NULL, ovl))
+		return 0;
+
 	ret = of_overlay_apply_tree(root, ovl);
 	if (ret == -ENODEV)
 		pr_debug("Not applied %s (not compatible)\n", filename);
@@ -293,3 +392,216 @@ int of_register_overlay(struct device_node *overlay)
 {
 	return of_register_fixup(of_overlay_fixup, overlay);
 }
+
+static char *of_overlay_filepattern;
+static char *of_overlay_dir;
+static char *of_overlay_basedir;
+
+/**
+ * of_overlay_set_basedir - set the overlay basedir
+ * @path: The new overlay basedir
+ *
+ * This specifies the base directory where overlay files are expected. By
+ * default this is the root directory, but it is overwritten by blspec to
+ * point to the rootfs of the about-to-be-booted system.
+ */
+void of_overlay_set_basedir(const char *path)
+{
+	free(of_overlay_basedir);
+	of_overlay_basedir = strdup(path);
+}
+
+static int of_overlay_apply_dir(struct device_node *root, const char *dirname,
+				bool filter)
+{
+	char *p, *path;
+	int ret = 0;
+	DIR *dir;
+
+	if (!dirname || !*dirname)
+		return 0;
+
+	pr_debug("Applying overlays from %s\n", dirname);
+
+	dir = opendir(dirname);
+	if (!dir)
+		return -errno;
+
+	p = path = strdup(of_overlay_filepattern);
+
+	while (1) {
+		struct dirent *ent;
+		char *filename;
+
+		ent = readdir(dir);
+		if (!ent)
+			break;
+
+		if (!strcmp(dir->d.d_name, ".") || !strcmp(dir->d.d_name, ".."))
+			continue;
+
+		filename = basprintf("%s/%s", dirname, dir->d.d_name);
+
+		of_overlay_apply_file(root, filename, filter);
+
+		free(filename);
+	}
+
+	closedir(dir);
+
+	return ret;
+}
+
+static int of_overlay_global_fixup(struct device_node *root, void *data)
+{
+	char *dir;
+	int ret;
+
+	if (*of_overlay_dir == '/')
+		return of_overlay_apply_dir(root, of_overlay_dir, true);
+
+	dir = concat_path_file(of_overlay_basedir, of_overlay_dir);
+
+	ret = of_overlay_apply_dir(root, of_overlay_dir, true);
+
+	free(dir);
+
+	return ret;
+}
+
+/**
+ * of_overlay_register_filter - register a new overlay filter
+ * @filter: The new filter
+ *
+ * Register a new overlay filter. A filter can either match on
+ * the filename or on the content of an overlay, but not on both.
+ * If that's desired two filters have to be registered.
+ *
+ * @return: 0 for success, negative error code otherwise
+ */
+int of_overlay_register_filter(struct of_overlay_filter *filter)
+{
+	if (filter->filter_filename && filter->filter_content)
+		return -EINVAL;
+
+	list_add_tail(&filter->list, &of_overlay_filters);
+
+	return 0;
+}
+
+/**
+ * of_overlay_filter_filename - A filter that matches on the filename of
+ *                                an overlay
+ * @f: The filter
+ * @filename: The filename of the overlay
+ *
+ * This filter matches when the filename matches one of the patterns given
+ * in global.of.overlay.filepattern. global.of.overlay.filepattern shall
+ * contain a space separated list of wildcard patterns.
+ *
+ * @return: True when the overlay shall be applied, false otherwise.
+ */
+static bool of_overlay_filter_filename(struct of_overlay_filter *f,
+				       const char *filename)
+{
+	char *p, *path, *n;
+	int ret;
+	bool apply;
+
+	p = path = strdup(of_overlay_filepattern);
+
+	while ((n = strsep_unescaped(&p, " "))) {
+		if (!*n)
+			continue;
+
+		ret = fnmatch(n, filename, 0);
+
+		if (!ret) {
+			apply = true;
+			goto out;
+		}
+	}
+
+	apply = false;
+out:
+	free(path);
+
+	return apply;
+}
+
+static struct of_overlay_filter of_overlay_filepattern_filter = {
+	.name = "filepattern",
+	.filter_filename = of_overlay_filter_filename,
+};
+
+/**
+ * of_overlay_filter_compatible - A filter that matches on the compatible of
+ *                                an overlay
+ * @f: The filter
+ * @ovl: The overlay
+ *
+ * This filter matches when the compatible of an overlay matches to one
+ * of the compatibles given in global.of.overlay.compatible. When the
+ * overlay doesn't contain a compatible entry it is considered matching.
+ * Also when no compatibles are given in global.of.overlay.compatible
+ * all overlays will match.
+ *
+ * @return: True when the overlay shall be applied, false otherwise.
+ */
+static bool of_overlay_filter_compatible(struct of_overlay_filter *f,
+					struct device_node *ovl)
+{
+	char *p, *n, *compatibles;
+	bool res = false;
+
+	if (!of_overlay_compatible || !*of_overlay_compatible)
+		return true;
+	if (!of_find_property(ovl, "compatible", NULL))
+		return true;
+
+	p = compatibles = xstrdup(of_overlay_compatible);
+
+	while ((n = strsep_unescaped(&p, " "))) {
+		if (!*n)
+			continue;
+
+		if (of_device_is_compatible(ovl, n)) {
+			res = true;
+			break;
+		}
+	}
+
+	free(compatibles);
+
+	return res;
+}
+
+static struct of_overlay_filter of_overlay_compatible_filter = {
+	.name = "compatible",
+	.filter_content = of_overlay_filter_compatible,
+};
+
+static int of_overlay_init(void)
+{
+	of_overlay_filepattern = strdup("*");
+	of_overlay_filter = strdup("filepattern compatible");
+	of_overlay_set_basedir("/");
+
+	globalvar_add_simple_string("of.overlay.compatible", &of_overlay_compatible);
+	globalvar_add_simple_string("of.overlay.filepattern", &of_overlay_filepattern);
+	globalvar_add_simple_string("of.overlay.filter", &of_overlay_filter);
+	globalvar_add_simple_string("of.overlay.dir", &of_overlay_dir);
+
+	of_overlay_register_filter(&of_overlay_filepattern_filter);
+	of_overlay_register_filter(&of_overlay_compatible_filter);
+
+	of_register_fixup(of_overlay_global_fixup, NULL);
+
+	return 0;
+}
+device_initcall(of_overlay_init);
+
+BAREBOX_MAGICVAR(global.of.overlay.compatible, "space separated list of compatibles an overlay must match");
+BAREBOX_MAGICVAR(global.of.overlay.filepattern, "space separated list of filepatterns an overlay must match");
+BAREBOX_MAGICVAR(global.of.overlay.dir, "Directory to look for dt overlays");
+BAREBOX_MAGICVAR(global.of.overlay.filter, "space separated list of filters");
diff --git a/include/of.h b/include/of.h
index 59c1250fb2..b698220b1e 100644
--- a/include/of.h
+++ b/include/of.h
@@ -1022,12 +1022,20 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
 	return node;
 }
 
+struct of_overlay_filter {
+	bool (*filter_filename)(struct of_overlay_filter *, const char *filename);
+	bool (*filter_content)(struct of_overlay_filter *, struct device_node *);
+	char *name;
+	struct list_head list;
+};
+
 #ifdef CONFIG_OF_OVERLAY
 struct device_node *of_resolve_phandles(struct device_node *root,
 					const struct device_node *overlay);
 int of_overlay_apply_tree(struct device_node *root,
 			  struct device_node *overlay);
-int of_overlay_apply_file(struct device_node *root, const char *filename);
+int of_overlay_apply_file(struct device_node *root, const char *filename,
+			  bool filter);
 int of_register_overlay(struct device_node *overlay);
 int of_process_overlay(struct device_node *root,
 		    struct device_node *overlay,
@@ -1038,6 +1046,8 @@ int of_process_overlay(struct device_node *root,
 int of_overlay_pre_load_firmware(struct device_node *root, struct device_node *overlay);
 int of_overlay_load_firmware(void);
 void of_overlay_load_firmware_clear(void);
+void of_overlay_set_basedir(const char *path);
+int of_overlay_register_filter(struct of_overlay_filter *);
 #else
 static inline struct device_node *of_resolve_phandles(struct device_node *root,
 					const struct device_node *overlay)
@@ -1052,7 +1062,7 @@ static inline int of_overlay_apply_tree(struct device_node *root,
 }
 
 static inline int of_overlay_apply_file(struct device_node *root,
-					const char *filename)
+					const char *filename, bool filter)
 {
 	return -ENOSYS;
 }
@@ -1086,6 +1096,10 @@ static inline void of_overlay_load_firmware_clear(void)
 {
 }
 
+static inline void of_overlay_set_basedir(const char *path)
+{
+}
+
 #endif
 
 #endif /* __OF_H */
-- 
2.29.2


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


  parent reply	other threads:[~2021-06-24  8:56 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-24  8:52 [PATCH v3 00/18] Apply device tree overlays to kernel tree Sascha Hauer
2021-06-24  8:52 ` [PATCH 01/18] of: of_copy_node(): Copy phandles as well Sascha Hauer
2021-06-24  8:52 ` [PATCH 02/18] of: Add function to duplicate a device tree Sascha Hauer
2021-06-24  8:52 ` [PATCH 03/18] fdt: Check blob size during unflattening Sascha Hauer
2021-06-24  8:52 ` [PATCH 04/18] firmware: make device_node argument non const Sascha Hauer
2021-06-24  8:52 ` [PATCH 05/18] libbb: Add find_path function Sascha Hauer
2021-06-24  8:52 ` [PATCH 06/18] firmware: consolidate ifdefs Sascha Hauer
2021-06-24  8:52 ` [PATCH 07/18] firmware: Add search path Sascha Hauer
2021-06-24  8:52 ` [PATCH 08/18] firmware: Fix device_node matching Sascha Hauer
2021-06-24  8:52 ` [PATCH 09/18] firmware: recognize by reproducible name Sascha Hauer
2021-06-24  8:52 ` [PATCH 10/18] blspec: Set firmware searchpath Sascha Hauer
2021-06-24  8:52 ` [PATCH 11/18] overlay: Add of_overlay_apply_file() Sascha Hauer
2021-06-24  8:52 ` [PATCH 12/18] firmware: Load from global search path Sascha Hauer
2021-06-24  8:52 ` [PATCH 13/18] blspec: Rework firmware load Sascha Hauer
2021-06-24  8:52 ` Sascha Hauer [this message]
2021-06-28 18:58   ` [PATCH 14/18] overlay: Add filters to choose which overlays to apply Michael Riesch
2021-06-28 20:13     ` Sascha Hauer
2021-06-24  8:52 ` [PATCH 15/18] blspec: Apply overlays from rootfs Sascha Hauer
2021-06-24  8:52 ` [PATCH 16/18] doc: devicetree: Refer to internal device tree also as live tree Sascha Hauer
2021-06-24  8:52 ` [PATCH 17/18] Documentation: Add documentation for device tree overlays Sascha Hauer
2021-06-24  8:52 ` [PATCH 18/18] of_firmware: Fix handling of firmware-name property Sascha Hauer

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=20210624085223.14616-15-s.hauer@pengutronix.de \
    --to=s.hauer@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