* [PATCH 1/5] boot: aggregate bootentry provider entries one by one
2026-02-09 9:08 [PATCH 0/5] blspec: sort entries according to specification Ahmad Fatoum
@ 2026-02-09 9:08 ` Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 2/5] blspec: sort entries according to specification Ahmad Fatoum
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2026-02-09 9:08 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To allow individual bootentry providers to more easily sort their
entries, pass each provider an empty list that's spliced onto the tail
of the aggregated list at the end.
This way we maintain that boot entries of different providers are sorted
by provider priority and boot entries of a single provider can be sorted
by each provider without having to worry about foreign boot entries.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
common/boot.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/common/boot.c b/common/boot.c
index 78d990f1617e..8ccc269ac5bd 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -26,6 +26,14 @@ int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry)
return 0;
}
+#define BOOTENTRIES(name) \
+ struct bootentries name = { .entries = LIST_HEAD_INIT(name.entries) }
+
+static inline void bootentries_merge(struct bootentries *dst, struct bootentries *src)
+{
+ list_splice_tail_init(&src->entries, &dst->entries);
+}
+
struct bootentries *bootentries_alloc(void)
{
struct bootentries *bootentries;
@@ -452,9 +460,17 @@ int bootentry_create_from_name(struct bootentries *bootentries,
name = nfspath;
list_for_each_entry(p, &bootentry_providers, list) {
- ret = p->generate(bootentries, name);
+ BOOTENTRIES(provider_bootentries);
+
+ ret = p->generate(&provider_bootentries, name);
if (ret > 0)
found += ret;
+
+ /* We want to allow for providers to sort their bootentries as
+ * they see fit, so they are passed an empty list above with
+ * only their own entries and then we aggregate here
+ */
+ bootentries_merge(bootentries, &provider_bootentries);
}
free(nfspath);
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 2/5] blspec: sort entries according to specification
2026-02-09 9:08 [PATCH 0/5] blspec: sort entries according to specification Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 1/5] boot: aggregate bootentry provider entries one by one Ahmad Fatoum
@ 2026-02-09 9:08 ` Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 3/5] boot: give struct bootentry a path member Ahmad Fatoum
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2026-02-09 9:08 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Boot entries generated from bootloader spec are currently sorted by the
order the files are read from the file system.
This is inadequate if we have multiple entries with different kernels
and we want to sort the newer kernels higher.
The UAPI.1 Boot Loader Specification defines an algorithm[1] to order the
entries that takes care of this, so implement it into barebox.
[1]: https://uapi-group.org/specifications/specs/boot_loader_specification/#sorting
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
common/Kconfig | 1 +
common/blspec.c | 62 ++++++++++++++++++++++++++++++++++++++-
common/boot.c | 7 +++++
include/asm-generic/bug.h | 7 +++++
include/boot.h | 2 ++
5 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/common/Kconfig b/common/Kconfig
index 50c26695b2b5..cd002865f736 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -715,6 +715,7 @@ config BLSPEC
select BOOT
select BOOTM
select OFTREE
+ select VERSION_CMP
bool
prompt "Support bootloader spec"
help
diff --git a/common/blspec.c b/common/blspec.c
index 624e4c115272..f13e44f1de71 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -28,6 +28,7 @@ struct blspec_entry {
struct cdev *cdev;
const char *rootpath;
const char *configpath;
+ char *sortkey;
};
/*
@@ -181,6 +182,7 @@ static void blspec_entry_free(struct bootentry *be)
of_delete_node(entry->node);
free_const(entry->configpath);
free_const(entry->rootpath);
+ free(entry->sortkey);
free(entry);
}
@@ -268,6 +270,12 @@ static struct blspec_entry *blspec_entry_open(struct bootentries *bootentries,
}
}
+ /* This will be read often during comparison, so we cache it */
+ if (!strcmp(name, "sort-key")) {
+ entry->sortkey = xstrdup(val);
+ continue;
+ }
+
blspec_entry_var_set(entry, name, val);
}
@@ -414,6 +422,58 @@ static const char *get_blspec_prefix_path(const char *configname)
return get_mounted_path(configname);
}
+static int blspec_compare(struct list_head *list_a, struct list_head *list_b)
+{
+ struct bootentry *be_a = container_of(list_a, struct bootentry, list);
+ struct bootentry *be_b = container_of(list_b, struct bootentry, list);
+ struct blspec_entry *a, *b;
+ const char *a_version, *b_version;
+ int r;
+
+ /* The boot entry providers are called one by one and passed an empty
+ * list that's aggregated later, so we should only be encountering
+ * bootloader spec entries here.
+ */
+ DEBUG_ASSERT(is_blspec_entry(be_a) && is_blspec_entry(be_b));
+
+ a = container_of(be_a, struct blspec_entry, entry);
+ b = container_of(be_b, struct blspec_entry, entry);
+
+ a_version = a->configpath;
+ b_version = b->configpath;
+
+ if (a->sortkey && b->sortkey) {
+ const char *a_machine_id, *b_machine_id;
+
+ /* A-Z, increasing alphanumerical order */
+ r = strcmp(a->sortkey, b->sortkey);
+ if (r != 0)
+ return r;
+
+ a_machine_id = blspec_entry_var_get(a, "machine-id");
+ b_machine_id = blspec_entry_var_get(b, "machine-id");
+
+ /* A-Z, increasing alphanumerical order) */
+ r = strcmp_ptr(a_machine_id, b_machine_id);
+ if (r != 0)
+ return r;
+
+ /* Will be compared in decreasing version order */
+ a_version = blspec_entry_var_get(a, "version");
+ b_version = blspec_entry_var_get(b, "version");
+ } else if (a->sortkey != b->sortkey) {
+ /* If sort-key is set on one entry, it sorts earlier. */
+ return a->sortkey ? -1 : 1;
+ }
+
+ /* At the end, if necessary, when sort-key is not set or
+ * those fields are not set or are all equal, the boot loader
+ * should sort using the file name of the entry (decreasing
+ * version sort), with the suffix removed.
+ */
+ return -strverscmp(a_version, b_version);
+}
+
static int __blspec_scan_file(struct bootentries *bootentries, const char *root,
const char *configname)
{
@@ -459,7 +519,7 @@ static int __blspec_scan_file(struct bootentries *bootentries, const char *root,
entry->entry.me.type = MENU_ENTRY_NORMAL;
entry->entry.release = blspec_entry_free;
- bootentries_add_entry(bootentries, &entry->entry);
+ bootentries_add_entry_sorted(bootentries, &entry->entry, blspec_compare);
return 1;
}
diff --git a/common/boot.c b/common/boot.c
index 8ccc269ac5bd..2e5294ffa2f0 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -34,6 +34,13 @@ static inline void bootentries_merge(struct bootentries *dst, struct bootentries
list_splice_tail_init(&src->entries, &dst->entries);
}
+void bootentries_add_entry_sorted(struct bootentries *entries, struct bootentry *entry,
+ int (*compare)(struct list_head *, struct list_head *))
+
+{
+ list_add_sort(&entry->list, &entries->entries, compare);
+}
+
struct bootentries *bootentries_alloc(void)
{
struct bootentries *bootentries;
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 514801dab10e..1953c6e642c1 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -78,4 +78,11 @@
BUG_ON(!(expr)); \
} while (0)
+
+#ifdef DEBUG
+#define DEBUG_ASSERT(expr) BUG_ON(!(expr))
+#else
+#define DEBUG_ASSERT(expr) ((void)(expr))
+#endif
+
#endif
diff --git a/include/boot.h b/include/boot.h
index e6309b65a0c3..fdc108b7a21d 100644
--- a/include/boot.h
+++ b/include/boot.h
@@ -24,6 +24,8 @@ struct bootentry {
};
int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry);
+void bootentries_add_entry_sorted(struct bootentries *entries, struct bootentry *entry,
+ int (*compare)(struct list_head *, struct list_head *));
struct bootentry_provider {
const char *name;
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/5] boot: give struct bootentry a path member
2026-02-09 9:08 [PATCH 0/5] blspec: sort entries according to specification Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 1/5] boot: aggregate bootentry provider entries one by one Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 2/5] blspec: sort entries according to specification Ahmad Fatoum
@ 2026-02-09 9:08 ` Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 4/5] commands: boot: support file path in boot -M for default entry Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 5/5] test: self: add bootloader spec files test Ahmad Fatoum
4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2026-02-09 9:08 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Both bootloader spec entries and boot spec maintain a path to the file
the struct bootentry was generated from. Add this member to the struct
bootentry directly to allow referencing it from the boot command in a
follow-up commit.
No functional change intended.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
common/blspec.c | 12 ++++--------
common/boot.c | 17 +++++++----------
include/boot.h | 1 +
3 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/common/blspec.c b/common/blspec.c
index f13e44f1de71..c07e3a2d672d 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -27,7 +27,7 @@ struct blspec_entry {
struct device_node *node;
struct cdev *cdev;
const char *rootpath;
- const char *configpath;
+ /* configpath is entry.path */
char *sortkey;
};
@@ -180,7 +180,6 @@ static void blspec_entry_free(struct bootentry *be)
struct blspec_entry *entry = container_of(be, struct blspec_entry, entry);
of_delete_node(entry->node);
- free_const(entry->configpath);
free_const(entry->rootpath);
free(entry->sortkey);
free(entry);
@@ -305,7 +304,7 @@ static int blspec_have_entry(struct bootentries *bootentries, const char *path)
if (!is_blspec_entry(be))
continue;
e = container_of(be, struct blspec_entry, entry);
- if (e->configpath && !strcmp(e->configpath, path))
+ if (e->entry.path && !strcmp(e->entry.path, path))
return 1;
}
@@ -426,8 +425,8 @@ static int blspec_compare(struct list_head *list_a, struct list_head *list_b)
{
struct bootentry *be_a = container_of(list_a, struct bootentry, list);
struct bootentry *be_b = container_of(list_b, struct bootentry, list);
+ const char *a_version = be_a->path, *b_version = be_b->path;
struct blspec_entry *a, *b;
- const char *a_version, *b_version;
int r;
/* The boot entry providers are called one by one and passed an empty
@@ -439,9 +438,6 @@ static int blspec_compare(struct list_head *list_a, struct list_head *list_b)
a = container_of(be_a, struct blspec_entry, entry);
b = container_of(be_b, struct blspec_entry, entry);
- a_version = a->configpath;
- b_version = b->configpath;
-
if (a->sortkey && b->sortkey) {
const char *a_machine_id, *b_machine_id;
@@ -489,7 +485,7 @@ static int __blspec_scan_file(struct bootentries *bootentries, const char *root,
root = root ?: get_blspec_prefix_path(configname);
entry->rootpath = xstrdup_const(root);
- entry->configpath = xstrdup_const(configname);
+ entry->entry.path = xstrdup_const(configname);
entry->cdev = get_cdev_by_mountpath(root);
if (!entry_is_of_compatible(entry)) {
diff --git a/common/boot.c b/common/boot.c
index 2e5294ffa2f0..a325f350a80c 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -64,6 +64,7 @@ void bootentries_free(struct bootentries *bootentries)
list_del(&be->list);
free_const(be->title);
free(be->description);
+ free_const(be->path);
free_const(be->me.display);
be->release(be);
}
@@ -81,7 +82,6 @@ void bootentries_free(struct bootentries *bootentries)
struct bootentry_script {
struct bootentry entry;
- const char *scriptpath;
};
/*
@@ -96,7 +96,7 @@ static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun)
struct bootm_data backup = {}, data = {};
if (dryrun == 1) {
- printf("Would run %s\n", bs->scriptpath);
+ printf("Would run %s\n", bs->entry.path);
return 0;
}
@@ -107,9 +107,9 @@ static int bootscript_boot(struct bootentry *entry, int verbose, int dryrun)
bootm_nattempts = bootm_command_attempts();
- ret = run_command(bs->scriptpath);
+ ret = run_command(bs->entry.path);
if (ret) {
- pr_err("Running script '%s' failed: %pe\n", bs->scriptpath, ERR_PTR(ret));
+ pr_err("Running script '%s' failed: %s\n", bs->entry.path, strerror(-ret));
goto out;
}
@@ -222,10 +222,7 @@ static void bootsource_action(struct menu *m, struct menu_entry *me)
static void bootscript_entry_release(struct bootentry *entry)
{
- struct bootentry_script *bs = container_of(entry, struct bootentry_script, entry);
-
- free_const(bs->scriptpath);
- free(bs);
+ free(entry);
}
/*
@@ -248,8 +245,8 @@ static int bootscript_create_entry(struct bootentries *bootentries, const char *
bs->entry.me.type = MENU_ENTRY_NORMAL;
bs->entry.release = bootscript_entry_release;
bs->entry.boot = bootscript_boot;
- bs->scriptpath = xstrdup_const(name);
- bs->entry.title = xstrdup_const(kbasename(bs->scriptpath));
+ bs->entry.path = xstrdup_const(name);
+ bs->entry.title = xstrdup_const(kbasename(bs->entry.path));
bs->entry.description = basprintf("script: %s", name);
bootentries_add_entry(bootentries, &bs->entry);
diff --git a/include/boot.h b/include/boot.h
index fdc108b7a21d..836a180a0c7b 100644
--- a/include/boot.h
+++ b/include/boot.h
@@ -18,6 +18,7 @@ struct bootentry {
struct menu_entry me;
const char *title;
char *description;
+ const char *path;
int (*boot)(struct bootentry *entry, int verbose, int dryrun);
void (*release)(struct bootentry *entry);
struct bootm_overrides overrides;
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/5] commands: boot: support file path in boot -M for default entry
2026-02-09 9:08 [PATCH 0/5] blspec: sort entries according to specification Ahmad Fatoum
` (2 preceding siblings ...)
2026-02-09 9:08 ` [PATCH 3/5] boot: give struct bootentry a path member Ahmad Fatoum
@ 2026-02-09 9:08 ` Ahmad Fatoum
2026-02-09 9:08 ` [PATCH 5/5] test: self: add bootloader spec files test Ahmad Fatoum
4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2026-02-09 9:08 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The boot command supports specifying a preselection by index, but this
is not so useful when the boot entries come from external media as the
order may easily change.
Add thus the ability to preselect an entry by file path as well via the
-M option. Unlike -M 1337 which would usually leads to a normal error, a
non-existent file path will just be silently ignored and the menu shown
anyway without preselection.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
commands/boot.c | 62 +++++++++++++++++++++++++++++++++++++----------
include/fnmatch.h | 9 +++++++
2 files changed, 58 insertions(+), 13 deletions(-)
diff --git a/commands/boot.c b/commands/boot.c
index 9cfb7c6b0d5e..555667ee098c 100644
--- a/commands/boot.c
+++ b/commands/boot.c
@@ -7,6 +7,7 @@
#include <malloc.h>
#include <boot.h>
#include <bootm.h>
+#include <fnmatch.h>
#include <complete.h>
#include <linux/stat.h>
@@ -55,12 +56,41 @@ static int boot_add_override(struct bootm_overrides *overrides, char *var)
return 0;
}
+static int bootentries_get_default_menu_entry(struct bootentries *entries,
+ char *id)
+{
+ struct bootentry *entry;
+ int ret, idx = 1;
+
+ if (!id)
+ return 0;
+
+ /* To simplify scripting, an empty string is treated as 1 */
+ if (*id == '\0')
+ return 1;
+
+ ret = kstrtouint(id, 0, &idx);
+ if (!ret)
+ return idx;
+ if (ret != -EINVAL)
+ return ret;
+
+ bootentries_for_each_entry(entries, entry) {
+ if ((IS_ENABLED(CONFIG_FNMATCH) && !fnmatch(id, entry->path, 0)) ||
+ streq_ptr(id, entry->path))
+ return idx;
+ idx++;
+ }
+
+ return 0;
+}
+
static int do_boot(int argc, char *argv[])
{
char *freep = NULL;
int opt, ret = 0, do_list = 0, do_menu = 0;
int dryrun = 0, verbose = 0, timeout = -1;
- unsigned default_menu_entry = 0;
+ char *default_menu_entry = NULL;
struct bootentries *entries;
struct bootentry *entry;
struct bootm_overrides overrides = {};
@@ -80,14 +110,7 @@ static int do_boot(int argc, char *argv[])
dryrun++;
break;
case 'M':
- /* To simplify scripting, an empty string is treated as 1 */
- if (*optarg == '\0') {
- default_menu_entry = 1;
- } else {
- ret = kstrtouint(optarg, 0, &default_menu_entry);
- if (ret)
- return ret;
- }
+ default_menu_entry = optarg;
fallthrough;
case 'm':
do_menu = 1;
@@ -152,10 +175,17 @@ static int do_boot(int argc, char *argv[])
goto out;
}
- if (do_list)
+ if (do_list) {
bootsources_list(entries);
- else if (do_menu)
- bootsources_menu(entries, default_menu_entry, timeout);
+ } else if (do_menu) {
+ int idx;
+
+ idx = bootentries_get_default_menu_entry(entries, default_menu_entry);
+ if (idx < 0)
+ return idx;
+
+ bootsources_menu(entries, idx, timeout);
+ }
ret = 0;
out:
@@ -189,6 +219,12 @@ BAREBOX_CMD_HELP_TEXT("- \"storage.removable\": boot from removable media")
BAREBOX_CMD_HELP_TEXT("- \"storage.builtin\": boot from non-removable media")
BAREBOX_CMD_HELP_TEXT("- \"storage\": boot from any available media")
#endif
+BAREBOX_CMD_HELP_TEXT("DEFAULT can be:")
+BAREBOX_CMD_HELP_TEXT("- the numeric index of the menu entry to choose")
+BAREBOX_CMD_HELP_TEXT("- the path to the file backing the entry (e.g. bootspec config file)")
+#ifdef CONFIG_FNMATCH
+BAREBOX_CMD_HELP_TEXT(" with wildcards allowed")
+#endif
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Multiple bootsources may be given which are probed in order until")
BAREBOX_CMD_HELP_TEXT("one succeeds.")
@@ -198,7 +234,7 @@ BAREBOX_CMD_HELP_OPT ("-v","Increase verbosity")
BAREBOX_CMD_HELP_OPT ("-d","Dryrun. See what happens but do no actually boot (pass twice to run scripts)")
BAREBOX_CMD_HELP_OPT ("-l","List available boot sources")
BAREBOX_CMD_HELP_OPT ("-m","Show a menu with boot options")
-BAREBOX_CMD_HELP_OPT ("-M INDEX","Show a menu with boot options with entry INDEX preselected")
+BAREBOX_CMD_HELP_OPT ("-M DEFAULT","Show a menu with boot options with entry DEFAULT preselected")
BAREBOX_CMD_HELP_OPT ("-w SECS","Start watchdog with timeout SECS before booting")
#ifdef CONFIG_BOOT_OVERRIDE
BAREBOX_CMD_HELP_OPT ("-o VAR[=VAL]","override VAR (bootm.{oftree,initrd}) with VAL")
diff --git a/include/fnmatch.h b/include/fnmatch.h
index 1bc2cf87391a..b4895e8d8467 100644
--- a/include/fnmatch.h
+++ b/include/fnmatch.h
@@ -17,8 +17,17 @@
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
#define FNM_NOMATCH 1
+#ifdef CONFIG_FNMATCH
+
/* Match NAME against the filename pattern PATTERN,
returning zero if it matches, FNM_NOMATCH if not. */
extern int fnmatch(const char *pattern, const char *name, int flags);
+#else
+static inline int fnmatch(const char *pattern, const char *name, int flags)
+{
+ return 0;
+}
+#endif
+
#endif /* fnmatch.h */
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 5/5] test: self: add bootloader spec files test
2026-02-09 9:08 [PATCH 0/5] blspec: sort entries according to specification Ahmad Fatoum
` (3 preceding siblings ...)
2026-02-09 9:08 ` [PATCH 4/5] commands: boot: support file path in boot -M for default entry Ahmad Fatoum
@ 2026-02-09 9:08 ` Ahmad Fatoum
4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2026-02-09 9:08 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With spec-conformant sorting added for bootloader specification entries,
add a simple self test that verifies that the sort order is as one would
expect.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
include/bselftest.h | 20 ++++++++
test/self/Kconfig | 5 ++
test/self/Makefile | 2 +
test/self/blspec.c | 46 +++++++++++++++++++
.../data/test/boot/boot.sh | 3 ++
.../data/test/loader/entries/boarda.conf | 6 +++
.../data/test/loader/entries/boardb.conf | 6 +++
.../data/test/loader/entries/boardc.conf | 6 +++
.../data/test/loader/entries/boardd.conf | 5 ++
9 files changed, 99 insertions(+)
create mode 100644 test/self/blspec.c
create mode 100755 test/self/defaultenv-blspec-test/data/test/boot/boot.sh
create mode 100644 test/self/defaultenv-blspec-test/data/test/loader/entries/boarda.conf
create mode 100644 test/self/defaultenv-blspec-test/data/test/loader/entries/boardb.conf
create mode 100644 test/self/defaultenv-blspec-test/data/test/loader/entries/boardc.conf
create mode 100644 test/self/defaultenv-blspec-test/data/test/loader/entries/boardd.conf
diff --git a/include/bselftest.h b/include/bselftest.h
index c3f323864358..fc698d7c5c0c 100644
--- a/include/bselftest.h
+++ b/include/bselftest.h
@@ -78,4 +78,24 @@ static inline void selftests_run(void)
int selftest_run(struct selftest *test);
bool selftest_is_running(struct selftest *test);
+#define __assert_cond(cond) ({ total_tests++; cond || (failed_tests++, false); })
+
+#define assert_cond(cond) ({ \
+ bool __cond = __assert_cond(cond); \
+ if (!__cond) \
+ pr_warn("%s:%d: condition %s unexpectedly false\n", \
+ __func__, __LINE__, #cond); \
+ __cond; \
+})
+
+#define assert_inteq(a, b) assert_cond(a == b)
+
+#define assert_streq(a, b) ({ \
+ bool __cond = __assert_cond(strcmp(a, b) == 0); \
+ if (!__cond) \
+ pr_warn("%s:%d: %s and %s unexpectedly unequal\n", \
+ __func__, __LINE__, a, b); \
+ __cond; \
+})
+
#endif
diff --git a/test/self/Kconfig b/test/self/Kconfig
index 2ccdfe621821..85a3ef790116 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -51,6 +51,7 @@ config SELFTEST_ENABLE_ALL
select SELFTEST_TLV
select SELFTEST_DM
select SELFTEST_TALLOC
+ select SELFTEST_BLSPEC if BLSPEC && DEFAULT_ENVIRONMENT
help
Selects all self-tests compatible with current configuration
@@ -127,6 +128,10 @@ config SELFTEST_SETJMP
bool "setjmp/longjmp library selftest"
depends on ARCH_HAS_SJLJ
+config SELFTEST_BLSPEC
+ bool "bootloader spec selftest"
+ depends on BLSPEC && DEFAULT_ENVIRONMENT
+
config SELFTEST_REGULATOR
bool "Regulator selftest"
depends on REGULATOR_FIXED
diff --git a/test/self/Makefile b/test/self/Makefile
index 9f839fc0d07a..2bfdbb9949df 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -25,6 +25,8 @@ obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
obj-$(CONFIG_SELFTEST_IDR) += idr.o
obj-$(CONFIG_SELFTEST_TLV) += tlv.o tlv.dtb.o
obj-$(CONFIG_SELFTEST_DM) += dm.o
+obj-$(CONFIG_SELFTEST_BLSPEC) += blspec.o
+bbenv-$(CONFIG_SELFTEST_BLSPEC) += defaultenv-blspec-test
ifdef REGENERATE_KEYTOC
diff --git a/test/self/blspec.c b/test/self/blspec.c
new file mode 100644
index 000000000000..0e9eca4d06e9
--- /dev/null
+++ b/test/self/blspec.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <boot.h>
+#include <envfs.h>
+#include <init.h>
+
+BSELFTEST_GLOBALS();
+
+static void test_blspec(void)
+{
+ struct bootentries *entries;
+ struct bootentry *entry;
+ int ret, i = 0;
+ const char *expected[] = {
+ "/env/data/test/loader/entries/boardb.conf",
+ "/env/data/test/loader/entries/boardc.conf",
+ "/env/data/test/loader/entries/boarda.conf",
+ "/env/data/test/loader/entries/boardd.conf",
+ };
+
+ entries = bootentries_alloc();
+
+ ret = bootentry_create_from_name(entries, "/env/data/test");
+ if (!assert_inteq(ret, 4))
+ return;
+
+ if (!assert_cond(!list_empty(&entries->entries)))
+ return;
+
+ bootentries_for_each_entry(entries, entry) {
+ assert_streq(expected[i], entry->path);
+ i++;
+ }
+}
+bselftest(parser, test_blspec);
+
+static int test_blspec_env_init(void)
+{
+ defaultenv_append_directory(defaultenv_blspec_test);
+ return 0;
+}
+late_initcall(test_blspec_env_init);
diff --git a/test/self/defaultenv-blspec-test/data/test/boot/boot.sh b/test/self/defaultenv-blspec-test/data/test/boot/boot.sh
new file mode 100755
index 000000000000..19ba0024098b
--- /dev/null
+++ b/test/self/defaultenv-blspec-test/data/test/boot/boot.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo boot test
diff --git a/test/self/defaultenv-blspec-test/data/test/loader/entries/boarda.conf b/test/self/defaultenv-blspec-test/data/test/loader/entries/boarda.conf
new file mode 100644
index 000000000000..a0b8356476be
--- /dev/null
+++ b/test/self/defaultenv-blspec-test/data/test/loader/entries/boarda.conf
@@ -0,0 +1,6 @@
+title barebox - Test Image - v6.1-rc2 A
+version Dummy v6.1-rc2
+options rootwait
+linux /boot/boot.sh
+sort-key blspec-test
+linux-appendroot true
diff --git a/test/self/defaultenv-blspec-test/data/test/loader/entries/boardb.conf b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardb.conf
new file mode 100644
index 000000000000..93d5ab5f0fac
--- /dev/null
+++ b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardb.conf
@@ -0,0 +1,6 @@
+title barebox - Test Image - v6.19 B
+version Dummy v6.19
+options rootwait
+linux /boot/boot.sh
+sort-key blspec-test
+linux-appendroot true
diff --git a/test/self/defaultenv-blspec-test/data/test/loader/entries/boardc.conf b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardc.conf
new file mode 100644
index 000000000000..8df597d4f251
--- /dev/null
+++ b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardc.conf
@@ -0,0 +1,6 @@
+title barebox - Test Image - v6.9 C
+version Dummy v6.9
+options rootwait
+linux /boot/boot.sh
+sort-key blspec-test
+linux-appendroot true
diff --git a/test/self/defaultenv-blspec-test/data/test/loader/entries/boardd.conf b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardd.conf
new file mode 100644
index 000000000000..b2581147c3d7
--- /dev/null
+++ b/test/self/defaultenv-blspec-test/data/test/loader/entries/boardd.conf
@@ -0,0 +1,5 @@
+title barebox - Test Image - v9999 (no sort-key) D
+version v9999
+options rootwait
+linux /boot/boot.sh
+linux-appendroot true
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread