summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/lib32/bootm.c4
-rw-r--r--arch/arm/lib32/bootu.c5
-rw-r--r--arch/arm/lib32/bootz.c3
-rw-r--r--arch/arm/lib64/armlinux.c5
-rw-r--r--arch/kvx/lib/bootm.c6
-rw-r--r--arch/mips/lib/bootm.c4
-rw-r--r--arch/powerpc/lib/ppclinux.c4
-rw-r--r--arch/riscv/lib/bootm.c5
-rw-r--r--commands/of_overlay.c4
-rw-r--r--common/blspec.c73
-rw-r--r--common/oftree.c2
-rw-r--r--drivers/of/of_firmware.c111
-rw-r--r--drivers/of/overlay.c5
-rw-r--r--include/of.h17
14 files changed, 163 insertions, 85 deletions
diff --git a/arch/arm/lib32/bootm.c b/arch/arm/lib32/bootm.c
index 0ffb374cf1..2bba585e96 100644
--- a/arch/arm/lib32/bootm.c
+++ b/arch/arm/lib32/bootm.c
@@ -325,6 +325,10 @@ static int __do_bootm_linux(struct image_data *data, unsigned long free_mem,
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
if (data->tee_res)
tee = (void *)data->tee_res->start;
else
diff --git a/arch/arm/lib32/bootu.c b/arch/arm/lib32/bootu.c
index 24c744da58..0540a16280 100644
--- a/arch/arm/lib32/bootu.c
+++ b/arch/arm/lib32/bootu.c
@@ -8,7 +8,7 @@
static int do_bootu(int argc, char *argv[])
{
- int fd;
+ int fd, ret;
void *kernel = NULL;
void *oftree = NULL;
@@ -25,6 +25,9 @@ static int do_bootu(int argc, char *argv[])
#ifdef CONFIG_OFTREE
oftree = of_get_fixed_tree(NULL);
#endif
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
start_linux(kernel, 0, 0, 0, oftree, ARM_STATE_SECURE, NULL);
diff --git a/arch/arm/lib32/bootz.c b/arch/arm/lib32/bootz.c
index a2a26ac2f9..486e945a74 100644
--- a/arch/arm/lib32/bootz.c
+++ b/arch/arm/lib32/bootz.c
@@ -111,6 +111,9 @@ static int do_bootz(int argc, char *argv[])
#ifdef CONFIG_OFTREE
oftree = of_get_fixed_tree(NULL);
#endif
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
start_linux(zimage, swap, 0, 0, oftree, ARM_STATE_SECURE, NULL);
diff --git a/arch/arm/lib64/armlinux.c b/arch/arm/lib64/armlinux.c
index 0ba4d30b8e..078841b1d4 100644
--- a/arch/arm/lib64/armlinux.c
+++ b/arch/arm/lib64/armlinux.c
@@ -11,11 +11,16 @@ static int do_bootm_linux(struct image_data *data)
void (*fn)(unsigned long dtb, unsigned long x1, unsigned long x2,
unsigned long x3);
phys_addr_t devicetree;
+ int ret;
fn = booti_load_image(data, &devicetree);
if (IS_ERR(fn))
return PTR_ERR(fn);
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
fn(devicetree, 0, 0, 0);
diff --git a/arch/kvx/lib/bootm.c b/arch/kvx/lib/bootm.c
index 198eef7980..4d17e1846d 100644
--- a/arch/kvx/lib/bootm.c
+++ b/arch/kvx/lib/bootm.c
@@ -24,11 +24,17 @@ typedef void __noreturn (*boot_func_entry)(unsigned long, void *);
static int do_boot_entry(struct image_data *data, boot_func_entry entry,
void *fdt_load_addr)
{
+ int ret;
+
printf("starting elf (entry at %p)\n", entry);
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
/* Synchronize I-cache with D-cache */
diff --git a/arch/mips/lib/bootm.c b/arch/mips/lib/bootm.c
index 6c56202ea9..b71b8d5c38 100644
--- a/arch/mips/lib/bootm.c
+++ b/arch/mips/lib/bootm.c
@@ -65,6 +65,10 @@ static int do_bootm_elf(struct image_data *data)
if (data->dryrun)
goto bootm_free_fdt;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
entry = (void *) (unsigned long) data->os_address;
diff --git a/arch/powerpc/lib/ppclinux.c b/arch/powerpc/lib/ppclinux.c
index 05c29be7da..b4cb59a524 100644
--- a/arch/powerpc/lib/ppclinux.c
+++ b/arch/powerpc/lib/ppclinux.c
@@ -67,6 +67,10 @@ static int do_bootm_linux(struct image_data *data)
if (data->dryrun)
return 0;
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
/* Relocate the device tree if outside the initial
* Linux mapped TLB.
*/
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index b3e41de4a8..8504ee63a9 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -8,11 +8,16 @@ static int do_bootm_linux(struct image_data *data)
{
void (*fn)(unsigned long a0, unsigned long dtb, unsigned long a2);
phys_addr_t devicetree;
+ int ret;
fn = booti_load_image(data, &devicetree);
if (IS_ERR(fn))
return PTR_ERR(fn);
+ ret = of_overlay_load_firmware();
+ if (ret)
+ return ret;
+
shutdown_barebox();
fn(0, devicetree, 0);
diff --git a/commands/of_overlay.c b/commands/of_overlay.c
index 71f41a3c04..b3660b4bf1 100644
--- a/commands/of_overlay.c
+++ b/commands/of_overlay.c
@@ -32,10 +32,6 @@ static int do_of_overlay(int argc, char *argv[])
if (IS_ERR(overlay))
return PTR_ERR(overlay);
- ret = of_firmware_load_overlay(overlay);
- if (ret)
- goto err;
-
ret = of_register_overlay(overlay);
if (ret) {
printf("cannot apply oftree overlay: %s\n", strerror(-ret));
diff --git a/common/blspec.c b/common/blspec.c
index 148e52b038..ca96a45487 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -32,71 +32,33 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
val ? strlen(val) + 1 : 0, 1);
}
-static int blspec_apply_oftree_overlay(char *file, const char *abspath,
- int dryrun)
-{
- int ret = 0;
- struct fdt_header *fdt;
- struct device_node *overlay;
- char *path;
- size_t size;
-
- path = basprintf("%s/%s", abspath, file);
-
- fdt = read_file(path, &size);
- if (!fdt) {
- pr_warn("unable to read \"%s\"\n", path);
- ret = -EINVAL;
- goto out;
- }
-
- overlay = of_unflatten_dtb(fdt, size);
- free(fdt);
- if (IS_ERR(overlay)) {
- ret = PTR_ERR(overlay);
- goto out;
- }
-
- if (dryrun) {
- pr_info("dry run: skip overlay %s\n", path);
- of_delete_node(overlay);
- goto out;
- }
-
- ret = of_firmware_load_overlay(overlay);
- if (ret) {
- pr_warn("failed to load firmware: skip overlay \"%s\"\n", path);
- of_delete_node(overlay);
- goto out;
- }
-
- ret = of_register_overlay(overlay);
- if (ret) {
- pr_warn("cannot register devicetree overlay \"%s\"\n", path);
- of_delete_node(overlay);
- }
-
-out:
- free(path);
-
- return ret;
-}
-
-static void blspec_apply_oftree_overlays(const char *overlays,
- const char *abspath, int dryrun)
+static int blspec_overlay_fixup(struct device_node *root, void *ctx)
{
+ struct blspec_entry *entry = ctx;
+ const char *overlays;
char *overlay;
char *sep, *freep;
+ overlays = blspec_entry_var_get(entry, "devicetree-overlay");
+
sep = freep = xstrdup(overlays);
while ((overlay = strsep(&sep, " "))) {
+ char *path;
+
if (!*overlay)
continue;
- blspec_apply_oftree_overlay(overlay, abspath, dryrun);
+
+ path = basprintf("%s/%s", entry->rootpath, overlay);
+
+ of_overlay_apply_file(root, path);
+
+ free(path);
}
free(freep);
+
+ return 0;
}
/*
@@ -153,7 +115,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
}
if (overlays)
- blspec_apply_oftree_overlays(overlays, abspath, dryrun);
+ of_register_fixup(blspec_overlay_fixup, entry);
if (initrd)
data.initrd_file = basprintf("%s/%s", abspath, initrd);
@@ -189,6 +151,9 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
if (ret)
pr_err("Booting failed\n");
+ if (overlays)
+ of_unregister_fixup(blspec_overlay_fixup, entry);
+
firmware_set_searchpath(old_fws);
err_out:
diff --git a/common/oftree.c b/common/oftree.c
index da8043809b..7028c49aca 100644
--- a/common/oftree.c
+++ b/common/oftree.c
@@ -326,6 +326,8 @@ int of_fix_tree(struct device_node *node)
struct of_fixup *of_fixup;
int ret;
+ of_overlay_load_firmware_clear();
+
list_for_each_entry(of_fixup, &of_fixup_list, list) {
ret = of_fixup->fixup(node, of_fixup->context);
if (ret)
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
index 12ce1d95d0..2d8245a02e 100644
--- a/drivers/of/of_firmware.c
+++ b/drivers/of/of_firmware.c
@@ -21,12 +21,21 @@ static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
return NULL;
}
+struct fw_load_entry {
+ struct firmware_mgr *mgr;
+ char *firmware;
+ struct list_head list;
+};
+
+static LIST_HEAD(fw_load_list);
+
static int load_firmware(struct device_node *target,
struct device_node *fragment, void *unused)
{
const char *firmware_name;
int err;
struct firmware_mgr *mgr;
+ struct fw_load_entry *fle;
err = of_property_read_string(fragment,
"firmware-name", &firmware_name);
@@ -43,32 +52,86 @@ static int load_firmware(struct device_node *target,
if (!mgr)
return -EINVAL;
- err = firmwaremgr_load_file(mgr, firmware_name);
+ fle = xzalloc(sizeof(*fle));
+ fle->mgr = mgr;
+ fle->firmware = xstrdup(firmware_name);
- return err;
+ list_add_tail(&fle->list, &fw_load_list);
+
+ return 0;
}
-int of_firmware_load_overlay(struct device_node *overlay)
+/*
+ * The dt overlay API says that a "firmware-name" property found in an overlay
+ * node compatible to "fpga-region" triggers loading of the firmware with the
+ * name given in the "firmware-name" property.
+ *
+ * barebox applies overlays to the Kernel device tree as part of booting the
+ * Kernel. When a firmware is needed for an overlay then it shall be loaded,
+ * so that the Kernel already finds the firmware loaded.
+ *
+ * In barebox overlays are applied as a of_fixup to the desired tree. The fixups
+ * might be executed multiple times not only as part of booting the Kernel, but
+ * also during of_diff command execution and other actions. It's not desired
+ * that we (re-)load all firmwares each time this happens, so the process is
+ * splitted up. During application of an overlay the needed firmwares are only
+ * collected to a list, but not actually loaded. Only once it's clear we want to
+ * boot with that device tree the firmwares are loaded by explicitly calling
+ * of_overlay_load_firmware().
+ */
+
+/**
+ * of_overlay_pre_load_firmware() - check overlay node for firmware to load
+ * @root: The device tree to apply the overlay to
+ * @overlay: The overlay
+ *
+ * This function checks the given overlay for firmware to load. If a firmware
+ * is needed then it is not directly loaded, but instead added to a list of
+ * firmware to be loaded. The firmware files on this list can then be loaded
+ * with of_overlay_load_firmware().
+ *
+ * Return: 0 for success or negative error code otherwise
+ */
+int of_overlay_pre_load_firmware(struct device_node *root, struct device_node *overlay)
{
- int err;
- struct device_node *root;
- struct device_node *resolved;
- struct device_node *ovl;
-
- root = of_get_root_node();
- resolved = of_resolve_phandles(root, overlay);
- /*
- * If the overlay cannot be resolved, use the load_firmware callback
- * with the unresolved overlay to verify that the overlay does not
- * depend on a firmware to be loaded. If a required firmware cannot be
- * loaded, the overlay must not be applied.
- */
- ovl = resolved ? resolved : overlay;
-
- err = of_process_overlay(root, ovl, load_firmware, NULL);
-
- if (resolved)
- of_delete_node(resolved);
-
- return err;
+ return of_process_overlay(root, overlay, load_firmware, NULL);
+}
+
+/**
+ * of_overlay_load_firmware() - load all firmware files
+ *
+ * This function loads all firmware files previously collected in
+ * of_overlay_pre_load_firmware().
+ *
+ * Return: 0 when all firmware files could be loaded, negative error code
+ * otherwise.
+ */
+int of_overlay_load_firmware(void)
+{
+ struct fw_load_entry *fle;
+ int ret;
+
+ list_for_each_entry(fle, &fw_load_list, list) {
+ ret = firmwaremgr_load_file(fle->mgr, fle->firmware);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * of_overlay_load_firmware_clear() - Clear list of firmware files
+ *
+ * This function clears the list of firmware files.
+ */
+void of_overlay_load_firmware_clear(void)
+{
+ struct fw_load_entry *fle, *tmp;
+
+ list_for_each_entry_safe(fle, tmp, &fw_load_list, list) {
+ list_del(&fle->list);
+ free(fle->firmware);
+ free(fle);
+ }
}
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index f238b39ff1..26b5fee775 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -181,6 +181,10 @@ int of_overlay_apply_tree(struct device_node *root,
if (!resolved)
return -EINVAL;
+ err = of_overlay_pre_load_firmware(root, resolved);
+ if (err)
+ goto out_err;
+
/* Copy symbols from resolved overlay to base device tree */
of_overlay_apply_symbols(root, resolved);
@@ -191,6 +195,7 @@ int of_overlay_apply_tree(struct device_node *root,
pr_warn("failed to apply %s\n", fragment->name);
}
+out_err:
of_delete_node(resolved);
return err;
diff --git a/include/of.h b/include/of.h
index e1752fb28f..59c1250fb2 100644
--- a/include/of.h
+++ b/include/of.h
@@ -1035,7 +1035,9 @@ int of_process_overlay(struct device_node *root,
struct device_node *overlay, void *data),
void *data);
-int of_firmware_load_overlay(struct device_node *overlay);
+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);
#else
static inline struct device_node *of_resolve_phandles(struct device_node *root,
const struct device_node *overlay)
@@ -1069,10 +1071,21 @@ static inline int of_process_overlay(struct device_node *root,
return -ENOSYS;
}
-static inline int of_firmware_load_overlay(struct device_node *overlay)
+static inline int of_overlay_pre_load_firmware(struct device_node *root,
+ struct device_node *overlay)
{
return -ENOSYS;
}
+
+static inline int of_overlay_load_firmware(void)
+{
+ return 0;
+}
+
+static inline void of_overlay_load_firmware_clear(void)
+{
+}
+
#endif
#endif /* __OF_H */