diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index ceea368ef2..d503f09548 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -501,10 +501,10 @@
considered 'good' from then on.
The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
- state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can
- freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
- automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom
- item of the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
+ state are ordered towards the end of the list, and entries in 'good' or 'indeterminate' towards the beginning.
+ The user can freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry
+ to boot is automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred as
+ boot entries are tried in sort order, and 'bad' entries will only be considered if there are no 'good' or
'indeterminate' entries left.
The kernel-install8 kernel
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index fc346956df..189461974b 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1492,7 +1492,6 @@ static void config_entry_add_from_file(
entry->device = device;
entry->id = xstrdup(file);
- StrLwr(entry->id);
config_add_entry(config, entry);
@@ -1603,13 +1602,24 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
assert(a);
assert(b);
- /* Order entries that have no tries left to the beginning of the list */
+ /* Order entries that have no tries left towards the end of the list. They have
+ * proven to be bad and should not be selected automatically. */
if (a->tries_left != 0 && b->tries_left == 0)
- return 1;
- if (a->tries_left == 0 && b->tries_left != 0)
return -1;
+ if (a->tries_left == 0 && b->tries_left != 0)
+ return 1;
- r = strverscmp_improved(a->id, b->id);
+ r = strcasecmp_ptr(a->title ?: a->id, b->title ?: b->id);
+ if (r != 0)
+ return r;
+
+ /* Sort by machine id now so that different installations don't interleave their versions. */
+ r = strcasecmp_ptr(a->machine_id, b->machine_id);
+ if (r != 0)
+ return r;
+
+ /* Reverse version comparison order so that higher versions are preferred. */
+ r = strverscmp_improved(b->version, a->version);
if (r != 0)
return r;
@@ -1617,19 +1627,20 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
b->tries_left == UINTN_MAX)
return 0;
- /* If both items have boot counting, and otherwise are identical, put the entry with more tries left last */
+ /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
if (a->tries_left > b->tries_left)
- return 1;
- if (a->tries_left < b->tries_left)
return -1;
+ if (a->tries_left < b->tries_left)
+ return 1;
/* If they have the same number of tries left, then let the one win which was tried fewer times so far */
if (a->tries_done < b->tries_done)
- return 1;
- if (a->tries_done > b->tries_done)
return -1;
+ if (a->tries_done > b->tries_done)
+ return 1;
- return 0;
+ /* As a last resort, use the id (file name). */
+ return strverscmp_improved(a->id, b->id);
}
static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
@@ -1638,7 +1649,7 @@ static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
if (!needle)
return IDX_INVALID;
- for (INTN i = config->entry_count - 1; i >= 0; i--)
+ for (UINTN i = 0; i < config->entry_count; i++)
if (MetaiMatch(config->entries[i]->id, (CHAR16*) needle))
return i;
@@ -1673,9 +1684,8 @@ static void config_default_entry_select(Config *config) {
return;
}
- /* select the last suitable entry */
- i = config->entry_count;
- while (i--) {
+ /* Select the first suitable entry. */
+ for (i = 0; i < config->entry_count; i++) {
if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
continue;
config->idx_default = i;
@@ -1830,8 +1840,6 @@ static ConfigEntry *config_entry_add_loader(
.tries_left = UINTN_MAX,
};
- StrLwr(entry->id);
-
config_add_entry(config, entry);
return entry;
}
@@ -2326,12 +2334,14 @@ static void config_load_all_entries(
/* Similar, but on any XBOOTLDR partition */
config_load_xbootldr(config, loaded_image->DeviceHandle);
+ /* Add these now, so they get sorted with the rest. */
+ config_entry_add_osx(config);
+ config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
+
/* sort entries after version number */
sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
/* if we find some well-known loaders, add them to the end of the list */
- config_entry_add_osx(config);
- config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,