summaryrefslogtreecommitdiffstats
path: root/common/blspec.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/blspec.c')
-rw-r--r--common/blspec.c366
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;