diff options
-rw-r--r-- | arch/arm/lib32/bootm.c | 4 | ||||
-rw-r--r-- | arch/arm/lib32/bootu.c | 5 | ||||
-rw-r--r-- | arch/arm/lib32/bootz.c | 3 | ||||
-rw-r--r-- | arch/arm/lib64/armlinux.c | 5 | ||||
-rw-r--r-- | arch/kvx/lib/bootm.c | 6 | ||||
-rw-r--r-- | arch/mips/lib/bootm.c | 4 | ||||
-rw-r--r-- | arch/powerpc/lib/ppclinux.c | 4 | ||||
-rw-r--r-- | arch/riscv/lib/bootm.c | 5 | ||||
-rw-r--r-- | commands/of_overlay.c | 4 | ||||
-rw-r--r-- | common/blspec.c | 73 | ||||
-rw-r--r-- | common/oftree.c | 2 | ||||
-rw-r--r-- | drivers/of/of_firmware.c | 111 | ||||
-rw-r--r-- | drivers/of/overlay.c | 5 | ||||
-rw-r--r-- | include/of.h | 17 |
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 */ |