diff --git a/man/bootctl.xml b/man/bootctl.xml
index 45ea93252c..816b618087 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -115,7 +115,8 @@
Shows all available boot loader entries implementing the Boot Loader
- Specification
+ Specification, as well as any other entries discovered or automatically generated by the boot
+ loader.
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 955453d119..c97d21c228 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1027,6 +1027,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
static int verb_list(int argc, char *argv[], void *userdata) {
_cleanup_(boot_config_free) BootConfig config = {};
+ _cleanup_free_ char **found_by_loader = NULL;
sd_id128_t uuid = SD_ID128_NULL;
int r;
@@ -1044,6 +1045,10 @@ static int verb_list(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
+ r = efi_loader_get_entries(&found_by_loader);
+ if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP))
+ log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m");
+
if (config.n_entries == 0)
log_info("No boot loader entries found.");
else {
@@ -1059,9 +1064,20 @@ static int verb_list(int argc, char *argv[], void *userdata) {
return r;
puts("");
+
+ strv_remove(found_by_loader, config.entries[n].id);
}
}
+ if (!strv_isempty(found_by_loader)) {
+ char **i;
+
+ printf("Automatic/Other Entries Found by Boot Loader:\n\n");
+
+ STRV_FOREACH(i, found_by_loader)
+ puts(*i);
+ }
+
return 0;
}
diff --git a/src/shared/efivars.c b/src/shared/efivars.c
index e4b16a5743..3931bee559 100644
--- a/src/shared/efivars.c
+++ b/src/shared/efivars.c
@@ -22,6 +22,7 @@
#include "macro.h"
#include "parse-util.h"
#include "stdio-util.h"
+#include "strv.h"
#include "time-util.h"
#include "utf8.h"
#include "util.h"
@@ -753,6 +754,56 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
return 0;
}
+int efi_loader_get_entries(char ***ret) {
+ _cleanup_free_ char16_t *entries = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ size_t size, i, start;
+ int r;
+
+ assert(ret);
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
+ r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
+ if (r < 0)
+ return r;
+
+ /* The variable contains a series of individually NUL terminated UTF-16 strings. */
+
+ for (i = 0, start = 0;; i++) {
+ char *decoded;
+ bool end;
+
+ /* Is this the end of the variable's data? */
+ end = i * sizeof(char16_t) >= size;
+
+ /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
+ * so, let's go to the next entry. */
+ if (!end && entries[i] != 0)
+ continue;
+
+ /* We reached the end of a string, let's decode it into UTF-8 */
+ decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
+ if (!decoded)
+ return -ENOMEM;
+
+ r = strv_consume(&l, decoded);
+ if (r < 0)
+ return r;
+
+ /* We reached the end of the variable */
+ if (end)
+ break;
+
+ /* Continue after the NUL byte */
+ start = i + 1;
+ }
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
#endif
char *efi_tilt_backslashes(char *s) {
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index dac66da9f8..9b5ab39fee 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -41,6 +41,8 @@ int efi_get_boot_options(uint16_t **options);
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
+int efi_loader_get_entries(char ***ret);
+
#else
static inline bool is_efi_boot(void) {
@@ -111,6 +113,10 @@ static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
return -EOPNOTSUPP;
}
+static inline int efi_loader_get_entries(char ***ret) {
+ return -EOPNOTSUPP;
+}
+
#endif
char *efi_tilt_backslashes(char *s);