summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/blspec.c2
-rw-r--r--drivers/of/overlay.c314
-rw-r--r--include/of.h18
3 files changed, 330 insertions, 4 deletions
diff --git a/common/blspec.c b/common/blspec.c
index ca96a45487..4146915ffb 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -51,7 +51,7 @@ static int blspec_overlay_fixup(struct device_node *root, void *ctx)
path = basprintf("%s/%s", entry->rootpath, overlay);
- of_overlay_apply_file(root, path);
+ of_overlay_apply_file(root, path, false);
free(path);
}
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 26b5fee775..42b309805f 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, 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 */