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);