diff --git a/man/bootctl.xml b/man/bootctl.xml index 57455f2dac..6fa279aa35 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -361,6 +361,24 @@ + + + This option modifies the behaviour of status: it shows the + absolute path to the boot loader EFI binary used for the current boot if this information is + available. Note that no attempt is made to verify whether the binary still exists. + + + + + + + This option modifies the behaviour of status: it shows the + absolute path to the UKI/stub EFI binary used for the current boot if this information is + available. Note that no attempt is made to verify whether the binary still exists. + + + + diff --git a/src/basic/efivars.c b/src/basic/efivars.c index 8470d085c7..0ff07fa113 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -145,8 +145,10 @@ int efi_get_variable( int efi_get_variable_string(const char *variable, char **ret) { _cleanup_free_ void *s = NULL; size_t ss = 0; - int r; char *x; + int r; + + assert(variable); r = efi_get_variable(variable, NULL, &s, &ss); if (r < 0) @@ -156,10 +158,27 @@ int efi_get_variable_string(const char *variable, char **ret) { if (!x) return -ENOMEM; - *ret = x; + if (ret) + *ret = x; + return 0; } +int efi_get_variable_path(const char *variable, char **ret) { + int r; + + assert(variable); + + r = efi_get_variable_string(variable, ret); + if (r < 0) + return r; + + if (ret) + efi_tilt_backslashes(*ret); + + return r; +} + static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) { _cleanup_free_ void *buf = NULL; size_t n; diff --git a/src/basic/efivars.h b/src/basic/efivars.h index 34d697fbff..fe2be3107a 100644 --- a/src/basic/efivars.h +++ b/src/basic/efivars.h @@ -11,6 +11,7 @@ #include "sd-id128.h" #include "efivars-fundamental.h" +#include "string-util.h" #include "time-util.h" #define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f) @@ -47,6 +48,7 @@ int efi_get_variable(const char *variable, uint32_t *attribute, void **ret_value, size_t *ret_size); int efi_get_variable_string(const char *variable, char **ret); +int efi_get_variable_path(const char *variable, char **ret); int efi_set_variable(const char *variable, const void *value, size_t size); int efi_set_variable_string(const char *variable, const char *p); @@ -68,6 +70,10 @@ static inline int efi_get_variable_string(const char *variable, char **ret) { return -EOPNOTSUPP; } +static inline int efi_get_variable_path(const char *variable, char **ret) { + return -EOPNOTSUPP; +} + static inline int efi_set_variable(const char *variable, const void *value, size_t size) { return -EOPNOTSUPP; } @@ -100,3 +106,7 @@ static inline int systemd_efi_options_efivarfs_if_newer(char **line) { return -ENODATA; } #endif + +static inline char *efi_tilt_backslashes(char *s) { + return string_replace_char(s, '\\', '/'); +} diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index f86d102f0b..4853aaefe9 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -219,14 +219,12 @@ static int acquire_boot_count_path( uint64_t left, done; int r; - r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path); + r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path); if (r == -ENOENT) return -EUNATCH; /* in this case, let the caller print a message */ if (r < 0) return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m"); - efi_tilt_backslashes(path); - if (!path_is_normalized(path)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path read from LoaderBootCountPath is not normalized, refusing: %s", diff --git a/src/boot/bootctl-status.c b/src/boot/bootctl-status.c index 192fddbf5e..58c6527ab8 100644 --- a/src/boot/bootctl-status.c +++ b/src/boot/bootctl-status.c @@ -298,12 +298,24 @@ fail: return r; } -static void read_efi_var(const char *variable, char **ret) { +static int efi_get_variable_string_and_warn(const char *variable, char **ret) { int r; r = efi_get_variable_string(variable, ret); if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read EFI variable %s: %m", variable); + return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable); + + return r; +} + +static int efi_get_variable_path_and_warn(const char *variable, char **ret) { + int r; + + r = efi_get_variable_path(variable, ret); + if (r < 0 && r != -ENOENT) + return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable); + + return r; } static void print_yes_no_line(bool first, bool good, const char *name) { @@ -396,26 +408,23 @@ int verb_status(int argc, char *argv[], void *userdata) { { EFI_STUB_FEATURE_MULTI_PROFILE_UKI, "Stub understands profile selector" }, { EFI_STUB_FEATURE_REPORT_STUB_PARTITION, "Stub sets stub partition information" }, }; - _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL; - sd_id128_t loader_part_uuid = SD_ID128_NULL; + _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL, + *current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL; uint64_t loader_features = 0, stub_features = 0; Tpm2Support s; int have; - read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type); - read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info); - read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader); - read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub); - read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderInfo), &loader); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(StubInfo), &stub); + (void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path); + (void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(StubImageIdentifier), &stub_path); (void) efi_loader_get_features(&loader_features); (void) efi_stub_get_features(&stub_features); - - if (loader_path) - efi_tilt_backslashes(loader_path); - - k = efi_loader_get_device_part_uuid(&loader_part_uuid); - if (k < 0 && k != -ENOENT) - r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m"); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntrySelected), ¤t_entry); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &oneshot_entry); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryDefault), &default_entry); SecureBootMode secure = efi_get_secure_boot_mode(); printf("%sSystem:%s\n", ansi_underline(), ansi_normal()); @@ -463,34 +472,58 @@ int verb_status(int argc, char *argv[], void *userdata) { } printf("\n"); - printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal()); - printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal()); + if (loader) { + printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal()); + printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal()); + for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++) + print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name); - for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++) - print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name); + sd_id128_t loader_partition_uuid; + bool have_loader_partition_uuid = efi_loader_get_device_part_uuid(&loader_partition_uuid) >= 0; - sd_id128_t bootloader_esp_uuid; - bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0; + print_yes_no_line(false, have_loader_partition_uuid, "Boot loader set ESP information"); - print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information"); - if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) && - !sd_id128_equal(esp_uuid, bootloader_esp_uuid)) - printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n", - SD_ID128_FORMAT_VAL(bootloader_esp_uuid), - SD_ID128_FORMAT_VAL(esp_uuid)); + if (current_entry) + printf("Current Entry: %s\n", current_entry); + if (default_entry) + printf("Default Entry: %s\n", default_entry); + if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry)) + printf("OneShot Entry: %s\n", oneshot_entry); + + if (have_loader_partition_uuid && !sd_id128_is_null(esp_uuid) && !sd_id128_equal(esp_uuid, loader_partition_uuid)) + printf("WARNING: The boot loader reports a different partition UUID than the detected ESP ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n", + SD_ID128_FORMAT_VAL(loader_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid)); + + if (!sd_id128_is_null(loader_partition_uuid)) + printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n", + SD_ID128_FORMAT_VAL(loader_partition_uuid)); + else + printf(" Partition: n/a\n"); + printf(" Loader: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path)); + printf("\n"); + } if (stub) { - printf(" Stub: %s\n", stub); + printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal()); + printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal()); for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++) print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name); + + sd_id128_t stub_partition_uuid; + bool have_stub_partition_uuid = efi_stub_get_device_part_uuid(&stub_partition_uuid) >= 0; + + if (have_stub_partition_uuid && (!(!sd_id128_is_null(esp_uuid) && sd_id128_equal(esp_uuid, stub_partition_uuid)) && + !(!sd_id128_is_null(xbootldr_uuid) && sd_id128_equal(xbootldr_uuid, stub_partition_uuid)))) + printf("WARNING: The stub loader reports a different UUID than the detected ESP or XBOOTDLR partition ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR"/"SD_ID128_UUID_FORMAT_STR")!\n", + SD_ID128_FORMAT_VAL(stub_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid), SD_ID128_FORMAT_VAL(xbootldr_uuid)); + if (!sd_id128_is_null(stub_partition_uuid)) + printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n", + SD_ID128_FORMAT_VAL(stub_partition_uuid)); + else + printf(" Partition: n/a\n"); + printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_path)); + printf("\n"); } - if (!sd_id128_is_null(loader_part_uuid)) - printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n", - SD_ID128_FORMAT_VAL(loader_part_uuid)); - else - printf(" ESP: n/a\n"); - printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path)); - printf("\n"); printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal()); have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index b2f84f00f7..176de8d55d 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -16,12 +16,14 @@ #include "build.h" #include "devnum-util.h" #include "dissect-image.h" +#include "efi-loader.h" #include "escape.h" #include "find-esp.h" #include "main-func.h" #include "mount-util.h" #include "pager.h" #include "parse-argument.h" +#include "path-util.h" #include "pretty-print.h" #include "utf8.h" #include "varlink-io.systemd.BootControl.h" @@ -38,6 +40,8 @@ char *arg_esp_path = NULL; char *arg_xbootldr_path = NULL; bool arg_print_esp_path = false; bool arg_print_dollar_boot_path = false; +bool arg_print_loader_path = false; +bool arg_print_stub_path = false; unsigned arg_print_root_device = 0; bool arg_touch_variables = true; bool arg_install_random_seed = true; @@ -133,6 +137,71 @@ int acquire_xbootldr( return 1; } +static int print_loader_or_stub_path(void) { + _cleanup_free_ char *p = NULL; + sd_id128_t uuid; + int r; + + if (arg_print_loader_path) { + r = efi_loader_get_device_part_uuid(&uuid); + if (r == -ENOENT) + return log_error_errno(r, "No loader partition UUID passed."); + if (r < 0) + return log_error_errno(r, "Unable to determine loader partition UUID: %m"); + + r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &p); + if (r == -ENOENT) + return log_error_errno(r, "No loader EFI binary path passed."); + if (r < 0) + return log_error_errno(r, "Unable to determine loader EFI binary path: %m"); + } else { + assert(arg_print_stub_path); + + r = efi_stub_get_device_part_uuid(&uuid); + if (r == -ENOENT) + return log_error_errno(r, "No stub partition UUID passed."); + if (r < 0) + return log_error_errno(r, "Unable to determine stub partition UUID: %m"); + + r = efi_get_variable_path(EFI_LOADER_VARIABLE(StubImageIdentifier), &p); + if (r == -ENOENT) + return log_error_errno(r, "No stub EFI binary path passed."); + if (r < 0) + return log_error_errno(r, "Unable to determine stub EFI binary path: %m"); + } + + sd_id128_t esp_uuid; + r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, + /* ret_part= */ NULL, /* ret_pstart= */ NULL, /* ret_psize= */ NULL, + &esp_uuid, /* ret_devid= */ NULL); + if (r < 0) + return r; + + const char *found_path = NULL; + if (sd_id128_equal(esp_uuid, uuid)) + found_path = arg_esp_path; + else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */ + sd_id128_t xbootldr_uuid; + + r = acquire_xbootldr(/* unprivileged_mode= */ false, &xbootldr_uuid, /* ret_devid= */ NULL); + if (r < 0) + return r; + + if (sd_id128_equal(xbootldr_uuid, uuid)) + found_path = arg_xbootldr_path; + } + + if (!found_path) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid)); + + _cleanup_free_ char *j = path_join(found_path, p); + if (!j) + return log_oom(); + + puts(j); + return 0; +} + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL; int r; @@ -182,6 +251,9 @@ static int help(int argc, char *argv[], void *userdata) { " Where to pick files when using --root=/--image=\n" " -p --print-esp-path Print path to the EFI System Partition mount point\n" " -x --print-boot-path Print path to the $BOOT partition mount point\n" + " --print-loader-path\n" + " Print path to currently booted boot loader binary\n" + " --print-stub-path Print path to currently booted unified kernel binary\n" " -R --print-root-device\n" " Print path to the block device node backing the\n" " root file system (returns e.g. /dev/nvme0n1p5)\n" @@ -235,6 +307,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_ARCH_ALL, ARG_EFI_BOOT_OPTION_DESCRIPTION, ARG_DRY_RUN, + ARG_PRINT_LOADER_PATH, + ARG_PRINT_STUB_PATH, }; static const struct option options[] = { @@ -250,6 +324,8 @@ static int parse_argv(int argc, char *argv[]) { { "print-esp-path", no_argument, NULL, 'p' }, { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ { "print-boot-path", no_argument, NULL, 'x' }, + { "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH }, + { "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH }, { "print-root-device", no_argument, NULL, 'R' }, { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, { "random-seed", required_argument, NULL, ARG_RANDOM_SEED }, @@ -332,6 +408,14 @@ static int parse_argv(int argc, char *argv[]) { arg_print_dollar_boot_path = true; break; + case ARG_PRINT_LOADER_PATH: + arg_print_loader_path = true; + break; + + case ARG_PRINT_STUB_PATH: + arg_print_stub_path = true; + break; + case 'R': arg_print_root_device++; break; @@ -414,9 +498,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached(); } - if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) > 1) + if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined."); + "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path cannot be combined."); if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list", "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup")) @@ -541,6 +625,9 @@ static int run(int argc, char *argv[]) { return 0; } + if (arg_print_loader_path || arg_print_stub_path) + return print_loader_or_stub_path(); + /* Open up and mount the image */ if (arg_image) { assert(!arg_root); diff --git a/src/shared/efi-api.h b/src/shared/efi-api.h index 09071b22c9..61ef91df9f 100644 --- a/src/shared/efi-api.h +++ b/src/shared/efi-api.h @@ -66,9 +66,5 @@ static inline bool efi_has_tpm2(void) { #endif -static inline char *efi_tilt_backslashes(char *s) { - return string_replace_char(s, '\\', '/'); -} - sd_id128_t efi_guid_to_id128(const void *guid); void efi_id128_to_guid(sd_id128_t id, void *ret_guid); diff --git a/src/shared/efi-loader.c b/src/shared/efi-loader.c index ab377aaa8b..74144af95a 100644 --- a/src/shared/efi-loader.c +++ b/src/shared/efi-loader.c @@ -61,30 +61,19 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) { return 0; } -int efi_loader_get_device_part_uuid(sd_id128_t *ret) { - _cleanup_free_ char *p = NULL; - int r; - unsigned parsed[16]; - +static int get_device_part_uuid(const char *variable, sd_id128_t *ret) { if (!is_efi_boot()) return -EOPNOTSUPP; - r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p); - if (r < 0) - return r; + return efi_get_variable_id128(variable, ret); +} - if (sscanf(p, SD_ID128_UUID_FORMAT_STR, - &parsed[0], &parsed[1], &parsed[2], &parsed[3], - &parsed[4], &parsed[5], &parsed[6], &parsed[7], - &parsed[8], &parsed[9], &parsed[10], &parsed[11], - &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) - return -EIO; +int efi_loader_get_device_part_uuid(sd_id128_t *ret) { + return get_device_part_uuid(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), ret); +} - if (ret) - for (unsigned i = 0; i < ELEMENTSOF(parsed); i++) - ret->bytes[i] = parsed[i]; - - return 0; +int efi_stub_get_device_part_uuid(sd_id128_t *ret) { + return get_device_part_uuid(EFI_LOADER_VARIABLE(StubDevicePartUUID), ret); } int efi_loader_get_entries(char ***ret) { @@ -353,6 +342,22 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat return 0; } +int efi_get_variable_id128(const char *variable, sd_id128_t *ret) { + int r; + + assert(variable); + + /* This is placed here (rather than in basic/efivars.c) because code in basic/ is not allowed to link + * against libsystemd.so */ + + _cleanup_free_ char *p = NULL; + r = efi_get_variable_string(variable, &p); + if (r < 0) + return r; + + return sd_id128_from_string(p, ret); +} + #endif bool efi_loader_entry_name_valid(const char *s) { diff --git a/src/shared/efi-loader.h b/src/shared/efi-loader.h index c878eea72f..0b6d059398 100644 --- a/src/shared/efi-loader.h +++ b/src/shared/efi-loader.h @@ -11,6 +11,7 @@ #if ENABLE_EFI int efi_loader_get_device_part_uuid(sd_id128_t *ret); +int efi_stub_get_device_part_uuid(sd_id128_t *ret); int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader); int efi_loader_get_entries(char ***ret); @@ -23,6 +24,8 @@ int efi_measured_uki(int log_level); int efi_loader_get_config_timeout_one_shot(usec_t *ret); int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat); +int efi_get_variable_id128(const char *variable, sd_id128_t *ret); + #else static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) { @@ -58,6 +61,10 @@ static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct st return -EOPNOTSUPP; } +static inline int efi_get_variable_id128(const char *variable, sd_id128_t *ret) { + return -EOPNOTSUPP; +} + #endif bool efi_loader_entry_name_valid(const char *s);