bless-boot: skip gracefully on kexec

If KExecsCount > 0 then do not attempt to mark a UKI, as the stub (and also
any addon and the bootloader) will not have been exercised, so there's no
guaranteed a full reboot with the same image will actually work.

Fixes https://github.com/systemd/systemd/issues/43738
This commit is contained in:
Luca Boccassi
2026-09-22 21:25:20 +02:00
parent f0f521c147
commit 06c2dfa73c
3 changed files with 43 additions and 0 deletions
+3
View File
@@ -45,6 +45,9 @@
left" numeric boot counters from the filename, which indicates to future invocations of the boot loader left" numeric boot counters from the filename, which indicates to future invocations of the boot loader
that the entry has completed booting successfully at least once. (This service will hence rename the boot that the entry has completed booting successfully at least once. (This service will hence rename the boot
loader entry file or unified kernel image file on the first successful boot.)</para> loader entry file or unified kernel image file on the first successful boot.)</para>
<para>Note that if the system was booted via kexec and LUO is available and enabled, this service will
skip marking the boot entry as good or bad, since kexec bypasses the EFI boot loader entirely.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
+33
View File
@@ -3,6 +3,7 @@
#include <unistd.h> #include <unistd.h>
#include "sd-json.h" #include "sd-json.h"
#include "sd-varlink.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "build.h" #include "build.h"
@@ -20,6 +21,7 @@
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "sync-util.h" #include "sync-util.h"
#include "varlink-util.h"
#include "verbs.h" #include "verbs.h"
#include "virt.h" #include "virt.h"
@@ -443,6 +445,8 @@ VERB_FULL(verb_set, "indeterminate", NULL, VERB_ANY, 1, 0, STATUS_INDETERMINATE,
"Undo any marking as good or bad"); "Undo any marking as good or bad");
static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) { static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) {
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL; _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
_cleanup_(sd_varlink_unrefp) sd_varlink *link = NULL;
sd_json_variant *reply = NULL;
const char *target = NULL, *source1 = NULL, *source2 = NULL; /* avoid false maybe-uninitialized warning */ const char *target = NULL, *source1 = NULL, *source2 = NULL; /* avoid false maybe-uninitialized warning */
uint64_t left, done; uint64_t left, done;
Status status = data; Status status = data;
@@ -454,6 +458,35 @@ static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) {
if (r < 0) if (r < 0)
return r; return r;
/* kexec doesn't go through the EFI loaders, so we shouldn't try to mark an image as good/bad.
* E.g.: booting the kernel+initrd might work, but the EFI stub might not, and we wouldn't notice */
r = sd_varlink_connect_address(&link, "/run/systemd/io.systemd.Manager");
if (r < 0)
return log_error_errno(r, "Failed to connect to service manager: %m");
r = varlink_call_and_log(link, "io.systemd.Manager.Describe", /* parameters= */ NULL, &reply);
if (r < 0)
return r;
static const sd_json_dispatch_field dispatch_table[] = {
{ "KExecCount", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, 0, 0 },
{},
};
uint64_t kexec_count = 0;
r = sd_json_dispatch(
sd_json_variant_by_key(reply, "runtime"),
dispatch_table,
SD_JSON_LOG|SD_JSON_ALLOW_EXTENSIONS,
&kexec_count);
if (r < 0)
return r;
if (kexec_count > 0) {
log_info("System was booted via kexec, skipping boot entry marking.");
return 0;
}
r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix); r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
if (r == -EUNATCH) /* acquire_boot_count_path() won't log on its own for this specific error */ if (r == -EUNATCH) /* acquire_boot_count_path() won't log on its own for this specific error */
return log_error_errno(r, "Not booted with boot counting in effect."); return log_error_errno(r, "Not booted with boot counting in effect.");
+7
View File
@@ -80,6 +80,13 @@ if grep -qw luo_nboot=1 /proc/cmdline; then
assert_eq "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager KExecCount | jq -r '.data')" "1" assert_eq "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager KExecCount | jq -r '.data')" "1"
# Check that bless-boot does not fail after a kexec
if [[ -d /sys/firmware/efi ]]; then
for verb in good bad indeterminate; do
/usr/lib/systemd/systemd-bless-boot --path=/nonexistent "$verb"
done
fi
# The previous boot's shutdown timestamps were preserved across the kexec via LUO and are now exposed # The previous boot's shutdown timestamps were preserved across the kexec via LUO and are now exposed
# as the PreviousShutdown* properties (the live Shutdown* ones describe this boot, which is not shutting # as the PreviousShutdown* properties (the live Shutdown* ones describe this boot, which is not shutting
# down, so they are unset here). # down, so they are unset here).