diff options
Diffstat (limited to 'common/blspec.c')
-rw-r--r-- | common/blspec.c | 366 |
1 files changed, 163 insertions, 203 deletions
diff --git a/common/blspec.c b/common/blspec.c index 7fb62d310f..23a24c63db 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -1,15 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-or-later #define pr_fmt(fmt) "blspec: " fmt #include <environment.h> @@ -26,6 +15,7 @@ #include <libbb.h> #include <init.h> #include <bootm.h> +#include <glob.h> #include <net.h> #include <fs.h> #include <of.h> @@ -43,76 +33,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) +static int blspec_overlay_fixup(struct device_node *root, void *ctx) { - int ret = 0; - struct fdt_header *fdt; - struct device_node *overlay; - char *path; - char *firmware_path; - - path = basprintf("%s/%s", abspath, file); - - fdt = read_file(path, NULL); - if (!fdt) { - pr_warn("unable to read \"%s\"\n", path); - ret = -EINVAL; - goto out; - } - - overlay = of_unflatten_dtb(fdt); - 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; - } + struct blspec_entry *entry = ctx; + const char *overlays; + char *overlay; + char *sep, *freep; - /* - * Unfortunately the device tree overlay contains only the filename of - * the firmware and relies on the firmware search paths to find the - * actual file. Use /lib/firmware in the Linux root directory and hope - * for the best. - */ - firmware_path = basprintf("%s/%s", abspath, "/lib/firmware"); - ret = of_firmware_load_overlay(overlay, firmware_path); - free(firmware_path); - if (ret) { - pr_warn("failed to load firmware: skip overlay \"%s\"\n", path); - of_delete_node(overlay); - goto out; - } + overlays = blspec_entry_var_get(entry, "devicetree-overlay"); - ret = of_register_overlay(overlay); - if (ret) { - pr_warn("cannot register devicetree overlay \"%s\"\n", path); - of_delete_node(overlay); - } + sep = freep = xstrdup(overlays); -out: - free(path); + while ((overlay = strsep(&sep, " "))) { + char *path; - return ret; -} + if (!*overlay) + continue; -static void blspec_apply_oftree_overlays(const char *overlays, - const char *abspath, int dryrun) -{ - char *overlay; - char *sep, *freep; + path = basprintf("%s/%s", entry->rootpath, overlay); - sep = freep = xstrdup(overlays); + of_overlay_apply_file(root, path, false); - while ((overlay = strsep(&sep, " "))) - blspec_apply_oftree_overlay(overlay, abspath, dryrun); + free(path); + } free(freep); + + return 0; } /* @@ -120,7 +67,7 @@ static void blspec_apply_oftree_overlays(const char *overlays, * * This boots an entry. On success this function does not return. * In case of an error the error code is returned. This function may - * return 0 in case of a succesful dry run. + * return 0 in case of a successful dry run. */ static int blspec_boot(struct bootentry *be, int verbose, int dryrun) { @@ -129,20 +76,20 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun) const char *abspath, *devicetree, *options, *initrd, *linuximage; const char *overlays; const char *appendroot; + char *old_fws, *fws; struct bootm_data data = { - .initrd_address = UIMAGE_INVALID_ADDRESS, - .os_address = UIMAGE_SOME_ADDRESS, - .verbose = verbose, .dryrun = dryrun, }; globalvar_set_match("linux.bootargs.dyn.", ""); - globalvar_set_match("bootm.image", ""); - globalvar_set_match("bootm.oftree", ""); - globalvar_set_match("bootm.initrd", ""); + globalvar_set("bootm.image", ""); + globalvar_set("bootm.oftree", ""); + globalvar_set("bootm.initrd", ""); bootm_data_init_defaults(&data); + data.verbose = max(verbose, data.verbose); + devicetree = blspec_entry_var_get(entry, "devicetree"); initrd = blspec_entry_var_get(entry, "initrd"); options = blspec_entry_var_get(entry, "options"); @@ -168,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); @@ -192,9 +139,27 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun) (entry->cdev && entry->cdev->dev) ? dev_name(entry->cdev->dev) : "none"); + of_overlay_set_basedir(abspath); + + old_fws = firmware_get_searchpath(); + if (old_fws && *old_fws) + fws = basprintf("%s/lib/firmware:%s", abspath, old_fws); + else + fws = basprintf("%s/lib/firmware", abspath); + firmware_set_searchpath(fws); + free(fws); + ret = bootm_boot(&data); if (ret) pr_err("Booting failed\n"); + + if (overlays) + of_unregister_fixup(blspec_overlay_fixup, entry); + + of_overlay_set_basedir("/"); + firmware_set_searchpath(old_fws); + free(old_fws); + err_out: free((char *)data.oftree_file); free((char *)data.initrd_file); @@ -298,6 +263,18 @@ static struct blspec_entry *blspec_entry_open(struct bootentries *bootentries, val = end; + if (!strcmp(name, "options")) { + /* If there was a previous "options" key given, prepend its value + * (as per spec). */ + const char *prev_val = blspec_entry_var_get(entry, name); + if (prev_val) { + char *opts = xasprintf("%s %s", prev_val, val); + blspec_entry_var_set(entry, name, opts); + free(opts); + continue; + } + } + blspec_entry_var_set(entry, name, val); } @@ -339,7 +316,7 @@ static int blspec_have_entry(struct bootentries *bootentries, const char *path) */ static const char *nfs_find_mountpath(const char *nfshostpath) { - struct fs_device_d *fsdev; + struct fs_device *fsdev; for_each_fs_device(fsdev) { if (fsdev->backingstore && !strcmp(fsdev->backingstore, nfshostpath)) @@ -364,10 +341,10 @@ static char *parse_nfs_url(const char *url) int ret; if (!IS_ENABLED(CONFIG_FS_NFS)) - return ERR_PTR(-ENOSYS); + return NULL; if (strncmp(url, "nfs://", 6)) - return ERR_PTR(-EINVAL); + return NULL; url += 6; @@ -436,7 +413,7 @@ out: if (ret) free(mountpath); - return ret ? ERR_PTR(ret) : mountpath; + return ret ? NULL : mountpath; } /* @@ -449,14 +426,14 @@ static bool entry_is_of_compatible(struct blspec_entry *entry) { const char *devicetree; const char *abspath; - size_t size; - void *fdt = NULL; int ret; - struct device_node *root = NULL, *barebox_root; + struct device_node *barebox_root; + size_t size; + void *fdt; const char *compat; char *filename; - /* If the entry doesn't specifiy a devicetree we are compatible */ + /* If the entry doesn't specify a devicetree we are compatible */ devicetree = blspec_entry_var_get(entry, "devicetree"); if (!devicetree) return true; @@ -482,33 +459,22 @@ static bool entry_is_of_compatible(struct blspec_entry *entry) fdt = read_file(filename, &size); if (!fdt) { - pr_err("Cannot read: %s\n", filename); ret = false; goto out; } - root = of_unflatten_dtb(fdt); - if (IS_ERR(root)) { - ret = false; - root = NULL; - goto out; - } - - if (of_device_is_compatible(root, compat)) { + if (fdt_machine_is_compatible(fdt, size, compat)) { ret = true; goto out; } - pr_info("ignoring entry with incompatible devicetree \"%s\"\n", - (char *)of_get_property(root, "compatible", NULL)); + pr_info("ignoring entry with incompatible devicetree: %s\n", devicetree); ret = false; out: - if (root) - of_delete_node(root); - free(filename); free(fdt); + free(filename); return ret; } @@ -528,7 +494,7 @@ static bool entry_is_match_machine_id(struct blspec_entry *entry) if (env_machineid) { const char *machineid = blspec_entry_var_get(entry, "machine-id"); if (!machineid || strcmp(machineid, env_machineid)) { - pr_debug("ignoring entry with missmatched machine-id " \ + pr_debug("ignoring entry with mismatched machine-id " \ "\"%s\" != \"%s\"\n", env_machineid, machineid); ret = false; } @@ -537,6 +503,55 @@ static bool entry_is_match_machine_id(struct blspec_entry *entry) return ret; } +int blspec_scan_file(struct bootentries *bootentries, const char *root, + const char *configname) +{ + char *devname = NULL, *hwdevname = NULL; + struct blspec_entry *entry; + + if (blspec_have_entry(bootentries, configname)) + return -EEXIST; + + entry = blspec_entry_open(bootentries, configname); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + root = root ?: get_mounted_path(configname); + entry->rootpath = xstrdup(root); + entry->configpath = xstrdup(configname); + entry->cdev = get_cdev_by_mountpath(root); + + if (!entry_is_of_compatible(entry)) { + blspec_entry_free(&entry->entry); + return -ENODEV; + } + + if (!entry_is_match_machine_id(entry)) { + blspec_entry_free(&entry->entry); + return -ENODEV; + } + + if (entry->cdev && entry->cdev->dev) { + devname = xstrdup(dev_name(entry->cdev->dev)); + if (entry->cdev->dev->parent) + hwdevname = xstrdup(dev_name(entry->cdev->dev->parent)); + } + + entry->entry.title = xasprintf("%s (%s)", blspec_entry_var_get(entry, "title"), + configname); + entry->entry.description = basprintf("blspec entry, device: %s hwdevice: %s", + devname ? devname : "none", + hwdevname ? hwdevname : "none"); + free(devname); + free(hwdevname); + + entry->entry.me.type = MENU_ENTRY_NORMAL; + entry->entry.release = blspec_entry_free; + + bootentries_add_entry(bootentries, &entry->entry); + return 1; +} + /* * blspec_scan_directory - scan over a directory * @@ -546,114 +561,40 @@ static bool entry_is_match_machine_id(struct blspec_entry *entry) */ int blspec_scan_directory(struct bootentries *bootentries, const char *root) { - struct blspec_entry *entry; - DIR *dir; - struct dirent *d; + glob_t globb; char *abspath; int ret, found = 0; const char *dirname = "loader/entries"; - char *nfspath = NULL; - - nfspath = parse_nfs_url(root); - if (!IS_ERR(nfspath)) - root = nfspath; + int i; pr_debug("%s: %s %s\n", __func__, root, dirname); - abspath = basprintf("%s/%s", root, dirname); + abspath = basprintf("%s/%s/*.conf", root, dirname); - dir = opendir(abspath); - if (!dir) { + ret = glob(abspath, 0, NULL, &globb); + if (ret) { pr_debug("%s: %s: %s\n", __func__, abspath, strerror(errno)); ret = -errno; goto err_out; } - while ((d = readdir(dir))) { - char *configname; + for (i = 0; i < globb.gl_pathc; i++) { + const char *configname = globb.gl_pathv[i]; struct stat s; - char *dot; - char *devname = NULL, *hwdevname = NULL; - - if (*d->d_name == '.') - continue; - - configname = basprintf("%s/%s", abspath, d->d_name); - - dot = strrchr(configname, '.'); - if (!dot) { - free(configname); - continue; - } - - if (strcmp(dot, ".conf")) { - free(configname); - continue; - } ret = stat(configname, &s); - if (ret) { - free(configname); - continue; - } - - if (!S_ISREG(s.st_mode)) { - free(configname); + if (ret || !S_ISREG(s.st_mode)) continue; - } - - if (blspec_have_entry(bootentries, configname)) { - free(configname); - continue; - } - - entry = blspec_entry_open(bootentries, configname); - if (IS_ERR(entry)) { - free(configname); - continue; - } - - entry->rootpath = xstrdup(root); - entry->configpath = configname; - entry->cdev = get_cdev_by_mountpath(root); - if (!entry_is_of_compatible(entry)) { - blspec_entry_free(&entry->entry); - continue; - } - - if (!entry_is_match_machine_id(entry)) { - blspec_entry_free(&entry->entry); - continue; - } - - found++; - - if (entry->cdev && entry->cdev->dev) { - devname = xstrdup(dev_name(entry->cdev->dev)); - if (entry->cdev->dev->parent) - hwdevname = xstrdup(dev_name(entry->cdev->dev->parent)); - } - - entry->entry.title = xstrdup(blspec_entry_var_get(entry, "title")); - entry->entry.description = basprintf("blspec entry, device: %s hwdevice: %s", - devname ? devname : "none", - hwdevname ? hwdevname : "none"); - free(devname); - free(hwdevname); - - entry->entry.me.type = MENU_ENTRY_NORMAL; - entry->entry.release = blspec_entry_free; - - bootentries_add_entry(bootentries, &entry->entry); + ret = blspec_scan_file(bootentries, root, configname); + if (ret > 0) + found += ret; } ret = found; - closedir(dir); + globfree(&globb); err_out: - if (!IS_ERR(nfspath)) - free(nfspath); free(abspath); return ret; @@ -666,11 +607,11 @@ err_out: * entries found in the UBI volumes * * returns the number of entries found or a negative error code if some unexpected - * error occured. + * error occurred. */ static int blspec_scan_ubi(struct bootentries *bootentries, struct cdev *cdev) { - struct device_d *child; + struct device *child; int ret, found = 0; pr_debug("%s: %s\n", __func__, cdev->name); @@ -695,7 +636,7 @@ static int blspec_scan_ubi(struct bootentries *bootentries, struct cdev *cdev) * entries found under /bootentries/entries/. * * returns the number of entries found or a negative error code if some unexpected - * error occured. + * error occurred. */ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev) { @@ -725,7 +666,7 @@ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev) found += ret; } - rootpath = cdev_mount_default(cdev, NULL); + rootpath = cdev_mount(cdev); if (!IS_ERR(rootpath)) { ret = blspec_scan_directory(bootentries, rootpath); if (ret > 0) @@ -740,11 +681,11 @@ static int blspec_scan_cdev(struct bootentries *bootentries, struct cdev *cdev) * * Iterate over all devices and collect child their cdevs. * Returns the number of entries found or a negative error code if some unexpected - * error occured. + * error occurred. */ int blspec_scan_devices(struct bootentries *bootentries) { - struct device_d *dev; + struct device *dev; struct block_device *bdev; int ret, found = 0; @@ -752,7 +693,7 @@ int blspec_scan_devices(struct bootentries *bootentries) device_detect(dev); for_each_block_device(bdev) { - struct cdev *cdev = &bdev->cdev; + struct cdev *cdev; list_for_each_entry(cdev, &bdev->dev->cdevs, devices_list) { ret = blspec_scan_cdev(bootentries, cdev); @@ -770,11 +711,11 @@ int blspec_scan_devices(struct bootentries *bootentries) * Given a device this functions scans over all child cdevs looking * for bootentries entries. * Returns the number of entries found or a negative error code if some unexpected - * error occured. + * error occurred. */ -int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) +int blspec_scan_device(struct bootentries *bootentries, struct device *dev) { - struct device_d *child; + struct device *child; struct cdev *cdev; int ret, found = 0; @@ -788,7 +729,7 @@ int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) * partition with the MBR type id of 0xEA already exists it * should be used as $BOOT */ - if (cdev->dos_partition_type == 0xea) { + if (cdev_is_mbr_partitioned(cdev->master) && cdev->dos_partition_type == 0xea) { ret = blspec_scan_cdev(bootentries, cdev); if (ret == 0) ret = -ENOENT; @@ -832,15 +773,18 @@ int blspec_scan_device(struct bootentries *bootentries, struct device_d *dev) * Given a name of a hardware device this functions scans over all child * cdevs looking for bootentries entries. * Returns the number of entries found or a negative error code if some unexpected - * error occured. + * error occurred. */ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname) { - struct device_d *dev; + struct device *dev; struct cdev *cdev; pr_debug("%s: %s\n", __func__, devname); + /* Support both boot /dev/disk0.rootfs and boot disk0.rootfs */ + devname += str_has_prefix(devname, "/dev/"); + device_detect_by_name(devname); cdev = cdev_by_name(devname); @@ -860,6 +804,7 @@ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname) static int blspec_bootentry_provider(struct bootentries *bootentries, const char *name) { + struct stat s; int ret, found = 0; ret = blspec_scan_devicename(bootentries, name); @@ -867,9 +812,24 @@ static int blspec_bootentry_provider(struct bootentries *bootentries, found += ret; if (*name == '/' || !strncmp(name, "nfs://", 6)) { - ret = blspec_scan_directory(bootentries, name); + char *nfspath = parse_nfs_url(name); + + if (nfspath) + name = nfspath; + + ret = stat(name, &s); + if (ret) + goto out; + + if (S_ISDIR(s.st_mode)) + ret = blspec_scan_directory(bootentries, name); + else if (S_ISREG(s.st_mode) && strends(name, ".conf")) + ret = blspec_scan_file(bootentries, NULL, name); if (ret > 0) found += ret; + +out: + free(nfspath); } return found; |