diff --git a/man/bootctl.xml b/man/bootctl.xml
index 8d5b1d3d2c..4aab53ece1 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -46,33 +46,35 @@
- Shows brief information about the system firmware, the boot loader that was used to boot the
- system, the boot loaders currently available in the ESP, the boot loaders listed in the firmware's list of boot
- loaders and the current default boot loader entry. If no command is specified, this is the implied
- default.
+ Shows brief information about the system firmware, the boot loader that was used to
+ boot the system, the boot loaders currently available in the ESP, the boot loaders listed in the
+ firmware's list of boot loaders and the current default boot loader entry. If no command is
+ specified, this is the implied default.
+
+ See the example below for details of the output.
+
+
+
+
+ BOOL
+
+ Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
+ boolean argument which controls whether to show the firmware setup on next system reboot. If the
+ argument is omitted shows the current status of the flag, or whether the flag is supported. This
+ controls the same flag as systemctl reboot --firmware-setup, but is more low-level
+ and allows setting the flag independently from actually requesting a reboot.
+
+
+
+ STRING
+
+ When called without the optional argument, prints the current value of the
+ SystemdOptions EFI variable. When called with an argument, sets the variable to
+ that value. See
+ systemd1 for the
+ meaning of that variable.
-
-
- BOOL
-
- Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
- boolean argument which controls whether to show the firmware setup on next system reboot. If the
- argument is omitted shows the current status of the flag, or whether the flag is supported. This
- controls the same flag as systemctl reboot --firmware-setup, but is more
- low-level and allows setting the flag independently from actually requesting a
- reboot.
-
-
-
- STRING
-
- When called without the optional argument, prints the current value of the
- SystemdOptions EFI variable. When called with an argument, sets the
- variable to that value. See
- systemd1
- for the meaning of that variable.
-
@@ -84,17 +86,16 @@
systemd-boot.
-
Shows all available boot loader entries implementing the Boot Loader Specification, as well as any
other entries discovered or automatically generated by a boot loader implementing the Boot Loader
- Interface.
+ url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface.
+ JSON output may be requested with .
- JSON output may be requested with .
+ See the example below for details of the output.
@@ -104,9 +105,9 @@
Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob
pattern as argument. The command will set the default entry only for the next boot,
- the will set it persistently for all future boots.
+ the will set it persistently for all future boots.
- Optionally, the boot loader entry ID may be specified as one of: ,
+ Optionally, the boot loader entry ID may be specified as one of: ,
or , which correspond to the current default boot loader
entry for all future boots, the current default boot loader entry for the next boot, and the currently booted
boot loader entry. These special IDs are resolved to the current values of the EFI variables
@@ -137,7 +138,6 @@
disables the timeout while always showing the menu. When an empty string ("") is specified the
bootloader will revert to its default menu timeout.
-
@@ -347,6 +347,111 @@
the Extended Boot Loader partition.
+
+ Examples
+
+
+ Output from status and list
+
+ $ bootctl status
+System:
+ Firmware: UEFI 2.40 (firmware-version) ← firmware vendor and version
+ Secure Boot: disabled (setup) ← secure boot status
+ TPM2 Support: yes
+ Boot into FW: supported ← does the firmware support booting into itself
+
+Current Boot Loader: ← details about sd-boot or another boot loader
+ Product: systemd-boot version implementing the Boot Loader Interface
+ Features: ✓ Boot counting
+ ✓ Menu timeout control
+ ✓ One-shot menu timeout control
+ ✓ Default entry control
+ ✓ One-shot entry control
+ ✓ Support for XBOOTLDR partition
+ ✓ Support for passing random seed to OS
+ ✓ Load drop-in drivers
+ ✓ Boot loader sets ESP information
+ ESP: /dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000
+ File: └─/EFI/systemd/systemd-bootx64.efi
+
+Random Seed: ← random seed used for entropy in early boot
+ Passed to OS: yes
+ System Token: set
+ Exists: yes
+
+Available Boot Loaders on ESP:
+ ESP: /boot/efi (/dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000)
+ File: └─/EFI/systemd/systemd-bootx64.efi (systemd-boot 251
+ File: └─/EFI/BOOT/BOOTX64.EFI (systemd-boot 251
+
+Boot Loaders Listed in EFI Variables:
+ Title: Linux Boot Manager
+ ID: 0x0001
+ Status: active, boot-order
+ Partition: /dev/disk/by-partuuid/…
+ File: └─/EFI/systemd/systemd-bootx64.efi
+
+ Title: Fedora
+ ID: 0x0000
+ Status: active, boot-order
+ Partition: /dev/disk/by-partuuid/…
+ File: └─/EFI/fedora/shimx64.efi
+
+ Title: Linux-Firmware-Updater
+ ID: 0x0002
+ Status: active, boot-order
+ Partition: /dev/disk/by-partuuid/…
+ File: └─/EFI/fedora/fwupdx64.efi
+
+Boot Loader Entries:
+ $BOOT: /boot/efi (/dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000)
+
+Default Boot Loader Entry:
+ type: Boot Loader Specification Type #1 (.conf)
+ title: Fedora Linux 36 (Workstation Edition)
+ id: …
+ source: /boot/efi/loader/entries/entry-token-kernel-version.conf
+ version: kernel-version
+ machine-id: …
+ linux: /entry-token/kernel-version/linux
+ initrd: /entry-token/kernel-version/initrd
+ options: root=…
+
+
+ $ bootctl list
+Boot Loader Entries:
+ type: Boot Loader Specification Type #1 (.conf)
+ title: Fedora Linux 36 (Workstation Edition) (default) (selected)
+ id: …
+ source: /boot/efi/loader/entries/entry-token-kernel-version.conf
+ version: kernel-version
+ machine-id: …
+ linux: /entry-token/kernel-version/linux
+ initrd: /entry-token/kernel-version/initrd
+ options: root=…
+
+ type: Boot Loader Specification Type #2 (.efi)
+ title: Fedora Linux 35 (Workstation Edition)
+ id: …
+ source: /boot/efi/EFI/Linux/fedora-kernel-version.efi
+ version: kernel-version
+ machine-id: …
+ linux: /EFI/Linux/fedora-kernel-version.efi
+ options: root=…
+
+ type: Automatic
+ title: Reboot Into Firmware Interface
+ id: auto-reboot-to-firmware-setup
+ source: /sys/firmware/efi/efivars/LoaderEntries-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+
+
+ In the listing, (default) specifies the entry that will be
+ used by default, and (selected) specifies the entry that was
+ selected the last time (i.e. is currently running).
+
+
+
See Also
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 759b3ca86a..a393370f8d 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -115,7 +115,7 @@ static int acquire_esp(
free_and_replace(arg_esp_path, np);
log_debug("Using EFI System Partition at %s.", arg_esp_path);
- return 1;
+ return 0;
}
static int acquire_xbootldr(
@@ -583,6 +583,35 @@ static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type, BootEntryType);
+static int boot_config_load_and_select(
+ BootConfig *config,
+ const char *esp_path,
+ dev_t esp_devid,
+ const char *xbootldr_path,
+ dev_t xbootldr_devid) {
+
+ _cleanup_strv_free_ char **efi_entries = NULL;
+ int r;
+
+ /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
+ * find the same entries twice. */
+ bool same = esp_path && xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
+
+ r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
+ if (r < 0)
+ return r;
+
+ r = efi_loader_get_entries(&efi_entries);
+ if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+ log_debug_errno(r, "Boot loader reported no entries.");
+ else if (r < 0)
+ log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
+ else
+ (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
+
+ return boot_config_select_special_entries(config);
+}
+
static int boot_entry_show(
const BootEntry *e,
bool show_as_default,
@@ -682,16 +711,17 @@ static int boot_entry_show(
}
static int status_entries(
+ const BootConfig *config,
const char *esp_path,
sd_id128_t esp_partition_uuid,
const char *xbootldr_path,
sd_id128_t xbootldr_partition_uuid) {
- _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
sd_id128_t dollar_boot_partition_uuid;
const char *dollar_boot_path;
int r;
+ assert(config);
assert(esp_path || xbootldr_path);
if (xbootldr_path) {
@@ -709,21 +739,13 @@ static int status_entries(
SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
printf("\n\n");
- r = boot_config_load(&config, esp_path, xbootldr_path);
- if (r < 0)
- return r;
-
- r = boot_config_select_special_entries(&config);
- if (r < 0)
- return r;
-
- if (config.default_entry < 0)
- printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
+ if (config->default_entry < 0)
+ printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
else {
printf("Default Boot Loader Entry:\n");
r = boot_entry_show(
- boot_config_default_entry(&config),
+ boot_config_default_entry(config),
/* show_as_default= */ false,
/* show_as_selected= */ false,
/* show_discovered= */ false);
@@ -1593,13 +1615,9 @@ static void print_yes_no_line(bool first, bool good, const char *name) {
name);
}
-static int are_we_installed(void) {
+static int are_we_installed(const char *esp_path) {
int r;
- r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL);
- if (r < 0)
- return r;
-
/* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
* check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
* loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
@@ -1614,7 +1632,7 @@ static int are_we_installed(void) {
* → It specifically checks for systemd-boot, not for other boot loaders (which a check for
* /boot/loader/entries would do). */
- _cleanup_free_ char *p = path_join(arg_esp_path, "/EFI/systemd/");
+ _cleanup_free_ char *p = path_join(esp_path, "/EFI/systemd/");
if (!p)
return log_oom();
@@ -1645,7 +1663,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid);
if (arg_print_dollar_boot_path) {
if (r == -EACCES)
- return log_error_errno(r, "Failed to determine XBOOTLDR location: %m");
+ return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
if (r < 0)
return r;
@@ -1659,8 +1677,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (arg_print_esp_path || arg_print_dollar_boot_path)
return 0;
- r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
- * can show */
+ r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
+ * show what we can show */
pager_open(arg_pager_flags);
@@ -1776,16 +1794,20 @@ static int verb_status(int argc, char *argv[], void *userdata) {
}
if (arg_esp_path || arg_xbootldr_path) {
- /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */
- bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
+ _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
- k = status_entries(
- arg_esp_path,
- esp_uuid,
- same ? NULL : arg_xbootldr_path,
- same ? SD_ID128_NULL : xbootldr_uuid);
+ k = boot_config_load_and_select(&config,
+ arg_esp_path, esp_devid,
+ arg_xbootldr_path, xbootldr_devid);
if (k < 0)
r = k;
+ else {
+ k = status_entries(&config,
+ arg_esp_path, esp_uuid,
+ arg_xbootldr_path, xbootldr_uuid);
+ if (k < 0)
+ r = k;
+ }
}
return r;
@@ -1793,17 +1815,17 @@ 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 = BOOT_CONFIG_NULL;
- _cleanup_strv_free_ char **efi_entries = NULL;
dev_t esp_devid = 0, xbootldr_devid = 0;
int r;
- /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
- * off logging about access errors and turn off potentially privileged device probing. Here we're interested in
- * the latter but not the former, hence request the mode, and log about EACCES. */
+ /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
+ * things: turn off logging about access errors and turn off potentially privileged device probing.
+ * Here we're interested in the latter but not the former, hence request the mode, and log about
+ * EACCES. */
r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
- return log_error_errno(r, "Failed to determine ESP: %m");
+ return log_error_errno(r, "Failed to determine ESP location: %m");
if (r < 0)
return r;
@@ -1813,22 +1835,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */
- bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
-
- r = boot_config_load(&config, arg_esp_path, same ? NULL : arg_xbootldr_path);
- if (r < 0)
- return r;
-
- r = efi_loader_get_entries(&efi_entries);
- if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
- log_debug_errno(r, "Boot loader reported no entries.");
- else if (r < 0)
- log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
- else
- (void) boot_config_augment_from_loader(&config, efi_entries, /* only_auto= */ false);
-
- r = boot_config_select_special_entries(&config);
+ r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
if (r < 0)
return r;
@@ -2063,7 +2070,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
if (!install) {
/* If we are updating, don't do anything if sd-boot wasn't actually installed. */
- r = are_we_installed();
+ r = are_we_installed(arg_esp_path);
if (r < 0)
return r;
if (r == 0) {
@@ -2209,7 +2216,11 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
static int verb_is_installed(int argc, char *argv[], void *userdata) {
int r;
- r = are_we_installed();
+ r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = are_we_installed(arg_esp_path);
if (r < 0)
return r;
diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h
index 99bc5f69a4..93fb22e9d4 100644
--- a/src/shared/bootspec.h
+++ b/src/shared/bootspec.h
@@ -71,7 +71,7 @@ typedef struct BootConfig {
BootEntry* boot_config_find_entry(BootConfig *config, const char *id);
-static inline BootEntry* boot_config_default_entry(BootConfig *config) {
+static inline const BootEntry* boot_config_default_entry(const BootConfig *config) {
assert(config);
if (config->default_entry < 0)